import { SYSTEM } from "../config/system.mjs" /** * Système de dés de Célestopol 1922. * * Le jet de base est : (valeur compétence)d6 comparé à un seuil de difficulté. * Le dé de lune ajoute un bonus selon la phase actuelle. * Destin et Spleen modifient le nombre de dés. */ export class CelestopolRoll extends Roll { static CHAT_TEMPLATE = "systems/fvtt-celestopol/templates/chat-message.hbs" get resultType() { return this.options.resultType } get isSuccess() { return this.resultType === "success" } get isFailure() { return this.resultType === "failure" } get actorId() { return this.options.actorId } get actorName() { return this.options.actorName } get actorImage() { return this.options.actorImage } get skillLabel() { return this.options.skillLabel } get difficulty() { return this.options.difficulty } get moonBonus() { return this.options.moonBonus ?? 0 } /** * Ouvre le dialogue de configuration du jet via DialogV2 et exécute le jet. * @param {object} options * @returns {Promise} */ static async prompt(options = {}) { const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) const fieldRollMode = new foundry.data.fields.StringField({ choices: rollModes, blank: false, default: "publicroll", }) const dialogContext = { actorName: options.actorName, statLabel: options.statLabel, skillLabel: options.skillLabel, skillValue: options.skillValue, woundMalus: options.woundMalus ?? 0, nbDice: Math.max(1, options.skillValue ?? 1), difficultyChoices:SYSTEM.DIFFICULTY_CHOICES, moonPhaseChoices: SYSTEM.MOON_DICE_PHASES, defaultDifficulty:options.difficulty ?? "normal", defaultMoonPhase: options.moonPhase ?? "none", rollModes, fieldRollMode, } const content = await foundry.applications.handlebars.renderTemplate( "systems/fvtt-celestopol/templates/roll-dialog.hbs", dialogContext ) const title = `${game.i18n.localize("CELESTOPOL.Roll.title")} — ${game.i18n.localize(options.skillLabel ?? "")}` const rollContext = await foundry.applications.api.DialogV2.wait({ window: { title }, classes: ["fvtt-celestopol", "roll-dialog"], content, buttons: [ { label: game.i18n.localize("CELESTOPOL.Roll.roll"), callback: (event, button) => { return Array.from(button.form.elements).reduce((obj, input) => { if (input.name) obj[input.name] = input.value return obj }, {}) }, }, ], rejectClose: false, }) if (!rollContext) return null const difficulty = rollContext.difficulty ?? "normal" const diffConfig = SYSTEM.DIFFICULTY_CHOICES[difficulty] ?? SYSTEM.DIFFICULTY_CHOICES.normal const moonPhase = rollContext.moonPhase ?? "none" const moonConfig = SYSTEM.MOON_DICE_PHASES[moonPhase] ?? SYSTEM.MOON_DICE_PHASES.none const modifier = parseInt(rollContext.modifier ?? 0) || 0 const woundMalus = options.woundMalus ?? 0 const skillValue = Math.max(0, (options.skillValue ?? 0) + woundMalus) const nbDice = Math.max(1, skillValue) const moonBonus = moonConfig.bonus ?? 0 const totalModifier = moonBonus + modifier const formula = totalModifier !== 0 ? `${nbDice}d6 + ${totalModifier}` : `${nbDice}d6` const rollData = { ...options, difficulty, difficultyValue: diffConfig.value, moonPhase, moonBonus, modifier, formula, rollMode: rollContext.visibility ?? "publicroll", } const roll = new this(formula, {}, rollData) await roll.evaluate() roll.computeResult() await roll.toMessage({}, { rollMode: rollData.rollMode }) // Mémoriser les préférences sur l'acteur const actor = game.actors.get(options.actorId) if (actor) { await actor.update({ "system.prefs.moonPhase": moonPhase, "system.prefs.difficulty": difficulty, }) } return roll } /** Détermine succès/échec selon le total vs le seuil. */ computeResult() { const threshold = SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.value ?? 0 if (threshold === 0) { this.options.resultType = "unknown" } else if (this.total >= threshold) { this.options.resultType = "success" } else { this.options.resultType = "failure" } } /** @override */ async render(chatOptions = {}) { const data = await this._getChatCardData(chatOptions.isPrivate) return foundry.applications.handlebars.renderTemplate(this.constructor.CHAT_TEMPLATE, data) } async _getChatCardData(isPrivate) { const statLabel = this.options.statLabel const skillLabel = this.options.skillLabel const resultType = this.resultType const diceResults = this.dice[0]?.results?.map(r => r.result) ?? [] return { cssClass: [SYSTEM.id, "dice-roll"].join(" "), actorId: this.actorId, actorName: this.actorName, actorImg: this.actorImage, statLabel: statLabel ? game.i18n.localize(statLabel) : "", skillLabel: skillLabel ? game.i18n.localize(skillLabel) : "", formula: this.formula, total: this.total, resultType, resultClass: resultType === "success" ? "success" : resultType === "failure" ? "failure" : "", success: this.isSuccess, failure: this.isFailure, difficulty: this.options.difficulty, difficultyLabel:game.i18n.localize(SYSTEM.DIFFICULTY_CHOICES[this.options.difficulty]?.label ?? ""), moonPhase: this.options.moonPhase, moonPhaseLabel: game.i18n.localize(SYSTEM.MOON_DICE_PHASES[this.options.moonPhase]?.label ?? ""), moonBonus: this.moonBonus, modifier: this.options.modifier, isPrivate, tooltip: isPrivate ? "" : await this.getTooltip(), diceResults, } } /** @override */ async toMessage(messageData = {}, { rollMode, create = true } = {}) { return super.toMessage( { flavor: `${game.i18n.localize(this.skillLabel ?? "")}`, ...messageData, }, { rollMode } ) } }