foundry-dsa41-game/src/module/sheets/characterSheet.mjs

412 lines
16 KiB
JavaScript

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