feat: implémentation complète du système Célestopol 1922 pour FoundryVTT v13
- DataModels (character, npc, anomaly, aspect, attribute, equipment) - ApplicationV2 sheets (character 5 tabs, npc 3 tabs, 4 item sheets) - DialogV2 pour les jets de dés avec phase de lune - Templates Handlebars complets (fiches PJ/PNJ, items, jet, chat) - Styles LESS → CSS compilé (thème vert foncé / orange CopaseticNF) - i18n fr.json complet (clés CELESTOPOL.*) - Point d'entrée fvtt-celestopol.mjs avec hooks init/ready - Assets : polices CopaseticNF, images UI, icônes items - Mise à jour copilot-instructions.md avec l'architecture réelle Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
212
fvtt-celestopol.mjs
Normal file
212
fvtt-celestopol.mjs
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* 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,
|
||||
CelestopolAttribute,
|
||||
CelestopolEquipment,
|
||||
} from "./module/models/_module.mjs"
|
||||
import {
|
||||
CelestopolActor,
|
||||
CelestopolItem,
|
||||
CelestopolChatMessage,
|
||||
CelestopolRoll,
|
||||
} from "./module/documents/_module.mjs"
|
||||
import {
|
||||
CelestopolCharacterSheet,
|
||||
CelestopolNPCSheet,
|
||||
CelestopolAnomalySheet,
|
||||
CelestopolAspectSheet,
|
||||
CelestopolAttributeSheet,
|
||||
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.attribute = CelestopolAttribute
|
||||
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.value"],
|
||||
},
|
||||
npc: {
|
||||
bar: ["blessures.lvl"],
|
||||
value: ["initiative"],
|
||||
},
|
||||
}
|
||||
|
||||
// ── Sheets: unregister core, register system sheets ─────────────────────
|
||||
foundry.applications.sheets.ActorSheetV2.unregisterSheet?.("core", "Actor", { types: ["character", "npc"] })
|
||||
Actors.unregisterSheet("core", ActorSheet)
|
||||
Actors.registerSheet(SYSTEM_ID, CelestopolCharacterSheet, {
|
||||
types: ["character"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.character",
|
||||
})
|
||||
Actors.registerSheet(SYSTEM_ID, CelestopolNPCSheet, {
|
||||
types: ["npc"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.npc",
|
||||
})
|
||||
|
||||
Items.unregisterSheet("core", ItemSheet)
|
||||
Items.registerSheet(SYSTEM_ID, CelestopolAnomalySheet, {
|
||||
types: ["anomaly"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.anomaly",
|
||||
})
|
||||
Items.registerSheet(SYSTEM_ID, CelestopolAspectSheet, {
|
||||
types: ["aspect"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.aspect",
|
||||
})
|
||||
Items.registerSheet(SYSTEM_ID, CelestopolAttributeSheet, {
|
||||
types: ["attribute"],
|
||||
makeDefault: true,
|
||||
label: "CELESTOPOL.Sheet.attribute",
|
||||
})
|
||||
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 : 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 : let (scope variable assignment inside template)
|
||||
Handlebars.registerHelper("let", function(value, options) {
|
||||
return options.fn({ value })
|
||||
})
|
||||
}
|
||||
|
||||
/* ─── Settings ───────────────────────────────────────────────────────────── */
|
||||
|
||||
function _registerSettings() {
|
||||
game.settings.register(SYSTEM_ID, "defaultMoonPhase", {
|
||||
name: "CELESTOPOL.Setting.defaultMoonPhase.name",
|
||||
hint: "CELESTOPOL.Setting.defaultMoonPhase.hint",
|
||||
scope: "world",
|
||||
config: true,
|
||||
type: String,
|
||||
default: "nouvelleLune",
|
||||
choices: Object.fromEntries(
|
||||
Object.entries(SYSTEM.MOON_DICE_PHASES).map(([k, v]) => [k, v.label])
|
||||
),
|
||||
})
|
||||
|
||||
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}/attribute.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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user