Merge branch 'v1.4-commerce' into 'v1.4'

Payer donne l'argent à qui de droit

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!200
This commit is contained in:
Leratier Bretonnien 2021-04-15 06:16:42 +00:00
commit fde5096c59
8 changed files with 208 additions and 82 deletions

View File

@ -48,6 +48,39 @@ export class RdDActor extends Actor {
Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId)); 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) { static getParentActor(document) {
return document?.parent instanceof Actor ? document.parent : undefined return document?.parent instanceof Actor ? document.parent : undefined
} }
@ -826,15 +859,15 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_isConteneurContenu(item, conteneurId) { _isConteneurContenu(item, conteneur) {
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 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) { for (let id of Misc.templateData(item).contenu) {
let subObjet = this.getObjet(id); let subObjet = this.getObjet(id);
if (subObjet?.id == conteneurId) { if (subObjet?.id == conteneur.id) {
return true; // Loop detected ! return true; // Loop detected !
} }
if (subObjet?.type == 'conteneur') { if (subObjet?.isConteneur()) {
return this._isConteneurContenu(subObjet, conteneurId); 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}) !`); 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; 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} !`); ui.notifications.warn(`Impossible de déplacer un conteneur parent (${item.name}) dans un de ses contenus ${destData.name} !`);
return false; // Loop detected ! return false; // Loop detected !
} }
@ -3077,48 +3110,61 @@ export class RdDActor extends Actor {
return; 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 parValeur = Misc.classifyFirst(monnaies, it => Misc.templateData(it).valeur_deniers);
let fortune = { let nouvelleFortune = {
1000: Math.floor(sumDenier / 1000), // or 1000: Math.floor(fortuneTotale / 1000), // or
100: Math.floor(sumDenier / 100) % 10, // argent 100: Math.floor(fortuneTotale / 100) % 10, // argent
10: Math.floor(sumDenier / 10) % 10, // bronze 10: Math.floor(fortuneTotale / 10) % 10, // bronze
1: sumDenier % 10 // étain 1: fortuneTotale % 10 // étain
} }
let updates = [] 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 }); updates.push({ _id: parValeur[valeur]._id, 'data.quantite': nombre });
} }
await this.updateEmbeddedDocuments('Item', updates); await this.updateEmbeddedDocuments('Item', updates);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async payerDenier(sumDenier, dataObj = undefined, quantite = 1) { async depenserDeniers(depense, dataObj = undefined, quantite = 1, toActorId) {
let monnaies = Monnaie.filtrerMonnaies(this.data.items); depense = Number(depense);
if (monnaies.length < 4) { let fortune = this.getFortune();
ui.notifications.warn("Problème de monnaies manquantes, impossible de payer correctement!") console.log("depenserDeniers", game.user.character, depense, fortune);
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);
let msg = ""; let msg = "";
let isPayed = false; if (depense == 0) {
if (denierDisponible >= sumDenier) {
denierDisponible -= sumDenier;
this.optimizeArgent(denierDisponible, monnaies);
msg = `Vous avez payé <strong>${sumDenier} Deniers</strong>, qui ont été soustraits de votre argent.`;
RdDAudio.PlayContextAudio("argent"); // Petit son
isPayed = true;
if (dataObj) { 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 += `<br>L'objet <strong>${dataObj.payload.name}</strong> 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é <strong>${depense} Deniers</strong>${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; dataObj.payload.data.quantite = quantite;
await this.createEmbeddedDocuments('Item', [dataObj.payload]); await this.createEmbeddedDocuments('Item', [dataObj.payload]);
msg += `<br>Et l'objet <strong>${dataObj.payload.name}</strong> a été ajouté à votre inventaire.`; msg += `<br>Et l'objet <strong>${dataObj.payload.name}</strong> a été ajouté à votre inventaire.`;
@ -3126,6 +3172,7 @@ export class RdDActor extends Actor {
} else { } else {
msg = "Vous n'avez pas assez d'argent pour payer cette somme !"; msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
} }
}
let message = { let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
@ -3134,6 +3181,24 @@ export class RdDActor extends Actor {
ChatMessage.create(message); 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 <strong>${gain} Deniers</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés de votre argent.`
});
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async monnaieIncDec(id, value) { async monnaieIncDec(id, value) {
let monnaie = this.getMonnaie(id); let monnaie = this.getMonnaie(id);

View File

@ -1,3 +1,4 @@
import { Misc } from "./misc.js";
/** /**
* Class providing helper methods to get the list of users, and * Class providing helper methods to get the list of users, and
@ -7,31 +8,45 @@ export class ChatUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static onSocketMessage(sockmsg) { static onSocketMessage(sockmsg) {
switch (sockmsg.msg) { 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) { static onRemoveMessages(data) {
if (game.user.id == gmId) { if (game.user.isGM || game.user.id == data.gmId) {
const toDelete = game.messages.filter(it => it.data.content.includes(part)); if (data.part){
const toDelete = game.messages.filter(it => it.data.content.includes(data.part));
toDelete.forEach(it => it.delete()); toDelete.forEach(it => it.delete());
} }
if (data.messageId){
game.messages.get(data.messageId)?.delete();
}
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static removeChatMessageContaining(part) { 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) { if (game.user.isGM) {
ChatUtility.onRemoveMessages(part, game.user.id); ChatUtility.onRemoveMessages(removeMessageData);
} }
else { else {
game.socket.emit("system.foundryvtt-reve-de-dragon", { game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_delete_chat_message", data: removeMessageData });
msg: "msg_delete_chat_message", data: { }
part:part, }
gmId: gmId,
}}); 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 });
} }
} }

View File

@ -104,9 +104,9 @@ export class RdDItem extends Item {
async quantiteIncDec(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) { async quantiteIncDec(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) {
const itemData = Misc.data(this); const itemData = Misc.data(this);
const quantite = itemData.data.quantite; const quantite = Number(itemData.data.quantite ??-1);
if (quantite != undefined) { if (quantite >=0 ) {
const reste = Math.max(quantite + nombre, 0); const reste = Math.max(quantite + Number(nombre), 0);
if (reste == 0) { if (reste == 0) {
if (options.supprimerSiZero){ if (options.supprimerSiZero){
@ -150,7 +150,9 @@ export class RdDItem extends Item {
let chatData = duplicate(Misc.data(this)); let chatData = duplicate(Misc.data(this));
const properties = this[`_${chatData.type}ChatData`](); const properties = this[`_${chatData.type}ChatData`]();
chatData["properties"] = properties chatData["properties"] = properties
if (this.actor){
chatData.actor = {id: this.actor.id };
}
//Check if the posted item should have availability/pay buttons //Check if the posted item should have availability/pay buttons
chatData.hasPrice = "cout" in chatData.data; chatData.hasPrice = "cout" in chatData.data;
chatData.data.cout_deniers = 0; chatData.data.cout_deniers = 0;
@ -205,7 +207,7 @@ export class RdDItem extends Item {
if (chatData.hasPrice) { if (chatData.hasPrice) {
if (quantiteEnvoi > 0) if (quantiteEnvoi > 0)
chatData.postQuantity = Number(quantiteEnvoi); chatData.postQuantity = Number(quantiteEnvoi);
if (prixTotal > 0) { if (prixTotal >= 0) {
chatData.postPrice = prixTotal; chatData.postPrice = prixTotal;
chatData.data.cout_deniers = Math.floor(prixTotal * 100); // Mise à jour cout en deniers chatData.data.cout_deniers = Math.floor(prixTotal * 100); // Mise à jour cout en deniers
} }

View File

@ -47,11 +47,13 @@ export class Misc {
const parsed = parseInt(value); const parsed = parseInt(value);
return isNaN(parsed) ? 0 : parsed; return isNaN(parsed) ? 0 : parsed;
} }
static keepDecimals(num, decimals) { static keepDecimals(num, decimals) {
if (decimals<=0 || decimals>6) return num; if (decimals<=0 || decimals>6) return num;
const decimal = Math.pow(10, parseInt(decimals)); const decimal = Math.pow(10, parseInt(decimals));
return Math.round(num * decimal) / decimal; return Math.round(num * decimal) / decimal;
} }
static getFractionHtml(diviseur) { static getFractionHtml(diviseur) {
if (!diviseur || diviseur <= 1) return undefined; if (!diviseur || diviseur <= 1) return undefined;
switch (diviseur || 1) { switch (diviseur || 1) {
@ -111,4 +113,8 @@ export class Misc {
static templateData(it) { static templateData(it) {
return Misc.data(it)?.data ?? {} return Misc.data(it)?.data ?? {}
} }
static connectedGM() {
return game.user.isGM ? game.user.id : game.users.entities.find(u => u.isGM && u.active)?.id;
}
} }

View File

@ -1211,7 +1211,7 @@ export class RdDCombat {
attackerId: this.attackerId, attackerId: this.attackerId,
defenderTokenId: defenderTokenId, defenderTokenId: defenderTokenId,
attackerRoll: attackerRoll, attackerRoll: attackerRoll,
gmId: game.users.entities.find(u => u.isGM)?.id, gmId: Misc.connectedGM(),
} }
}); });
} }

View File

@ -140,10 +140,11 @@ Hooks.once("init", async function () {
}; };
/* -------------------------------------------- */ /* -------------------------------------------- */
game.socket.on("system.foundryvtt-reve-de-dragon", data => { game.socket.on("system.foundryvtt-reve-de-dragon", sockmsg => {
RdDUtility.onSocketMesssage(data); RdDUtility.onSocketMesssage(sockmsg);
RdDCombat.onSocketMessage(data); RdDCombat.onSocketMessage(sockmsg);
ChatUtility.onSocketMessage(data); ChatUtility.onSocketMessage(sockmsg);
RdDActor.onSocketMessage(sockmsg);
}); });
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -529,25 +529,62 @@ export class RdDUtility {
}); });
// Gestion du bouton payer // Gestion du bouton payer
html.on("click", '.payer-button', event => { html.on("click", '.payer-button', event => {
let sumdenier = event.currentTarget.attributes['data-somme-denier'].value; let sumdenier = event.currentTarget.attributes['data-somme-denier']?.value ?? 0;
let quantite = 1; let quantite = event.currentTarget.attributes['data-quantite']?.value ?? 1;
if (event.currentTarget.attributes['data-quantite']) { let fromActorId = event.currentTarget.attributes['data-actor-id']?.value;
quantite = event.currentTarget.attributes['data-quantite'].value;
}
let jsondata = event.currentTarget.attributes['data-jsondata'] let jsondata = event.currentTarget.attributes['data-jsondata']
let objData let objData
if (jsondata) { if (jsondata) {
objData = JSON.parse(jsondata.value) objData = JSON.parse(jsondata.value)
} }
if (game.user.character) { let actor = RdDUtility.getSelectedActor("Pour effectuer le paiement:");
game.user.character.payerDenier(sumdenier, objData, quantite); if (actor) {
} else { actor.depenserDeniers(sumdenier, objData, quantite, fromActorId);
let msgPayer = "Vous devez avoir un acteur relié pour effectuer le paiement"; // TODO: diminuer la quantité ou supprimer le message
ChatMessage.create({ content: msgPayer, whisper: [game.user] }); // 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 += "<br>le token sélectionné doit être lié à un personnage";
}
if (game.user.character) {
return game.user.character;
}
msgPlayer += "<br>vous pouvez sélectionner un seul token lié à un personnage";
msgPlayer += "<br>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) { static createMonnaie(name, valeur_deniers, img = "", enc = 0.01) {
let piece = { let piece = {

View File

@ -15,20 +15,20 @@
<b>Quantité: </b> <span class="postQuantity">{{postQuantity}}</span> <b>Quantité: </b> <span class="postQuantity">{{postQuantity}}</span>
{{/if}} {{/if}}
{{#if postPrice}} {{#if postPrice}}
<b>Prix: </b> <span class="postPrice">{{postPrice}} Sols</span><br> <b>Prix: </b> <span class="postPrice">{{postPrice}} Sols</span>
{{/if}} {{/if}}
</span> </span><br>
{{/if}} {{/if}}
{{#if finalPrice}}
<span> <span>
<b>Prix Total: </b> <span class="postPrice">{{finalPrice}} Sols</span><br> <b>Prix Total: </b> <span class="postPrice">{{finalPrice}} Sols</span><br>
</span> </span>
{{/if}}
</p> </p>
{{#if hasPrice}} {{#if hasPrice}}
<span class="chat-card-button-area"> <span class="chat-card-button-area">
<a class='payer-button chat-card-button market-button' data-jsondata='{{jsondata}}' <a class='payer-button chat-card-button market-button'
data-jsondata='{{jsondata}}'
{{#if actor.id}}data-actor-id='{{actor.id}}'{{/if}}
data-somme-denier="{{data.cout_deniers_total}}" data-quantite="{{data.quantite}}">Payer</a> data-somme-denier="{{data.cout_deniers_total}}" data-quantite="{{data.quantite}}">Payer</a>
</span> </span>
{{/if}} {{/if}}