- Onglets item (Anomalie/Aspect/Attribut): correction tabGroups + data-group sur chaque <a> et <section> - Onglets acteur (PJ/PNJ): tab.cssClass dans les templates pour l'état actif initial - Message de tchat: alignement des noms de variables _getChatCardData <-> chat-message.hbs - actorName, actorImg, success/failure, diceResults, statLabel/skillLabel localisés - difficultyLabel et moonPhaseLabel localisés depuis SYSTEM - Dialogue de jet (roll-dialog.hbs): correction noms variables + min/max modificateur - lang/fr.json: ajout Roll.title, Roll.roll, clés Moon (minuscules), Difficulty (unknown/ardu) - character.mjs: passage statLabel à CelestopolRoll.prompt() - global.less: padding + overflow-y sur .tab pour contenu visible - item-scores.hbs: passage system=system au partial + suppression garde isEditable - Templates anomaly/aspect/attribute: passage system=system au partial item-scores - chat-message.mjs: getHTML() → renderHTML() (dépréciation FVTT v13) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
181 lines
6.3 KiB
JavaScript
181 lines
6.3 KiB
JavaScript
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<CelestopolRoll|null>}
|
|
*/
|
|
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: `<strong>${game.i18n.localize(this.skillLabel ?? "")}</strong>`,
|
|
...messageData,
|
|
},
|
|
{ rollMode }
|
|
)
|
|
}
|
|
}
|