/** * 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 { MAGICS } from "../config/constants.js" export function registerHandlebarsHelpers() { const { Handlebars } = globalThis if (!Handlebars) return Handlebars.registerHelper("getMagicLabel", function (magic) { return game.i18n.localize(MAGICS[magic]?.label ?? "") }) Handlebars.registerHelper("getMagicAspectLabel", function (magic) { return game.i18n.localize(MAGICS[magic]?.aspectlabel ?? "") }) Handlebars.registerHelper("getMagicSpecialityLabel", function (magic, speciality) { return game.i18n.localize(MAGICS[magic]?.speciality?.[speciality]?.label ?? "") }) Handlebars.registerHelper("getMagicSpecialityClassIcon", function (magic, speciality) { return MAGICS[magic]?.speciality?.[speciality]?.classicon ?? "" }) Handlebars.registerHelper("getMagicSpecialityElementIcon", function (magic, speciality) { return MAGICS[magic]?.speciality?.[speciality]?.elementicon ?? "" }) Handlebars.registerHelper("getMagicSpecialityLabelIcon", function (magic, speciality) { return MAGICS[magic]?.speciality?.[speciality]?.labelicon ?? "" }) Handlebars.registerHelper("getMagicSpecialityLabelElement", function (magic, speciality) { return game.i18n.localize(MAGICS[magic]?.speciality?.[speciality]?.labelelement ?? "") }) Handlebars.registerHelper("getMagicAspectIcon", function (magic) { const icons = { internalcinnabar: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.webp", alchemy: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.webp", masteryoftheway: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.webp", exorcism: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.webp", geomancy: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.webp", } return icons[magic] ?? "" }) Handlebars.registerHelper("getElementIcon", function (aspect) { const icons = { metal: "/systems/fvtt-chroniques-de-l-etrange/images/cde_metal.webp", water: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.webp", earth: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.webp", fire: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.webp", wood: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.webp", // legacy French keys eau: "/systems/fvtt-chroniques-de-l-etrange/images/cde_eau.webp", terre: "/systems/fvtt-chroniques-de-l-etrange/images/cde_terre.webp", feu: "/systems/fvtt-chroniques-de-l-etrange/images/cde_feu.webp", bois: "/systems/fvtt-chroniques-de-l-etrange/images/cde_bois.webp", } return icons[aspect] ?? "" }) Handlebars.registerHelper("getOrientationIcon", function (orientation) { const icons = { yin: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yin.webp", yang: "/systems/fvtt-chroniques-de-l-etrange/images/cde_yang.webp", yinyang: "/systems/fvtt-chroniques-de-l-etrange/images/yin_yang.webp", } return icons[orientation] ?? "" }) Handlebars.registerHelper("getOrientationLabel", function (orientation) { const keys = { yin: "CDE.OrientationYin", yang: "CDE.OrientationYang", yinyang: "CDE.OrientationYinYang", } return game.i18n.localize(keys[orientation] ?? "CDE.Orientation") }) Handlebars.registerHelper("getActivationLabel", function (activation) { const keys = { "action-attack": "CDE.ActivationAttack", "action-defense": "CDE.ActivationDefense", "action-aid": "CDE.ActivationAid", "action-attack-defense": "CDE.ActivationAttackOrDefense", reaction: "CDE.ActivationReaction", dice: "CDE.ActivationDice", "damage-inflicted": "CDE.ActivationDamageInflicted", "damage-received": "CDE.ActivationDamageReceived", } return game.i18n.localize(keys[activation] ?? "CDE.Activation") }) /** * Compute the SVG x,y coordinates for a cran on the initiative wheel. * Cran 1–24 are arranged counter-clockwise from the bottom (reference at 6 o'clock). * angle = 90° + cran * 15° (counter-clockwise = positive in standard math, negative in SVG). * In SVG coords: x = cx + r*cos(a), y = cy - r*sin(a) [y-axis is flipped in SVG]. */ Handlebars.registerHelper("cranPosition", function (cran, cx, cy, r) { const angleDeg = 90 + cran * 15 // counter-clockwise from bottom const angleRad = (angleDeg * Math.PI) / 180 const x = Math.round(cx + r * Math.cos(angleRad)) const y = Math.round(cy - r * Math.sin(angleRad)) return { x, y } }) /** X offset for overlapping fighters on the same cran. Centres a 30px image on the cran cx. */ Handlebars.registerHelper("fighterX", function (cx, index, total) { const offset = total > 1 ? (index - (total - 1) / 2) * 34 : 0 return Math.round(cx - 15 + offset) }) /** Y offset for fighters — positions image just above the cran circle. */ Handlebars.registerHelper("fighterY", function (cy, index, total) { return Math.round(cy - 50) }) }