- _calculateGroupReserve utilisait une règle simplifiée (niveau × 2) au lieu du nombre de personnages membres. Désormais max = 3 + membres (personnages uniquement), plafonné à 10D, conforme aux règles p. 68-69. - Un Groupe nouvellement créé démarre avec sa Réserve à son maximum.
183 lines
6.5 KiB
JavaScript
183 lines
6.5 KiB
JavaScript
/**
|
|
* DataModel pour les acteurs de type "group" (groupe).
|
|
* Étend foundry.abstract.TypeDataModel.
|
|
*/
|
|
import {
|
|
woundSchema,
|
|
combatStatusSchema,
|
|
equipmentSchema,
|
|
attributeSchema,
|
|
levelSchema
|
|
} from "./_shared.mjs"
|
|
|
|
export default class VermineGroupData extends foundry.abstract.TypeDataModel {
|
|
|
|
/** @override */
|
|
static LOCALIZATION_PREFIXES = ["VERMINE.group"]
|
|
|
|
/** @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é du groupe
|
|
identity: new fields.SchemaField({
|
|
totem: 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: "" }),
|
|
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: "" }),
|
|
notes: new fields.HTMLField({ required: true, initial: "" })
|
|
}),
|
|
|
|
// Équipement
|
|
equipment: equipmentSchema(),
|
|
|
|
// Notes de voyage (lieux, dangers, routes…)
|
|
roadNotes: new fields.HTMLField({ required: true, initial: "" }),
|
|
|
|
// Niveau du groupe (1-10)
|
|
level: levelSchema(1, 1, 10),
|
|
|
|
// Réputation
|
|
reputation: new fields.SchemaField({
|
|
value: new fields.NumberField({ required: true, nullable: true, integer: true, initial: 10, min: 2 }),
|
|
min: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 2, min: 0 }),
|
|
max: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 10, min: 0 })
|
|
}),
|
|
|
|
// Moral
|
|
morale: new fields.SchemaField({
|
|
level: new fields.StringField({ required: true, nullable: false, initial: "high" }),
|
|
value: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 7, min: 0, max: 7 }),
|
|
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: 7, min: 0 })
|
|
}),
|
|
|
|
// Réserve
|
|
reserve: attributeSchema(0, 0, 10),
|
|
|
|
// Objectifs (majeurs et mineurs)
|
|
objectives: new fields.SchemaField({
|
|
major: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })),
|
|
minor: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" }))
|
|
}),
|
|
|
|
// Capacités de groupe
|
|
groupAbilities: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })),
|
|
|
|
// Membres (IDs d'acteurs)
|
|
members: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" })),
|
|
|
|
// Dés d'Expérience collectifs (réserve de Groupe)
|
|
experience: new fields.SchemaField({
|
|
dice: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
|
phase: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
|
// Limites d'apprentissage par phase
|
|
learned: new fields.SchemaField({
|
|
phase: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0, min: 0 }),
|
|
level: new fields.BooleanField({ required: true, initial: false }),
|
|
reserve: new fields.BooleanField({ required: true, initial: false }),
|
|
ability: new fields.BooleanField({ required: true, initial: false })
|
|
})
|
|
}),
|
|
|
|
// Rencontres
|
|
encounters: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" }))
|
|
}
|
|
}
|
|
|
|
/** @override */
|
|
prepareDerivedData() {
|
|
super.prepareDerivedData()
|
|
|
|
// 1. Initialiser les données de groupe si absentes
|
|
this._initGroupData()
|
|
|
|
// 2. Calculer la réserve max selon le niveau
|
|
this._calculateGroupReserve()
|
|
|
|
// 3. Mettre à jour le moral selon la valeur de dés
|
|
this._updateGroupMorale()
|
|
|
|
// 4. Mettre à jour le statut de combat
|
|
this._updateCombatStatus()
|
|
}
|
|
|
|
/**
|
|
* Initialise les champs optionnels du groupe s'ils ne sont pas présents.
|
|
*/
|
|
_initGroupData() {
|
|
if (!this.objectives) {
|
|
this.objectives = { major: [], minor: [] }
|
|
}
|
|
if (!this.groupAbilities) {
|
|
this.groupAbilities = []
|
|
}
|
|
if (!this.reserve) {
|
|
this.reserve = { value: 0, min: 0, max: 10 }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calcule la réserve max selon le nombre de membres (personnages).
|
|
* Règles p. 68-69 : la Réserve débute à 3D et gagne 1D par personnage,
|
|
* avec un maximum de 10D.
|
|
*/
|
|
_calculateGroupReserve() {
|
|
const members = Array.isArray(this.members) ? this.members : []
|
|
const characterCount = members.filter((id) => game.actors?.get(id)?.type === "character").length
|
|
this.reserve.max = Math.min(10, 3 + characterCount)
|
|
|
|
if (this.reserve.value > this.reserve.max) {
|
|
this.reserve.value = this.reserve.max
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Met à jour le niveau de moral en fonction de la valeur de dés.
|
|
* Règles : 7D+ = Haut, 6-3D = Normal, 2-1D = Bas, 0D = Crise.
|
|
*/
|
|
_updateGroupMorale() {
|
|
const moraleValue = this.morale?.value || 0
|
|
|
|
// Ne pas écraser un niveau explicitement défini (sauf "high" qui est la valeur par défaut)
|
|
if (this.morale.level && this.morale.level !== "high") return
|
|
|
|
if (moraleValue >= 7) {
|
|
this.morale.level = "high"
|
|
} else if (moraleValue >= 3) {
|
|
this.morale.level = "normal"
|
|
} else if (moraleValue >= 1) {
|
|
this.morale.level = "low"
|
|
} else {
|
|
this.morale.level = "crisis"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
}
|
|
}
|