Improve init for monsters and some fixwes around shields
Release Creation / build (release) Successful in 48s

This commit is contained in:
2026-05-17 13:22:29 +02:00
parent 54421e4a83
commit db3e8b5d35
36 changed files with 366 additions and 352 deletions
+22
View File
@@ -18,6 +18,7 @@ export class LethalFantasyCombatTracker extends foundry.applications.sidebar.tab
actions: {
initiativePlus: LethalFantasyCombatTracker.#initiativePlus,
initiativeMinus: LethalFantasyCombatTracker.#initiativeMinus,
rollMonsterProgression: LethalFantasyCombatTracker.#rollMonsterProgression,
},
});
@@ -49,6 +50,15 @@ export class LethalFantasyCombatTracker extends foundry.applications.sidebar.tab
c.update({ 'initiative': newInit });
}
/**
* Roll progression dice for all monster combatants that are eligible this round.
* @param {Event} ev Click event.
*/
static async #rollMonsterProgression(ev) {
ev.preventDefault();
await game.combat.rollMonsterProgression();
}
activateListeners(html) {
super.activateListeners(html);
// Display Combat settings
@@ -130,6 +140,17 @@ export class LethalFantasyCombat extends Combat {
return this;
}
/** Roll progression dice for all eligible monster combatants this round. Called manually by the GM. */
async rollMonsterProgression() {
const currentRound = this.round;
for (let c of this.combatants) {
if (c.actor.type !== "monster") continue;
if (c.initiative !== null && currentRound >= c.initiative) {
await c.actor.system.rollProgressionDice(this.id, c.id);
}
}
}
resetProgression(cId) {
let c = this.combatants.get(cId);
c.update({ 'system.progressionCount': 0 });
@@ -203,6 +224,7 @@ export class LethalFantasyCombat extends Combat {
for (let c of this.combatants) {
if (nextRound >= c.initiative) {
if (c.actor.type === "monster") continue; // Monsters roll manually via the "Roll Monsters" button
const playerOwner = game.users.find(u => u.active && !u.isGM && u.character?.id === c.actor.id);
if (game.user.isGM && playerOwner) {
game.socket.emit(`system.${SYSTEM.id}`, { type: "rollProgressionDice", userId: playerOwner.id, progressionCount: c.system.progressionCount + 1, actorId: c.actor.id, combatId: this.id, combatantId: c.id });
+1 -1
View File
@@ -147,6 +147,6 @@ export async function rollFreeDie(dieType, count = 1, explode = false) {
content,
sound: CONFIG.sounds.dice,
}
ChatMessage.applyRollMode(msgData, rollMode)
ChatMessage.applyMode(msgData, rollMode)
await ChatMessage.create(msgData)
}
@@ -172,7 +172,7 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
static async #onRollInitiative(event, target) {
+1 -1
View File
@@ -111,7 +111,7 @@ export default class LethalFantasyMonsterSheet extends LethalFantasyActorSheet {
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
static async #onRollInitiative(event, target) {
+9 -9
View File
@@ -320,7 +320,7 @@ export default class LethalFantasyRoll extends Roll {
hasModifier = false
}
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes);
console.log("Roll mode", rollModes)
const fieldRollMode = new foundry.data.fields.StringField({
@@ -676,7 +676,7 @@ export default class LethalFantasyRoll extends Roll {
/* ***********************************************************/
static async promptInitiative(options = {}) {
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
blank: false,
@@ -730,7 +730,7 @@ export default class LethalFantasyRoll extends Roll {
let initRoll = new Roll(formula, options.data)
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}` }, { messageMode: rollContext.visibility })
if (game?.dice3d && initRoll.dice?.length) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
@@ -744,7 +744,7 @@ export default class LethalFantasyRoll extends Roll {
/* ***********************************************************/
static async promptCombatAction(options = {}) {
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes); // v12 : Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
blank: false,
@@ -1001,7 +1001,7 @@ export default class LethalFantasyRoll extends Roll {
let max = roll.dice[0].faces - 1
max = Math.min(currentAction.progressionCount, max)
let msg = await roll.toMessage({ flavor: `Progression Roll for ${currentAction.name}, progression count : ${currentAction.progressionCount}/${max}` }, { rollMode: rollContext.visibility })
let msg = await roll.toMessage({ flavor: `Progression Roll for ${currentAction.name}, progression count : ${currentAction.progressionCount}/${max}` }, { messageMode: rollContext.visibility })
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
@@ -1043,7 +1043,7 @@ export default class LethalFantasyRoll extends Roll {
/* ***********************************************************/
static async promptRangedDefense(options = {}) {
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes);
const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes);
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
blank: false,
@@ -1332,11 +1332,11 @@ export default class LethalFantasyRoll extends Roll {
*
* @param {Object} [messageData={}] Additional data to include in the message.
* @param {Object} options Options for message creation.
* @param {string} options.rollMode The mode of the roll (e.g., public, private).
* @param {string} options.messageMode The mode of the roll (e.g., public, private).
* @param {boolean} [options.create=true] Whether to create the message.
* @returns {Promise} - A promise that resolves when the message is created.
*/
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
async toMessage(messageData = {}, { messageMode, create = true } = {}) {
return await super.toMessage(
{
isSave: this.isSave,
@@ -1354,7 +1354,7 @@ export default class LethalFantasyRoll extends Roll {
rollData: this.rollData,
...messageData,
},
{ rollMode, create },
{ messageMode, create },
)
}
+2 -2
View File
@@ -297,7 +297,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
async rollInitiative(combatId = undefined, combatantId = undefined) {
@@ -318,7 +318,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
async rollProgressionDice(combatId, combatantId, rollProgressionCount) {
+3 -3
View File
@@ -180,7 +180,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
async prepareMonsterRoll(rollType, rollKey, rollDice = undefined, tokenId = undefined, damageModifier = undefined, defenderId = undefined, defenderTokenId = undefined, extraShieldDr = 0) {
@@ -305,7 +305,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
async rollProgressionDice(combatId, combatantId) {
@@ -317,7 +317,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
return
}
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes)
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
blank: false,
+16 -9
View File
@@ -401,7 +401,7 @@ export default class LethalFantasyUtils {
defenderTokenId,
isRanged: true
}
await roll.toMessage({}, { rollMode: roll.options.rollMode })
await roll.toMessage({}, { messageMode: roll.options.rollMode })
}
return
}
@@ -1092,25 +1092,32 @@ export default class LethalFantasyUtils {
static async applyDamage(message, event) {
// Récupérer les données du message
let combatantId = event.currentTarget.dataset.combatantId
if (!combatantId || !game.combat) {
if (!combatantId) {
ui.notifications.error("No combatant selected")
return
}
let combatant = game.combat.combatants.get(combatantId)
if (!combatant) {
ui.notifications.error("Combatant not found")
return
// Try to find the target: first as a combat combatant, then as a scene token
let targetActor = null
if (game.combat) {
const combatant = game.combat.combatants.get(combatantId)
if (combatant) {
targetActor = combatant.token?.actor || game.actors.get(combatant.actorId)
}
}
if (!targetActor) {
// Fall back to scene token lookup (non-combat tokens use tokenId as their combatantId)
const token = canvas.tokens?.placeables?.find(t => t.id === combatantId)
targetActor = token?.actor
}
let targetActor = combatant.token?.actor || game.actors.get(combatant.actorId)
if (!targetActor) {
ui.notifications.error("Target actor not found")
return
}
// Récupérer les données de dégâts du message
let damageTotal = message.rolls[0]?.total || 0
// Use options.rollTotal (includes weapon modifier bonus) rather than roll.total (dice formula only)
let damageTotal = message.rolls[0]?.options?.rollTotal ?? message.rolls[0]?.total ?? 0
let weaponName = message.rolls[0]?.options?.rollName || "Unknown Weapon"
// Calculer les DR