feat(traits): phase 1 — réparations (Bien conçu, Pratique, Coque réparable)
- Coque : épuisée stockée {value:0, max:n} au lieu de null → réparable ; nouveau mode « Réparer la coque » (difficulté = coque.max) dans le dialogue ; affichage (0/n) sur la fiche
- Bien conçu : Réussite automatique en réparation classique (restauration complète) ; +1 Fiabilité à la 1ère réparation de fortune réussie (compteur traits.bien_conçu.value)
- Pratique : Handicap +I sur toutes les réparations (required +1, note sur la carte)
- i18n fr/en (9 clés)
- TRAITS_PLAN.md : suivi du plan d'automatisation des Traits
This commit is contained in:
@@ -81,8 +81,10 @@ export class VermineReliability {
|
||||
traitsNotes.push(game.i18n.localize('VERMINE.reliability_etanche'));
|
||||
}
|
||||
|
||||
// Trait Coque (n) : ignore les (n) premiers Dommages puis se perd.
|
||||
// Trait Coque (n) : ignore les (n) premiers Dommages puis s'épuise.
|
||||
// La coque épuisée est stockée { value: 0, max: n } : réparable (Phase 1).
|
||||
let coqueValue = parseInt(traits.coque?.value ?? 0, 10) || 0;
|
||||
const coqueInit = parseInt(traits.coque?.max ?? traits.coque?.value ?? 0, 10) || 0;
|
||||
const updates = {};
|
||||
if (coqueValue > 0 && dommages > 0) {
|
||||
const absorbed = Math.min(coqueValue, dommages);
|
||||
@@ -92,8 +94,8 @@ export class VermineReliability {
|
||||
updates['system.traits.coque.value'] = coqueValue;
|
||||
traitsNotes.push(game.i18n.format('VERMINE.reliability_coque_absorbed', { absorbed, remaining: coqueValue }));
|
||||
} else {
|
||||
updates['system.traits.coque'] = null;
|
||||
traitsNotes.push(game.i18n.localize('VERMINE.reliability_coque_lost'));
|
||||
updates['system.traits.coque'] = { value: 0, max: coqueInit };
|
||||
traitsNotes.push(game.i18n.format('VERMINE.reliability_coque_lost', { max: coqueInit }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,10 +156,10 @@ export class VermineReliability {
|
||||
}
|
||||
|
||||
/**
|
||||
* Réparation classique ou de fortune.
|
||||
* Réparation classique, de fortune ou de coque.
|
||||
* @param {Item} item
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.mode "classique" | "fortune"
|
||||
* @param {string} opts.mode "classique" | "fortune" | "coque"
|
||||
* @param {string} opts.skill Compétence de réparation classique (clé de skills)
|
||||
* @returns {Promise<Object|null>}
|
||||
*/
|
||||
@@ -168,13 +170,25 @@ export class VermineReliability {
|
||||
return null;
|
||||
}
|
||||
|
||||
const traits = item.system?.traits ?? {};
|
||||
const current = parseInt(item.system?.reliability ?? 0, 10) || 0;
|
||||
const damagesValue = parseInt(item.system?.damages?.value ?? 0, 10) || 0;
|
||||
const handicap = this.panneHandicap(item);
|
||||
|
||||
// Trait Pratique : +1 Handicap sur toutes les réparations.
|
||||
const pratique = !!traits.pratique;
|
||||
const pratiqueHandicap = pratique ? 1 : 0;
|
||||
const pratiqueNote = pratique ? game.i18n.localize('VERMINE.reliability_pratique') : '';
|
||||
|
||||
const isCoque = mode === 'coque';
|
||||
const coqueMax = isCoque ? parseInt(traits.coque?.max ?? 0, 10) || 0 : 0;
|
||||
|
||||
// Difficulté : Fiabilité d'origine (≈ actuelle + Dommages subis) pour la
|
||||
// réparation classique ; Fiabilité actuelle pour la réparation de fortune.
|
||||
const difficulty = mode === 'classique' ? Math.min(10, current + damagesValue) : current;
|
||||
// réparation classique ; Fiabilité actuelle pour la réparation de fortune ;
|
||||
// valeur maximale du Trait pour la réparation de coque.
|
||||
let difficulty = current;
|
||||
if (isCoque) difficulty = coqueMax;
|
||||
else if (mode === 'classique') difficulty = Math.min(10, current + damagesValue);
|
||||
|
||||
const precision = parseInt(actor.system?.abilities?.precision?.value ?? 0, 10) || 0;
|
||||
const skillValue = mode === 'fortune'
|
||||
@@ -198,27 +212,57 @@ export class VermineReliability {
|
||||
}
|
||||
}
|
||||
|
||||
const roll = new Roll(`${NoD + fortuneBonus}d10cs>=${difficulty}`);
|
||||
await roll.evaluate();
|
||||
await VermineUtils.showDiceSoNice(roll);
|
||||
// Trait Bien conçu : Réussite automatique sur les Réparations classiques.
|
||||
const bienConcu = !!traits.bien_conçu;
|
||||
const autoSuccess = bienConcu && mode === 'classique';
|
||||
let bienConcuNote = '';
|
||||
|
||||
const results = (roll.dice ?? []).flatMap((d) => (d.results ?? []).map((r) => ({
|
||||
value: r.result ?? 0,
|
||||
isSuccess: r.success,
|
||||
})));
|
||||
const successes = roll._total ?? 0;
|
||||
const required = 1 + handicap + fortuneHandicap;
|
||||
const success = successes >= required;
|
||||
// Trait Bien conçu (fortune) : la 1ʳᵉ réparation de fortune réussie rend
|
||||
// 1 point de Fiabilité (compteur traits.bien_conçu.value).
|
||||
const bienConcuUsed = bienConcu && mode === 'fortune' && (parseInt(traits.bien_conçu?.value ?? 0, 10) || 0) > 0;
|
||||
const bienConcuBonus = bienConcu && mode === 'fortune' && !bienConcuUsed;
|
||||
|
||||
// Réparation de coque : pas de panne à réparer, Difficulté = max du Trait.
|
||||
const required = 1 + (isCoque ? 0 : handicap) + (isCoque ? 0 : fortuneHandicap) + pratiqueHandicap;
|
||||
|
||||
const roll = autoSuccess ? null : new Roll(`${NoD + fortuneBonus}d10cs>=${difficulty}`);
|
||||
if (roll) {
|
||||
await roll.evaluate();
|
||||
await VermineUtils.showDiceSoNice(roll);
|
||||
}
|
||||
|
||||
const results = roll
|
||||
? (roll.dice ?? []).flatMap((d) => (d.results ?? []).map((r) => ({
|
||||
value: r.result ?? 0,
|
||||
isSuccess: r.success,
|
||||
})))
|
||||
: [];
|
||||
const successes = roll?._total ?? required;
|
||||
const success = autoSuccess || successes >= required;
|
||||
|
||||
let gained = 0;
|
||||
let fortuneFixed = false;
|
||||
let coqueRestored = false;
|
||||
let newReliability = null;
|
||||
let resultLabel = '';
|
||||
if (success) {
|
||||
if (mode === 'classique') {
|
||||
gained = successes;
|
||||
if (isCoque) {
|
||||
coqueRestored = true;
|
||||
resultLabel = game.i18n.format('VERMINE.reliability_coque_restored', { max: coqueMax });
|
||||
} else if (mode === 'classique') {
|
||||
gained = autoSuccess ? damagesValue : successes;
|
||||
newReliability = Math.min(10, current + gained);
|
||||
resultLabel = game.i18n.format('VERMINE.reliability_gained', { gained });
|
||||
if (autoSuccess) {
|
||||
bienConcuNote = game.i18n.localize('VERMINE.reliability_bien_concu');
|
||||
}
|
||||
} else {
|
||||
fortuneFixed = true;
|
||||
if (bienConcuBonus) {
|
||||
gained = 1;
|
||||
newReliability = Math.min(10, current + 1);
|
||||
bienConcuNote = game.i18n.localize('VERMINE.reliability_bien_concu_fortune');
|
||||
}
|
||||
resultLabel = game.i18n.localize('VERMINE.reliability_fixed');
|
||||
}
|
||||
} else {
|
||||
@@ -226,31 +270,44 @@ export class VermineReliability {
|
||||
}
|
||||
|
||||
const updates = {};
|
||||
if (mode === 'classique' && gained > 0) {
|
||||
updates['system.reliability'] = Math.min(10, current + gained);
|
||||
if (isCoque && coqueRestored) {
|
||||
updates['system.traits.coque'] = { value: coqueMax, max: coqueMax };
|
||||
} else if (mode === 'classique' && gained > 0) {
|
||||
updates['system.reliability'] = newReliability;
|
||||
updates['system.damages.value'] = Math.max(0, damagesValue - gained);
|
||||
} else if (mode === 'fortune' && fortuneFixed) {
|
||||
updates['system.damages.value'] = 0;
|
||||
if (bienConcuBonus) {
|
||||
updates['system.reliability'] = newReliability;
|
||||
updates['system.traits.bien_conçu'] = { value: 1 };
|
||||
}
|
||||
}
|
||||
if (Object.keys(updates).length) await item.update(updates);
|
||||
|
||||
const skillLabel = mode === 'fortune'
|
||||
? game.i18n.localize('SKILLS.diy.name')
|
||||
: game.i18n.localize(`SKILLS.${skill}.name`);
|
||||
const modeLabel = isCoque
|
||||
? game.i18n.localize('VERMINE.reliability_repair_coque_label')
|
||||
: game.i18n.localize(mode === 'classique' ? 'VERMINE.reliability_repair_classic' : 'VERMINE.reliability_repair_fortune');
|
||||
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/vermine2047/templates/reliability-repair.hbs',
|
||||
{
|
||||
item: { name: item.name, img: item.img },
|
||||
modeLabel: game.i18n.localize(mode === 'classique' ? 'VERMINE.reliability_repair_classic' : 'VERMINE.reliability_repair_fortune'),
|
||||
modeLabel,
|
||||
skillLabel,
|
||||
difficulty,
|
||||
handicap,
|
||||
handicap: isCoque ? 0 : handicap,
|
||||
pratiqueHandicap,
|
||||
results,
|
||||
successes,
|
||||
required,
|
||||
resultLabel,
|
||||
autoSuccess,
|
||||
fortuneNote,
|
||||
bienConcuNote,
|
||||
pratiqueNote,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user