import Advsf from "./character/advsf.mjs" import Combat from "./character/combat.mjs" import Effects from "./character/effects.mjs" import Equipment from "./character/equipment.mjs" import Liturgies from "./character/liturgies.mjs" import Meta from "./character/meta.mjs" import Skills from "./character/skills.mjs" import Social from "./character/social.mjs"; import Spells from "./character/spells.mjs" const {HandlebarsApplicationMixin} = foundry.applications.api const {ActorSheetV2} = foundry.applications.sheets const {ContextMenu} = foundry.applications.ux class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) { /** @inheritDoc */ static DEFAULT_OPTIONS = { position: {width: 1100, height: 640}, classes: ['dsa41', 'sheet', 'actor', 'character'], tag: 'form', form: { submitOnChange: true, closeOnSubmit: false, handler: CharacterSheet.#onSubmitForm }, window: { resizable: true, }, actions: { rollCombatSkill: CharacterSheet.#rollCombatSkill, rollSkill: CharacterSheet.#rollSkill, roll: CharacterSheet.#dieRoll, editImage: ActorSheetV2.DEFAULT_OPTIONS.actions.editImage, openEmbeddedDocument: CharacterSheet.#openEmbeddedDocument, } } static TABS = { sheet: { tabs: [], initial: 'meta' } } /** @inheritDoc */ static PARTS = { form: { template: `systems/DSA_4-1/templates/actor/character/main-sheet.hbs` }, meta: { template: Meta.template }, social: { template: Social.template }, advsf: { template: Advsf.template }, combat: { template: Combat.template }, equipment: { template: Equipment.template }, skills: { template: Skills.template }, spells: { template: Spells.template }, liturgies: { template: Liturgies.template }, effects: { template: Effects.template } } /** * * @param {PointerEvent} event */ static #rollSkill(event) { const {id} = event.target.dataset const skill = this.document.items.get(id) if (skill?.system?.roll) { skill.system.roll("publicroll") } } /** * * @param {PointerEvent} event */ static #rollCombatSkill(event) { const {id} = event.target.dataset const skill = this.document.items.get(id) if (skill?.system?.roll) { skill.system.roll("publicroll", event.shiftKey ? "PARRY" : "ATTACK") } } static #dieRoll(event) { event.preventDefault() const dataset = event.currentTarget.dataset if (dataset.roll) { let label = dataset.label ? `[Attribut] ${dataset.label}` : '' let roll = new Roll(dataset.roll, this.actor.getRollData()) roll.toMessage({ speaker: ChatMessage.getSpeaker({actor: this.actor}), flavor: label, rollMode: game.settings.get('core', 'rollMode'), }); return roll } } /** * * @param {MouseEvent} event */ static #openEmbeddedDocument(event) { const dataset = event.target.parentElement.dataset const id = dataset.itemId ?? dataset.id this.document.items.get(id).sheet.render(true) } /** * Handle form submission * @this {AdvantageSheet} * @param {SubmitEvent} event * @param {HTMLFormElement} form * @param {FormDataExtended} formData */ static async #onSubmitForm(event, form, formData) { event.preventDefault() await this.document.update(formData.object) // Note: formData.object } _getTabsConfig(group) { const tabs = foundry.utils.deepClone(super._getTabsConfig(group)) Meta._getTabConfig(tabs, this); Social._getTabConfig(tabs, this); Advsf._getTabConfig(tabs, this) Combat._getTabConfig(tabs, this) Equipment._getTabConfig(tabs, this) Skills._getTabConfig(tabs, this) Spells._getTabConfig(tabs, this) Liturgies._getTabConfig(tabs, this) Effects._getTabConfig(tabs, this) return tabs } async _preparePartContext(partId, context) { switch (partId) { case "form": const actorData = context.document context.system = actorData.system context.isOwner = actorData.isOwner context.flags = actorData.flags context.derived = context.document.system context.professions = actorData.items.filter(p => p.type === 'Profession').map(p => { // is tarnidentitaet revealed? if (p.system.revealed) { return { id: p.id, name: p.name, alias: p.system.alias, } } else { return { id: p.id, name: p.system.alias ?? p.name, alias: p.name, } } }) context.originalName = actorData.name context.name = context.derived.name ?? actorData.name context.img = actorData.img context.effects = actorData.effects ?? [] context.maxWounds = actorData.system.wunden.max ?? 3 context.wounds = actorData.system.wunden.aktuell ?? 0 context.woundsFilled = [] for (let i = 1; i <= context.maxWounds; i++) { context.woundsFilled[i] = i <= context.wounds } context.zonenruestung = game.settings.get("DSA_4-1", "optional_ruestungzonen") context.trefferzonen = game.settings.get("DSA_4-1", "optional_trefferzonen") context.ausdauer = game.settings.get("DSA_4-1", "optional_ausdauer") context.colorfulDice = game.settings.get('DSA_4-1', 'optional_colorfuldice') context.inidice = actorData.system.ini.wuerfel context.inivalue = actorData.system.ini.aktuell context.inimod = actorData.system.ini.mod context.aupper = Math.min((actorData.system.aup.aktuell / actorData.system.aup.max) * 100, 100) context.lepper = Math.min((actorData.system.lep.aktuell / actorData.system.lep.max) * 100, 100) context.keper = Math.min((actorData.system.kap.aktuell / actorData.system.kap.max) * 100, 100) context.aspper = Math.min((actorData.system.asp.aktuell / actorData.system.asp.max) * 100, 100) context.lepcurrent = actorData.system.lep.aktuell ?? 0 context.aupcurrent = actorData.system.aup.aktuell ?? 0 const fernkampf = actorData.findEquipmentOnSlot("fernkampf", actorData.system.setEquipped, actorData) const links = actorData.findEquipmentOnSlot("links", actorData.system.setEquipped, actorData) const rechts = actorData.findEquipmentOnSlot("rechts", actorData.system.setEquipped, actorData) context.attacks = []; if (fernkampf) { const fkitems = fernkampf.system.rangedSkills.map((skillInQuestion) => actorData.items.find(p => p.name === skillInQuestion)) fkitems.forEach(async skill => { const obj = await skill context.attacks.push({ name: obj.name, using: fernkampf.name, atroll: `1d20cs<${this.document.system.fk.aktuell + obj.system.at}`, at: `${this.document.system.fk.aktuell + obj.system.at}`, tproll: `${fernkampf.system.rangedAttackDamage}`, // TODO consider adding TP/KK mod and Range mod tp: `${fernkampf.system.rangedAttackDamage}`, iniroll: `(${context.inidice})d6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`, ini: `${context.inidice}w6 + ${context.inivalue + fernkampf.system.iniModifier ?? 0}`, }) }) } if (links) { const meitems = [] links.system.meleeSkills.forEach((skillInQuestion) => { const item = actorData.items.find(p => p.name === skillInQuestion) if (item) { meitems.push(item) } }) meitems.forEach(skill => { const obj = skill context.attacks.push({ name: obj.name, using: links.name, atroll: `1d20cs<${this.document.system.at.links.aktuell + obj.system.at + links.system.attackModifier}`, // TODO consider adding W/M at: `${this.document.system.at.links.aktuell + obj.system.at + links.system.attackModifier}`, paroll: `1d20cs<${this.document.system.pa.links.aktuell + obj.system.pa + links.system.parryModifier}`, // TODO consider adding W/M pa: `${this.document.system.pa.links.aktuell + obj.system.pa + links.system.parryModifier}`, tproll: `${links.system.meleeAttackDamage}`, // TODO consider adding TP/KK mod tp: `${links.system.meleeAttackDamage}`, iniroll: `(${context.inidice})d6 + ${context.inivalue + links.system.iniModifier ?? 0}`, ini: `${context.inidice}w6 + ${context.inivalue + links.system.iniModifier ?? 0}`, }) }) } if (rechts) { const meitems = [] rechts.system.meleeSkills.forEach((skillInQuestion) => { const item = actorData.items.find(p => p.name === skillInQuestion) if (item) { meitems.push(item) } }) meitems.forEach(skill => { const obj = skill context.attacks.push({ name: obj.name, using: rechts.name, atroll: `1d20cs<${this.document.system.at.rechts.aktuell + obj.system.at + rechts.system.attackModifier}`, // TODO consider adding W/M at: `${this.document.system.at.rechts.aktuell + obj.system.at + rechts.system.attackModifier}`, paroll: `1d20cs<${this.document.system.pa.rechts.aktuell + obj.system.pa + rechts.system.parryModifier}`, // TODO consider adding W/M pa: `${this.document.system.pa.rechts.aktuell + obj.system.pa + rechts.system.parryModifier}`, tproll: `${rechts.system.meleeAttackDamage}`, // TODO consider adding TP/KK mod tp: `${rechts.system.meleeAttackDamage}`, iniroll: `(${context.inidice})d6 + ${context.inivalue + rechts.system.iniModifier ?? 0}`, ini: `${context.inidice}w6 + ${context.inivalue + rechts.system.iniModifier ?? 0}`, }) }) } context.attributes = [ { eigenschaft: "mu", name: "MU", tooltip: "Mut", wert: context.derived.attribute.mu.aktuell ?? 0, }, { eigenschaft: "kl", name: "KL", tooltip: "Klugheit", wert: context.derived.attribute.kl.aktuell ?? 0, }, { eigenschaft: "in", name: "IN", tooltip: "Intuition", wert: context.derived.attribute.in.aktuell ?? 0, }, { eigenschaft: "ch", name: "CH", tooltip: "Charisma", wert: context.derived.attribute.ch.aktuell ?? 0, }, { eigenschaft: "ff", name: "FF", tooltip: "Fingerfertigkeit", wert: context.derived.attribute.ff.aktuell ?? 0, }, { eigenschaft: "ge", name: "GE", tooltip: "Geschicklichkeit", wert: context.derived.attribute.ge.aktuell ?? 0, }, { eigenschaft: "ko", name: "KO", tooltip: "Konstitution", wert: context.derived.attribute.ko.aktuell ?? 0, }, { eigenschaft: "kk", name: "KK", tooltip: "Körperkraft", wert: context.derived.attribute.kk.aktuell ?? 0, }, ] break; case "meta": await Meta._prepareContext(context, this.document) break case "social": await Social._prepareContext(context, this.document) break case "advsf": await Advsf._prepareContext(context, this.document) break case "combat": await Combat._prepareContext(context, this.document) break case "equipment": await Equipment._prepareContext(context, this.document) break case "skills": await Skills._prepareContext(context, this.document) break case "spells": await Spells._prepareContext(context, this.document) break case "liturgies": await Liturgies._prepareContext(context, this.document) break case "effects": await Effects._prepareContext(context, this.document) break } return context } _onRender(context, options) { Meta._onRender(context, options, this.element) Social._onRender(context, options, this.element) Advsf._onRender(context, options, this) Combat._onRender(context, options, this.element) Effects._onRender(context, options, this.element) Equipment._onRender(context, options, this) Liturgies._onRender(context, options, this.element) Skills._onRender(context, options, this.element) Spells._onRender(context, options, this.element) } async _canDragDrop() { return true } // TODO needs to be fixed once Character Sheet is migrated to ActorSheetV2 async _onDrop(event) { const data = TextEditor.implementation.getDragEventData(event); const actor = this.actor; const allowed = Hooks.call("dropActorSheetData", actor, this, data); if (allowed === false) return; // Dropped Documents const documentClass = foundry.utils.getDocumentClass(data.type); if (documentClass) { const document = await documentClass.fromDropData(data); if (document.type === "Equipment" || document.type === "Advantage" || document.type === "Spell" || document.type === "Liturgy" || document.type === "ActiveEffect" || document.type === "SpecialAbility") { // No duplication by moving items from one actor to another if (document.parent && document.parent !== this.actor) { document.parent.items.get(document._id).delete() } await this._onDropDocument(event, document); } } } } export default CharacterSheet