import { SYSTEM } from "../config/system.mjs" import { prompt } from "./roll-prompt.mjs" import { promptInitiative, promptCombatAction, promptRangedDefense, promptRangedAttack } from "./roll-combat.mjs" import { rollSpellDamageToMessage } from "./roll-damage.mjs" export default class LethalFantasyRoll extends Roll { /** * The HTML template path used to render dice checks of this type * @type {string} */ static CHAT_TEMPLATE = "systems/fvtt-lethal-fantasy/templates/chat-message.hbs" get type() { return this.options.type } get titleFormula() { return this.options.titleFormula } get rollName() { return this.options.rollName } get target() { return this.options.target } get value() { return this.options.value } get treshold() { return this.options.treshold } get actorId() { return this.options.actorId } get actorName() { return this.options.actorName } get actorImage() { return this.options.actorImage } get modifier() { return this.options.modifier } get resultType() { return this.options.resultType } get isFailure() { return this.resultType === "failure" } get hasTarget() { return this.options.hasTarget } get targetName() { return this.options.targetName } get targetArmor() { return this.options.targetArmor } get targetMalus() { return this.options.targetMalus } get realDamage() { return this.options.realDamage } get rollTotal() { return this.options.rollTotal } get diceResults() { return this.options.diceResults } get rollTarget() { return this.options.rollTarget } get D30result() { return this.options.D30result } get D30message() { return this.options.D30message } get badResult() { return this.options.badResult } get rollData() { return this.options.rollData } get defenderId() { return this.options.defenderId } /** * Creates a title based on the given type. * * @param {string} type The type of the roll. * @param {string} target The target of the roll. * @returns {string} The generated title. */ static createTitle(type, target) { switch (type) { case "challenge": return `${game.i18n.localize("LETHALFANTASY.Label.titleChallenge")}` case "save": return `${game.i18n.localize("LETHALFANTASY.Label.titleSave")}` case "monster-skill": case "skill": return `${game.i18n.localize("LETHALFANTASY.Label.titleSkill")}` case "weapon-attack": return `${game.i18n.localize("LETHALFANTASY.Label.weapon-attack")}` case "weapon-defense": return `${game.i18n.localize("LETHALFANTASY.Label.weapon-defense")}` case "weapon-damage": return `${game.i18n.localize("LETHALFANTASY.Label.weapon-damage")}` case "spell": case "spell-attack": case "spell-power": return `${game.i18n.localize("LETHALFANTASY.Label.spell")}` case "miracle": case "miracle-attack": case "miracle-power": return `${game.i18n.localize("LETHALFANTASY.Label.miracle")}` default: return game.i18n.localize("LETHALFANTASY.Label.titleStandard") } } /** @override */ async render(chatOptions = {}) { let chatData = await this._getChatCardData(chatOptions.isPrivate) log("ChatData", chatData) return await foundry.applications.handlebars.renderTemplate(this.constructor.CHAT_TEMPLATE, chatData) } /* * Generates the data required for rendering a roll chat card. */ async _getChatCardData(isPrivate) { // Générer la liste des combatants de la scène let combatants = [] let isAttack = this.type === "weapon-attack" || this.type === "monster-attack" || this.type === "spell-attack" || this.type === "miracle-attack" if (this.rollData?.isDamage || isAttack) { // D'abord, ajouter les combattants du combat actif if (game?.combat?.combatants) { for (let c of game.combat.combatants) { if (c.actorId !== this.actorId) { combatants.push({ id: c.id, name: c.name, tokenId: c.token.id }) } } } // Ensuite, ajouter tous les tokens de la scène active qui ne sont pas déjà dans la liste if (canvas?.scene?.tokens) { const existingTokenIds = new Set(combatants.map(c => c.tokenId)) for (let token of canvas.scene.tokens) { if (token.actorId !== this.actorId && !existingTokenIds.has(token.id)) { combatants.push({ id: token.id, name: token.name, tokenId: token.id }) } } } } // Récupérer les informations de l'arme pour les attaques réussies let weaponDamageOptions = null 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, weaponName: weapon.name, damageM: weapon.system?.damage?.damageM } log("Weapon damage options:", weaponDamageOptions) } else if (this.type === "monster-attack" && this.rollTarget) { weaponDamageOptions = { weaponId: this.rollTarget.rollKey, weaponName: this.rollTarget.name, damageFormula: this.rollTarget.damageDice, damageModifier: this.rollTarget.damageModifier, isMonster: true } log("Monster damage options:", weaponDamageOptions) } const cardData = { css: [SYSTEM.id, "dice-roll"], data: this.data, diceTotal: this.dice.reduce((t, d) => t + d.total, 0), isGM: game.user.isGM, formula: this.formula, titleFormula: this.titleFormula, rollName: this.rollName, rollType: this.type, rollTarget: this.rollTarget, total: this.rollTotal, isFailure: this.isFailure, actorId: this.actorId, diceResults: this.diceResults, actingCharName: this.actorName, actingCharImg: this.actorImage, resultType: this.resultType, hasTarget: this.hasTarget, targetName: this.targetName, targetArmor: this.targetArmor, D30result: this.D30result, D30message: this.D30message, badResult: this.badResult, rollData: this.rollData, isPrivate: isPrivate, combatants: combatants, weaponDamageOptions: weaponDamageOptions, isAttack: isAttack, defenderId: this.defenderId, // Vérifier si l'utilisateur peut sélectionner une cible (est GM ou possède l'acteur) canSelectTarget: game.user.isGM || game.actors.get(this.actorId)?.testUserPermission(game.user, "OWNER") } cardData.cssClass = cardData.css.join(" ") cardData.tooltip = isPrivate ? "" : await this.getTooltip() return cardData } /** * Converts the roll result to a chat message. * * @param {Object} [messageData={}] Additional data to include in the message. * @param {Object} options Options for message creation. * @param {string} options.messageMode The mode of the roll (e.g., public, private). * @param {boolean} [options.create=true] Whether to create the message. * @returns {Promise} - A promise that resolves when the message is created. */ async toMessage(messageData = {}, { messageMode, create = true } = {}) { return await super.toMessage( { isSave: this.isSave, isChallenge: this.isChallenge, isFailure: this.resultType === "failure", rollType: this.type, rollTarget: this.rollTarget, actingCharName: this.actorName, actingCharImg: this.actorImage, hasTarget: this.hasTarget, targetName: this.targetName, targetArmor: this.targetArmor, targetMalus: this.targetMalus, realDamage: this.realDamage, rollData: this.rollData, ...messageData, }, { messageMode, create }, ) } } // Attach imported prompt methods LethalFantasyRoll.prompt = prompt LethalFantasyRoll.promptInitiative = promptInitiative LethalFantasyRoll.promptCombatAction = promptCombatAction LethalFantasyRoll.promptRangedDefense = promptRangedDefense LethalFantasyRoll.promptRangedAttack = promptRangedAttack LethalFantasyRoll.rollSpellDamageToMessage = rollSpellDamageToMessage