Implements actions management
All checks were successful
Release Creation / build (release) Successful in 53s

This commit is contained in:
2025-04-17 17:35:45 +02:00
parent cb60bb6027
commit 87a90b73ce
28 changed files with 487 additions and 226 deletions

View File

@ -247,7 +247,7 @@ export default class LethalFantasyRoll extends Roll {
hasChangeDice = false
hasMaxValue = false
hasExplode = false
options.rollTarget.value = 0
options.rollTarget.value = 0
} else if (options.rollType.includes("weapon-damage")) {
options.rollName = options.rollTarget.name
@ -602,6 +602,230 @@ export default class LethalFantasyRoll extends Roll {
}
static async promptCombatAction(options = {}) {
const rollModes = 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,
default: "public",
})
let combatant = game.combats.get(options.combatId)?.combatants?.get(options.combatantId)
if (!combatant) {
console.error("No combatant found for this combat")
return
}
let currentAction = combatant.getFlag(SYSTEM.id, "currentAction")
let dialogContext = {
progressionDiceId: "",
fieldRollMode,
rollModes,
currentAction,
...options
}
const content = await renderTemplate("systems/fvtt-lethal-fantasy/templates/combat-action-dialog.hbs", dialogContext)
let buttons = []
if (currentAction) {
if (currentAction.type === "weapon") {
buttons.push({
action: "roll",
label: "Roll progression dice",
callback: (event, button, dialog) => {
return "rollProgressionDice"
},
})
} else if (currentAction.type === "spell" || currentAction.type === "miracle") {
let label = ""
if ( currentAction.castingDone) {
label = "Roll lethargy dice"
} else {
label = "Wait casting time"
}
buttons.push({
action: "roll",
label: label,
callback: (event, button, dialog) => {
return "rollLethargyDice"
},
})
}
} else {
buttons.push({
action: "roll",
label: "Select action",
callback: (event, button, dialog) => {
const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value
return obj
}, {})
return output
},
},
)
}
buttons.push({
action: "cancel",
label: "Other action, not listed here",
callback: (event, button, dialog) => {
return null;
}
})
const rollContext = await foundry.applications.api.DialogV2.wait({
window: { title: "Combat Action Dialog" },
classes: ["lethalfantasy"],
content,
buttons,
rejectClose: false // Click on Close button will not launch an error
})
console.log("RollContext", dialogContext, rollContext)
// If action is cancelled, exit
if (rollContext === null || rollContext === "cancel") {
await combatant.setFlag(SYSTEM.id, "currentAction", "")
let message = `${combatant.name} : Other action, progression reset`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
return
}
// Setup the current action
if (!currentAction || currentAction === "") {
// Get the item from the returned selectedChoice value
let selectedChoice = rollContext.selectedChoice
let rangedMode
if (selectedChoice.match("simpleAim")) {
selectedChoice = selectedChoice.replace("simpleAim", "")
rangedMode = "simpleAim"
}
if (selectedChoice.match("carefulAim")) {
selectedChoice = selectedChoice.replace("carefulAim", "")
rangedMode = "carefulAim"
}
if (selectedChoice.match("focusedAim")) {
selectedChoice = selectedChoice.replace("focusedAim", "")
rangedMode = "focusedAim"
}
let selectedItem = combatant.actor.items.find(i => i.id === selectedChoice)
// Setup flag for combat action usage
let actionItem = foundry.utils.duplicate(selectedItem)
actionItem.progressionCount = 1
actionItem.rangedMode = rangedMode
actionItem.castingTime = 1
actionItem.castingDone = false
// Set the flag on the combatant
await combatant.setFlag(SYSTEM.id, "currentAction", actionItem)
let message = `${combatant.name} action : ${selectedItem.name}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
combatant.actor.prepareRoll(actionItem.type === "weapon" ? "weapon-attack" : "spell-attack", selectedChoice)
return
}
if (currentAction) {
if (rollContext === "rollLethargyDice") {
if ( !currentAction.castingDone) {
if ( currentAction.castingTime <= currentAction.system.castingTime) {
let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${currentAction.system.castingTime}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime += 1
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
return
} else {
let message = `Spell ${currentAction.name} has ben casted !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime = 1
currentAction.castingDone = true
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
return
}
} else {
// Roll lethargy dice
let dice = LethalFantasyUtils.getLethargyDice(currentAction.system.level)
let roll = new Roll(dice)
await roll.evaluate()
let max = roll.dice[0].faces - 1
let toCompare = Math.min(options.rollProgressionCount, max)
if (roll.total <= toCompare) {
// Notify that the player can act now with a chat message
let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyOK", { name: combatant.actor.name, weapon: currentAction.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
// Update the combatant progression count
await combatant.setFlag(SYSTEM.id, "currentAction", "")
// Display the action selection window again
combatant.actor.system.rollProgressionDice(options.combatId, options.combatantId )
} else {
// Notify that the player cannot act now with a chat message
currentAction.progressionCount += 1
combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyKO", { name: combatant.actor.name, weapon: currentAction.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
}
}
}
if (rollContext === "rollProgressionDice") {
let formula = currentAction.system.combatProgressionDice
if (currentAction?.rangedMode) {
let toSplit = currentAction.system.speed[currentAction.rangedMode]
let split = toSplit.split("+")
currentAction.rangedLoad = Number(split[0]) || 0
formula = split[1]
console.log("Ranged Mode", currentAction.rangedMode, currentAction.rangedLoad, formula)
}
// Range weapon loading
if (!currentAction.weaponLoaded && currentAction.rangedLoad) {
if (currentAction.progressionCount <= currentAction.rangedLoad) {
let message = `Ranged weapon ${currentAction.name} is loading, loading count : ${currentAction.progressionCount}/${currentAction.rangedLoad}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.progressionCount += 1
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
} else {
let message = `Ranged weapon ${currentAction.name} is loaded !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.weaponLoaded = true
currentAction.progressionCount = 1
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
}
return
}
// Melee mode
let isMonster = combatant.actor.type === "monster"
// Get the dice and roll it if
let roll = new Roll(formula)
await roll.evaluate()
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 })
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
if (roll.total <= max) {
// Notify that the player can act now with a chat message
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionOK", { isMonster, name: combatant.actor.name, weapon: currentAction.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
// Update the combatant progression count
await combatant.setFlag(SYSTEM.id, "currentAction", "")
// Display the action selection window again
combatant.actor.system.rollProgressionDice(options.combatId, options.combatantId )
} else {
// Notify that the player cannot act now with a chat message
currentAction.progressionCount += 1
combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKO", { isMonster, name: combatant.actor.name, weapon: currentAction.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
}
}
}
}
static async promptProgressionDice(options = {}) {
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
@ -654,11 +878,11 @@ export default class LethalFantasyRoll extends Roll {
rejectClose: false // Click on Close button will not launch an error
})
console.log("RollContext", dialogContext,rollContext)
console.log("RollContext", dialogContext, rollContext)
let combat = game.combats.get(options.combatId)
let actor = game.actors.get(options.actorId)
if ( rollContext === "casting") {
if (rollContext === "casting") {
combat.setCasting(options.combatantId)
let message = `Starting casting a spell !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
@ -677,25 +901,25 @@ export default class LethalFantasyRoll extends Roll {
// Get the weapons from the actor items
let rangedMode
let searchId = rollContext.progressionDiceId
if ( searchId.match("simpleAim")) {
if (searchId.match("simpleAim")) {
searchId = searchId.replace("simpleAim", "")
rangedMode = "simpleAim"
rangedMode = "simpleAim"
}
if ( searchId.match("carefulAim")) {
if (searchId.match("carefulAim")) {
searchId = searchId.replace("carefulAim", "")
rangedMode = "carefulAim"
rangedMode = "carefulAim"
}
if ( searchId.match("focusedAim")) {
if (searchId.match("focusedAim")) {
searchId = searchId.replace("focusedAim", "")
rangedMode = "focusedAim"
rangedMode = "focusedAim"
}
if (searchId.match("spell")) {
searchId = searchId.replace("spell", "")
let spell = actor.items.find(i => i.type === "spell" && i.id === searchId)
let dice = LethalFantasyUtils.getLethargyDice(spell.system.level)
let dice = LethalFantasyUtils.getLethargyDice(spell.system.level)
if (combat.isCasting(options.combatantId)) {
if (options.rollProgressionCount <= spell.system.castingTime ) {
if (options.rollProgressionCount <= spell.system.castingTime) {
let message = `Spell casting time : ${spell.name}, count : ${options.rollProgressionCount}/${spell.system.castingTime}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
return
@ -707,7 +931,7 @@ export default class LethalFantasyRoll extends Roll {
combat.resetProgression(options.combatantId)
return
}
} else{
} else {
let formula = dice
let roll = new Roll(formula)
await roll.evaluate()
@ -740,7 +964,7 @@ export default class LethalFantasyRoll extends Roll {
console.log("Ranged Mode", rangedMode, rangedLoad, formula, options.rollProgressionCount)
}
if (rangedLoad && options.rollProgressionCount <= rangedLoad ) {
if (rangedLoad && options.rollProgressionCount <= rangedLoad) {
let message = `Ranged weapon ${weapon.name} is loading, loading count : ${options.rollProgressionCount}/${rangedLoad}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
return