From 9621d72f923a8de1b977c87c70698c181dacb694 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Fri, 7 Oct 2022 19:05:56 +0200 Subject: [PATCH] Correction des monnaies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Des deniers sont créés si on n'a rien d'autre * Gagner ou dépenser de l'argent fonctionne même si on n'a pas tous les types de pièces * Tous les acteurs peuvent acheter/vendre s'ils ont de l'argent => Pratique pour créer une auberge! * Seuls les personnages peuvent boire et manger * plus de problèmes de monnaies manquantes --- module/actor.js | 67 +++++---------------- module/dialog-item-achat.js | 81 ++++++++++++-------------- module/dialog-item-vente.js | 4 +- module/item-monnaie.js | 99 ++++++++++++++++++++------------ module/item.js | 31 +++++----- module/rdd-utility.js | 12 ++-- templates/dialog-item-achat.html | 62 ++++++++++---------- 7 files changed, 166 insertions(+), 190 deletions(-) diff --git a/module/actor.js b/module/actor.js index bcd34914..ab1aee95 100644 --- a/module/actor.js +++ b/module/actor.js @@ -116,17 +116,13 @@ export class RdDActor extends Actor { const isPersonnage = actorData.type == "personnage"; // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic if (actorData.items) { - let actor = await super.create(actorData, options); - if (isPersonnage) { - await actor.checkMonnaiePresence(); - } - return actor; + return await super.create(actorData, options); } if (isPersonnage) { const competences = await RdDUtility.loadCompendium(RdDItemCompetence.actorCompendium(actorData.type)); actorData.items = competences.map(i => i.toObject()); - actorData.items = actorData.items.concat(Monnaie.monnaiesData()); + actorData.items = actorData.items.concat(Monnaie.monnaiesStandard()); } else { actorData.items = []; @@ -180,8 +176,6 @@ export class RdDActor extends Actor { await this.cleanupConteneurs(); await this.computeEncombrementTotalEtMalusArmure(); this.computeEtatGeneral(); - // Sanity check - await this.checkMonnaiePresence(); } /* -------------------------------------------- */ @@ -194,15 +188,6 @@ export class RdDActor extends Actor { } } - /* -------------------------------------------- */ - async checkMonnaiePresence() { // Ajout opportuniste si les pièces n'existent pas. - if (!this.items) return; // Sanity check during import - let manquantes = Monnaie.monnaiesManquantes(this); - if (manquantes.length > 0) { - await this.createEmbeddedDocuments('Item', manquantes, { renderSheet: false }); - } - } - /* -------------------------------------------- */ isCreature() { return this.type == 'creature' || this.type == 'entite'; @@ -3610,33 +3595,11 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ getFortune() { - let monnaies = this.itemTypes['monnaie']; - if (monnaies.length < 4) { - ui.notifications.error("Problème de monnaies manquantes, impossible de payer correctement!") - throw "Problème de monnaies manquantes, impossible de payer correctement!"; - } - return monnaies.map(m => Number(m.system.valeur_deniers) * Number(m.system.quantite)) + return this.itemTypes['monnaie'] + .map(m => Number(m.system.valeur_deniers) * Number(m.system.quantite)) .reduce(Misc.sum(), 0); } - /* -------------------------------------------- */ - async optimizeArgent(fortuneTotale) { - let monnaies = this.itemTypes['monnaie']; - let parValeur = Misc.classifyFirst(monnaies, it => it.system.valeur_deniers); - let nouvelleFortune = { - 1000: Math.floor(fortuneTotale / 1000), // or - 100: Math.floor(fortuneTotale / 100) % 10, // argent - 10: Math.floor(fortuneTotale / 10) % 10, // bronze - 1: fortuneTotale % 10 // étain - } - console.log('RdDActor.optimizeArgent', fortuneTotale, 'nouvelleFortune', nouvelleFortune, 'monnaie_par_valeur', parValeur); - let updates = []; - for (const [valeur, nombre] of Object.entries(nouvelleFortune)) { - updates.push({ _id: parValeur[valeur].id, 'system.quantite': nombre }); - } - await this.updateEmbeddedDocuments('Item', updates); - } - /* -------------------------------------------- */ async depenserDeniers(depense, dataObj = undefined, quantite = 1, toActorId) { depense = Number(depense); @@ -3653,10 +3616,9 @@ export class RdDActor extends Actor { } else { if (fortune >= depense) { - fortune -= depense; const toActor = game.actors.get(toActorId) await toActor?.ajouterDeniers(depense, this.id); - await this.optimizeArgent(fortune); + await Monnaie.optimiser(this, fortune - depense); msg = `Vous avez payé ${depense} Deniers${toActor ? " à " + toActor.name : ''}, qui ont été soustraits de votre argent.`; RdDAudio.PlayContextAudio("argent"); // Petit son @@ -3679,18 +3641,19 @@ export class RdDActor extends Actor { } async depenser(depense) { - depense = Number(depense); - let fortune = this.getFortune(); - let reste = fortune - depense; + let reste = this.getFortune() - Number.parseInt(depense); if (reste >= 0) { - fortune -= depense; - await this.optimizeArgent(fortune); + await Monnaie.optimiser(this, reste); } return reste; } async ajouterDeniers(gain, fromActorId = undefined) { gain = Number.parseInt(gain); + if (gain < 0) { + ui.notifications.error(`Impossible d'ajouter un gain de ${gain} <0`); + return; + } if (gain == 0) { return; } @@ -3703,9 +3666,7 @@ export class RdDActor extends Actor { } else { const fromActor = game.actors.get(fromActorId) - let fortune = this.getFortune(); - fortune += gain; - await this.optimizeArgent(fortune); + await Monnaie.optimiser(this, gain + this.getFortune()); RdDAudio.PlayContextAudio("argent"); // Petit son ChatMessage.create({ @@ -3744,8 +3705,8 @@ export class RdDActor extends Actor { const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined; const messageVente = game.messages.get(achat.chatMessageIdVente); const html = await messageVente.getHTML(); - const buttonAcheter = html.find(".button-acheter")[0]; - const vente = DialogItemAchat.prepareVenteData(buttonAcheter, achat.vendeurId, vendeur, acheteur); + const button = html.find(".button-acheter")[0]; + const vente = DialogItemAchat.venteData(button); const itemId = vente.item._id; const isItemEmpilable = "quantite" in vente.item.system; diff --git a/module/dialog-item-achat.js b/module/dialog-item-achat.js index 8b10e7b9..a7c3ec91 100644 --- a/module/dialog-item-achat.js +++ b/module/dialog-item-achat.js @@ -1,34 +1,52 @@ +import { Monnaie } from "./item-monnaie.js"; import { Misc } from "./misc.js"; import { RdDUtility } from "./rdd-utility.js"; export class DialogItemAchat extends Dialog { - static async onButtonAcheter(event) { - const buttonAcheter = event.currentTarget; - if (!buttonAcheter.attributes['data-jsondata']?.value) { - ui.notifications.warn("Impossible d'acheter: informations sur l'objet manquantes") - return; - } - const chatMessageIdVente = RdDUtility.findChatMessageId(buttonAcheter); - - const vendeurId = buttonAcheter.attributes['data-vendeurId']?.value; + static venteData(button) { + const vendeurId = button.attributes['data-vendeurId']?.value; const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined; const acheteur = RdDUtility.getSelectedActor(); - + const json = button.attributes['data-jsondata']?.value; if (!acheteur && !vendeur) { ui.notifications.info("Pas d'acheteur ni de vendeur, aucun changement"); - return; + return undefined; + } + if (!json) { + ui.notifications.warn("Impossible d'acheter: informations sur l'objet manquantes") + return undefined; } - let venteData = DialogItemAchat.prepareVenteData(buttonAcheter, vendeurId, vendeur, acheteur); + const prixLot = Monnaie.arrondiDeniers(button.attributes['data-prixLot']?.value ?? 0); + return { + item: json ? JSON.parse(json) : undefined, + vendeurId: vendeurId, + vendeur: vendeur, + acheteur: acheteur, + tailleLot: parseInt(button.attributes['data-tailleLot']?.value ?? 1), + quantiteIllimite: button.attributes['data-quantiteIllimite']?.value == 'true', + quantiteNbLots: parseInt(button.attributes['data-quantiteNbLots']?.value), + choix: { + nombreLots: 1, + seForcer: false, + supprimerSiZero: true + }, + prixLot: prixLot, + prixTotal: prixLot, + isVente: prixLot > 0, + chatMessageIdVente: RdDUtility.findChatMessageId(button) + }; + } + static async onAcheter(venteData) { const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData); - const dialog = new DialogItemAchat(html, vendeur, acheteur, venteData, chatMessageIdVente); + const dialog = new DialogItemAchat(html, venteData); dialog.render(true); } - constructor(html, vendeur, acheteur, venteData, chatMessageIdVente) { - const isConsommable = venteData.item.type == 'nourritureboisson'; + constructor(html, venteData) { + const isConsommable = venteData.item.type == 'nourritureboisson' && venteData.acheteur?.isPersonnage(); let options = { classes: ["dialogachat"], width: 400, height: isConsommable ? 450 : 350, 'z-index': 99999 }; const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre"; @@ -47,42 +65,17 @@ export class DialogItemAchat extends Dialog { super(conf, options); - this.vendeur = vendeur; - this.acheteur = acheteur; - this.chatMessageIdVente = chatMessageIdVente; this.venteData = venteData; } - static prepareVenteData(buttonAcheter, vendeurId, vendeur, acheteur) { - const jsondata = buttonAcheter.attributes['data-jsondata']?.value; - const prixLot = parseInt(buttonAcheter.attributes['data-prixLot']?.value ?? 0); - return { - item: JSON.parse(jsondata), - vendeurId: vendeurId, - vendeur: vendeur, - acheteur: acheteur, - tailleLot: parseInt(buttonAcheter.attributes['data-tailleLot']?.value ?? 1), - quantiteIllimite: buttonAcheter.attributes['data-quantiteIllimite']?.value == 'true', - quantiteNbLots: parseInt(buttonAcheter.attributes['data-quantiteNbLots']?.value), - choix: { - nombreLots: 1, - seForcer: false, - supprimerSiZero: true - }, - prixLot: prixLot, - prixTotal: prixLot, - isVente: prixLot > 0 - }; - } - async onAchat() { await $(".nombreLots").change(); - (this.vendeur ?? this.acheteur).achatVente({ + (this.venteData.vendeur ?? this.venteData.acheteur).achatVente({ userId: game.user.id, - vendeurId: this.vendeur?.id, - acheteurId: this.acheteur?.id, + vendeurId: this.venteData.vendeur?.id, + acheteurId: this.venteData.acheteur?.id, prixTotal: this.venteData.prixTotal, - chatMessageIdVente: this.chatMessageIdVente, + chatMessageIdVente: this.venteData.chatMessageIdVente, choix: this.venteData.choix }); } diff --git a/module/dialog-item-vente.js b/module/dialog-item-vente.js index 3dba68d0..2b2ebdcd 100644 --- a/module/dialog-item-vente.js +++ b/module/dialog-item-vente.js @@ -3,7 +3,7 @@ import { Misc } from "./misc.js"; export class DialogItemVente extends Dialog { - static async create(item, callback) { + static async display(item, callback) { const quantite = item.isConteneur() ? 1 : item.system.quantite; const venteData = { item: item, @@ -20,7 +20,7 @@ export class DialogItemVente extends Dialog { isOwned: item.isOwned, }; const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.html`, venteData); - return new DialogItemVente(venteData, html, callback); + return new DialogItemVente(venteData, html, callback).render(true); } constructor(venteData, html, callback) { diff --git a/module/item-monnaie.js b/module/item-monnaie.js index df3e2454..b1a57e66 100644 --- a/module/item-monnaie.js +++ b/module/item-monnaie.js @@ -1,44 +1,32 @@ import { Misc } from "./misc.js"; -import { LOG_HEAD, SYSTEM_RDD } from "./constants.js"; +import { LOG_HEAD } from "./constants.js"; -const MONNAIES_STANDARD = [ - { - name: "Etain (1 denier)", type: 'monnaie', - img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp", - system: { quantite: 0, valeur_deniers: 1, encombrement: 0.001, description: "" } - }, - { - name: "Bronze (10 deniers)", type: 'monnaie', - img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp", - system: { quantite: 0, valeur_deniers: 10, encombrement: 0.002, description: "" } - }, - { - name: "Argent (1 sol)", type: 'monnaie', - img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp", - system: { quantite: 0, valeur_deniers: 100, encombrement: 0.003, description: "" } - }, - { - name: "Or (10 sols)", type: 'monnaie', - img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp", - system: { quantite: 0, valeur_deniers: 1000, encombrement: 0.004, description: "" } - } -] -const VALEURS_STANDARDS = MONNAIES_STANDARD.map(it =>it.system.valeur_deniers); +const MONNAIE_ETAIN = { + name: "Etain (1 denier)", type: 'monnaie', + img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp", + system: { quantite: 0, valeur_deniers: 1, encombrement: 0.001, description: "" } +}; +const MONNAIE_BRONZE = { + name: "Bronze (10 deniers)", type: 'monnaie', + img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp", + system: { quantite: 0, valeur_deniers: 10, encombrement: 0.002, description: "" } +}; +const MONNAIE_ARGENT = { + name: "Argent (1 sol)", type: 'monnaie', + img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp", + system: { quantite: 0, valeur_deniers: 100, encombrement: 0.003, description: "" } +}; +const MONNAIE_OR = { + name: "Or (10 sols)", type: 'monnaie', + img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp", + system: { quantite: 0, valeur_deniers: 1000, encombrement: 0.004, description: "" } +}; + +const MONNAIES_STANDARD = [MONNAIE_ETAIN, MONNAIE_BRONZE, MONNAIE_ARGENT, MONNAIE_OR]; export class Monnaie { - static isSystemMonnaie(item, items) { - if (item.type == 'monnaie') { - const valeur = item.system.valeur_deniers; - if (VALEURS_STANDARDS.includes(valeur)) { - const monnaiesDeValeur = items.filter(it => it.type == 'monnaie' && it.system.valeur_deniers == valeur) - return monnaiesDeValeur.length<=1; - } - } - return false; - } - - static monnaiesData() { + static monnaiesStandard() { return MONNAIES_STANDARD; } @@ -56,10 +44,47 @@ export class Monnaie { } static arrondiDeniers(sols) { - return sols.toFixed(2); + return Number(sols).toFixed(2); } static triValeurDenier() { return Misc.ascending(item => item.system.valeur_deniers) } + + static async creerMonnaiesStandard(actor) { + await actor.createEmbeddedDocuments('Item', MONNAIES_STANDARD, { renderSheet: false }); + } + + static async creerMonnaiesDeniers(actor, fortune) { + await actor.createEmbeddedDocuments('Item', [Monnaie.creerDeniers(fortune)], { renderSheet: false }); + } + + static creerDeniers(fortune) { + const deniers = duplicate(MONNAIE_ETAIN); + deniers.system.quantite = fortune; + return deniers; + } + + static async optimiser(actor, fortune) { + let reste = fortune; + let monnaies = actor.itemTypes['monnaie']; + let updates = []; + let parValeur = Misc.classifyFirst(monnaies, it => it.system.valeur_deniers); + for (let valeur of [1000, 100, 10, 1]) { + if (parValeur[valeur]) { + const piecesDeCetteValeur = Math.floor(reste / valeur); + updates.push({ _id: parValeur[valeur].id, 'system.quantite': piecesDeCetteValeur }); + reste -= piecesDeCetteValeur*valeur; + } + } + console.log('Monnaie.optimiser', actor.name, 'total', fortune, 'parValeur', parValeur, 'updates', updates, 'reste', reste); + if (updates.length > 0) { + await actor.updateEmbeddedDocuments('Item', updates); + } + if (reste>0){ + // créer le reste en deniers fortune en deniers + await Monnaie.creerMonnaiesDeniers(actor, reste); + } + } + } diff --git a/module/item.js b/module/item.js index 48da84f1..22e60a37 100644 --- a/module/item.js +++ b/module/item.js @@ -268,26 +268,23 @@ export class RdDItem extends Item { async proposerVente() { console.log(this); if (this.isConteneurNonVide()) { - ui.notifications.warn(`Votre ${this.name} n'est pas vide, pas possible de le donner ou le vendre`); + ui.notifications.warn(`Votre ${this.name} n'est pas vide, pas possible de le proposer`); return; } - const dialog = await DialogItemVente.create(this, (vente) => this._onProposerVente(vente)) - dialog.render(true); - } - - async _onProposerVente(venteData) { - venteData["properties"] = this.getProprietes(); - if (venteData.isOwned) { - if (venteData.quantiteNbLots * venteData.tailleLot > venteData.quantiteMax) { - ui.notifications.warn(`Vous avez ${venteData.quantiteMax} ${venteData.item.name}, ce n'est pas suffisant pour vendre ${venteData.quantiteNbLots} de ${venteData.tailleLot}`) - return; + await DialogItemVente.display(this, async (vente) => { + vente["properties"] = this.getProprietes(); + if (vente.isOwned) { + if (vente.quantiteNbLots * vente.tailleLot > vente.quantiteMax) { + ui.notifications.warn(`Vous avez ${vente.quantiteMax} ${vente.item.name}, ce n'est pas suffisant pour vendre ${vente.quantiteNbLots} de ${vente.tailleLot}`) + return; + } } - } - venteData.jsondata = JSON.stringify(venteData.item); - - console.log(venteData); - let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', venteData); - ChatMessage.create(RdDUtility.chatDataSetup(html)); + vente.jsondata = JSON.stringify(vente.item); + + console.log(vente); + let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente); + ChatMessage.create(RdDUtility.chatDataSetup(html)); + }); } /* -------------------------------------------- */ diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 1d3632dc..c029cb10 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -750,7 +750,12 @@ export class RdDUtility { }); // gestion bouton tchat Acheter - html.on("click", '.button-acheter', event => DialogItemAchat.onButtonAcheter(event)); + html.on("click", '.button-acheter', event => { + const venteData = DialogItemAchat.venteData(event.currentTarget); + if (venteData) { + DialogItemAchat.onAcheter(venteData); + } + }); html.on("click", '.button-creer-acteur', event => RdDNameGen.onCreerActeur(event)); // Gestion du bouton payer @@ -888,11 +893,6 @@ export class RdDUtility { /* -------------------------------------------- */ static async confirmerSuppressionItem(sheet, item, htmlToDelete) { const itemId = item.id; - if (Monnaie.isSystemMonnaie(item, sheet.actor.items)) { - ui.notifications.warn("Suppression des monnaies de base impossible"); - return; - } - const confirmationSuppression = { settingConfirmer: "confirmation-supprimer-" + item.getItemGroup(), content: `

Etes vous certain de vouloir supprimer: ${item.name}?

`, diff --git a/templates/dialog-item-achat.html b/templates/dialog-item-achat.html index 3363a358..c54eaf55 100644 --- a/templates/dialog-item-achat.html +++ b/templates/dialog-item-achat.html @@ -45,38 +45,38 @@ - {{#if (eq item.type 'nourritureboisson')}} -

- Si vous souhaitez {{#if item.system.boisson}}boire{{else}}manger{{/if}}: -

+ {{#if (and (eq item.type 'nourritureboisson') (eq acheteur.type 'personnage'))}} +

Si vous souhaitez {{#if item.system.boisson}}boire{{else}}manger{{/if}}:

- {{#if item.system.sust}} -

Cette {{#if item.system.boisson}}boisson{{else}}nourriture{{/if}} vous apportera {{totalSust}} de sustantation.

- {{/if}} - {{#if item.system.boisson}} -

{{#if item.system.alcoolise}} - C'est une boisson alcoolisée de force {{item.system.force}}, vous effectuerez un jet d'éthylisme. - {{/if}} - Cette boisson vous apportera {{totalDesaltere}} unités d'eau. -

- {{/if}} - {{#if (gt item.system.qualite 0)}} - {{#if (gt item.system.qualite cuisine.system.niveau)}} -

La qualité du plat est telle qu'un jet de Goût/Cuisine à {{numberFormat item.system.qualite decimals=0 sign=true}} - vous permettra un jet de moral heureux.

- {{/if}} - {{/if}} - - {{#if (or (lt item.system.qualite 0) (lt item.system.exotisme 0))}} -

- Pour surmonter {{#if (lt item.system.qualite 0)}}le mauvais goût{{else}}l'exotisme{{/if}}, vous devez effectuer un jet de Volonté/Cuisine à {{numberFormat (min item.system.exotisme item.system.qualite) decimals=0 sign=true}}. -
- - - -

- {{/if}} + {{#if item.system.sust}} +

Cette {{#if item.system.boisson}}boisson{{else}}nourriture{{/if}} vous apportera + {{totalSust}} + de sustantation.

+ {{/if}} + {{#if item.system.boisson}} +

+ {{#if item.system.alcoolise}} + C'est une boisson alcoolisée de force {{item.system.force}}, vous effectuerez un jet d'éthylisme. + {{/if}} + Cette boisson vous apportera {{totalDesaltere}} unités d'eau. +

+ {{/if}} + {{#if (gt item.system.qualite 0)}} + {{#if (gt item.system.qualite cuisine.system.niveau)}} +

La qualité du plat est telle qu'un jet de Goût/Cuisine à {{numberFormat item.system.qualite decimals=0 sign=true}} + vous permettra un jet de moral heureux.

+ {{/if}} + {{/if}} + + {{#if (or (lt item.system.qualite 0) (lt item.system.exotisme 0))}} +

+ Pour surmonter {{#if (lt item.system.qualite 0)}}le mauvais goût{{else}}l'exotisme{{/if}}, vous devez effectuer un jet de Volonté/Cuisine à {{numberFormat (min item.system.exotisme item.system.qualite) decimals=0 sign=true}}. +
+ + + +

+ {{/if}} {{/if}} {{#if isVente}}