Support de plusieurs actors

This commit is contained in:
Vincent Vandemeulebrouck
2022-12-28 23:36:48 +01:00
parent 6adeb790a0
commit 2062ff0777
11 changed files with 518 additions and 334 deletions

View File

@ -34,9 +34,9 @@ import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE, HIDE_DICE, SHOW_DIC
import { RdDConfirm } from "./rdd-confirm.js";
import { DialogValidationEncaissement } from "./dialog-validation-encaissement.js";
import { RdDRencontre } from "./item-rencontre.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Targets } from "./targets.js";
import { DialogRepos } from "./dialog-repos.js";
import { RdDBaseActor } from "./actor/base-actor.js";
const POSSESSION_SANS_DRACONIC = {
img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp',
@ -56,88 +56,7 @@ export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
*/
export class RdDActor extends Actor {
/* -------------------------------------------- */
static init() {
Hooks.on("preUpdateItem", (item, change, options, id) => RdDActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id));
// TODO: replace with pre-hooks?
Hooks.on("createItem", (item, options, id) => RdDActor.getParentActor(item)?.onCreateItem(item, options, id));
Hooks.on("deleteItem", (item, options, id) => RdDActor.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 RdDActor.onRemoteActorCall(sockmsg.data, sockmsg.userId);
case "msg_reset_nombre_astral":
console.log("RESET ASTRAL", game.user.character);
game.user.character.resetNombreAstral();
return;
}
}
static remoteActorCall(callData, userId = undefined) {
userId = userId ?? Misc.firstConnectedGMId();
if (userId == game.user.id) {
RdDActor.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 = game.actors.get(callData?.actorId);
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(`RdDActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDActor.${callData.method}(`, ...args, ')');
actor[callData.method](...args);
}
}
}
/* -------------------------------------------- */
static getParentActor(document) {
return document?.parent instanceof Actor ? document.parent : undefined
}
/* -------------------------------------------- */
/**
* Override the create() function to provide additional RdD functionality.
*
* This overrided create() function adds initial items
* Namely: Basic skills, money,
*
* @param {Object} actorData Barebones actor template data which this function adds onto.
* @param {Object} options Additional options which customize the creation workflow.
*
*/
static async create(actorData, options) {
// Case of compendium global import
if (actorData instanceof Array) {
return super.create(actorData, options);
}
const isPersonnage = actorData.type == "personnage";
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
if (actorData.items) {
return await super.create(actorData, options);
}
if (isPersonnage) {
const competences = await SystemCompendiums.getCompetences(actorData.type);
actorData.items = competences.map(i => i.toObject())
.concat(Monnaie.monnaiesStandard());
}
else {
actorData.items = [];
}
return super.create(actorData, options);
}
export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
prepareData() {
@ -145,6 +64,8 @@ export class RdDActor extends Actor {
// Dynamic computing fields
this.encTotal = 0;
// TODO: separate derived/base data preparation
// TODO: split by actor class
// Make separate methods for each Actor type (character, npc, etc.) to keep
// things organized.
@ -178,9 +99,9 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async cleanupConteneurs() {
let updates = this.listItemsData('conteneur')
.filter(c => c.system.contenu.filter(id => this.getObjet(id) == undefined).length > 0)
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getObjet(id) != undefined) } });
let updates = this.listItems('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)
}
@ -206,23 +127,6 @@ export class RdDActor extends Actor {
return false;
}
/* -------------------------------------------- */
isCreatureEntite() {
return this.type == 'creature' || this.type == 'entite';
}
isCreature() {
return this.type == 'creature';
}
isEntite() {
return this.type == 'entite';
}
/* -------------------------------------------- */
isPersonnage() {
return this.type == 'personnage';
}
isVehicule() {
return this.type == 'vehicule';
}
/* -------------------------------------------- */
isHautRevant() {
return this.isPersonnage() && this.system.attributs.hautrevant.value != ""
@ -330,50 +234,33 @@ export class RdDActor extends Actor {
}
/* -------------------------------------------- */
getObjet(id) {
return this.getEmbeddedDocument('Item', id);
}
listItemsData(type) {
return this.itemTypes[type];
}
filterItems(filter) {
return this.items.filter(filter);
}
getItemOfType(idOrName, type) {
return this.items.find(it => it.id == idOrName && it.type == type)
?? Misc.findFirstLike(idOrName, this.items, { filter: it => it.type == type, description: type });
}
getMonnaie(id) {
return this.getItemOfType(id, 'monnaie');
return this.findItemLike(id, 'monnaie');
}
getTache(id) {
return this.getItemOfType(id, 'tache');
return this.findItemLike(id, 'tache');
}
getMeditation(id) {
return this.getItemOfType(id, 'meditation');
return this.findItemLike(id, 'meditation');
}
getChant(id) {
return this.getItemOfType(id, 'chant');
return this.findItemLike(id, 'chant');
}
getDanse(id) {
return this.getItemOfType(id, 'danse');
return this.findItemLike(id, 'danse');
}
getMusique(id) {
return this.getItemOfType(id, 'musique');
return this.findItemLike(id, 'musique');
}
getOeuvre(id, type = 'oeuvre') {
return this.getItemOfType(id, type);
return this.findItemLike(id, type);
}
getJeu(id) {
return this.getItemOfType(id, 'jeu');
return this.findItemLike(id, 'jeu');
}
getRecetteCuisine(id) {
return this.getItemOfType(id, 'recettecuisine');
return this.findItemLike(id, 'recettecuisine');
}
/* -------------------------------------------- */
getDraconicList() {
@ -476,6 +363,7 @@ export class RdDActor extends Actor {
);
dialog.render(true);
}
async repos() {
await DialogRepos.create(this);
}
@ -1109,7 +997,7 @@ export class RdDActor extends Actor {
_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 item.system.contenu) {
let subObjet = this.getObjet(id);
let subObjet = this.getItem(id);
if (subObjet?.id == conteneur.id) {
return true; // Loop detected !
}
@ -1130,17 +1018,17 @@ export class RdDActor extends Actor {
if (objet.type != 'conteneur') {
return Number(tplData.encombrement) * Number(tplData.quantite);
}
const encContenus = tplData.contenu.map(idContenu => this.getRecursiveEnc(this.getObjet(idContenu)));
const encContenus = tplData.contenu.map(idContenu => this.getRecursiveEnc(this.getItem(idContenu)));
return encContenus.reduce(Misc.sum(), 0)
+ Number(tplData.encombrement) /* TODO? Number(tplData.quantite) -- on pourrait avoir plusieurs conteneurs...*/
}
/* -------------------------------------------- */
buildSubConteneurObjetList(conteneurId, deleteList) {
let conteneur = this.getObjet(conteneurId);
let conteneur = this.getItem(conteneurId);
if (conteneur?.type == 'conteneur') { // Si c'est un conteneur
for (let subId of conteneur.system.contenu) {
let subObj = this.getObjet(subId);
let subObj = this.getItem(subId);
if (subObj) {
if (subObj.type == 'conteneur') {
this.buildSubConteneurObjetList(subId, deleteList);
@ -1228,12 +1116,12 @@ export class RdDActor extends Actor {
return false;
}
let result = true;
const item = this.getObjet(itemId);
const item = this.getItem(itemId);
if (item?.isInventaire() && sourceActorId == targetActorId) {
// rangement
if (srcId != destId && itemId != destId) { // déplacement de l'objet
const src = this.getObjet(srcId);
const dest = this.getObjet(destId);
const src = this.getItem(srcId);
const dest = this.getItem(destId);
const cible = this.getContenantOrParent(dest);
const [empilable, message] = item.isInventaireEmpilable(dest);
if (empilable) {
@ -1303,7 +1191,7 @@ export class RdDActor extends Actor {
itemsList.push({ id: itemId, conteneurId: undefined }); // Init list
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
const itemsDataToCreate = itemsList.map(it => sourceActor.getObjet(it.id))
const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
.map(it => duplicate(it))
.map(it => { it.system.contenu = []; return it; });
let newItems = await this.createEmbeddedDocuments('Item', itemsDataToCreate);
@ -1314,7 +1202,7 @@ export class RdDActor extends Actor {
// gestion conteneur/contenu
if (item.conteneurId) { // l'Objet était dans un conteneur
let newConteneurId = itemMap[item.conteneurId]; // Get conteneur
let newConteneur = this.getObjet(newConteneurId);
let newConteneur = this.getItem(newConteneurId);
let newItemId = itemMap[item.id]; // Get newItem
@ -1377,7 +1265,7 @@ export class RdDActor extends Actor {
hasItemNamed(type, name) {
name = Grammar.toLowerCaseNoAccent(name);
return this.listItemsData(type).find(it => Grammar.toLowerCaseNoAccent(it.name) == name);
return this.listItems(type).find(it => Grammar.toLowerCaseNoAccent(it.name) == name);
}
/* -------------------------------------------- */
@ -1444,6 +1332,10 @@ export class RdDActor extends Actor {
}
}
recompute(){
this.computeEtatGeneral();
}
/* -------------------------------------------- */
computeEtatGeneral() {
// Pas d'état général pour les entités forçage à 0
@ -2027,7 +1919,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async consommerNourritureboisson(itemId, choix = { doses: 1, seForcer: false, supprimerSiZero: false }, userId = undefined) {
if (userId != undefined && userId != game.user.id) {
RdDActor.remoteActorCall({
RdDBaseActor.remoteActorCall({
actorId: this.id,
method: 'consommerNourritureboisson',
args: [itemId, choix, userId]
@ -2035,7 +1927,7 @@ export class RdDActor extends Actor {
userId)
return;
}
const item = this.getObjet(itemId)
const item = this.getItem(itemId)
if (!item.isComestible()) {
return;
}
@ -2792,7 +2684,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async rollDanse(id) {
const artData = { art: 'danse', verbe: 'Danser', forceCarac: {} };
const oeuvre = duplicate(this.getItemOfType(id, artData.art));
const oeuvre = duplicate(this.findItemLike(id, artData.art));
if (oeuvre.system.agilite) {
artData.forceCarac['agilite'] = duplicate(this.system.carac.agilite);
}
@ -2814,7 +2706,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async rollMusique(id) {
const artData = { art: 'musique', verbe: 'Jouer' };
const oeuvre = this.getItemOfType(id, artData.art);
const oeuvre = this.findItemLike(id, artData.art);
await this._rollArt(artData, "ouie", oeuvre);
}
@ -2911,7 +2803,7 @@ export class RdDActor extends Actor {
async rollOeuvre(id) {
const artData = { art: 'oeuvre', verbe: 'Interpréter' }
const oeuvre = duplicate(this.getItemOfType(id, artData.art))
const oeuvre = duplicate(this.findItemLike(id, artData.art))
await this._rollArt(artData, oeuvre.system.default_carac, oeuvre)
}
@ -2963,7 +2855,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
_getSignesDraconiques(coord) {
const type = TMRUtility.getTMRType(coord);
return this.listItemsData("signedraconique").filter(it => it.system.typesTMR.includes(type));
return this.listItems("signedraconique").filter(it => it.system.typesTMR.includes(type));
}
/* -------------------------------------------- */
@ -3174,7 +3066,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async resetNombreAstral() {
let toDelete = this.listItemsData('nombreastral');
let toDelete = this.listItems('nombreastral');
const deletions = toDelete.map(it => it._id);
await this.deleteEmbeddedDocuments("Item", deletions);
}
@ -3194,7 +3086,7 @@ export class RdDActor extends Actor {
await this.createEmbeddedDocuments("Item", [item]);
// Suppression des anciens nombres astraux
let toDelete = this.listItemsData('nombreastral').filter(it => it.system.jourindex < game.system.rdd.calendrier.getCurrentDayIndex());
let toDelete = this.listItems('nombreastral').filter(it => it.system.jourindex < game.system.rdd.calendrier.getCurrentDayIndex());
const deletions = toDelete.map(it => it._id);
await this.deleteEmbeddedDocuments("Item", deletions);
@ -3256,7 +3148,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
getSortList() {
return this.listItemsData("sort");
return this.listItems("sort");
}
/* -------------------------------------------- */
@ -3319,7 +3211,7 @@ export class RdDActor extends Actor {
fatigue: RdDUtility.calculFatigueHtml(fatigue, endurance),
draconic: this.getDraconicList(),
sort: this.getSortList(),
signes: this.listItemsData("signedraconique"),
signes: this.listItems("signedraconique"),
caracReve: this.system.carac.reve.value,
pointsReve: this.getReveActuel(),
isRapide: isRapide,
@ -3470,7 +3362,7 @@ export class RdDActor extends Actor {
async validerEncaissement(rollData, show) {
if (ReglesOptionelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
RdDActor.remoteActorCall({
RdDBaseActor.remoteActorCall({
actorId: this.id,
method: 'validerEncaissement',
args: [rollData, show]
@ -3691,7 +3583,7 @@ export class RdDActor extends Actor {
if (depense == 0) {
return;
}
let fortune = Monnaie.getFortune(this);
let fortune = super.getFortune();
console.log("payer", game.user.character, depense, fortune);
let msg = "";
if (fortune >= depense) {
@ -3710,7 +3602,7 @@ export class RdDActor extends Actor {
}
async depenserSols(sols) {
let reste = Monnaie.getFortune(this) - Number(sols);
let reste = super.getFortune() - Number(sols);
if (reste >= 0) {
await Monnaie.optimiserFortune(this, reste);
}
@ -3727,7 +3619,7 @@ export class RdDActor extends Actor {
return;
}
if (fromActorId && !game.user.isGM) {
RdDActor.remoteActorCall({
RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(),
actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId]
@ -3735,7 +3627,7 @@ export class RdDActor extends Actor {
}
else {
const fromActor = game.actors.get(fromActorId)
await Monnaie.optimiserFortune(this, sols + Monnaie.getFortune(this));
await Monnaie.optimiserFortune(this, sols + this.getFortune());
RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({
@ -3761,7 +3653,7 @@ export class RdDActor extends Actor {
return;
}
if (!Misc.isUniqueConnectedGM()) {
RdDActor.remoteActorCall({
RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente',
args: [achat]
@ -3771,21 +3663,21 @@ export class RdDActor extends Actor {
const cout = Number(achat.prixTotal ?? 0);
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
const service = achat.serviceId ? (vendeur?.getObjet(achat.serviceId) ?? game.items.get(achat.serviceId)) : undefined;
const service = achat.serviceId ? (vendeur?.getItem(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));
const itemVendu = vendeur?.getItem(vente.item._id) ?? (await RdDItem.getCorrespondingItem(vente.item));
if (!this.verifierQuantite(service, vente.serviceSubItem, vendeur, itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
return
}
if (Monnaie.getFortune(acheteur) < Number(cout)) {
if ((acheteur?.getFortune() ?? 0) < Number(cout)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
return;
}
await this.decrementerVente(service, vendeur, itemVendu, quantite, cout);
await this.decrementerVente(service, vendeur, itemVendu, quantite, cout);
if (acheteur) {
await acheteur.depenserSols(cout);
let createdItemId = await acheteur.creerQuantiteItem(vente.item, quantite);
@ -3871,7 +3763,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async effectuerTacheAlchimie(recetteId, tacheAlchimie, texteTache) {
let recetteData = this.getItemOfType(recetteId, 'recettealchimique');
let recetteData = this.findItemLike(recetteId, 'recettealchimique');
if (recetteData) {
if (tacheAlchimie != "couleur" && tacheAlchimie != "consistance") {
ui.notifications.warn(`L'étape alchimique ${tacheAlchimie} - ${texteTache} est inconnue`);
@ -4141,7 +4033,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async diminuerQuantiteObjet(id, nb, options = { supprimerSiZero: false }) {
const item = this.getObjet(id);
const item = this.getItem(id);
if (item) {
await item.diminuerQuantite(nb, options);
}