- acteur community (identité, géographie, histoire, objectifs, taille/effectif) - 4 Domaines x 2 jauges avec états (Crise/Bas/Stable/Opulent), moral, réserves - onglets Identité / Domaines / Ressources, édition ProseMirror (formInput) - header avec fond, totem, niveau, effectif ; icône par défaut - libellé du type via typeLabels (sans écraser TYPES.* du core)
196 lines
7.5 KiB
JavaScript
196 lines
7.5 KiB
JavaScript
/**
|
||
* DataModel pour les acteurs de type "community" (Communauté).
|
||
* La Communauté est l'extension sédentarisée d'un Groupe : elle englobe le
|
||
* Groupe de personnages, des Figures et des Habitants répartis en Compagnies.
|
||
* Le socle couvre l'identité, la géographie, l'histoire, les objectifs, la
|
||
* taille/l'effectif, les quatre Domaines (8 jauges), le Moral et les réserves.
|
||
*/
|
||
import {
|
||
attributeSchema,
|
||
levelSchema,
|
||
combatStatusSchema
|
||
} from "./_shared.mjs"
|
||
|
||
export default class VermineCommunityData extends foundry.abstract.TypeDataModel {
|
||
|
||
/** @override */
|
||
static LOCALIZATION_PREFIXES = ["VERMINE.community"]
|
||
|
||
/** @override */
|
||
static defineSchema() {
|
||
const fields = foundry.data.fields
|
||
const reqInt = { required: true, nullable: false, integer: true }
|
||
const gauge = () => attributeSchema(0, 0, 10)
|
||
|
||
return {
|
||
// Statut de combat (base, peu utilisé pour une Communauté)
|
||
combatStatus: combatStatusSchema("9"),
|
||
|
||
// Identité
|
||
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: "" }),
|
||
// Âge de la Communauté : recent | established | old
|
||
age: new fields.StringField({ required: true, nullable: false, initial: "recent" }),
|
||
notes: new fields.HTMLField({ required: true, initial: "", textSearch: true })
|
||
}),
|
||
|
||
// Géographie / localisation
|
||
location: new fields.SchemaField({
|
||
sector: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
placeType: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
landmarks: new fields.HTMLField({ required: true, initial: "", textSearch: true }),
|
||
territory: new fields.HTMLField({ required: true, initial: "", textSearch: true })
|
||
}),
|
||
|
||
// Histoire
|
||
history: new fields.SchemaField({
|
||
founding: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
events: new fields.HTMLField({ required: true, initial: "", textSearch: true }),
|
||
relations: new fields.HTMLField({ required: true, initial: "", textSearch: true })
|
||
}),
|
||
|
||
// Objectifs : un majeur + des mineurs
|
||
objectives: new fields.SchemaField({
|
||
major: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
minor: new fields.ArrayField(new fields.StringField({ required: true, nullable: false, initial: "" }))
|
||
}),
|
||
|
||
// Taille (1-3) et effectif
|
||
size: attributeSchema(1, 1, 3),
|
||
population: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||
|
||
// Domaines : 4 Domaines × 2 sections (jauges en Dés)
|
||
domains: new fields.SchemaField({
|
||
resources: new fields.SchemaField({
|
||
vivre: gauge(),
|
||
supplies: gauge()
|
||
}),
|
||
security: new fields.SchemaField({
|
||
defense: gauge(),
|
||
care: gauge()
|
||
}),
|
||
dynamism: new fields.SchemaField({
|
||
cohesion: gauge(),
|
||
productivity: gauge()
|
||
}),
|
||
relations: new fields.SchemaField({
|
||
human: gauge(),
|
||
ecology: gauge()
|
||
})
|
||
}),
|
||
|
||
// Moral (comme le Groupe) : value 0-7, level dérivé
|
||
morale: new fields.SchemaField({
|
||
level: new fields.StringField({ required: true, nullable: false, initial: "normal" }),
|
||
value: new fields.NumberField({ ...reqInt, initial: 7, min: 0, max: 7 }),
|
||
min: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||
max: new fields.NumberField({ ...reqInt, initial: 7, min: 0 })
|
||
}),
|
||
|
||
// Réserve de Groupe
|
||
reserve: attributeSchema(0, 0, 10),
|
||
|
||
// Dés collectifs de la Communauté
|
||
material: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||
experience: new fields.SchemaField({
|
||
dice: new fields.NumberField({ ...reqInt, initial: 0, min: 0 })
|
||
}),
|
||
|
||
// Niveau de la Communauté (pour l'expérience des habitants)
|
||
level: levelSchema(1, 1, 10),
|
||
|
||
// Groupe de personnages rattaché (id d'acteur)
|
||
groupId: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
|
||
// Habitants (phase 2) : Figures et Compagnies
|
||
figures: new fields.ArrayField(new fields.SchemaField({
|
||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
role: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
totem: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
specialty: new fields.SchemaField({
|
||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
score: new fields.NumberField({ ...reqInt, initial: 3, min: 0, max: 10 })
|
||
})
|
||
})),
|
||
companies: new fields.ArrayField(new fields.SchemaField({
|
||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
specialty: new fields.SchemaField({
|
||
name: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||
score: new fields.NumberField({ ...reqInt, initial: 3, min: 0, max: 10 })
|
||
}),
|
||
level: levelSchema(1, 1, 4),
|
||
members: new fields.NumberField({ ...reqInt, initial: 1, min: 0 }),
|
||
figureName: new fields.StringField({ required: true, nullable: false, initial: "" })
|
||
}))
|
||
}
|
||
}
|
||
|
||
/** @override */
|
||
prepareDerivedData() {
|
||
super.prepareDerivedData()
|
||
|
||
// 1. État (Crise/Bas/Stable/Opulent) de chaque jauge de Domaine
|
||
this._computeGaugeStates()
|
||
|
||
// 2. Les quatre Domaines sont-ils Bas ? (Moral inactif)
|
||
this._computeAllDomainsLow()
|
||
|
||
// 3. Mettre à jour le niveau de Moral selon la valeur
|
||
this._updateMorale()
|
||
}
|
||
|
||
/**
|
||
* État d'une jauge selon sa valeur en Dés.
|
||
* Crise 0D / Bas 1-2D / Stable 3-5D / Opulent 6D+.
|
||
* @param {number} value
|
||
* @returns {string}
|
||
*/
|
||
static gaugeState(value) {
|
||
const v = parseInt(value, 10) || 0
|
||
if (v >= 6) return "opulent"
|
||
if (v >= 3) return "stable"
|
||
if (v >= 1) return "low"
|
||
return "crisis"
|
||
}
|
||
|
||
_computeGaugeStates() {
|
||
for (const domain of Object.values(this.domains)) {
|
||
for (const gauge of Object.values(domain)) {
|
||
gauge.state = VermineCommunityData.gaugeState(gauge.value)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Un Domaine est "Bas" lorsque ses deux sections sont à 2D ou moins.
|
||
* Si les quatre Domaines sont Bas, le Bonus de Moral est inactif.
|
||
*/
|
||
_computeAllDomainsLow() {
|
||
const domainLow = (domain) => Object.values(domain).every(g => (parseInt(g.value, 10) || 0) <= 2)
|
||
this.allDomainsLow = Object.values(this.domains).every(domainLow)
|
||
}
|
||
|
||
/**
|
||
* 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.
|
||
*/
|
||
_updateMorale() {
|
||
const moraleValue = parseInt(this.morale?.value, 10) || 0
|
||
if (this.morale.level && this.morale.level !== "normal") 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"
|
||
}
|
||
}
|
||
}
|