Corrections diverses autout du combat

This commit is contained in:
2026-04-13 14:19:24 +02:00
parent 44cc07db73
commit d69144f506
46 changed files with 1340 additions and 241 deletions

View File

@@ -78,6 +78,22 @@ export class CelestopolRoll extends Roll {
.reduce((sum, a) => sum + Math.abs(a.system.protection ?? a.system.malus ?? 0), 0)
}
/**
* Résout un acteur à partir de son UUID si disponible, sinon via son identifiant monde.
* @param {object} ref
* @param {string|null} ref.actorUuid
* @param {string|null} ref.actorId
* @returns {Promise<Actor|null>}
*/
static async resolveActor({ actorUuid = null, actorId = null } = {}) {
if (actorUuid) {
const actorFromUuid = await fromUuid(actorUuid)
if (actorFromUuid) return actorFromUuid
}
if (actorId) return game.actors.get(actorId) ?? null
return null
}
/**
* Ouvre le dialogue de configuration du jet via DialogV2 et exécute le jet.
* @param {object} options
@@ -298,11 +314,14 @@ export class CelestopolRoll extends Roll {
const useFortune = fortuneValue > 0 && (rollContext.useFortune === true || rollContext.useFortune === "true")
const puiserRessources = rollContext.puiserRessources === true || rollContext.puiserRessources === "true"
const rollMoonDie = rollContext.rollMoonDie === true || rollContext.rollMoonDie === "true"
const selectedCombatTargetId = typeof rollContext.targetSelect === "string" ? rollContext.targetSelect : ""
const selectedCombatTarget = selectedCombatTargetId
? availableTargets.find(t => t.id === selectedCombatTargetId) ?? null
const selectedCombatTargetRef = typeof rollContext.targetSelect === "string" ? rollContext.targetSelect : ""
const selectedCombatTarget = selectedCombatTargetRef
? availableTargets.find(t => t.uuid === selectedCombatTargetRef || t.id === selectedCombatTargetRef) ?? null
: null
const resolvedWeaponName = (isRangedDefense && selectedCombatTarget?.weaponName) ? selectedCombatTarget.weaponName : weaponName
const resolvedWeaponDegats = (isRangedDefense && selectedCombatTarget?.weaponDegats) ? selectedCombatTarget.weaponDegats : weaponDegats
const targetActorId = selectedCombatTarget?.id || ""
const targetActorUuid = selectedCombatTarget?.uuid || ""
const targetActorName = selectedCombatTarget?.name || ""
// En résistance : forcer puiser=false, lune=false, fortune=false, destin=false
@@ -352,9 +371,10 @@ export class CelestopolRoll extends Roll {
isCombat,
isRangedDefense,
weaponType,
weaponName,
weaponDegats,
weaponName: resolvedWeaponName,
weaponDegats: resolvedWeaponDegats,
targetActorId,
targetActorUuid,
targetActorName,
availableTargets,
rangedMod: effectiveRangedMod,
@@ -375,7 +395,10 @@ export class CelestopolRoll extends Roll {
roll.computeResult()
// Test de résistance échoué → cocher automatiquement la prochaine case de blessure
const actor = game.actors.get(options.actorId)
const actor = await this.resolveActor({
actorUuid: options.actorUuid ?? null,
actorId: options.actorId ?? null,
})
if (isResistance && actor && roll.options.resultType === "failure") {
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
if (nextLvl <= 8) {
@@ -384,16 +407,25 @@ export class CelestopolRoll extends Roll {
}
}
// Mêlée échouée OU défense à distance échouée → joueur prend une blessure
// Mêlée échouée OU défense à distance échouée → le protagoniste subit les dégâts de l'arme PNJ
if (isCombat && (weaponType === "melee" || isRangedDefense) && actor && roll.options.resultType === "failure") {
const nextLvl = (actor.system.blessures.lvl ?? 0) + 1
if (nextLvl <= 8) {
const incomingWounds = this.getIncomingWounds(resolvedWeaponDegats)
const protection = this.getActorArmorProtection(actor)
const appliedWounds = incomingWounds === null
? 1
: Math.max(0, incomingWounds - protection)
if (appliedWounds > 0) {
const nextLvl = Math.min(8, (actor.system.blessures.lvl ?? 0) + appliedWounds)
await actor.update({ "system.blessures.lvl": nextLvl })
roll.options.woundTaken = nextLvl
roll.options.woundTakenCount = appliedWounds
roll.options.incomingWounds = incomingWounds
roll.options.selectedTargetProtection = protection
roll.options.selectedTargetAppliedWounds = appliedWounds
}
}
await roll.toMessage({}, { rollMode: rollData.rollMode })
await roll.toMessage({}, { messageMode: rollData.rollMode })
// Batching de toutes les mises à jour de l'acteur en un seul appel réseau
if (actor) {
@@ -506,12 +538,16 @@ export class CelestopolRoll extends Roll {
const incomingWounds = isWeaponHit ? this.constructor.getIncomingWounds(weaponDegats) : null
const hasVariableDamage = isWeaponHit && incomingWounds === null
const targetActorId = this.options.targetActorId ?? ""
const targetActorUuid = this.options.targetActorUuid ?? ""
const targetActorName = this.options.targetActorName ?? ""
const availableTargets = (this.options.availableTargets ?? []).map(target => ({
...target,
selected: target.id === targetActorId,
selected: target.uuid === targetActorUuid || target.id === targetActorId,
}))
const selectedTargetActor = targetActorId ? game.actors.get(targetActorId) : null
const selectedTargetActor = await this.constructor.resolveActor({
actorUuid: targetActorUuid,
actorId: targetActorId,
})
const selectedTargetProtection = selectedTargetActor
? this.constructor.getActorArmorProtection(selectedTargetActor)
: null
@@ -569,6 +605,7 @@ export class CelestopolRoll extends Roll {
weaponType: this.options.weaponType ?? null,
isRangedDefense: this.options.isRangedDefense ?? false,
woundTaken: this.options.woundTaken ?? null,
woundTakenCount: this.options.woundTakenCount ?? null,
situationMod: this.options.situationMod ?? 0,
rangedMod: this.options.rangedMod ?? 0,
hasDamageSummary: isWeaponHit,
@@ -577,6 +614,7 @@ export class CelestopolRoll extends Roll {
hasVariableDamage,
canApplyWeaponDamage: incomingWounds !== null,
targetActorId,
targetActorUuid,
targetActorName,
selectedTargetProtection,
selectedTargetAppliedWounds,
@@ -595,14 +633,40 @@ export class CelestopolRoll extends Roll {
}
/** @override */
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
async toMessage(messageData = {}, { messageMode, rollMode, create = true } = {}) {
if (rollMode) {
messageMode = Roll._mapLegacyRollMode(rollMode)
}
messageMode ||= game.settings.get("core", "messageMode")
if (!this._evaluated) await this.evaluate({ allowInteractive: messageMode !== "blind" })
const skillLocalized = this.skillLabel ? game.i18n.localize(this.skillLabel) : ""
const statLocalized = this.options.statLabel
? game.i18n.localize(this.options.statLabel) : ""
const flavor = statLocalized
? `<strong>${statLocalized} ${skillLocalized}</strong>`
: `<strong>${skillLocalized}</strong>`
return super.toMessage({ flavor, ...messageData }, { rollMode })
const speakerActor = await this.constructor.resolveActor({
actorUuid: this.options.actorUuid ?? null,
actorId: this.options.actorId ?? null,
})
const content = await this.render({ isPrivate: messageMode !== "public" })
const chatData = foundry.utils.mergeObject({
author: game.user.id,
content,
flavor,
sound: CONFIG.sounds.dice,
rolls: [this],
speaker: speakerActor ? ChatMessage.getSpeaker({ actor: speakerActor }) : undefined,
style: CONST.CHAT_MESSAGE_STYLES.OTHER,
}, messageData)
const cls = foundry.utils.getDocumentClass("ChatMessage")
const msg = new cls(chatData)
msg.applyMode(messageMode)
if (create) return cls.create(msg)
return msg.toObject()
}
/**
@@ -641,7 +705,7 @@ export class CelestopolRoll extends Roll {
content,
speaker,
rolls: [roll],
style: CONST.CHAT_MESSAGE_STYLES?.ROLL ?? 5,
style: CONST.CHAT_MESSAGE_STYLES.OTHER,
})
}
}