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));
}
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,48 +3110,61 @@ 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é <strong>${sumDenier} Deniers</strong>, 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 += `<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;
await this.createEmbeddedDocuments('Item', [dataObj.payload]);
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 {
msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
}
}
let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
@ -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 <strong>${gain} Deniers</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés de votre argent.`
});
}
}
/* -------------------------------------------- */
async monnaieIncDec(id, value) {
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
@ -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));
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 });
}
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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(),
}
});
}

View File

@ -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);
});
/* -------------------------------------------- */

View File

@ -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 += `<option value="${i}">${i}</option>`
}
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 += "<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) {
let piece = {

View File

@ -15,20 +15,20 @@
<b>Quantité: </b> <span class="postQuantity">{{postQuantity}}</span>
{{/if}}
{{#if postPrice}}
<b>Prix: </b> <span class="postPrice">{{postPrice}} Sols</span><br>
<b>Prix: </b> <span class="postPrice">{{postPrice}} Sols</span>
{{/if}}
</span>
</span><br>
{{/if}}
{{#if finalPrice}}
<span>
<b>Prix Total: </b> <span class="postPrice">{{finalPrice}} Sols</span><br>
</span>
{{/if}}
</p>
{{#if hasPrice}}
<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>
</span>
{{/if}}