Migration datamodels !
This commit is contained in:
@@ -23,6 +23,7 @@ export class WastelandUtility {
|
||||
/* -------------------------------------------- */
|
||||
static async init() {
|
||||
Hooks.on('renderChatLog', (log, html, data) => WastelandUtility.chatListeners(html))
|
||||
Hooks.on('renderChatMessageHTML', (message, html, data) => WastelandUtility.chatListeners(html))
|
||||
Hooks.on("getChatMessageContextOptions", (html, options) => WastelandUtility.chatRollMenu(html, options))
|
||||
|
||||
Hooks.on("getCombatTrackerEntryContext", (html, options) => {
|
||||
@@ -112,11 +113,8 @@ export class WastelandUtility {
|
||||
const skills = await WastelandUtility.loadCompendium("fvtt-wasteland.skills")
|
||||
this.skills = skills.map(i => i.toObject())
|
||||
|
||||
game.system.wasteland.config.listeNiveauSkill = WastelandUtility.createDirectOptionList(0, 10)
|
||||
game.system.wasteland.config.listeNiveauCreature = WastelandUtility.createDirectOptionList(0, 35)
|
||||
game.system.wasteland.config.modificateurOptions = WastelandUtility.createArrayOptionList(-15, 15)
|
||||
game.system.wasteland.config.pointsAmeOptions = WastelandUtility.createDirectOptionList(0, 20)
|
||||
|
||||
// Note: listeNiveauSkill, listeNiveauCreature, etc. are now created in init hook
|
||||
// to be available immediately when sheets are opened
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -160,9 +158,35 @@ export class WastelandUtility {
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Sauvegarder le résultat précédent
|
||||
let previousResult = {
|
||||
diceResult: rollData.diceResult,
|
||||
finalResult: rollData.finalResult,
|
||||
diceFormula: rollData.diceFormula
|
||||
}
|
||||
|
||||
// Récupérer la prédilection utilisée
|
||||
let predilectionUsed = foundry.utils.duplicate(rollData.competence.system.predilections[predIdx])
|
||||
|
||||
// Marquer la prédilection comme utilisée
|
||||
await actor.setPredilectionUsed(rollData.competence._id, predIdx)
|
||||
|
||||
// Mettre à jour la compétence dans rollData
|
||||
rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
|
||||
await WastelandUtility.rollWasteland(rollData)
|
||||
|
||||
// Lancer le reroll avec prédilection
|
||||
await WastelandUtility.rollWastelandPredilection(rollData, predilectionUsed, previousResult)
|
||||
})
|
||||
|
||||
$(html).on("click", '.arme-roll-degats', async event => {
|
||||
let messageId = WastelandUtility.findChatMessageId(event.currentTarget)
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "wasteland-roll")
|
||||
if (rollData && rollData.arme) {
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
await actor.rollArmeDegats(rollData.arme._id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,8 +194,10 @@ export class WastelandUtility {
|
||||
static async preloadHandlebarsTemplates() {
|
||||
|
||||
const templatePaths = [
|
||||
'systems/fvtt-wasteland/templates/editor-notes-gm.html',
|
||||
'systems/fvtt-wasteland/templates/partial-item-description.html'
|
||||
'systems/fvtt-wasteland/templates/editor-notes-gm.hbs',
|
||||
'systems/fvtt-wasteland/templates/partial-item-description.hbs',
|
||||
'systems/fvtt-wasteland/templates/partial-item-header.hbs',
|
||||
'systems/fvtt-wasteland/templates/partial-item-nav.hbs'
|
||||
]
|
||||
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||
}
|
||||
@@ -356,16 +382,19 @@ export class WastelandUtility {
|
||||
if (rollData.attrKey == "tochoose") { // No attr selected, force address
|
||||
rollData.attrKey = "adr"
|
||||
}
|
||||
if (!rollData.attr) {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
|
||||
if (!rollData.attr && actor && actor.system.attributs && actor.system.attributs[rollData.attrKey]) {
|
||||
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
|
||||
if (rollData.attr.labelnorm) {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + rollData.attr.labelnorm + ".webp"
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.charme) {
|
||||
rollData.diceFormula = rollData.charmeDice
|
||||
} else {
|
||||
rollData.diceFormula = rollData.mainDice
|
||||
if (rollData.doubleD20) { // Multiply result !
|
||||
// Double D20 ne s'applique que si on lance un D20, pas un D10
|
||||
if (rollData.doubleD20 && rollData.mainDice === "1d20") {
|
||||
rollData.diceFormula += "*2"
|
||||
if (!rollData.isReroll) {
|
||||
actor.changeEclat(-1)
|
||||
@@ -377,14 +406,55 @@ export class WastelandUtility {
|
||||
if (rollData.competence) {
|
||||
rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
|
||||
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
|
||||
rollData.diceFormula += `+${rollData.attr.value}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
|
||||
} else {
|
||||
rollData.diceFormula += `+${rollData.attr.value}*2+${rollData.modificateur}`
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}*2+${rollData.modificateur}`
|
||||
}
|
||||
if (rollData.arme && rollData.arme.type == "arme") {
|
||||
rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
|
||||
}
|
||||
|
||||
// Apply desavantages (tactical advantages)
|
||||
let desavantagesBonus = 0
|
||||
if (rollData.desavantages) {
|
||||
for (let desavantage in rollData.desavantages) {
|
||||
if (rollData.desavantages[desavantage]) {
|
||||
desavantagesBonus += 5
|
||||
}
|
||||
}
|
||||
desavantagesBonus = Math.min(15, desavantagesBonus)
|
||||
if (desavantagesBonus > 0) {
|
||||
rollData.diceFormula += `+${desavantagesBonus}`
|
||||
}
|
||||
}
|
||||
|
||||
// Monté ?
|
||||
if (rollData.isMonte) {
|
||||
rollData.diceFormula += "+5"
|
||||
}
|
||||
|
||||
// Specific modifiers for ranged combat
|
||||
if (rollData.arme?.system?.isDistance) {
|
||||
if (rollData.visee) {
|
||||
rollData.diceFormula += "+5"
|
||||
}
|
||||
if (rollData.cibleconsciente && rollData.defender) {
|
||||
rollData.diceFormula += `-${rollData.defender.system.attributs.adr.value}`
|
||||
}
|
||||
if (rollData.ciblecourt) {
|
||||
if (rollData.difficulte <= 15) { // Portée courte ou moins
|
||||
rollData.diceFormula += `-5`
|
||||
} else {
|
||||
rollData.diceFormula += `-10`
|
||||
}
|
||||
}
|
||||
if (rollData.typeCouvert && rollData.typeCouvert != "aucun" && rollData.config.couverts[rollData.typeCouvert]) {
|
||||
rollData.diceFormula += `+${rollData.config.couverts[rollData.typeCouvert].value}`
|
||||
}
|
||||
}
|
||||
|
||||
let myRoll = await new Roll(rollData.diceFormula).roll()
|
||||
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||
@@ -395,7 +465,7 @@ export class WastelandUtility {
|
||||
await this.computeResult(rollData, actor)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
|
||||
}, rollData)
|
||||
|
||||
}
|
||||
@@ -413,11 +483,137 @@ export class WastelandUtility {
|
||||
await this.computeResult(rollData)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
|
||||
}, rollData)
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async rollWastelandPredilection(rollData, predilectionUsed, previousResult) {
|
||||
// Sauvegarder le premier résultat
|
||||
rollData.firstRoll = {
|
||||
diceResult: previousResult.diceResult,
|
||||
finalResult: previousResult.finalResult,
|
||||
diceFormula: previousResult.diceFormula
|
||||
}
|
||||
rollData.predilectionUsed = predilectionUsed
|
||||
rollData.isPredilectionReroll = true
|
||||
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
if (rollData.attrKey == "tochoose") {
|
||||
rollData.attrKey = "adr"
|
||||
}
|
||||
if (!rollData.attr && actor && actor.system.attributs && actor.system.attributs[rollData.attrKey]) {
|
||||
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
|
||||
if (rollData.attr.labelnorm) {
|
||||
rollData.actionImg = "systems/fvtt-wasteland/assets/icons/" + rollData.attr.labelnorm + ".webp"
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.charme) {
|
||||
rollData.diceFormula = rollData.charmeDice
|
||||
} else {
|
||||
rollData.diceFormula = rollData.mainDice
|
||||
// Double D20 ne s'applique que si on lance un D20, pas un D10
|
||||
if (rollData.doubleD20 && rollData.mainDice === "1d20") {
|
||||
rollData.diceFormula += "*2"
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.competence) {
|
||||
rollData.predilections = foundry.utils.duplicate(rollData.competence.system.predilections.filter(pred => !pred.used) || [])
|
||||
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}+${rollData.competence.system.niveau}+${rollData.modificateur}+${compmod}`
|
||||
} else {
|
||||
let attrValue = rollData.attr?.value || 0
|
||||
rollData.diceFormula += `+${attrValue}*2+${rollData.modificateur}`
|
||||
}
|
||||
if (rollData.arme && rollData.arme.type == "arme") {
|
||||
rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
|
||||
}
|
||||
|
||||
// Apply desavantages (tactical advantages)
|
||||
if (rollData.desavantages) {
|
||||
let totalDesavantage = 0
|
||||
for (let key in rollData.desavantages) {
|
||||
if (rollData.desavantages[key]) {
|
||||
totalDesavantage += 5
|
||||
}
|
||||
}
|
||||
totalDesavantage = Math.min(totalDesavantage, 15)
|
||||
if (totalDesavantage > 0) {
|
||||
rollData.diceFormula += `+${totalDesavantage}`
|
||||
}
|
||||
}
|
||||
|
||||
// Apply mounted bonus
|
||||
if (rollData.isMonte) {
|
||||
rollData.diceFormula += `+5`
|
||||
}
|
||||
|
||||
// Apply ranged combat modifiers
|
||||
if (rollData.arme && rollData.arme.system && rollData.arme.system.isDistance) {
|
||||
if (rollData.visee) {
|
||||
rollData.diceFormula += `+5`
|
||||
}
|
||||
if (rollData.cibleconsciente && rollData.defender) {
|
||||
let adrMalus = -(rollData.defender.system?.attributs?.adr?.value || 0)
|
||||
rollData.diceFormula += `${adrMalus}`
|
||||
}
|
||||
if (rollData.ciblecourt) {
|
||||
if (rollData.portee === "portee-courte" || rollData.portee === "portee-moyenne") {
|
||||
rollData.diceFormula += `-5`
|
||||
} else {
|
||||
rollData.diceFormula += `-10`
|
||||
}
|
||||
}
|
||||
if (rollData.couvert && rollData.typeCouvertValue) {
|
||||
rollData.diceFormula += `${rollData.typeCouvertValue}`
|
||||
}
|
||||
}
|
||||
|
||||
// Nouveau jet de dé
|
||||
let myRoll = await new Roll(rollData.diceFormula).roll()
|
||||
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||
rollData.diceResult = myRoll.total
|
||||
|
||||
rollData.bonusFormula = "0"
|
||||
if (rollData.charme && rollData.charme.system.bonusmajeur > 0) {
|
||||
rollData.bonusFormula = `${rollData.charme.system.bonusmajeur}d6`
|
||||
rollData.textBonus = "Bonus de Charme"
|
||||
}
|
||||
|
||||
rollData.finalResult = rollData.diceResult
|
||||
let bonusRoll = await new Roll(rollData.bonusFormula).roll()
|
||||
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
|
||||
rollData.finalResult += rollData.bonusRoll.total
|
||||
|
||||
// Comparer avec le premier jet et garder le meilleur
|
||||
rollData.secondRoll = {
|
||||
diceResult: rollData.diceResult,
|
||||
finalResult: rollData.finalResult
|
||||
}
|
||||
|
||||
if (rollData.firstRoll.finalResult > rollData.finalResult) {
|
||||
// Garder le premier résultat
|
||||
rollData.diceResult = rollData.firstRoll.diceResult
|
||||
rollData.finalResult = rollData.firstRoll.finalResult
|
||||
rollData.keptRoll = "first"
|
||||
} else {
|
||||
// Garder le second résultat
|
||||
rollData.keptRoll = "second"
|
||||
}
|
||||
|
||||
await this.computeResult(rollData)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-wasteland/templates/chat-generic-result-v2.hbs`, rollData)
|
||||
}, rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getUsers(filter) {
|
||||
return game.users.filter(filter).map(user => user.data._id);
|
||||
@@ -508,6 +704,7 @@ export class WastelandUtility {
|
||||
pointAmeOptions: this.getPointAmeOptions(),
|
||||
difficulte: 0,
|
||||
modificateur: 0,
|
||||
config: game.system.wasteland.config,
|
||||
}
|
||||
WastelandUtility.updateWithTarget(rollData)
|
||||
return rollData
|
||||
@@ -519,6 +716,7 @@ export class WastelandUtility {
|
||||
if (target) {
|
||||
rollData.defenderTokenId = target.id
|
||||
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
||||
rollData.defender = defender.toObject() // For template access
|
||||
rollData.armeDefense = defender.getBestDefenseValue()
|
||||
if (rollData.armeDefense) {
|
||||
rollData.difficulte = rollData.armeDefense.system.totalDefensif
|
||||
@@ -567,6 +765,85 @@ export class WastelandUtility {
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static cancelBlessure(li) {
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Vérifier que l'acteur a au moins 1 point d'éclat
|
||||
if (actor.getEclat() < 1) {
|
||||
ui.notifications.warn("Pas assez de points d'Éclat")
|
||||
return
|
||||
}
|
||||
|
||||
// Déduire 1 point d'éclat
|
||||
actor.changeEclat(-1)
|
||||
|
||||
// Annuler la dernière blessure (incrémenter la santé)
|
||||
let currentSante = actor.system.sante.value
|
||||
let maxSante = actor.system.sante.max
|
||||
if (currentSante < maxSante) {
|
||||
actor.update({ 'system.sante.value': currentSante + 1 })
|
||||
ui.notifications.info("Blessure annulée - 1 Point d'Éclat dépensé")
|
||||
} else {
|
||||
ui.notifications.warn("Vous êtes déjà en pleine santé")
|
||||
// Rembourser le point d'éclat
|
||||
actor.changeEclat(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static reloadBA(li) {
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Vérifier que l'acteur a au moins 1 point d'éclat
|
||||
if (actor.getEclat() < 1) {
|
||||
ui.notifications.warn("Pas assez de points d'Éclat")
|
||||
return
|
||||
}
|
||||
|
||||
// Déduire 1 point d'éclat
|
||||
actor.changeEclat(-1)
|
||||
|
||||
// Recharger les points de Bonne Aventure au maximum
|
||||
let maxBA = actor.system.bonneaventure.base
|
||||
actor.update({ 'system.bonneaventure.actuelle': maxBA })
|
||||
ui.notifications.info(`Points de Bonne Aventure rechargés à ${maxBA} - 1 Point d'Éclat dépensé`)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static incDecSante(li, value, costBA) {
|
||||
let msgId = $(li).data("message-id")
|
||||
let msg = game.messages.get(msgId)
|
||||
if (msg) {
|
||||
let rollData = msg.getFlag("world", "wasteland-roll")
|
||||
let actor = WastelandUtility.getActorFromRollData(rollData)
|
||||
|
||||
// Vérifier que l'acteur a assez de points de Bonne Aventure
|
||||
if (actor.getBonneAventure() < costBA) {
|
||||
ui.notifications.warn("Pas assez de points de Bonne Aventure")
|
||||
return
|
||||
}
|
||||
|
||||
// Déduire les points de Bonne Aventure
|
||||
actor.changeBonneAventure(-costBA)
|
||||
|
||||
// Incrémenter la santé
|
||||
actor.incDecSante(value)
|
||||
|
||||
ui.notifications.info(`+${value} Point(s) de Santé - ${costBA} Point(s) de Bonne Aventure dépensé(s)`)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static chatRollMenu(html, options) {
|
||||
let canApply = li => canvas.tokens.controlled.length && li.find(".wasteland-roll").length
|
||||
@@ -640,7 +917,7 @@ export class WastelandUtility {
|
||||
name: "Gain de 1 Point de Santé / 24 heure (1 point de Bonne Aventure)",
|
||||
icon: "<i class='fas fa-user-plus'></i>",
|
||||
condition: canApply && hasBA,
|
||||
callback: li => WastelandUtility.incDecSante(1)
|
||||
callback: li => WastelandUtility.incDecSante(li, 1, 1)
|
||||
}
|
||||
)
|
||||
options.push(
|
||||
@@ -648,7 +925,7 @@ export class WastelandUtility {
|
||||
name: "Gain de 2 Points de Santé / 24 heure (2 points de Bonne Aventure)",
|
||||
icon: "<i class='fas fa-user-plus'></i>",
|
||||
condition: canApply && hasBA2,
|
||||
callback: li => WastelandUtility.incDecSante(2)
|
||||
callback: li => WastelandUtility.incDecSante(li, 2, 2)
|
||||
}
|
||||
)
|
||||
options.push(
|
||||
@@ -704,30 +981,24 @@ export class WastelandUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async confirmDelete(actorSheet, li) {
|
||||
let itemId = li.data("item-id");
|
||||
let msgTxt = "<p>Are you sure to remove this Item ?";
|
||||
let buttons = {
|
||||
delete: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Yes, remove it",
|
||||
callback: () => {
|
||||
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||
li.slideUp(200, () => actorSheet.render(false));
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Cancel"
|
||||
}
|
||||
const itemId = li.dataset.itemId
|
||||
if (!itemId) return
|
||||
|
||||
const item = actorSheet.document.items.get(itemId)
|
||||
if (!item) return
|
||||
|
||||
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||
window: { title: "Confirmer la suppression" },
|
||||
content: `<p>Êtes-vous sûr de vouloir supprimer <strong>${item.name}</strong> ?</p>`,
|
||||
rejectClose: false,
|
||||
modal: true,
|
||||
yes: { label: "Supprimer", icon: "fa-trash" },
|
||||
no: { label: "Annuler", icon: "fa-times" }
|
||||
})
|
||||
|
||||
if (confirmed) {
|
||||
await actorSheet.document.deleteEmbeddedDocuments("Item", [itemId])
|
||||
}
|
||||
msgTxt += "</p>";
|
||||
let d = new Dialog({
|
||||
title: "Confirm removal",
|
||||
content: msgTxt,
|
||||
buttons: buttons,
|
||||
default: "cancel"
|
||||
});
|
||||
d.render(true);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user