Files
foundryvtt-reve-de-dragon/module/rdd-roll.js
T
fabres 25e3df1f5d Migration Foundry v14 : classes V1→V2, corrections CSS et bugs
- 21 classes Dialog → DialogV2
- 2 classes Application → ApplicationV2 + HandlebarsApplicationMixin
- 1 classe FormApplication → ApplicationV2
- 10 appels new Dialog() → DialogV2 (rdd-confirm, rdd-commands, etc.)
- base-actor-sheet.mjs : 3 appels new Dialog → DialogV2
- Sheet journal : JournalTextPageSheet → JournalEntryPageProseMirrorSheet
- CSS : backdrop-filter, box-shadow, bordures V2 supprimes pour calendar/astrologie
- FenetreRechercheTirage : _getWindowControls + dragDrop ajoutes
- HtmlUtility.showControlWhen : compat jQuery + DOM natif
- rdd-main.js : init calendrier en setTimeout
- Constantes : duplicate → deepClone, _id → id
- renderTemplate global → foundry.applications.handlebars.renderTemplate
2026-07-22 00:29:56 +02:00

336 lines
15 KiB
JavaScript

import { RollDataAjustements } from "./rolldata-ajustements-v1.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemSort } from "./item-sort.js";
import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { CARACS, RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { Grammar } from "./grammar.js";
import { ACTOR_TYPES, RDD_CONFIG, renderTemplate } from "./constants.js";
import { EMPOIGNADE } from "./item/arme.js";
export class RdDRoll extends foundry.applications.api.DialogV2 {
static async create(actor, rollData, dialogConfig, action) {
RdDRoll._ensureCorrectAction(action)
RdDRoll._setDefaultOptions(actor, rollData)
const content = await renderTemplate(dialogConfig.html, rollData)
const options = { classes: ["rdd-roll-dialog"], width: 650, height: "fit-content" }
return new RdDRoll(actor, rollData, content, options, action)
}
static _setDefaultOptions(actor, rollData) {
let defaultRollData = {
alias: actor.getAlias(),
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
difficultesLibres: CONFIG.RDD.difficultesLibres,
etat: actor.getEtatGeneral(),
moral: actor.getMoralTotal(),
amoureux: actor.listeAmoureux(),
carac: foundry.utils.deepClone(actor.system.carac),
finalLevel: 0,
diffConditions: 0,
diffLibre: rollData.competence?.system.default_diffLibre ?? 0,
perteMoralEchec: false,
use: {
astrologique: true,
moral: false,
libre: true,
coeur: undefined,
conditions: true,
surenc: actor.isSurenc(),
encTotal: true
},
isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence?.name),
encTotal: actor.getEncTotal(),
ajustementAstrologique: actor.ajustementAstrologique(),
surprise: actor.getSurprise(false),
canClose: true,
isGM: game.user.isGM,
forceDiceResult: -1
}
if (actor.type == ACTOR_TYPES.personnage) {
defaultRollData.carac[CARACS.REVE_ACTUEL] = actor.system.reve.reve
}
foundry.utils.mergeObject(rollData, defaultRollData, { recursive: true, overwrite: false })
if (rollData.forceCarac) {
rollData.carac = rollData.forceCarac
}
rollData.diviseurSignificative = RdDRoll.getDiviseurSignificative(rollData)
RollDataAjustements.calcul(rollData, actor)
}
static getDiviseurSignificative(rollData) {
let facteurSign = 1
if (rollData.surprise == "demi") facteurSign *= 2
if (rollData.needParadeSignificative) facteurSign *= 2
if (RdDBonus.isDefenseAttaqueFinesse(rollData)) facteurSign *= 2
if (!ReglesOptionnelles.isUsing("tripleSignificative")) facteurSign = Math.min(facteurSign, 4)
return facteurSign
}
static _ensureCorrectAction(action) {
if (action.callbacks == undefined) {
console.warn("No callback defined for ", action.name)
action.callbacks = [{ action: r => console.warn(action.name, r) }]
}
}
constructor(actor, rollData, content, options, action) {
super(foundry.utils.mergeObject(options, {
title: action.label,
content,
tag: "div",
buttons: [
{ label: action.label, action: "onAction", callback: () => { this.rollData.canClose = true; this.onAction(action) } }
]
}))
this.actor = actor
this.rollData = rollData
}
_onRender(context) {
super._onRender(context)
this.bringToFront()
console.log("RdDRoll._onRender", this.rollData)
const el = this.element
if (!this.rollData.selectedCarac && this.rollData.competence) {
this.rollData.selectedCarac = this.rollData.carac[this.actor.mapCarac(this.rollData.competence.system.defaut_carac)]
}
if (this.rollData.selectedCarac) {
const caracEl = el.querySelector("[name='carac']")
if (caracEl) {
caracEl.value = RdDCarac.caracDetails(this.rollData.selectedCarac.label, { onMessage: () => {} })?.code
?? this.rollData.selectedCarac.code
?? Grammar.toLowerCaseNoAccentNoSpace(this.rollData.selectedCarac.label)
}
}
if (this.rollData.selectedSort) {
this.setSelectedSort(this.rollData.selectedSort)
const dracEl = el.querySelector(".draconic")
if (dracEl) dracEl.value = this.rollData.selectedSort.system.listIndex
}
RdDItemSort.setCoutReveReel(this.rollData.selectedSort)
const dl = el.querySelector("[name='diffLibre']")
if (dl) dl.value = Misc.toInt(this.rollData.diffLibre)
const dc = el.querySelector("[name='diffConditions']")
if (dc) dc.value = Misc.toInt(this.rollData.diffConditions)
this.updateRollResult()
el.querySelector("[name='diffLibre']")?.addEventListener("change", event => {
this.rollData.diffLibre = Misc.toInt(event.currentTarget.value)
this.updateRollResult()
})
el.querySelector("[name='diffConditions']")?.addEventListener("change", event => {
this.rollData.diffConditions = Misc.toInt(event.currentTarget.value)
this.updateRollResult()
})
el.querySelector("[name='force-dice-result']")?.addEventListener("change", event => {
this.rollData.forceDiceResult = Misc.toInt(event.currentTarget.value)
})
el.querySelector("[name='carac']")?.addEventListener("change", event => {
this.rollData.selectedCarac = this.rollData.carac[event.currentTarget.value]
this.updateRollResult()
})
el.querySelector(".roll-draconic")?.addEventListener("change", event => {
this.rollData.competence = this.rollData.draconicList[Misc.toInt(event.currentTarget.value)]
this.updateRollResult()
})
el.querySelector(".roll-sort")?.addEventListener("change", event => {
this.setSelectedSort(this.rollData.sortList[Misc.toInt(event.currentTarget.value)])
this.updateRollResult()
const dl2 = el.querySelector("[name='diffLibre']")
if (dl2) dl2.value = this.rollData.diffLibre
})
el.querySelector(".roll-text")?.addEventListener("change", event => {
this.rollData.competence = this.rollData.competences.find(it => Grammar.equalsInsensitive(it.name, event.currentTarget.value))
this.updateRollResult()
})
el.querySelector(".select-suivant-coeur")?.addEventListener("change", event => {
const selectedActorId = event.currentTarget.value
this.rollData.use.coeur = this.actor.getSuivant(selectedActorId)
if (this.rollData.use.coeur) {
const img = el.querySelector(".utilisation-coeur img.selected-suivant-coeur")
if (img) {
img.src = this.rollData.use.coeur?.img
img.title = this.rollData.use.coeur?.name
}
}
this.updateRollResult()
})
el.querySelector(".roll-signedraconique")?.addEventListener("change", event => {
this.setSelectedSigneDraconique(this.rollData.signes[Misc.toInt(event.currentTarget.value)])
this.updateRollResult()
})
el.querySelector("[name='ptreve-variable']")?.addEventListener("change", event => {
this.rollData.selectedSort.system.ptreve_reel = Misc.toInt(event.currentTarget.value)
console.log("RdDRollSelectDialog - Cout reve", this.rollData.selectedSort.system.ptreve_reel)
this.updateRollResult()
})
el.querySelector("input.check-mortalite")?.addEventListener("change", event => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? RDD_CONFIG.encaissement.nonmortel : RDD_CONFIG.encaissement.mortel
this.updateRollResult()
})
el.querySelector(".cuisine-proportions")?.addEventListener("change", event => {
this.rollData.proportions = Number(event.currentTarget.value)
this.updateRollResult()
})
el.querySelectorAll(".select-by-name").forEach(input =>
input.addEventListener("change", event => {
this.rollData[event.currentTarget.name] = event.currentTarget.value
this.updateRollResult()
})
)
el.querySelectorAll(".checkbox-by-name").forEach(input =>
input.addEventListener("change", event => {
this.rollData[event.currentTarget.name] = event.currentTarget.checked
this.updateRollResult()
})
)
el.querySelectorAll("input.use-astrologique").forEach(input =>
input.addEventListener("change", event => {
this.rollData.use.astrologique = event.currentTarget.checked
this.updateRollResult()
})
)
el.querySelectorAll("input.use-encTotal").forEach(input =>
input.addEventListener("change", event => {
this.rollData.use.encTotal = event.currentTarget.checked
this.updateRollResult()
})
)
el.querySelectorAll("input.use-surenc").forEach(input =>
input.addEventListener("change", event => {
this.rollData.use.surenc = event.currentTarget.checked
this.updateRollResult()
})
)
el.querySelector(".appel-moral")?.addEventListener("click", () => {
this.rollData.use.moral = !this.rollData.use.moral
const icon = el.querySelector(".icon-appel-moral")
const tooltip = el.querySelector(".tooltipAppelAuMoralText")
if (this.rollData.use.moral) {
if (this.rollData.moral > 0) {
if (tooltip) tooltip.innerHTML = "Appel au moral"
if (icon) icon.src = "/systems/foundryvtt-reve-de-dragon/icons/moral-heureux.svg"
} else {
if (tooltip) tooltip.innerHTML = "Appel à l'énergie du désespoir"
if (icon) icon.src = "/systems/foundryvtt-reve-de-dragon/icons/moral-malheureux.svg"
}
} else {
if (tooltip) tooltip.innerHTML = "Sans appel au moral"
if (icon) icon.src = "/systems/foundryvtt-reve-de-dragon/icons/moral-neutre.svg"
}
this.updateRollResult()
})
el.querySelectorAll(".conditionMeditation").forEach(input =>
input.addEventListener("change", event => {
this.rollData.conditionMeditation[event.currentTarget.name] = event.currentTarget.checked
this.updateRollResult()
})
)
}
close() {
if (this.rollData.canClose) {
return super.close()
}
ui.notifications.info("Vous devez faire ce jet de dés!")
}
async onAction(action) {
const el = this.element
this.rollData.forceDiceResult = Number.parseInt(el.querySelector("[name='force-dice-result']")?.value) ?? -1
await RdDResolutionTable.rollData(this.rollData)
for (let callback of action.callbacks) {
await callback.action(this.rollData)
}
}
async setSelectedSort(sort) {
this.rollData.selectedSort = sort
this.rollData.competence = RdDItemSort.getBestDraconicSort(this.rollData.draconicList, sort)
this.rollData.bonus = RdDItemSort.getCaseBonus(sort, this.rollData.tmr.coord, this.actor)
this.rollData.diffLibre = RdDItemSort.getDifficulte(sort, -7)
RdDItemSort.setCoutReveReel(sort)
const descHtml = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/partial-description-sort.hbs", { sort })
const el = this.element
const sortEl = el.querySelector(".sort-ou-rituel")
if (sortEl) sortEl.textContent = sort.system.isrituel ? "rituel" : "sort"
const bonusEl = el.querySelector(".bonus-case")
if (bonusEl) bonusEl.textContent = `${this.rollData.bonus}%`
const descPlace = el.querySelector(".placeholder-description-sort")
if (descPlace) descPlace.innerHTML = descHtml
const dracEl = el.querySelector(".roll-draconic")
if (dracEl) dracEl.value = sort.system.listIndex
const diffFixe = el.querySelector(".div-sort-difficulte-fixe")
if (diffFixe) diffFixe.textContent = Misc.toSignedString(sort.system.difficulte)
const ptFixe = el.querySelector(".div-sort-ptreve-fixe")
if (ptFixe) ptFixe.textContent = sort.system.ptreve
const diffVariable = RdDItemSort.isDifficulteVariable(sort)
const coutVariable = RdDItemSort.isCoutVariable(sort)
const show = (sel, v) => { const e = el.querySelector(sel); if (e) HtmlUtility.showControlWhen(e, v) }
show(".div-sort-non-rituel", !sort.system.isrituel)
show(".div-sort-difficulte-var", diffVariable)
show(".div-sort-difficulte-fixe", !diffVariable)
show(".div-sort-ptreve-var", coutVariable)
show(".div-sort-ptreve-fixe", !coutVariable)
}
async setSelectedSigneDraconique(signe) {
this.rollData.signe = signe
this.rollData.diffLibre = signe.system.difficulte
const sd = this.element.querySelector(".signe-difficulte")
if (sd) sd.textContent = Misc.toSignedString(this.rollData.diffLibre)
}
async updateRollResult() {
const rollData = this.rollData
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor)
rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.dmg.mortalite = rollData.dmg.mortalite ?? "mortel"
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac?.label)
RollDataAjustements.calcul(rollData, this.actor)
const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel))
const adjustements = await this.buildAjustements(rollData)
const el = this.element
const show = (sel, v) => { const e = el.querySelector(sel); if (e) HtmlUtility.showControlWhen(e, v) }
show(".use-encTotal", rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac?.label))
show(".use-surenc", rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac?.label))
show(".use-astrologique", rollData.ajustements.astrologique.visible)
show(".utilisation-moral", rollData.use.appelAuMoral)
show(".divAppelAuMoral", rollData.use.appelAuMoral)
show(".utilisation-coeur", rollData.ajustements.coeur.visible)
show(".utilisation-coeur img.selected-suivant-coeur", rollData.ajustements.coeur.visible && rollData.use.coeur != undefined)
const titleEl = el.querySelector(".dialog-roll-title")
if (titleEl) titleEl.textContent = this._getTitle(rollData)
const mortEl = el.querySelector("input.check-mortalite")
if (mortEl) mortEl.checked = rollData.dmg.mortalite == RDD_CONFIG.encaissement.nonmortel
const dmgLabel = el.querySelector("label.dmg-arme-actor")
if (dmgLabel) dmgLabel.textContent = rollData.dmg.isEmpoignade ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total)
const mortLabel = el.querySelector("label.arme-mortalite")
if (mortLabel) mortLabel.textContent = rollData.dmg.mortalite
const ajPlace = el.querySelector("div.placeholder-ajustements")
if (ajPlace) ajPlace.innerHTML = adjustements
const resPlace = el.querySelector("div.placeholder-resolution")
if (resPlace) resPlace.innerHTML = resolutionTable
}
async buildAjustements(rollData) {
return await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.hbs", rollData)
}
_getTitle(rollData) {
const alias = rollData.alias
const carac = rollData.selectedCarac.label
if (!rollData.competence) return `${alias}: ${carac}`
const compName = rollData.competence.name
const niveau = Misc.toSignedString(rollData.competence.system.niveau)
if (compName == carac) return `${alias}: ${carac} Niveau ${niveau}`
if (rollData.draconicList && rollData.selectedSort) return `${alias}: ${rollData.competence.name} Niveau ${niveau} ${rollData.selectedSort.name}`
if (rollData.arme && rollData.arme.name != compName) return `${alias}: ${carac} / ${compName} (${rollData.arme.name}) Niveau ${niveau}`
return `${alias}: ${carac} / ${compName} Niveau ${niveau}`
}
}