202 lines
7.3 KiB
JavaScript
202 lines
7.3 KiB
JavaScript
/**
|
||
* 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
|
||
};
|
||
});
|
||
}
|
||
}
|