New combat management and various improvments
All checks were successful
Release Creation / build (release) Successful in 48s

This commit is contained in:
2026-01-19 23:22:32 +01:00
parent a06dfa0ae9
commit 52877e3a68
46 changed files with 4655 additions and 475 deletions

View File

@@ -273,7 +273,7 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
* @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
* @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
*/
async roll(rollType, rollTarget) {
async roll(rollType, rollTarget, defenderId) {
const hasTarget = false
let roll = await LethalFantasyRoll.prompt({
rollType,
@@ -282,7 +282,8 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
actorName: this.parent.name,
actorImage: this.parent.img,
hasTarget,
target: false
target: false,
defenderId
})
if (!roll) return null
@@ -311,6 +312,13 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
}
async rollProgressionDice(combatId, combatantId, rollProgressionCount) {
let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
// Don't roll if the combatant is defeated
if (combatant?.isDefeated) {
ui.notifications.warn(`${this.parent.name} is defeated and cannot attack.`)
return
}
// Get all weapons from the actor
let weapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "melee")

View File

@@ -122,6 +122,10 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
shieldDamageReduction: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
shieldDefenseDice: new fields.StringField({ required: true, nullable: false, initial: "d4" })
})
schema.combatHTH = new fields.SchemaField({
attack1: attackField("1"),
attack2: attackField("2")
})
return schema
@@ -137,7 +141,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
* @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
* @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
*/
async roll(rollType, rollTarget) {
async roll(rollType, rollTarget, defenderId = undefined) {
const hasTarget = false
let roll = await LethalFantasyRoll.prompt({
rollType,
@@ -146,14 +150,15 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
actorName: this.parent.name,
actorImage: this.parent.img,
hasTarget,
target: false
target: false,
defenderId
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
async prepareMonsterRoll(rollType, rollKey, rollDice = undefined, tokenId = undefined, damageModifier = undefined) {
async prepareMonsterRoll(rollType, rollKey, rollDice = undefined, tokenId = undefined, damageModifier = undefined, defenderId = undefined) {
let rollTarget
switch (rollType) {
case "monster-attack":
@@ -166,6 +171,18 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
rollTarget.damageModifier = damageModifier
}
break
case "monster-attack-hth":
case "monster-defense-hth":
case "monster-damage-hth":
rollTarget = foundry.utils.duplicate(this.combatHTH[rollKey])
rollTarget.rollKey = rollKey
// Si damageModifier est fourni (depuis le chat), l'utiliser au lieu de celui de la fiche
if (damageModifier !== undefined && rollType === "monster-damage-hth") {
rollTarget.damageModifier = damageModifier
}
// Convertir le type de roll pour utiliser les mêmes handlers que les attaques normales
rollType = rollType.replace("-hth", "")
break
case "monster-skill":
rollTarget = foundry.utils.duplicate(this.resists[rollKey])
rollTarget.rollKey = rollKey
@@ -237,7 +254,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
if (rollTarget) {
rollTarget.tokenId = tokenId
console.log(rollTarget)
await this.roll(rollType, rollTarget)
await this.roll(rollType, rollTarget, defenderId)
}
}
@@ -261,6 +278,13 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
}
async rollProgressionDice(combatId, combatantId) {
let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
// Don't roll if the combatant is defeated
if (combatant?.isDefeated) {
ui.notifications.warn(`${this.parent.name} is defeated and cannot attack.`)
return
}
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const fieldRollMode = new foundry.data.fields.StringField({
@@ -271,7 +295,6 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
let roll = new Roll("1D12")
await roll.evaluate()
let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
let msg = await roll.toMessage({ flavor: `Progression Roll for ${this.parent.name}` })
if (game?.dice3d) {
@@ -283,8 +306,16 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
let attack = this.attacks[key]
if (attack.enabled && attack.attackScore > 0 && attack.attackScore === roll.total) {
hasAttack = true
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionOKMonster", { isMonster: true, name: this.parent.name, weapon: attack.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: true,
actorName: this.parent.name,
weaponName: attack.name,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
let token = combatant?.token
this.prepareMonsterRoll("monster-attack", key, undefined, token?.id)
if (token?.object) {
@@ -293,9 +324,41 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
}
}
}
// Check Hand To Hand attacks as well
if (!hasAttack) {
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKOMonster", { isMonster: true, name: this.parent.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
for (let key in this.combatHTH) {
let attack = this.combatHTH[key]
if (attack.enabled && attack.attackScore > 0 && attack.attackScore === roll.total) {
hasAttack = true
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: true,
actorName: this.parent.name,
weaponName: `${attack.name} (HTH)`,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
let token = combatant?.token
this.prepareMonsterRoll("monster-attack-hth", key, undefined, token?.id)
if (token?.object) {
token.object?.control({ releaseOthers: true });
return canvas.animatePan(token.object.center);
}
}
}
}
if (!hasAttack) {
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
success: false,
actorName: this.parent.name,
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
}
}