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
115 lines
3.6 KiB
JavaScript
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;
|
|
}
|
|
}
|