- Nouvel item Capacité de créature (creaturecapacity) : DataModel, fiche AppV2, chat card, types Base/Survie/Cauchemar/Apocalypse, liste sur l'onglet Informations de la créature avec drag&drop et post au chat. - Blessures : coches séquentielles respectant la gravité (onClickWound), malus de blessure -1D/-2D/-3D appliqué aux jets (Règles p.42), montée en gravité automatique si les cercles sont pleins. - Fiche Groupe : objectifs majeurs/mineurs éditables, liste de membres simplifiée, notes de voyage (roadNotes), totem qui remplit instinct/interdits, refonte visuelle des onglets. - Créature : Réaction lançable, seuils/cercles de blessures corrigés (Taille cumulée, Groupe sans seuil), libellés Taille/Meute. - Corrections : icône de carte de chat plafonnée à 64px, slash orphelin d'entrave à 0, notes de matériel du Groupe éditable (formInput), bonus de Réaction non double-compté. - Divers : suppression des captures de débogage, licence README.
207 lines
8.2 KiB
JavaScript
207 lines
8.2 KiB
JavaScript
/**
|
|
* DataModel pour les acteurs de type "creature" (créature).
|
|
* Étend foundry.abstract.TypeDataModel.
|
|
*/
|
|
import {
|
|
woundSchema,
|
|
combatStatusSchema,
|
|
equipmentSchema,
|
|
attributeSchema
|
|
} from "./_shared.mjs"
|
|
|
|
export default class VermineCreatureData extends foundry.abstract.TypeDataModel {
|
|
|
|
/** @override */
|
|
static LOCALIZATION_PREFIXES = ["VERMINE.creature"]
|
|
|
|
/** @override */
|
|
static defineSchema() {
|
|
const fields = foundry.data.fields
|
|
|
|
return {
|
|
// Blessures (base)
|
|
minorWound: new fields.SchemaField(woundSchema(1, 5)),
|
|
majorWound: new fields.SchemaField(woundSchema(4, 4)),
|
|
deadlyWound: new fields.SchemaField(woundSchema(8, 2)),
|
|
|
|
// Statut de combat (base)
|
|
combatStatus: combatStatusSchema(),
|
|
|
|
// Identité
|
|
identity: new fields.SchemaField({
|
|
profile: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
origin: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
theme: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
notes: new fields.HTMLField({ required: true, initial: "" }),
|
|
biography: new fields.HTMLField({ required: true, initial: "" })
|
|
}),
|
|
|
|
// Compétences (description libre)
|
|
skills: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
|
|
|
// Modes de jeu actifs
|
|
modes: new fields.SchemaField({
|
|
survival: new fields.BooleanField({ required: true, initial: true }),
|
|
nightmare: new fields.BooleanField({ required: true, initial: true }),
|
|
apocalypse: new fields.BooleanField({ required: true, initial: false })
|
|
}),
|
|
|
|
// Niveaux de créature (patron, taille, rôle, meute)
|
|
pattern: attributeSchema(1, 1, 4),
|
|
size: attributeSchema(0, 0, 3),
|
|
role: attributeSchema(1, 1, 4),
|
|
pack: attributeSchema(0, 0, 3),
|
|
|
|
// Valeurs calculées (dérivées de pattern/size/role/pack)
|
|
computed: new fields.SchemaField({
|
|
attack: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
damage: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
vigor: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
reaction: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
reactionBonus: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
pools: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
gear: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 9 }),
|
|
gearHindrance: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }),
|
|
protection: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1 })
|
|
}),
|
|
|
|
// Équipement
|
|
equipment: equipmentSchema()
|
|
}
|
|
}
|
|
|
|
/** @override */
|
|
prepareDerivedData() {
|
|
super.prepareDerivedData()
|
|
|
|
// 1. Calculer les valeurs dérivées (attaque, dégâts, vigueur, etc.)
|
|
this._calculateCreatureComputedValues()
|
|
|
|
// 2. Calculer les seuils de blessures
|
|
this._calculateCreatureWoundThresholds()
|
|
|
|
// 3. Mettre à jour le statut de combat
|
|
this._updateCombatStatus()
|
|
}
|
|
|
|
/**
|
|
* Calcule les valeurs dérivées à partir des niveaux de patron, taille, rôle et meute.
|
|
* Utilise les configs CONFIG.VERMINE.creaturePatternLevels, .creatureSizeLevels,
|
|
* .creatureRoleLevels, .creaturePackLevels.
|
|
*
|
|
* Règles :
|
|
* - Attaque = pattern.attack + size.attack + pack.attack + role.reaction
|
|
* - Dégâts = pattern.damage + size.vigor + pack.damage
|
|
* - Vigueur = size.vigor + pack.damage
|
|
* - Réaction = role.reaction + role.reaction_bonus
|
|
*/
|
|
_calculateCreatureComputedValues() {
|
|
const patternLevel = this.pattern?.value || 1
|
|
const sizeLevel = this.size?.value || 0
|
|
const roleLevel = this.role?.value || 1
|
|
const packLevel = this.pack?.value || 0
|
|
|
|
const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {}
|
|
const roleConfig = CONFIG.VERMINE.creatureRoleLevels[roleLevel] || {}
|
|
const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {}
|
|
|
|
// Les modificateurs de Taille se cumulent : une créature de Taille N applique
|
|
// les modificateurs de chaque Taille de 1 à N (Taille 0 = insecte, aucun).
|
|
const sizeLevels = CONFIG.VERMINE.creatureSizeLevels || {}
|
|
let sizeAttack = 0
|
|
let sizeVigor = 0
|
|
for (let i = 1; i <= sizeLevel; i++) {
|
|
const cfg = sizeLevels[i] || {}
|
|
sizeAttack += cfg.attack || 0
|
|
sizeVigor += cfg.vigor || 0
|
|
}
|
|
|
|
// Attaque : patron + taille + meute + réaction du rôle
|
|
this.computed.attack = (patternConfig.attack || 0)
|
|
+ sizeAttack
|
|
+ (packConfig.attack || 0)
|
|
+ (roleConfig.reaction || 0)
|
|
|
|
// Dégâts : patron + vigueur de taille + meute
|
|
this.computed.damage = (patternConfig.damage || 0)
|
|
+ sizeVigor
|
|
+ (packConfig.damage || 0)
|
|
|
|
// Vigueur : taille + meute
|
|
this.computed.vigor = sizeVigor + (packConfig.damage || 0)
|
|
|
|
// Réaction : rôle (le bonus de rôle est compté séparément et ajouté
|
|
// comme réussites automatiques par le NpcRollDialog — cf. modèle PNJ).
|
|
this.computed.reaction = roleConfig.reaction || 0
|
|
this.computed.reactionBonus = roleConfig.reaction_bonus || 0
|
|
|
|
// Réserves
|
|
this.computed.pools = roleConfig.pools || 0
|
|
|
|
// Équipement et handicap
|
|
this.computed.gear = roleConfig.gear || 9
|
|
this.computed.gearHindrance = roleConfig.gear_hindrance || 0
|
|
|
|
// Protection
|
|
this.computed.protection = roleConfig.protection || 1
|
|
}
|
|
|
|
/**
|
|
* Calcule les seuils et les cercles de blessures à partir du patron, de la
|
|
* taille et de la meute.
|
|
*
|
|
* Règles (regles_creatures.txt) :
|
|
* - Seuil = contribution du Gabarit + contributions de Taille (cumulées).
|
|
* Le Groupe n'ajoute PAS de seuil (tableau « Groupe » : cercles seuls).
|
|
* - Cercles = cercles du Gabarit + cercles de Taille (cumulés) + cercles du Groupe.
|
|
*/
|
|
_calculateCreatureWoundThresholds() {
|
|
const patternLevel = this.pattern?.value || 1
|
|
const sizeLevel = this.size?.value || 0
|
|
const packLevel = this.pack?.value || 0
|
|
|
|
const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {}
|
|
const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {}
|
|
|
|
// Les modificateurs de Taille se cumulent (Taille N = somme des tailles 1..N).
|
|
const sizeLevels = CONFIG.VERMINE.creatureSizeLevels || {}
|
|
const sizeThresholds = { minor: 0, major: 0, deadly: 0 }
|
|
const sizeWounds = { minor: 0, major: 0, deadly: 0 }
|
|
for (let i = 1; i <= sizeLevel; i++) {
|
|
const cfg = sizeLevels[i] || {}
|
|
sizeThresholds.minor += cfg.minorThreshold || 0
|
|
sizeThresholds.major += cfg.majorThreshold || 0
|
|
sizeThresholds.deadly += cfg.deadlyThreshold || 0
|
|
sizeWounds.minor += cfg.minorWound || 0
|
|
sizeWounds.major += cfg.majorWound || 0
|
|
sizeWounds.deadly += cfg.deadlyWound || 0
|
|
}
|
|
|
|
// Seuils : gabarit + taille cumulée (pas de contribution du groupe).
|
|
this.minorWound.threshold = (patternConfig.minorThreshold ?? 0) + sizeThresholds.minor
|
|
this.majorWound.threshold = (patternConfig.majorThreshold ?? 0) + sizeThresholds.major
|
|
this.deadlyWound.threshold = (patternConfig.deadlyThreshold ?? 0) + sizeThresholds.deadly
|
|
|
|
// Cercles : gabarit + taille cumulée + groupe.
|
|
this.minorWound.max = (patternConfig.minorWound ?? 0) + sizeWounds.minor + (packConfig.minorWound ?? 0)
|
|
this.majorWound.max = (patternConfig.majorWound ?? 0) + sizeWounds.major + (packConfig.majorWound ?? 0)
|
|
this.deadlyWound.max = (patternConfig.deadlyWound ?? 0) + sizeWounds.deadly + (packConfig.deadlyWound ?? 0)
|
|
}
|
|
|
|
/**
|
|
* Met à jour le label du statut de combat en fonction de la difficulté.
|
|
*/
|
|
_updateCombatStatus() {
|
|
const difficulty = parseInt(this.combatStatus.difficulty) || 9
|
|
let newLabel = "Passif"
|
|
switch (difficulty) {
|
|
case 5: newLabel = "Offensif"; break
|
|
case 7: newLabel = "Actif"; break
|
|
case 9: newLabel = "Passif"; break
|
|
}
|
|
if (this.combatStatus.label !== newLabel) {
|
|
this.combatStatus.label = newLabel
|
|
}
|
|
}
|
|
}
|