diff --git a/module/actor.js b/module/actor.js index c6c67f35..fc90154b 100644 --- a/module/actor.js +++ b/module/actor.js @@ -48,6 +48,39 @@ export class RdDActor extends Actor { Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId)); } + static onSocketMessage(sockmsg) { + switch (sockmsg.msg) { + case "msg_remote_actor_call": + return RdDActor.onRemoteActorCall(sockmsg.data); + } + } + + static remoteActorCall(actorId, method, ...args) { + game.socket.emit("system.foundryvtt-reve-de-dragon", { + msg: "msg_remote_actor_call", + data: { + gmId: Misc.connectedGM(), + toActorId: actorId, + method: method, + args: args + } + }); + } + + static onRemoteActorCall(data) { + if (game.user.id == data.gmId) { // Seul le GM connecté choisi effectue l'appel + const actor = game.actors.get(data?.toActorId); + if (!actor) { + console.info("RdDActor.onRemoteActorCall: Pas d'Actor disponible ", data); + } + else { + const args = data.args; + console.info(`RdDActor.onRemoteActorCall: pour l'Actor ${data.toActorId}, appel de RdDActor.${data.method}(`, ...args, ')'); + actor[data.method](...args); + } + } + } + static getParentActor(document) { return document?.parent instanceof Actor ? document.parent : undefined } @@ -826,15 +859,15 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - _isConteneurContenu(item, conteneurId) { - if (Misc.data(item)?.type == 'conteneur') { // Si c'est un conteneur, il faut vérifier qu'on ne le déplace pas vers un sous-conteneur lui appartenant + _isConteneurContenu(item, conteneur) { + if (item?.isConteneur()) { // Si c'est un conteneur, il faut vérifier qu'on ne le déplace pas vers un sous-conteneur lui appartenant for (let id of Misc.templateData(item).contenu) { let subObjet = this.getObjet(id); - if (subObjet?.id == conteneurId) { + if (subObjet?.id == conteneur.id) { return true; // Loop detected ! } - if (subObjet?.type == 'conteneur') { - return this._isConteneurContenu(subObjet, conteneurId); + if (subObjet?.isConteneur()) { + return this._isConteneurContenu(subObjet, conteneur); } } } @@ -969,7 +1002,7 @@ export class RdDActor extends Actor { ui.notifications.warn(`Impossible de déplacer un objet (${item.name}) vers un ${destData.type} qui n'est pas un conteneur (${dest.name}) !`); return false; } - if (this._isConteneurContenu(item, conteneurId)) { + if (this._isConteneurContenu(item, dest)) { ui.notifications.warn(`Impossible de déplacer un conteneur parent (${item.name}) dans un de ses contenus ${destData.name} !`); return false; // Loop detected ! } @@ -3077,54 +3110,68 @@ export class RdDActor extends Actor { return; } /* -------------------------------------------- */ - async optimizeArgent(sumDenier, monnaies) { + getFortune() { + let monnaies = Monnaie.filtrerMonnaies(this.data.items); + if (monnaies.length < 4) { + ui.notifications.error("Problème de monnaies manquantes, impossible de payer correctement!") + return 0; + } + return monnaies.map(m => Misc.templateData(m)) + .map(tpl => tpl.valeur_deniers * Number(tpl.quantite)) + .reduce(Misc.sum(), 0); + } + /* -------------------------------------------- */ + async optimizeArgent(fortuneTotale) { + let monnaies = Monnaie.filtrerMonnaies(this.data.items); let parValeur = Misc.classifyFirst(monnaies, it => Misc.templateData(it).valeur_deniers); - let fortune = { - 1000: Math.floor(sumDenier / 1000), // or - 100: Math.floor(sumDenier / 100) % 10, // argent - 10: Math.floor(sumDenier / 10) % 10, // bronze - 1: sumDenier % 10 // étain + 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 } let updates = [] - for (const [valeur, nombre] of Object.entries(fortune)) { + for (const [valeur, nombre] of Object.entries(nouvelleFortune)) { updates.push({ _id: parValeur[valeur]._id, 'data.quantite': nombre }); } await this.updateEmbeddedDocuments('Item', updates); } /* -------------------------------------------- */ - async payerDenier(sumDenier, dataObj = undefined, quantite = 1) { - let monnaies = Monnaie.filtrerMonnaies(this.data.items); - if (monnaies.length < 4) { - ui.notifications.warn("Problème de monnaies manquantes, impossible de payer correctement!") - return; - } - - sumDenier = Number(sumDenier); - let denierDisponible = 0; - - for (let pieceData of monnaies.map(m => Misc.data(m))) { - denierDisponible += pieceData.data.valeur_deniers * Number(pieceData.data.quantite); - } - console.log("DENIER", game.user.character, sumDenier, denierDisponible); - + async depenserDeniers(depense, dataObj = undefined, quantite = 1, toActorId) { + depense = Number(depense); + let fortune = this.getFortune(); + console.log("depenserDeniers", game.user.character, depense, fortune); let msg = ""; - let isPayed = false; - if (denierDisponible >= sumDenier) { - denierDisponible -= sumDenier; - this.optimizeArgent(denierDisponible, monnaies); - msg = `Vous avez payé ${sumDenier} Deniers, qui ont été soustraits de votre argent.`; - RdDAudio.PlayContextAudio("argent"); // Petit son - isPayed = true; + if (depense == 0) { if (dataObj) { - dataObj.payload.data.cout = sumDenier / 100; // Mise à jour du prix en sols , avec le prix acheté + dataObj.payload.data.cout = depense / 100; // Mise à jour du prix en sols , avec le prix acheté dataObj.payload.data.quantite = quantite; await this.createEmbeddedDocuments('Item', [dataObj.payload]); - msg += `
Et l'objet ${dataObj.payload.name} a été ajouté à votre inventaire.`; + msg += `
L'objet ${dataObj.payload.name} a été ajouté gratuitement à votre inventaire.`; + } + } + else { + if (fortune >= depense) { + fortune -= depense; + const toActor = game.actors.get(toActorId) + if (toActor) { + toActor.ajouterDeniers(depense, this.id); + } + await this.optimizeArgent(fortune); + msg = `Vous avez payé ${depense} Deniers${toActor ? " à " + toActor.name : ''}, qui ont été soustraits de votre argent.`; + RdDAudio.PlayContextAudio("argent"); // Petit son + + if (dataObj) { + dataObj.payload.data.cout = depense / 100; // Mise à jour du prix en sols , avec le prix acheté + dataObj.payload.data.quantite = quantite; + await this.createEmbeddedDocuments('Item', [dataObj.payload]); + msg += `
Et l'objet ${dataObj.payload.name} a été ajouté à votre inventaire.`; + } + } else { + msg = "Vous n'avez pas assez d'argent pour payer cette somme !"; } - } else { - msg = "Vous n'avez pas assez d'argent pour payer cette somme !"; } let message = { @@ -3134,6 +3181,24 @@ export class RdDActor extends Actor { ChatMessage.create(message); } + async ajouterDeniers(gain, fromActorId = undefined) { + if (fromActorId && !game.user.isGM) { + RdDActor.remoteActorCall(this.id, 'ajouterDeniers', gain, fromActorId); + } + else { + const fromActor = game.actors.get(fromActorId) + let fortune = this.getFortune(); + fortune += Number(gain); + await this.optimizeArgent(fortune); + + RdDAudio.PlayContextAudio("argent"); // Petit son + ChatMessage.create({ + whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), + content: `Vous avez reçu ${gain} Deniers ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés de votre argent.` + }); + } + } + /* -------------------------------------------- */ async monnaieIncDec(id, value) { let monnaie = this.getMonnaie(id); diff --git a/module/chat-utility.js b/module/chat-utility.js index 7e95b705..38cc2733 100644 --- a/module/chat-utility.js +++ b/module/chat-utility.js @@ -1,3 +1,4 @@ +import { Misc } from "./misc.js"; /** * Class providing helper methods to get the list of users, and @@ -7,31 +8,45 @@ export class ChatUtility { /* -------------------------------------------- */ static onSocketMessage(sockmsg) { switch (sockmsg.msg) { - case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.part, sockmsg.gmId); + case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.data); } } /* -------------------------------------------- */ - static onRemoveMessages(part, gmId) { - if (game.user.id == gmId) { - const toDelete = game.messages.filter(it => it.data.content.includes(part)); - toDelete.forEach(it => it.delete()); + static onRemoveMessages(data) { + if (game.user.isGM || game.user.id == data.gmId) { + if (data.part){ + const toDelete = game.messages.filter(it => it.data.content.includes(data.part)); + toDelete.forEach(it => it.delete()); + } + if (data.messageId){ + game.messages.get(data.messageId)?.delete(); + } } } - + /* -------------------------------------------- */ static removeChatMessageContaining(part) { - const gmId = game.user.isGM ? game.user.id : game.users.entities.find(u => u.isGM && u.active)?.id; + const removeMessageData = { + part: part, + gmId: Misc.connectedGM() + }; - if (!gmId || game.user.isGM) { - ChatUtility.onRemoveMessages(part, game.user.id); + if (game.user.isGM) { + ChatUtility.onRemoveMessages(removeMessageData); } else { - game.socket.emit("system.foundryvtt-reve-de-dragon", { - msg: "msg_delete_chat_message", data: { - part:part, - gmId: gmId, - }}); + game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_delete_chat_message", data: removeMessageData }); + } + } + + static removeChatMessageId(messageId) { + const removeMessageData = { messageId: messageId, gmId: Misc.connectedGM() }; + if (game.user.isGM) { + ChatUtility.onRemoveMessages(removeMessageData); + } + else { + game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_delete_chat_message", data: removeMessageData }); } } @@ -108,5 +123,5 @@ export class ChatUtility { ChatMessage.create(data); } } - + } diff --git a/module/item.js b/module/item.js index 39346eee..9fbbb961 100644 --- a/module/item.js +++ b/module/item.js @@ -104,9 +104,9 @@ export class RdDItem extends Item { async quantiteIncDec(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) { const itemData = Misc.data(this); - const quantite = itemData.data.quantite; - if (quantite != undefined) { - const reste = Math.max(quantite + nombre, 0); + const quantite = Number(itemData.data.quantite ??-1); + if (quantite >=0 ) { + const reste = Math.max(quantite + Number(nombre), 0); if (reste == 0) { if (options.supprimerSiZero){ @@ -150,7 +150,9 @@ export class RdDItem extends Item { let chatData = duplicate(Misc.data(this)); const properties = this[`_${chatData.type}ChatData`](); chatData["properties"] = properties - + if (this.actor){ + chatData.actor = {id: this.actor.id }; + } //Check if the posted item should have availability/pay buttons chatData.hasPrice = "cout" in chatData.data; chatData.data.cout_deniers = 0; @@ -205,7 +207,7 @@ export class RdDItem extends Item { if (chatData.hasPrice) { if (quantiteEnvoi > 0) chatData.postQuantity = Number(quantiteEnvoi); - if (prixTotal > 0) { + if (prixTotal >= 0) { chatData.postPrice = prixTotal; chatData.data.cout_deniers = Math.floor(prixTotal * 100); // Mise à jour cout en deniers } diff --git a/module/misc.js b/module/misc.js index e0a07c61..2faa6f8d 100644 --- a/module/misc.js +++ b/module/misc.js @@ -47,11 +47,13 @@ export class Misc { const parsed = parseInt(value); return isNaN(parsed) ? 0 : parsed; } + static keepDecimals(num, decimals) { if (decimals<=0 || decimals>6) return num; const decimal = Math.pow(10, parseInt(decimals)); return Math.round(num * decimal) / decimal; } + static getFractionHtml(diviseur) { if (!diviseur || diviseur <= 1) return undefined; switch (diviseur || 1) { @@ -111,4 +113,8 @@ export class Misc { static templateData(it) { return Misc.data(it)?.data ?? {} } + + static connectedGM() { + return game.user.isGM ? game.user.id : game.users.entities.find(u => u.isGM && u.active)?.id; + } } \ No newline at end of file diff --git a/module/rdd-combat.js b/module/rdd-combat.js index a8ae51e3..4b78d9c5 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -1211,7 +1211,7 @@ export class RdDCombat { attackerId: this.attackerId, defenderTokenId: defenderTokenId, attackerRoll: attackerRoll, - gmId: game.users.entities.find(u => u.isGM)?.id, + gmId: Misc.connectedGM(), } }); } diff --git a/module/rdd-main.js b/module/rdd-main.js index 822b9b4b..f76c177c 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -140,10 +140,11 @@ Hooks.once("init", async function () { }; /* -------------------------------------------- */ - game.socket.on("system.foundryvtt-reve-de-dragon", data => { - RdDUtility.onSocketMesssage(data); - RdDCombat.onSocketMessage(data); - ChatUtility.onSocketMessage(data); + game.socket.on("system.foundryvtt-reve-de-dragon", sockmsg => { + RdDUtility.onSocketMesssage(sockmsg); + RdDCombat.onSocketMessage(sockmsg); + ChatUtility.onSocketMessage(sockmsg); + RdDActor.onSocketMessage(sockmsg); }); /* -------------------------------------------- */ diff --git a/module/rdd-utility.js b/module/rdd-utility.js index db6ef01c..bb5ed094 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -204,9 +204,9 @@ export class RdDUtility { } /* -------------------------------------------- */ - static buildListOptions( min, max ) { + static buildListOptions(min, max) { let options = "" - for(let i=min; i<= max; i++) { + for (let i = min; i <= max; i++) { options += `` } return options; @@ -529,25 +529,62 @@ export class RdDUtility { }); // Gestion du bouton payer html.on("click", '.payer-button', event => { - let sumdenier = event.currentTarget.attributes['data-somme-denier'].value; - let quantite = 1; - if (event.currentTarget.attributes['data-quantite']) { - quantite = event.currentTarget.attributes['data-quantite'].value; - } + let sumdenier = event.currentTarget.attributes['data-somme-denier']?.value ?? 0; + let quantite = event.currentTarget.attributes['data-quantite']?.value ?? 1; + let fromActorId = event.currentTarget.attributes['data-actor-id']?.value; let jsondata = event.currentTarget.attributes['data-jsondata'] let objData if (jsondata) { objData = JSON.parse(jsondata.value) } - if (game.user.character) { - game.user.character.payerDenier(sumdenier, objData, quantite); - } else { - let msgPayer = "Vous devez avoir un acteur relié pour effectuer le paiement"; - ChatMessage.create({ content: msgPayer, whisper: [game.user] }); + let actor = RdDUtility.getSelectedActor("Pour effectuer le paiement:"); + if (actor) { + actor.depenserDeniers(sumdenier, objData, quantite, fromActorId); + // TODO: diminuer la quantité ou supprimer le message + // message: => document.querySelector("#chat-log > li:nth-child(61) > div > div > span > a") + // => ../../../..[@data-message-id] + let chatMessageId = RdDUtility.findChatMessageId(event.currentTarget); + if (chatMessageId) { + ChatUtility.removeChatMessageId(chatMessageId); + } } }); } + static findChatMessageId(current) { + const isChatMessageWithId = it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id'); + return RdDUtility.findNodeMatching(current, isChatMessageWithId)?.attributes.getNamedItem('data-message-id').value; + } + + static findNodeMatching(current, predicate) { + if (current) { + if (predicate(current)) { + return current; + } + return RdDUtility.findNodeMatching(current.parentElement, predicate); + } + return undefined; + } + + static getSelectedActor(msgPlayer = '') { + if (canvas.tokens.controlled.length == 1) { + let token = canvas.tokens.controlled[0]; + if (token.actor && token.data.actorLink) { + return token.actor; + } + msgPlayer += "
le token sélectionné doit être lié à un personnage"; + } + if (game.user.character) { + return game.user.character; + } + msgPlayer += "
vous pouvez sélectionner un seul token lié à un personnage"; + msgPlayer += "
vous devez être connecté comme joueur avec un personnage sélectionné"; + + ui.notifications.warn(msgPlayer); + ChatMessage.create({ content: msgPlayer, whisper: [game.user] }); + return undefined; + } + /* -------------------------------------------- */ static createMonnaie(name, valeur_deniers, img = "", enc = 0.01) { let piece = { diff --git a/templates/post-item.html b/templates/post-item.html index a569f947..519b8725 100644 --- a/templates/post-item.html +++ b/templates/post-item.html @@ -15,21 +15,21 @@ Quantité: {{postQuantity}} {{/if}} {{#if postPrice}} - Prix: {{postPrice}} Sols
+ Prix: {{postPrice}} Sols {{/if}} - +
{{/if}} - {{#if finalPrice}} Prix Total: {{finalPrice}} Sols
- {{/if}}

{{#if hasPrice}} - Payer + Payer {{/if}}