Files
vermine2047/module/system/traits.mjs
T
uberwald ef01651b1d feat(traits): tâche 0 + phases 2-4 — automatisation complète des Traits
Tâche 0 :
- item.mjs : champ equipped sur items génériques + toggle fiche
- nouveau module/system/traits.mjs (helpers partagés : equippedItems, lourdMalus, mobilityHandicap, feticheItem…)

Phase 2 — Combat :
- Lourd : malus cumulés auto (Vigueur < n) dans le dialogue + pool
- Rapide : handicap (n) pré-rempli dans la défense (character + PNJ) + note carte
- Rafale : case tir en rafale (+2 difficulté), +n réussites si réussi (bonusOnSuccess)
- Portée : portée effective n × Vigueur affichée
- Maniable/Mobilité : note de rappel (dialogue + carte)
- Incapacitant : bouton « Jet de Santé (n) » sur la carte d'échange
- getLabel() : libellés caractéristiques/compétences localisés

Phase 3 — Jets d'action :
- Ponctuel : annule jusqu'à n Handicaps, consommé au jet (Traits supprimé à 0)
- Pratique : outil sélectionné +2D au lieu de +1D
- Intimidant : case « Brandit » → -1D Psychologie ou Relance 1D
- Malus : champ « Malus temporaires » manuel déduit du pool
- Durée : note « n tours » sur la carte de jet d'arme
- roll() : transmission effectiveRange/mobilityHandicap/ponctuelUsed/intimidant (corrige l'affichage Portée/Mobilité)

Phase 4 — Acteur :
- Fétiche : +1D Effort ET Sang-Froid (max dérivé, config corrigé), alerte 2+ fétiches, action « Perdre le fétiche » (-2D, max -1D persistant feticheLoss)
- Zone : note + bouton « Appliquer aux cibles de la zone » (#onApplyZone)

CSS : styles LESS pour les nouvelles classes (fetiche-block, exchange-reminder/zone, traits-reminder, tool-bonus/ponctuel, duree-note)
i18n : 29 clés fr/en
TRAITS_PLAN.md : plan complet cochée
2026-08-07 15:30:41 +02:00

115 lines
3.6 KiB
JavaScript

/**
* Helpers partagés pour les Traits (regles/regles_traits.txt).
*
* Centralise la lecture des Traits sur les items (présence, valeur) et les
* calculs liés aux objets équipés d'un acteur (Lourd, Mobilité, Fétiche…).
* Consommé par les dialogues de jet (combat, action) et les cartes de chat.
*/
export class VermineTraits {
/**
* Présence d'un Trait sur un item.
* @param {Item|null} item
* @param {string} key clé du Trait (ex. "lourd")
* @returns {boolean}
*/
static hasTrait(item, key) {
return Boolean(item?.system?.traits?.[key]);
}
/**
* Valeur (n) d'un Trait sur un item (0 si absent ou sans valeur).
* @param {Item|null} item
* @param {string} key clé du Trait
* @returns {number}
*/
static traitValue(item, key) {
if (!item?.system?.traits?.[key]) return 0;
return parseInt(item.system.traits[key]?.value ?? 0, 10) || 0;
}
/**
* Items équipés d'un acteur (armes + protections + objets génériques)
* possédant un Trait donné.
* @param {Actor} actor
* @param {string} traitKey
* @returns {Item[]}
*/
static equippedItems(actor, traitKey) {
if (!actor?.itemTypes) return [];
return [...actor.itemTypes.weapon || [], ...actor.itemTypes.defense || [], ...actor.itemTypes.item || []]
.filter(i => i.system?.equipped && this.hasTrait(i, traitKey));
}
/**
* Vigueur de l'acteur : caractéristique pour le personnage, tables pour
* PNJ (Menace) et créature (computed).
* @param {Actor} actor
* @returns {number}
*/
static vigor(actor) {
if (!actor?.system) return 0;
if (actor.type === 'npc') {
const threat = actor.system?.threat?.value || 1;
return CONFIG.VERMINE.npcThreatLevels?.[threat]?.vigor || 0;
}
if (actor.type === 'creature') return actor.system?.computed?.vigor || 0;
return actor.system?.abilities?.vigor?.value || 0;
}
/**
* Malus cumulés des objets Lourd (n) équipés dont la Vigueur du porteur
* est inférieure à (n). Les Malus se cumulent entre objets (règles p. …).
* @param {Actor} actor
* @returns {number}
*/
static lourdMalus(actor) {
const vigor = this.vigor(actor);
return this.equippedItems(actor, 'lourd').reduce((sum, item) => {
const n = this.traitValue(item, 'lourd');
return sum + (n > 0 && vigor < n ? 1 : 0);
}, 0);
}
/**
* Handicap de Mobilité total : somme des handicaps des protections équipées
* qui ne sont pas Maniables (le Trait Maniable annule le Handicap).
* @param {Actor} actor
* @returns {number}
*/
static mobilityHandicap(actor) {
const defenses = (actor?.itemTypes?.defense || []).filter(d => d.system?.equipped);
return defenses.reduce((sum, d) => sum + (this.hasTrait(d, 'maniable') ? 0 : (parseInt(d.system?.mobility ?? 0, 10) || 0)), 0);
}
/**
* Note de rappel sur la Mobilité (protections équipées non Maniables),
* ou null si aucun Handicap de Mobilité.
* @param {Actor} actor
* @returns {string|null}
*/
static mobilityNote(actor) {
const handicap = this.mobilityHandicap(actor);
if (handicap <= 0) return null;
return game.i18n.format('VERMINE.traits_mobility_note', { handicap });
}
/**
* Premier item équipé possédant le Trait Fétiche, ou null.
* @param {Actor} actor
* @returns {Item|null}
*/
static feticheItem(actor) {
return this.equippedItems(actor, 'fetiche')[0] ?? null;
}
/**
* Nombre d'items équipés avec le Trait Fétiche.
* @param {Actor} actor
* @returns {number}
*/
static feticheCount(actor) {
return this.equippedItems(actor, 'fetiche').length;
}
}