const { TypeDataModel } = foundry.abstract; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { DocumentSheetConfig } = foundry.applications.apps; const { loadTemplates, renderTemplate } = foundry.applications.handlebars; const { ActorSheetV2, ItemSheetV2 } = foundry.applications.sheets; const { Notifications } = foundry.applications.ui; const { SetField, SchemaField, NumberField, StringField, BooleanField } = foundry.data.fields; const { Roll } = foundry.dice; const { OperatorTerm, NumericTerm } = foundry.dice.terms; const { ChatMessage } = foundry.documents; Hooks.once("i18nInit", async function() { const lang = game.i18n.lang; await game.i18n.setLanguage("de"); const fallback = game.i18n.translations; await game.i18n.setLanguage(lang); game.i18n._fallback = foundry.utils.mergeObject(fallback, game.i18n._fallback); }); class DSA41_Notifications extends Notifications { notify(message, type="info", {localize=false, permanent=false, console=true}={}) { if (permanent && message === "ERROR.RESOLUTION.Window") return; return super.notify(message, type, { localize, permanent, console }); } } Hooks.once("init", async function() { // CONFIG.debug.applications = true; // CONFIG.debug.combat = true; // CONFIG.debug.dice = true; // CONFIG.debug.documents = true; // CONFIG.debug.hooks = true; // CONFIG.debug.rollParsing = true; // CONFIG.ActiveEffect.documentClass = ActiveEffect; // CONFIG.ActiveEffect.legacyTransferral = false; CONFIG.ui.notifications = DSA41_Notifications CONFIG.Actor.dataModels.Player = DSA41_CharacterData; CONFIG.Actor.documentClass = DSA41_CharacterDocument; CONFIG.ChatMessage.documentClass = DSA41_ChatMessage; CONFIG.Combat.initiative.formula = "1d6 + @computed.initiative.wert"; CONFIG.Item.dataModels.Gegenstand = DSA41_GegenstandData; CONFIG.Item.dataModels.Ruestung = DSA41_RuestungData; CONFIG.Item.dataModels.Bewaffnung = DSA41_BewaffnungData; CONFIG.Item.dataModels.Talent = DSA41_TalentData; CONFIG.Item.dataModels.Kampftalent = DSA41_KampftalentData; CONFIG.Item.dataModels.Sonderfertigkeit = DSA41_SonderfertigkeitData; CONFIG.Item.dataModels.VorNachteil = DSA41_VorNachteilData; CONFIG.Item.dataModels.Zauber = DSA41_ZauberData; CONFIG.statusEffects = []; DocumentSheetConfig.registerSheet(Actor, "dsa41", DSA41_ActorSheet, { makeDefault: true, types: [ "Player", ] }); DocumentSheetConfig.registerSheet(Item, "dsa41", DSA41_ItemSheetV2, { makeDefault: true, types: [ "Gegenstand", "Ruestung", "Bewaffnung", "Talent", "Kampftalent", "Sonderfertigkeit", "VorNachteil", "Zauber", ] }); Handlebars.registerHelper({ "string-length": (value, options) => { return value.toString().length; } }); Handlebars.registerHelper({ "mul": (a, b, options) => { return a * b; } }); Handlebars.registerHelper({ maybeLocalize: (value, options) => { const prefix = options.hash.prefix ? options.hash.prefix.string : null; if (prefix) { const id = prefix + "." + value; const localized = game.i18n.localize(id); return id === localized ? value : localized; } return value; } }); Handlebars.registerHelper({ sorted: (array, options) => { return array.toSorted((a, b) => a.sort - b.sort); } }); Handlebars.registerHelper({ DSA41_input: (field_name, options) => { field_name = field_name.toString(); let fields = field_name.startsWith("system.") ? options.data.root.document.system.schema.fields : options.data.root.document.schema.fields; let field = foundry.utils.getProperty(fields, field_name.replace(/^(system\.)/, "").replaceAll(".", ".fields.")); if (!field) { console.error("Non-existent data field provided to {{DSA4_input}} handlebars helper."); return Handlebars.SafeString(""); } options.hash.placeholder ??= game.i18n.localize(options.hash.subtitle); const value = foundry.utils.getProperty(options.data.root.document, field_name); const input = field.toInput({ localize: true, value: value, ...options.hash }); if (options.hash.subtitle) { let subtitle_div = document.createElement("div"); subtitle_div.className = "subtitle"; subtitle_div.innerText = game.i18n.localize(options.hash.subtitle); let outer_div = document.createElement("div"); outer_div.replaceChildren(input, subtitle_div); return new Handlebars.SafeString(outer_div.outerHTML); } return new Handlebars.SafeString(input.outerHTML); } }); await loadTemplates({ "editable-input": "systems/dsa-4th-edition/src/EditableInput.hbs", "attribute_tooltip": "systems/dsa-4th-edition/src/Tooltips/Attribute.hbs", "attacke_tooltip": "systems/dsa-4th-edition/src/Tooltips/Attacke.hbs", "parade_tooltip": "systems/dsa-4th-edition/src/Tooltips/Parade.hbs", "trefferpunkte_tooltip": "systems/dsa-4th-edition/src/Tooltips/Trefferpunkte.hbs", "fernkampf_attacke_tooltip": "systems/dsa-4th-edition/src/Tooltips/FernkampfAttacke.hbs", "fernkampf_trefferpunkte_tooltip": "systems/dsa-4th-edition/src/Tooltips/FernkampfTrefferpunkte.hbs", "CHAT_HEADER": "systems/dsa-4th-edition/src/Chat/Header.hbs", "TrefferpunkteTargets": "systems/dsa-4th-edition/src/Chat/TrefferpunkteTargets.hbs", "talent_chat": "systems/dsa-4th-edition/src/Chat/Talent.hbs", "zauber_chat": "systems/dsa-4th-edition/src/Chat/Zauber.hbs", }); }); function string_compare(a, b) { return a > b ? 1 : a < b ? -1 : 0; } function get_targeted_actors() { const targets = []; for (const token of game.user.targets) { if (token.actor) { targets.push(token.actor.uuid); } } return targets; } async function instantiateTemplate(template, context) { const html = await renderTemplate(template, context); const template_element = document.createElement("template"); template_element.innerHTML = html; return template_element.content.firstChild; } class DSA41_ChatMessage extends ChatMessage { get actor() { if (this.speaker.scene && this.speaker.token) { const scene = game.scenes.get(this.speaker.scene); const token = scene?.tokens.get(this.speaker.token); if (token) return token.actor; } return game.actors.get(this.speaker.actor); } async renderHTML({ canDelete, canClose=false, ...rest }={}) { const html = await super.renderHTML({ canDelete, canClose, ...rest }); const img = this.actor?.img ?? this.author.avatar; const name = this.alias; const header = await instantiateTemplate("CHAT_HEADER", { img: img, name: name, author: this.author?.name ?? "" }); const sender = html.querySelector(".message-sender"); sender?.replaceChildren(header); if (this.flags.dsa41?.type === "trefferpunkte" && this.flags.dsa41?.targets?.length != 0) { const targets = this.flags.dsa41?.targets.map(x => fromUuidSync(x, { strict: false })).filter(x => x !== null); const targets_list = await instantiateTemplate("TrefferpunkteTargets", targets); html.querySelector(".message-content")?.appendChild(targets_list); } for (const element of html.querySelectorAll("[data-action]")) { element.addEventListener("click", async event => { const target = event.target; const action = target.dataset.action; if (action === null) return; if (action === "apply_damage") { const target_actor_id = target.closest("[data-actor-id]")?.dataset.actorId; if (!target_actor_id) return; const target_actor = fromUuidSync(target_actor_id); if (!target_actor) return; const dialog_data = await DSA41_Dialog.wait("Trefferzone", { window: {title: "Trefferzone"} }); const trefferzone = dialog_data.trefferzone; const rolled_damage = this.rolls[0].total; const target_hp = target_actor.system.lebenspunkte.aktuell; const target_rs = target_actor.system.computed.kampf.ruestungen_gesamt[trefferzone]; const damage = Math.max(rolled_damage - target_rs, 0); const new_hp = target_hp - damage; target_actor.update({ "system.lebenspunkte.aktuell": new_hp }); } }); } return html; } } function get_minified_formula(formula, data) { const terms = Roll.simplifyTerms(Roll.parse(formula, data)); let output = []; let is_combinable = true; for (let i = 0; i < terms.length; i++) { const term = terms[i]; if (is_combinable && term instanceof NumericTerm) { for (let j = i + 1; j < terms.length - 1; j += 2) { let operator = terms[j]; let next = terms[j + 1]; if (!(operator instanceof OperatorTerm && next instanceof NumericTerm)) break; switch (operator.operator) { case "+": { term.number += next.number; i += 2; } break; case "-": { term.number -= next.number; i += 2; } break; default: break; } } is_combinable = false; } else { if (term instanceof OperatorTerm && (term.operator == "+" || term.operator == "-")) { is_combinable = true; } else { is_combinable = false; } } output.push(term); } output = Roll.simplifyTerms(output); return Roll.getFormula(output); } class PreisUnitField extends StringField { constructor(options={}, context={}) { return super({ required: true, choices: { "kreuzer": "DSA41.currency.kreuzer", "heller": "DSA41.currency.heller", "silbertaler": "DSA41.currency.silbertaler", "dukaten": "DSA41.currency.dukaten", }, initial: "silbertaler", ...options }, context); } } class PreisField extends SchemaField { constructor() { return super({ value: new NumberField({ initial: 0, min: 0 }), unit: new PreisUnitField(), }); } _toInput(config) { const value_input = this.fields["value"].toInput({ value: config.value.value, }); const unit_input = this.fields["unit"] .toInput({ value: config.value.unit, localize: true }); let outer_div = document.createElement("div"); outer_div.className = "price-input"; outer_div.replaceChildren(value_input, unit_input); return outer_div; } } class GewichtUnitField extends StringField { constructor(options={}, context={}) { return super({ required: true, choices: { "gran": "DSA41.weight.gran", "karat": "DSA41.weight.karat", "skrupel": "DSA41.weight.skrupel", "unze": "DSA41.weight.unze", "stein": "DSA41.weight.stein", "sack": "DSA41.weight.sack", "quader": "DSA41.weight.quader", }, initial: "stein", ...options }, context); } } class GewichtField extends SchemaField { constructor() { return super({ value: new NumberField({ initial: 0, min: 0 }), unit: new GewichtUnitField(), }); } _toInput(config) { const value_input = this.fields["value"].toInput({ value: config.value.value, }); const unit_input = this.fields["unit"] .toInput({ value: config.value.unit, localize: true }); let outer_div = document.createElement("div"); outer_div.className = "weight-input"; outer_div.replaceChildren(value_input, unit_input); return outer_div; } } class LaengeUnitField extends StringField { constructor(options={}, context={}) { return super({ required: true, choices: { "halbfinger": "DSA41.laenge.halbfinger", "finger": "DSA41.laenge.finger", "spann": "DSA41.laenge.spann", "schritt": "DSA41.laenge.schritt", "faden": "DSA41.laenge.faden", "lot": "DSA41.laenge.lot", "meile": "DSA41.laenge.meile", "tagesreise": "DSA41.laenge.tagesreise", "baryd": "DSA41.laenge.baryd", }, initial: "schritt", ...options }, context); } } class LaengeField extends SchemaField { constructor() { return super({ value: new NumberField({ initial: 0, min: 0 }), unit: new LaengeUnitField(), }); } _toInput(config) { const value_input = this.fields["value"].toInput({ value: config.value.value, }); const unit_input = this.fields["unit"] .toInput({ value: config.value.unit, localize: true }); let outer_div = document.createElement("div"); outer_div.className = "length-input"; outer_div.replaceChildren(value_input, unit_input); return outer_div; } } class AttributeField extends SchemaField { constructor() { return super({ initial: new NumberField({ integer: true, initial: 8, min: 0 }), advancement: new NumberField({ integer: true, initial: 0, min: 0 }), modifier: new NumberField({ integer: true, initial: 0 }), }); } } class AttributeChoiceField extends StringField { constructor(options={}, context={}) { return super({ required: true, choices: { "courage": "DSA41.attributes.long.courage", "cleverness": "DSA41.attributes.long.cleverness", "intuition": "DSA41.attributes.long.intuition", "charisma": "DSA41.attributes.long.charisma", "dexterity": "DSA41.attributes.long.dexterity", "agility": "DSA41.attributes.long.agility", "constitution": "DSA41.attributes.long.constitution", "strength": "DSA41.attributes.long.strength", }, initial: "courage", ...options, }, context); } } class SteigerungsKategorieField extends StringField { constructor(options={}, context={}) { return super({ required: true, choices: { "A_Star": "DSA41.steigerungskategorie.A_Star", "A": "DSA41.steigerungskategorie.A", "B": "DSA41.steigerungskategorie.B", "C": "DSA41.steigerungskategorie.C", "D": "DSA41.steigerungskategorie.D", "E": "DSA41.steigerungskategorie.E", "F": "DSA41.steigerungskategorie.F", "G": "DSA41.steigerungskategorie.G", "H": "DSA41.steigerungskategorie.H", }, initial: "A_Star", ...options, }, context); } } class DSA41_CharacterDocument extends Actor { static async create(data, operation) { const actor = await super.create(data, operation); if (data.type === "Player") { actor.prototypeToken.update({ actorLink: true }); const talente_compendium = game.packs.get("dsa-4th-edition.talente"); const talente = await talente_compendium.getDocuments({ name__in: [ // Basis Körperliche Talente "Athletik", "Klettern", "Körperbeherrschung", "Schleichen", "Schwimmen", "Selbstbeherrschung", "Sich Verstecken", "Singen", "Sinnenschärfe", "Tanzen", "Zechen", // Basis Gesellschaftliche Talente "Menschenkenntnis", "Überreden", // Basis Natur Talente "Fährtensuchen", "Orientierung", "Wildnisleben", // Basis Wissens Talente "Götter/Kulte", "Rechnen", "Sagen/Legenden", // Basis Handwerks Talente "Heilkunde Wunden", "Holzbearbeitung", "Kochen", "Lederarbeiten", "Malen/Zeichnen", "Schneidern", // Basis Kampftalente "Dolche", "Hiebwaffen", "Raufen", "Ringen", "Säbel", "Wurfmesser", ]}); await actor.createEmbeddedDocuments("Item", talente); } return actor; } get_hp_roll_modifier() { if (this.system.lebenspunkte.aktuell < Math.round(this.system.computed.lebenspunkte.max / 4)) return 3; if (this.system.lebenspunkte.aktuell < Math.round(this.system.computed.lebenspunkte.max / 3)) return 2; if (this.system.lebenspunkte.aktuell < Math.round(this.system.computed.lebenspunkte.max / 2)) return 1; return 0; } } class DSA41_CharacterData extends TypeDataModel { static defineSchema() { return { race: new StringField(), culture: new StringField(), profession: new StringField(), sozialstatus: new NumberField({ integer: true, inital: 1 }), allgemein: new SchemaField({ geschlecht: new StringField(), alter: new StringField(), groesse: new LaengeField(), gewicht: new GewichtField(), haarfarbe: new StringField(), augenfarbe: new StringField(), stand: new StringField(), titel: new StringField(), aussehen: new StringField(), hintergrund: new StringField(), biografie: new StringField(), }), abenteuerpunkte: new SchemaField({ ausgegeben: new NumberField({ integer: true, initial: 0 }), gesamt: new NumberField({ integer: true, initial: 0 }), }), currency: new SchemaField({ dukaten: new NumberField({ integer: true, initial: 0 }), silbertaler: new NumberField({ integer: true, initial: 0 }), heller: new NumberField({ integer: true, initial: 0 }), kreuzer: new NumberField({ integer: true, initial: 0 }), }), attributes: new SchemaField({ courage: new AttributeField(), cleverness: new AttributeField(), intuition: new AttributeField(), charisma: new AttributeField(), dexterity: new AttributeField(), agility: new AttributeField(), constitution: new AttributeField(), strength: new AttributeField(), }), lebenspunkte: new SchemaField({ modifikator: new NumberField({integer: true, initial: 0}), zukauf: new NumberField({integer: true, initial: 0}), verlust: new NumberField({integer: true, initial: 0}), aktuell: new NumberField({integer: true, initial: 0}), }), ausdauer: new SchemaField({ modifikator: new NumberField({integer: true, initial: 0}), zukauf: new NumberField({integer: true, initial: 0}), verlust: new NumberField({integer: true, initial: 0}), aktuell: new NumberField({integer: true, initial: 0}), }), astralenergie: new SchemaField({ modifikator: new NumberField({integer: true, initial: 0}), zukauf: new NumberField({integer: true, initial: 0}), verlust: new NumberField({integer: true, initial: 0}), aktuell: new NumberField({integer: true, initial: 0}), }), karmalenergie: new NumberField({integer: true, inital: 0}), magieresistenz: new SchemaField({ modifikator: new NumberField({integer: true, initial: 0}), zukauf: new NumberField({integer: true, initial: 0}), aktuell: new NumberField({integer: true, initial: 0}), }), modifikator_initiative: new NumberField({integer: true, initial: 0}), modifikator_attacke: new NumberField({integer: true, initial: 0}), modifikator_parade: new NumberField({integer: true, initial: 0}), modifikator_fernkampf: new NumberField({integer: true, initial: 0}), wunden: new SchemaField({ kopf: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), brust: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), bauch: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), linker_arm: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), rechter_arm: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), linkes_bein: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), rechtes_bein: new NumberField({integer: true, initial: 0, min: 0, max: 3 }), }), } } async prepareDerivedData() { super.prepareDerivedData(); this.computed = { abenteuerpunkte: {}, attributes: {}, attributes_without_modifiers: {}, num_vorteile: 0, num_nachteile: 0, num_allgemeine_sonderfertigkeiten: 0, num_kampf_sonderfertigkeiten: 0, num_magische_sonderfertigkeiten: 0, num_waffen: 0, num_fernkampf_waffen: 0, kampf: { talente: {}, waffen: {}, fernkampf_waffen: {}, ruestungen: {}, ruestungen_gesamt: { kopf: 0, brust: 0, ruecken: 0, bauch: 0, linker_arm: 0, rechter_arm: 0, linkes_bein: 0, rechtes_bein: 0, gesamt_ruestungsschutz: 0.0, gesamt_behinderung: 0.0, }, }, wunden_modifiers: { courage: 0, cleverness: 0, intuition: 0, dexterity: 0, agility: 0, constitution: 0, strength: 0, initiative_basis: 0, initiative: "", attacke: 0, parade: 0, schaden: "", }, }; if (this.wunden.kopf >= 1) { this.computed.wunden_modifiers.courage += -2 * this.wunden.kopf; this.computed.wunden_modifiers.cleverness += -2 * this.wunden.kopf; this.computed.wunden_modifiers.intuition += -2 * this.wunden.kopf; this.computed.wunden_modifiers.initiative_basis += -2 * this.wunden.kopf; this.computed.wunden_modifiers.initiative += (-2 * this.wunden.kopf) + "d6"; if (this.wunden.kopf >= 3) this.computed.wunden_modifiers.schaden += "+ 2d6"; } if (this.wunden.brust >= 1) { this.computed.wunden_modifiers.attacke += -this.wunden.brust; this.computed.wunden_modifiers.parade += -this.wunden.brust; this.computed.wunden_modifiers.constitution += -this.wunden.brust; this.computed.wunden_modifiers.strength += -this.wunden.brust; this.computed.wunden_modifiers.schaden += "+" + this.wunden.brust + "d6"; } if (this.wunden.bauch >= 1) { this.computed.wunden_modifiers.attacke += -this.wunden.bauch; this.computed.wunden_modifiers.parade += -this.wunden.bauch; this.computed.wunden_modifiers.constitution += -this.wunden.bauch; this.computed.wunden_modifiers.strength += -this.wunden.bauch; this.computed.wunden_modifiers.initiative_basis += -this.wunden.bauch; this.computed.wunden_modifiers.schaden += "+" + this.wunden.bauch + "d6"; } if (this.wunden.linker_arm >= 1) { this.computed.wunden_modifiers.attacke += -2 * this.wunden.linker_arm; this.computed.wunden_modifiers.parade += -2 * this.wunden.linker_arm; this.computed.wunden_modifiers.strength += -2 * this.wunden.linker_arm; this.computed.wunden_modifiers.dexterity += -2 * this.wunden.linker_arm; } if (this.wunden.rechter_arm >= 1) { this.computed.wunden_modifiers.attacke += -2 * this.wunden.rechter_arm; this.computed.wunden_modifiers.parade += -2 * this.wunden.rechter_arm; this.computed.wunden_modifiers.strength += -2 * this.wunden.rechter_arm; this.computed.wunden_modifiers.dexterity += -2 * this.wunden.rechter_arm; } if (this.wunden.linkes_bein >= 1) { this.computed.wunden_modifiers.attacke += -2 * this.wunden.linkes_bein; this.computed.wunden_modifiers.parade += -2 * this.wunden.linkes_bein; this.computed.wunden_modifiers.agility += -2 * this.wunden.linkes_bein; this.computed.wunden_modifiers.initiative_basis += -2 * this.wunden.linkes_bein; } if (this.wunden.rechtes_bein >= 1) { this.computed.wunden_modifiers.attacke += -2 * this.wunden.rechtes_bein; this.computed.wunden_modifiers.parade += -2 * this.wunden.rechtes_bein; this.computed.wunden_modifiers.agility += -2 * this.wunden.rechtes_bein; this.computed.wunden_modifiers.initiative_basis += -2 * this.wunden.rechtes_bein; } this.computed.abenteuerpunkte.uebrig = this.abenteuerpunkte.gesamt - this.abenteuerpunkte.ausgegeben; this.computed.attributes_without_modifiers.courage = this.attributes.courage.initial + this.attributes.courage.advancement; this.computed.attributes_without_modifiers.cleverness = this.attributes.cleverness.initial + this.attributes.cleverness.advancement; this.computed.attributes_without_modifiers.intuition = this.attributes.intuition.initial + this.attributes.intuition.advancement; this.computed.attributes_without_modifiers.charisma = this.attributes.charisma.initial + this.attributes.charisma.advancement; this.computed.attributes_without_modifiers.dexterity = this.attributes.dexterity.initial + this.attributes.dexterity.advancement; this.computed.attributes_without_modifiers.agility = this.attributes.agility.initial + this.attributes.agility.advancement; this.computed.attributes_without_modifiers.constitution = this.attributes.constitution.initial + this.attributes.constitution.advancement; this.computed.attributes_without_modifiers.strength = this.attributes.strength.initial + this.attributes.strength.advancement; this.computed.attributes.courage = this.computed.attributes_without_modifiers.courage + this.attributes.courage.modifier + this.computed.wunden_modifiers.courage; this.computed.attributes.cleverness = this.computed.attributes_without_modifiers.cleverness + this.attributes.cleverness.modifier + this.computed.wunden_modifiers.cleverness; this.computed.attributes.intuition = this.computed.attributes_without_modifiers.intuition + this.attributes.intuition.modifier + this.computed.wunden_modifiers.intuition; this.computed.attributes.charisma = this.computed.attributes_without_modifiers.charisma + this.attributes.charisma.modifier; this.computed.attributes.dexterity = this.computed.attributes_without_modifiers.dexterity + this.attributes.dexterity.modifier + this.computed.wunden_modifiers.dexterity; this.computed.attributes.agility = this.computed.attributes_without_modifiers.agility + this.attributes.agility.modifier + this.computed.wunden_modifiers.agility; this.computed.attributes.constitution = this.computed.attributes_without_modifiers.constitution + this.attributes.constitution.modifier + this.computed.wunden_modifiers.constitution; this.computed.attributes.strength = this.computed.attributes_without_modifiers.strength + this.attributes.strength.modifier + this.computed.wunden_modifiers.strength; this.computed.lebenspunkte = {}; this.computed.ausdauer = {}; this.computed.astralenergie = {}; this.computed.magieresistenz = {}; this.computed.lebenspunkte.basiswert = Math.round((this.computed.attributes_without_modifiers.constitution + this.computed.attributes_without_modifiers.constitution + this.computed.attributes_without_modifiers.strength) / 2); this.computed.ausdauer.basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.constitution + this.computed.attributes_without_modifiers.agility) / 2); this.computed.astralenergie.basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.charisma) / 2); this.computed.magieresistenz.basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.cleverness + this.computed.attributes_without_modifiers.constitution) / 5); this.computed.lebenspunkte.max = this.computed.lebenspunkte.basiswert + this.lebenspunkte.modifikator + this.lebenspunkte.zukauf - this.lebenspunkte.verlust; this.computed.ausdauer.max = this.computed.ausdauer.basiswert + this.ausdauer.modifikator + this.ausdauer.zukauf - this.ausdauer.verlust; this.computed.astralenergie.max = this.computed.astralenergie.basiswert + this.astralenergie.modifikator + this.astralenergie.zukauf - this.astralenergie.verlust; this.computed.magieresistenz.max = this.computed.magieresistenz.basiswert + this.magieresistenz.modifikator + this.magieresistenz.zukauf; this.computed.lebenspunkte.prozent = this.lebenspunkte.aktuell / this.computed.lebenspunkte.max * 100; this.computed.ausdauer.prozent = this.ausdauer.aktuell / this.computed.ausdauer.max * 100; this.computed.astralenergie.prozent = this.astralenergie.aktuell / this.computed.astralenergie.max * 100; this.computed.magieresistenz.prozent = this.magieresistenz.aktuell / this.computed.magieresistenz.max * 100; this.computed.initiative = {}; this.computed.attacke = {}; this.computed.parade = {}; this.computed.fernkampf = {}; this.computed.initiative.basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.agility) / 5); this.computed.attacke.basiswert = Math.round((this.computed.attributes_without_modifiers.courage + this.computed.attributes_without_modifiers.agility + this.computed.attributes_without_modifiers.strength) / 5); this.computed.parade.basiswert = Math.round((this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.agility + this.computed.attributes_without_modifiers.strength) / 5); this.computed.fernkampf.basiswert = Math.round((this.computed.attributes_without_modifiers.intuition + this.computed.attributes_without_modifiers.dexterity + this.computed.attributes_without_modifiers.strength) / 5); const equipped_ruestungen = this.parent.items.filter((x) => x.type === "Ruestung" && x.system.angelegt === true); for (const item of equipped_ruestungen) { this.computed.kampf.ruestungen[item._id] = { item: item }; this.computed.kampf.ruestungen_gesamt.kopf += item.system.kopf; this.computed.kampf.ruestungen_gesamt.brust += item.system.brust; this.computed.kampf.ruestungen_gesamt.ruecken += item.system.ruecken; this.computed.kampf.ruestungen_gesamt.bauch += item.system.bauch; this.computed.kampf.ruestungen_gesamt.linker_arm += item.system.linker_arm; this.computed.kampf.ruestungen_gesamt.rechter_arm += item.system.rechter_arm; this.computed.kampf.ruestungen_gesamt.linkes_bein += item.system.linkes_bein; this.computed.kampf.ruestungen_gesamt.rechtes_bein += item.system.rechtes_bein; this.computed.kampf.ruestungen_gesamt.gesamt_ruestungsschutz += item.system.gesamt_ruestungsschutz; this.computed.kampf.ruestungen_gesamt.gesamt_behinderung += item.system.gesamt_behinderung; } for (let [key, value] of Object.entries(this.computed.kampf.ruestungen_gesamt)) { this.computed.kampf.ruestungen_gesamt[key] = Math.round(value); } this.computed.initiative.wert = this.computed.initiative.basiswert + this.modifikator_initiative - this.computed.kampf.ruestungen_gesamt.gesamt_behinderung + this.computed.wunden_modifiers.initiative_basis; this.computed.attacke.wert = this.computed.attacke.basiswert + this.modifikator_attacke + this.computed.wunden_modifiers.attacke; this.computed.parade.wert = this.computed.parade.basiswert + this.modifikator_parade + this.computed.wunden_modifiers.parade; this.computed.fernkampf.wert = this.computed.fernkampf.basiswert + this.modifikator_fernkampf; this.kampftalente = this.parent.items.filter((x) => x.type === "Kampftalent").sort((a, b) => string_compare(a.name, b.name)); for(const talent of this.kampftalente) { this.computed.kampf.talente[talent.name] = {}; this.computed.kampf.talente[talent.name].attacke = (talent.system.kategorie === "fernkampf" ? this.computed.fernkampf.wert : this.computed.attacke.wert) + talent.system.attacke; this.computed.kampf.talente[talent.name].parade = this.computed.parade.wert + talent.system.parade; this.computed.kampf.talente[talent.name].talent_attacke = talent.system.attacke; this.computed.kampf.talente[talent.name].talent_parade = talent.system.parade; } const equipped_bewaffnung = this.parent.items.filter((x) => x.type === "Bewaffnung" && x.system.angelegt === true); const equipped_nahkampfwaffen = equipped_bewaffnung.filter((x) => x.system.nahkampfwaffe.aktiv); const equipped_parierwaffen = equipped_bewaffnung.filter((x) => x.system.parierwaffe.aktiv); const equipped_schilde = equipped_bewaffnung.filter((x) => x.system.schild.aktiv); const equipped_fernkampfwaffen = equipped_bewaffnung.filter((x) => x.system.fernkampfwaffe.aktiv); for(const item of equipped_nahkampfwaffen) { if (item.system.nahkampfwaffe.aktiv) { this.computed.num_waffen += 1; let computed = this.computed.kampf.waffen[item._id] = { item: item, attacke: 0, parade: 0, parade_crit: 0, trefferpunkte: "", trefferpunkte_display: "", basis_attacke: 0, basis_parade: 0, talent_attacke: 0, talent_parade: 0, modifikator_attacke: 0, modifikator_parade: 0, tp_kk: 0, parierwaffe_attacke: 0, parierwaffe_parade: 0, schild_attacke: 0, schild_parade: 0, }; computed.basis_attacke = this.computed.attacke.wert; computed.basis_parade = this.computed.parade.wert; const talent = item.system.nahkampfwaffe.kampftalente; computed.talent_attacke = this.computed.kampf.talente[talent]?.talent_attacke ?? 0; computed.talent_parade = this.computed.kampf.talente[talent]?.talent_parade ?? 0; computed.modifikator_attacke = item.system.nahkampfwaffe.modifikator_attacke; computed.modifikator_parade = item.system.nahkampfwaffe.modifikator_parade; if (item.system.nahkampfwaffe.schadensschritte != 0) { computed.tp_kk = Math.trunc((this.computed.attributes.strength - item.system.nahkampfwaffe.schwellenwert) / item.system.nahkampfwaffe.schadensschritte); } for (const parierwaffe of equipped_parierwaffen) { if (parierwaffe._id === item._id) continue; computed.parierwaffe_attacke += parierwaffe.system.parierwaffe.modifikator_attacke; computed.parierwaffe_parade += parierwaffe.system.parierwaffe.modifikator_parade; } for (const schild of equipped_schilde) { if (schild._id === item._id) continue; computed.schild_attacke += schild.system.schild.modifikator_attacke; computed.schild_parade += schild.system.schild.modifikator_parade; } computed.attacke = computed.basis_attacke + computed.talent_attacke + computed.modifikator_attacke + computed.parierwaffe_attacke + computed.schild_attacke + Math.min(computed.tp_kk, 0); computed.parade = computed.basis_parade + computed.talent_parade + computed.modifikator_parade + computed.parierwaffe_parade + computed.schild_parade + Math.min(computed.tp_kk, 0); computed.parade_crit = Math.round(computed.parade / 2); computed.trefferpunkte = get_minified_formula(item.system.nahkampfwaffe.basis + (computed.tp_kk != 0 ? " + " + computed.tp_kk : "")); computed.trefferpunkte_display = computed.trefferpunkte.replace(/[\+\-]/, (op) => "
" + op); } } for (const item of equipped_fernkampfwaffen) { this.computed.num_fernkampf_waffen += 1; let computed = this.computed.kampf.fernkampf_waffen[item._id] = { item: item, attacke: 0, trefferpunkte: "", trefferpunkte_display: "", basis_attacke: 0, talent_attacke: 0, }; computed.basis_attacke = this.computed.fernkampf.wert; const talent = item.system.nahkampfwaffe.kampftalente; computed.talent_attacke = this.computed.kampf.talente[talent]?.talent_attacke ?? 0; computed.attacke = computed.basis_attacke + computed.talent_attacke; computed.trefferpunkte = get_minified_formula(item.system.fernkampfwaffe.basis); computed.trefferpunkte_display = computed.trefferpunkte.replace(/[\+\-]/, (op) => "
" + op); } const talente = this.parent.items.filter((x) => x.type === "Talent").sort((a, b) => string_compare(a.name, b.name)); this.talente = { koerperliche: talente.filter((x) => x.system.kategorie === "koerperliche"), gesellschaftliche: talente.filter((x) => x.system.kategorie === "gesellschaftliche"), natur: talente.filter((x) => x.system.kategorie === "natur"), wissens: talente.filter((x) => x.system.kategorie === "wissens"), handwerks: talente.filter((x) => x.system.kategorie === "handwerks"), }; this.computed.num_allgemeine_sonderfertigkeiten = this.parent.items.filter((x) => x.type === "Sonderfertigkeit" && x.system.kategorie === "allgemein").length; this.computed.num_kampf_sonderfertigkeiten = this.parent.items.filter((x) => x.type === "Sonderfertigkeit" && x.system.kategorie === "kampf").length; this.computed.num_magische_sonderfertigkeiten = this.parent.items.filter((x) => x.type === "Sonderfertigkeit" && x.system.kategorie === "magisch").length; this.computed.num_vorteile = this.parent.items.filter((x) => x.type === "VorNachteil" && x.system.kategorie === "vorteil").length; this.computed.num_nachteile = this.parent.items.filter((x) => x.type === "VorNachteil" && x.system.kategorie === "nachteil").length; } } class DSA41_GegenstandData extends TypeDataModel { static defineSchema() { return { gewicht: new GewichtField(), preis: new PreisField(), anzahl: new NumberField({ integer: true, initial: 1, min: 0 }), beschreibung: new StringField({ initial: "" }), }; } } class DSA41_RuestungData extends TypeDataModel { static defineSchema() { return { angelegt: new BooleanField({ initial: false }), gewicht: new GewichtField(), preis: new PreisField(), kopf: new NumberField({ integer: true, initial: 0, min: 0 }), brust: new NumberField({ integer: true, initial: 0, min: 0 }), ruecken: new NumberField({ integer: true, initial: 0, min: 0 }), bauch: new NumberField({ integer: true, initial: 0, min: 0 }), linker_arm: new NumberField({ integer: true, initial: 0, min: 0 }), rechter_arm: new NumberField({ integer: true, initial: 0, min: 0 }), linkes_bein: new NumberField({ integer: true, initial: 0, min: 0 }), rechtes_bein: new NumberField({ integer: true, initial: 0, min: 0 }), gesamt_ruestungsschutz: new NumberField({ integer: false, initial: 0, min: 0 }), gesamt_behinderung: new NumberField({ integer: false, initial: 0, min: 0 }), beschreibung: new StringField({ initial: "" }), }; } } class DSA41_BewaffnungData extends TypeDataModel { static defineSchema() { return { angelegt: new BooleanField({ initial: false }), gewicht: new GewichtField(), preis: new PreisField(), beschreibung: new StringField({ initial: "" }), nahkampfwaffe: new SchemaField({ aktiv: new BooleanField({ initial: false }), basis: new StringField({ initial: "1d4" }), schwellenwert: new NumberField({ integer: true, initial: 0, min: 0 }), schadensschritte: new NumberField({ integer: true, initial: 0, min: 0 }), modifikator_attacke: new NumberField({ integer: true, initial: 0 }), modifikator_parade: new NumberField({ integer: true, initial: 0 }), initiative: new NumberField({ integer: true, initial: 0 }), bruchfaktor: new NumberField({ integer: true, initial: 0 }), distanzklasse: new StringField({ initial: "" }), kampftalente: new StringField({ initial: "" }), laenge: new LaengeField(), zweihaendig: new BooleanField({ initial: false }), werfbar: new BooleanField({ initial: false }), improvisiert: new BooleanField({ initial: false }), priviligiert: new BooleanField({ initial: false }), }), parierwaffe: new SchemaField({ aktiv: new BooleanField({ initial: false }), modifikator_attacke: new NumberField({ integer: true, initial: 0 }), modifikator_parade: new NumberField({ integer: true, initial: 0 }), initiative: new NumberField({ integer: true, initial: 0 }), bruchfaktor: new NumberField({ integer: true, initial: 0 }), }), schild: new SchemaField({ aktiv: new BooleanField({ initial: false }), groesse: new StringField({ required: true, choices: { "klein": "DSA41.bewaffnung.schild.groesse.klein", "gross": "DSA41.bewaffnung.schild.groesse.gross", "sehr_gross": "DSA41.bewaffnung.schild.groesse.sehr_gross", }, initial: "klein", }), modifikator_attacke: new NumberField({ integer: true, initial: 0 }), modifikator_parade: new NumberField({ integer: true, initial: 0 }), initiative: new NumberField({ integer: true, initial: 0 }), bruchfaktor: new NumberField({ integer: true, initial: 0 }), }), fernkampfwaffe: new SchemaField({ aktiv: new BooleanField({ initial: false }), basis: new StringField({ initial: "1d6 + 1" }), laden: new NumberField({ integer: true, initial: 0 }), reichweite1: new NumberField({ integer: true, initial: 0 }), reichweite2: new NumberField({ integer: true, initial: 0 }), reichweite3: new NumberField({ integer: true, initial: 0 }), reichweite4: new NumberField({ integer: true, initial: 0 }), reichweite5: new NumberField({ integer: true, initial: 0 }), modifikator1: new NumberField({ integer: true, initial: 0, nullable: true }), modifikator2: new NumberField({ integer: true, initial: 0, nullable: true }), modifikator3: new NumberField({ integer: true, initial: 0, nullable: true }), modifikator4: new NumberField({ integer: true, initial: 0, nullable: true }), modifikator5: new NumberField({ integer: true, initial: 0, nullable: true }), munitionskosten: new NumberField({ integer: true, initial: 0 }), munitionsgewicht: new NumberField({ integer: true, initial: 0 }), }), }; } } class DSA41_TalentData extends TypeDataModel { static defineSchema() { return { kategorie: new StringField({ required: true, choices: { "koerperliche": "DSA41.talente.koerperliche.label", "gesellschaftliche": "DSA41.talente.gesellschaftliche.label", "natur": "DSA41.talente.natur.label", "wissens": "DSA41.talente.wissens.label", "handwerks": "DSA41.talente.handwerks.label", }, initial: "koerperliche" }), behinderung: new StringField({ initial: "" }), attribute1: new AttributeChoiceField(), attribute2: new AttributeChoiceField(), attribute3: new AttributeChoiceField(), talentwert: new NumberField({ integer: true, initial: 0 }), beschreibung: new StringField({ initial: "" }), }; } } class DSA41_KampftalentData extends TypeDataModel { static defineSchema() { return { kategorie: new StringField({ required: true, choices: { "waffenlos": "DSA41.kampftalent.kategorie.waffenlos", "nahkampf": "DSA41.kampftalent.kategorie.nahkampf", "fernkampf": "DSA41.kampftalent.kategorie.fernkampf", }, initial: "waffenlos" }), behinderung: new StringField({ initial: "" }), steigern: new SteigerungsKategorieField(), beschreibung: new StringField({ initial: "" }), talentwert: new NumberField({ integer: true, initial: 0 }), attacke: new NumberField({ integer: true, initial: 0 }), parade: new NumberField({ integer: true, initial: 0 }), }; } } class DSA41_SonderfertigkeitData extends TypeDataModel { static defineSchema() { return { kategorie: new StringField({ required: true, choices: { "allgemein": "DSA41.sonderfertigkeiten.kategorie.allgemein", "kampf": "DSA41.sonderfertigkeiten.kategorie.kampf", "magisch": "DSA41.sonderfertigkeiten.kategorie.magisch", "klerikal": "DSA41.sonderfertigkeiten.kategorie.klerikal", }, initial: "allgemein", }), kosten: new NumberField({ integer: true, initial: 0 }), verbreitung: new NumberField({ integer: true, initial: 0 }), beschreibung: new StringField({ initial: "" }), }; } } class DSA41_VorNachteilData extends TypeDataModel { static defineSchema() { return { kategorie: new StringField({ required: true, choices: { "vorteil": "DSA41.vornachteil.kategorie.vorteil", "nachteil": "DSA41.vornachteil.kategorie.nachteil", }, initial: "vorteil", }), kosten: new NumberField({ integer: true, initial: 0 }), beschreibung: new StringField({ initial: "" }), }; } } class DSA41_ZauberData extends TypeDataModel { static defineSchema() { return { attribute1: new AttributeChoiceField(), attribute2: new AttributeChoiceField(), attribute3: new AttributeChoiceField(), zauberdauer: new StringField(), wirkungsdauer: new StringField(), kosten: new StringField(), komplexitaet: new SteigerungsKategorieField(), repraesentation: new StringField({ required: true, choices: { "borbaradianisch": "DSA41.zauber.repraesentation.borbaradianisch", "druidisch": "DSA41.zauber.repraesentation.druidisch", "elfisch": "DSA41.zauber.repraesentation.elfisch", "geodisch": "DSA41.zauber.repraesentation.geodisch", "satuarisch": "DSA41.zauber.repraesentation.satuarisch", "kristallomantisch": "DSA41.zauber.repraesentation.kristallomantisch", "gildenmagisch": "DSA41.zauber.repraesentation.gildenmagisch", "scharlatanisch": "DSA41.zauber.repraesentation.scharlatanisch", "schelmisch": "DSA41.zauber.repraesentation.schelmisch", }, initial: "borbaradianisch", }), merkmale: new SetField(new StringField({ choices: { "anitmagie": "DSA41.zauber.merkmale.anitmagie", "beschwoerung": "DSA41.zauber.merkmale.beschwoerung", "daemonisch_allgemein": "DSA41.zauber.merkmale.daemonisch_allgemein", "daemonisch_agrimoth_widharcal": "DSA41.zauber.merkmale.daemonisch_agrimoth_widharcal", "daemonisch_amazeroth_iribaar": "DSA41.zauber.merkmale.daemonisch_amazeroth_iribaar", "daemonisch_asfaloth_calijnaar": "DSA41.zauber.merkmale.daemonisch_asfaloth_calijnaar", "daemonisch_belhalhar_xarfai": "DSA41.zauber.merkmale.daemonisch_belhalhar_xarfai", "daemonisch_blakharaz_tyakraman": "DSA41.zauber.merkmale.daemonisch_blakharaz_tyakraman", "daemonisch_lolgramoth_thezzphai": "DSA41.zauber.merkmale.daemonisch_lolgramoth_thezzphai", "daemonisch_belzhorash_mishkara": "DSA41.zauber.merkmale.daemonisch_belzhorash_mishkara", "daemonisch_thargunitoth_tijakool": "DSA41.zauber.merkmale.daemonisch_thargunitoth_tijakool", "eigenschaften": "DSA41.zauber.merkmale.eigenschaften", "einfluss": "DSA41.zauber.merkmale.einfluss", "elementar_allgemein": "DSA41.zauber.merkmale.elementar_allgemein", "elementar_eis": "DSA41.zauber.merkmale.elementar_eis", "elementar_erz": "DSA41.zauber.merkmale.elementar_erz", "elementar_feuer": "DSA41.zauber.merkmale.elementar_feuer", "elementar_humus": "DSA41.zauber.merkmale.elementar_humus", "elementar_luft": "DSA41.zauber.merkmale.elementar_luft", "elementar_wasser": "DSA41.zauber.merkmale.elementar_wasser", "form": "DSA41.zauber.merkmale.form", "geisterwesen": "DSA41.zauber.merkmale.geisterwesen", "heilung": "DSA41.zauber.merkmale.heilung", "hellsicht": "DSA41.zauber.merkmale.hellsicht", "herbeirufung": "DSA41.zauber.merkmale.herbeirufung", "herrschaft": "DSA41.zauber.merkmale.herrschaft", "illusion": "DSA41.zauber.merkmale.illusion", "kraft": "DSA41.zauber.merkmale.kraft", "limbus": "DSA41.zauber.merkmale.limbus", "metamagie": "DSA41.zauber.merkmale.metamagie", "objekt": "DSA41.zauber.merkmale.objekt", "schaden": "DSA41.zauber.merkmale.schaden", "telekinese": "DSA41.zauber.merkmale.telekinese", "temporal": "DSA41.zauber.merkmale.temporal", "umwelt": "DSA41.zauber.merkmale.umwelt", "verständigung": "DSA41.zauber.merkmale.verständigung", }, })), modifikationen: new SetField(new StringField({ choices: { "zauberdauer": "DSA41.zauber.modifikationen.zauberdauer", "erzwingen": "DSA41.zauber.modifikationen.erzwingen", "kosten": "DSA41.zauber.modifikationen.kosten", "mehrere_ziele": "DSA41.zauber.modifikationen.mehrere_ziele", "mehrere_ziele_freiwillig": "DSA41.zauber.modifikationen.mehrere_ziele_freiwillig", "reichweite": "DSA41.zauber.modifikationen.reichweite", "reichweite_beruehrung": "DSA41.zauber.modifikationen.reichweite_beruehrung", "reichweite_selbst": "DSA41.zauber.modifikationen.reichweite_selbst", "wirkungsdauer": "DSA41.zauber.modifikationen.wirkungsdauer", "ziel_objekt": "DSA41.zauber.modifikationen.ziel_objekt", }, })), magieresistenz: new BooleanField({ initial: false }), haus: new BooleanField({ initial: false }), zauberfertigkeitswert: new NumberField({ integer: true, initial: 0 }), beschreibung: new StringField({ initial: "" }), }; } } function DSA41_ApplicationMixin(BaseApplication) { class DSA41_Application extends HandlebarsApplicationMixin(BaseApplication) { static DEFAULT_OPTIONS= { classes: [ "DSA41" ], window: { resizable: true }, form: { submitOnChange: true }, }; async _prepareContext(options) { return this; } } return DSA41_Application; } class DSA41_Dialog extends DSA41_ApplicationMixin(ApplicationV2) { static PARTS = { Eigenschaft: { template: "systems/dsa-4th-edition/src/Dialogs/Attribute.hbs" }, Talent: { template: "systems/dsa-4th-edition/src/Dialogs/Talent.hbs" }, Zauber: { template: "systems/dsa-4th-edition/src/Dialogs/Zauber.hbs" }, Attacke: { template: "systems/dsa-4th-edition/src/Dialogs/Attacke.hbs" }, Parade: { template: "systems/dsa-4th-edition/src/Dialogs/Parade.hbs" }, Trefferpunkte: { template: "systems/dsa-4th-edition/src/Dialogs/Trefferpunkte.hbs" }, FernkampfAttacke: { template: "systems/dsa-4th-edition/src/Dialogs/FernkampfAttacke.hbs" }, FernkampfTrefferpunkte: { template: "systems/dsa-4th-edition/src/Dialogs/FernkampfTrefferpunkte.hbs" }, Trefferzone: { template: "systems/dsa-4th-edition/src/Dialogs/Trefferzone.hbs" }, footer: { template: "templates/generic/form-footer.hbs" }, }; static DEFAULT_OPTIONS = { classes: [ "DSA41", "Dialog" ], window: { title: "Dialog", minimizable: false, resizable: false }, tag: "form", form: { closeOnSubmit: false, submitOnChange: true, } }; get formData() { const inputs = this.element.querySelector("[data-application-part]")?.querySelectorAll("input") ?? []; const types = Object.fromEntries(Array.from(inputs, x => [x.name, x.type])); const data = Object.fromEntries( new FormData(this.element).entries().map(([key, value])=>{ if (types[key] == "number") return [key, Number(value)]; return [key, value]; }) ); return data; } buttons = [{ type: "submit", label: "Confirm" }]; static async wait(dialog_type, options) { return new Promise((resolve, reject) => { const dialog = new this({ dialog_type: dialog_type, form: { handler: (event, form, formData) => { if (event.type != "submit") { dialog.render({ force: false }); return; } dialog.close(); resolve(dialog.formData); } }, ...options, }); dialog.render({ force: true }); }); } _configureRenderOptions(options) { super._configureRenderOptions(options); options.parts = [ this.options.dialog_type, "footer" ]; } _onFirstRender(context, options) { this.render(options) } } class DSA41_ActorSheet extends DSA41_ApplicationMixin(ActorSheetV2) { static PARTS = { ActorSheet: { template: "systems/dsa-4th-edition/src/ActorSheet.hbs", scrollable: [".scroll-container"] }, }; static DEFAULT_OPTIONS = { classes: [ "DSA41", "ActorSheet" ], position: { width: "800", height: "650" }, actions: { "roll": async function(event, target) { var roll_formula = event.target.closest("[data-roll]")?.dataset.roll; const roll_type = event.target.closest("[data-roll-type]")?.dataset.rollType; const success_value = event.target.closest("[data-success-value]")?.dataset.successValue; const item_id = event.target.closest("[data-item-id]")?.dataset.itemId; const item = this.document.items.get(item_id); const hp_roll_modifier = this.document.get_hp_roll_modifier(); let flavor = game.i18n.localize("DSA41.roll_types." + roll_type); if (typeof success_value !== 'undefined') { flavor += " <= " + success_value; } if (roll_type == "courage" || roll_type == "cleverness" || roll_type == "intuition" || roll_type == "charisma" || roll_type == "dexterity" || roll_type == "agility" || roll_type == "constitution" || roll_type == "strength") { const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + this.document.name; const data = await DSA41_Dialog.wait("Eigenschaft", { window: { title: title }, attribute: this.document.system.attributes[roll_type] }); let flavor = game.i18n.localize("DSA41.roll_types." + roll_type); if (typeof success_value !== 'undefined') { flavor += " <= " + (Number(success_value) + data.modifikator); } if (hp_roll_modifier !== 0) roll_formula = roll_formula + " + " + hp_roll_modifier; let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); return; } if (roll_type == "talent") { const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("Talent", { window: {title: title}, item: item }); const eBE = (await new Roll(item.system.behinderung || "0", { BE: this.document.system.computed.kampf.ruestungen_gesamt.gesamt_behinderung }).evaluate()).total; data.modifikator -= eBE; const talentwert = item.system.talentwert + data.modifikator; const roll_modifier = talentwert < 0 ? -talentwert: 0; if (hp_roll_modifier !== 0) roll_modifier += hp_roll_modifier; const roll1 = (await new Roll("1d20").evaluate()).total + roll_modifier; const roll2 = (await new Roll("1d20").evaluate()).total + roll_modifier; const roll3 = (await new Roll("1d20").evaluate()).total + roll_modifier; const attribute1 = this.document.system.computed.attributes[item.system.attribute1]; const attribute2 = this.document.system.computed.attributes[item.system.attribute2]; const attribute3 = this.document.system.computed.attributes[item.system.attribute3]; const needed_taw_roll1 = Math.max(roll1 - attribute1, 0); const needed_taw_roll2 = Math.max(roll2 - attribute2, 0); const needed_taw_roll3 = Math.max(roll3 - attribute3, 0); const leftover_taw = Math.max(talentwert, 0) - needed_taw_roll1 - needed_taw_roll2 - needed_taw_roll3; const context = { talent: item, modifikator: data.modifikator, attribute1: { type: item.system.attribute1, value: attribute1 }, attribute2: { type: item.system.attribute2, value: attribute2 }, attribute3: { type: item.system.attribute3, value: attribute3 }, roll1: roll1, roll2: roll2, roll3: roll3, needed_taw_roll1: -needed_taw_roll1, needed_taw_roll2: -needed_taw_roll2, needed_taw_roll3: -needed_taw_roll3, leftover_taw: Math.min(leftover_taw, item.system.talentwert), }; const message = await ChatMessage.create( { content: await renderTemplate("talent_chat", context), speaker: { actor: this.actor }, sound: CONFIG.sounds.dice, }, ); return; } if (roll_type == "attacke") { const item = this.document.system.computed.kampf.waffen[item_id]; const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("Attacke", { window: { title: title }, item: item }); let flavor = game.i18n.localize("DSA41.roll_types." + roll_type); if (typeof success_value !== 'undefined') { flavor += " <= " + (Number(success_value) + data.modifikator); } if (data.anmerkung) { flavor += "
" + data.anmerkung; } if (hp_roll_modifier !== 0) roll_formula = roll_formula + " + " + hp_roll_modifier; let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); return; } if (roll_type == "parade") { const item = this.document.system.computed.kampf.waffen[item_id]; const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("Parade", { window: { title: title }, item: item }); let flavor = game.i18n.localize("DSA41.roll_types." + roll_type); flavor += " <= " + (Number((data.crit == "on") ? item.parade_crit : item.parade) + data.modifikator); if (data.anmerkung) { flavor += "
" + data.anmerkung; } if (hp_roll_modifier !== 0) roll_formula = roll_formula + " + " + hp_roll_modifier; let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); return; } if (roll_type == "trefferpunkte") { const item = this.document.system.computed.kampf.waffen[item_id]; const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("Trefferpunkte", { window: { title: title }, item: item }); if (data.crit == "on") { roll_formula = "2 * (" + item.item.system.nahkampfwaffe.basis + ")" + " + " + item.tp_kk; } let roll = new Roll(roll_formula + " + " + data.modifikator, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); return; } if (roll_type == "fernkampf-attacke") { const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("FernkampfAttacke", { window: { title: title }, item: this.document.system.computed.kampf.fernkampf_waffen[item_id] }); const groessen_modifikator = Math.max(data.ziel_groesse + data.deckung + data.ziel_bewegung, -2); const andere_modifikator = data.entfernung + data.wind + data.modifikator; const total_modifikator = groessen_modifikator + andere_modifikator + hp_roll_modifier; let roll = new Roll(roll_formula + " + " + total_modifikator, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: game.i18n.localize("DSA41.roll_types." + roll_type), flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); return; } if (roll_type == "fernkampf-trefferpunkte") { const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("FernkampfTrefferpunkte", { window: { title: title }, item: this.document.system.computed.kampf.fernkampf_waffen[item_id] }); const entfernung = item.system.fernkampfwaffe[data.entfernung]; if (data.crit == "on") { roll_formula = "2 * (" + roll_formula + ")"; } let roll = new Roll(roll_formula + " + " + (entfernung + data.modifikator), this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: game.i18n.localize("DSA41.roll_types." + roll_type), flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); return; } if (roll_type == "zauber") { const title = game.i18n.localize("DSA41.roll_types." + roll_type) + ": " + item.name; const data = await DSA41_Dialog.wait("Zauber", { window: {title: title}, item: item }); const zauberfertigkeitswert = item.system.zauberfertigkeitswert - data.modifikator - (data.magieresistenz ?? null); const roll_modifier = zauberfertigkeitswert < 0 ? -zauberfertigkeitswert: 0; if (hp_roll_modifier !== 0) roll_modifier += hp_roll_modifier; const roll1 = (await new Roll("1d20").evaluate()).total + roll_modifier; const roll2 = (await new Roll("1d20").evaluate()).total + roll_modifier; const roll3 = (await new Roll("1d20").evaluate()).total + roll_modifier; const attribute1 = this.document.system.computed.attributes[item.system.attribute1]; const attribute2 = this.document.system.computed.attributes[item.system.attribute2]; const attribute3 = this.document.system.computed.attributes[item.system.attribute3]; const needed_zfw_roll1 = Math.max(roll1 - attribute1, 0); const needed_zfw_roll2 = Math.max(roll2 - attribute2, 0); const needed_zfw_roll3 = Math.max(roll3 - attribute3, 0); const leftover_zfw = Math.max(zauberfertigkeitswert, 0) - needed_zfw_roll1 - needed_zfw_roll2 - needed_zfw_roll3; const context = { zauber: item, modifikator: -data.modifikator, magieresistenz: -(data.magieresistenz ?? 0), attribute1: { type: item.system.attribute1, value: attribute1 }, attribute2: { type: item.system.attribute2, value: attribute2 }, attribute3: { type: item.system.attribute3, value: attribute3 }, roll1: roll1, roll2: roll2, roll3: roll3, needed_zfw_roll1: -needed_zfw_roll1, needed_zfw_roll2: -needed_zfw_roll2, needed_zfw_roll3: -needed_zfw_roll3, leftover_zfw: Math.min(leftover_zfw, item.system.zauberfertigkeitswert), }; const message = await ChatMessage.create( { content: await renderTemplate("zauber_chat", context), speaker: { actor: this.actor }, sound: CONFIG.sounds.dice, }, ); return; } let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, flags: { dsa41: { type: roll_type, targets: get_targeted_actors(), } }, }); }, "item-open": async function(event, target) { const item_id = event.target.closest("[data-item-id]").dataset.itemId; const item = this.document.items.get(item_id); item.sheet.render(true) }, "item-delete": async function(event, target) { const item_id = event.target.closest("[data-item-id]").dataset.itemId; const item = this.document.items.get(item_id); item.delete(); }, "toggle_equipped": async function(event, target) { const item_id = event.target.closest("[data-item-id]").dataset.itemId; const item = this.document.items.get(item_id); item.update({ "system.angelegt": !item.system.angelegt }); }, "post-zauber": async function(event, target) { const item_id = event.target.closest("[data-item-id]").dataset.itemId; const item = this.document.items.get(item_id); ChatMessage.create({ speaker: ChatMessage.getSpeaker({ actor: this.document }), author: game.user.id, sound: CONFIG.sounds.dice, content: item.system.beschreibung, }); }, "toggle-wound": async function(event, target) { const location = event.target.closest("[data-location]").dataset.location; const current_count = this.document.system.wunden[location]; const toggled_count = Array.prototype.indexOf.call(event.target.parentNode.children, event.target) + 1; this.document.update({ ["system.wunden." + location]: toggled_count > current_count ? toggled_count : toggled_count - 1 }); } }, }; // allow changing embedded item fields async _onChangeForm(formConfig, event) { const item_id = event.target.closest("[data-item-id]")?.dataset.itemId; const data_name = event.target.dataset.name; if (!item_id || !data_name) return super._onChangeForm(formConfig, event); event.stopImmediatePropagation(); const item = await this.actor.items.get(item_id); const value = event.target.value; item.update({ [data_name]: value }); } tabGroups = { primary: "eigenschaften" }; } class DSA41_ItemSheetV2 extends DSA41_ApplicationMixin(ItemSheetV2) { static PARTS = { Bewaffnung: { template: "systems/dsa-4th-edition/src/ItemSheets/Bewaffnung.hbs" }, Gegenstand: { template: "systems/dsa-4th-edition/src/ItemSheets/Gegenstand.hbs" }, Ruestung: { template: "systems/dsa-4th-edition/src/ItemSheets/Ruestung.hbs" }, Talent: { template: "systems/dsa-4th-edition/src/ItemSheets/Talent.hbs" }, Kampftalent: { template: "systems/dsa-4th-edition/src/ItemSheets/Kampftalent.hbs" }, Sonderfertigkeit: { template: "systems/dsa-4th-edition/src/ItemSheets/Sonderfertigkeit.hbs" }, VorNachteil: { template: "systems/dsa-4th-edition/src/ItemSheets/VorNachteil.hbs" }, Zauber: { template: "systems/dsa-4th-edition/src/ItemSheets/Zauber.hbs" }, }; static DEFAULT_OPTIONS = { position: { width: "600", height: "auto" }, }; tabGroups = { primary: "tab1" }; _configureRenderOptions(options) { super._configureRenderOptions(options); options.parts = [ this.options.document.type ]; } }