Ajout des "boutiques"
Une boutique est un Item service permettant de définir l'inventaire en vente, et de le vendre facilement. Les boutiques peuvent être accédées par les joueurs (avec le lien) pour y faire leurs courses.
This commit is contained in:
parent
f397c82c6d
commit
7c70e944b1
BIN
icons/items/services.webp
Normal file
BIN
icons/items/services.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
@ -27,6 +27,7 @@
|
||||
"TypeArmure": "Armure",
|
||||
"TypeConteneur": "Conteneur",
|
||||
"TypeNourritureboisson": "Nourriture & boisson",
|
||||
"TypeService": "Services/Boutique",
|
||||
"TypeChant": "Chant",
|
||||
"TypeDanse": "Danse",
|
||||
"TypeMusique": "Musique",
|
||||
|
@ -188,17 +188,17 @@ export class RdDActor extends Actor {
|
||||
|
||||
canReceive(item) {
|
||||
if (this.isCreature()) {
|
||||
return item.type == 'competencecreature' || RdDItem.isItemInventaire(item);
|
||||
return item.type == 'competencecreature' || item.isInventaire();
|
||||
}
|
||||
if (this.isEntite()) {
|
||||
return item.type == 'competencecreature';
|
||||
}
|
||||
if (this.isVehicule()) {
|
||||
return RdDItem.isItemInventaire(item);
|
||||
return item.isInventaire();
|
||||
}
|
||||
if (this.isPersonnage()) {
|
||||
switch (item.type) {
|
||||
case 'competencecreature': case 'tarot':
|
||||
case 'competencecreature': case 'tarot': case 'service':
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -3770,35 +3770,32 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
|
||||
const cout = Number(achat.prixTotal ?? 0);
|
||||
const vente = achat.vente;
|
||||
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
|
||||
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
|
||||
let itemVendu = vendeur?.getObjet(vente.item._id);
|
||||
|
||||
if (vendeur && (itemVendu?.getQuantite() ?? 0) < achat.quantiteTotal) {
|
||||
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a plus assez de ${vente.item.name} !`);
|
||||
return;
|
||||
const service = achat.serviceId ? (vendeur?.getObjet(achat.serviceId) ?? game.items.get(achat.serviceId)) : undefined;
|
||||
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
|
||||
const vente = achat.vente;
|
||||
const quantite = (achat.choix.nombreLots ?? 1) * (vente.tailleLot);
|
||||
const itemVendu = vendeur?.getObjet(vente.item._id) ?? (await RdDItem.getCorrespondingItem(vente.item));
|
||||
if (!this.verifierQuantite(service, vendeur, itemVendu, quantite)) {
|
||||
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
|
||||
return
|
||||
}
|
||||
|
||||
if (Monnaie.getFortune(acheteur) < Number(cout)) {
|
||||
ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
|
||||
return;
|
||||
}
|
||||
|
||||
achat.quantiteTotal = (achat.choix.nombreLots ?? 1) * (vente.tailleLot);
|
||||
if (vendeur) {
|
||||
await vendeur.ajouterSols(cout);
|
||||
await vendeur.decrementerQuantiteItem(itemVendu, achat.quantiteTotal,);
|
||||
}
|
||||
await this.decrementerVente(service, vendeur, itemVendu, quantite, cout);
|
||||
if (acheteur) {
|
||||
await acheteur.depenserSols(cout);
|
||||
let createdItemId = await acheteur.creerQuantiteItem(vente.item, achat.quantiteTotal);
|
||||
let createdItemId = await acheteur.creerQuantiteItem(vente.item, quantite);
|
||||
await acheteur.consommerNourritureAchetee(achat, vente, createdItemId);
|
||||
}
|
||||
if (cout > 0) {
|
||||
RdDAudio.PlayContextAudio("argent");
|
||||
}
|
||||
const chatAchatItem = duplicate(vente);
|
||||
chatAchatItem.quantiteTotal = achat.quantiteTotal;
|
||||
chatAchatItem.quantiteTotal = quantite;
|
||||
ChatMessage.create({
|
||||
user: achat.userId,
|
||||
speaker: { alias: (acheteur ?? vendeur).name },
|
||||
@ -3810,8 +3807,8 @@ export class RdDActor extends Actor {
|
||||
if (vente.quantiteNbLots <= achat.choix.nombreLots) {
|
||||
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
|
||||
}
|
||||
else {
|
||||
vente["properties"] = new RdDItem(vente.item).getProprietes();
|
||||
else if (!service) {
|
||||
vente["properties"] = itemVendu.getProprietes();
|
||||
vente.quantiteNbLots -= achat.choix.nombreLots;
|
||||
vente.jsondata = JSON.stringify(vente.item);
|
||||
const messageVente = game.messages.get(achat.chatMessageIdVente);
|
||||
@ -3821,6 +3818,21 @@ export class RdDActor extends Actor {
|
||||
}
|
||||
}
|
||||
|
||||
async decrementerVente(service, vendeur, itemVendu, quantite, cout) {
|
||||
if (service) {
|
||||
await service.venteRefItem(itemVendu, quantite, cout)
|
||||
}
|
||||
else if (vendeur) {
|
||||
await vendeur.ajouterSols(cout);
|
||||
await vendeur.decrementerQuantiteItem(itemVendu, quantite);
|
||||
}
|
||||
}
|
||||
|
||||
verifierQuantite(service, vendeur, item, quantiteTotal) {
|
||||
const disponible = service ? service.getQuantiteDisponible(item, quantiteTotal) : (vendeur ? (item?.getQuantite() ?? 0) : quantiteTotal);
|
||||
return disponible >= quantiteTotal;
|
||||
}
|
||||
|
||||
async consommerNourritureAchetee(achat, vente, createdItemId) {
|
||||
if (achat.choix.consommer && vente.item.type == 'nourritureboisson' && createdItemId != undefined) {
|
||||
achat.choix.doses = achat.choix.nombreLots;
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Misc } from "./misc.js";
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
|
||||
export class DialogItemAchat extends Dialog {
|
||||
|
||||
static venteData(button) {
|
||||
const vendeurId = button.attributes['data-vendeurId']?.value;
|
||||
static preparerAchat(chatButton) {
|
||||
const vendeurId = chatButton.attributes['data-vendeurId']?.value;
|
||||
const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined;
|
||||
const acheteur = RdDUtility.getSelectedActor();
|
||||
const json = button.attributes['data-jsondata']?.value;
|
||||
const json = chatButton.attributes['data-jsondata']?.value;
|
||||
if (!acheteur && !vendeur) {
|
||||
ui.notifications.info("Pas d'acheteur ni de vendeur, aucun changement");
|
||||
return undefined;
|
||||
@ -16,46 +17,68 @@ export class DialogItemAchat extends Dialog {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const prixLot = Number(button.attributes['data-prixLot']?.value ?? 0);
|
||||
return {
|
||||
item: json ? JSON.parse(json) : undefined,
|
||||
actingUserId: game.user.id,
|
||||
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)
|
||||
item: (json ? JSON.parse(json) : undefined),
|
||||
vendeur,
|
||||
acheteur,
|
||||
nbLots: parseInt(chatButton.attributes['data-quantiteNbLots']?.value),
|
||||
tailleLot: parseInt(chatButton.attributes['data-tailleLot']?.value ?? 1),
|
||||
prixLot: Number(chatButton.attributes['data-prixLot']?.value ?? 0),
|
||||
quantiteIllimite: chatButton.attributes['data-quantiteIllimite']?.value == 'true',
|
||||
chatMessageIdVente: RdDUtility.findChatMessageId(chatButton),
|
||||
};
|
||||
}
|
||||
|
||||
static async onAcheter(venteData) {
|
||||
|
||||
static async onAcheter({ item, vendeur, acheteur, service, tailleLot, prixLot, nbLots, quantiteIllimite, chatMessageIdVente }) {
|
||||
const venteData = {
|
||||
item,
|
||||
actingUserId: game.user.id,
|
||||
vendeurId: vendeur?.id,
|
||||
vendeur,
|
||||
acheteur,
|
||||
service,
|
||||
tailleLot,
|
||||
quantiteIllimite,
|
||||
quantiteNbLots: nbLots,
|
||||
choix: { seForcer: false, supprimerSiZero: true },
|
||||
prixLot,
|
||||
isVente: prixLot > 0,
|
||||
isConsommable: item.type == 'nourritureboisson' && acheteur?.isPersonnage(),
|
||||
chatMessageIdVente
|
||||
};
|
||||
|
||||
DialogItemAchat.changeNombreLots(venteData, 1);
|
||||
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData);
|
||||
new DialogItemAchat(html, venteData).render(true);
|
||||
}
|
||||
|
||||
static changeNombreLots(venteData, nombreLots) {
|
||||
venteData.choix.nombreLots = nombreLots;
|
||||
venteData.prixTotal = (nombreLots * venteData.prixLot).toFixed(2);
|
||||
if (venteData.isConsommable) {
|
||||
const doses = nombreLots * venteData.tailleLot;
|
||||
venteData.totalSust = Misc.keepDecimals(doses * (venteData.item.system.sust ?? 0), 2);
|
||||
venteData.totalDesaltere = venteData.item.system.boisson
|
||||
? Misc.keepDecimals(doses * (venteData.item.system.desaltere ?? 0), 2)
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(html, venteData) {
|
||||
const isConsommable = venteData.item.type == 'nourritureboisson' && venteData.acheteur?.isPersonnage();
|
||||
let options = { classes: ["dialogachat"], width: 400, height: 'fit-content', 'z-index': 99999 };
|
||||
|
||||
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
|
||||
const buttons = {};
|
||||
if (isConsommable) {
|
||||
if (venteData.isConsommable) {
|
||||
buttons["consommer"] = { label: venteData.item.system.boisson ? "Boire" : "Manger", callback: it => this.onAchatConsommer() }
|
||||
}
|
||||
buttons[actionAchat] = { label: actionAchat, callback: it => { this.onAchat(); } };
|
||||
buttons["decliner"] = { label: "Décliner", callback: it => { } };
|
||||
const acheteur = venteData.acheteur?.name ?? 'Un acheteur';
|
||||
const vendeur = (venteData.service ?? venteData.vendeur)?.name ?? 'Un vendeur';
|
||||
let conf = {
|
||||
title: venteData.acheteur ? venteData.acheteur.name + " - " + actionAchat : actionAchat,
|
||||
title: `${acheteur} - ${actionAchat} à ${vendeur}`,
|
||||
content: html,
|
||||
default: actionAchat,
|
||||
buttons: buttons
|
||||
@ -69,6 +92,7 @@ export class DialogItemAchat extends Dialog {
|
||||
await this.html.find(".nombreLots").change();
|
||||
(this.venteData.vendeur ?? this.venteData.acheteur).achatVente({
|
||||
userId: game.user.id,
|
||||
serviceId: this.venteData.service?.id,
|
||||
vendeurId: this.venteData.vendeur?.id,
|
||||
acheteurId: this.venteData.acheteur?.id,
|
||||
prixTotal: this.venteData.prixTotal,
|
||||
@ -96,13 +120,21 @@ export class DialogItemAchat extends Dialog {
|
||||
}
|
||||
|
||||
setNombreLots(nombreLots) {
|
||||
if (nombreLots > this.venteData.quantiteNbLots) {
|
||||
|
||||
if (!this.venteData.quantiteIllimite) {
|
||||
if (!this.venteData.quantiteIllimite && nombreLots > this.venteData.quantiteNbLots) {
|
||||
ui.notifications.warn(`Seulement ${this.venteData.quantiteNbLots} lots disponibles, vous ne pouvez pas en prendre ${nombreLots}`)
|
||||
}
|
||||
this.venteData.choix.nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
|
||||
this.venteData.prixTotal = (nombreLots * this.venteData.prixLot).toFixed(2);
|
||||
this.html.find(".nombreLots").val(this.venteData.choix.nombreLots);
|
||||
this.html.find(".prixTotal").text(this.venteData.prixTotal);
|
||||
nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
|
||||
}
|
||||
|
||||
DialogItemAchat.changeNombreLots(this.venteData, nombreLots);
|
||||
|
||||
this.html.find(".nombreLots").val(nombreLots);
|
||||
this.html.find(".prixTotal").text(this.venteData.prixTotal);
|
||||
this.html.find("span.total-sust").text(this.venteData.totalSust);
|
||||
this.html.find("span.total-desaltere").text(this.venteData.totalDesaltere);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -2,12 +2,15 @@ import { HtmlUtility } from "./html-utility.js";
|
||||
|
||||
export class DialogItemVente extends Dialog {
|
||||
|
||||
static async display(item, callback) {
|
||||
const quantite = item.isConteneur() ? 1 : item.system.quantite;
|
||||
static async display({ item, callback, service = undefined, quantiteMax = undefined }) {
|
||||
const quantite = quantiteMax ?? item.getQuantite();
|
||||
const isOwned = item.isOwned;
|
||||
// const isOwned = item.isOwned || service?.actor;
|
||||
const venteData = {
|
||||
item: item,
|
||||
alias: item.actor?.name ?? game.user.name,
|
||||
vendeurId: item.actor?.id,
|
||||
alias: item.actor?.name ?? service?.name ?? game.user.name,
|
||||
serviceId: service?.id,
|
||||
vendeurId: item.actor?.id ?? service?.actor?.id,
|
||||
prixOrigine: item.system.cout,
|
||||
prixUnitaire: item.system.cout,
|
||||
prixLot: item.system.cout,
|
||||
@ -15,8 +18,8 @@ export class DialogItemVente extends Dialog {
|
||||
quantiteNbLots: quantite,
|
||||
quantiteMaxLots: quantite,
|
||||
quantiteMax: quantite,
|
||||
quantiteIllimite: !item.isOwned,
|
||||
isOwned: item.isOwned,
|
||||
quantiteIllimite: service? service.system.illimite : !isOwned,
|
||||
isOwned: isOwned,
|
||||
};
|
||||
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.html`, venteData);
|
||||
return new DialogItemVente(venteData, html, callback).render(true);
|
||||
@ -68,14 +71,12 @@ export class DialogItemVente extends Dialog {
|
||||
this.html.find(".prixLot").val(this.venteData.prixLot);
|
||||
}
|
||||
this.venteData.tailleLot = tailleLot;
|
||||
if (this.venteData.isOwned) {
|
||||
// recalculer le nombre de lots max
|
||||
this.venteData.quantiteMaxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
|
||||
this.venteData.quantiteNbLots = Math.min(this.venteData.quantiteMaxLots, this.venteData.quantiteNbLots);
|
||||
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
|
||||
this.html.find(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
|
||||
}
|
||||
}
|
||||
|
||||
setNbLots(nbLots) {
|
||||
if (this.venteData.isOwned) {
|
||||
|
81
module/item-service-sheet.js
Normal file
81
module/item-service-sheet.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { RdDItemSheet } from "./item-sheet.js";
|
||||
import { Misc } from "./misc.js";
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
import { SystemCompendiums } from "./settings/system-compendiums.js";
|
||||
import { DialogItemAchat } from "./dialog-item-achat.js";
|
||||
import { RdDItem } from "./item.js";
|
||||
import { RdDItemService } from "./item-service.js";
|
||||
|
||||
export class RdDServiceItemSheet extends RdDItemSheet {
|
||||
|
||||
static get ITEM_TYPE() { return "service" };
|
||||
|
||||
async getData() {
|
||||
const formData = await super.getData();
|
||||
formData.disabled = formData.isGM || formData.isOwned ? '' : 'disabled';
|
||||
return formData;
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
this.html.find('a.rdd-world-content-link').click(async event => {
|
||||
const itemRef = this.getItemRef(event);
|
||||
game.items.get(itemRef.id)?.sheet.render(true)
|
||||
});
|
||||
|
||||
this.html.find('a.sub-item-acheter').click(async event => {
|
||||
const subItem = this.item.findRefItem(this.getItemRef(event));
|
||||
await this.item.acheter(RdDUtility.getSelectedActor(), subItem);
|
||||
});
|
||||
|
||||
if (!this.options.editable) return;
|
||||
|
||||
this.html.find('a.sub-item-vendre').click(async event => {
|
||||
const subItem = this.item.findRefItem(this.getItemRef(event));
|
||||
await this.item.vendre(subItem);
|
||||
});
|
||||
|
||||
this.html.find('a.sub-item-delete').click(async event => {
|
||||
await this.item.removeRefItem(this.getItemRef(event));
|
||||
});
|
||||
|
||||
this.html.find('a.sub-item-quantite-moins').click(async event => await this.item.increaseRefItemQuantite(this.getItemRef(event), -1))
|
||||
this.html.find('a.sub-item-quantite-plus').click(async event => await this.item.increaseRefItemQuantite(this.getItemRef(event), 1))
|
||||
this.html.find('input.sub-item-quantite').change(async event => {
|
||||
const newQuantite = Math.max(0, Number.parseInt(this.html.find(event.currentTarget).val()));
|
||||
await this.item.updateRefItem(this.getItemRef(event), it => it.system.quantite = newQuantite);
|
||||
})
|
||||
this.html.find('input.sub-item-cout').change(async event => {
|
||||
const newCout = Math.max(0, Number(this.html.find(event.currentTarget).val()));
|
||||
await this.item.updateRefItem(this.getItemRef(event), it => it.system.cout = newCout);
|
||||
})
|
||||
this.html.find('a.sub-item-info-add').click(__ =>
|
||||
ui.notifications.info(`Utiliser le glisser-déposer pour ajouter des objets depuis un compendium ou les objets du monde`)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
async _onDropItem(event, dragData) {
|
||||
let linkedItem = fromUuidSync(dragData.uuid);
|
||||
const existing = this.item.system.items.find(it => it.pack == linkedItem.pack && it.id == linkedItem.id && it.type == linkedItem.type);
|
||||
if (existing) {
|
||||
ui.notifications.warn(`${this.item.name} contient déjà un ${existing.name}`);
|
||||
return;
|
||||
}
|
||||
if (linkedItem.pack) {
|
||||
linkedItem = await SystemCompendiums.loadDocument(linkedItem);
|
||||
}
|
||||
if (linkedItem.isInventaire()) {
|
||||
await this.item.addRefItem(RdDServiceItemSheet.createSubItem(linkedItem));
|
||||
}
|
||||
else {
|
||||
ui.notifications.warn(`${this.item.name} ne peut pas proposer à la vente de ${Misc.typeName('Item', linkedItem.type)}: ${linkedItem.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
getItemRef(event) {
|
||||
const itemRow = this.html.find(event.currentTarget)?.parents('.item.service-item');
|
||||
return { id: itemRow?.data("item-id"), pack: itemRow?.data("pack") ?? undefined }
|
||||
}
|
||||
}
|
140
module/item-service.js
Normal file
140
module/item-service.js
Normal file
@ -0,0 +1,140 @@
|
||||
import { DialogItemAchat } from "./dialog-item-achat.js";
|
||||
import { RdDItem } from "./item.js";
|
||||
import { Misc } from "./misc.js";
|
||||
|
||||
export class RdDItemService extends RdDItem {
|
||||
|
||||
static get defaultIcon() {
|
||||
return "systems/foundryvtt-reve-de-dragon/icons/items/services.webp";
|
||||
}
|
||||
|
||||
/** @override*/
|
||||
getUserLevel(user) {
|
||||
const level = super.getUserLevel(user);
|
||||
if (level == CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
||||
// si quelqu'un a accès au lien d'un service, il peut le voir
|
||||
return CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
isService() { return true; }
|
||||
getChatItemTemplate() { return 'systems/foundryvtt-reve-de-dragon/templates/post-item-service.html'; }
|
||||
getProprietes() { return []; }
|
||||
|
||||
getServiceItem(itemRef) {
|
||||
if (this.isService()) {
|
||||
return this.system.items.find(it => it.id == itemRef.id && it.pack == itemRef.pack);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getQuantiteDisponible(itemRef, max) {
|
||||
if (this.system.illimite) {
|
||||
return max;
|
||||
}
|
||||
const subItem = this.getServiceItem(itemRef);
|
||||
return subItem?.system.quantite ?? 0;
|
||||
}
|
||||
|
||||
async venteRefItem(ref, quantite, cout) {
|
||||
if (this.actor) {
|
||||
await this.actor.ajouterSols(cout);
|
||||
}
|
||||
await this.increaseRefItemQuantite(ref, -quantite);
|
||||
}
|
||||
|
||||
async vendre(subItem) {
|
||||
const item = await RdDItem.getCorrespondingItem(subItem);
|
||||
const quantiteMax = this.system.illimite ? undefined : subItem.system.quantite;
|
||||
await item.proposerVente({ service: this, quantiteMax });
|
||||
}
|
||||
|
||||
async acheter(acheteur, subItem) {
|
||||
if (!acheteur) {
|
||||
ui.notifications.warn(`Pas d'acheteur sélectionné`);
|
||||
return;
|
||||
}
|
||||
const nbLots = this.system.illimite ? 1 : subItem.system.quantite;
|
||||
if (nbLots <= 0) {
|
||||
ui.notifications.warn(`${this.name} n'a plus de ${subItem.name} en vente`);
|
||||
return;
|
||||
}
|
||||
|
||||
await DialogItemAchat.onAcheter({
|
||||
item: await RdDItem.getCorrespondingItem(subItem),
|
||||
acheteur,
|
||||
service: this,
|
||||
quantiteIllimite: this.system.illimite,
|
||||
nbLots,
|
||||
tailleLot: 1,
|
||||
prixLot: subItem.system.cout
|
||||
});
|
||||
}
|
||||
|
||||
static createSubItem(linkedItem) {
|
||||
return {
|
||||
id: linkedItem.id,
|
||||
pack: linkedItem.pack,
|
||||
name: linkedItem.name,
|
||||
img: linkedItem.img,
|
||||
system: {
|
||||
quantite: 1,
|
||||
cout: linkedItem.system.cout ?? 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static matchRefItem({ id, pack }) {
|
||||
return it => it.id == id && (pack ? (it.pack == pack) : (!it.pack));
|
||||
}
|
||||
|
||||
findRefItem(ref) {
|
||||
return this.system.items.find(RdDItemService.matchRefItem(ref));
|
||||
}
|
||||
|
||||
async increaseRefItemQuantite(ref, quantite) {
|
||||
await this.updateRefItem(ref,
|
||||
it => it.system.quantite = Math.max(0, it.system.quantite + quantite)
|
||||
);
|
||||
}
|
||||
|
||||
async updateRefItem(ref, update = it => { }) {
|
||||
await this.updateRefItems(RdDItemService.matchRefItem(ref), update);
|
||||
}
|
||||
|
||||
async addRefItem(newItem) {
|
||||
if (!newItem.id) {
|
||||
ui.notifications.warn(`${newItem?.name ?? '??'} n'a pas d'identifiant`);
|
||||
return;
|
||||
}
|
||||
if (this.system.items.find(RdDItemService.matchRefItem(newItem))) {
|
||||
ui.notifications.warn(`${newItem?.name ?? newItem.id} est déjà présent ici`);
|
||||
return;
|
||||
}
|
||||
await this.setRefItems([...this.system.items, newItem]);
|
||||
}
|
||||
|
||||
async removeRefItem(ref) {
|
||||
await this.removeRefItems(RdDItemService.matchRefItem(ref));
|
||||
}
|
||||
|
||||
async removeRefItems(matcher = it => false) {
|
||||
await this.setRefItems(this.system.items.filter(it => !matcher(it)));
|
||||
}
|
||||
|
||||
async updateRefItems(matcher = it => false, update = it => { }) {
|
||||
const updatedList = this.system.items.map(it => {
|
||||
if (matcher(it)) {
|
||||
update(it);
|
||||
}
|
||||
return it;
|
||||
});
|
||||
await this.setRefItems(updatedList);
|
||||
}
|
||||
|
||||
async setRefItems(newItems) {
|
||||
await this.update({ 'system.items': newItems.sort(Misc.ascending(it => it.type + ':' + it.name)) });
|
||||
}
|
||||
|
||||
}
|
@ -58,6 +58,7 @@ export const defaultItemImg = {
|
||||
poison: "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/venin.webp",
|
||||
oeuvre: "systems/foundryvtt-reve-de-dragon/icons/competence_comedie.webp",
|
||||
nourritureboisson: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp",
|
||||
service: "systems/foundryvtt-reve-de-dragon/icons/items/services.webp",
|
||||
signedraconique: "systems/foundryvtt-reve-de-dragon/icons/tmr/signe_draconique.webp",
|
||||
gemme: "systems/foundryvtt-reve-de-dragon/icons/gemmes/almaze.webp",
|
||||
possession: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
|
||||
@ -73,10 +74,6 @@ export class RdDItem extends Item {
|
||||
return game.system.rdd.itemClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
|
||||
}
|
||||
|
||||
static isItemInventaire(newLocal) {
|
||||
return typesObjetsInventaire.includes(newLocal.type);
|
||||
}
|
||||
|
||||
static isFieldInventaireModifiable(type, field) {
|
||||
switch (field) {
|
||||
case 'quantite':
|
||||
@ -154,7 +151,7 @@ export class RdDItem extends Item {
|
||||
return typesObjetsCompetence.includes(this.type)
|
||||
}
|
||||
isInventaire() {
|
||||
return RdDItem.isItemInventaire(this)
|
||||
return typesObjetsInventaire.includes(this.type);
|
||||
}
|
||||
isOeuvre() {
|
||||
return typesObjetsOeuvres.includes(this.type)
|
||||
@ -222,7 +219,7 @@ export class RdDItem extends Item {
|
||||
}
|
||||
|
||||
getQuantite() {
|
||||
return Math.round(this.isConteneur() ? 1 : (this.system.quantite ?? 0))
|
||||
return Math.round(this.system.quantite ?? 0)
|
||||
}
|
||||
|
||||
getEncTotal() {
|
||||
@ -423,13 +420,17 @@ export class RdDItem extends Item {
|
||||
return [true, undefined];
|
||||
}
|
||||
|
||||
async proposerVente() {
|
||||
async proposerVente({ service = undefined, quantiteMax = undefined }) {
|
||||
console.log(this);
|
||||
if (this.isConteneurNonVide()) {
|
||||
ui.notifications.warn(`Votre ${this.name} n'est pas vide, pas possible de le proposer`);
|
||||
return;
|
||||
}
|
||||
await DialogItemVente.display(this, async (vente) => {
|
||||
await DialogItemVente.display({
|
||||
item: this,
|
||||
service,
|
||||
quantiteMax,
|
||||
callback: async (vente) => {
|
||||
vente["properties"] = this.getProprietes();
|
||||
if (vente.isOwned) {
|
||||
if (vente.quantiteNbLots * vente.tailleLot > vente.quantiteMax) {
|
||||
@ -439,16 +440,19 @@ export class RdDItem extends Item {
|
||||
}
|
||||
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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getProprietes() {
|
||||
if (this[`_${this.type}ChatData`]) {
|
||||
return this[`_${this.type}ChatData`]().filter(it => it != undefined);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async postItem(modeOverride) {
|
||||
@ -465,12 +469,19 @@ export class RdDItem extends Item {
|
||||
payload: chatData,
|
||||
});
|
||||
|
||||
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-item.html', chatData).then(html => {
|
||||
renderTemplate(this.getChatItemTemplate(), chatData).then(html => {
|
||||
let chatOptions = RdDUtility.chatDataSetup(html, modeOverride);
|
||||
ChatMessage.create(chatOptions)
|
||||
});
|
||||
}
|
||||
|
||||
getChatItemTemplate() {
|
||||
switch (this.type) {
|
||||
case 'service': return 'systems/foundryvtt-reve-de-dragon/templates/post-item-service.html';
|
||||
}
|
||||
return 'systems/foundryvtt-reve-de-dragon/templates/post-item.html';
|
||||
}
|
||||
|
||||
static propertyIfDefined(name, val, condition = true) {
|
||||
return condition ? `<b>${name}</b>: ${val}` : undefined;
|
||||
}
|
||||
@ -719,5 +730,4 @@ export class RdDItem extends Item {
|
||||
...this._inventaireTemplateChatData()
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ import { Environnement } from "./environnement.js";
|
||||
import { RdDIngredientItemSheet } from "./item-ingredient-sheet.js";
|
||||
import { RdDFauneItemSheet } from "./item-faune-sheet.js";
|
||||
import { RdDConteneurItemSheet } from "./item-conteneur-sheet.js";
|
||||
import { RdDServiceItemSheet } from "./item-service-sheet.js";
|
||||
import { RdDItemService } from "./item-service.js";
|
||||
|
||||
/**
|
||||
* RdD system
|
||||
@ -52,6 +54,7 @@ export class SystemReveDeDragon {
|
||||
this.RdDUtility = RdDUtility;
|
||||
this.RdDHotbar = RdDHotbar;
|
||||
this.itemClasses = {
|
||||
service: RdDItemService
|
||||
}
|
||||
this.actorClasses = {
|
||||
}
|
||||
@ -190,6 +193,7 @@ export class SystemReveDeDragon {
|
||||
RdDItemSheet.register(RdDHerbeItemSheet);
|
||||
RdDItemSheet.register(RdDFauneItemSheet);
|
||||
RdDItemSheet.register(RdDIngredientItemSheet);
|
||||
RdDItemSheet.register(RdDServiceItemSheet);
|
||||
|
||||
Items.registerSheet(SYSTEM_RDD, RdDItemSheet, {
|
||||
types: [
|
||||
|
@ -203,6 +203,7 @@ export class RdDUtility {
|
||||
'systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/item-possession-sheet.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/item-extraitpoetique-sheet.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/item-service-sheet.html',
|
||||
// partial enums
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-caracteristiques.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
|
||||
@ -431,6 +432,7 @@ export class RdDUtility {
|
||||
formData.oeuvres = this.arrayOrEmpty(itemTypes['oeuvre']);
|
||||
formData.jeux = this.arrayOrEmpty(itemTypes['jeu']);
|
||||
|
||||
formData.services = this.arrayOrEmpty(itemTypes['service']);
|
||||
formData.recettescuisine = this.arrayOrEmpty(itemTypes['recettecuisine']);
|
||||
formData.recettesAlchimiques = this.arrayOrEmpty(itemTypes['recettealchimique']);
|
||||
formData.maladies = this.arrayOrEmpty(itemTypes['maladie']);
|
||||
@ -814,7 +816,7 @@ export class RdDUtility {
|
||||
|
||||
// gestion bouton tchat Acheter
|
||||
html.on("click", '.button-acheter', event => {
|
||||
const venteData = DialogItemAchat.venteData(event.currentTarget);
|
||||
const venteData = DialogItemAchat.preparerAchat(event.currentTarget);
|
||||
if (venteData) {
|
||||
DialogItemAchat.onAcheter(venteData);
|
||||
}
|
||||
@ -830,6 +832,10 @@ export class RdDUtility {
|
||||
ChatUtility.removeChatMessageId(RdDUtility.findChatMessageId(event.currentTarget));
|
||||
}
|
||||
});
|
||||
html.on("click", '.rdd-world-content-link', async event => {
|
||||
const itemId = html.find(event.currentTarget)?.data("id");
|
||||
game.items.get(itemId)?.sheet.render(true)
|
||||
});
|
||||
}
|
||||
|
||||
static findChatMessageId(current) {
|
||||
|
@ -599,7 +599,7 @@ input:is(.blessure-premiers_soins, .blessure-soins_complets) {
|
||||
.editor {
|
||||
border: 2;
|
||||
height: fit-content;
|
||||
min-height: 8rem;
|
||||
min-height: 5rem;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
@ -611,7 +611,7 @@ input:is(.blessure-premiers_soins, .blessure-soins_complets) {
|
||||
|
||||
.small-editor {
|
||||
border: 2;
|
||||
min-height: 4rem;
|
||||
min-height: 2rem;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
|
@ -562,6 +562,7 @@
|
||||
"recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre",
|
||||
"objet", "arme", "armure", "conteneur", "herbe", "ingredient", "faune", "livre", "potion", "munition",
|
||||
"monnaie", "nourritureboisson", "gemme",
|
||||
"service",
|
||||
"meditation", "rencontre", "queue", "ombre", "souffle", "tete", "casetmr", "signedraconique", "sort", "sortreserve",
|
||||
"nombreastral", "tache", "maladie", "poison", "possession",
|
||||
"tarot", "extraitpoetique"
|
||||
@ -741,6 +742,12 @@
|
||||
"prpermanent": false,
|
||||
"prdate": 0
|
||||
},
|
||||
"service": {
|
||||
"templates": [ "description"],
|
||||
"illimite": false,
|
||||
"items": [],
|
||||
"services": []
|
||||
},
|
||||
"musique": {
|
||||
"templates": [ "description" ],
|
||||
"niveau": "",
|
||||
|
@ -2,15 +2,17 @@
|
||||
<div>
|
||||
<div class="flexrow flex-center">
|
||||
<div>
|
||||
{{#if vendeur}}
|
||||
{{#if service}}
|
||||
<img class="chat-icon" src="{{service.img}}" title="{{service.name}}" alt="{{service.name}}" />
|
||||
{{else if vendeur}}
|
||||
<img class="chat-icon" src="{{vendeur.img}}" title="{{vendeur.name}}" alt="{{vendeur.name}}" />
|
||||
{{else}}
|
||||
<img class="chat-icon" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon_echoppe.webp" title="Un commerçant" alt="Vendeur MJ" />
|
||||
{{/if}}
|
||||
</div>
|
||||
<div><i class="fas fa-sign-out-alt"></i></div>
|
||||
<div><i class="fa-solid fa-arrow-right-long"></i></div>
|
||||
<div><img class="chat-icon" src="{{item.img}}" title="{{item.name}}" alt="{{item.name}}" /></div>
|
||||
<div><i class="fas fa-sign-in-alt"></i></div>
|
||||
<div><i class="fa-solid fa-arrow-right-long"></i></div>
|
||||
<div>
|
||||
{{#if acheteur}}
|
||||
<img class="chat-icon" src="{{acheteur.img}}" title="{{acheteur.name}}" alt="{{acheteur.name}}" />
|
||||
@ -40,8 +42,10 @@
|
||||
{{else}}Quantité{{/if}}
|
||||
</label>
|
||||
<div class="flexrow">
|
||||
<input name="nombreLots" class="nombreLots flex-shrink number-x2" type="number" min="1" max="{{quantiteNbLots}}"
|
||||
value="{{choix.nombreLots}}" data-dtype="Number" />
|
||||
<input name="nombreLots" class="nombreLots flex-shrink number-x2" type="number" min="1"
|
||||
{{#unless quantiteIllimite}} max="{{quantiteNbLots}}" {{/unless}}
|
||||
value="{{choix.nombreLots}}"
|
||||
data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -50,8 +54,7 @@
|
||||
|
||||
{{#if item.system.sust}}
|
||||
<p>Cette {{#if item.system.boisson}}boisson{{else}}nourriture{{/if}} vous apportera
|
||||
<span class="total-sust">{{totalSust}}</span>
|
||||
de sustantation.</p>
|
||||
<span class="total-sust">{{totalSust}}</span> de sustantation.</p>
|
||||
{{/if}}
|
||||
{{#if item.system.boisson}}
|
||||
<p>
|
||||
|
103
templates/item-service-sheet.html
Normal file
103
templates/item-service-sheet.html
Normal file
@ -0,0 +1,103 @@
|
||||
<form class="{{cssClass}}" autocomplete="off">
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/header-item.html"}}
|
||||
|
||||
<section class="sheet-body">
|
||||
<div class="flexcol form-group small-editor">
|
||||
{{editor description target="system.description" button=true owner=owner editable=(or isGM isOwner) engine="prosemirror"}}
|
||||
</div>
|
||||
{{!--
|
||||
<div class="flexcol">
|
||||
<ul class="item-list alterne-list">
|
||||
<li class="item flexrow list-item">
|
||||
<label class="flex-grow">Service</label>
|
||||
<label>Moral</label>
|
||||
<label>Qualité</label>
|
||||
<label>Prix (sols)</label>
|
||||
<label>
|
||||
{{#unless disabled}}
|
||||
<a class="service-add"><i class="fas fa-plus-circle"></i></a>
|
||||
{{/unless}}
|
||||
</label>
|
||||
</li>
|
||||
{{#each system.services as |service key|}}
|
||||
<li class="item flexrow list-item" data-key="{{key}}">
|
||||
<input {{@root.disabled}} type="text" name="services[{{key}}].name" value="{{service.name}}" data-dtype="String" />
|
||||
<input {{@root.disabled}} type="checkbox" name="services[{{key}}].system.moral" {{#if service.system.moral}}checked{{/if}} />
|
||||
<input {{@root.disabled}} type="number" name="services[{{key}}].system.qualite" value="{{service.system.qualite}}" data-dtype="Number" min="-10" max="10"/>
|
||||
<input {{@root.disabled}} type="number" class="input-prix" name="services[{{key}}].system.cout" value="{{numberFormat service.system.cout decimals=2 sign=false}}" data-dtype="Number" min="0" />
|
||||
<div class="item-controls">
|
||||
<a class="service-acheter" title="Acheter"><i class="fa-sharp fa-solid fa-coins"></i></a>
|
||||
{{#unless @root.disabled}}
|
||||
<a class="service-vendre" title="Proposer"><i class="fas fa-comments-dollar"></i></a>
|
||||
<a class="service-delete" title="Supprimer"><i class="fas fa-trash"></i></a>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
--}}
|
||||
<div class="form-group">
|
||||
<input {{@root.disabled}} class="attribute-value" type="checkbox" name="system.illimite" {{#if system.illimite}}checked{{/if}}/>
|
||||
<span for="system.illimite">Quantité en vente illimitée</span>
|
||||
</div>
|
||||
<div class="flexcol">
|
||||
<ul class="item-list alterne-list">
|
||||
<li class="item flexrow list-item">
|
||||
<label class="flex-grow">A vendre</label>
|
||||
{{#unless system.illimite}}
|
||||
<label>Quantite</label>
|
||||
{{/unless}}
|
||||
<label>Prix (sols)</label>
|
||||
<div class="item-controls">
|
||||
{{#unless disabled}}
|
||||
<a class="sub-item-info-add" title="Utiliser le drag&drop pour ajouter un objet dans la liste">
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
</a>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</li>
|
||||
{{#each system.items as |item key|}}
|
||||
<li class="item flexrow list-item service-item" data-item-id="{{item.id}}" data-pack="{{item.pack}}" data-key="{{key}}">
|
||||
<label class="flex-grow">
|
||||
{{#if item.img}}<img class="sheet-competence-img" src="{{item.img}}" title="{{item.name}}"/>{{/if}}
|
||||
{{> 'systems/foundryvtt-reve-de-dragon/templates/common/compendium-link.hbs'
|
||||
pack=item.pack id=item.id name=item.name}}
|
||||
</label>
|
||||
{{#unless @root.system.illimite}}
|
||||
<span class="flexrow">
|
||||
{{#unless @root.disabled}}
|
||||
<a class="sub-item-quantite-moins"><i class="fas fa-minus-square"></i></a>
|
||||
{{/unless}}
|
||||
<input {{@root.disabled}} type="number" class="sub-item-quantite" name="items[{{key}}].system.quantite" value="{{item.system.quantite}}" data-dtype="Number" />
|
||||
{{#unless @root.disabled}}
|
||||
<a class="sub-item-quantite-plus"><i class="fas fa-plus-square"></i></a>
|
||||
{{/unless}}
|
||||
</span>
|
||||
{{/unless}}
|
||||
<span class="flexrow">
|
||||
<input {{@root.disabled}} type="number" class="input-prix number-x3 sub-item-cout" name="items[{{key}}].system.cout" value="{{numberFormat item.system.cout decimals=2 sign=false}}" data-dtype="Number" />
|
||||
</span>
|
||||
<div class="item-controls">
|
||||
<a class="sub-item-acheter" title="Acheter"><i class="fa-regular fa-coins"></i></a>
|
||||
{{#unless @root.disabled}}
|
||||
<a class="sub-item-vendre" title="Vendre"><i class="fas fa-comments-dollar"></i></a>
|
||||
<a class="sub-item-delete" title="Supprimer"><i class="fas fa-trash"></i></a>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{#if isGM}}
|
||||
<br>
|
||||
<div class="flexcol">
|
||||
<span><label>Description (MJ seulement): </label></span>
|
||||
<div class="form-group medium-editor">
|
||||
{{editor descriptionmj target="system.descriptionmj" button=true owner=owner editable=true engine="prosemirror"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
</form>
|
8
templates/post-item-service.html
Normal file
8
templates/post-item-service.html
Normal file
@ -0,0 +1,8 @@
|
||||
<div class="post-item" data-transfer="{{transfer}}">
|
||||
{{#if img}}
|
||||
<img class="chat-icon" src="{{img}}" title="{{name}}" />
|
||||
{{/if}}
|
||||
<p>{{> 'systems/foundryvtt-reve-de-dragon/templates/common/compendium-link.hbs' pack=pack id=_id name=name}}</p>
|
||||
<p class="card-content">{{{system.description}}}</p>
|
||||
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user