feat: gestion de l'expérience (XP)
- Schéma xp dans CelestopolCharacter : actuel (éditable), log[] ({montant, raison, date}), depense (calculé dans prepareDerivedData)
- Bouton 'Dépenser XP' → DialogV2 (montant + raison) : décrémente actuel, logge l'entrée
- Suppression d'entrée de log avec remboursement des points (mode édition)
- Section XP en haut de l'onglet Biographie : compteurs, tableau du log, référentiel des coûts
- i18n : section CELESTOPOL.XP.* complète
- CSS : .xp-section avec compteurs, tableau de log et accordéon de référence
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -42,6 +42,7 @@ export class CelestopolRoll extends Roll {
|
||||
const fortuneValue = options.fortuneValue ?? 0
|
||||
const isResistance = options.isResistance ?? false
|
||||
const isCombat = options.isCombat ?? false
|
||||
const isRangedDefense = options.isRangedDefense ?? false
|
||||
const weaponType = options.weaponType ?? "melee"
|
||||
const weaponName = options.weaponName ?? null
|
||||
const weaponDegats = options.weaponDegats ?? "0"
|
||||
@@ -72,6 +73,7 @@ export class CelestopolRoll extends Roll {
|
||||
defaultRollMoonDie: options.rollMoonDie ?? false,
|
||||
isResistance,
|
||||
isCombat,
|
||||
isRangedDefense,
|
||||
weaponType,
|
||||
weaponName,
|
||||
weaponDegats,
|
||||
@@ -221,6 +223,7 @@ export class CelestopolRoll extends Roll {
|
||||
autoSuccess,
|
||||
isResistance,
|
||||
isCombat,
|
||||
isRangedDefense,
|
||||
weaponType,
|
||||
weaponName,
|
||||
weaponDegats,
|
||||
@@ -243,66 +246,49 @@ export class CelestopolRoll extends Roll {
|
||||
// Test de résistance échoué → cocher automatiquement la prochaine case de blessure
|
||||
const actor = game.actors.get(options.actorId)
|
||||
if (isResistance && actor && roll.options.resultType === "failure") {
|
||||
const wounds = actor.system.blessures
|
||||
const nextIdx = [1,2,3,4,5,6,7,8].find(i => !wounds[`b${i}`]?.checked)
|
||||
if (nextIdx) {
|
||||
await actor.update({ [`system.blessures.b${nextIdx}.checked`]: true })
|
||||
roll.options.woundTaken = nextIdx
|
||||
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
|
||||
if (nextLvl <= 8) {
|
||||
await actor.update({ "system.blessures.lvl": nextLvl })
|
||||
roll.options.woundTaken = nextLvl
|
||||
}
|
||||
}
|
||||
|
||||
// Combat mêlée échoué → joueur prend une blessure
|
||||
if (isCombat && weaponType === "melee" && actor && roll.options.resultType === "failure") {
|
||||
const wounds = actor.system.blessures
|
||||
const nextIdx = [1,2,3,4,5,6,7,8].find(i => !wounds[`b${i}`]?.checked)
|
||||
if (nextIdx) {
|
||||
await actor.update({ [`system.blessures.b${nextIdx}.checked`]: true })
|
||||
roll.options.woundTaken = nextIdx
|
||||
// Mêlée échouée OU défense à distance échouée → joueur prend une blessure
|
||||
if (isCombat && (weaponType === "melee" || isRangedDefense) && actor && roll.options.resultType === "failure") {
|
||||
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
|
||||
if (nextLvl <= 8) {
|
||||
await actor.update({ "system.blessures.lvl": nextLvl })
|
||||
roll.options.woundTaken = nextLvl
|
||||
}
|
||||
}
|
||||
|
||||
await roll.toMessage({}, { rollMode: rollData.rollMode })
|
||||
|
||||
// Destin utilisé → vider la jauge (reset à 0)
|
||||
if (rollData.useDestin && actor) {
|
||||
await actor.update({
|
||||
"system.destin.lvl": 0,
|
||||
"system.destin.d1.checked": false,
|
||||
"system.destin.d2.checked": false,
|
||||
"system.destin.d3.checked": false,
|
||||
"system.destin.d4.checked": false,
|
||||
"system.destin.d5.checked": false,
|
||||
"system.destin.d6.checked": false,
|
||||
"system.destin.d7.checked": false,
|
||||
"system.destin.d8.checked": false,
|
||||
})
|
||||
}
|
||||
|
||||
// Fortune utilisée → décrémenter de 1 (min 0)
|
||||
if (rollData.useFortune && actor) {
|
||||
const currentFortune = actor.system.attributs.fortune.value ?? 0
|
||||
await actor.update({ "system.attributs.fortune.value": Math.max(0, currentFortune - 1) })
|
||||
}
|
||||
|
||||
// Puiser dans ses ressources → coche une case de spleen
|
||||
if (rollData.puiserRessources && actor) {
|
||||
const currentSpleen = actor.system.spleen.lvl ?? 0
|
||||
if (currentSpleen < 8) {
|
||||
const newLvl = currentSpleen + 1
|
||||
const key = `s${newLvl}`
|
||||
await actor.update({
|
||||
"system.spleen.lvl": newLvl,
|
||||
[`system.spleen.${key}.checked`]: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Mémoriser les préférences sur l'acteur
|
||||
// Batching de toutes les mises à jour de l'acteur en un seul appel réseau
|
||||
if (actor) {
|
||||
await actor.update({
|
||||
"system.prefs.rollMoonDie": rollData.rollMoonDie,
|
||||
"system.prefs.difficulty": difficulty,
|
||||
})
|
||||
const updateData = {}
|
||||
|
||||
if (rollData.useDestin) {
|
||||
updateData["system.destin.lvl"] = 0
|
||||
}
|
||||
|
||||
if (rollData.useFortune) {
|
||||
const currentFortune = actor.system.attributs.fortune.value ?? 0
|
||||
updateData["system.attributs.fortune.value"] = Math.max(0, currentFortune - 1)
|
||||
}
|
||||
|
||||
if (rollData.puiserRessources) {
|
||||
const currentSpleen = actor.system.spleen.lvl ?? 0
|
||||
if (currentSpleen < 8) {
|
||||
updateData["system.spleen.lvl"] = currentSpleen + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Mémoriser les préférences
|
||||
updateData["system.prefs.rollMoonDie"] = rollData.rollMoonDie
|
||||
updateData["system.prefs.difficulty"] = difficulty
|
||||
|
||||
await actor.update(updateData)
|
||||
}
|
||||
|
||||
return roll
|
||||
@@ -421,6 +407,7 @@ export class CelestopolRoll extends Roll {
|
||||
weaponName: this.options.weaponName ?? null,
|
||||
weaponDegats: this.options.weaponDegats ?? null,
|
||||
weaponType: this.options.weaponType ?? null,
|
||||
isRangedDefense: this.options.isRangedDefense ?? false,
|
||||
woundTaken: this.options.woundTaken ?? null,
|
||||
// Dé de lune
|
||||
hasMoonDie: moonDieResult !== null,
|
||||
|
||||
Reference in New Issue
Block a user