import { M5Character } from "../actors/M5Character"; import M5ModAggregate from "../actors/M5ModAggregate"; import { enumKeys, M5Attributes, M5ModOperation, M5ModPair, M5ModType, M5RollData, M5RollResult, M5Stats } from "../M5Base"; import { M5Roll } from "../rolls/M5Roll"; export class M5Item extends Item { static readonly SKILL = "skill"; prepareDerivedData() { const itemId: string = (this as any).id; const itemType: string = (this as any).type; const actor = this.actor as any; const character = actor as M5Character; const itemData = (this as any).system; const calc = itemData.calc; const name = (this as any).name; if (itemType === "item") { calc.containers = null; if (actor) { const actorCalc = actor.derivedData({ containers: false, items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true }); if (actorCalc) { calc.containers = actorCalc.gear.containers; } const container = character.getItem(itemData.containerId); //console.log("M5Item.prepareDerivedData:containers", itemData, container?.system) if (container) { container.prepareDerivedData(); const containerData = container.system; } } } else if (itemType === "skill") { calc.fw = itemData.fw; calc.bonus = 0; let pairs: Array = [ { source: (this as any).name, mod: { type: M5ModType.SKILL, id: itemId, operation: M5ModOperation.SET, value: itemData.fw, }, }, ]; // Adjust attribute Aussehen based on Athletik skill if (name === "Athletik") { itemData.mods[0] = { type: "attribute", id: "au", operation: "add100", value: Math.floor(calc.fw / 3) }; } // Adjust stat Bewegungsweite based on Laufen skill if (name === "Laufen") { itemData.mods[0] = { type: "stat", id: "movement", operation: "add", value: Math.floor(calc.fw / 3) }; } // Adjust stat Kälte based on Überleben (Gebirge) skill if (name === "Überleben (Gebirge)") { itemData.mods[0] = { type: "stat", id: "deprivationCold", operation: "add", value: Math.floor(calc.fw * 5) }; } // // Adjust stat Kälte based on Überleben (Steppe) skill if (name === "Überleben (Steppe)") { itemData.mods[0] = { type: "stat", id: "deprivationHeat", operation: "add", value: Math.floor(calc.fw * 5) }; } // // Adjust stat Durst & Hunger based on Robustheit skill if (name === "Robustheit") { itemData.mods[0] = { type: "stat", id: "deprivationFood", operation: "add", value: Math.floor(calc.fw * 5) }; } if (character) { const actorCalc = character.derivedData({ containers: true, skills: true, items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true, }); if (actorCalc?.skillMods && Object.keys(actorCalc.skillMods).indexOf(itemId) !== -1) { pairs = pairs.concat(actorCalc.skillMods[itemId]); } if (itemData?.attribute && itemData.attribute !== "") { pairs.push({ source: (this as any).name, mod: { type: M5ModType.SKILL, id: itemId, operation: M5ModOperation.ADD, value: actorCalc.attributes[itemData.attribute].bonus, }, }); } } const res = M5ModAggregate.processPairs(pairs); res.mods.forEach((mod) => { if ([M5ModOperation.SET, M5ModOperation.FIXED].includes(mod.operation)) calc.fw = mod.value; else if ([M5ModOperation.SUBTRACT].includes(mod.operation)) calc.bonus -= mod.value; else if ([M5ModOperation.ADD].includes(mod.operation)) calc.bonus += mod.value; }); calc.ew = calc.fw + calc.bonus; calc.sources = res.mods; } else if (itemType === "weapon") { calc.fw = 0; calc.bonus = 0; calc.special = itemData.special ? 2 : 0; calc.ew = calc.special + itemData.stats.attackBonus; calc.combatSkills = null; calc.containers = null; if (actor) { const actorCalc = character.derivedData({ items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true }); if (actorCalc) { calc.ew += actorCalc.stats.attackBonus.value; calc.combatSkills = actorCalc.skills.combat; calc.containers = actorCalc.gear.containers; } const container = character.getItem(itemData.containerId); //console.log("M5Item.prepareDerivedData:containers", itemData, container?.system) if (container) { container.prepareDerivedData(); const containerData = container.system; } const skill = character.getItem(itemData.skillId); //console.log("M5Item.prepareDerivedData:weapon", itemData, skill?.system) if (skill) { skill.prepareDerivedData(); const skillData = skill.system; calc.ew += skillData.calc.ew; calc.bonus += skillData.calc.bonus; calc.fw += skillData.fw; } else { calc.ew += 4; calc.fw += 4; } } } else if (itemType === "defensiveWeapon") { calc.fw = 0; calc.bonus = 0; calc.special = itemData.special ? 2 : 0; calc.ew = calc.special + itemData.stats.defenseBonus; calc.combatSkills = null; calc.containers = null; if (actor) { const actorCalc = character.derivedData({ items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true }); if (actorCalc) { calc.ew += actorCalc.stats.defense.value + actorCalc.stats.defenseBonus.value; calc.combatSkills = actorCalc.skills.combat; calc.containers = actorCalc.gear.containers; } const container = character.getItem(itemData.containerId); //console.log("M5Item.prepareDerivedData:containers", itemData, container?.system) if (container) { container.prepareDerivedData(); const containerData = container.system; } const skill = character.getItem(itemData.skillId); //console.log("M5Item.prepareDerivedData:weapon", itemData, skill?.system) if (skill) { skill.prepareDerivedData(); const skillData = skill.system; calc.ew += skillData.calc.ew; calc.bonus += skillData.calc.bonus; calc.fw += skillData.fw; } } } else if (itemType === "armor") { itemData.mods[0] = { type: "stat", id: "defenseBonus", operation: "add", value: itemData.stats.defenseBonus }; itemData.mods[1] = { type: "stat", id: "attackBonus", operation: "add", value: itemData.stats.attackBonus }; itemData.mods[2] = { type: "stat", id: "movement", operation: "add", value: itemData.stats.movementBonus }; itemData.mods[3] = { type: "attribute", id: "gw", operation: "add100", value: itemData.attributeMod.gw }; itemData.mods[4] = { type: "stat", id: "lpProtection", operation: "set", value: itemData.lpProtection }; itemData.mods[5] = { type: "stat", id: "apProtection", operation: "set", value: itemData.apProtection }; calc.containers = null; if (actor) { const actorCalc = actor.derivedData({ items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true }); if (actorCalc) { calc.containers = actorCalc.gear.containers; } const container = character.getItem(itemData.containerId); //console.log("M5Item.prepareDerivedData:containers", itemData, container?.system) if (container) { container.prepareDerivedData(); const containerData = container.system; } } } else if (itemType === "spell") { calc.fw = 0; if (actor) { const actorCalc = character.derivedData({ containers: true, items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true }); if (actorCalc) { calc.ew = actorCalc.stats.spellCasting.value; } } } else if (itemType === "kampfkunst") { calc.fw = 0; calc.bonus = 0; calc.ew = 0; calc.generalSkills = null; if (actor) { const actorCalc = character.derivedData({ containers: true, items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, class: true }); if (actorCalc) { calc.generalSkills = actorCalc.skills.general; } const skill = character.getItem(itemData.skillId); if (skill) { skill.prepareDerivedData(); const skillData = skill.system; calc.ew = skillData.calc.ew; calc.fw = skillData.fw + calc.bonus; itemData.rolls.formulas[0].label = skill.name; } } } else if (itemType === "class") { itemData.mods[0] = { type: "stat", id: "resistanceBody", operation: "add", value: itemData.resistanceBody }; itemData.mods[1] = { type: "stat", id: "resistanceMind", operation: "add", value: itemData.resistanceMind }; } if (itemData?.mods) { calc.mods = {}; for (let modKey in itemData.mods) { if (itemData.mods[modKey].type === M5ModType.SKILL && itemData.mods[modKey].id?.includes("midgard5")) { itemData.mods[modKey].id = actor?.items.find((x) => x.name === game["i18n"].localize(itemData.mods[modKey].id))?.id; } } Object.keys(itemData?.mods).forEach((key) => { const mod = itemData.mods[key]; const modCalc = {}; switch (mod.type) { case M5ModType.ATTRIBUTE: { for (const key of enumKeys(M5Attributes)) { const val: string = M5Attributes[key]; modCalc[val] = (game as Game).i18n.localize(`midgard5.actor-${val}-long`); } break; } case M5ModType.STAT: { for (const key of enumKeys(M5Stats)) { const val: string = M5Stats[key]; modCalc[val] = (game as Game).i18n.localize(`midgard5.mod-stat-${val}`); } break; } case M5ModType.SKILL: { if (character) { const actorCalc = character.derivedData({ items: true, spells: true, effects: true, kampfkuenste: true, encumbrance: true, }); if (actorCalc) { let category = (game as Game).i18n.localize("midgard5.skill"); Object.keys(actorCalc.skills.general).forEach((skillId) => { const skill = character.getItem(skillId); if (skill) modCalc[skillId] = `${category}: ${skill.name}`; }); category = (game as Game).i18n.localize("midgard5.language"); Object.keys(actorCalc.skills.language).forEach((skillId) => { const skill = character.getItem(skillId); if (skill) modCalc[skillId] = `${category}: ${skill.name}`; }); category = (game as Game).i18n.localize("midgard5.weapon-skill"); Object.keys(actorCalc.skills.combat).forEach((skillId) => { const skill = character.getItem(skillId); if (skill) modCalc[skillId] = `${category}: ${skill.name}`; }); category = (game as Game).i18n.localize("midgard5.innate-ability"); Object.keys(actorCalc.skills.innate).forEach((skillId) => { const skill = character.getItem(skillId); if (skill) modCalc[skillId] = `${category}: ${skill.name}`; }); } } break; } } calc.mods[key] = modCalc; }); } } getRollData() { const actor = this.actor as any; const item = this as any; let ret: M5RollData = actor?.getRollData() ?? { c: null, i: null, iType: null, rolls: {}, res: {}, }; ret.i = item.system; ret.iType = item.type; return ret; } async roll(toggleAutomatedRoll = false) { const item = this as any; // Initialize chat data. const speaker = ChatMessage.getSpeaker({ actor: this.actor }); const rollMode = (game as Game).settings.get("core", "rollMode"); const label = `[${item.type}] ${item.name}`; // If there's no roll data, send a chat message. const formulaNames = item.system.rolls?.formulas ? Object.keys(item.system.rolls.formulas) : []; if (formulaNames.length > 0) { const rollData = this.getRollData(); formulaNames.forEach((formulaName) => { const formula = item.system.rolls.formulas[formulaName]; if (formula) { rollData.rolls[formulaName] = { formula: formula.formula, label: formula.label, enabled: formula.enabled, result: "", total: 0, totalStr: "", dice: {}, } as M5RollResult; } }); const roll = new M5Roll(rollData, this.actor, item.name, item.id); if (await roll.toMessage(toggleAutomatedRoll)) { if (item.type === "spell" || item.type === "kampfkunst") { if (this.actor["system"].ap.value >= item.system.ap) { this.actor["update"]({ data: { ap: { value: this.actor["system"].ap.value - item.system.ap, }, }, }); } } } } else { ChatMessage.create({ speaker: speaker, rollMode: rollMode, flavor: label, content: item.system.description ?? "", }); return null; } } getItem(itemId: string): any { return (this as any).getEmbeddedDocument("Item", itemId); } // migrateSystemData(): any { // const item = (this as any) // const data = item.system // if (item.type === "spell") { // if (typeof data.ap !== "string") { // data.ap = Number.isFinite(data.ap) ? "" + data.ap : "" // } // } // return super.migrateSystemData() // } }