Final fixes and code review checks
Release Creation / build (release) Successful in 39s

This commit is contained in:
2026-06-12 08:19:42 +02:00
parent 37badf2619
commit cbeaaeec99
13 changed files with 137 additions and 180 deletions
+25 -43
View File
@@ -4,7 +4,6 @@
*/
import { SYSTEM } from "./module/config/system.mjs"
globalThis.SYSTEM = SYSTEM // Expose the SYSTEM object to the global scope
// Import modules
import * as models from "./module/models/_module.mjs"
@@ -14,15 +13,24 @@ import * as applications from "./module/applications/_module.mjs"
import { LethalFantasyCombatTracker, LethalFantasyCombat } from "./module/applications/combat.mjs"
import { Macros } from "./module/macros.mjs"
import { setupTextEnrichers } from "./module/enrichers.mjs"
import { default as LethalFantasyUtils } from "./module/utils.mjs"
export class ClassCounter { static printHello() { console.log("Hello") } static sendJsonPostRequest(e, s) { const t = { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify(s) }; return fetch(e, t).then((e => { if (!e.ok) throw new Error("La requête a échoué avec le statut " + e.status); return e.json() })).catch((e => { throw console.error("Erreur envoi de la requête:", e), e })) } static registerUsageCount(e = game.system.id, s = {}) { if (game.user.isGM) { game.settings.register(e, "world-key", { name: "Unique world key", scope: "world", config: !1, default: "", type: String }); let t = game.settings.get(e, "world-key"); null != t && "" != t && "NONE" != t && "none" != t.toLowerCase() || (t = foundry.utils.randomID(32), game.settings.set(e, "world-key", t)); let a = { name: e, system: game.system.id, worldKey: t, version: game.system.version, language: game.settings.get("core", "language"), remoteAddr: game.data.addresses.remote, nbInstalledModules: game.modules.size, nbActiveModules: game.modules.filter((e => e.active)).length, nbPacks: game.world.packs.size, nbUsers: game.users.size, nbScenes: game.scenes.size, nbActors: game.actors.size, nbPlaylist: game.playlists.size, nbTables: game.tables.size, nbCards: game.cards.size, optionsData: s, foundryVersion: `${game.release.generation}.${game.release.build}` }; this.sendJsonPostRequest("https://www.uberwald.me/fvtt_appcount/count_post.php", a) } } }
import LethalFantasyUtils, { log } from "./module/utils.mjs"
Hooks.once("init", function () {
globalThis.SYSTEM = SYSTEM
globalThis.pendingDefenses = new Map()
console.info("Lethal Fantasy RPG | Initializing System")
console.info(SYSTEM.ASCII)
game.settings.register(game.system.id, "debug", {
name: "Debug logging",
scope: "client",
config: true,
default: false,
type: Boolean,
})
globalThis.lethalFantasy = game.system
globalThis.log = log
game.system.CONST = SYSTEM
// Expose the system API
@@ -54,12 +62,10 @@ Hooks.once("init", function () {
miracle: models.LethalFantasyMiracle
}
// Register sheet application classes
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
// Register sheet application classes (V2)
foundry.documents.collections.Actors.registerSheet("lethalFantasy", applications.LethalFantasyCharacterSheet, { types: ["character"], makeDefault: true })
foundry.documents.collections.Actors.registerSheet("lethalFantasy", applications.LethalFantasyMonsterSheet, { types: ["monster"], makeDefault: true })
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
foundry.documents.collections.Items.registerSheet("lethalFantasy", applications.LethalFantasySkillSheet, { types: ["skill"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("lethalFantasy", applications.LethalFantasyGiftSheet, { types: ["gift"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("lethalFantasy", applications.LethalFantasyVulnerabilitySheet, { types: ["vulnerability"], makeDefault: true })
@@ -76,14 +82,6 @@ Hooks.once("init", function () {
// Dice system configuration
CONFIG.Dice.rolls.push(documents.LethalFantasyRoll)
game.settings.register("lethalFantasy", "worldKey", {
name: "Unique world key",
scope: "world",
config: false,
type: String,
default: "",
})
// Activate socket handler
game.socket.on(`system.${SYSTEM.id}`, LethalFantasyUtils.handleSocketEvent)
@@ -113,10 +111,6 @@ Hooks.once("ready", function () {
// Initialiser la table des résultats D30
documents.D30Roll.initialize()
if (!SYSTEM.DEV_MODE) {
registerWorldCount("lethalFantasy")
}
// Saignement piloté par le combat tracker
_registerBleedingHooks()
@@ -333,7 +327,7 @@ Hooks.on("renderChatMessageHTML", (message, html, data) => {
? (rollTargetData?._id || rollTargetData?.id)
: (rollTargetData?.weapon?.id || rollTargetData?.weapon?._id)
const attackRollKey = rollTargetData?.rollKey
console.log(`[LF] request-defense-btn | attackRollType=${attackRollType} defender=${defenderName} defenderType=${combatant.actor?.type}`)
log(`[LF] request-defense-btn | attackRollType=${attackRollType} defender=${defenderName} defenderType=${combatant.actor?.type}`)
const attackD30result = message.rolls[0]?.options?.D30result || null
const attackD30message = message.rolls[0]?.options?.D30message || null
const attackDiceResults = message.rolls[0]?.options?.diceResults || null
@@ -552,7 +546,7 @@ Hooks.on("preCreateChatMessage", (message) => {
[`flags.${SYSTEM.id}.attackData`]: game.lethalFantasy.nextDefenseData
})
console.log("Added attack data to defense message:", game.lethalFantasy.nextDefenseData)
log("Added attack data to defense message:", game.lethalFantasy.nextDefenseData)
// Nettoyer
delete game.lethalFantasy.nextDefenseData
@@ -563,7 +557,7 @@ Hooks.on("preCreateChatMessage", (message) => {
Hooks.on("createChatMessage", async (message) => {
const rollType = message.rolls[0]?.options?.rollType
console.log("Defense hook checking message, rollType:", rollType)
log("Defense hook checking message, rollType:", rollType)
// Vérifier si c'est un message de défense
if (rollType !== "weapon-defense" && rollType !== "monster-defense" && rollType !== "save") return
@@ -571,10 +565,10 @@ Hooks.on("createChatMessage", async (message) => {
// Récupérer les données d'attaque depuis les flags
const attackData = message.flags?.[SYSTEM.id]?.attackData
console.log("Defense message confirmed, attackData:", attackData)
log("Defense message confirmed, attackData:", attackData)
if (!attackData) {
console.log("No attack data found in message flags")
log("No attack data found in message flags")
return
}
@@ -596,7 +590,7 @@ Hooks.on("createChatMessage", async (message) => {
let defenseRoll = message.rolls[0]?.options?.rollTotal || message.rolls[0]?.total || 0
const defenseD30message = message.rolls[0]?.options?.D30message || null
console.log("Processing defense:", { attackRoll, defenseRoll, attackerId, defenderId })
log("Processing defense:", { attackRoll, defenseRoll, attackerId, defenderId })
// Attendre l'animation 3D
if (game?.dice3d) {
@@ -1058,7 +1052,7 @@ Hooks.on("createChatMessage", async (message) => {
))
if (shouldCreateMessage) {
console.log("Creating comparison message", { attackerHandledBonus, defenderHandledBonus, isDefenderOwner: defender.isOwner })
log("Creating comparison message", { attackerHandledBonus, defenderHandledBonus, isDefenderOwner: defender.isOwner })
await LethalFantasyUtils.compareAttackDefense({
attackerName,
@@ -1080,7 +1074,7 @@ Hooks.on("createChatMessage", async (message) => {
attackD30message
})
} else {
console.log("Skipping message creation", { attackerHandledBonus, defenderHandledBonus })
log("Skipping message creation", { attackerHandledBonus, defenderHandledBonus })
}
})
@@ -1161,7 +1155,7 @@ Hooks.on("createChatMessage", async (message) => {
const defenderId = message.rolls[0]?.options?.defenderId
const isDamage = message.rolls[0]?.options?.rollData?.isDamage
console.log("Auto-damage hook:", { defenderId, isDamage, rollType: message.rolls[0]?.options?.rollType })
log("Auto-damage hook:", { defenderId, isDamage, rollType: message.rolls[0]?.options?.rollType })
if (!defenderId || !isDamage) return
@@ -1186,11 +1180,11 @@ Hooks.on("createChatMessage", async (message) => {
}
if (!shouldApplyDamage) {
console.log("Auto-damage hook: Not responsible for applying damage, skipping")
log("Auto-damage hook: Not responsible for applying damage, skipping")
return
}
console.log("Auto-damage hook: Applying damage as responsible user")
log("Auto-damage hook: Applying damage as responsible user")
// Attendre l'animation 3D avant d'appliquer les dégâts
if (game?.dice3d) {
@@ -1401,16 +1395,4 @@ Hooks.on("hotbarDrop", (bar, data, slot) => {
*/
Hooks.on("renderChatLog", (_chatLog, html) => applications.injectDiceTray(_chatLog, html))
/**
* Register world usage statistics
* @param {string} registerKey
*/
async function registerWorldCount(registerKey) {
if (game.user.isGM) {
try {
ClassCounter.registerUsageCount(game.system.id, {})
} catch {
console.log("No usage log ")
}
}
}
+13 -41
View File
@@ -24,30 +24,29 @@ export class LethalFantasyCombatTracker extends foundry.applications.sidebar.tab
async _prepareContext(options) {
let data = await super._prepareContext(options);
console?.log("Combat Tracker Data", data);
log("Combat Tracker Data", data);
/*for (let u of data.turns) {
let c = game.combat.combatants.get(u.id);
u.progressionCount = c.system.progressionCount
u.isMonster = c.actor.type === "monster"
}
console.log("Combat Data", data);*/
log("Combat Data", data);*/
return data;
}
static #initiativePlus(ev) {
static async #initiativePlus(ev) {
ev.preventDefault();
let cId = ev.target.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
c.update({ 'initiative': c.initiative + 1 });
console.log("Initiative Plus");
await c.update({ 'initiative': c.initiative + 1 });
}
static #initiativeMinus(ev) {
static async #initiativeMinus(ev) {
ev.preventDefault();
let cId = ev.target.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
let newInit = Math.max(c.initiative - 1, 0);
c.update({ 'initiative': newInit });
await c.update({ 'initiative': newInit });
}
/**
@@ -58,33 +57,6 @@ export class LethalFantasyCombatTracker extends foundry.applications.sidebar.tab
ev.preventDefault();
await game.combat.rollMonsterProgression();
}
activateListeners(html) {
super.activateListeners(html);
// Display Combat settings
html.find(".initiative-plus").click(ev => {
ev.preventDefault();
let cId = ev.currentTarget.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
c.update({ 'initiative': c.initiative + 1 });
});
html.find(".initiative-minus").click(ev => {
ev.preventDefault();
let cId = ev.currentTarget.closest(".combatant").dataset.combatantId;
let c = game.combat.combatants.get(cId);
c.update({ 'initiative': c.initiative - 1 });
console.log("Initiative Minus");
});
}
/* -------------------------------------------- */
static get defaultOptions() {
let path = "systems/fvtt-lethal-fantasy/templates/combat-tracker.hbs";
return foundry.utils.mergeObject(super.defaultOptions, {
template: path,
});
}
}
export class LethalFantasyCombat extends Combat {
@@ -94,7 +66,7 @@ export class LethalFantasyCombat extends Combat {
* @returns {Combatant[]}
*/
setupTurns() {
console?.log("Setup Turns....");
log("Setup Turns....");
this.turns ||= [];
// Determine the turn order and the current turn
@@ -164,19 +136,19 @@ export class LethalFantasyCombat extends Combat {
}
}
resetProgression(cId) {
async resetProgression(cId) {
let c = this.combatants.get(cId);
c.update({ 'system.progressionCount': 0 });
await c.update({ 'system.progressionCount': 0 });
}
setCasting(cId) {
async setCasting(cId) {
let c = this.combatants.get(cId);
c.setFlag(SYSTEM.id, "casting", true);
await c.setFlag(SYSTEM.id, "casting", true);
}
resetCasting(cId) {
async resetCasting(cId) {
let c = this.combatants.get(cId);
c.setFlag(SYSTEM.id, "casting", false);
await c.setFlag(SYSTEM.id, "casting", false);
}
isCasting(cId) {
@@ -78,7 +78,6 @@ export default class LethalFantasyActorSheet extends HandlebarsApplicationMixin(
/** @override */
_onRender(context, options) {
this.#dragDrop.forEach((d) => d.bind(this.element))
// Add listeners to rollable elements
const rollables = this.element.querySelectorAll(".rollable")
rollables.forEach((d) => d.addEventListener("click", this._onRoll.bind(this)))
}
@@ -234,12 +233,12 @@ export default class LethalFantasyActorSheet extends HandlebarsApplicationMixin(
const attr = target.dataset.edit
const current = foundry.utils.getProperty(this.document, attr)
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
const fp = new FilePicker({
const fp = new foundry.applications.ux.FilePicker.implementation({
current,
type: "image",
redirectToRoot: img ? [img] : [],
callback: (path) => {
this.document.update({ [attr]: path })
callback: async (path) => {
await this.document.update({ [attr]: path })
},
top: this.position.top + 40,
left: this.position.left + 10,
@@ -261,7 +260,7 @@ export default class LethalFantasyActorSheet extends HandlebarsApplicationMixin(
item = await fromUuid(uuid)
if (!item) item = this.document.items.get(id)
if (!item) return
item.sheet.render(true)
item.sheet.render({ force: true })
}
/**
@@ -284,8 +283,8 @@ export default class LethalFantasyActorSheet extends HandlebarsApplicationMixin(
* @private
* @static
*/
static #onCreateSpell(event, target) {
const item = this.document.createEmbeddedDocuments("Item", [{ name: "Nouveau sortilège", type: "spell" }])
static async #onCreateSpell(event, target) {
await this.document.createEmbeddedDocuments("Item", [{ name: "Nouveau sortilège", type: "spell" }])
}
// #endregion
@@ -177,12 +177,12 @@ export default class LethalFantasyItemSheet extends HandlebarsApplicationMixin(f
const attr = target.dataset.edit
const current = foundry.utils.getProperty(this.document, attr)
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
const fp = new FilePicker({
const fp = new foundry.applications.ux.FilePicker.implementation({
current,
type: "image",
redirectToRoot: img ? [img] : [],
callback: (path) => {
this.document.update({ [attr]: path })
callback: async (path) => {
await this.document.update({ [attr]: path })
},
top: this.position.top + 40,
left: this.position.left + 10,
+16 -17
View File
@@ -180,44 +180,44 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
await this.document.system.rollInitiative()
}
static #onArmorHitPointsPlus(event, target) {
static async #onArmorHitPointsPlus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints
armorHP += 1
this.actor.update({ "system.combat.armorHitPoints": armorHP })
await this.actor.update({ "system.combat.armorHitPoints": armorHP })
}
static #onArmorHitPointsMinus(event, target) {
static async #onArmorHitPointsMinus(event, target) {
let armorHP = this.actor.system.combat.armorHitPoints
armorHP -= 1
this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) })
await this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) })
}
static #onDivinityPointsPlus(event, target) {
static async #onDivinityPointsPlus(event, target) {
let points = this.actor.system.divinityPoints.value
points += 1
points = Math.min(points, this.actor.system.divinityPoints.max)
this.actor.update({ "system.divinityPoints.value": points })
await this.actor.update({ "system.divinityPoints.value": points })
}
static #onDivinityPointsMinus(event, target) {
static async #onDivinityPointsMinus(event, target) {
let points = this.actor.system.divinityPoints.value
points -= 1
points = Math.max(points, 0)
this.actor.update({ "system.divinityPoints.value": points })
await this.actor.update({ "system.divinityPoints.value": points })
}
static #onAetherPointsPlus(event, target) {
static async #onAetherPointsPlus(event, target) {
let points = this.actor.system.aetherPoints.value
points += 1
points = Math.min(points, this.actor.system.aetherPoints.max)
this.actor.update({ "system.aetherPoints.value": points })
await this.actor.update({ "system.aetherPoints.value": points })
}
static #onAetherPointsMinus(event, target) {
static async #onAetherPointsMinus(event, target) {
let points = this.actor.system.aetherPoints.value
points -= 1
points = Math.max(points, 0)
this.actor.update({ "system.aetherPoints.value": points })
await this.actor.update({ "system.aetherPoints.value": points })
}
/**
@@ -293,10 +293,9 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
}
_onRender(context, options) {
// Inputs with class `item-quantity`
const woundDescription = this.element.querySelectorAll('.wound-data')
for (const input of woundDescription) {
input.addEventListener("change", (e) => {
input.addEventListener("change", async (e) => {
e.preventDefault();
e.stopImmediatePropagation();
const newValue = e.currentTarget.value
@@ -304,11 +303,11 @@ export default class LethalFantasyCharacterSheet extends LethalFantasyActorSheet
const fieldName = e.currentTarget.dataset.name
let tab = foundry.utils.duplicate(this.actor.system.hp.wounds)
tab[index][fieldName] = newValue
console.log(tab, index, fieldName, newValue)
this.actor.update({ "system.hp.wounds": tab });
log(tab, index, fieldName, newValue)
await this.actor.update({ "system.hp.wounds": tab });
})
}
super._onRender();
super._onRender(context, options);
}
+1 -1
View File
@@ -91,7 +91,7 @@ export default class LethalFantasyMonsterSheet extends LethalFantasyActorSheet {
*/
async _onDrop(event) {
if (!this.isEditable || !this.isEditMode) return
const data = TextEditor.getDragEventData(event)
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
// Handle different data types
switch (data.type) {
+2 -2
View File
@@ -72,7 +72,7 @@ export default class LethalFantasyActor extends Actor {
/* *************************************************/
async applyDamage(hpLoss) {
let hp = this.system.hp.value + hpLoss
this.update({ "system.hp.value": hp })
await this.update({ "system.hp.value": hp })
}
/* *************************************************/
@@ -163,7 +163,7 @@ export default class LethalFantasyActor extends Actor {
/* *************************************************/
async prepareRoll(rollType, rollKey, rollDice, defenderId, defenderTokenId, extraShieldDr = 0, d30Effects = {}) {
console.log("Preparing roll", rollType, rollKey, rollDice, defenderId)
log("Preparing roll", rollType, rollKey, rollDice, defenderId)
let rollTarget
switch (rollType) {
case "granted":
+3 -1
View File
@@ -1,3 +1,5 @@
import { log } from "../utils.mjs"
/**
* Classe pour gérer les résultats du D30 dans Lethal Fantasy
*/
@@ -41,7 +43,7 @@ export default class D30Roll {
this.resultsTable = data.d30_dice_results
this.definitions = data.definitions
console.log("D30Roll | D30 results table loaded successfully")
log("D30Roll | D30 results table loaded successfully")
} catch (error) {
console.error("D30Roll | Error loading D30 table:", error)
ui.notifications.error("Unable to load D30 results table")
+22 -22
View File
@@ -392,7 +392,7 @@ export default class LethalFantasyRoll extends Roll {
{
label: label,
callback: (event, button, dialog) => {
console.log("Roll context", event, button, dialog)
log("Roll context", 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) => {
@@ -435,7 +435,7 @@ export default class LethalFantasyRoll extends Roll {
// If the user cancels the dialog, exit
if (rollContext === null) return
console.log("rollContext", rollContext, hasGrantedDice)
log("rollContext", rollContext, hasGrantedDice)
rollContext.saveSpell = saveSpell // Update fucking flag
let fullModifier = 0
@@ -552,7 +552,7 @@ export default class LethalFantasyRoll extends Roll {
if (rollContext.favor === "favor") {
rollFavor = new this(baseFormula, options.data, rollData)
await rollFavor.evaluate()
console.log("Rolling with favor", rollFavor)
log("Rolling with favor", rollFavor)
if (game?.dice3d) {
game.dice3d.showForRoll(rollFavor, game.user, true)
}
@@ -861,12 +861,12 @@ export default class LethalFantasyRoll extends Roll {
rejectClose: false // Click on Close button will not launch an error
})
console.log("RollContext", dialogContext, rollContext)
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 }) })
await ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
return
}
@@ -924,7 +924,7 @@ export default class LethalFantasyRoll extends Roll {
// Set the flag on the combatant
await combatant.setFlag(SYSTEM.id, "currentAction", actionItem)
let message = `${combatant.name} action : ${selectedItem.name}, start rolling progression dice or casting time`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
rollContext = (actionItem.type === "weapon") ? "rollProgressionDice" : "rollLethargyDice" // Set the roll context to rollProgressionDice
currentAction = actionItem
}
@@ -935,14 +935,14 @@ export default class LethalFantasyRoll extends Roll {
let time = currentAction.type === "spell" ? currentAction.system.castingTime : currentAction.system.prayerTime
if (currentAction.castingTime < time) {
let message = `Casting time : ${currentAction.name}, count : ${currentAction.castingTime}/${time}`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await 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 {
// Last counting second — announce ready and transition immediately (no extra second consumed)
let message = `Casting time : ${currentAction.name}, count : ${time}/${time} — ready to cast next second !`
ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
currentAction.castingTime = 1
currentAction.spellStatus = "toBeCasted"
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
@@ -982,7 +982,7 @@ export default class LethalFantasyRoll extends Roll {
isLethargy: true
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
// Update the combatant progression count
await combatant.setFlag(SYSTEM.id, "currentAction", "")
// Display the action selection window again
@@ -1002,7 +1002,7 @@ export default class LethalFantasyRoll extends Roll {
isLethargy: true
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
}
}
}
@@ -1014,18 +1014,18 @@ export default class LethalFantasyRoll extends Roll {
let split = toSplit.split("+")
currentAction.rangedLoad = Number(split[0]) || 0
formula = split[1]
console.log("Ranged Mode", currentAction.rangedMode, currentAction.rangedLoad, formula)
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 }) })
await 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 }) })
await 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))
@@ -1057,13 +1057,13 @@ export default class LethalFantasyRoll extends Roll {
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await combatant.setFlag(SYSTEM.id, "currentAction", "")
combatant.actor.prepareRoll(currentAction.type === "weapon" ? "weapon-attack" : "spell-attack", currentAction._id)
} else {
// Notify that the player cannot act now with a chat message
currentAction.progressionCount += 1
combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
await combatant.setFlag(SYSTEM.id, "currentAction", foundry.utils.duplicate(currentAction))
const messageContent = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-lethal-fantasy/templates/progression-message.hbs",
{
@@ -1074,7 +1074,7 @@ export default class LethalFantasyRoll extends Roll {
progressionCount: currentAction.progressionCount
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
await ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: combatant.actor }) })
}
}
}
@@ -1130,7 +1130,7 @@ export default class LethalFantasyRoll extends Roll {
// If the user cancels the dialog, exit
if (rollContext === null) return
console.log("RollContext", rollContext)
log("RollContext", rollContext)
// Add disfavor/favor option if point blank range
if (rollContext.range === "pointblank") {
rollContext.movement = rollContext.movement.replace("kh", "")
@@ -1426,7 +1426,7 @@ export default class LethalFantasyRoll extends Roll {
/** @override */
async render(chatOptions = {}) {
let chatData = await this._getChatCardData(chatOptions.isPrivate)
console.log("ChatData", chatData)
log("ChatData", chatData)
return await foundry.applications.handlebars.renderTemplate(this.constructor.CHAT_TEMPLATE, chatData)
}
@@ -1464,15 +1464,15 @@ export default class LethalFantasyRoll extends Roll {
// Récupérer les informations de l'arme pour les attaques réussies
let weaponDamageOptions = null
console.log("Roll type:", this.type, "rollTarget:", this.rollTarget, "Has weapon:", !!this.rollTarget?.weapon)
log("Roll type:", this.type, "rollTarget:", this.rollTarget, "Has weapon:", !!this.rollTarget?.weapon)
if (this.type === "weapon-attack" && this.rollTarget?.weapon) {
const weapon = this.rollTarget.weapon
weaponDamageOptions = {
weaponId: weapon._id || weapon.id,
weaponId: weapon.id,
weaponName: weapon.name,
damageM: weapon.system?.damage?.damageM
}
console.log("Weapon damage options:", weaponDamageOptions)
log("Weapon damage options:", weaponDamageOptions)
} else if (this.type === "monster-attack" && this.rollTarget) {
weaponDamageOptions = {
weaponId: this.rollTarget.rollKey,
@@ -1481,7 +1481,7 @@ export default class LethalFantasyRoll extends Roll {
damageModifier: this.rollTarget.damageModifier,
isMonster: true
}
console.log("Monster damage options:", weaponDamageOptions)
log("Monster damage options:", weaponDamageOptions)
}
const cardData = {
+5 -5
View File
@@ -32,20 +32,20 @@ export class Macros {
dropData.rollType === "save"
? `game.actors.get('${dropData.actorId}').system.roll('${dropData.rollType}', '${dropData.rollTarget}', '=');`
: `game.actors.get('${dropData.actorId}').system.roll('${dropData.rollType}', '${dropData.rollTarget}');`
const rollName = `${game.i18n.localize("TENEBRIS.Label.jet")} ${game.i18n.localize(`TENEBRIS.Manager.${dropData.rollTarget}`)}`
const rollName = `${game.i18n.localize("LETHALFANTASY.Label.jet")} ${game.i18n.localize(`LETHALFANTASY.Label.${dropData.rollTarget}`)}`
this.createMacro(slot, rollName, rollCommand, "icons/svg/d20-grey.svg")
break
case "rollDamage":
const weapon = game.actors.get(dropData.actorId).items.get(dropData.rollTarget)
const rollDamageCommand = `game.actors.get('${dropData.actorId}').system.roll('${dropData.rollType}', '${dropData.rollTarget}');`
const rollDamageName = `${game.i18n.localize("TENEBRIS.Label.jet")} ${weapon.name}`
const rollDamageName = `${game.i18n.localize("LETHALFANTASY.Label.jet")} ${weapon.name}`
this.createMacro(slot, rollDamageName, rollDamageCommand, weapon.img)
break
case "rollAttack":
const rollAttackCommand = `game.actors.get('${dropData.actorId}').system.roll('${dropData.rollValue}', '${dropData.rollTarget}');`
const rollAttackName = `${game.i18n.localize("TENEBRIS.Label.jet")} ${dropData.rollTarget}`
const rollAttackName = `${game.i18n.localize("LETHALFANTASY.Label.jet")} ${dropData.rollTarget}`
this.createMacro(slot, rollAttackName, rollAttackCommand, "icons/svg/d20-grey.svg")
break
@@ -57,7 +57,7 @@ export class Macros {
/**
* Create a macro
* All macros are flaged with a tenebris.macro flag at true
* All macros are flaged with a lethalFantasy.macro flag at true
* @param {*} slot
* @param {*} name
* @param {*} command
@@ -72,7 +72,7 @@ export class Macros {
type: "script",
img: img,
command: command,
flags: { "tenebris.macro": true },
flags: { "lethalFantasy.macro": true },
},
{ displaySheet: false },
)
+3 -3
View File
@@ -383,7 +383,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
await 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) {
@@ -407,7 +407,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
await 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) {
@@ -426,7 +426,7 @@ export default class LethalFantasyMonster extends foundry.abstract.TypeDataModel
rollResult: roll.total
}
)
ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
await ChatMessage.create({ content: messageContent, speaker: ChatMessage.getSpeaker({ actor: this.parent }) })
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
import { SYSTEM } from "../config/system.mjs"
import { WEAPON_TYPE } from "../config/weapon.mjs"
export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
export default class LethalFantasyWeapon extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
@@ -62,7 +63,7 @@ export default class LethalFantasySkill extends foundry.abstract.TypeDataModel {
static LOCALIZATION_PREFIXES = ["LETHALFANTASY.Weapon"]
get weaponCategory() {
return game.i18n.localize(CATEGORY[this.weaponType].label)
return game.i18n.localize(WEAPON_TYPE[this.weaponType] || this.weaponType)
}
}
+35 -33
View File
@@ -1,8 +1,9 @@
import { SYSTEM } from "./config/system.mjs"
// Map temporaire pour stocker les données d'attaque en attente de défense
if (!globalThis.pendingDefenses) {
globalThis.pendingDefenses = new Map()
export function log(...args) {
if (game?.settings?.get(game.system.id, "debug")) {
console.log(...args)
}
}
export default class LethalFantasyUtils {
@@ -28,6 +29,7 @@ export default class LethalFantasyUtils {
static setHookListeners() {
Hooks.on('renderTokenHUD', async (hud, html, token) => {
if (html.querySelector(".lethal-hp-loss-hud")) return
// HP Loss Button (existing)
const lossHPButton = await foundry.applications.handlebars.renderTemplate('systems/fvtt-lethal-fantasy/templates/loss-hp-hud.hbs', {})
$(html).find('div.left').append(lossHPButton);
@@ -50,14 +52,14 @@ export default class LethalFantasyUtils {
$(html).find('.hp-loss-wrap')[2].classList.add('hp-loss-hud-disabled');
}
})
$(html).find('.loss-hp-hud-click').click((event) => {
$(html).find('.loss-hp-hud-click').click(async (event) => {
event.preventDefault();
let hpLoss = event.currentTarget.dataset.hpValue;
if (token) {
let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
console.log(tokenFull, token)
let tokenFull = canvas.tokens.placeables.find(t => t.id === token.id);
log(tokenFull, token)
let actor = tokenFull.actor;
actor.applyDamage(Number(hpLoss));
await actor.applyDamage(Number(hpLoss));
$(html).find('.hp-loss-wrap')[0].classList.remove('hp-loss-hud-active');
$(html).find('.hp-loss-wrap')[0].classList.add('hp-loss-hud-disabled');
$(html).find('.hp-loss-wrap')[1].classList.remove('hp-loss-hud-active');
@@ -89,14 +91,14 @@ export default class LethalFantasyUtils {
$(html).find('.hp-gain-wrap')[2].classList.add('hp-gain-hud-disabled');
}
})
$(html).find('.gain-hp-hud-click').click((event) => {
$(html).find('.gain-hp-hud-click').click(async (event) => {
event.preventDefault();
let hpGain = event.currentTarget.dataset.hpValue;
if (token) {
let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
console.log(tokenFull, token)
let tokenFull = canvas.tokens.placeables.find(t => t.id === token.id);
log(tokenFull, token)
let actor = tokenFull.actor;
actor.applyDamage(Number(hpGain)); // Positive value to add HP
await actor.applyDamage(Number(hpGain)); // Positive value to add HP
$(html).find('.hp-gain-wrap')[0].classList.remove('hp-gain-hud-active');
$(html).find('.hp-gain-wrap')[0].classList.add('hp-gain-hud-disabled');
$(html).find('.hp-gain-wrap')[1].classList.remove('hp-gain-hud-active');
@@ -120,16 +122,16 @@ export default class LethalFantasyUtils {
wrap.classList.add('luck-grit-hud-disabled');
}
})
$(html).find('.luck-grit-btn').click((event) => {
$(html).find('.luck-grit-btn').click(async (event) => {
event.preventDefault();
if (token) {
let tokenFull = canvas.tokens.placeables.find(t => t.id === token._id);
let tokenFull = canvas.tokens.placeables.find(t => t.id === token.id);
let actor = tokenFull.actor;
const resource = event.currentTarget.dataset.resource;
const amount = Number(event.currentTarget.dataset.amount);
const current = Number(foundry.utils.getProperty(actor.system, `${resource}.current`)) || 0;
const newValue = Math.max(0, current + amount);
actor.update({ [`system.${resource}.current`]: newValue });
await actor.update({ [`system.${resource}.current`]: newValue });
$(html).find('.luck-grit-wrap')[0].classList.remove('luck-grit-hud-active');
$(html).find('.luck-grit-wrap')[0].classList.add('luck-grit-hud-disabled');
}
@@ -138,8 +140,8 @@ export default class LethalFantasyUtils {
}
/* -------------------------------------------- */
static handleSocketEvent(msg = {}) {
console.log(`handleSocketEvent !`, msg)
static async handleSocketEvent(msg = {}) {
log(`handleSocketEvent !`, msg)
let actor
switch (msg.type) {
case "applyDamage":
@@ -149,18 +151,18 @@ export default class LethalFantasyUtils {
? 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)
if (actor) await actor.applyDamage(msg.damage)
}
break
case "rollInitiative":
if (msg.userId && msg.userId !== game.user.id) break
actor = game.actors.get(msg.actorId)
actor.system.rollInitiative(msg.combatId, msg.combatantId)
await actor.system.rollInitiative(msg.combatId, msg.combatantId)
break
case "rollProgressionDice":
if (msg.userId && msg.userId !== game.user.id) break
actor = game.actors.get(msg.actorId)
actor.system.rollProgressionDice(msg.combatId, msg.combatantId, msg.rollProgressionCount)
await actor.system.rollProgressionDice(msg.combatId, msg.combatantId, msg.rollProgressionCount)
break
case "requestDefense":
// Vérifier si le message est destiné à cet utilisateur
@@ -184,7 +186,7 @@ export default class LethalFantasyUtils {
const slot = wounds.findIndex(w => !w.value && !w.duration)
if (slot !== -1) {
wounds[slot] = { value: msg.damage, duration: msg.damage, description: "Bleeding" }
actor.update({ "system.hp.wounds": wounds })
await actor.update({ "system.hp.wounds": wounds })
}
}
}
@@ -497,7 +499,7 @@ export default class LethalFantasyUtils {
const isMonster = defender.type === "monster"
console.log(`[LF] showDefenseRequest | attackRollType=${attackRollType} isMonster=${isMonster} defender=${defender?.name}`)
log(`[LF] showDefenseRequest | attackRollType=${attackRollType} isMonster=${isMonster} defender=${defender?.name}`)
// Spell/miracle attacks use saving throws instead of weapon defense
const isSpellAttack = attackRollType === "spell-attack" || attackRollType === "miracle-attack"
@@ -559,9 +561,9 @@ export default class LethalFantasyUtils {
defenderTokenId
}
if (isMonster) {
defender.system.prepareMonsterRoll("save", result)
await defender.system.prepareMonsterRoll("save", result)
} else {
defender.prepareRoll("save", result)
await defender.prepareRoll("save", result)
}
}
return
@@ -637,7 +639,7 @@ export default class LethalFantasyUtils {
isRanged: msg.isRanged
}
defender.system.prepareMonsterRoll("monster-defense", result)
await defender.system.prepareMonsterRoll("monster-defense", result)
}
return
}
@@ -744,9 +746,9 @@ export default class LethalFantasyUtils {
isRanged: msg.isRanged
}
console.log("Storing defense data for character:", defender.id)
log("Storing defense data for character:", defender.id)
defender.prepareRoll("weapon-defense", result)
await defender.prepareRoll("weapon-defense", result)
}
}
@@ -1285,7 +1287,7 @@ export default class LethalFantasyUtils {
/* -------------------------------------------- */
static async compareAttackDefense(data) {
console.log("compareAttackDefense called with:", data)
log("compareAttackDefense called with:", data)
// Compute D30 effects from the attack D30 message directly.
// This is more reliable than depending on the caller-provided values, which are
@@ -1300,11 +1302,11 @@ export default class LethalFantasyUtils {
const outcome = data.outcome || (data.attackRoll > data.defenseRoll ? "hit" : "miss")
const isAttackWin = outcome !== "miss"
console.log("isAttackWin:", isAttackWin, "attackRoll:", data.attackRoll, "defenseRoll:", data.defenseRoll)
log("isAttackWin:", isAttackWin, "attackRoll:", data.attackRoll, "defenseRoll:", data.defenseRoll)
let damageButton = ""
if (isAttackWin && (data.attackWeaponId || data.attackRollKey)) {
console.log("Creating damage button. defenderId:", data.defenderId)
log("Creating damage button. defenderId:", data.defenderId)
// Déterminer le type de dégâts à lancer
if (data.attackRollType === "weapon-attack") {
damageButton = `
@@ -1385,12 +1387,12 @@ export default class LethalFantasyUtils {
</div>
`
console.log("Creating combat result message...")
log("Creating combat result message...")
await ChatMessage.create({
content: resultMessage,
speaker: { alias: "Combat System" }
})
console.log("Combat result message created!")
log("Combat result message created!")
}
static registerHandlebarsHelpers() {
@@ -1527,7 +1529,7 @@ export default class LethalFantasyUtils {
return eval(expr);
})
Handlebars.registerHelper('isOwnerOrGM', function (actor) {
console.log("Testing actor", actor.isOwner, game.userId)
log("Testing actor", actor.isOwner, game.userId)
return actor.isOwner || game.isGM;
})
Handlebars.registerHelper('upperCase', function (text) {
@@ -1665,7 +1667,7 @@ export default class LethalFantasyUtils {
}
)
ChatMessage.create({
await ChatMessage.create({
user: game.user.id,
speaker: { alias: targetActor.name },
mode: "gm",