/** * DataModel pour les acteurs de type "character" (personnage). * Étend foundry.abstract.TypeDataModel. */ import { woundSchema, combatStatusSchema, equipmentSchema, attributeSchema, abilitiesSchema, skillCategoriesSchema, skillsSchema } from "./_shared.mjs" export default class VermineCharacterData extends foundry.abstract.TypeDataModel { /** @override */ static LOCALIZATION_PREFIXES = ["VERMINE.character"] /** @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("7"), // Adaptation (totems humain/adapté) adaptation: new fields.SchemaField({ value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1, min: 0, max: 5 }), min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 5, min: 0 }), totems: new fields.SchemaField({ human: new fields.SchemaField({ value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1, min: 0, max: 3 }), min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 3, min: 0 }) }), adapted: new fields.SchemaField({ value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 1, min: 0, max: 3 }), min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }), max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 3, min: 0 }) }) }) }), // Identité identity: new fields.SchemaField({ height: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), weight: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 }), totem: new fields.StringField({ required: true, nullable: false, initial: "" }), age: new fields.StringField({ required: true, nullable: false, initial: "15" }), ageType: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 2 }), profile: new fields.StringField({ required: true, nullable: false, initial: "" }), theme: new fields.StringField({ required: true, nullable: false, initial: "" }), instincts: new fields.StringField({ required: true, nullable: false, initial: "" }), prohibits: new fields.StringField({ required: true, nullable: false, initial: "" }), objectives: new fields.StringField({ required: true, nullable: false, initial: "" }), relations: new fields.HTMLField({ required: true, initial: "" }), biography: new fields.HTMLField({ required: true, initial: "" }) }), // Équipement equipment: equipmentSchema(), // Attributs (XP, réputation, sang-froid, effort) attributes: new fields.SchemaField({ xp: attributeSchema(0, 0, 10), reputation: attributeSchema(0, 0, 10), self_control: attributeSchema(0, 0, 5), effort: attributeSchema(0, 0, 5) }), // Rencontres encounters: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })), // Caractéristiques (8) abilities: abilitiesSchema(), // Catégories de compétences skill_categories: skillCategoriesSchema(), // Compétences (30) skills: skillsSchema() } } /** @override */ prepareDerivedData() { super.prepareDerivedData() // 1. Déterminer la tranche d'âge this._setAgeType() // 2. Calculer les modificateurs de caractéristiques this._setAbilityModifiers() // 3. Calculer les réserves (sang-froid et effort) this._setSelfControlMax() this._setEffortMax() // 4. Calculer les seuils de blessures this._setWoundThresholds() // 5. Mettre à jour le statut de combat this._updateCombatStatus() } /** * Détermine la tranche d'âge (1=jeune, 2=adulte, 3=vieux) * à partir de l'âge et de la config VERMINE.AgeTypes. */ _setAgeType() { const age = this.identity.age const ageTypes = CONFIG.VERMINE.AgeTypes for (const [type, cfg] of Object.entries(ageTypes)) { if (age >= parseInt(cfg.beginning, 10)) { this.identity.ageType = parseInt(type, 10) } } } /** * Calcule les modificateurs de caractéristiques (règle d20). */ _setAbilityModifiers() { for (const ability of Object.values(this.abilities)) { ability.mod = Math.floor((ability.value - 10) / 2) } } /** * Calcule le max de sang-froid : * somme des caractéristiques mentales + sociales + modificateur d'âge. */ _setSelfControlMax() { const abilities = Object.values(this.abilities) const modFromAge = this._getModFromAgeSelfControl() const sum = abilities .filter(a => a.category === "mental" || a.category === "social") .reduce((acc, a) => acc + a.value, 0) this.attributes.self_control.max = sum + modFromAge } /** * Calcule le max d'effort : * somme des caractéristiques physiques + manuelles + modificateur d'âge. */ _setEffortMax() { const abilities = Object.values(this.abilities) const modFromAge = this._getModFromAgeEffort() const sum = abilities .filter(a => a.category === "physical" || a.category === "manual") .reduce((acc, a) => acc + a.value, 0) this.attributes.effort.max = sum + modFromAge } /** * Calcule les seuils de blessures à partir de la Santé. */ _setWoundThresholds() { const health = this.abilities.health.value const ageMods = this._getModFromAgeWounds() this.minorWound.threshold = health this.majorWound.threshold = health + 3 this.deadlyWound.threshold = (health + 7 < 11) ? health + 7 : 10 this.minorWound.max = 4 + ageMods.l this.majorWound.max = 3 + ageMods.h this.deadlyWound.max = 2 + ageMods.d } /** * 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 } } // ── Modificateurs liés à l'âge ────────────────────────────────────── /** @returns {number} Modificateur de sang-froid selon l'âge. */ _getModFromAgeSelfControl() { return this.identity.ageType === 1 ? -1 : 0 } /** @returns {number} Modificateur d'effort selon l'âge. */ _getModFromAgeEffort() { if (this.identity.ageType === 1) return -1 if (this.identity.ageType === 3) return -2 return 0 } /** @returns {{l: number, h: number, d: number}} Modificateurs de blessures selon l'âge. */ _getModFromAgeWounds() { if (this.identity.ageType === 1) return { l: 0, h: 0, d: -1 } if (this.identity.ageType === 3) return { l: -1, h: -1, d: -1 } return { l: 0, h: 0, d: 0 } } }