feat(experience): atout Socle de compétences — +1D gratuit dans le dialogue d'apprentissage

This commit is contained in:
2026-08-09 07:47:56 +02:00
parent 0ef0de45ff
commit 9ab2c6b01a
4 changed files with 40 additions and 1 deletions
+35 -1
View File
@@ -145,6 +145,20 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
return { total: items.length, byLevel };
}
/**
* Bonus de l'atout "Socle de compétences" (règles p. 112).
* Rend le niveau (1-3) de l'atout possédé par le personnage, ou 0.
* @returns {number}
*/
#socleCompetencesBonus() {
const item = (this.#actor.itemTypes?.ability || []).find(i =>
i.system?.type === "character"
&& (i.name || "").trim().toLowerCase() === "socle de compétences".toLowerCase()
);
if (!item) return 0;
return Math.min(3, Math.max(1, parseInt(item.system?.level?.value, 10) || 1));
}
async _onRender() {
this.element.dataset.actorId = this.#actor.id;
for (const inp of this.element.querySelectorAll("[data-roll]")) {
@@ -201,13 +215,16 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
label = game.i18n.localize(VermineExperience.skillLevelLabelKey(targetLevel));
const diff = VermineExperience.skillLearningDifficulty(targetLevel);
const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
// Atout "Socle de compétences" : 1D gratuit si la Compétence est déjà possédée.
const socleBonus = current >= 1 ? this.#socleCompetencesBonus() : 0;
return {
type,
key,
targetLevel,
difficulty: domain ? Math.max(3, diff - 2) : diff,
handicap: VermineExperience.skillLearningHandicap(targetLevel, rarity),
poolModifier: 0,
poolModifier: socleBonus,
socleBonus,
label,
targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
};
@@ -363,6 +380,15 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
set("#diff-value", c.difficulty);
set("#handicap-value", c.handicap);
set("#required-value", 1 + c.handicap);
const socleHint = this.#el.querySelector("#socle-bonus-hint");
if (socleHint) {
if (type === "skill" && c.socleBonus > 0) {
socleHint.style.display = "";
socleHint.textContent = game.i18n.format("VERMINE.socle_competences_hint", { level: c.socleBonus });
} else {
socleHint.style.display = "none";
}
}
const pool = Math.max(0, this.#getDiceSpent() + c.poolModifier);
let poolText = `${pool}D${c.poolModifier ? ` (${c.poolModifier >= 0 ? "+" : ""}${c.poolModifier}D)` : ""}`;
if (c.adaptedDie) poolText += " — " + game.i18n.localize("VERMINE.adapted_die_x2");
@@ -496,6 +522,14 @@ export default class LearningDialog extends HandlebarsApplicationMixin(foundry.a
if ((roll._total ?? 0) >= required) {
await this.#applySuccess(c, specialtyName, roll._total ?? 0);
}
// Atout "Socle de compétences" : rappeler le bonus conféré aux camarades
// du Groupe qui apprennent une Compétence possédée par le personnage.
if (c.socleBonus > 0) {
await ChatMessage.create({
content: game.i18n.format("VERMINE.socle_competences_message", { level: c.socleBonus }),
speaker: ChatMessage.getSpeaker({ actor })
});
}
// Les Dés d'Expérience sont perdus que le jet soit réussi ou raté.
await actor.update({ "system.experience.dice": Math.max(0, (actor.system.experience.dice || 0) - spent) });
this.close();