/** * Donjon & Cie - Systeme FoundryVTT * * Donjon & Cie est un jeu de role edite par John Doe. * Ce systeme FoundryVTT est une implementation independante et n'est pas * affilie a John Doe. * * @author LeRatierBretonnien * @copyright 2025–2026 LeRatierBretonnien * @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/ */ import { DONJON_ET_CIE } from "./donjon-et-cie-config.mjs"; export class DonjonEtCieUtility { static defaultItemIcons = { arme: "systems/fvtt-donjon-et-cie/assets/icons/system/items/arme.svg", armure: "systems/fvtt-donjon-et-cie/assets/icons/system/items/armure.svg", trait: "systems/fvtt-donjon-et-cie/assets/icons/system/items/trait.svg", sortilege: "systems/fvtt-donjon-et-cie/assets/icons/system/items/sortilege.svg", equipement: "systems/fvtt-donjon-et-cie/assets/icons/system/items/equipement.svg", other: "systems/fvtt-donjon-et-cie/assets/icons/system/items/autre.svg" }; static async preloadHandlebarsTemplates() { return foundry.applications.handlebars.loadTemplates([ "systems/fvtt-donjon-et-cie/templates/actors/employe-sheet.hbs", "systems/fvtt-donjon-et-cie/templates/actors/pnj-sheet.hbs", "systems/fvtt-donjon-et-cie/templates/items/item-sheet.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/characteristic-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/initiative-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/weapon-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/damage-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/spell-roll.hbs", "systems/fvtt-donjon-et-cie/templates/dialogs/usage-roll.hbs", "systems/fvtt-donjon-et-cie/templates/chat/roll-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/spell-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/chaos-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/hit-dice-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/damage-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/damage-application-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/favor-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/initiative-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/usage-card.hbs", "systems/fvtt-donjon-et-cie/templates/chat/item-card.hbs" ]); } static getCharacteristicEntries(system) { return Object.entries(DONJON_ET_CIE.characteristics).map(([key, metadata]) => ({ key, label: metadata.label, short: metadata.short, value: system.caracteristiques?.[key]?.value ?? 0 })); } static formatUsageDie(value) { return value ? `Δ${value}` : "—"; } static getDefaultItemIcon(type) { return this.defaultItemIcons[type] ?? this.defaultItemIcons.other; } static getCurrentSceneId() { return canvas?.scene?.id ?? game.scenes?.current?.id ?? "global"; } static getSceneDamageTargets() { const scene = canvas?.scene ?? game.scenes?.current; const tokens = scene?.tokens?.contents ?? []; return tokens .map((token) => { const actor = token.actor; if (!actor || !["employe", "pnj"].includes(actor.type)) return null; const tokenName = token.name || actor.name; const actorName = actor.name || tokenName; const label = tokenName === actorName ? tokenName : `${tokenName} (${actorName})`; return { tokenId: token.id, tokenUuid: token.uuid, actorUuid: actor.uuid, label }; }) .filter(Boolean) .sort((a, b) => a.label.localeCompare(b.label, "fr", { sensitivity: "base" })); } static getMagicResourceContext(actor) { const rank = Number(actor.system.anciennete?.rang ?? actor.system.sante?.dv ?? 0); const focusDelta = Number(actor.system.magie?.focus?.delta ?? 0); const focusResult = Number(actor.system.magie?.focus?.resultat ?? 0); const focusSceneId = actor.system.magie?.focus?.sceneId ?? ""; const activeFocus = focusSceneId === this.getCurrentSceneId() ? focusResult : 0; const chaosDelta = Number(actor.system.magie?.chaos?.delta ?? 12); return { rank, focusDelta, focusLabel: this.formatUsageDie(focusDelta), focusSceneId, focusStoredResult: focusResult, focusActiveValue: activeFocus, focusIsActive: activeFocus > 0, focusDisplay: activeFocus > 0 ? `${activeFocus} (${this.formatUsageDie(focusDelta)})` : "—", chaosDelta, chaosLabel: this.formatUsageDie(chaosDelta), chaosAvailable: chaosDelta >= 4 }; } static getFavorLabel(key) { return DONJON_ET_CIE.favorDepartments[key] ?? key; } static getFavorEntries(system) { const favors = system.faveurs ?? {}; return Object.entries(DONJON_ET_CIE.favorDepartments).map(([key, label]) => { const delta = Number(favors[key]?.delta ?? 0); return { key, label, delta, deltaLabel: this.formatUsageDie(delta), hasFavor: delta > 0 }; }); } static getAvailableFavorOptions(actor) { return this.getFavorEntries(actor.system) .filter((entry) => entry.hasFavor) .map((entry) => ({ value: entry.key, label: `${entry.label} (${entry.deltaLabel})` })); } static getChaosTableEntries() { return Object.entries(DONJON_ET_CIE.chaosTable) .map(([value, entry]) => ({ value: Number(value), ...entry })) .sort((a, b) => a.value - b.value); } static degradeUsageDie(value) { const sequence = [12, 10, 8, 6, 4]; const index = sequence.indexOf(Number(value)); if (index === -1) return 0; return sequence[index + 1] ?? 0; } static sortByName(documents) { return [...documents].sort((a, b) => a.name.localeCompare(b.name, "fr", { sensitivity: "base" })); } static getWeaponCharacteristicKey(category) { return category === "distance" ? "dexterite" : "force"; } static getWeaponCharacteristicLabel(category) { const key = this.getWeaponCharacteristicKey(category); return DONJON_ET_CIE.characteristics[key]?.label ?? key; } static enrichItemForSheet(item) { const system = item.system; const delta = Number(system.delta ?? 0); return { id: item.id, name: item.name, type: item.type, img: item.img, system, uuid: item.uuid, usageLabel: delta > 0 ? this.formatUsageDie(delta) : null, protectionLabel: item.type === "armure" && Number(system.resultatProtection ?? 0) > 0 ? `Protection ${system.resultatProtection}` : null, weaponCharacteristicLabel: item.type === "arme" ? this.getWeaponCharacteristicLabel(system.categorie) : null, canRoll: ["arme", "sortilege"].includes(item.type), canUse: delta > 0, canRollDamage: Boolean(system.degats), rollAction: item.type === "sortilege" ? "rollSpell" : "rollWeapon", damageAction: "rollDamage", isEquipped: Boolean(system.equipee) }; } static buildActorSections(actor) { return Object.entries(DONJON_ET_CIE.actorSections).map(([key, metadata]) => { const types = DONJON_ET_CIE.sectionTypes[key]; const items = this.sortByName(actor.items.filter((item) => types.includes(item.type))).map((item) => this.enrichItemForSheet(item)); return { key, label: metadata.label, createType: metadata.createType, items }; }); } }