IMplémentation de la ajorité des remarques de Nepherius

This commit is contained in:
2026-04-06 17:48:30 +02:00
parent a3f7b11f82
commit 1022597bf8
51 changed files with 1900 additions and 443 deletions

View File

@@ -74,9 +74,9 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
vision: persoAttrField(),
})
// Factions — score entier direct (0-9)
// Factions — niveau de relation -4 (hostile) à +4 (allié), 0 = neutre
const factionField = () => new fields.SchemaField({
value: new fields.NumberField({ ...reqInt, initial: 0, min: 0, max: 9 }),
value: new fields.NumberField({ ...reqInt, initial: 0, min: -4, max: 4 }),
})
schema.factions = new fields.SchemaField({
pinkerton: factionField(),
@@ -89,11 +89,11 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
cour: factionField(),
perso1: new fields.SchemaField({
label: new fields.StringField({ required: true, nullable: false, initial: "" }),
value: new fields.NumberField({ ...reqInt, initial: 0 }),
value: new fields.NumberField({ ...reqInt, initial: 0, min: -4, max: 4 }),
}),
perso2: new fields.SchemaField({
label: new fields.StringField({ required: true, nullable: false, initial: "" }),
value: new fields.NumberField({ ...reqInt, initial: 0 }),
value: new fields.NumberField({ ...reqInt, initial: 0, min: -4, max: 4 }),
}),
})
@@ -114,8 +114,9 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
})
// Description & notes
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
schema.descriptionPhysique = new fields.HTMLField({ required: true, textSearch: true })
schema.descriptionPsychologique = new fields.HTMLField({ required: true, textSearch: true })
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
// Données biographiques
schema.biodata = new fields.SchemaField({
@@ -152,6 +153,20 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
// XP dépensée = somme des montants du log
this.xp.depense = this.xp.log.reduce((sum, entry) => sum + entry.montant, 0)
// Malus d'armure(s) équipée(s)
this.armorMalus = this.getArmorMalus()
}
/**
* Retourne le malus total des armures équipées portées par le protagoniste.
* @returns {number}
*/
getArmorMalus() {
if (!this.parent) return 0
return -(this.parent.itemTypes.armure
.filter(a => a.system.equipped && a.system.malus > 0)
.reduce((sum, a) => sum + a.system.malus, 0))
}
/**
@@ -183,6 +198,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
skillLabel: skill.label,
skillValue: skill.value,
woundMalus: this.getWoundMalus(),
armorMalus: this.getArmorMalus(),
woundLevel: this.blessures.lvl,
difficulty: this.prefs.difficulty,
rollMoonDie: this.prefs.rollMoonDie ?? false,
@@ -213,6 +229,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
skillLabel: "CELESTOPOL.Roll.resistanceTest",
skillValue: statData.res,
woundMalus: this.getWoundMalus(),
armorMalus: this.getArmorMalus(),
woundLevel: this.blessures.lvl,
isResistance: true,
rollMoonDie: false,
@@ -222,6 +239,38 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
})
}
/**
* Collecte les tokens PNJs disponibles comme cibles de combat.
* Priorise le combat tracker, sinon les tokens ciblés par l'utilisateur.
* @returns {Array<{id:string, name:string, corps:number}>}
*/
_getCombatTargets() {
const toEntry = actor => ({
id: actor.id,
name: actor.name,
corps: actor.system.stats?.corps?.res ?? 0,
})
// Priorité 1 : PNJs dans le combat actif
if (game.combat?.active) {
const list = game.combat.combatants
.filter(c => c.actor?.type === "npc" && c.actorId !== this.parent.id)
.map(c => toEntry(c.actor))
if (list.length) return list
}
// Priorité 2 : Tokens ciblés par le joueur
const targeted = [...(game.user?.targets ?? [])]
.filter(t => t.actor?.type === "npc")
.map(t => toEntry(t.actor))
if (targeted.length) return targeted
// Priorité 3 : Tous les tokens NPC de la scène active
if (canvas?.tokens?.placeables) {
return canvas.tokens.placeables
.filter(t => t.actor?.type === "npc" && t.actor.id !== this.parent.id)
.map(t => toEntry(t.actor))
}
return []
}
/**
* Lance une attaque avec une arme.
* Mêlée : test Échauffourée vs Corps PNJ ; échec → blessure joueur.
@@ -238,24 +287,26 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
if (!echauffouree) return null
return CelestopolRoll.prompt({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
statId: "corps",
skillId: "echauffouree",
statLabel: SYSTEM.STATS.corps.label,
skillLabel: SYSTEM.SKILLS.corps.echauffouree.label,
skillValue: echauffouree.value,
woundMalus: this.getWoundMalus(),
woundLevel: this.blessures.lvl,
rollMoonDie: this.prefs.rollMoonDie ?? false,
destGaugeFull: this.destin.lvl > 0,
fortuneValue: this.attributs.fortune.value,
isCombat: true,
isRangedDefense: false,
weaponType: item.system.type,
weaponName: item.name,
weaponDegats: item.system.degats,
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
statId: "corps",
skillId: "echauffouree",
statLabel: SYSTEM.STATS.corps.label,
skillLabel: SYSTEM.SKILLS.corps.echauffouree.label,
skillValue: echauffouree.value,
woundMalus: this.getWoundMalus(),
armorMalus: this.getArmorMalus(),
woundLevel: this.blessures.lvl,
rollMoonDie: this.prefs.rollMoonDie ?? false,
destGaugeFull: this.destin.lvl > 0,
fortuneValue: this.attributs.fortune.value,
isCombat: true,
isRangedDefense: false,
weaponType: item.system.type,
weaponName: item.name,
weaponDegats: item.system.degats,
availableTargets: this._getCombatTargets(),
})
}
@@ -274,24 +325,26 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel
if (!mobilite) return null
return CelestopolRoll.prompt({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
statId: "corps",
skillId: "mobilite",
statLabel: SYSTEM.STATS.corps.label,
skillLabel: SYSTEM.SKILLS.corps.mobilite.label,
skillValue: mobilite.value,
woundMalus: this.getWoundMalus(),
woundLevel: this.blessures.lvl,
rollMoonDie: this.prefs.rollMoonDie ?? false,
destGaugeFull: this.destin.lvl > 0,
fortuneValue: this.attributs.fortune.value,
isCombat: true,
isRangedDefense: true,
weaponType: "distance",
weaponName: item.name,
weaponDegats: "0",
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
statId: "corps",
skillId: "mobilite",
statLabel: SYSTEM.STATS.corps.label,
skillLabel: SYSTEM.SKILLS.corps.mobilite.label,
skillValue: mobilite.value,
woundMalus: this.getWoundMalus(),
armorMalus: this.getArmorMalus(),
woundLevel: this.blessures.lvl,
rollMoonDie: this.prefs.rollMoonDie ?? false,
destGaugeFull: this.destin.lvl > 0,
fortuneValue: this.attributs.fortune.value,
isCombat: true,
isRangedDefense: true,
weaponType: "distance",
weaponName: item.name,
weaponDegats: "0",
availableTargets: this._getCombatTargets(),
})
}
}