8 Commits

Author SHA1 Message Date
a043117ec7 Enhance rolls and fix dialog position
All checks were successful
Release Creation / build (release) Successful in 47s
2025-04-25 21:28:17 +02:00
791a7d6b67 Enhance rolls and fix dialog position 2025-04-25 21:28:02 +02:00
2ce5088471 Fix mortal+shield again
All checks were successful
Release Creation / build (release) Successful in 44s
2025-04-24 07:08:02 +02:00
ebb7bfe3d6 Fix prayer roll
All checks were successful
Release Creation / build (release) Successful in 45s
2025-04-23 16:05:01 +02:00
16959dd52e 8 attcks for monster + fix lethargy
All checks were successful
Release Creation / build (release) Successful in 46s
2025-04-22 23:44:04 +02:00
ccebf8dc1f Auto-jump to monster token when activated 2025-04-22 16:28:10 +02:00
a2364e1252 Fix spells 2025-04-22 15:48:25 +02:00
d961e130e0 Fix actions again
All checks were successful
Release Creation / build (release) Successful in 58s
2025-04-22 08:42:01 +02:00
36 changed files with 311 additions and 375 deletions

View File

@ -1951,6 +1951,18 @@ i.lethalfantasy {
.lethalfantasy-roll-dialog fieldset { .lethalfantasy-roll-dialog fieldset {
padding: 4px; padding: 4px;
} }
.lethalfantasy-roll-dialog .goto-token-button {
justify-content: center;
align-items: center;
align-content: center;
margin-left: 0.5rem;
max-width: 8rem;
background-color: var(--color-dark-6);
color: var(--color-dark-2);
border: none;
border-radius: 4px;
padding: 0.5rem;
}
.lethalfantasy-range-defense-dialog { .lethalfantasy-range-defense-dialog {
width: 18rem; width: 18rem;
} }

View File

@ -281,6 +281,7 @@
} }
}, },
"Label": { "Label": {
"gotoToken": "Go to token",
"combatAction": "Combat action", "combatAction": "Combat action",
"currentAction": "Current ongoing action", "currentAction": "Current ongoing action",
"selectAction": "Select an action", "selectAction": "Select an action",
@ -288,6 +289,7 @@
"spell-attack": "Spell - Attack", "spell-attack": "Spell - Attack",
"miracle-power": "Miracle - Power", "miracle-power": "Miracle - Power",
"miracle-attack": "Miracle - Attack", "miracle-attack": "Miracle - Attack",
"spell": "Spell",
"will":"Will", "will":"Will",
"dodge":"Dodge", "dodge":"Dodge",
"toughness":"Toughness", "toughness":"Toughness",
@ -489,11 +491,11 @@
"rollTypeNotFound": "Roll type not found", "rollTypeNotFound": "Roll type not found",
"skillNotFound": "Skill not found", "skillNotFound": "Skill not found",
"messageProgressionOK": "{name} can perform his action !", "messageProgressionOK": "{name} can perform his action !",
"messageLethargyOK": "Lethargy ended. <br>{name} can perform a new action !", "messageLethargyOK": "{spellName} : Lethargy ended ( dice result {roll}). <br>{name} can perform a new action !",
"messageLethargyKO": "Lethargy stil ongoing ...", "messageLethargyKO": "{spellName} : Lethargy still ongoing ... ( dice result : {roll} )",
"messageProgressionKO": "{name} can't attack this second.", "messageProgressionKO": "{name} can't attack this second.",
"messageProgressionOKMonster": "{name} can attack this second with {weapon}.", "messageProgressionOKMonster": "{name} can attack this second with {weapon}.",
"messageProgressionKOMonster": "{name} can't attack this second." "messageProgressionKOMonster": "{name} can't attack this second (dice result {roll})."
}, },
"Opponent": { "Opponent": {
"FIELDS": {} "FIELDS": {}

View File

@ -138,7 +138,6 @@ export class LethalFantasyCombat extends Combat {
} }
async nextRound() { async nextRound() {
console.log('NEXT ROUND')
this.turnsDone = false this.turnsDone = false
let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently. let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently.
@ -162,13 +161,12 @@ export class LethalFantasyCombat extends Combat {
for (let c of this.combatants) { for (let c of this.combatants) {
if ( nextRound >= c.initiative) { if ( nextRound >= c.initiative) {
c.update({ 'system.progressionCount': c.system.progressionCount + 1 });
let user = game.users.find(u => u.active && u.character && u.character.id === c.actor.id); let user = game.users.find(u => u.active && u.character && u.character.id === c.actor.id);
if (user?.hasPlayerOwner) { if (user?.hasPlayerOwner) {
game.socket.emit(`system.${SYSTEM.id}`, { type: "rollProgressionDice", progressionCount: c.system.progressionCount+1, actorId: c.actor.id, combatId: this.id, combatantId: c.id }); game.socket.emit(`system.${SYSTEM.id}`, { type: "rollProgressionDice", progressionCount: c.system.progressionCount+1, actorId: c.actor.id, combatId: this.id, combatantId: c.id });
} else { } else {
user = game.users.find(u => u.active && u.isGM); user = game.users.find(u => u.active && u.isGM);
c.actor.system.rollProgressionDice(this.id, c.id, c.system.progressionCount+1); c.actor.system.rollProgressionDice(this.id, c.id);
} }
} }
} }

View File

@ -166,71 +166,8 @@ export default class LethalFantasyMonsterSheet extends LethalFantasyActorSheet {
async _onRoll(event, target) { async _onRoll(event, target) {
if (this.isEditMode) return if (this.isEditMode) return
const rollType = event.target.dataset.rollType const rollType = event.target.dataset.rollType
let rollTarget
let rollKey = event.target.dataset.rollKey let rollKey = event.target.dataset.rollKey
switch (rollType) { let rollDice = event.target.dataset?.rollDice || "0"
case "monster-attack": this.actor.system.prepareMonsterRoll(rollType, rollKey, rollDice)
case "monster-defense":
case "monster-damage":
rollTarget = foundry.utils.duplicate(this.document.system.attacks[rollKey])
rollTarget.rollKey = rollKey
break
case "monster-skill":
rollTarget = foundry.utils.duplicate(this.document.system.resists[rollKey])
rollTarget.rollKey = rollKey
break
case "save":
rollTarget = foundry.utils.duplicate(this.document.system.saves[rollKey])
rollTarget.rollKey = rollKey
rollTarget.rollDice = event.target.dataset?.rollDice
break
case "weapon-damage-small":
case "weapon-damage-medium":
case "weapon-attack":
case "weapon-defense":
let weapon = this.actor.items.find((i) => i.type === "weapon" && i.id === rollKey)
let skill
let skills = this.actor.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.actor.items.filter((i) => i.type === "skill" && i.name.toLowerCase().replace(" skill", "") === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.actor.items.filter((i) => i.type === "skill" && i.system.weaponClass === weapon.system.weaponClass)
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.5)
} else {
skills = this.actor.items.filter((i) => i.type === "skill" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass]))
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.25)
} else {
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
}
}
}
if (!weapon || !skill) {
console.error("Weapon or skill not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
rollTarget = skill
rollTarget.weapon = weapon
rollTarget.weaponSkillModifier = skill.weaponSkillModifier
rollTarget.rollKey = rollKey
rollTarget.combat = foundry.utils.duplicate(this.actor.system.combat)
break
default:
ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType))
break
}
// In all cases
console.log(rollTarget)
await this.document.system.roll(rollType, rollTarget)
} }
// #endregion
} }

