/** * Chroniques de l'Étrange — Système FoundryVTT * * Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions. * Ce système FoundryVTT est une implémentation indépendante et n'est pas * affilié à Antre-Monde Éditions, * mais a été réalisé avec l'autorisation d'Antre-Monde Éditions. * * @author LeRatierBretonnien * @copyright 2024–2026 LeRatierBretonnien * @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/ */ import { getLoksyuData, setLoksyuData } from "./singletons.js" import { SYSTEM_ID } from "../../config/constants.js" export class CDELoksyuApp extends foundry.applications.api.HandlebarsApplicationMixin( foundry.applications.api.ApplicationV2 ) { static DEFAULT_OPTIONS = { id: "cde-loksyu-app", tag: "div", window: { title: "CDE.Loksyu", icon: "fas fa-yin-yang", resizable: false, }, classes: ["cde-app", "cde-loksyu-standalone"], position: { width: 520, height: "auto" }, actions: { resetElement: CDELoksyuApp.#onResetElement, resetAll: CDELoksyuApp.#onResetAll, zoomVisual: CDELoksyuApp.#onZoomVisual, }, } static PARTS = { main: { template: `systems/${SYSTEM_ID}/templates/apps/cde-loksyu-app.html`, }, } /** @type {Function|null} bound hook handler */ _updateHook = null /** @type {Function|null} updateSetting hook handler (for socket-propagated writes) */ _settingHook = null /** Singleton accessor — open or bring to front */ static open() { const existing = Array.from(foundry.applications.instances.values()).find( (app) => app instanceof CDELoksyuApp ) if (existing) { existing.bringToFront(); return existing } const app = new CDELoksyuApp() app.render(true) return app } async _prepareContext() { const sys = getLoksyuData() const ELEMENTS = [ { key: "wood", nameKey: "CDE.Wood", qualKey: "CDE.WoodQualities", img: `systems/${SYSTEM_ID}/images/cde_bois.webp` }, { key: "fire", nameKey: "CDE.Fire", qualKey: "CDE.FireQualities", img: `systems/${SYSTEM_ID}/images/cde_feu.webp` }, { key: "earth", nameKey: "CDE.Earth", qualKey: "CDE.EarthQualities", img: `systems/${SYSTEM_ID}/images/cde_terre.webp` }, { key: "metal", nameKey: "CDE.Metal", qualKey: "CDE.MetalQualities", img: `systems/${SYSTEM_ID}/images/cde_metal.webp` }, { key: "water", nameKey: "CDE.Water", qualKey: "CDE.WaterQualities", img: `systems/${SYSTEM_ID}/images/cde_eau.webp` }, ] return { canEdit: game.user.isGM, elements: ELEMENTS.map((el) => ({ ...el, yang: sys[el.key]?.yang ?? 0, yin: sys[el.key]?.yin ?? 0, })), } } _onRender(context, options) { super._onRender(context, options) this.#bindInputs() if (!this._updateHook) { this._updateHook = Hooks.on("cde:loksyuUpdated", () => this.render()) } if (!this._settingHook) { this._settingHook = Hooks.on("updateSetting", (setting) => { if (setting.key === `${SYSTEM_ID}.loksyuData`) this.render() }) } } _onClose(options) { if (this._updateHook !== null) { Hooks.off("cde:loksyuUpdated", this._updateHook) this._updateHook = null } if (this._settingHook !== null) { Hooks.off("updateSetting", this._settingHook) this._settingHook = null } super._onClose(options) } #bindInputs() { const inputs = this.element?.querySelectorAll("input[data-field]") if (!inputs?.length) return inputs.forEach((input) => { input.addEventListener("change", async (ev) => { const field = ev.currentTarget.dataset.field const val = parseInt(ev.currentTarget.value, 10) if (!field || isNaN(val)) return // field is like "wood.yin" or "fire.yang" const [aspect, dim] = field.split(".") if (!aspect || !dim) return const data = getLoksyuData() if (!data[aspect]) data[aspect] = { yin: 0, yang: 0 } data[aspect][dim] = Math.max(0, val) await setLoksyuData(data) }) }) } static async #onResetElement(event, target) { const key = target.dataset.element if (!key) return const data = getLoksyuData() data[key] = { yin: 0, yang: 0 } await setLoksyuData(data) } static async #onResetAll(_event, _target) { const KEYS = ["wood", "fire", "earth", "metal", "water"] const data = getLoksyuData() for (const k of KEYS) data[k] = { yin: 0, yang: 0 } await setLoksyuData(data) } static #onZoomVisual(_event, target) { new ImagePopout(target.src, { title: game.i18n.localize("CDE.LoksyuDiagramTitle"), shareable: false, }).render(true) } }