- 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
336 lines
15 KiB
JavaScript
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}`
|
|
}
|
|
}
|