Correction des monnaies

* 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
This commit is contained in:
Vincent Vandemeulebrouck 2022-10-07 19:05:56 +02:00
parent 5382fb5df3
commit 9621d72f92
7 changed files with 166 additions and 190 deletions

View File

@ -116,17 +116,13 @@ export class RdDActor extends Actor {
const isPersonnage = actorData.type == "personnage"; const isPersonnage = actorData.type == "personnage";
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
if (actorData.items) { if (actorData.items) {
let actor = await super.create(actorData, options); return await super.create(actorData, options);
if (isPersonnage) {
await actor.checkMonnaiePresence();
}
return actor;
} }
if (isPersonnage) { if (isPersonnage) {
const competences = await RdDUtility.loadCompendium(RdDItemCompetence.actorCompendium(actorData.type)); const competences = await RdDUtility.loadCompendium(RdDItemCompetence.actorCompendium(actorData.type));
actorData.items = competences.map(i => i.toObject()); actorData.items = competences.map(i => i.toObject());
actorData.items = actorData.items.concat(Monnaie.monnaiesData()); actorData.items = actorData.items.concat(Monnaie.monnaiesStandard());
} }
else { else {
actorData.items = []; actorData.items = [];
@ -180,8 +176,6 @@ export class RdDActor extends Actor {
await this.cleanupConteneurs(); await this.cleanupConteneurs();
await this.computeEncombrementTotalEtMalusArmure(); await this.computeEncombrementTotalEtMalusArmure();
this.computeEtatGeneral(); 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() { isCreature() {
return this.type == 'creature' || this.type == 'entite'; return this.type == 'creature' || this.type == 'entite';
@ -3610,33 +3595,11 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
getFortune() { getFortune() {
let monnaies = this.itemTypes['monnaie']; return this.itemTypes['monnaie']
if (monnaies.length < 4) { .map(m => Number(m.system.valeur_deniers) * Number(m.system.quantite))
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))
.reduce(Misc.sum(), 0); .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) { async depenserDeniers(depense, dataObj = undefined, quantite = 1, toActorId) {
depense = Number(depense); depense = Number(depense);
@ -3653,10 +3616,9 @@ export class RdDActor extends Actor {
} }
else { else {
if (fortune >= depense) { if (fortune >= depense) {
fortune -= depense;
const toActor = game.actors.get(toActorId) const toActor = game.actors.get(toActorId)
await toActor?.ajouterDeniers(depense, this.id); await toActor?.ajouterDeniers(depense, this.id);
await this.optimizeArgent(fortune); await Monnaie.optimiser(this, fortune - depense);
msg = `Vous avez payé <strong>${depense} Deniers</strong>${toActor ? " à " + toActor.name : ''}, qui ont été soustraits de votre argent.`; msg = `Vous avez payé <strong>${depense} Deniers</strong>${toActor ? " à " + toActor.name : ''}, qui ont été soustraits de votre argent.`;
RdDAudio.PlayContextAudio("argent"); // Petit son RdDAudio.PlayContextAudio("argent"); // Petit son
@ -3679,18 +3641,19 @@ export class RdDActor extends Actor {
} }
async depenser(depense) { async depenser(depense) {
depense = Number(depense); let reste = this.getFortune() - Number.parseInt(depense);
let fortune = this.getFortune();
let reste = fortune - depense;
if (reste >= 0) { if (reste >= 0) {
fortune -= depense; await Monnaie.optimiser(this, reste);
await this.optimizeArgent(fortune);
} }
return reste; return reste;
} }
async ajouterDeniers(gain, fromActorId = undefined) { async ajouterDeniers(gain, fromActorId = undefined) {
gain = Number.parseInt(gain); gain = Number.parseInt(gain);
if (gain < 0) {
ui.notifications.error(`Impossible d'ajouter un gain de ${gain} <0`);
return;
}
if (gain == 0) { if (gain == 0) {
return; return;
} }
@ -3703,9 +3666,7 @@ export class RdDActor extends Actor {
} }
else { else {
const fromActor = game.actors.get(fromActorId) const fromActor = game.actors.get(fromActorId)
let fortune = this.getFortune(); await Monnaie.optimiser(this, gain + this.getFortune());
fortune += gain;
await this.optimizeArgent(fortune);
RdDAudio.PlayContextAudio("argent"); // Petit son RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({ ChatMessage.create({
@ -3744,8 +3705,8 @@ export class RdDActor extends Actor {
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined; const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
const messageVente = game.messages.get(achat.chatMessageIdVente); const messageVente = game.messages.get(achat.chatMessageIdVente);
const html = await messageVente.getHTML(); const html = await messageVente.getHTML();
const buttonAcheter = html.find(".button-acheter")[0]; const button = html.find(".button-acheter")[0];
const vente = DialogItemAchat.prepareVenteData(buttonAcheter, achat.vendeurId, vendeur, acheteur); const vente = DialogItemAchat.venteData(button);
const itemId = vente.item._id; const itemId = vente.item._id;
const isItemEmpilable = "quantite" in vente.item.system; const isItemEmpilable = "quantite" in vente.item.system;

View File

@ -1,34 +1,52 @@
import { Monnaie } from "./item-monnaie.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
export class DialogItemAchat extends Dialog { export class DialogItemAchat extends Dialog {
static async onButtonAcheter(event) { static venteData(button) {
const buttonAcheter = event.currentTarget; const vendeurId = button.attributes['data-vendeurId']?.value;
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;
const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined; const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined;
const acheteur = RdDUtility.getSelectedActor(); const acheteur = RdDUtility.getSelectedActor();
const json = button.attributes['data-jsondata']?.value;
if (!acheteur && !vendeur) { if (!acheteur && !vendeur) {
ui.notifications.info("Pas d'acheteur ni de vendeur, aucun changement"); 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 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); dialog.render(true);
} }
constructor(html, vendeur, acheteur, venteData, chatMessageIdVente) { constructor(html, venteData) {
const isConsommable = venteData.item.type == 'nourritureboisson'; const isConsommable = venteData.item.type == 'nourritureboisson' && venteData.acheteur?.isPersonnage();
let options = { classes: ["dialogachat"], width: 400, height: isConsommable ? 450 : 350, 'z-index': 99999 }; let options = { classes: ["dialogachat"], width: 400, height: isConsommable ? 450 : 350, 'z-index': 99999 };
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre"; const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
@ -47,42 +65,17 @@ export class DialogItemAchat extends Dialog {
super(conf, options); super(conf, options);
this.vendeur = vendeur;
this.acheteur = acheteur;
this.chatMessageIdVente = chatMessageIdVente;
this.venteData = venteData; 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() { async onAchat() {
await $(".nombreLots").change(); await $(".nombreLots").change();
(this.vendeur ?? this.acheteur).achatVente({ (this.venteData.vendeur ?? this.venteData.acheteur).achatVente({
userId: game.user.id, userId: game.user.id,
vendeurId: this.vendeur?.id, vendeurId: this.venteData.vendeur?.id,
acheteurId: this.acheteur?.id, acheteurId: this.venteData.acheteur?.id,
prixTotal: this.venteData.prixTotal, prixTotal: this.venteData.prixTotal,
chatMessageIdVente: this.chatMessageIdVente, chatMessageIdVente: this.venteData.chatMessageIdVente,
choix: this.venteData.choix choix: this.venteData.choix
}); });
} }

View File

@ -3,7 +3,7 @@ import { Misc } from "./misc.js";
export class DialogItemVente extends Dialog { export class DialogItemVente extends Dialog {
static async create(item, callback) { static async display(item, callback) {
const quantite = item.isConteneur() ? 1 : item.system.quantite; const quantite = item.isConteneur() ? 1 : item.system.quantite;
const venteData = { const venteData = {
item: item, item: item,
@ -20,7 +20,7 @@ export class DialogItemVente extends Dialog {
isOwned: item.isOwned, isOwned: item.isOwned,
}; };
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.html`, venteData); 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) { constructor(venteData, html, callback) {

View File

@ -1,44 +1,32 @@
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js"; import { LOG_HEAD } from "./constants.js";
const MONNAIES_STANDARD = [ const MONNAIE_ETAIN = {
{
name: "Etain (1 denier)", type: 'monnaie', name: "Etain (1 denier)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp", img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp",
system: { quantite: 0, valeur_deniers: 1, encombrement: 0.001, description: "" } system: { quantite: 0, valeur_deniers: 1, encombrement: 0.001, description: "" }
}, };
{ const MONNAIE_BRONZE = {
name: "Bronze (10 deniers)", type: 'monnaie', name: "Bronze (10 deniers)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp", img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp",
system: { quantite: 0, valeur_deniers: 10, encombrement: 0.002, description: "" } system: { quantite: 0, valeur_deniers: 10, encombrement: 0.002, description: "" }
}, };
{ const MONNAIE_ARGENT = {
name: "Argent (1 sol)", type: 'monnaie', name: "Argent (1 sol)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp", img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp",
system: { quantite: 0, valeur_deniers: 100, encombrement: 0.003, description: "" } system: { quantite: 0, valeur_deniers: 100, encombrement: 0.003, description: "" }
}, };
{ const MONNAIE_OR = {
name: "Or (10 sols)", type: 'monnaie', name: "Or (10 sols)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp", img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp",
system: { quantite: 0, valeur_deniers: 1000, encombrement: 0.004, description: "" } system: { quantite: 0, valeur_deniers: 1000, encombrement: 0.004, description: "" }
} };
]
const VALEURS_STANDARDS = MONNAIES_STANDARD.map(it =>it.system.valeur_deniers); const MONNAIES_STANDARD = [MONNAIE_ETAIN, MONNAIE_BRONZE, MONNAIE_ARGENT, MONNAIE_OR];
export class Monnaie { export class Monnaie {
static isSystemMonnaie(item, items) { static monnaiesStandard() {
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() {
return MONNAIES_STANDARD; return MONNAIES_STANDARD;
} }
@ -56,10 +44,47 @@ export class Monnaie {
} }
static arrondiDeniers(sols) { static arrondiDeniers(sols) {
return sols.toFixed(2); return Number(sols).toFixed(2);
} }
static triValeurDenier() { static triValeurDenier() {
return Misc.ascending(item => item.system.valeur_deniers) 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);
}
}
} }

View File

@ -268,26 +268,23 @@ export class RdDItem extends Item {
async proposerVente() { async proposerVente() {
console.log(this); console.log(this);
if (this.isConteneurNonVide()) { 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; return;
} }
const dialog = await DialogItemVente.create(this, (vente) => this._onProposerVente(vente)) await DialogItemVente.display(this, async (vente) => {
dialog.render(true); vente["properties"] = this.getProprietes();
} if (vente.isOwned) {
if (vente.quantiteNbLots * vente.tailleLot > vente.quantiteMax) {
async _onProposerVente(venteData) { ui.notifications.warn(`Vous avez ${vente.quantiteMax} ${vente.item.name}, ce n'est pas suffisant pour vendre ${vente.quantiteNbLots} de ${vente.tailleLot}`)
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; return;
} }
} }
venteData.jsondata = JSON.stringify(venteData.item); vente.jsondata = JSON.stringify(vente.item);
console.log(venteData); console.log(vente);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', venteData); let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente);
ChatMessage.create(RdDUtility.chatDataSetup(html)); ChatMessage.create(RdDUtility.chatDataSetup(html));
});
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -750,7 +750,12 @@ export class RdDUtility {
}); });
// gestion bouton tchat Acheter // 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)); html.on("click", '.button-creer-acteur', event => RdDNameGen.onCreerActeur(event));
// Gestion du bouton payer // Gestion du bouton payer
@ -888,11 +893,6 @@ export class RdDUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async confirmerSuppressionItem(sheet, item, htmlToDelete) { static async confirmerSuppressionItem(sheet, item, htmlToDelete) {
const itemId = item.id; const itemId = item.id;
if (Monnaie.isSystemMonnaie(item, sheet.actor.items)) {
ui.notifications.warn("Suppression des monnaies de base impossible");
return;
}
const confirmationSuppression = { const confirmationSuppression = {
settingConfirmer: "confirmation-supprimer-" + item.getItemGroup(), settingConfirmer: "confirmation-supprimer-" + item.getItemGroup(),
content: `<p>Etes vous certain de vouloir supprimer: ${item.name}?</p>`, content: `<p>Etes vous certain de vouloir supprimer: ${item.name}?</p>`,

View File

@ -45,17 +45,17 @@
</div> </div>
</div> </div>
{{#if (eq item.type 'nourritureboisson')}} {{#if (and (eq item.type 'nourritureboisson') (eq acheteur.type 'personnage'))}}
<p> <p>Si vous souhaitez {{#if item.system.boisson}}boire{{else}}manger{{/if}}:</p>
Si vous souhaitez {{#if item.system.boisson}}boire{{else}}manger{{/if}}:
</p>
{{#if item.system.sust}} {{#if item.system.sust}}
<p>Cette {{#if item.system.boisson}}boisson{{else}}nourriture{{/if}} vous apportera <span <p>Cette {{#if item.system.boisson}}boisson{{else}}nourriture{{/if}} vous apportera
class="total-sust">{{totalSust}}</span> de sustantation.</p> <span class="total-sust">{{totalSust}}</span>
de sustantation.</p>
{{/if}} {{/if}}
{{#if item.system.boisson}} {{#if item.system.boisson}}
<p>{{#if item.system.alcoolise}} <p>
{{#if item.system.alcoolise}}
C'est une boisson alcoolisée de force {{item.system.force}}, vous effectuerez un jet d'éthylisme. C'est une boisson alcoolisée de force {{item.system.force}}, vous effectuerez un jet d'éthylisme.
{{/if}} {{/if}}
Cette boisson vous apportera <span class="total-desaltere">{{totalDesaltere}}</span> unités d'eau. Cette boisson vous apportera <span class="total-desaltere">{{totalDesaltere}}</span> unités d'eau.