const { TypeDataModel } = foundry.abstract; const { SchemaField, NumberField, StringField, BooleanField } = foundry.data.fields; const { ActorSheetV2, ItemSheetV2 } = foundry.applications.sheets; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { OperatorTerm, NumericTerm } = foundry.dice.terms; Hooks.once("i18nInit", async function() { game.i18n._fallback = foundry.utils.mergeObject(await game.i18n._getTranslations("de"), game.i18n._fallback); }); Hooks.once("init", async function() { CONFIG.Combat.initiative.formula = "1d6 + @computed.ini_basiswert[INI-Basiswert]"; CONFIG.Actor.dataModels.Player = DSA41_CharacterData; CONFIG.Actor.documentClass = DSA41_CharacterDocument; 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; DocumentSheetConfig.unregisterSheet(Actor, "core", ActorSheet); DocumentSheetConfig.registerSheet(Actor, "dsa41", DSA41_ActorSheet, { makeDefault: true, types: [ "Player", ] }); DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet); DocumentSheetConfig.registerSheet(Item, "dsa41", DSA41_ItemSheetV2, { makeDefault: true, types: [ "Gegenstand", "Ruestung", "Bewaffnung", "Talent", "Kampftalent", "Sonderfertigkeit", "VorNachteil", ] }); 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; } }); 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", "talent_chat": "systems/dsa-4th-edition/src/Chat/Talent.hbs", }); }); 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 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 DSA41_CharacterDocument extends Actor { static async create(data, operation) { const actor = await super.create(data, operation); if (data.type === "Player") { 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; } } class DSA41_CharacterData extends TypeDataModel { static defineSchema() { return { race: new StringField(), culture: new StringField(), profession: new StringField(), sozialstatus: new NumberField({ integer: true, inital: 1 }), 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}), }), 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}), } } async prepareDerivedData() { super.prepareDerivedData(); this.computed = { attributes: {}, attributes_without_modifiers: {}, 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, }, }, }; for (const [attribute, values] of Object.entries(this.attributes)) { this.computed.attributes[attribute] = values.initial + values.advancement + values.modifier; this.computed.attributes_without_modifiers[attribute] = values.initial + values.advancement; } 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); this.computed.initiative.wert = this.computed.initiative.basiswert + this.modifikator_initiative; this.computed.attacke.wert = this.computed.attacke.basiswert + this.modifikator_attacke; this.computed.parade.wert = this.computed.parade.basiswert + this.modifikator_parade; this.computed.fernkampf.wert = this.computed.fernkampf.basiswert + this.modifikator_fernkampf; const equipped_ruestungen = this.parent.items.filter((x) => x.type === "Ruestung"); 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.kampftalente = this.parent.items.filter((x) => x.type === "Kampftalent").sort((a, b) => a.name > b.name); for(const talent of this.kampftalente) { this.computed.kampf.talente[talent.name] = {}; this.computed.kampf.talente[talent.name].attacke = 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"); 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) { let computed = this.computed.kampf.waffen[item._id] = { item: item, attacke: 0, parade: 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.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) { 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) => 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"), }; } } class DSA41_GegenstandData extends TypeDataModel { static defineSchema() { return { gewicht: new NumberField({ integer: false, initial: 0, min: 0 }), preis: new NumberField({ integer: true, initial: 0, min: 0 }), }; } } class DSA41_RuestungData extends TypeDataModel { static defineSchema() { return { gewicht: new NumberField({ integer: false, initial: 0, min: 0 }), preis: new NumberField({ integer: true, initial: 0, min: 0 }), 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 }), }; } } class DSA41_BewaffnungData extends TypeDataModel { static defineSchema() { return { gewicht: new NumberField({ integer: false, initial: 0, min: 0 }), preis: new NumberField({ integer: true, initial: 0, min: 0 }), angelegt: new BooleanField(), nahkampfwaffe: new SchemaField({ aktiv: new BooleanField(), basis: new StringField(), 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(), kampftalente: new StringField(), laenge: new NumberField({ integer: true, initial: 0, min: 0 }), zweihaendig: new BooleanField(), werfbar: new BooleanField(), improvisiert: new BooleanField(), priviligiert: new BooleanField(), }), parierwaffe: new SchemaField({ aktiv: new BooleanField(), 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(), groesse: new StringField(), 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(), 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(), behinderung: new StringField(), attribute1: new StringField(), attribute2: new StringField(), attribute3: new StringField(), talentwert: new NumberField({ integer: true, initial: 0 }), }; } } class DSA41_KampftalentData extends TypeDataModel { static defineSchema() { return { kategorie: new StringField(), behinderung: new StringField(), steigern: new StringField(), beschreibung: new StringField(), 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(), kosten: new NumberField({ integer: true, initial: 0 }), verbreitung: new NumberField({ integer: true, initial: 0 }), beschreibung: new StringField(), }; } } class DSA41_VorNachteilData extends TypeDataModel { static defineSchema() { return { kategorie: new StringField(), kosten: new NumberField({ integer: true, initial: 0 }), beschreibung: new StringField(), }; } } 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" }, 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" }, 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 data = Object.fromEntries( new FormData(this.element).entries().map(([key, value])=>{ if (typeof value != "string") return [key, value]; return isNaN(value) ? [key, value] : [key, Number(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" }, }; static DEFAULT_OPTIONS = { 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); 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); } let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, }); 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 talentwert = item.system.talentwert + data.modifikator; const roll1 = (await new Roll("1d20").evaluate()).total; const roll2 = (await new Roll("1d20").evaluate()).total; const roll3 = (await new Roll("1d20").evaluate()).total; 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 = talentwert - 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: attribute3 }, 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: leftover_taw, }; 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); } let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, }); 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); if (typeof success_value !== 'undefined') { flavor += " <= " + (Number(success_value) + data.modifikator); } if (data.crit == "on") { roll_formula = "round((" + roll_formula + ") / 2)"; } let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, }); 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, }); 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; 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), }); 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), }); return; } let roll = new Roll(roll_formula, this.document.system); roll.toMessage({ speaker: ChatMessage.getSpeaker({ actor: this.document }), flavor: flavor, }); }, "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(); }, }, }; _onFirstRender(context, options) { super._onFirstRender(context, options); const content = this.element.querySelector(".window-content"); content.addEventListener("drop", async (event) => { event.preventDefault(); const data = TextEditor.getDragEventData(event); switch (data.type) { case "Item": { const item = await Item.implementation.fromDropData(data); await this.actor.createEmbeddedDocuments("Item", [item.toObject()]); } break; } }); } // 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: "tab1" }; } 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" }, }; static DEFAULT_OPTIONS = { position: { width: "600", height: "auto" }, }; tabGroups = { primary: "tab1" }; _configureRenderOptions(options) { super._configureRenderOptions(options); options.parts = [ this.options.document.type ]; } }