View File

@ -53,10 +53,12 @@ export const MORTAL_CHOICES = {
"shirefolk": {label: "Shire Folk", value: "Shire Folk", defenseBonus: 2}, "shirefolk": {label: "Shire Folk", value: "Shire Folk", defenseBonus: 2},
"Elf": {label: "Elf", value: "Elf", defenseBonus: 0}, "Elf": {label: "Elf", value: "Elf", defenseBonus: 0},
"Half-orc": {label: "Half-Orc", value: "Half-Orc", defenseBonus: 0}, "Half-orc": {label: "Half-Orc", value: "Half-Orc", defenseBonus: 0},
"Half-Orc": {label: "Half-Orc", value: "Half-Orc", defenseBonus: 0},
"Dwarf": {label: "Dwarf", value: "Dwarf", defenseBonus: 0}, "Dwarf": {label: "Dwarf", value: "Dwarf", defenseBonus: 0},
"Half-elf": {label: "Half-Elf", value: "Half-Elf", defenseBonus: 0}, "Half-elf": {label: "Half-Elf", value: "Half-Elf", defenseBonus: 0},
"Gnome": {label: "Gnome", value: "Gnome", defenseBonus: 2}, "Gnome": {label: "Gnome", value: "Gnome", defenseBonus: 2},
"Shire Folk": {label: "Shire Folk", value: "Shire Folk", defenseBonus: 2}, "Shire Folk": {label: "Shire Folk", value: "Shire Folk", defenseBonus: 2},
"Shire folk": {label: "Shire Folk", value: "Shire Folk", defenseBonus: 2},
"Mankind": {label: "Human", value: "Human", defenseBonus: 0}, "Mankind": {label: "Human", value: "Human", defenseBonus: 0},
} }
@ -191,6 +193,7 @@ export const CHARACTERISTIC_DEFENSE = [ "int", "wis", "dex" ]
export const CHARACTERISTIC_DAMAGE = [ "str" ] export const CHARACTERISTIC_DAMAGE = [ "str" ]
export const DEFENSE_DICE_VALUES = { export const DEFENSE_DICE_VALUES = {
"0": "0",
"d3": "D3", "d3": "D3",
"d4": "D4", "d4": "D4",
"d6": "D6", "d6": "D6",

View File

@ -41,6 +41,7 @@ export default class LethalFantasyActor extends Actor {
} }
} }
/* *************************************************/
getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) { getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) {
let maxValue = 0 let maxValue = 0
let goodSkill = skills[0] let goodSkill = skills[0]
@ -68,6 +69,7 @@ export default class LethalFantasyActor extends Actor {
return goodSkill return goodSkill
} }
/* *************************************************/
async prepareRoll(rollType, rollKey, rollDice ) { async prepareRoll(rollType, rollKey, rollDice ) {
console.log("Preparing roll", rollType, rollKey, rollDice) console.log("Preparing roll", rollType, rollKey, rollDice)
let rollTarget let rollTarget

View File

@ -221,9 +221,13 @@ export default class LethalFantasyRoll extends Roll {
hasChangeDice = false hasChangeDice = false
options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier + options.rollTarget.actorModifiers.intSpellModifier options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier + options.rollTarget.actorModifiers.intSpellModifier
options.rollTarget.charModifier = options.rollTarget.actorModifiers.intSpellModifier options.rollTarget.charModifier = options.rollTarget.actorModifiers.intSpellModifier
hasStaticModifier = true // options.rollType === "spell-power" hasStaticModifier = options.rollType === "spell-power"
hasModifier = true //options.rollType !== "spell-attack" //hasModifier = options.rollType !== "spell-attack"
options.rollTarget.staticModifier = options.rollTarget.actorLevel if (hasStaticModifier) {
options.rollTarget.staticModifier = options.rollTarget.actorLevel
} else {
options.rollTarget.staticModifier = 0
}
} else if (options.rollType === "miracle" || options.rollType === "miracle-attack" || options.rollType === "miracle-power") { } else if (options.rollType === "miracle" || options.rollType === "miracle-attack" || options.rollType === "miracle-power") {
hasD30 = true hasD30 = true
@ -234,9 +238,13 @@ export default class LethalFantasyRoll extends Roll {
hasChangeDice = false hasChangeDice = false
options.rollTarget.value = options.rollTarget.actorModifiers.levelMiracleModifier + options.rollTarget.actorModifiers.chaMiracleModifier options.rollTarget.value = options.rollTarget.actorModifiers.levelMiracleModifier + options.rollTarget.actorModifiers.chaMiracleModifier
options.rollTarget.charModifier = options.rollTarget.actorModifiers.chaMiracleModifier options.rollTarget.charModifier = options.rollTarget.actorModifiers.chaMiracleModifier
hasStaticModifier = true // options.rollType === "spell-power" hasStaticModifier = options.rollType === "miracle-power"
hasModifier = true // options.rollType !== "miracle-attack" //hasModifier = options.rollType !== "miracle-attack"
options.rollTarget.staticModifier = options.rollTarget.actorLevel if (hasStaticModifier) {
options.rollTarget.staticModifier = options.rollTarget.actorLevel
} else {
options.rollTarget.staticModifier = 0
}
} else if (options.rollType === "shield-roll") { } else if (options.rollType === "shield-roll") {
hasD30 = false hasD30 = false
@ -302,6 +310,7 @@ export default class LethalFantasyRoll extends Roll {
rollType: options.rollType, rollType: options.rollType,
rollTarget: options.rollTarget, rollTarget: options.rollTarget,
rollName: options.rollName, rollName: options.rollName,
actorName: options.actorName,
rollModes, rollModes,
hasModifier, hasModifier,
hasFavor, hasFavor,
@ -322,16 +331,19 @@ export default class LethalFantasyRoll extends Roll {
} }
const content = await renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-dialog.hbs", dialogContext) const content = await renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-dialog.hbs", dialogContext)
const title = LethalFantasyRoll.createTitle(options.rollType, options.rollTarget) let position = game.user.getFlag(SYSTEM.id, "roll-dialog-pos") || { top: -1, left: -1 }
const label = game.i18n.localize("LETHALFANTASY.Roll.roll") const label = game.i18n.localize("LETHALFANTASY.Roll.roll")
const rollContext = await foundry.applications.api.DialogV2.wait({ const rollContext = await foundry.applications.api.DialogV2.wait({
window: { title: title }, window: { title: "Roll dialog" },
classes: ["lethalfantasy"], classes: ["lethalfantasy"],
content, content,
position,
buttons: [ buttons: [
{ {
label: label, label: label,
callback: (event, button, dialog) => { callback: (event, button, dialog) => {
let position = $(dialog).position()
game.user.setFlag(SYSTEM.id, "roll-dialog-pos", foundry.utils.duplicate(position))
const output = Array.from(button.form.elements).reduce((obj, input) => { const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value if (input.name) obj[input.name] = input.value
return obj return obj
@ -343,6 +355,15 @@ export default class LethalFantasyRoll extends Roll {
actions: { actions: {
"selectGranted": (event, button, dialog) => { "selectGranted": (event, button, dialog) => {
hasGrantedDice = true hasGrantedDice = true
},
"gotoToken" : (event, button, dialog) => {
let tokenId = $(button).data("tokenId")
let token = canvas.tokens?.get(tokenId)
if (token) {
canvas.animatePan({ x: token.x, y: token.y, duration: 200 })
canvas.tokens.releaseAll();
token.control({ releaseOthers: true });
}
} }
}, },
rejectClose: false // Click on Close button will not launch an error rejectClose: false // Click on Close button will not launch an error
@ -373,7 +394,7 @@ export default class LethalFantasyRoll extends Roll {
if (hasStaticModifier) { if (hasStaticModifier) {
modifierFormula += ` + ${options.rollTarget.staticModifier}` modifierFormula += ` + ${options.rollTarget.staticModifier}`
} }
modifierFormula += ` + ${options.rollTarget.charModifier}` // modifierFormula += ` + ${options.rollTarget.charModifier}`
let sign = fullModifier < 0 ? "-" : "+" let sign = fullModifier < 0 ? "-" : "+"
if (hasExplode) { if (hasExplode) {
titleFormula = `${dice}E ${sign} ${modifierFormula}` titleFormula = `${dice}E ${sign} ${modifierFormula}`
@ -543,6 +564,7 @@ export default class LethalFantasyRoll extends Roll {
return rollBase return rollBase
} }
/* ***********************************************************/
static async promptInitiative(options = {}) { static async promptInitiative(options = {}) {
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)])) const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const fieldRollMode = new foundry.data.fields.StringField({ const fieldRollMode = new foundry.data.fields.StringField({
@ -603,6 +625,7 @@ export default class LethalFantasyRoll extends Roll {
} }
/* ***********************************************************/
static async promptCombatAction(options = {}) { static async promptCombatAction(options = {}) {
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)])) const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
@ -619,6 +642,8 @@ export default class LethalFantasyRoll extends Roll {
} }
let currentAction = combatant.getFlag(SYSTEM.id, "currentAction") let currentAction = combatant.getFlag(SYSTEM.id, "currentAction")
let position = game.user.getFlag(SYSTEM.id, "combat-action-dialog-pos") || { top: -1, left: -1 }
let dialogContext = { let dialogContext = {
progressionDiceId: "", progressionDiceId: "",
fieldRollMode, fieldRollMode,
@ -636,33 +661,45 @@ export default class LethalFantasyRoll extends Roll {
action: "roll", action: "roll",
label: "Roll progression dice", label: "Roll progression dice",
callback: (event, button, dialog) => { callback: (event, button, dialog) => {
let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
return "rollProgressionDice" return "rollProgressionDice"
}, },
}) })
} else if (currentAction.type === "spell" || currentAction.type === "miracle") { } else if (currentAction.type === "spell" || currentAction.type === "miracle") {
let label = "" let label = ""
if ( currentAction.spellStatus === "castingTime") { if (currentAction.spellStatus === "castingTime") {
let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
label = "Wait casting time" label = "Wait casting time"
} }
if ( currentAction.spellStatus === "toBeCasted") { if (currentAction.spellStatus === "toBeCasted") {
let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
label = "Cast spell/miracle" label = "Cast spell/miracle"
} }
if ( currentAction.spellStatus === "lethargy") { if (currentAction.spellStatus === "lethargy") {
let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", pos)
label = "Roll lethargy dice" label = "Roll lethargy dice"
} }
buttons.push({ buttons.push({
action: "roll", action: "roll",
label: label, label: label,
callback: (event, button, dialog) => { callback: (event, button, dialog) => {
return "rollLethargyDice" let pos = $('#combat-action-dialog').position()
}, game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", foundry.utils.duplicate(pos))
}) return "rollLethargyDice"
},
})
} }
} else { } else {
buttons.push({ buttons.push({
action: "roll", action: "roll",
label: "Select action", label: "Select action",
callback: (event, button, dialog) => { callback: (event, button, dialog) => {
let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", foundry.utils.duplicate(pos))
const output = Array.from(button.form.elements).reduce((obj, input) => { const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value if (input.name) obj[input.name] = input.value
return obj return obj
@ -676,13 +713,17 @@ export default class LethalFantasyRoll extends Roll {
action: "cancel", action: "cancel",
label: "Other action, not listed here", label: "Other action, not listed here",
callback: (event, button, dialog) => { callback: (event, button, dialog) => {
let pos = $('#combat-action-dialog').position()
game.user.setFlag(SYSTEM.id, "combat-action-dialog-pos", foundry.utils.duplicate(pos))
return null; return null;
} }
}) })
let rollContext = await foundry.applications.api.DialogV2.wait({ let rollContext = await foundry.applications.api.DialogV2.wait({
window: { title: "Combat Action Dialog" }, window: { title: "Combat Action Dialog" },
id: "combat-action-dialog",
classes: ["lethalfantasy"], classes: ["lethalfantasy"],
position,
content, content,
buttons, buttons,
rejectClose: false // Click on Close button will not launch an error rejectClose: false // Click on Close button will not launch an error
@ -723,7 +764,7 @@ export default class LethalFantasyRoll extends Roll {
actionItem.spellStatus = "castingTime" actionItem.spellStatus = "castingTime"
// Set the flag on the combatant // Set the flag on the combatant
await combatant.setFlag(SYSTEM.id, "currentAction", actionItem) await combatant.setFlag(SYSTEM.id, "currentAction", actionItem)
let message = `${combatant.name} action : ${selectedItem.name}, start rolling progression dice` let message = `${combatant.name} action : ${selectedItem.name}, start rolling progression dice or casting time`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
rollContext = (actionItem.type == "weapon") ? "rollProgressionDice" : "rollLethargyDice" // Set the roll context to rollProgressionDice rollContext = (actionItem.type == "weapon") ? "rollProgressionDice" : "rollLethargyDice" // Set the roll context to rollProgressionDice
currentAction = actionItem currentAction = actionItem
@ -731,15 +772,16 @@ export default class LethalFantasyRoll extends Roll {
if (currentAction) { if (currentAction) {
if (rollContext === "rollLethargyDice") { if (rollContext === "rollLethargyDice") {
if ( currentAction.spellStatus === "castingTime") { if (currentAction.spellStatus === "castingTime") {
if ( currentAction.castingTime < currentAction.system.castingTime) { let time = currentAction.type === "spell" ? currentAction.system.castingTime : currentAction.system.prayerTime
let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${currentAction.system.castingTime}` if (currentAction.castingTime < time) {
let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${time}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime += 1 currentAction.castingTime += 1
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction)) await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
return return
} else { } else {
let message = `Spell ${currentAction.name} ready to be cast on next second !` let message = `Spell/Miracle ${currentAction.name} ready to be cast on next second !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime = 1 currentAction.castingTime = 1
currentAction.spellStatus = "toBeCasted" currentAction.spellStatus = "toBeCasted"
@ -747,8 +789,8 @@ export default class LethalFantasyRoll extends Roll {
return return
} }
} }
if ( currentAction.spellStatus === "toBeCasted") { if (currentAction.spellStatus === "toBeCasted") {
combatant.actor.prepareRoll( (currentAction.type === "spell") ? "spell-attack" : "miracle-attack" , currentAction._id) combatant.actor.prepareRoll((currentAction.type === "spell") ? "spell-attack" : "miracle-attack", currentAction._id)
if (currentAction.type === "spell") { if (currentAction.type === "spell") {
currentAction.spellStatus = "lethargy" currentAction.spellStatus = "lethargy"
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction)) await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
@ -758,26 +800,29 @@ export default class LethalFantasyRoll extends Roll {
} }
return return
} }
if ( currentAction.spellStatus === "lethargy") { if (currentAction.spellStatus === "lethargy") {
// Roll lethargy dice // Roll lethargy dice
let dice = LethalFantasyUtils.getLethargyDice(currentAction.system.level) let dice = LethalFantasyUtils.getLethargyDice(currentAction.system.level)
let roll = new Roll(dice) let roll = new Roll(dice)
await roll.evaluate() await roll.evaluate()
if (game?.dice3d) {
await game.dice3d.showForRoll(roll)
}
let max = roll.dice[0].faces - 1 let max = roll.dice[0].faces - 1
let toCompare = Math.min(options.rollProgressionCount, max) let toCompare = Math.min(currentAction.progressionCount, max)
if (roll.total <= toCompare) { if (roll.total <= toCompare) {
// Notify that the player can act now with a chat message // 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 }) let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyOK", { name: combatant.actor.name, spellName: currentAction.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
// Update the combatant progression count // Update the combatant progression count
await combatant.setFlag(SYSTEM.id, "currentAction", "") await combatant.setFlag(SYSTEM.id, "currentAction", "")
// Display the action selection window again // Display the action selection window again
combatant.actor.system.rollProgressionDice(options.combatId, options.combatantId ) combatant.actor.system.rollProgressionDice(options.combatId, options.combatantId)
} else { } else {
// Notify that the player cannot act now with a chat message // Notify that the player cannot act now with a chat message
currentAction.progressionCount += 1 currentAction.progressionCount += 1
combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction)) await 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 }) let message = game.i18n.format("LETHALFANTASY.Notifications.messageLethargyKO", { name: combatant.actor.name, spellName: currentAction.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
} }
} }
@ -839,178 +884,7 @@ export default class LethalFantasyRoll extends Roll {
} }
} }
/* ***********************************************************/
static async promptProgressionDice(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 dialogContext = {
progressionDiceId: "",
fieldRollMode,
rollModes,
...options
}
const content = await renderTemplate("systems/fvtt-lethal-fantasy/templates/roll-progression-dice-dialog.hbs", dialogContext)
const label = game.i18n.localize("LETHALFANTASY.Label.rollProgressionDice")
const rollContext = await foundry.applications.api.DialogV2.wait({
window: { title: "Progression Roll" },
classes: ["lethalfantasy"],
content,
buttons: [
{
action: "roll",
label: "Roll Progression Dice or Continue Loading",
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
},
},
{
action: "cast",
label: "Cast a spell",
callback: (event, button, dialog) => {
return "casting"
},
},
{
action: "cancel",
label: "Other action, no progression dice",
callback: (event, button, dialog) => {
return null;
}
}
],
rejectClose: false // Click on Close button will not launch an error
})
console.log("RollContext", dialogContext, rollContext)
let combat = game.combats.get(options.combatId)
let actor = game.actors.get(options.actorId)
if (rollContext === "casting") {
combat.setCasting(options.combatantId)
let message = `Starting casting a spell !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
return
}
if (rollContext === null || !rollContext?.progressionDiceId) {
c.resetCasting(options.combatantId)
combat.resetProgression(options.combatantId)
let message = `${actor.name} : Other action, progression reset`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
return
}
// Get the weapons from the actor items
let rangedMode
let searchId = rollContext.progressionDiceId
if (searchId.match("simpleAim")) {
searchId = searchId.replace("simpleAim", "")
rangedMode = "simpleAim"
}
if (searchId.match("carefulAim")) {
searchId = searchId.replace("carefulAim", "")
rangedMode = "carefulAim"
}
if (searchId.match("focusedAim")) {
searchId = searchId.replace("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)
if (combat.isCasting(options.combatantId)) {
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
}
if (options.rollProgressionCount > spell.system.castingTime) {
let message = `Spell ${spell.name} has been cast !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
combat.resetCasting(options.combatantId)
combat.resetProgression(options.combatantId)
return
}
} else {
let formula = dice
let roll = new Roll(formula)
await roll.evaluate()
let max = roll.dice[0].faces - 1
let toCompare = Math.min(options.rollProgressionCount, max)
let message
if (roll.total > toCompare) {
message = `Spell Lethargy ongoing ... (${roll.total}/${toCompare}, spell level ${spell.system.level})`
} else {
combat.resetProgression(options.combatantId)
message = `Spell Lethargy ended ! (${roll.total}/${toCompare}, spell level ${spell.system.level})<br>${actor.name} can return to normal actions.`
}
let msg = await roll.toMessage({ flavor: message }, { rollMode: rollContext.visibility })
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
}
return
}
let weapon = actor.items.find(i => i.type === "weapon" && i.id === searchId)
let formula = weapon.system.combatProgressionDice
let rangedLoad
if (rangedMode) {
let toSplit = weapon.system.speed[rangedMode]
let split = toSplit.split("+")
rangedLoad = Number(split[0]) || 0
formula = split[1]
console.log("Ranged Mode", rangedMode, rangedLoad, formula, options.rollProgressionCount)
}
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
}
let isMonster = 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(options.rollProgressionCount, max)
let msg = await roll.toMessage({ flavor: `Progression Roll for ${weapon.name}, progression count : ${options.rollProgressionCount}/${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: actor.name, weapon: weapon.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
// Update the combatant progression count
let combat = game.combats.get(options.combatId)
let combatant = combat.combatants.get(options.combatantId)
combatant.update({ 'system.progressionCount': 0 })
} else {
// Notify that the player cannot act now with a chat message
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKO", { isMonster, name: actor.name, weapon: weapon.name, roll: roll.total })
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: actor }) })
}
}
static async promptRangedDefense(rollTarget) { static async promptRangedDefense(rollTarget) {
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)])) const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))

View File

@ -298,8 +298,17 @@ export default class LethalFantasyCharacter extends foundry.abstract.TypeDataMod
if (this.biodata.magicUser || this.biodata.clericUser) { if (this.biodata.magicUser || this.biodata.clericUser) {
let spells = this.parent.items.filter(i => i.type === "spell" || i.type === "miracle") let spells = this.parent.items.filter(i => i.type === "spell" || i.type === "miracle")
for (let s of spells) { for (let s of spells) {
let dice = LethalFantasyUtils.getLethargyDice(s.system.level) let title = ""
weaponsChoices.push({ id: s.id, name: `${s.name} (Time: ${s.system.castingTime}, Lethargy: ${dice})`, combatProgressionDice: `${s.system.castingTime}+${dice}` }) let formula = ""
if (s.type === "spell") {
let dice = LethalFantasyUtils.getLethargyDice(s.system.level)
title = `${s.name} (Casting time: ${s.system.castingTime}, Lethargy: ${dice})`
formula = `${s.system.castingTime}+${dice}`
} else {
title = `${s.name} (Prayer time: ${s.system.prayerTime})`
formula = `${s.system.prayerTime}`
}
weaponsChoices.push({ id: s.id, name: title, combatProgressionDice: formula })
} }
} }

View File

@ -82,6 +82,10 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
attack2: attackField("2"), attack2: attackField("2"),
attack3: attackField("3"), attack3: attackField("3"),
attack4: attackField("4"), attack4: attackField("4"),
attack5: attackField("5"),
attack6: attackField("6"),
attack7: attackField("7"),
attack8: attackField("8")
}) })
schema.perception = new fields.SchemaField({ schema.perception = new fields.SchemaField({
@ -145,6 +149,74 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
await roll.toMessage({}, { rollMode: roll.options.rollMode }) await roll.toMessage({}, { rollMode: roll.options.rollMode })
} }
async prepareMonsterRoll(rollType, rollKey, rollDice = undefined, tokenId = undefined) {
let rollTarget
switch (rollType) {
case "monster-attack":
case "monster-defense":
case "monster-damage":
rollTarget = foundry.utils.duplicate(this.attacks[rollKey])
rollTarget.rollKey = rollKey
break
case "monster-skill":
rollTarget = foundry.utils.duplicate(this.resists[rollKey])
rollTarget.rollKey = rollKey
break
case "save":
rollTarget = foundry.utils.duplicate(this.saves[rollKey])
rollTarget.rollKey = rollKey
rollTarget.rollDice = rollDice
break
case "weapon-damage-small":
case "weapon-damage-medium":
case "weapon-attack":
case "weapon-defense":
let weapon = this.actor.items.find((i) => i.type === "weapon" && i.id === rollKey)
let skill
let skills = this.actor.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.actor.items.filter((i) => i.type === "skill" && i.name.toLowerCase().replace(" skill", "") === weapon.name.toLowerCase())
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 1.0)
} else {
skills = this.actor.items.filter((i) => i.type === "skill" && i.system.weaponClass === weapon.system.weaponClass)
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.5)
} else {
skills = this.actor.items.filter((i) => i.type === "skill" && i.system.weaponClass.includes(SYSTEM.WEAPON_CATEGORIES[weapon.system.weaponClass]))
if (skills.length > 0) {
skill = this.getBestWeaponClassSkill(skills, rollType, 0.25)
} else {
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
}
}
}
if (!weapon || !skill) {
console.error("Weapon or skill not found", weapon, skill)
ui.notifications.warn(game.i18n.localize("LETHALFANTASY.Notifications.skillNotFound"))
return
}
rollTarget = skill
rollTarget.weapon = weapon
rollTarget.weaponSkillModifier = skill.weaponSkillModifier
rollTarget.rollKey = rollKey
rollTarget.combat = foundry.utils.duplicate(this.combat)
break
default:
ui.notifications.error(game.i18n.localize("LETHALFANTASY.Notifications.rollTypeNotFound") + String(rollType))
break
}
// In all cases
rollTarget.tokenId = tokenId
console.log(rollTarget)
await this.roll(rollType, rollTarget)
}
async rollInitiative(combatId = undefined, combatantId = undefined) { async rollInitiative(combatId = undefined, combatantId = undefined) {
const hasTarget = false const hasTarget = false
@ -164,7 +236,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
await roll.toMessage({}, { rollMode: roll.options.rollMode }) await roll.toMessage({}, { rollMode: roll.options.rollMode })
} }
async rollProgressionDice(combatId, combatantId, rollProgressionCount) { async rollProgressionDice(combatId, combatantId) {
const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)])) const rollModes = Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)]))
const fieldRollMode = new foundry.data.fields.StringField({ const fieldRollMode = new foundry.data.fields.StringField({
@ -175,7 +247,8 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
let roll = new Roll("1D8") let roll = new Roll("1D8")
await roll.evaluate() await roll.evaluate()
let max = rollProgressionCount let combatant = game.combats.get(combatId)?.combatants?.get(combatantId)
let msg = await roll.toMessage({ flavor: `Progression Roll for ${this.parent.name}` } ) let msg = await roll.toMessage({ flavor: `Progression Roll for ${this.parent.name}` } )
if (game?.dice3d) { if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id) await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
@ -188,14 +261,19 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
hasAttack = true hasAttack = true
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionOKMonster", { isMonster: true, name: this.parent.name, weapon: attack.name, roll: roll.total }) 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 }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
let token = combatant?.token
this.prepareMonsterRoll("monster-attack", key, undefined, token?.id)
if ( token?.object ) {
token.object?.control({releaseOthers: true});
return canvas.animatePan(token.object.center);
}
} }
} }
if (!hasAttack) { if (!hasAttack) {
let message = game.i18n.format("LETHALFANTASY.Notifications.messageProgressionKOMonster", { isMonster: true, name: this.parent.name, roll: roll.total }) 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 }) }) ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
} }
} }
} }

View File

@ -1 +1 @@
MANIFEST-000219 MANIFEST-000261

View File

@ -1,8 +1,8 @@
2025/04/21-17:09:30.765172 7f5dceffd6c0 Recovering log #217 2025/04/25-21:22:26.619523 7fa7f51fa6c0 Recovering log #259
2025/04/21-17:09:30.774995 7f5dceffd6c0 Delete type=3 #215 2025/04/25-21:22:26.629405 7fa7f51fa6c0 Delete type=3 #257
2025/04/21-17:09:30.775106 7f5dceffd6c0 Delete type=0 #217 2025/04/25-21:22:26.629471 7fa7f51fa6c0 Delete type=0 #259
2025/04/21-17:40:19.481504 7f5dcd3ff6c0 Level-0 table #222: started 2025/04/25-21:28:11.794720 7fa7eebff6c0 Level-0 table #264: started
2025/04/21-17:40:19.481549 7f5dcd3ff6c0 Level-0 table #222: 0 bytes OK 2025/04/25-21:28:11.794743 7fa7eebff6c0 Level-0 table #264: 0 bytes OK
2025/04/21-17:40:19.518709 7f5dcd3ff6c0 Delete type=0 #220 2025/04/25-21:28:11.828838 7fa7eebff6c0 Delete type=0 #262
2025/04/21-17:40:19.572101 7f5dcd3ff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872804 7fa7eebff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end)
2025/04/21-17:40:19.634424 7f5dcd3ff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872877 7fa7eebff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end)

View File

@ -1,8 +1,8 @@
2025/04/17-17:21:12.564483 7f5c837fe6c0 Recovering log #213 2025/04/25-21:12:25.063567 7fa7f49f96c0 Recovering log #255
2025/04/17-17:21:12.575284 7f5c837fe6c0 Delete type=3 #211 2025/04/25-21:12:25.123483 7fa7f49f96c0 Delete type=3 #253
2025/04/17-17:21:12.575342 7f5c837fe6c0 Delete type=0 #213 2025/04/25-21:12:25.123549 7fa7f49f96c0 Delete type=0 #255
2025/04/17-17:34:42.493086 7f5c81bff6c0 Level-0 table #218: started 2025/04/25-21:21:41.663813 7fa7eebff6c0 Level-0 table #260: started
2025/04/17-17:34:42.493111 7f5c81bff6c0 Level-0 table #218: 0 bytes OK 2025/04/25-21:21:41.663837 7fa7eebff6c0 Level-0 table #260: 0 bytes OK
2025/04/17-17:34:42.499036 7f5c81bff6c0 Delete type=0 #216 2025/04/25-21:21:41.670063 7fa7eebff6c0 Delete type=0 #258
2025/04/17-17:34:42.518959 7f5c81bff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683068 7fa7eebff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end)
2025/04/17-17:34:42.519032 7f5c81bff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683094 7fa7eebff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!znm6T1ef4qQI8BX7' @ 0 : 0; will stop at (end)

View File

@ -1 +1 @@
MANIFEST-000218 MANIFEST-000260

View File

@ -1,8 +1,8 @@
2025/04/21-17:09:30.778463 7f5dcdffb6c0 Recovering log #216 2025/04/25-21:22:26.632055 7fa7ef7fe6c0 Recovering log #258
2025/04/21-17:09:30.789411 7f5dcdffb6c0 Delete type=3 #214 2025/04/25-21:22:26.642442 7fa7ef7fe6c0 Delete type=3 #256
2025/04/21-17:09:30.789468 7f5dcdffb6c0 Delete type=0 #216 2025/04/25-21:22:26.642504 7fa7ef7fe6c0 Delete type=0 #258
2025/04/21-17:40:19.406720 7f5dcd3ff6c0 Level-0 table #221: started 2025/04/25-21:28:11.762447 7fa7eebff6c0 Level-0 table #263: started
2025/04/21-17:40:19.406774 7f5dcd3ff6c0 Level-0 table #221: 0 bytes OK 2025/04/25-21:28:11.762488 7fa7eebff6c0 Level-0 table #263: 0 bytes OK
2025/04/21-17:40:19.446347 7f5dcd3ff6c0 Delete type=0 #219 2025/04/25-21:28:11.794608 7fa7eebff6c0 Delete type=0 #261
2025/04/21-17:40:19.572073 7f5dcd3ff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872774 7fa7eebff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end)
2025/04/21-17:40:19.572119 7f5dcd3ff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872860 7fa7eebff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end)

View File

@ -1,8 +1,8 @@
2025/04/17-17:21:12.578876 7f5c82ffd6c0 Recovering log #212 2025/04/25-21:12:25.126076 7fa7ef7fe6c0 Recovering log #254
2025/04/17-17:21:12.589060 7f5c82ffd6c0 Delete type=3 #210 2025/04/25-21:12:25.194012 7fa7ef7fe6c0 Delete type=3 #252
2025/04/17-17:21:12.589120 7f5c82ffd6c0 Delete type=0 #212 2025/04/25-21:12:25.194129 7fa7ef7fe6c0 Delete type=0 #254
2025/04/17-17:34:42.486913 7f5c81bff6c0 Level-0 table #217: started 2025/04/25-21:21:41.677033 7fa7eebff6c0 Level-0 table #259: started
2025/04/17-17:34:42.486964 7f5c81bff6c0 Level-0 table #217: 0 bytes OK 2025/04/25-21:21:41.677055 7fa7eebff6c0 Level-0 table #259: 0 bytes OK
2025/04/17-17:34:42.492952 7f5c81bff6c0 Delete type=0 #215 2025/04/25-21:21:41.682956 7fa7eebff6c0 Delete type=0 #257
2025/04/17-17:34:42.518936 7f5c81bff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683087 7fa7eebff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end)
2025/04/17-17:34:42.519016 7f5c81bff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683125 7fa7eebff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!zjvGljrLk5SshC9D' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@ -1 +1 @@
MANIFEST-000218 MANIFEST-000260

View File

@ -1,8 +1,8 @@
2025/04/21-17:09:30.749204 7f5dce7fc6c0 Recovering log #216 2025/04/25-21:22:26.603891 7fa7f49f96c0 Recovering log #258
2025/04/21-17:09:30.759893 7f5dce7fc6c0 Delete type=3 #214 2025/04/25-21:22:26.613982 7fa7f49f96c0 Delete type=3 #256
2025/04/21-17:09:30.760044 7f5dce7fc6c0 Delete type=0 #216 2025/04/25-21:22:26.614103 7fa7f49f96c0 Delete type=0 #258
2025/04/21-17:40:19.292988 7f5dcd3ff6c0 Level-0 table #221: started 2025/04/25-21:28:11.828962 7fa7eebff6c0 Level-0 table #263: started
2025/04/21-17:40:19.293026 7f5dcd3ff6c0 Level-0 table #221: 0 bytes OK 2025/04/25-21:28:11.828987 7fa7eebff6c0 Level-0 table #263: 0 bytes OK
2025/04/21-17:40:19.329102 7f5dcd3ff6c0 Delete type=0 #219 2025/04/25-21:28:11.872564 7fa7eebff6c0 Delete type=0 #261
2025/04/21-17:40:19.406586 7f5dcd3ff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872823 7fa7eebff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2025/04/21-17:40:19.406620 7f5dcd3ff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872896 7fa7eebff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)

View File

@ -1,8 +1,8 @@
2025/04/17-17:21:12.549824 7f5c827fc6c0 Recovering log #212 2025/04/25-21:12:25.004274 7fa7f51fa6c0 Recovering log #254
2025/04/17-17:21:12.560061 7f5c827fc6c0 Delete type=3 #210 2025/04/25-21:12:25.060536 7fa7f51fa6c0 Delete type=3 #252
2025/04/17-17:21:12.560203 7f5c827fc6c0 Delete type=0 #212 2025/04/25-21:12:25.060709 7fa7f51fa6c0 Delete type=0 #254
2025/04/17-17:34:42.505447 7f5c81bff6c0 Level-0 table #217: started 2025/04/25-21:21:41.670183 7fa7eebff6c0 Level-0 table #259: started
2025/04/17-17:34:42.505471 7f5c81bff6c0 Level-0 table #217: 0 bytes OK 2025/04/25-21:21:41.670208 7fa7eebff6c0 Level-0 table #259: 0 bytes OK
2025/04/17-17:34:42.518767 7f5c81bff6c0 Delete type=0 #215 2025/04/25-21:21:41.676916 7fa7eebff6c0 Delete type=0 #257
2025/04/17-17:34:42.518998 7f5c81bff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683078 7fa7eebff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2025/04/17-17:34:42.519066 7f5c81bff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683166 7fa7eebff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@ -1 +1 @@
MANIFEST-000218 MANIFEST-000260

View File

@ -1,8 +1,8 @@
2025/04/21-17:09:30.792733 7f5dcf7fe6c0 Recovering log #216 2025/04/25-21:22:26.644388 7fa7effff6c0 Recovering log #258
2025/04/21-17:09:30.802787 7f5dcf7fe6c0 Delete type=3 #214 2025/04/25-21:22:26.654206 7fa7effff6c0 Delete type=3 #256
2025/04/21-17:09:30.802873 7f5dcf7fe6c0 Delete type=0 #216 2025/04/25-21:22:26.654257 7fa7effff6c0 Delete type=0 #258
2025/04/21-17:40:19.329256 7f5dcd3ff6c0 Level-0 table #221: started 2025/04/25-21:28:11.724988 7fa7eebff6c0 Level-0 table #263: started
2025/04/21-17:40:19.329290 7f5dcd3ff6c0 Level-0 table #221: 0 bytes OK 2025/04/25-21:28:11.725025 7fa7eebff6c0 Level-0 table #263: 0 bytes OK
2025/04/21-17:40:19.360850 7f5dcd3ff6c0 Delete type=0 #219 2025/04/25-21:28:11.762266 7fa7eebff6c0 Delete type=0 #261
2025/04/21-17:40:19.406600 7f5dcd3ff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872740 7fa7eebff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
2025/04/21-17:40:19.406634 7f5dcd3ff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2025/04/25-21:28:11.872841 7fa7eebff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)

View File

@ -1,8 +1,8 @@
2025/04/17-17:21:12.592168 7f5c83fff6c0 Recovering log #212 2025/04/25-21:12:25.197251 7fa7effff6c0 Recovering log #254
2025/04/17-17:21:12.602692 7f5c83fff6c0 Delete type=3 #210 2025/04/25-21:12:25.252610 7fa7effff6c0 Delete type=3 #252
2025/04/17-17:21:12.602743 7f5c83fff6c0 Delete type=0 #212 2025/04/25-21:12:25.252672 7fa7effff6c0 Delete type=0 #254
2025/04/17-17:34:42.499156 7f5c81bff6c0 Level-0 table #217: started 2025/04/25-21:21:41.657695 7fa7eebff6c0 Level-0 table #259: started
2025/04/17-17:34:42.499188 7f5c81bff6c0 Level-0 table #217: 0 bytes OK 2025/04/25-21:21:41.657740 7fa7eebff6c0 Level-0 table #259: 0 bytes OK
2025/04/17-17:34:42.505329 7f5c81bff6c0 Delete type=0 #215 2025/04/25-21:21:41.663703 7fa7eebff6c0 Delete type=0 #257
2025/04/17-17:34:42.518977 7f5c81bff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683056 7fa7eebff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
2025/04/17-17:34:42.519047 7f5c81bff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2025/04/25-21:21:41.683109 7fa7eebff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)

View File

@ -13,6 +13,19 @@
fieldset { fieldset {
padding: 4px; padding: 4px;
} }
.goto-token-button {
justify-content: center;
align-items: center;
align-content: center;
margin-left: 0.5rem;
max-width: 8rem;
background-color: var(--color-dark-6);
color: var(--color-dark-2);
border: none;
border-radius: 4px;
padding: 0.5rem;
}
} }
.lethalfantasy-range-defense-dialog { .lethalfantasy-range-defense-dialog {

View File

@ -1,7 +1,7 @@
<div class="lethalfantasy-combat-action-dialog"> <div class="lethalfantasy-combat-action-dialog">
<fieldSet class=""> <fieldSet class="">
<legend>{{localize "LETHALFANTASY.Label.combatAction"}}</legend> <legend>{{localize "LETHALFANTASY.Label.combatAction"}} for {{actorName}}</legend>
{{#if currentAction}} {{#if currentAction}}
<label>{{localize "LETHALFANTASY.Label.currentAction"}} : {{currentAction.name}}</label> <label>{{localize "LETHALFANTASY.Label.currentAction"}} : {{currentAction.name}}</label>

View File

@ -1,13 +1,19 @@
<div class="lethalfantasy-roll-dialog"> <div class="lethalfantasy-roll-dialog">
<fieldSet class=""> <fieldSet class="">
<legend>{{localize (concat "LETHALFANTASY.Label." rollType)}}</legend> <legend>{{localize (concat "LETHALFANTASY.Label." rollType)}} - {{actorName}}</legend>
{{#if rollTarget.tokenId}}
<div class="dialog-save">
<a class="goto-token-button" data-action="gotoToken" data-token-id="{{rollTarget.tokenId}}">{{localize "LETHALFANTASY.Label.gotoToken"}} </a>
</div>
{{/if}}
{{#if (match rollType "attack")}} {{#if (match rollType "attack")}}
<div class="dialog-save">Attack roll !</div> <div class="dialog-save">Attack roll ! - {{rollTarget.name}}</div>
{{/if}} {{/if}}
{{#if (match rollType "defense")}} {{#if (match rollType "defense")}}
<div class="dialog-save">Defense roll !</div> <div class="dialog-save">Defense roll ! - {{rollTarget.name}}</div>
{{/if}} {{/if}}
{{#if hasModifier}} {{#if hasModifier}}
@ -87,4 +93,6 @@
{{selectOptions rollModes selected=visibility}} {{selectOptions rollModes selected=visibility}}
</select> </select>
</fieldSet> </fieldSet>
</div> </div>