/** * fvtt-celestopol.mjs — Point d'entrée principal du système Célestopol 1922 * FoundryVTT v13+ / DataModels / ApplicationV2 */ import { SYSTEM, SYSTEM_ID, ASCII } from "./module/config/system.mjs" import { CelestopolCharacter, CelestopolNPC, CelestopolAnomaly, CelestopolAspect, CelestopolEquipment, } from "./module/models/_module.mjs" import { CelestopolActor, CelestopolItem, CelestopolChatMessage, CelestopolRoll, } from "./module/documents/_module.mjs" import { CelestopolCharacterSheet, CelestopolNPCSheet, CelestopolAnomalySheet, CelestopolAspectSheet, CelestopolEquipmentSheet, } from "./module/applications/_module.mjs" /* ─── Init hook ──────────────────────────────────────────────────────────── */ Hooks.once("init", () => { console.log(ASCII) console.log(`${SYSTEM_ID} | Initializing Célestopol 1922 system`) // Expose SYSTEM constants in game.system namespace game.celestopol = { SYSTEM } // ── DataModels ────────────────────────────────────────────────────────── CONFIG.Actor.dataModels.character = CelestopolCharacter CONFIG.Actor.dataModels.npc = CelestopolNPC CONFIG.Item.dataModels.anomaly = CelestopolAnomaly CONFIG.Item.dataModels.aspect = CelestopolAspect CONFIG.Item.dataModels.equipment = CelestopolEquipment // ── Document classes ──────────────────────────────────────────────────── CONFIG.Actor.documentClass = CelestopolActor CONFIG.Item.documentClass = CelestopolItem CONFIG.ChatMessage.documentClass = CelestopolChatMessage CONFIG.Dice.rolls.push(CelestopolRoll) // ── Token display defaults ─────────────────────────────────────────────── CONFIG.Actor.trackableAttributes = { character: { bar: ["blessures.lvl"], value: ["initiative", "anomaly.level"], }, npc: { bar: ["blessures.lvl"], value: ["initiative"], }, } // ── Sheets: unregister core, register system sheets ───────────────────── foundry.applications.sheets.ActorSheetV2.unregisterSheet?.("core", "Actor", { types: ["character", "npc"] }) foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet) foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, CelestopolCharacterSheet, { types: ["character"], makeDefault: true, label: "CELESTOPOL.Sheet.character", }) foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, CelestopolNPCSheet, { types: ["npc"], makeDefault: true, label: "CELESTOPOL.Sheet.npc", }) foundry.documents.collections.Items.unregisterSheet("core", ItemSheet) foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAnomalySheet, { types: ["anomaly"], makeDefault: true, label: "CELESTOPOL.Sheet.anomaly", }) foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAspectSheet, { types: ["aspect"], makeDefault: true, label: "CELESTOPOL.Sheet.aspect", }) foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolEquipmentSheet, { types: ["equipment"], makeDefault: true, label: "CELESTOPOL.Sheet.equipment", }) // ── Handlebars helpers ─────────────────────────────────────────────────── _registerHandlebarsHelpers() // ── Game settings ──────────────────────────────────────────────────────── _registerSettings() // ── Pre-load templates ─────────────────────────────────────────────────── _preloadTemplates() }) /* ─── Ready hook ─────────────────────────────────────────────────────────── */ Hooks.once("ready", () => { console.log(`${SYSTEM_ID} | System ready`) // Socket handler for GM-only operations (e.g. wound application) if (game.socket) { game.socket.on(`system.${SYSTEM_ID}`, _onSocketMessage) } }) /* ─── Handlebars helpers ─────────────────────────────────────────────────── */ function _registerHandlebarsHelpers() { // Helper : concat strings Handlebars.registerHelper("concat", (...args) => args.slice(0, -1).join("")) // Helper : strict equality Handlebars.registerHelper("eq", (a, b) => a === b) // Helper : greater than Handlebars.registerHelper("gt", (a, b) => a > b) // Helper : less than or equal Handlebars.registerHelper("lte", (a, b) => a <= b) // Helper : greater than or equal Handlebars.registerHelper("gte", (a, b) => a >= b) // Helper : less than Handlebars.registerHelper("lt", (a, b) => a < b) // Helper : logical OR Handlebars.registerHelper("or", (...args) => args.slice(0, -1).some(Boolean)) // Helper : build array from args (Handlebars doesn't have arrays natively) Handlebars.registerHelper("array", (...args) => args.slice(0, -1)) // Helper : nested object lookup with dot path or multiple keys Handlebars.registerHelper("lookup", (obj, ...args) => { const options = args.pop() // last arg is Handlebars options hash return args.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj) }) // Helper : negate a number (abs value helper) Handlebars.registerHelper("neg", n => -n) // Helper : absolute value Handlebars.registerHelper("abs", n => Math.abs(n)) // Helper : add two numbers Handlebars.registerHelper("add", (a, b) => a + b) Handlebars.registerHelper("let", function(value, options) { return options.fn({ value }) }) } /* ─── Settings ───────────────────────────────────────────────────────────── */ function _registerSettings() { game.settings.register(SYSTEM_ID, "rollMoonDieByDefault", { name: "CELESTOPOL.Setting.rollMoonDieByDefault.name", hint: "CELESTOPOL.Setting.rollMoonDieByDefault.hint", scope: "world", config: true, type: Boolean, default: false, }) game.settings.register(SYSTEM_ID, "autoWounds", { name: "CELESTOPOL.Setting.autoWounds.name", hint: "CELESTOPOL.Setting.autoWounds.hint", scope: "world", config: true, type: Boolean, default: false, }) } /* ─── Template preload ───────────────────────────────────────────────────── */ function _preloadTemplates() { const base = `systems/${SYSTEM_ID}/templates` loadTemplates([ `${base}/character-main.hbs`, `${base}/character-competences.hbs`, `${base}/character-blessures.hbs`, `${base}/character-factions.hbs`, `${base}/character-biography.hbs`, `${base}/npc-main.hbs`, `${base}/npc-competences.hbs`, `${base}/npc-blessures.hbs`, `${base}/anomaly.hbs`, `${base}/aspect.hbs`, `${base}/equipment.hbs`, `${base}/roll-dialog.hbs`, `${base}/chat-message.hbs`, `${base}/partials/item-scores.hbs`, ]) } /* ─── Socket handler ─────────────────────────────────────────────────────── */ function _onSocketMessage(data) { if (!game.user.isGM) return switch (data.type) { case "applyWound": { const actor = game.actors.get(data.actorId) if (actor) actor.update({ "system.blessures.lvl": data.level }) break } } }