147 lines
4.7 KiB
JavaScript
147 lines
4.7 KiB
JavaScript
/**
|
||
* 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)
|
||
}
|
||
}
|