Files
fvtt-yggdrasill/modules/applications/yggdrasill-roll-dialog.mjs

335 lines
11 KiB
JavaScript

import { YggdrasillUtility } from "../yggdrasill-utility.js"
const dureeGaldrSD = { "1d5a": 3, "1d10t": 6, "1d10m": 9, "1d10h": 12, "1d5j": 15}
const ciblesGaldrSD = { "1": 3, "2_4": 6, "5_9": 9, "10_49": 12, "50plus": 15}
const zonesciblesGaldrSD = { "INS10cm3": 3, "INS50cm3": 6, "INS1m3": 9, "INS5m3": 12, "INS10m3": 15}
/**
* Dialogue de jet de dé pour Yggdrasill - Version DialogV2
*/
export class YggdrasillRollDialog {
/**
* Create and display the roll dialog
* @param {YggdrasillActor} actor - The actor making the roll
* @param {Object} rollData - Data for the roll
* @returns {Promise}
*/
static async create(actor, rollData) {
// Initialiser attackData pour les armes si nécessaire
if ((rollData.mode === "armecc" || rollData.mode === "armetir" || rollData.mode === "armedist") && rollData.attackDef) {
// Les données sont déjà calculées dans attackDef par getAttaqueData/getTirData
// On les copie simplement dans attackData pour le template
rollData.attackData = { ...rollData.attackDef }
}
// Calculer srTotal pour les sorts
if (rollData.mode === "galdr") {
this._updateGaldrSD(rollData)
rollData.srTotal = 14 + (rollData.sdGaldr || 0)
} else if (rollData.mode === "sejdr") {
rollData.srTotal = 14 + (rollData.bonusdefense || 0)
} else if (rollData.mode === "rune") {
this._updateRuneData(rollData, actor)
}
// Préparer le contexte pour le template
const context = {
...rollData,
img: actor.img,
name: actor.name,
config: game.system.yggdrasill.config,
}
// Titre selon le mode
let title = "Test"
if (rollData.mode === "competence") title = "Test de Compétence"
else if (rollData.mode === "carac") title = "Test de Caractéristique"
else if (rollData.mode === "attribut") title = "Test d'Attribut"
else if (rollData.mode === "armecc") title = "Attaque au Corps à Corps"
else if (rollData.mode === "armetir" || rollData.mode === "armedist") title = "Attaque à Distance"
else if (rollData.mode === "sejdr") title = "Sort Sejdr"
else if (rollData.mode === "galdr") title = "Sort Galdr"
else if (rollData.mode === "rune") title = "Rune"
// Rendre le template en HTML
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-yggdrasill/templates/roll-dialog-generic-new.hbs",
context
)
// Utiliser DialogV2.wait avec le HTML rendu
return foundry.applications.api.DialogV2.wait({
window: {
title: title,
icon: "fa-solid fa-dice-d20"
},
classes: ["yggdrasill-roll-dialog"],
position: { width: 600 },
modal: false,
content,
buttons: [
{
action: "roll",
label: "Lancer le Test",
icon: "fa-solid fa-dice",
default: true,
callback: (event, button, dialog) => {
this._updateRollDataFromForm(rollData, button.form.elements, actor)
this._performRoll(rollData)
}
},
],
actions: {},
render: (event, dialog) => {
// Pour Galdr: recalculer srTotal quand durée, cibles ou zone changent
if (rollData.mode === "galdr") {
$("#dureeGaldr, #nbCibles, #zoneGaldr").on("change", () => {
rollData.dureeGaldr = $("#dureeGaldr").val()
rollData.nbCibles = $("#nbCibles").val()
rollData.zoneGaldr = $("#zoneGaldr").val()
this._updateGaldrSD(rollData)
rollData.srTotal = 14 + (rollData.sdGaldr || 0)
$("#srTotal").text(rollData.srTotal)
})
}
// Pour Sejdr: recalculer srTotal quand DM change
if (rollData.mode === "sejdr") {
$("#bonusdefense").on("change", () => {
rollData.bonusdefense = parseInt($("#bonusdefense").val()) || 0
rollData.srTotal = 14 + rollData.bonusdefense
$("#srTotal").text(rollData.srTotal)
})
}
// Pour Rune: recalculer srTotal quand support ou puissance changent
if (rollData.mode === "rune") {
$("#supportRune, #puissanceRune").on("change", () => {
rollData.supportRune = $("#supportRune").val()
rollData.puissanceRune = parseInt($("#puissanceRune").val()) || 1
this._updateRuneData(rollData, actor)
$("#runeDuree").text(rollData.runeDuree)
$("#runeDureeVie").text(rollData.runeDureeVie)
$("#srTotal").text(rollData.srTotal)
})
}
},
rejectClose: false,
})
}
/**
* Perform the roll based on mode
* @param {Object} rollData - The roll data
* @private
*/
static _performRoll(rollData) {
if (rollData.mode === "attribut") {
YggdrasillUtility.rollAttribute(rollData)
} else {
YggdrasillUtility.rollYggdrasill(rollData)
}
}
/**
* Mettre à jour rollData avec les valeurs du formulaire
* @param {Object} rollData - L'objet rollData à mettre à jour
* @param {HTMLFormControlsCollection} formElements - Les éléments du formulaire
* @param {YggdrasillActor} actor - L'acteur pour récupérer les données
* @private
*/
static _updateRollDataFromForm(rollData, formElements, actor) {
// Caractéristique (pour compétences)
if (formElements.caracName) {
const caracKey = formElements.caracName.value
rollData.caracName = caracKey
rollData.selectedCarac = this._findCaracByKey(actor, caracKey)
}
// Type d'attaque (pour armes)
if (formElements.typeAttack) {
rollData.attackDef = rollData.attackDef || {}
rollData.attackDef.typeAttack = formElements.typeAttack.value
this._updateAttackData(rollData, actor)
}
// Bonus/Malus
if (formElements.bonusMalus) {
rollData.bonusMalus = parseInt(formElements.bonusMalus.value) || 0
}
// Furor
if (formElements.furorUsage) {
rollData.furorUsage = parseInt(formElements.furorUsage.value) || 0
}
// Seuil de Réussite
if (formElements.sr) {
rollData.sr = parseInt(formElements.sr.value) || 9
}
// Galdr options
if (formElements.dureeGaldr) {
rollData.dureeGaldr = formElements.dureeGaldr.value
}
if (formElements.nbCibles) {
rollData.nbCibles = formElements.nbCibles.value
}
if (formElements.zoneGaldr) {
rollData.zoneGaldr = formElements.zoneGaldr.value
}
if (rollData.mode === "galdr") {
this._updateGaldrSD(rollData)
rollData.srTotal = 14 + (rollData.sdGaldr || 0)
}
// Sejdr options
if (formElements.bonusdefense) {
rollData.bonusdefense = parseInt(formElements.bonusdefense.value) || 0
}
if (rollData.mode === "sejdr") {
rollData.srTotal = 14 + (rollData.bonusdefense || 0)
}
// Rune options
if (formElements.runeDuree) {
rollData.runeDuree = formElements.runeDuree.value
}
}
/**
* Find characteristic by key in actor
* @param {YggdrasillActor} actor
* @param {String} caracKey
* @returns {Object}
* @private
*/
static _findCaracByKey(actor, caracKey) {
for (let [categKey, categ] of Object.entries(actor.system.carac)) {
for (let [key, carac] of Object.entries(categ.carac)) {
if (key === caracKey) {
return { ...carac, categName: categKey, caracName: key }
}
}
}
return null
}
/**
* Update attack data based on attack type
* @param {Object} rollData
* @param {YggdrasillActor} actor
* @private
*/
static _updateAttackData(rollData, actor) {
const config = game.system.yggdrasill.config
const attackType = rollData.attackDef.typeAttack
const attackMode = config.attackMode?.[attackType]
if (attackMode) {
rollData.attackData = rollData.attackData || {}
rollData.attackData.categName = attackMode.categName
rollData.attackData.caracName = attackMode.caracName
rollData.attackData.malus = this._computeValue(attackMode.malus, actor)
rollData.attackData.bonusdegats = this._computeValue(attackMode.bonusdegats, actor)
rollData.attackData.protection = this._computeValue(attackMode.protection, actor)
rollData.attackData.label = attackMode.label
rollData.attackData.description = attackMode.description
}
}
/**
* Compute value from string formula like "puissance;3"
* @param {String|Number} value
* @param {YggdrasillActor} actor
* @returns {Number}
* @private
*/
static _computeValue(value, actor) {
if (typeof value === 'number') return value
if (!value || value === "0") return 0
const parts = String(value).split(';')
if (parts.length === 2) {
const caracName = parts[0]
const multiplier = parseInt(parts[1]) || 1
const caracValue = this._getCaracValue(actor, caracName)
return caracValue * multiplier
}
return 0
}
/**
* Get characteristic value by name
* @param {YggdrasillActor} actor
* @param {String} caracName
* @returns {Number}
* @private
*/
static _getCaracValue(actor, caracName) {
for (let categ of Object.values(actor.system.carac)) {
if (categ.carac && categ.carac[caracName]) {
return categ.carac[caracName].value || 0
}
}
return 0
}
/**
* Update rune data based on support and power
* @param {Object} rollData
* @param {YggdrasillActor} actor
* @private
*/
static _updateRuneData(rollData, actor) {
let support = 0
rollData.dureeRune = 6 - (rollData.agiliteCarac?.value || 0)
if (rollData.supportRune === "peau") {
support = 3
rollData.echelleDuree = "Actions"
rollData.echelleDureeVie = "Heures"
} else if (rollData.supportRune === "tissu") {
support = 6
rollData.echelleDuree = "Tours"
rollData.echelleDureeVie = "Jours"
} else if (rollData.supportRune === "cuir") {
support = 9
rollData.echelleDuree = "Minutes"
rollData.echelleDureeVie = "Semaines"
} else if (rollData.supportRune === "bois") {
support = 12
rollData.echelleDuree = "Heures"
rollData.echelleDureeVie = "Mois"
} else if (rollData.supportRune === "pierremetal") {
support = 15
rollData.echelleDuree = "Jours"
rollData.echelleDureeVie = "Années"
}
rollData.runeDuree = `${rollData.dureeRune} ${rollData.echelleDuree}`
rollData.runeDureeVie = `${rollData.competence?.system.niveau || 1} ${rollData.echelleDureeVie}`
rollData.srTotal = (rollData.puissanceRune || 1) + (Number(rollData.sort?.system.niveau || 0) * 3) + support
}
/**
* Update Galdr SD based on duration and targets
* @param {Object} rollData
* @private
*/
static _updateGaldrSD(rollData) {
let sdDuree = Number(dureeGaldrSD[rollData.dureeGaldr]) || 0
let sdVar = 0
if (rollData.sort?.system.voie === "illusion") {
sdVar = Number(zonesciblesGaldrSD[rollData.zoneGaldr]) || 0
} else {
sdVar = Number(ciblesGaldrSD[rollData.nbCibles]) || 0
}
rollData.sdGaldr = sdDuree + sdVar
}
}