diff --git a/lang/en.json b/lang/en.json index 461653f..157a274 100644 --- a/lang/en.json +++ b/lang/en.json @@ -333,6 +333,9 @@ "CharacterNamePlaceholder": "Character Name", "ProfilePlaceholder": "Profile", "none": "None", + "all_characters": "All characters", + "no_group": "No group", + "include_in_phase": "Include in this phase", "bonuses": "Bonuses", "choose_ability": "Choose Ability", "choose_skill": "Choose Skill", @@ -469,7 +472,7 @@ "mode_survie": "Survival", "mode_cauchemar": "Nightmare", "mode_apocalypse": "Apocalypse", - "evolution_buy_hint": "The purchase consumes and erases all accumulated Evolution dice. Limits: Survival 5D, Nightmare 6D, Apocalypse 7D total. Level 3/4 Mutations are forbidden in Survival; only one level 4 Mutation.", + "evolution_buy_hint": "The purchase consumes a number of Evolution dice equal to the chosen level (the remainder is kept). Limits: Survival 5D, Nightmare 6D, Apocalypse 7D total. Level 3/4 Mutations are forbidden in Survival; only one level 4 Mutation.", "buy_evolution_done": "Adaptation/Mutation acquired. Evolution dice erased.", "error_evolution_no_name": "Provide a name for the Adaptation/Mutation.", "error_evolution_invalid_level": "Invalid level (1 to 4).", diff --git a/lang/fr.json b/lang/fr.json index 5a657e9..84c1a7e 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -365,6 +365,9 @@ "choose_ability": "Choisissez une caractéristique", "choose_skill": "Choisissez une compétence", "none": "Aucun", + "all_characters": "Tous les personnages", + "no_group": "Sans groupe", + "include_in_phase": "Inclure dans cette phase", "total": "Total", "bonuses": "Bonus", "handicap": "Handicap", @@ -452,7 +455,7 @@ "mode_survie": "Survie", "mode_cauchemar": "Cauchemar", "mode_apocalypse": "Apocalypse", - "evolution_buy_hint": "L'achat consomme et efface tous les Dés d'Évolution accumulés. Limites : Survie 5D, Cauchemar 6D, Apocalypse 7D au total. Mutations 3D/4D interdites en Mode Survie ; une seule Mutation à 4D.", + "evolution_buy_hint": "L'achat consomme un nombre de Dés d'Évolution égal au niveau choisi (le reste est conservé). Limites : Survie 5D, Cauchemar 6D, Apocalypse 7D au total. Mutations 3D/4D interdites en Mode Survie ; une seule Mutation à 4D.", "buy_evolution_done": "Adaptation/Mutation acquise. Dés d'Évolution effacés.", "error_evolution_no_name": "Indiquez un nom pour l'Adaptation/Mutation.", "error_evolution_invalid_level": "Niveau invalide (1 à 4).", diff --git a/module/system/dialogs/buyEvolutionDialog.mjs b/module/system/dialogs/buyEvolutionDialog.mjs index 3b8c9c5..c1f035f 100644 --- a/module/system/dialogs/buyEvolutionDialog.mjs +++ b/module/system/dialogs/buyEvolutionDialog.mjs @@ -5,8 +5,8 @@ const { HandlebarsApplicationMixin } = foundry.applications.api; /** * Dialogue d'achat d'une Adaptation/Mutation avec les Dés d'Évolution * accumulés. Vérifie le coût (niveau 1-4), la limite du Mode de jeu et les - * restrictions sur les Mutations ; en cas de succès, crée l'item et efface - * les Dés d'Évolution accumulés. + * restrictions sur les Mutations ; en cas de succès, crée l'item et consomme + * un nombre de Dés d'Évolution égal au niveau choisi. */ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) { @@ -61,7 +61,7 @@ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(found }; const levels = []; for (let value = 1; value <= 4; value++) { - levels.push({ value, cost: value }); + levels.push({ value }); } return { evolutionDice: actor.system?.experience?.evolution || 0, @@ -73,6 +73,18 @@ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(found }; } + async _onRender() { + this.element.querySelectorAll("[data-roll]").forEach(inp => inp.addEventListener("change", () => this.#updateUI())); + this.#updateUI(); + } + + /** Met à jour le total des niveaux « après achat » selon le niveau sélectionné. */ + #updateUI() { + const cost = parseInt(this.element.querySelector("#evolution-level")?.value, 10) || 0; + const el = this.element.querySelector("#evolution-total-value"); + if (el) el.textContent = `${VermineExperience.evolutionTotalLevels(this.#actor) + cost}D`; + } + static async #onCancel(event, target) { this.close(); } @@ -102,12 +114,12 @@ export default class BuyEvolutionDialog extends HandlebarsApplicationMixin(found mutation } }]); - // N'efface les Dés d'Évolution que si l'item a bien été créé. + // Ne consomme que le coût du niveau choisi (Règles p. 116-117 : coût = niveau). if (!created || created.length === 0) { ui.notifications.warn(game.i18n.localize("VERMINE.error_evolution_create")); return; } - await actor.update({ "system.experience.evolution": 0 }); + await actor.update({ "system.experience.evolution": Math.max(0, (actor.system?.experience?.evolution || 0) - cost) }); ui.notifications.info(game.i18n.localize("VERMINE.buy_evolution_done")); this.close(); diff --git a/module/system/dialogs/experiencePhaseDialog.mjs b/module/system/dialogs/experiencePhaseDialog.mjs index ef60b48..206d597 100644 --- a/module/system/dialogs/experiencePhaseDialog.mjs +++ b/module/system/dialogs/experiencePhaseDialog.mjs @@ -48,13 +48,18 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo if (!memberOf.has(id)) memberOf.set(id, g.id); } } + const groupById = new Map(groups.map(g => [g.id, g])); return { - characters: characters.map(a => ({ - id: a.id, - name: a.name, - dice: a.system?.experience?.dice || 0, - groupId: memberOf.get(a.id) || "" - })), + characters: characters.map(a => { + const groupId = memberOf.get(a.id) || ""; + return { + id: a.id, + name: a.name, + dice: a.system?.experience?.dice || 0, + groupId, + groupName: groupId ? groupById.get(groupId)?.name : "" + }; + }), groups: groups.map(a => ({ id: a.id, name: a.name, @@ -72,6 +77,9 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo async _onRender(context, options) { this.element.querySelectorAll("[data-roll]").forEach(inp => inp.addEventListener("change", () => this.#updateUI())); this.element.querySelector("#group-select")?.addEventListener("change", () => this.#updateGroupFilter()); + this.element.querySelectorAll("[name^='include-']").forEach(cb => + cb.addEventListener("click", event => event.stopPropagation()) + ); this.#updateUI(); this.#updateGroupFilter(); } @@ -84,11 +92,16 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo if (el) el.textContent = `${danger + discoveries + duration}D`; } - /** Filtre les personnages selon le groupe sélectionné (vide = tous). */ + /** Filtre les personnages selon le groupe sélectionné (vide = tous, "none" = sans groupe). */ #updateGroupFilter() { const groupId = this.element.querySelector("#group-select")?.value || ""; for (const row of this.element.querySelectorAll("[data-character-id]")) { - const show = !groupId || row.dataset.groupId === groupId; + const rowGroupId = row.dataset.groupId || ""; + const show = groupId === "" + ? true + : groupId === "none" + ? rowGroupId === "" + : rowGroupId === groupId; row.hidden = !show; } } @@ -102,8 +115,8 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo + (parseInt(this.element.querySelector("#collective-discoveries")?.value, 10) || 0) + (parseInt(this.element.querySelector("#collective-duration")?.value, 10) || 0); - const groupId = this.element.querySelector("#group-select")?.value || null; - if (groupId) { + const groupId = this.element.querySelector("#group-select")?.value || ""; + if (groupId && groupId !== "none") { const group = game.actors.get(groupId); if (group && collective > 0) { const current = group.system?.experience?.dice || 0; @@ -121,8 +134,11 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo const results = []; for (const row of this.element.querySelectorAll("[data-character-id]")) { - // Quand un groupe précis est choisi, ne récompenser que ses membres. - if (groupId && row.dataset.groupId !== groupId) continue; + // Filtre par groupe sélectionné (vide = tous, "none" = sans groupe) + const rowGroupId = row.dataset.groupId || ""; + if (groupId === "none" ? rowGroupId !== "" : groupId !== "" && rowGroupId !== groupId) continue; + // Case « Inclure » décochée → personnage exclu de cette phase + if (!row.querySelector(`[name="include-${row.dataset.characterId}"]`)?.checked) continue; const id = row.dataset.characterId; const actor = game.actors.get(id); if (!actor) continue; @@ -164,7 +180,7 @@ export default class ExperiencePhaseDialog extends HandlebarsApplicationMixin(fo } const lines = [`${game.i18n.localize("VERMINE.experience_phase")} +1`]; - if (groupId) { + if (groupId && groupId !== "none") { const group = game.actors.get(groupId); lines.push(`${group?.name ?? ""} (${game.i18n.localize("VERMINE.collective")}): +${collective}D`); } diff --git a/templates/dialogs/buy-evolution-dialog.hbs b/templates/dialogs/buy-evolution-dialog.hbs index 253ab27..7591779 100644 --- a/templates/dialogs/buy-evolution-dialog.hbs +++ b/templates/dialogs/buy-evolution-dialog.hbs @@ -15,7 +15,7 @@ diff --git a/templates/dialogs/experience-phase-dialog.hbs b/templates/dialogs/experience-phase-dialog.hbs index b638d01..e94741b 100644 --- a/templates/dialogs/experience-phase-dialog.hbs +++ b/templates/dialogs/experience-phase-dialog.hbs @@ -36,7 +36,8 @@
+ {{ this.name }} - {{ localize 'VERMINE.experience_dice' }}: {{ this.dice }}D + + {{#if this.groupName}}[{{ this.groupName }}]{{ else }}[{{ localize 'VERMINE.no_group' }}]{{/if}} + {{ localize 'VERMINE.experience_dice' }}: {{ this.dice }}D +