From cff700bd3de3dc6405fa96d0299bcc0b92295113 Mon Sep 17 00:00:00 2001 From: LeRatierBretonnier Date: Sat, 28 Mar 2026 17:21:18 +0100 Subject: [PATCH] Fix roll dialog CSS + JS: template
wrapper, moon-section, selectors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remplace
par
pour éviter les formulaires HTML imbriqués invalides (DialogV2 a son propre ) - Corrige le sélecteur CSS de .roll-dialog.celestopol vers .application.roll-dialog .roll-dialog-content - Remplace .form-group.form-moon par .moon-section (classe custom) pour éviter les conflits avec le CSS grid de FoundryVTT standard-form (label 130px de hauteur) - Met à jour le script JS inline pour utiliser document.querySelector('.roll-dialog-content') - Ajoute white-space: nowrap sur le label Destin pour éviter le wrapping sur 3 lignes - Supprime .application.roll-dialog .window-content padding override (remplacé par dialog-content) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- fvtt-celestopol.mjs | 26 +- lang/fr.json | 21 +- .../applications/sheets/base-actor-sheet.mjs | 155 ++++++- .../applications/sheets/base-item-sheet.mjs | 8 +- module/config/system.mjs | 6 +- module/documents/roll.mjs | 223 +++++++--- module/models/character.mjs | 26 +- module/models/items.mjs | 2 +- styles/character.less | 2 +- styles/global.less | 120 ++++- styles/roll.less | 412 ++++++++++++++---- system.json | 2 +- templates/anomaly.hbs | 2 +- templates/aspect.hbs | 2 +- templates/attribute.hbs | 2 +- templates/character-blessures.hbs | 15 +- templates/character-competences.hbs | 50 ++- templates/character-factions.hbs | 109 ++++- templates/character-main.hbs | 2 +- templates/chat-message.hbs | 95 ++-- templates/equipment.hbs | 2 +- templates/npc-main.hbs | 2 +- templates/partials/item-scores.hbs | 2 +- templates/roll-dialog.hbs | 130 +++++- 24 files changed, 1133 insertions(+), 283 deletions(-) diff --git a/fvtt-celestopol.mjs b/fvtt-celestopol.mjs index 0c0b8c7..ddfa16d 100644 --- a/fvtt-celestopol.mjs +++ b/fvtt-celestopol.mjs @@ -65,35 +65,35 @@ Hooks.once("init", () => { // ── 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, { + foundry.documents.collections.Actors.unregisterSheet("core", ActorSheet) + foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, CelestopolCharacterSheet, { types: ["character"], makeDefault: true, label: "CELESTOPOL.Sheet.character", }) - Actors.registerSheet(SYSTEM_ID, CelestopolNPCSheet, { + foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, CelestopolNPCSheet, { types: ["npc"], makeDefault: true, label: "CELESTOPOL.Sheet.npc", }) - Items.unregisterSheet("core", ItemSheet) - Items.registerSheet(SYSTEM_ID, CelestopolAnomalySheet, { + foundry.documents.collections.Items.unregisterSheet("core", ItemSheet) + foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAnomalySheet, { types: ["anomaly"], makeDefault: true, label: "CELESTOPOL.Sheet.anomaly", }) - Items.registerSheet(SYSTEM_ID, CelestopolAspectSheet, { + foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAspectSheet, { types: ["aspect"], makeDefault: true, label: "CELESTOPOL.Sheet.aspect", }) - Items.registerSheet(SYSTEM_ID, CelestopolAttributeSheet, { + foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolAttributeSheet, { types: ["attribute"], makeDefault: true, label: "CELESTOPOL.Sheet.attribute", }) - Items.registerSheet(SYSTEM_ID, CelestopolEquipmentSheet, { + foundry.documents.collections.Items.registerSheet(SYSTEM_ID, CelestopolEquipmentSheet, { types: ["equipment"], makeDefault: true, label: "CELESTOPOL.Sheet.equipment", @@ -144,7 +144,15 @@ function _registerHandlebarsHelpers() { return args.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj) }) - // Helper : let (scope variable assignment inside template) + // 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 }) }) diff --git a/lang/fr.json b/lang/fr.json index 893985a..07865df 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -85,7 +85,7 @@ }, "Tab": { "main": "Principal", - "competences": "Compétences", + "competences": "Domaines", "blessures": "Blessures", "factions": "Factions", "biography": "Biographie", @@ -106,7 +106,22 @@ "moonBonus": "Bonus de lune", "title": "Jet de dés", "roll": "Lancer !", - "rollTitle": "Lancer les dés" + "rollTitle": "Lancer les dés", + "aspect": "Modificateur d'Aspect", + "woundMalus": "Malus de blessures", + "diceSum": "Somme des dés", + "margin": "Marge", + "above": "au-dessus du seuil", + "below": "en dessous du seuil", + "destin": "Dépenser 1 Destin (+2 dés)", + "usedAspect": "Aspect utilisé", + "usedDestin": "Destin dépensé", + "criticalSuccessDesc": "Marge ≥ 5 — résultat exceptionnel !", + "criticalFailureDesc": "Marge ≤ −5 — résultat désastreux !", + "woundLevel": "Niveau de blessures", + "diceBreakdown": "Détail du jet", + "threshold": "Seuil", + "nbDiceBase": "Dés de base" }, "Moon": { "none": "Aucune phase", @@ -174,4 +189,4 @@ "rollFor": "Jet de {skill} ({stat})" } } -} +} \ No newline at end of file diff --git a/module/applications/sheets/base-actor-sheet.mjs b/module/applications/sheets/base-actor-sheet.mjs index cf31d37..aa892de 100644 --- a/module/applications/sheets/base-actor-sheet.mjs +++ b/module/applications/sheets/base-actor-sheet.mjs @@ -50,9 +50,28 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou this.element.querySelectorAll(".rollable").forEach(el => { el.addEventListener("click", this._onRoll.bind(this)) }) + + // Setup sequential checkbox logic for wound tracks + this._setupSequentialCheckboxes() + + // Setup sequential checkbox logic for factions + this._setupFactionCheckboxes() + } + + /** @override */ + _onClick(event) { + // Skip checkbox clicks in edit mode + if (this.isEditMode && event.target.classList.contains('skill-level-checkbox')) { + return + } + super._onClick(event) } async _onRoll(event) { + // Don't roll if clicking on a checkbox + if (event.target.classList.contains('skill-level-checkbox')) { + return + } if (!this.isPlayMode) return const el = event.currentTarget const statId = el.dataset.statId @@ -92,13 +111,12 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou await this.document.createEmbeddedDocuments("Item", [item.toObject()], { renderSheet: false }) } - static async #onEditImage(event, target) { - const attr = target.dataset.edit - const current = foundry.utils.getProperty(this.document, attr) + static async #onEditImage(event, _target) { + const current = this.document.img const fp = new FilePicker({ current, type: "image", - callback: (path) => this.document.update({ [attr]: path }), + callback: (path) => this.document.update({ img: path }), top: this.position.top + 40, left: this.position.left + 10, }) @@ -123,4 +141,133 @@ export default class CelestopolActorSheet extends HandlebarsApplicationMixin(fou const item = await fromUuid(uuid) await item?.deleteDialog() } + + /** + * Setup sequential checkbox logic for wound/destin/spleen tracks + * Only allows checking the next checkbox in sequence + */ + _setupSequentialCheckboxes() { + this.element.querySelectorAll('.wound-checkbox').forEach(checkbox => { + checkbox.addEventListener('change', (event) => { + this._handleSequentialCheckboxChange(event) + }) + }) + } + + /** + * Handle sequential checkbox change logic + * @param {Event} event - The change event + */ + _handleSequentialCheckboxChange(event) { + const checkbox = event.target + if (!checkbox.classList.contains('wound-checkbox') || checkbox.disabled) return + + const track = checkbox.dataset.track + const currentIndex = parseInt(checkbox.dataset.index) + const isChecked = checkbox.checked + + // Get all checkboxes in this track + const trackCheckboxes = Array.from(this.element.querySelectorAll(`.wound-checkbox[data-track="${track}"]`)) + + if (isChecked) { + // Checking a box: uncheck all boxes after this one + for (let i = currentIndex + 1; i < trackCheckboxes.length; i++) { + trackCheckboxes[i].checked = false + } + // Check all boxes before this one + for (let i = 0; i < currentIndex; i++) { + trackCheckboxes[i].checked = true + } + } else { + // Unchecking a box: uncheck all boxes after this one + for (let i = currentIndex; i < trackCheckboxes.length; i++) { + trackCheckboxes[i].checked = false + } + } + + // Update the visual state + this._updateTrackVisualState() + } + + /** + * Update visual state of track boxes based on checkbox states + */ + _updateTrackVisualState() { + this.element.querySelectorAll('.track-box').forEach(box => { + const checkbox = box.querySelector('.wound-checkbox') + if (checkbox) { + if (checkbox.checked) { + box.classList.add('checked') + } else { + box.classList.remove('checked') + } + } + }) + } + + /** + * Setup sequential checkbox logic for faction tracks + */ + _setupFactionCheckboxes() { + this.element.querySelectorAll('.faction-checkbox').forEach(checkbox => { + checkbox.addEventListener('change', (event) => { + this._handleFactionCheckboxChange(event) + }) + }) + } + + /** + * Handle faction checkbox change logic + * @param {Event} event - The change event + */ + _handleFactionCheckboxChange(event) { + const checkbox = event.target + if (!checkbox.classList.contains('faction-checkbox') || checkbox.disabled) return + + const factionId = checkbox.dataset.faction + const currentLevel = parseInt(checkbox.dataset.level) + const isChecked = checkbox.checked + + // Get all checkboxes for this faction + const factionCheckboxes = Array.from(this.element.querySelectorAll(`.faction-checkbox[data-faction="${factionId}"]`)) + + if (isChecked) { + // Checking a box: check all boxes before this one, uncheck all boxes after this one + for (let i = 0; i < currentLevel; i++) { + factionCheckboxes[i].checked = true + } + for (let i = currentLevel; i < factionCheckboxes.length; i++) { + factionCheckboxes[i].checked = false + } + } else { + // Unchecking a box: uncheck all boxes after this one + for (let i = currentLevel - 1; i < factionCheckboxes.length; i++) { + factionCheckboxes[i].checked = false + } + } + + // Update the count display + this._updateFactionCount(factionId) + } + + /** + * Update the faction count display based on checked checkboxes + * @param {string} factionId - The faction ID + */ + _updateFactionCount(factionId) { + const checkboxes = Array.from(this.element.querySelectorAll(`.faction-checkbox[data-faction="${factionId}"]:checked`)) + const count = checkboxes.length + + // Update the hidden input field + const input = this.element.querySelector(`input[name="system.factions.${factionId}.value"]`) + if (input) { + input.value = count + } + + // Update the visual count display + const countDisplay = this.element.querySelector(`.faction-row[data-faction="${factionId}"] .faction-count`) + if (countDisplay) { + countDisplay.textContent = count + } + } } diff --git a/module/applications/sheets/base-item-sheet.mjs b/module/applications/sheets/base-item-sheet.mjs index fa62910..80110cc 100644 --- a/module/applications/sheets/base-item-sheet.mjs +++ b/module/applications/sheets/base-item-sheet.mjs @@ -39,16 +39,16 @@ export default class CelestopolItemSheet extends HandlebarsApplicationMixin(foun }) } - static async #onEditImage(event, target) { - const attr = target.dataset.edit - const current = foundry.utils.getProperty(this.document, attr) + static async #onEditImage(event, _target) { + const current = this.document.img const fp = new FilePicker({ current, type: "image", - callback: (path) => this.document.update({ [attr]: path }), + callback: (path) => this.document.update({ img: path }), top: this.position.top + 40, left: this.position.left + 10, }) return fp.browse() } + } diff --git a/module/config/system.mjs b/module/config/system.mjs index 1c24cbe..2c5402c 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -7,7 +7,7 @@ export const ASCII = ` ░░░░░░░░░░░░░░░░░░1922░░░░░░░░░░░░░░░░░░░ ` -/** Les 4 attributs principaux (stats). Chacun a une résistance (res) et 4 compétences. */ +/** Les 4 attributs principaux (stats). Chacun a une résistance (res) et 4 domaines. */ export const STATS = { ame: { id: "ame", label: "CELESTOPOL.Stat.ame" }, corps: { id: "corps", label: "CELESTOPOL.Stat.corps" }, @@ -15,7 +15,7 @@ export const STATS = { esprit: { id: "esprit", label: "CELESTOPOL.Stat.esprit" }, } -/** Compétences groupées par attribut. */ +/** Domaines groupées par attribut. */ export const SKILLS = { ame: { artifice: { id: "artifice", label: "CELESTOPOL.Skill.artifice", stat: "ame" }, @@ -43,7 +43,7 @@ export const SKILLS = { }, } -/** Liste plate de toutes les compétences (utile pour les DataModels d'items). */ +/** Liste plate de tous les domaines (utile pour les DataModels d'items). */ export const ALL_SKILLS = Object.values(SKILLS).flatMap(group => Object.values(group)) /** Types d'anomalies (pouvoirs paranormaux). */ diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index d1470bd..6378309 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -1,24 +1,40 @@ import { SYSTEM } from "../config/system.mjs" +/** Symboles Unicode pour chaque phase de lune. */ +const MOON_SYMBOLS = { + none: "☽", + nouvellelune: "🌑", + premiercroissant: "🌒", + premierquartier: "🌓", + lunegibbeuse: "🌔", + lunevoutee: "🌕", + derniercroissant: "🌖", + dernierquartier: "🌗", + pleinelune: "🌕", +} + /** * Système de dés de Célestopol 1922. * - * Le jet de base est : (valeur compétence)d6 comparé à un seuil de difficulté. - * Le dé de lune ajoute un bonus selon la phase actuelle. - * Destin et Spleen modifient le nombre de dés. + * Le jet de base est : (valeur domaine + malus blessures)d6 + bonus lune + modificateurs + * comparé à un seuil de difficulté. + * - Succès critique : marge ≥ 5 + * - Échec critique : marge ≤ −5 */ export class CelestopolRoll extends Roll { static CHAT_TEMPLATE = "systems/fvtt-celestopol/templates/chat-message.hbs" - get resultType() { return this.options.resultType } - get isSuccess() { return this.resultType === "success" } - get isFailure() { return this.resultType === "failure" } - get actorId() { return this.options.actorId } - get actorName() { return this.options.actorName } - get actorImage() { return this.options.actorImage } - get skillLabel() { return this.options.skillLabel } - get difficulty() { return this.options.difficulty } - get moonBonus() { return this.options.moonBonus ?? 0 } + get resultType() { return this.options.resultType } + get isSuccess() { return this.resultType === "success" || this.resultType === "critical-success" } + get isFailure() { return this.resultType === "failure" || this.resultType === "critical-failure" } + get isCriticalSuccess(){ return this.resultType === "critical-success" } + get isCriticalFailure(){ return this.resultType === "critical-failure" } + get actorId() { return this.options.actorId } + get actorName() { return this.options.actorName } + get actorImage() { return this.options.actorImage } + get skillLabel() { return this.options.skillLabel } + get difficulty() { return this.options.difficulty } + get moonBonus() { return this.options.moonBonus ?? 0 } /** * Ouvre le dialogue de configuration du jet via DialogV2 et exécute le jet. @@ -26,26 +42,33 @@ export class CelestopolRoll extends Roll { * @returns {Promise} */ static async prompt(options = {}) { - const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) - const fieldRollMode = new foundry.data.fields.StringField({ - choices: rollModes, - blank: false, - default: "publicroll", - }) + const woundMalus = options.woundMalus ?? 0 + const baseSkillVal = options.skillValue ?? 0 + const nbDiceBase = Math.max(1, baseSkillVal + woundMalus) + const woundLevelId = options.woundLevel ?? 0 + const woundLabel = woundLevelId > 0 + ? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "") + : null + + // Construire les phases lune avec symboles + const moonPhaseChoices = Object.fromEntries( + Object.entries(SYSTEM.MOON_DICE_PHASES).map(([key, val]) => [ + key, { ...val, symbol: MOON_SYMBOLS[key] ?? "☽" } + ]) + ) const dialogContext = { - actorName: options.actorName, - statLabel: options.statLabel, - skillLabel: options.skillLabel, - skillValue: options.skillValue, - woundMalus: options.woundMalus ?? 0, - nbDice: Math.max(1, options.skillValue ?? 1), - difficultyChoices:SYSTEM.DIFFICULTY_CHOICES, - moonPhaseChoices: SYSTEM.MOON_DICE_PHASES, - defaultDifficulty:options.difficulty ?? "normal", - defaultMoonPhase: options.moonPhase ?? "none", - rollModes, - fieldRollMode, + actorName: options.actorName, + statLabel: options.statLabel, + skillLabel: options.skillLabel, + skillValue: baseSkillVal, + woundMalus, + woundLabel, + nbDiceBase, + difficultyChoices: SYSTEM.DIFFICULTY_CHOICES, + moonPhaseChoices, + defaultDifficulty: options.difficulty ?? "normal", + defaultMoonPhase: options.moonPhase ?? "none", } const content = await foundry.applications.handlebars.renderTemplate( @@ -53,7 +76,11 @@ export class CelestopolRoll extends Roll { dialogContext ) - const title = `${game.i18n.localize("CELESTOPOL.Roll.title")} — ${game.i18n.localize(options.skillLabel ?? "")}` + const skillLocalized = game.i18n.localize(options.skillLabel ?? "") + const statLocalized = options.statLabel ? game.i18n.localize(options.statLabel) : null + const title = statLocalized + ? `${statLocalized} › ${skillLocalized}` + : skillLocalized const rollContext = await foundry.applications.api.DialogV2.wait({ window: { title }, @@ -62,9 +89,12 @@ export class CelestopolRoll extends Roll { buttons: [ { label: game.i18n.localize("CELESTOPOL.Roll.roll"), + icon: "fa-solid fa-dice", callback: (event, button) => { return Array.from(button.form.elements).reduce((obj, input) => { - if (input.name) obj[input.name] = input.value + if (input.name) { + obj[input.name] = input.type === "checkbox" ? input.checked : input.value + } return obj }, {}) }, @@ -80,11 +110,13 @@ export class CelestopolRoll extends Roll { const moonPhase = rollContext.moonPhase ?? "none" const moonConfig = SYSTEM.MOON_DICE_PHASES[moonPhase] ?? SYSTEM.MOON_DICE_PHASES.none const modifier = parseInt(rollContext.modifier ?? 0) || 0 - const woundMalus = options.woundMalus ?? 0 - const skillValue = Math.max(0, (options.skillValue ?? 0) + woundMalus) - const nbDice = Math.max(1, skillValue) + const aspectMod = parseInt(rollContext.aspectModifier ?? 0) || 0 + const useDestin = rollContext.useDestin === true || rollContext.useDestin === "true" + const destinDice = useDestin ? 2 : 0 + const skillValue = Math.max(0, baseSkillVal + woundMalus) + const nbDice = Math.max(1, skillValue) + destinDice const moonBonus = moonConfig.bonus ?? 0 - const totalModifier = moonBonus + modifier + const totalModifier = moonBonus + modifier + aspectMod const formula = totalModifier !== 0 ? `${nbDice}d6 + ${totalModifier}` @@ -97,6 +129,10 @@ export class CelestopolRoll extends Roll { moonPhase, moonBonus, modifier, + aspectMod, + useDestin, + destinDice, + nbDice, formula, rollMode: rollContext.visibility ?? "publicroll", } @@ -118,16 +154,26 @@ export class CelestopolRoll extends Roll { return roll } - /** Détermine succès/échec selon le total vs le seuil. */ + /** + * Détermine succès/échec et critiques selon la marge (total − seuil). + * - Marge ≥ 5 → succès critique + * - Marge ≥ 0 → succès + * - Marge ≤ −5 → échec critique + * - Marge < 0 → échec + */ computeResult() { const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0 if (threshold === 0) { this.options.resultType = "unknown" - } else if (this.total >= threshold) { - this.options.resultType = "success" - } else { - this.options.resultType = "failure" + this.options.margin = null + return } + const margin = this.total - threshold + this.options.margin = margin + if (margin >= 5) this.options.resultType = "critical-success" + else if (margin >= 0) this.options.resultType = "success" + else if (margin <= -5) this.options.resultType = "critical-failure" + else this.options.resultType = "failure" } /** @override */ @@ -137,44 +183,77 @@ export class CelestopolRoll extends Roll { } async _getChatCardData(isPrivate) { - const statLabel = this.options.statLabel - const skillLabel = this.options.skillLabel - const resultType = this.resultType - const diceResults = this.dice[0]?.results?.map(r => r.result) ?? [] + const statLabel = this.options.statLabel + const skillLabel = this.options.skillLabel + const resultType = this.resultType + const diceResults = this.dice[0]?.results?.map(r => r.result) ?? [] + const diceSum = diceResults.reduce((a, b) => a + b, 0) + const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0 + const margin = this.options.margin + const moonPhase = this.options.moonPhase ?? "none" + const moonSymbol = MOON_SYMBOLS[moonPhase] ?? "☽" + const woundMalus = this.options.woundMalus ?? 0 + const woundLevelId = this.options.woundLevel ?? 0 + const woundLabel = woundLevelId > 0 + ? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "") + : null + + // Classe CSS principale du résultat + const resultClassMap = { + "critical-success": "critical-success", + "success": "success", + "failure": "failure", + "critical-failure": "critical-failure", + "unknown": "", + } return { - cssClass: [SYSTEM.id, "dice-roll"].join(" "), - actorId: this.actorId, - actorName: this.actorName, - actorImg: this.actorImage, - statLabel: statLabel ? game.i18n.localize(statLabel) : "", - skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "", - formula: this.formula, - total: this.total, - resultType, - resultClass: resultType === "success" ? "success" : resultType === "failure" ? "failure" : "", - success: this.isSuccess, - failure: this.isFailure, - difficulty: this.options.difficulty, - difficultyLabel:game.i18n.localize(SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.label ?? ""), - moonPhase: this.options.moonPhase, - moonPhaseLabel: game.i18n.localize(SYSTEM.MOON_DICE_PHASES[this.options.moonPhase]?.label ?? ""), - moonBonus: this.moonBonus, - modifier: this.options.modifier, - isPrivate, - tooltip: isPrivate ? "" : await this.getTooltip(), + cssClass: [SYSTEM.id, "dice-roll"].join(" "), + actorId: this.actorId, + actorName: this.actorName, + actorImg: this.actorImage, + statLabel: statLabel ? game.i18n.localize(statLabel) : "", + skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "", + formula: this.formula, + total: this.total, + diceSum, diceResults, + resultType, + resultClass: resultClassMap[resultType] ?? "", + isSuccess: this.isSuccess, + isFailure: this.isFailure, + isCriticalSuccess: this.isCriticalSuccess, + isCriticalFailure: this.isCriticalFailure, + difficulty: this.options.difficulty, + difficultyLabel: game.i18n.localize(SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.label ?? ""), + difficultyValue: threshold, + margin, + marginAbs: margin !== null ? Math.abs(margin) : null, + marginAbove: margin !== null && margin >= 0, + moonPhase, + moonPhaseLabel: game.i18n.localize(SYSTEM.MOON_DICE_PHASES[moonPhase]?.label ?? ""), + moonSymbol, + moonBonus: this.moonBonus, + modifier: this.options.modifier, + aspectMod: this.options.aspectMod ?? 0, + useDestin: this.options.useDestin ?? false, + destinDice: this.options.destinDice ?? 0, + nbDice: this.options.nbDice ?? diceResults.length, + woundMalus, + woundLabel, + isPrivate, + tooltip: isPrivate ? "" : await this.getTooltip(), } } /** @override */ async toMessage(messageData = {}, { rollMode, create = true } = {}) { - return super.toMessage( - { - flavor: `${game.i18n.localize(this.skillLabel ?? "")}`, - ...messageData, - }, - { rollMode } - ) + const skillLocalized = this.skillLabel ? game.i18n.localize(this.skillLabel) : "" + const statLocalized = this.options.statLabel + ? game.i18n.localize(this.options.statLabel) : "" + const flavor = statLocalized + ? `${statLocalized} › ${skillLocalized}` + : `${skillLocalized}` + return super.toMessage({ flavor, ...messageData }, { rollMode }) } } diff --git a/module/models/character.mjs b/module/models/character.mjs index e23dfac..550869a 100644 --- a/module/models/character.mjs +++ b/module/models/character.mjs @@ -19,10 +19,18 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 8 }), }) - // Les 4 stats avec leurs compétences + // Les 4 stats avec leurs domaines const skillField = (label) => new fields.SchemaField({ label: new fields.StringField({ required: true, initial: label }), value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 8 }), + level1: new fields.BooleanField({ required: true, initial: false }), + level2: new fields.BooleanField({ required: true, initial: false }), + level3: new fields.BooleanField({ required: true, initial: false }), + level4: new fields.BooleanField({ required: true, initial: false }), + level5: new fields.BooleanField({ required: true, initial: false }), + level6: new fields.BooleanField({ required: true, initial: false }), + level7: new fields.BooleanField({ required: true, initial: false }), + level8: new fields.BooleanField({ required: true, initial: false }), }) const statField = (statId) => { @@ -85,9 +93,18 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel vision: persoAttrField(), }) - // Factions + // Factions - 9 checkboxes per faction (like wound tracks) const factionField = () => new fields.SchemaField({ value: new fields.NumberField({ ...reqInt, initial: 0 }), + level1: new fields.BooleanField({ required: true, initial: false }), + level2: new fields.BooleanField({ required: true, initial: false }), + level3: new fields.BooleanField({ required: true, initial: false }), + level4: new fields.BooleanField({ required: true, initial: false }), + level5: new fields.BooleanField({ required: true, initial: false }), + level6: new fields.BooleanField({ required: true, initial: false }), + level7: new fields.BooleanField({ required: true, initial: false }), + level8: new fields.BooleanField({ required: true, initial: false }), + level9: new fields.BooleanField({ required: true, initial: false }), }) schema.factions = new fields.SchemaField({ pinkerton: factionField(), @@ -150,9 +167,9 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel } /** - * Lance les dés pour une compétence donnée. + * Lance les dés pour un domaine donné. * @param {string} statId - Id de la stat (ame, corps, coeur, esprit) - * @param {string} skillId - Id de la compétence + * @param {string} skillId - Id du domaine */ async roll(statId, skillId) { const { CelestopolRoll } = await import("../documents/roll.mjs") @@ -169,6 +186,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel skillLabel: skill.label, skillValue: skill.value, woundMalus: this.getWoundMalus(), + woundLevel: this.blessures.lvl, moonPhase: this.prefs.moonPhase, difficulty: this.prefs.difficulty, }) diff --git a/module/models/items.mjs b/module/models/items.mjs index 6fea258..2be4c07 100644 --- a/module/models/items.mjs +++ b/module/models/items.mjs @@ -1,6 +1,6 @@ import { SYSTEM } from "../config/system.mjs" -/** Schéma partagé pour les bonus/malus par compétence (utilisé dans anomaly/aspect/attribute). */ +/** Schéma partagé pour les bonus/malus par domaine (utilisé dans anomaly/aspect/attribute). */ function skillScoresSchema() { const fields = foundry.data.fields const reqInt = { required: true, nullable: false, integer: true } diff --git a/styles/character.less b/styles/character.less index edecf4b..0a7213c 100644 --- a/styles/character.less +++ b/styles/character.less @@ -45,7 +45,7 @@ } } - // Stats × Compétences grid + // Stats × Domaines grid .stats-grid { display: grid; grid-template-columns: 1fr 1fr; diff --git a/styles/global.less b/styles/global.less index 1504a09..a7e98f9 100644 --- a/styles/global.less +++ b/styles/global.less @@ -157,8 +157,88 @@ } // ─── Tabs ──────────────────────────────────────────────────────────────── + // Updated: 1774698726 - Enhanced tab styling .sheet-tabs { + + // ─── Faction checkboxes ───────────────────────────────────────────────── + + .faction-checkboxes-container { + display: flex; + align-items: center; + gap: 8px; + pointer-events: auto !important; + } + + .faction-checkboxes { + display: inline-flex; + gap: 2px; + pointer-events: auto !important; + } + + .faction-checkbox-wrapper { + display: inline-block; + pointer-events: auto !important; + position: relative; + z-index: 101; + } + + .faction-checkbox { + width: 14px; + height: 14px; + cursor: pointer; + pointer-events: auto !important; + margin: 0; + padding: 0; + position: relative; + z-index: 100; + } + + .faction-checkbox:disabled { + cursor: default; + opacity: 0.7; + } + + .faction-count { + margin-left: 8px; + font-weight: bold; + color: var(--cel-orange); + min-width: 20px; + text-align: right; + } + + .faction-value-input { + width: 40px; + margin-left: 8px; + } + + // Fix pointer-events for faction checkboxes in table cells + .faction-row { + pointer-events: auto !important; + } + + .faction-row td { + pointer-events: auto !important; + } + + // Ensure labels are clickable + .faction-checkbox-wrapper label { + cursor: pointer; + pointer-events: auto !important; + display: inline-block; + } +} + +// Ensure table cells allow pointer events +.faction-row { + pointer-events: auto !important; +} + +.faction-row td { + pointer-events: auto !important; +} + +.sheet-tabs { display: flex; background: var(--cel-green-dark); padding: 0; @@ -167,29 +247,51 @@ border-bottom: 2px solid var(--cel-orange); .item { - padding: 6px 14px; - color: rgba(240,232,212,0.7); + padding: 8px 16px; + color: rgba(240,232,212,0.8); font-family: var(--cel-font-title); - font-size: 0.82em; + font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.07em; cursor: pointer; border-right: 1px solid rgba(196,154,26,0.2); - transition: color 0.2s, border-bottom-color 0.2s; + transition: all 0.2s ease; // Art Déco underline indicator border-bottom: 3px solid transparent; position: relative; + background: rgba(240,232,212,0.1); + margin: 0 1px; &:hover { color: var(--cel-orange-light); - background: rgba(196,154,26,0.07); + background: rgba(196,154,26,0.15); } &.active { - color: var(--cel-orange); - background: rgba(196,154,26,0.1); - border-bottom-color: var(--cel-orange); - font-weight: bold; + color: var(--cel-cream) !important; + background: linear-gradient(to bottom, var(--cel-orange), var(--cel-orange-light)) !important; + border-bottom: 3px solid var(--cel-accent) !important; + font-weight: bold !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; + transform: translateY(-1px) !important; + position: relative !important; + z-index: 2 !important; + border-right-color: transparent !important; + border: 2px solid red !important; // DEBUG: bordure rouge très visible + + // Art Déco triangle indicator + &::before { + content: '' !important; + position: absolute !important; + top: -5px !important; + left: 50% !important; + transform: translateX(-50%) !important; + width: 0 !important; + height: 0 !important; + border-left: 8px solid transparent !important; + border-right: 8px solid transparent !important; + border-bottom: 8px solid var(--cel-accent) !important; + } } } } diff --git a/styles/roll.less b/styles/roll.less index da8893f..111eaf8 100644 --- a/styles/roll.less +++ b/styles/roll.less @@ -1,22 +1,137 @@ // ─── Roll dialog ───────────────────────────────────────────────────────────── -.roll-dialog.celestopol { - padding: 8px 12px; +// DialogV2 sets roll-dialog class on the .application element, not on the form. +// All dialog content lives in .application.roll-dialog .dialog-content + +.application.roll-dialog .window-content { + padding: 0 !important; +} + +.application.roll-dialog .dialog-content { + padding: 0 !important; + display: block !important; +} + +.application.roll-dialog .roll-dialog-content { + padding: 10px 14px; font-family: var(--cel-font-body, "Palatino Linotype", serif); + min-width: 340px; + display: flex; + flex-direction: column; + gap: 0; - .roll-title { + // ── Bloc infos acteur ── + .roll-info-block { + background: var(--cel-green, #1b3828); + background-image: url("../assets/ui/fond_cadrille.jpg"); + background-blend-mode: soft-light; + border-bottom: 2px solid var(--cel-orange, #c49a1a); + padding: 10px 14px 12px; + margin: -10px -14px 14px; text-align: center; - font-family: var(--cel-font-title, "CopaseticNF", serif); - font-size: 1.1em; - color: var(--cel-orange, #c49a1a); - margin-bottom: 10px; - .separator { - margin: 0 6px; - color: var(--cel-border, #7a5c20); + .roll-actor { + font-family: var(--cel-font-title, "CopaseticNF", serif); + color: var(--cel-orange-light, #ddb84a); + font-size: 0.82em; + letter-spacing: 0.06em; + opacity: 0.85; + } + + .roll-skill-line { + font-family: var(--cel-font-title, "CopaseticNF", serif); + font-size: 1.2em; + color: var(--cel-cream, #f0e8d4); + margin-top: 1px; + + .stat-label { color: var(--cel-orange-light, #ddb84a); } + .sep { color: var(--cel-border, #7a5c20); margin: 0 4px; } + } + + .roll-dice-summary { + margin-top: 8px; + padding: 5px 10px; + background: rgba(0,0,0,0.25); + border-radius: 4px; + + .dice-breakdown { + display: flex; + align-items: baseline; + justify-content: center; + gap: 5px; + font-size: 0.85em; + color: var(--cel-cream, #f0e8d4); + + .dval, .nb-dice { + font-family: var(--cel-font-title, "CopaseticNF", serif); + font-size: 1.6em; + color: var(--cel-orange, #c49a1a); + font-weight: bold; + line-height: 1; + } + + .dlabel { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.8; } + .dminus { color: #e8a0a0; font-weight: bold; } + .deq { opacity: 0.6; } + .ddice { font-size: 1em; color: var(--cel-orange, #c49a1a); } + } + + .wound-info { + font-size: 0.75em; + color: #e8a0a0; + margin-top: 3px; + } } } + // ── Phases de lune ── + .moon-section { + display: flex; + flex-direction: column; + gap: 4px; + margin-bottom: 10px; + + .moon-section-label { + font-size: 0.75em; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--cel-orange, #c49a1a); + } + + .moon-phases { + display: flex; + flex-wrap: wrap; + gap: 4px; + + .moon-option { + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + padding: 4px 5px; + border-radius: 3px; + border: 1px solid transparent; + flex: 1; + min-width: 52px; + transition: all 0.15s; + + &:hover { + background: rgba(196,154,26,0.12); + border-color: var(--cel-border, #7a5c20); + } + &.active { + background: rgba(27,56,40,0.15); + border-color: var(--cel-orange, #c49a1a); + } + + .moon-symbol { font-size: 1.4em; line-height: 1; } + .moon-name { font-size: 0.6em; text-align: center; margin-top: 2px; color: #555; } + .moon-bonus { font-size: 0.7em; color: var(--cel-orange, #c49a1a); font-weight: bold; } + } + } + } + + // ── Champs de formulaire ── .form-group { display: flex; align-items: center; @@ -24,8 +139,8 @@ margin-bottom: 8px; label { - flex: 0 0 140px; - font-size: 0.85em; + flex: 0 0 130px; + font-size: 0.82em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--cel-orange, #c49a1a); @@ -35,23 +150,62 @@ flex: 1; border: 1px solid var(--cel-border, #7a5c20); border-radius: 2px; - padding: 2px 6px; - background: rgba(255,255,255,0.7); + padding: 3px 6px; + background: rgba(255,255,255,0.75); + font-family: inherit; + } + + &.form-row label { flex: 0 0 130px; } + } + + .form-two-col { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + margin-bottom: 8px; + + .form-group { + flex-direction: column; + align-items: stretch; + margin-bottom: 0; + + label { + flex: none; + margin-bottom: 2px; + } } } + .form-destin { + margin-bottom: 10px; + + .destin-label { + display: flex; + align-items: center; + gap: 6px; + cursor: pointer; + color: var(--cel-orange, #c49a1a); + font-size: 0.85em; + white-space: nowrap; + flex-wrap: nowrap; + + input[type="checkbox"] { width: 14px; height: 14px; flex-shrink: 0; } + .destin-icon { font-size: 1em; flex-shrink: 0; } + } + } + + // ── Prévisualisation ── .dice-preview { text-align: center; - font-size: 1em; - margin-top: 10px; - padding: 6px; + margin-top: 4px; + padding: 8px; background: rgba(27,56,40,0.08); border-radius: 3px; border: 1px solid var(--cel-green, #1b3828); - .nb-dice { + .preview-formula { font-family: var(--cel-font-title, "CopaseticNF", serif); - font-size: 1.5em; + font-size: 1.4em; color: var(--cel-orange, #c49a1a); font-weight: bold; } @@ -66,6 +220,7 @@ overflow: hidden; font-family: var(--cel-font-body, "Palatino Linotype", serif); + // ── En-tête ── .roll-header { display: flex; align-items: center; @@ -77,100 +232,183 @@ border-bottom: 2px solid var(--cel-orange, #c49a1a); .actor-img { - width: 36px; - height: 36px; + width: 40px; + height: 40px; object-fit: cover; border: 1px solid var(--cel-orange, #c49a1a); border-radius: 2px; box-shadow: inset 0 0 4px rgba(196,154,26,0.3); + flex-shrink: 0; } .roll-info { + flex: 1; display: flex; flex-direction: column; + gap: 1px; + .actor-name { font-family: var(--cel-font-title, "CopaseticNF", serif); color: var(--cel-orange, #c49a1a); font-weight: bold; letter-spacing: 0.05em; + font-size: 0.95em; } .skill-info { color: var(--cel-cream, #f0e8d4); - font-size: 0.8em; + font-size: 0.78em; font-style: italic; + .stat-lbl { color: var(--cel-orange-light, #ddb84a); } + .sep { margin: 0 2px; color: var(--cel-border, #7a5c20); } } - } - } - - .roll-details { - padding: 8px; - background: var(--cel-cream, #f0e8d4); - - .dice-results { - display: flex; - gap: 4px; - flex-wrap: wrap; - margin-bottom: 6px; - - .die.d6 { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border: 2px solid var(--cel-border, #7a5c20); - border-radius: 3px; - background: white; - font-weight: bold; - font-size: 1em; + .wound-info { + font-size: 0.72em; + color: #e8a0a0; } } - .bonus-line { - display: flex; - justify-content: space-between; - font-size: 0.85em; - color: #666; - padding: 1px 0; - } - - .roll-total-line { + .moon-badge { display: flex; + flex-direction: column; align-items: center; - gap: 8px; - margin-top: 6px; - padding-top: 4px; - border-top: 1px solid var(--cel-border, #7a5c20); + font-size: 0.75em; + .moon-sym { font-size: 1.3em; line-height: 1; } + .moon-bon { color: var(--cel-orange-light, #ddb84a); font-weight: bold; } + } + } - .total-label { - text-transform: uppercase; - font-size: 0.75em; + // ── Zone dés ── + .dice-zone { + display: flex; + flex-wrap: wrap; + gap: 5px; + padding: 8px 10px 4px; + background: var(--cel-cream, #f0e8d4); + justify-content: center; + + .die-face { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + border: 2px solid var(--cel-border, #7a5c20); + border-radius: 4px; + background: white; + font-weight: bold; + font-size: 1em; + font-family: var(--cel-font-title, "CopaseticNF", serif); + box-shadow: 1px 1px 2px rgba(0,0,0,0.15); + + &.max { + background: var(--cel-green, #1b3828); color: var(--cel-orange, #c49a1a); + border-color: var(--cel-orange, #c49a1a); } - - .total-value { - font-family: var(--cel-font-title, "CopaseticNF", serif); - font-size: 1.6em; - font-weight: bold; - color: var(--cel-orange, #c49a1a); - } - - .vs-difficulty { - font-size: 0.8em; - color: #888; + &.min { + background: #f9e8e8; + color: #a03030; + border-color: #c07070; } } } - .roll-result-banner { + // ── Formule ── + .formula-line { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: center; + gap: 3px; + padding: 4px 10px; + background: var(--cel-cream, #f0e8d4); + font-size: 0.85em; + color: #555; + + .fl-ndice { color: var(--cel-green, #1b3828); font-weight: bold; } + .fl-sum { font-weight: bold; } + .fl-total { + font-family: var(--cel-font-title, "CopaseticNF", serif); + font-size: 1.4em; + color: var(--cel-orange, #c49a1a); + font-weight: bold; + } + .fl-moon { color: #666; } + .fl-mod { color: #444; } + .fl-asp { color: var(--cel-orange, #c49a1a); } + .fl-sep { font-weight: bold; color: var(--cel-border, #7a5c20); margin: 0 2px; } + .fl-eq { color: #888; } + .fl-op { color: #888; } + } + + // ── Seuil et marge ── + .threshold-line { + display: flex; + align-items: center; + justify-content: center; + gap: 5px; + padding: 4px 10px 6px; + background: var(--cel-cream, #f0e8d4); + border-top: 1px solid rgba(122,92,32,0.25); + font-size: 0.82em; + + .vs-label { color: #888; text-transform: uppercase; font-size: 0.85em; } + .diff-label{ font-style: italic; color: var(--cel-green, #1b3828); } + .diff-val { color: #888; } + + .margin-badge { + margin-left: 6px; + padding: 1px 7px; + border-radius: 10px; + font-weight: bold; + font-size: 0.95em; + font-family: var(--cel-font-title, "CopaseticNF", serif); + + &.above { + background: var(--cel-green, #1b3828); + color: var(--cel-orange, #c49a1a); + border: 1px solid var(--cel-orange, #c49a1a); + } + &.below { + background: var(--cel-accent, #6b1e28); + color: #e8c8c8; + border: 1px solid #8b3e48; + } + } + } + + // ── Destin utilisé ── + .used-info { text-align: center; - padding: 5px 8px; + font-size: 0.78em; + color: var(--cel-orange, #c49a1a); + padding: 2px 8px; + background: var(--cel-cream-dark, #e0d4b8); + border-top: 1px dashed var(--cel-border, #7a5c20); + } + + // ── Bandeau résultat ── + .roll-result-banner { + display: flex; + flex-direction: column; + align-items: center; + padding: 6px 8px; font-family: var(--cel-font-title, "CopaseticNF", serif); - font-size: 1.1em; text-transform: uppercase; letter-spacing: 0.1em; - // Success: jade green with gold text — elegant + .result-icon { font-size: 0.9em; opacity: 0.8; } + .result-label { font-size: 1.15em; line-height: 1.2; } + .result-desc { + font-size: 0.65em; + letter-spacing: 0.12em; + margin-top: 1px; + opacity: 0.85; + text-transform: none; + font-style: italic; + font-family: var(--cel-font-body, serif); + } + &.success { background-color: var(--cel-green, #1b3828); background-image: url("../assets/ui/fond_cadrille.jpg"); @@ -178,16 +416,26 @@ color: var(--cel-orange, #c49a1a); } - // Failure: deep burgundy — dark and grave + &.critical-success { + background: linear-gradient(135deg, #1b3828, #2c5a3f); + background-image: url("../assets/ui/fond_cadrille.jpg"); + background-blend-mode: soft-light; + color: var(--cel-orange-light, #ddb84a); + border-top: 2px solid var(--cel-orange, #c49a1a); + border-bottom: 2px solid var(--cel-orange, #c49a1a); + text-shadow: 0 0 8px rgba(196,154,26,0.5); + } + &.failure { background: var(--cel-accent, #6b1e28); color: #e8c8c8; } - .critical { - display: block; - font-size: 0.75em; - letter-spacing: 0.12em; + &.critical-failure { + background: linear-gradient(135deg, #3d0f18, #6b1e28); + color: #f0c8c8; + border-top: 2px solid #8b3e48; + border-bottom: 2px solid #8b3e48; } } } diff --git a/system.json b/system.json index 1ac868c..e77b9c5 100644 --- a/system.json +++ b/system.json @@ -24,7 +24,7 @@ "verified": "13" }, "esmodules": ["fvtt-celestopol.mjs"], - "styles": ["css/fvtt-celestopol.css"], + "styles": ["css/fvtt-celestopol.css?v=1774698726"], "languages": [ { "lang": "fr", diff --git a/templates/anomaly.hbs b/templates/anomaly.hbs index 59f1647..1ccbae8 100644 --- a/templates/anomaly.hbs +++ b/templates/anomaly.hbs @@ -1,6 +1,6 @@
-
+
{{item.name}}
diff --git a/templates/aspect.hbs b/templates/aspect.hbs index e56d54c..178df47 100644 --- a/templates/aspect.hbs +++ b/templates/aspect.hbs @@ -1,6 +1,6 @@
-
+
{{item.name}}
diff --git a/templates/attribute.hbs b/templates/attribute.hbs index e9e4f66..7b0f58c 100644 --- a/templates/attribute.hbs +++ b/templates/attribute.hbs @@ -1,6 +1,6 @@
-
+
{{item.name}}
diff --git a/templates/character-blessures.hbs b/templates/character-blessures.hbs index 2d52356..4199fc0 100644 --- a/templates/character-blessures.hbs +++ b/templates/character-blessures.hbs @@ -12,7 +12,10 @@
+ {{#unless ../isEditable}}disabled{{/unless}} + class="wound-checkbox" + data-track="blessures" + data-index="{{idx}}">
{{/each}} @@ -37,7 +40,10 @@
+ {{#unless ../isEditable}}disabled{{/unless}} + class="wound-checkbox" + data-track="destin" + data-index="{{@index}}">
{{/each}}
@@ -61,7 +67,10 @@
+ {{#unless ../isEditable}}disabled{{/unless}} + class="wound-checkbox" + data-track="spleen" + data-index="{{@index}}">
{{/each}}
diff --git a/templates/character-competences.hbs b/templates/character-competences.hbs index e32aa74..7d5ec30 100644 --- a/templates/character-competences.hbs +++ b/templates/character-competences.hbs @@ -1,5 +1,5 @@
- {{!-- Grille des 4 stats × 4 compétences --}} + {{!-- Grille des 4 stats × 4 domaines --}}
{{#each stats as |stat statId|}}
@@ -8,26 +8,50 @@
{{#if ../isEditMode}} - + {{else}} - {{lookup ../system.stats statId 'res'}} + {{lookup (lookup ../system.stats statId) 'res'}} {{/if}}
{{#each (lookup ../skills statId) as |skill skillId|}} -
+ {{#if @root.isEditMode}} +
{{localize skill.label}} - {{#if ../isEditMode}} - - {{else}} - {{lookup (lookup ../system.stats statId) skillId 'value'}} - {{/if}} +
+
+ {{#each (array 1 2 3 4 5 6 7 8) as |level|}} + + {{/each}} +
+
+
+ {{else}} +
+ {{localize skill.label}} +
+
+ {{#each (array 1 2 3 4 5 6 7 8) as |level|}} + + {{/each}} +
+
+ {{lookup (lookup @root.system.stats statId) skillId 'value'}} +
+ {{/if}} {{/each}}
diff --git a/templates/character-factions.hbs b/templates/character-factions.hbs index 2e72717..7868b95 100644 --- a/templates/character-factions.hbs +++ b/templates/character-factions.hbs @@ -8,21 +8,44 @@ {{#each factions as |faction factionId|}} - + {{localize faction.label}} - {{#if ../isEditMode}} - - {{else}} - {{lookup ../system.factions factionId 'value'}} - {{/if}} +
+
+ {{#each (array 1 2 3 4 5 6 7 8 9) as |level|}} + {{#if @root.isEditMode}} + + {{else}} + + {{/if}} + {{/each}} +
+ + {{#if ../isEditMode}} + + {{else}} + {{lookup (lookup ../system.factions factionId) 'value'}} + {{/if}} + +
{{/each}} {{!-- Factions personnalisées --}} - + {{#if isEditMode}} - {{#if isEditMode}} - - {{else}} - {{system.factions.perso1.value}} - {{/if}} +
+
+ {{#each (array 1 2 3 4 5 6 7 8 9) as |level|}} + {{#if ../isEditMode}} + + {{else}} + + {{/if}} + {{/each}} +
+ + {{#if ../isEditMode}} + + {{else}} + {{system.factions.perso1.value}} + {{/if}} + +
- + {{#if isEditMode}} - {{#if isEditMode}} - - {{else}} - {{system.factions.perso2.value}} - {{/if}} +
+
+ {{#each (array 1 2 3 4 5 6 7 8 9) as |level|}} + {{#if ../isEditMode}} + + {{else}} + + {{/if}} + {{/each}} +
+ + {{#if ../isEditMode}} + + {{else}} + {{system.factions.perso2.value}} + {{/if}} + +
diff --git a/templates/character-main.hbs b/templates/character-main.hbs index b86d5be..16ec410 100644 --- a/templates/character-main.hbs +++ b/templates/character-main.hbs @@ -1,6 +1,6 @@
-
+
{{actor.name}}
diff --git a/templates/chat-message.hbs b/templates/chat-message.hbs index 188ca83..d48ad7f 100644 --- a/templates/chat-message.hbs +++ b/templates/chat-message.hbs @@ -1,49 +1,88 @@
+ + {{!-- En-tête : acteur + domaine --}}
{{#if actorImg}} {{actorName}} {{/if}}
{{actorName}} - {{statLabel}} › {{skillLabel}} + + {{#if statLabel}}{{statLabel}}{{/if}} + {{skillLabel}} + + {{#if woundLabel}} + ⚠ {{woundLabel}} + {{/if}} +
+
+ {{moonSymbol}} + {{#if moonBonus}}+{{moonBonus}}{{/if}}
-
-
- {{#each diceResults as |die|}} - {{die}} - {{/each}} -
+ {{!-- Dés --}} +
+ {{#each diceResults as |die|}} + {{die}} + {{/each}} +
+ {{!-- Formule détaillée --}} +
+ {{nbDice}}d6 + = + {{diceSum}} {{#if moonBonus}} -
- {{localize "CELESTOPOL.Roll.moonBonus"}} ({{moonPhaseLabel}}) : - +{{moonBonus}} -
+ + + {{moonSymbol}} {{moonBonus}} {{/if}} - {{#if modifier}} -
- {{localize "CELESTOPOL.Roll.modifier"}} : - {{#if (gt modifier 0)}}+{{/if}}{{modifier}} -
+ {{#if (gt modifier 0)}}+{{else}}−{{/if}} + {{abs modifier}} {{/if}} - -
- {{localize "CELESTOPOL.Roll.total"}} - {{total}} - vs {{difficultyLabel}} -
+ {{#if aspectMod}} + {{#if (gt aspectMod 0)}}+{{else}}−{{/if}} + ✦ {{abs aspectMod}} + {{/if}} + = + {{total}}
+ {{!-- Seuil et marge --}} +
+ vs + {{difficultyLabel}} + ({{difficultyValue}}) + {{#if margin}} + + {{#if marginAbove}}+{{/if}}{{margin}} + + {{/if}} +
+ + {{!-- Infos bonus (Destin utilisé) --}} + {{#if useDestin}} +
+ ⭐ {{localize "CELESTOPOL.Roll.usedDestin"}} +
+ {{/if}} + + {{!-- Bandeau résultat --}}
- {{#if success}} - {{localize "CELESTOPOL.Roll.success"}} - {{#if criticalSuccess}}{{localize "CELESTOPOL.Roll.criticalSuccess"}}{{/if}} - {{else}} - {{localize "CELESTOPOL.Roll.failure"}} - {{#if criticalFailure}}{{localize "CELESTOPOL.Roll.criticalFailure"}}{{/if}} + {{#if isCriticalSuccess}} + + {{localize "CELESTOPOL.Roll.criticalSuccess"}} + {{localize "CELESTOPOL.Roll.criticalSuccessDesc"}} + {{else if isSuccess}} + {{localize "CELESTOPOL.Roll.success"}} + {{else if isCriticalFailure}} + + {{localize "CELESTOPOL.Roll.criticalFailure"}} + {{localize "CELESTOPOL.Roll.criticalFailureDesc"}} + {{else if isFailure}} + {{localize "CELESTOPOL.Roll.failure"}} {{/if}}
+
diff --git a/templates/equipment.hbs b/templates/equipment.hbs index 0ef8711..b7e3ae9 100644 --- a/templates/equipment.hbs +++ b/templates/equipment.hbs @@ -1,6 +1,6 @@
-
+
{{item.name}}
diff --git a/templates/npc-main.hbs b/templates/npc-main.hbs index 785d171..38f148e 100644 --- a/templates/npc-main.hbs +++ b/templates/npc-main.hbs @@ -1,6 +1,6 @@
-
+
{{actor.name}}
diff --git a/templates/partials/item-scores.hbs b/templates/partials/item-scores.hbs index 71e4f57..458da3d 100644 --- a/templates/partials/item-scores.hbs +++ b/templates/partials/item-scores.hbs @@ -1,4 +1,4 @@ -{{!-- Template partagé pour les scores bonus/malus d'un item par compétence --}} +{{!-- Template partagé pour les scores bonus/malus d'un item par domaine --}}
{{localize "CELESTOPOL.Item.scores"}} diff --git a/templates/roll-dialog.hbs b/templates/roll-dialog.hbs index 3c26e82..0c376fe 100644 --- a/templates/roll-dialog.hbs +++ b/templates/roll-dialog.hbs @@ -1,21 +1,48 @@ - -
- {{#if statLabel}}{{localize statLabel}}{{/if}} - {{localize skillLabel}} +
+ + {{!-- Info bloc : acteur + domaine --}} +
+
{{actorName}}
+
+ {{#if statLabel}}{{localize statLabel}}{{/if}} + {{localize skillLabel}} +
+ +
+
+ {{skillValue}} + {{localize "CELESTOPOL.Roll.nbDiceBase"}} + {{#if woundMalus}} + − {{abs woundMalus}} + {{localize "CELESTOPOL.Roll.woundMalus"}} + {{/if}} + = + {{nbDiceBase}} + d6 +
+ {{#if woundLabel}} +
⚠ {{woundLabel}}
+ {{/if}} +
-
- - + {{phase.symbol}} + {{localize phase.label}} + {{#if phase.bonus}}+{{phase.bonus}}{{else}}+0{{/if}} + {{/each}} - +
-
+ {{!-- Seuil de difficulté --}} +
-
- - + {{!-- Modificateur & Aspect en ligne --}} +
+
+ + +
+
+ + +
-
- {{localize "CELESTOPOL.Roll.nbDice"}} - {{nbDice}} - d6 + {{!-- Destin --}} +
+
- + + {{!-- Prévisualisation dynamique --}} +
+ {{nbDiceBase}}d6 +
+ +
+ +