Fix inititiative rolls
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,4 +7,5 @@ styles/*.css
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
.history
|
.history
|
||||||
|
.github/
|
||||||
|
|
||||||
|
|||||||
@@ -267,9 +267,13 @@ Hooks.on(hookName, (message, html, data) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Envoyer le message socket à l'utilisateur contrôlant le combatant
|
// Envoyer le message socket à l'utilisateur contrôlant le combatant
|
||||||
const owners = game.users.filter(u =>
|
// Only consider active (online) users; fall back to any active GM for unowned/GM monsters.
|
||||||
combatant.actor.testUserPermission(u, "OWNER")
|
let owners = game.users.filter(u =>
|
||||||
|
u.active && combatant.actor.testUserPermission(u, "OWNER")
|
||||||
)
|
)
|
||||||
|
if (owners.length === 0) {
|
||||||
|
owners = game.users.filter(u => u.active && u.isGM)
|
||||||
|
}
|
||||||
|
|
||||||
// Récupérer l'acteur attaquant pour vérifier qui l'a lancé
|
// Récupérer l'acteur attaquant pour vérifier qui l'a lancé
|
||||||
const attacker = game.actors.get(attackerId)
|
const attacker = game.actors.get(attackerId)
|
||||||
@@ -546,12 +550,27 @@ Hooks.on("createChatMessage", async (message) => {
|
|||||||
|
|
||||||
// Calculer les DR
|
// Calculer les DR
|
||||||
const armorDR = defender.computeDamageReduction() || 0
|
const armorDR = defender.computeDamageReduction() || 0
|
||||||
|
|
||||||
// Appliquer les dégâts avec armure DR par défaut
|
|
||||||
const finalDamage = Math.max(0, damageTotal - armorDR)
|
const finalDamage = Math.max(0, damageTotal - armorDR)
|
||||||
await defender.applyDamage(-finalDamage)
|
|
||||||
|
|
||||||
// Créer un message de confirmation
|
// For unlinked tokens (default for monsters), we need the specific token actor, not the base
|
||||||
|
// world actor — otherwise applyDamage would modify the base actor and affect every unlinked
|
||||||
|
// copy of that monster. Prefer the combatant actor, fall back to canvas scan.
|
||||||
|
const defenderCombatant = game.combat?.combatants?.find(c => c.actorId === defender.id)
|
||||||
|
const defenderTokenId = defenderCombatant?.token?.id
|
||||||
|
?? canvas.tokens?.placeables?.find(t => t.actor?.id === defender.id)?.id
|
||||||
|
?? null
|
||||||
|
|
||||||
|
// Apply damage. If the current user does not own the defender (e.g. player hitting a GM monster),
|
||||||
|
// route the HP update to the GM via socket. The confirmation message is still created here
|
||||||
|
// since all users can create chat messages.
|
||||||
|
if (defender.isOwner) {
|
||||||
|
const tokenActor = defenderCombatant?.actor ?? defender
|
||||||
|
await tokenActor.applyDamage(-finalDamage)
|
||||||
|
} else {
|
||||||
|
game.socket.emit(`system.${SYSTEM.id}`, { type: "applyDamage", actorId: defender.id, tokenId: defenderTokenId, damage: -finalDamage })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer un message de confirmation (visible to GM only)
|
||||||
const messageContent = await foundry.applications.handlebars.renderTemplate(
|
const messageContent = await foundry.applications.handlebars.renderTemplate(
|
||||||
"systems/fvtt-lethal-fantasy/templates/damage-applied-message.hbs",
|
"systems/fvtt-lethal-fantasy/templates/damage-applied-message.hbs",
|
||||||
{
|
{
|
||||||
@@ -566,7 +585,8 @@ Hooks.on("createChatMessage", async (message) => {
|
|||||||
|
|
||||||
await ChatMessage.create({
|
await ChatMessage.create({
|
||||||
content: messageContent,
|
content: messageContent,
|
||||||
speaker: ChatMessage.getSpeaker({ actor: defender })
|
speaker: ChatMessage.getSpeaker({ actor: defender }),
|
||||||
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -681,7 +681,15 @@ export default class LethalFantasyRoll extends Roll {
|
|||||||
rejectClose: false // Click on Close button will not launch an error
|
rejectClose: false // Click on Close button will not launch an error
|
||||||
})
|
})
|
||||||
|
|
||||||
let initRoll = new Roll(`min(${rollContext.initiativeDice}, ${options.maxInit})`, options.data, rollContext)
|
if (!rollContext) return
|
||||||
|
|
||||||
|
// When the value is a plain number (e.g. "1" for Declared Ready on Alert), wrapping it in
|
||||||
|
// min(1, maxInit) produces a dice-less formula that FoundryVTT cannot evaluate to a valid
|
||||||
|
// total. Use the constant directly; min() is only needed for actual dice expressions.
|
||||||
|
const isDiceFormula = /[dD]/.test(rollContext.initiativeDice)
|
||||||
|
const formula = isDiceFormula ? `min(${rollContext.initiativeDice}, ${options.maxInit})` : rollContext.initiativeDice
|
||||||
|
|
||||||
|
let initRoll = new Roll(formula, options.data)
|
||||||
await initRoll.evaluate()
|
await initRoll.evaluate()
|
||||||
let msg = await initRoll.toMessage({ flavor: `Initiative for ${options.actorName}` }, { rollMode: rollContext.visibility })
|
let msg = await initRoll.toMessage({ flavor: `Initiative for ${options.actorName}` }, { rollMode: rollContext.visibility })
|
||||||
if (game?.dice3d) {
|
if (game?.dice3d) {
|
||||||
@@ -690,7 +698,7 @@ export default class LethalFantasyRoll extends Roll {
|
|||||||
|
|
||||||
if (options.combatId && options.combatantId) {
|
if (options.combatId && options.combatantId) {
|
||||||
let combat = game.combats.get(options.combatId)
|
let combat = game.combats.get(options.combatId)
|
||||||
combat.updateEmbeddedDocuments("Combatant", [{ _id: options.combatantId, initiative: initRoll.total, 'system.progressionCount': 0 }]);
|
await combat.updateEmbeddedDocuments("Combatant", [{ _id: options.combatantId, initiative: initRoll.total, 'system.progressionCount': 0 }])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,16 @@ export default class LethalFantasyUtils {
|
|||||||
console.log(`handleSocketEvent !`, msg)
|
console.log(`handleSocketEvent !`, msg)
|
||||||
let actor
|
let actor
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
|
case "applyDamage":
|
||||||
|
if (game.user.isGM) {
|
||||||
|
// Prefer the specific token actor (correct for unlinked monsters); fall back to world actor.
|
||||||
|
actor = msg.tokenId
|
||||||
|
? canvas.tokens?.placeables?.find(t => t.id === msg.tokenId)?.actor
|
||||||
|
: (game.combat?.combatants?.find(c => c.actorId === msg.actorId)?.actor
|
||||||
|
?? game.actors.get(msg.actorId))
|
||||||
|
if (actor) actor.applyDamage(msg.damage)
|
||||||
|
}
|
||||||
|
break
|
||||||
case "rollInitiative":
|
case "rollInitiative":
|
||||||
actor = game.actors.get(msg.actorId)
|
actor = game.actors.get(msg.actorId)
|
||||||
actor.system.rollInitiative(msg.combatId, msg.combatantId)
|
actor.system.rollInitiative(msg.combatId, msg.combatantId)
|
||||||
|
|||||||
Reference in New Issue
Block a user