Fix apv2, WIP
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* DataModel pour les acteurs de type "npc" (PNJ).
|
||||
* Étend foundry.abstract.TypeDataModel.
|
||||
*
|
||||
* Note : le champ libre de compétences (texte descriptif) est nommé "freeSkills"
|
||||
* pour éviter le conflit avec le SchemaField "skills" qui contient les 30 compétences.
|
||||
*/
|
||||
import {
|
||||
woundSchema,
|
||||
combatStatusSchema,
|
||||
equipmentSchema,
|
||||
attributeSchema,
|
||||
abilitiesSchema,
|
||||
skillCategoriesSchema,
|
||||
skillsSchema
|
||||
} from "./_shared.mjs"
|
||||
|
||||
export default class VermineNpcData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
/** @override */
|
||||
static LOCALIZATION_PREFIXES = ["VERMINE.npc"]
|
||||
|
||||
/**
|
||||
* Migration des données avant traitement par le schéma.
|
||||
* Avant DataModel, template.json définissait "skills" comme un champ texte libre
|
||||
* pour les PNJ. Le DataModel réserve "skills" pour les 30 compétences individuelles
|
||||
* (SchemaField) et utilise "freeSkills" pour le texte libre.
|
||||
* @param {Object} source Données brutes avant validation du schéma
|
||||
* @returns {Object} Données migrées
|
||||
*/
|
||||
static migrateData(source) {
|
||||
if (typeof source.skills === "string") {
|
||||
source.freeSkills = source.skills
|
||||
}
|
||||
return super.migrateData(source)
|
||||
}
|
||||
|
||||
/** @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, difficulté par défaut 9 pour PNJ)
|
||||
combatStatus: combatStatusSchema("9"),
|
||||
|
||||
// Identité
|
||||
identity: new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
profile: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
origin: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
totem: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
theme: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
notes: new fields.StringField({ required: true, nullable: false, initial: "" })
|
||||
}),
|
||||
|
||||
// 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)
|
||||
}),
|
||||
|
||||
// Niveaux PNJ (menace, expérience, rôle)
|
||||
threat: attributeSchema(1, 1, 4),
|
||||
experience: attributeSchema(1, 1, 4),
|
||||
role: attributeSchema(1, 1, 4),
|
||||
|
||||
// Compétences (les 30 compétences individuelles)
|
||||
skills: skillsSchema(),
|
||||
|
||||
// Description libre des compétences (champ texte PNJ)
|
||||
freeSkills: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
|
||||
// Catégories de compétences
|
||||
skill_categories: skillCategoriesSchema(),
|
||||
|
||||
// Caractéristiques (8)
|
||||
abilities: abilitiesSchema(),
|
||||
|
||||
// Équipement
|
||||
equipment: equipmentSchema()
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData()
|
||||
|
||||
// 1. Calculer les seuils de blessures selon le niveau de menace
|
||||
this._setNpcWoundThresholds()
|
||||
|
||||
// 2. Calculer les réserves selon le niveau de rôle
|
||||
this._setNpcAttributes()
|
||||
|
||||
// 3. Définir les libellés des caractéristiques
|
||||
this._setAbilityLabels()
|
||||
|
||||
// 4. Mettre à jour le statut de combat
|
||||
this._updateCombatStatus()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule les seuils de blessures à partir du niveau de menace.
|
||||
* Utilise CONFIG.VERMINE.npcThreatLevels.
|
||||
*/
|
||||
_setNpcWoundThresholds() {
|
||||
const health = this.abilities?.health?.value || 1
|
||||
const threatLevel = this.threat?.value || 1
|
||||
const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {}
|
||||
|
||||
this.minorWound.threshold = threatConfig.minorWound || health
|
||||
this.majorWound.threshold = threatConfig.majorWound || (health + 3)
|
||||
this.deadlyWound.threshold = threatConfig.deadlyWound || (health + 7 < 11 ? health + 7 : 10)
|
||||
|
||||
this.minorWound.max = threatConfig.minorWound || 4
|
||||
this.majorWound.max = threatConfig.majorWound || 3
|
||||
this.deadlyWound.max = threatConfig.deadlyWound || 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit les attributs dérivés (effort, sang-froid) selon le niveau de rôle.
|
||||
* Utilise CONFIG.VERMINE.npcRoleLevels.
|
||||
*/
|
||||
_setNpcAttributes() {
|
||||
const roleLevel = this.role?.value || 1
|
||||
const roleConfig = CONFIG.VERMINE.npcRoleLevels[roleLevel] || {}
|
||||
|
||||
this.attributes.effort.max = roleConfig.pools || 0
|
||||
this.attributes.self_control.max = roleConfig.reaction_bonus || 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit les libellés localisés des caractéristiques.
|
||||
*/
|
||||
_setAbilityLabels() {
|
||||
for (const [k, v] of Object.entries(this.abilities)) {
|
||||
v.label = game.i18n.localize(CONFIG.VERMINE.abilities[k]) ?? k
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user