Vincent Vandemeulebrouck
d9cdfef688
simplification des tests de droits pour savoir si on transmet l'appel de méthode à un MJ connecté. De manière générale, si on est le owner: pas besoin d'appel remote. Donc si MJ pas besoin. Si on a un appel remote, seul un MJ le traite.
763 lines
26 KiB
JavaScript
763 lines
26 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 { ITEM_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 getDefaultValue(actorType, path) {
|
|
if (path.includes('.')) {
|
|
path = path.split('.')
|
|
}
|
|
let obj = game.model.Actor[actorType]
|
|
for (let p of path) {
|
|
obj = obj ? obj[p] : undefined
|
|
}
|
|
return obj
|
|
}
|
|
|
|
static getDefaultImg(itemType) {
|
|
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
|
|
}
|
|
|
|
static init() {
|
|
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
|
|
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
|
|
Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
|
|
Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(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) {
|
|
if (game.user.isGM) {
|
|
RdDBaseActor.onRemoteActorCall(callData, game.user.id)
|
|
return false
|
|
}
|
|
else {
|
|
game.socket.emit(SYSTEM_SOCKET_ID, {
|
|
msg: "msg_remote_actor_call",
|
|
data: callData,
|
|
userId: Misc.firstConnectedGMId()
|
|
})
|
|
return true
|
|
}
|
|
}
|
|
|
|
static onRemoteActorCall(callData, userId) {
|
|
const actor = RdDBaseActor.getRealActor(callData?.actorId, callData?.tokenId);
|
|
if (userId == game.user.id) {
|
|
// 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)
|
|
}
|
|
|
|
getAlias() {
|
|
if (this.token?.name != null && this.token != this.prototypeToken) {
|
|
return this.token.name
|
|
}
|
|
return this.name
|
|
}
|
|
|
|
isPersonnageJoueur() { return false }
|
|
|
|
static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; };
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
context.rdd = undefined
|
|
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()
|
|
}
|
|
|
|
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 updateCarac(caracName, to) {
|
|
}
|
|
/* -------------------------------------------- */
|
|
async onPreUpdateItem(item, change, options, id) { }
|
|
async onCreateItem(item, options, id) { }
|
|
async onUpdateActor(update, options, actorId) { }
|
|
async onDeleteItem(item, options, id) {
|
|
if (item.isInventaire()) {
|
|
this._removeItemFromConteneur(item)
|
|
}
|
|
}
|
|
|
|
_removeItemFromConteneur(item) {
|
|
this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id))
|
|
.forEach(conteneur => {
|
|
const nouveauContenu = conteneur.system.contenu.filter(id => id != item.id);
|
|
conteneur.update({ 'system.contenu': nouveauContenu });
|
|
});
|
|
}
|
|
|
|
async onTimeChanging(oldTimestamp, newTimestamp) {
|
|
this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
|
|
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
|
|
}
|
|
|
|
async creerObjetParMJ(object) {
|
|
if (this.isOwner) {
|
|
await this.createEmbeddedDocuments('Item', [object])
|
|
return
|
|
}
|
|
RdDBaseActor.remoteActorCall({
|
|
tokenId: this.token?.id,
|
|
actorId: this.id,
|
|
method: 'creerObjetParMJ', args: [object]
|
|
})
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async cleanupConteneurs() {
|
|
if (Misc.isOwnerPlayer(this)) {
|
|
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);
|
|
// TODO: passer en handlebars
|
|
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 !";
|
|
}
|
|
|
|
ChatMessage.create({
|
|
whisper: ChatUtility.getOwners(this),
|
|
content: msg
|
|
})
|
|
}
|
|
|
|
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 && !this.isOwner) {
|
|
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.getOwners(this),
|
|
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.isFirstConnectedGM()) {
|
|
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).getAlias() },
|
|
whisper: ChatUtility.getOwners(this),
|
|
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.diminuerQuantiteAchatVente(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 = Misc.keepDecimals(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?.isConteneur()) {
|
|
item.estContenu = true;
|
|
const nouveauContenu = [...conteneur.system.contenu, item.id];
|
|
await conteneur.update({ 'system.contenu': nouveauContenu });
|
|
onAjouterDansConteneur(item.id, conteneur.id)
|
|
}
|
|
else {
|
|
item.estContenu = false;
|
|
await conteneur?.update({ 'system.-=contenu': undefined })
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** 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.system.contenu != undefined) {
|
|
if (item.type == 'conteneur') {
|
|
corrections.push({ _id: item.id, 'system.contenu': [] });
|
|
}
|
|
else {
|
|
corrections.push({ _id: item.id, 'system.-=contenu': undefined });
|
|
}
|
|
}
|
|
}
|
|
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) {
|
|
if (conteneur.isConteneur()) {
|
|
const contenu = conteneur.system.contenu.filter(id => id != item.id);
|
|
await conteneur.update({ 'system.contenu': contenu });
|
|
onEnleverDeConteneur();
|
|
}
|
|
else {
|
|
await conteneur.update({ 'system.-=contenu': undefined })
|
|
}
|
|
}
|
|
item.estContenu = false;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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.getAlias(),
|
|
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.getAlias()} ne peut pas faire cette action: ${action}`)
|
|
|
|
}
|
|
|
|
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 ITEM_TYPES.conteneur: return await item.sheet.render(true);
|
|
}
|
|
return undefined
|
|
}
|
|
async resetItemUse() { }
|
|
async incDecItemUse(itemId, inc = 1) { }
|
|
getItemUse(itemId) { return 0; }
|
|
async finDeRound(options = { terminer: false }) { }
|
|
isActorCombat() { return false }
|
|
getCaracInit(competence) { return 0 }
|
|
listActionsCombat() { return [] }
|
|
listActionsPossessions() {
|
|
return this.itemTypes[ITEM_TYPES.possession]
|
|
.map(p => {
|
|
return {
|
|
name: p.name,
|
|
action: 'possession',
|
|
system: {
|
|
competence: p.name,
|
|
possessionid: p.system.possessionid,
|
|
}
|
|
}
|
|
})
|
|
}
|
|
} |