Vincent Vandemeulebrouck
0b1c5d0a3d
- remplacement des données/JSON dans le html par des Flags sur le ChatMessage - extraction de la gestion des infos de ventes pour rassembler la génération du ChatMessage - on ne perd plus la quantité ou le vendeur - attention au mergeObject: il modifie le premier parametre, ce qui modifiait parfois l'acteur (!!!) et toujours la quantité de l'objet du vendeur lors de la création de l'objet de l'acheteur!
702 lines
25 KiB
JavaScript
702 lines
25 KiB
JavaScript
import { ChatVente } from "../achat-vente/chat-vente.js";
|
|
import { ChatUtility } from "../chat-utility.js";
|
|
import { SYSTEM_SOCKET_ID } from "../constants.js";
|
|
import { Grammar } from "../grammar.js";
|
|
import { Monnaie } from "../item-monnaie.js";
|
|
import { TYPES } from "../item.js";
|
|
import { Misc } from "../misc.js";
|
|
import { RdDAudio } from "../rdd-audio.js";
|
|
import { RdDConfirm } from "../rdd-confirm.js";
|
|
import { RdDUtility } from "../rdd-utility.js";
|
|
import { SystemCompendiums } from "../settings/system-compendiums.js";
|
|
|
|
export class RdDBaseActor extends Actor {
|
|
|
|
static _findCaracNode(carac, name) {
|
|
return Object.entries(carac)
|
|
.filter(it => Grammar.equalsInsensitive(it[1].label, name))
|
|
.map(it => it[0])
|
|
.find(it => it)
|
|
}
|
|
static $findCaracByName(carac, name) {
|
|
const caracList = Object.entries(carac);
|
|
let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique' });
|
|
if (!entry || entry.length == 0) {
|
|
entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' });
|
|
}
|
|
return entry && entry.length > 0 ? carac[entry[0]] : undefined;
|
|
}
|
|
|
|
static getDefaultImg(itemType) {
|
|
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
|
|
}
|
|
|
|
static init() {
|
|
Hooks.on("preUpdateItem", (item, change, options, id) => RdDBaseActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id));
|
|
Hooks.on("createItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onCreateItem(item, options, id));
|
|
Hooks.on("deleteItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onDeleteItem(item, options, id));
|
|
Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId));
|
|
}
|
|
|
|
static onSocketMessage(sockmsg) {
|
|
switch (sockmsg.msg) {
|
|
case "msg_remote_actor_call":
|
|
return RdDBaseActor.onRemoteActorCall(sockmsg.data, sockmsg.userId);
|
|
}
|
|
}
|
|
|
|
static remoteActorCall(callData, userId = undefined) {
|
|
userId = userId ?? Misc.firstConnectedGMId();
|
|
if (userId == game.user.id) {
|
|
RdDBaseActor.onRemoteActorCall(callData, userId);
|
|
return false;
|
|
}
|
|
else {
|
|
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_remote_actor_call", data: callData, userId: userId });
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static onRemoteActorCall(callData, userId) {
|
|
if (userId == game.user.id) {
|
|
const actor = RdDBaseActor.getRealActor(callData?.actorId, callData?.tokenId);
|
|
if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon
|
|
const args = callData.args;
|
|
console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')');
|
|
actor[callData.method](...args);
|
|
}
|
|
}
|
|
}
|
|
|
|
static getRealActor(actorId, tokenId) {
|
|
if (tokenId) {
|
|
let token = canvas.tokens.get(tokenId)
|
|
if (token) {
|
|
return token.actor
|
|
}
|
|
}
|
|
return game.actors.get(actorId)
|
|
}
|
|
|
|
isPersonnageJoueur() { return false }
|
|
|
|
static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; };
|
|
|
|
static getParentActor(document) {
|
|
return document?.parent instanceof Actor ? document.parent : undefined
|
|
}
|
|
|
|
/**
|
|
* Cette methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
|
|
* compétences et monnaies.
|
|
*
|
|
* @param {Object} actorData template d'acteur auquel ajouter des informations.
|
|
* @param {Object} options optionspour customiser la création
|
|
*/
|
|
static async create(actorData, options) {
|
|
// import depuis un compendium
|
|
if (actorData instanceof Array) {
|
|
return super.create(actorData, options);
|
|
}
|
|
// Création d'un acteur avec des items (uniquement en cas de duplication): pas besoin d'ajouter d'items
|
|
if (actorData.items) {
|
|
return await super.create(actorData, options);
|
|
}
|
|
actorData.items = [];
|
|
if (actorData.type == "personnage") {
|
|
const competences = await SystemCompendiums.getCompetences(actorData.type);
|
|
actorData.items = actorData.items.concat(competences.map(i => i.toObject()))
|
|
.concat(Monnaie.monnaiesStandard());
|
|
}
|
|
else if (actorData.type == "commerce") {
|
|
actorData.items = actorData.items.concat(Monnaie.monnaiesStandard());
|
|
}
|
|
return super.create(actorData, options);
|
|
}
|
|
|
|
constructor(docData, context = {}) {
|
|
if (!context.rdd?.ready) {
|
|
foundry.utils.mergeObject(context, { rdd: { ready: true } });
|
|
const ActorConstructor = game.system.rdd.actorClasses[docData.type];
|
|
if (ActorConstructor) {
|
|
if (!docData.img) {
|
|
docData.img = ActorConstructor.defaultIcon;
|
|
}
|
|
return new ActorConstructor(docData, context);
|
|
}
|
|
}
|
|
super(docData, context);
|
|
}
|
|
|
|
findCaracByName(name) {
|
|
name = Grammar.toLowerCaseNoAccent(name)
|
|
switch (name) {
|
|
case 'reve-actuel': case 'reve actuel':
|
|
return this.system.carac.reve
|
|
case 'chance-actuelle': case 'chance actuelle':
|
|
return this.system.carac.chance
|
|
case 'vie':
|
|
return this.system.sante.vie
|
|
}
|
|
|
|
const carac = this.system.carac;
|
|
return RdDBaseActor.$findCaracByName(carac, name);
|
|
}
|
|
|
|
getCaracByName(name) {
|
|
switch (Grammar.toLowerCaseNoAccent(name)) {
|
|
case 'reve-actuel': case 'reve actuel':
|
|
return this.getCaracReveActuel();
|
|
case 'chance-actuelle': case 'chance-actuelle':
|
|
return this.getCaracChanceActuelle();
|
|
}
|
|
return this.findCaracByName(name);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async _preCreate(data, options, user) {
|
|
await super._preCreate(data, options, user);
|
|
|
|
// Configure prototype token settings
|
|
const prototypeToken = {};
|
|
if (this.type === "personnage") Object.assign(prototypeToken, {
|
|
sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
|
|
});
|
|
this.updateSource({ prototypeToken });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
prepareData() {
|
|
super.prepareData()
|
|
this.prepareActorData()
|
|
this.cleanupConteneurs()
|
|
this.computeEtatGeneral()
|
|
this.computeEncTotal()
|
|
}
|
|
|
|
async prepareActorData() { }
|
|
async computeEtatGeneral() { }
|
|
/* -------------------------------------------- */
|
|
findPlayer() {
|
|
return game.users.players.find(player => player.active && player.character?.id == this.id);
|
|
}
|
|
|
|
isCreatureEntite() { return this.isCreature() || this.isEntite() }
|
|
isCreature() { return false }
|
|
isEntite(typeentite = []) { return false }
|
|
isVehicule() { return false }
|
|
isPersonnage() { return false }
|
|
getItem(id, type = undefined) {
|
|
const item = this.items.get(id);
|
|
if (type == undefined || (item?.type == type)) {
|
|
return item;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
listeSuivants(filter = suivant => true) { return [] }
|
|
listeSuivants(filter = suivant => true) { return [] }
|
|
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
|
|
filterItems(filter, type = undefined) { return (type ? this.itemTypes[type] : this.items)?.filter(filter) ?? []; }
|
|
findItemLike(idOrName, type) {
|
|
return this.getItem(idOrName, type)
|
|
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
|
|
}
|
|
|
|
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
|
|
getEncombrementMax() { return 0 }
|
|
|
|
/* -------------------------------------------- */
|
|
async onPreUpdateItem(item, change, options, id) { }
|
|
|
|
async onCreateItem(item, options, id) { }
|
|
|
|
async onDeleteItem(item, options, id) { }
|
|
|
|
async onUpdateActor(update, options, actorId) { }
|
|
|
|
async onTimeChanging(oldTimestamp, newTimestamp) {
|
|
this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
|
|
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
|
|
}
|
|
|
|
async creerObjetParMJ(object) {
|
|
if (!Misc.isUniqueConnectedGM()) {
|
|
RdDBaseActor.remoteActorCall({
|
|
tokenId: this.token?.id,
|
|
actorId: this.id,
|
|
method: 'creerObjetParMJ',
|
|
args: [object]
|
|
});
|
|
return;
|
|
}
|
|
await this.createEmbeddedDocuments('Item', [object])
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async cleanupConteneurs() {
|
|
let updates = this.itemTypes['conteneur']
|
|
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
|
|
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
|
|
if (updates.length > 0) {
|
|
await this.updateEmbeddedDocuments("Item", updates)
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getFortune() {
|
|
return Monnaie.getFortune(this.itemTypes['monnaie']);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async itemQuantiteIncDec(id, value) {
|
|
let item = this.getItem(id);
|
|
if (item && item.isInventaire()) {
|
|
const quantite = Math.max(0, item.system.quantite + value);
|
|
await item.update({ 'system.quantite': quantite });
|
|
}
|
|
}
|
|
|
|
computePrixTotalEquipement() {
|
|
return this.items.filter(it => it.isInventaire())
|
|
.filter(it => !it.isMonnaie())
|
|
.map(it => it.valeurTotale())
|
|
.reduce(Misc.sum(), 0);
|
|
}
|
|
|
|
async payerSols(depense) {
|
|
depense = Number(depense);
|
|
if (depense == 0) {
|
|
return;
|
|
}
|
|
let fortune = this.getFortune();
|
|
console.log("payer", game.user.character, depense, fortune);
|
|
let msg = "";
|
|
if (fortune >= depense) {
|
|
await Monnaie.optimiserFortune(this, fortune - depense);
|
|
msg = `Vous avez payé <strong>${depense} Sols</strong>, qui ont été soustraits de votre argent.`;
|
|
RdDAudio.PlayContextAudio("argent"); // Petit son
|
|
} else {
|
|
msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
|
|
}
|
|
|
|
let message = {
|
|
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
|
|
content: msg
|
|
};
|
|
ChatMessage.create(message);
|
|
}
|
|
|
|
async depenserSols(sols) {
|
|
let reste = this.getFortune() - Number(sols);
|
|
if (reste >= 0) {
|
|
await Monnaie.optimiserFortune(this, reste);
|
|
}
|
|
return reste;
|
|
}
|
|
|
|
async ajouterSols(sols, fromActorId = undefined) {
|
|
sols = Number(sols);
|
|
if (sols == 0) {
|
|
return;
|
|
}
|
|
if (sols < 0) {
|
|
ui.notifications.error(`Impossible d'ajouter un gain de ${sols} <0`);
|
|
return;
|
|
}
|
|
if (fromActorId && !game.user.isGM) {
|
|
RdDBaseActor.remoteActorCall({
|
|
userId: Misc.connectedGMOrUser(),
|
|
tokenId: this.token?.id,
|
|
actorId: this.id,
|
|
method: 'ajouterSols', args: [sols, fromActorId]
|
|
});
|
|
}
|
|
else {
|
|
const fromActor = game.actors.get(fromActorId)
|
|
await Monnaie.optimiserFortune(this, sols + this.getFortune());
|
|
|
|
RdDAudio.PlayContextAudio("argent"); // Petit son
|
|
ChatMessage.create({
|
|
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
|
|
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
|
|
});
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
getQuantiteDisponible(item) {
|
|
return item?.isService() ? undefined : item?.getQuantite();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async achatVente(achat) {
|
|
if (achat.vendeurId == achat.acheteurId) {
|
|
ui.notifications.info("Inutile de se vendre à soi-même");
|
|
return;
|
|
}
|
|
if (!Misc.isUniqueConnectedGM()) {
|
|
RdDBaseActor.remoteActorCall({
|
|
actorId: achat.vendeurId ?? achat.acheteurId,
|
|
method: 'achatVente',
|
|
args: [achat]
|
|
});
|
|
return;
|
|
}
|
|
const cout = Number(achat.prixTotal ?? 0);
|
|
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
|
|
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
|
|
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
|
|
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
|
|
if (!itemVendu) {
|
|
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`);
|
|
return;
|
|
}
|
|
if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) {
|
|
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
|
|
return
|
|
}
|
|
if (acheteur && !acheteur.verifierFortune(cout)) {
|
|
ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
|
|
return;
|
|
}
|
|
await vendeur?.vendre(itemVendu, quantite, cout);
|
|
await acheteur?.acheter(itemVendu, quantite, cout, achat)
|
|
|
|
if (cout > 0) {
|
|
RdDAudio.PlayContextAudio("argent");
|
|
}
|
|
const chatAchatItem = foundry.utils.duplicate(achat.vente);
|
|
chatAchatItem.quantiteTotal = quantite;
|
|
ChatMessage.create({
|
|
user: achat.userId,
|
|
speaker: { alias: (acheteur ?? vendeur).name },
|
|
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
|
|
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
|
|
});
|
|
|
|
if (!achat.vente.quantiteIllimite) {
|
|
if (achat.vente.nbLots <= achat.choix.nombreLots) {
|
|
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
|
|
}
|
|
else if (achat.chatMessageIdVente) {
|
|
await ChatVente.diminuerQuantite(achat.chatMessageIdVente, achat.choix.nombreLots)
|
|
}
|
|
}
|
|
}
|
|
|
|
async vendre(item, quantite, cout) {
|
|
await this.ajouterSols(cout);
|
|
await this.decrementerQuantiteItem(item, quantite);
|
|
}
|
|
|
|
async acheter(item, quantite, cout, achat) {
|
|
await this.depenserSols(cout)
|
|
const createdItemId = await this.creerQuantiteItem(item, quantite)
|
|
if (achat.choix.consommer && item.type == 'nourritureboisson' && createdItemId != undefined) {
|
|
achat.choix.doses = achat.choix.nombreLots;
|
|
await this.consommerNourritureboisson(createdItemId, achat.choix, achat.vente.actingUserId);
|
|
}
|
|
}
|
|
|
|
verifierFortune(cout) {
|
|
return this.getFortune() >= cout;
|
|
}
|
|
|
|
verifierQuantite(item, quantiteDemande) {
|
|
const disponible = this.getQuantiteDisponible(item);
|
|
return disponible == undefined || disponible >= quantiteDemande;
|
|
}
|
|
|
|
async consommerNourritureboisson(itemId, choix, userId) { }
|
|
|
|
async decrementerQuantiteItem(item, quantite, options = { supprimerSiZero: true }) {
|
|
if (item.isService()) {
|
|
return;
|
|
}
|
|
const itemId = item.id;
|
|
let resteQuantite = (item.system.quantite ?? 1) - quantite;
|
|
if (resteQuantite <= 0) {
|
|
if (options.supprimerSiZero) {
|
|
await this.deleteEmbeddedDocuments("Item", [item.id]);
|
|
}
|
|
else {
|
|
await this.updateEmbeddedDocuments("Item", [{ _id: itemId, 'system.quantite': 0 }]);
|
|
}
|
|
if (resteQuantite < 0) {
|
|
ui.notifications.warn(`La quantité de ${item.name} était insuffisante, l'objet a donc été supprimé`)
|
|
}
|
|
}
|
|
else if (resteQuantite > 0) {
|
|
const realItem = this.getItem(item.id)
|
|
realItem.update({ 'system.quantite': resteQuantite });
|
|
await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': resteQuantite }]);
|
|
}
|
|
}
|
|
|
|
async creerQuantiteItem(item, quantite) {
|
|
if (this.canReceive(item)) {
|
|
const isItemEmpilable = "quantite" in item.system;
|
|
const baseItem = {
|
|
type: item.type,
|
|
img: item.img,
|
|
name: item.name,
|
|
system: foundry.utils.mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined }, { inplace: false })
|
|
};
|
|
const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
|
|
const items = await this.createEmbeddedDocuments("Item", newItems);
|
|
return items.length > 0 ? items[0].id : undefined;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async computeEncTotal() {
|
|
if (!this.pack) {
|
|
this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);
|
|
return this.encTotal;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
getEncTotal() {
|
|
return Math.floor(this.encTotal ?? 0);
|
|
}
|
|
|
|
async createItem(type, name = undefined) {
|
|
if (!name) {
|
|
name = 'Nouveau ' + Misc.typeName('Item', type);
|
|
}
|
|
await this.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
|
|
}
|
|
|
|
canReceive(item) {
|
|
return false;
|
|
}
|
|
|
|
async processDropItem(params) {
|
|
const targetActorId = this.id
|
|
const sourceActorId = params.sourceActorId
|
|
const sourceTokenId = params.sourceTokenId
|
|
const itemId = params.itemId
|
|
const destId = params.destId
|
|
const srcId = params.srcId
|
|
if (sourceActorId && sourceActorId != targetActorId) {
|
|
console.log("Moving objects", sourceActorId, sourceTokenId, targetActorId, itemId);
|
|
this.moveItemsBetweenActors(itemId, sourceActorId, sourceTokenId);
|
|
return false;
|
|
}
|
|
let result = true;
|
|
const item = this.getItem(itemId);
|
|
if (item?.isInventaire('all') && sourceActorId == targetActorId) {
|
|
// rangement
|
|
if (srcId != destId && itemId != destId) { // déplacement de l'objet
|
|
const src = this.getItem(srcId);
|
|
const dest = this.getItem(destId);
|
|
const cible = this.getContenantOrParent(dest);
|
|
const [empilable, message] = item.isInventaireEmpilable(dest);
|
|
if (empilable) {
|
|
await dest.empiler(item)
|
|
result = false;
|
|
}
|
|
// changer de conteneur
|
|
else if (!cible || this.conteneurPeutContenir(cible, item)) {
|
|
await this.enleverDeConteneur(item, src, params.onEnleverConteneur);
|
|
await this.ajouterDansConteneur(item, cible, params.onAjouterDansConteneur);
|
|
if (message && !dest.isConteneur()) {
|
|
ui.notifications.info(cible
|
|
? `${message}<br>${item.name} a été déplacé dans: ${cible.name}`
|
|
: `${message}<br>${item.name} a été sorti du conteneur`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
await this.computeEncTotal();
|
|
return result;
|
|
}
|
|
|
|
getContenantOrParent(dest) {
|
|
if (!dest || dest.isConteneur()) {
|
|
return dest;
|
|
}
|
|
return this.getContenant(dest);
|
|
}
|
|
|
|
getContenant(item) {
|
|
return this.itemTypes['conteneur'].find(it => it.system.contenu.includes(item.id));
|
|
}
|
|
|
|
|
|
/* -------------------------------------------- */
|
|
conteneurPeutContenir(dest, moved) {
|
|
if (!dest) {
|
|
return true;
|
|
}
|
|
if (!dest.isConteneur()) {
|
|
return false;
|
|
}
|
|
if (moved.isConteneurContenu(dest)) {
|
|
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${moved.name}) dans un de ses contenus ${dest.name} !`);
|
|
return false;
|
|
}
|
|
|
|
// Calculer le total actuel des contenus
|
|
const encContenu = dest.getEncContenu();
|
|
const newEnc = moved.getEncTotal(); // Calculer le total actuel du nouvel objet
|
|
const placeDisponible = Math.roundDecimals(dest.system.capacite - encContenu - newEnc, 4)
|
|
|
|
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
|
|
if (placeDisponible < 0) {
|
|
ui.notifications.warn(
|
|
`Le conteneur ${dest.name} a une capacité de ${dest.system.capacite}, et contient déjà ${encContenu}.
|
|
Impossible d'y ranger: ${moved.name} d'encombrement ${newEnc}!`);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Ajoute un item dans un conteneur, sur la base de leurs ID */
|
|
async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) {
|
|
if (!conteneur) {
|
|
// TODO: afficher
|
|
item.estContenu = false;
|
|
}
|
|
else if (conteneur.isConteneur()) {
|
|
item.estContenu = true;
|
|
const nouveauContenu = [...conteneur.system.contenu, item.id];
|
|
await conteneur.update({ 'system.contenu': nouveauContenu });
|
|
onAjouterDansConteneur(item.id, conteneur.id);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Fonction de remise à plat de l'équipement (ie vide les champs 'contenu') */
|
|
async nettoyerConteneurs() {
|
|
RdDConfirm.confirmer({
|
|
settingConfirmer: "confirmation-vider",
|
|
content: `<p>Etes vous certain de vouloir vider tous les conteneurs ?</p>`,
|
|
title: 'Vider les conteneurs',
|
|
buttonLabel: 'Vider',
|
|
onAction: async () => {
|
|
const corrections = [];
|
|
for (let item of this.items) {
|
|
if (item.estContenu) {
|
|
item.estContenu = undefined;
|
|
}
|
|
if (item.type == 'conteneur' && item.system.contenu.length > 0) {
|
|
corrections.push({ _id: item.id, 'system.contenu': [] });
|
|
}
|
|
}
|
|
if (corrections.length > 0) {
|
|
await this.updateEmbeddedDocuments('Item', corrections);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
buildSubConteneurObjetList(conteneurId, deleteList) {
|
|
let conteneur = this.getItem(conteneurId);
|
|
if (conteneur?.type == 'conteneur') { // Si c'est un conteneur
|
|
for (let subId of conteneur.system.contenu) {
|
|
let subObj = this.getItem(subId);
|
|
if (subObj) {
|
|
if (subObj.type == 'conteneur') {
|
|
this.buildSubConteneurObjetList(subId, deleteList);
|
|
}
|
|
deleteList.push({ id: subId, conteneurId: conteneurId });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async deleteAllConteneur(itemId, options) {
|
|
let list = [];
|
|
list.push({ id: itemId, conteneurId: undefined }); // Init list
|
|
this.buildSubConteneurObjetList(itemId, list);
|
|
await this.deleteEmbeddedDocuments('Item', list.map(it => it.id), options);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Supprime un item d'un conteneur, sur la base
|
|
* de leurs ID */
|
|
async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) {
|
|
if (conteneur?.isConteneur()) {
|
|
item.estContenu = false;
|
|
const contenu = conteneur.system.contenu.filter(id => id != item.id);
|
|
await conteneur.update({ 'system.contenu': contenu });
|
|
onEnleverDeConteneur();
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async moveItemsBetweenActors(itemId, sourceActorId, sourceTokenId) {
|
|
let sourceActor = RdDBaseActor.getRealActor(sourceActorId, sourceTokenId)
|
|
let itemsList = [{ id: itemId, conteneurId: undefined }]
|
|
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
|
|
|
|
const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
|
|
.map(it => foundry.utils.duplicate(it))
|
|
.map(it => { it.system.contenu = []; return it; });
|
|
let newItems = await this.createEmbeddedDocuments('Item', itemsDataToCreate);
|
|
|
|
let itemMap = this._buildMapOldNewId(itemsList, newItems);
|
|
|
|
for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs
|
|
// gestion conteneur/contenu
|
|
if (item.conteneurId) { // l'Objet était dans un conteneur
|
|
const newConteneurId = itemMap[item.conteneurId];
|
|
const newConteneur = this.getItem(newConteneurId);
|
|
const newItemId = itemMap[item.id]; // Get newItem
|
|
|
|
console.log('New conteneur filling!', newConteneur, newItemId, item);
|
|
const nouveauContenu = [...newConteneur.system.contenu, newItemId]
|
|
await newConteneur.update({ 'system.contenu': nouveauContenu })
|
|
}
|
|
}
|
|
const deletedItemIds = itemsList.map(it => it.id)
|
|
await sourceActor.deleteEmbeddedDocuments('Item', deletedItemIds);
|
|
}
|
|
|
|
_buildMapOldNewId(itemsList, newItems) {
|
|
let itemMap = {};
|
|
for (let i = 0; i < itemsList.length; i++) {
|
|
itemMap[itemsList[i].id] = newItems[i].id; // Pour garder le lien ancien / nouveau
|
|
}
|
|
return itemMap;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async postActorToChat(modeOverride) {
|
|
let chatData = {
|
|
doctype: 'Actor',
|
|
id: this.id,
|
|
type: this.type,
|
|
img: this.img,
|
|
pack: this.pack,
|
|
name: this.name,
|
|
system: { description: this.system.description }
|
|
}
|
|
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.html', chatData)
|
|
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
|
|
}
|
|
|
|
actionImpossible(action) {
|
|
ui.notifications.info(`${this.name} ne peut pas faire cette action: ${action}`)
|
|
|
|
}
|
|
async roll() { this.actionImpossible("jet de caractéristiques") }
|
|
async jetEthylisme() { this.actionImpossible("jet d'éthylisme") }
|
|
async rollAppelChance() { this.actionImpossible("appel à la chance") }
|
|
async jetDeMoral() { this.actionImpossible("jet de moral") }
|
|
|
|
async actionPrincipale(item, onActionItem = async () => { }) {
|
|
switch (item.type) {
|
|
case TYPES.conteneur: return await item.sheet.render(true);
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
} |