diff --git a/changelog.md b/changelog.md
index 0d2488df..fb32e08b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,6 +6,7 @@
- La récupération d'éthylisme
- La récupération de rêve (y compris fleurs de rêve et Rêves de Dragon: la rencontre a lieu, mais ne donne pas de rêve)
- Séparation des véhicules dans leur propre acteur
+- Séparation des entités dans leur propre acteur
## v11.0.28 - les fractures de Khrachtchoum
- La gravité de la blessure est affichée dans le résumé de l'encaissement
diff --git a/module/actor-sheet.js b/module/actor-sheet.js
index b1716797..57ee0e72 100644
--- a/module/actor-sheet.js
+++ b/module/actor-sheet.js
@@ -11,17 +11,18 @@ import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
-import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js";
+import { RdDBaseActorReveSheet } from "./actor/base-actor-reve-sheet.js";
import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
+import { ChatUtility } from "./chat-utility.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
-export class RdDActorSheet extends RdDBaseActorSheet {
+export class RdDActorSheet extends RdDBaseActorReveSheet {
/** @override */
static get defaultOptions() {
@@ -56,7 +57,7 @@ export class RdDActorSheet extends RdDBaseActorSheet {
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
- surEncombrementMessage: this.actor.getMessageSurEncombrement(),
+ surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "",
malusArmure: this.actor.getMalusArmure()
})
@@ -115,7 +116,8 @@ export class RdDActorSheet extends RdDBaseActorSheet {
return formData;
}
- /* -------------------------------------------- */ /** @override */
+ /* -------------------------------------------- */
+ /** @override */
activateListeners(html) {
super.activateListeners(html);
@@ -124,10 +126,10 @@ export class RdDActorSheet extends RdDBaseActorSheet {
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
- this.html.find('.item-action').click(async event => {
- const item = RdDSheetUtility.getItem(event, this.actor);
- item?.actionPrincipale(this.actor, async () => this.render())
- });
+ this.html.find('.sheet-possession-attack').click(async event => {
+ const poss = RdDSheetUtility.getItem(event, this.actor)
+ this.actor.conjurerPossession(poss)
+ })
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
@@ -158,18 +160,6 @@ export class RdDActorSheet extends RdDBaseActorSheet {
this.actor.updateCompteurValue("experience", parseInt(event.target.value));
});
- this.html.find('.encaisser-direct').click(async event => {
- this.actor.encaisser();
- })
- this.html.find('.sheet-possession-attack').click(async event => {
- const poss = RdDSheetUtility.getItem(event, this.actor)
- this.actor.conjurerPossession(poss)
- })
- this.html.find('.remise-a-neuf').click(async event => {
- if (game.user.isGM) {
- this.actor.remiseANeuf();
- }
- });
this.html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
@@ -206,11 +196,6 @@ export class RdDActorSheet extends RdDBaseActorSheet {
});
// Roll Carac
- this.html.find('.carac-label a').click(async event => {
- let caracName = event.currentTarget.attributes.name.value;
- this.actor.rollCarac(caracName.toLowerCase());
- });
-
this.html.find('.chance-actuelle').click(async event => {
this.actor.rollCarac('chance-actuelle');
});
@@ -219,14 +204,10 @@ export class RdDActorSheet extends RdDBaseActorSheet {
this.actor.rollAppelChance();
});
+ // Roll Skill
this.html.find('[name="jet-astrologie"]').click(async event => {
this.actor.astrologieNombresAstraux();
});
-
- // Roll Skill
- this.html.find('a.competence-label').click(async event => {
- this.actor.rollCompetence(RdDSheetUtility.getItemId(event));
- });
this.html.find('.tache-label a').click(async event => {
this.actor.rollTache(RdDSheetUtility.getItemId(event));
});
@@ -292,35 +273,12 @@ export class RdDActorSheet extends RdDBaseActorSheet {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
}
});
- // Display TMR, visualisation
- this.html.find('.visu-tmr').click(async event => {
- this.actor.displayTMR("visu");
- });
+ // Display TMR
+ this.html.find('.visu-tmr').click(async event => { this.actor.displayTMR("visu") })
+ this.html.find('.monte-tmr').click(async event => { this.actor.displayTMR("normal") })
+ this.html.find('.monte-tmr-rapide').click(async event => { this.actor.displayTMR("rapide") })
- // Display TMR, normal
- this.html.find('.monte-tmr').click(async event => {
- this.actor.displayTMR("normal");
- });
-
- // Display TMR, fast
- this.html.find('.monte-tmr-rapide').click(async event => {
- this.actor.displayTMR("rapide");
- });
-
- this.html.find('.repos').click(async event => {
- await this.actor.repos();
- });
- this.html.find('.delete-active-effect').click(async event => {
- if (game.user.isGM) {
- let effect = this.html.find(event.currentTarget).parents(".active-effect").data('effect');
- this.actor.removeEffect(effect);
- }
- });
- this.html.find('.enlever-tous-effets').click(async event => {
- if (game.user.isGM) {
- await this.actor.removeEffects();
- }
- });
+ this.html.find('.repos').click(async event => { await this.actor.repos() })
this.html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
@@ -334,30 +292,20 @@ export class RdDActorSheet extends RdDBaseActorSheet {
if (this.options.vueDetaillee) {
// On carac change
- this.html.find('.carac-value').change(async event => {
- let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
- this.actor.updateCarac(caracName, parseInt(event.target.value));
- });
this.html.find('input.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "");
this.actor.updateCaracXP(caracName, parseInt(event.target.value));
});
- // On competence change
- this.html.find('.competence-value').change(async event => {
- let compName = event.currentTarget.attributes.compname.value;
- //console.log("Competence changed :", compName);
- this.actor.updateCompetence(compName, parseInt(event.target.value));
- });
// On competence xp change
this.html.find('input.competence-xp').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXP(compName, parseInt(event.target.value));
});
- // On competence xp change
this.html.find('input.competence-xp-sort').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
+
this.html.find('.toggle-archetype').click(async event => {
this.options.vueArchetype = !this.options.vueArchetype;
this.render(true);
@@ -394,11 +342,6 @@ export class RdDActorSheet extends RdDBaseActorSheet {
this.actor.setPointsDeSeuil(event.currentTarget.value);
});
- // On stress change
- this.html.find('.compteur-edit').change(async event => {
- let fieldName = event.currentTarget.attributes.name.value;
- this.actor.updateCompteurValue(fieldName, parseInt(event.target.value));
- });
this.html.find('.stress-test').click(async event => {
this.actor.transformerStress();
@@ -429,12 +372,6 @@ export class RdDActorSheet extends RdDBaseActorSheet {
this.html.find('.vie-moins').click(async event => {
this.actor.santeIncDec("vie", -1);
});
- this.html.find('.endurance-plus').click(async event => {
- this.actor.santeIncDec("endurance", 1);
- });
- this.html.find('.endurance-moins').click(async event => {
- this.actor.santeIncDec("endurance", -1);
- });
this.html.find('.ptreve-actuel-plus').click(async event => {
this.actor.reveActuelIncDec(1);
});
diff --git a/module/actor.js b/module/actor.js
index e0b43328..547e55d5 100644
--- a/module/actor.js
+++ b/module/actor.js
@@ -1,4 +1,4 @@
-import { RdDUtility } from "./rdd-utility.js";
+import { MAX_ENDURANCE_FATIGUE, RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { RdDRollDialogEthylisme } from "./rdd-roll-ethylisme.js";
import { RdDRoll } from "./rdd-roll.js";
@@ -10,13 +10,9 @@ import { RdDRollTables } from "./rdd-rolltables.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDItemSort } from "./item-sort.js";
import { Grammar } from "./grammar.js";
-import { RdDEncaisser } from "./rdd-roll-encaisser.js";
-import { RdDCombat } from "./rdd-combat.js";
import { RdDItemCompetence } from "./item-competence.js";
-import { RdDItemArme } from "./item-arme.js";
import { RdDAlchimie } from "./rdd-alchimie.js";
-import { STATUSES, StatusEffects } from "./settings/status-effects.js";
-import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
+import { STATUSES } from "./settings/status-effects.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
@@ -26,11 +22,9 @@ import { DialogConsommer } from "./dialog-item-consommer.js";
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDPossession } from "./rdd-possession.js";
-import { ENTITE_INCARNE, ENTITE_NONINCARNE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
+import { SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDConfirm } from "./rdd-confirm.js";
-import { DialogValidationEncaissement } from "./dialog-validation-encaissement.js";
import { RdDRencontre } from "./item/rencontre.js";
-import { Targets } from "./targets.js";
import { DialogRepos } from "./sommeil/dialog-repos.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
@@ -39,6 +33,7 @@ import { AppAstrologie } from "./sommeil/app-astrologie.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { ExperienceLog, XP_TOPIC } from "./actor/experience-log.js";
import { TYPES } from "./item.js";
+import { RdDBaseActorVivant } from "./actor/base-actor-vivant.js";
const POSSESSION_SANS_DRACONIC = {
img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp',
@@ -56,57 +51,59 @@ 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 RdDBaseActor {
-
- /* -------------------------------------------- */
- prepareData() {
- super.prepareData();
-
- // 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.
- if (this.isPersonnage()) this._prepareCharacterData(this)
- if (this.isCreatureEntite()) this._prepareCreatureData(this)
- this.recompute();
- }
-
- /* -------------------------------------------- */
- _prepareCreatureData(actorData) {
- this.computeEncTotal();
- }
+export class RdDActor extends RdDBaseActorVivant {
/* -------------------------------------------- */
/**
* Prepare Character type specific data
*/
- async _prepareCharacterData(actorData) {
+ prepareActorData() {
+ // TODO: separate derived/base data preparation
+ // TODO: split by actor class
+ if (this.isPersonnage()) this.$prepareCharacterData()
+ }
+
+ $prepareCharacterData() {
// Initialize empty items
- RdDCarac.computeCarac(actorData.system)
+ this.$computeCaracDerivee()
this.computeIsHautRevant();
- await this.cleanupConteneurs();
- await this.computeEncTotal();
+ this.cleanupConteneurs();
}
/* -------------------------------------------- */
- 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)
- }
+ $computeCaracDerivee() {
+ this.system.carac.force.value = Math.min(this.system.carac.force.value, parseInt(this.system.carac.taille.value) + 4);
+
+ this.system.carac.derobee.value = Math.floor(parseInt(((21 - this.system.carac.taille.value)) + parseInt(this.system.carac.agilite.value)) / 2);
+ let bonusDomKey = Math.floor((parseInt(this.system.carac.force.value) + parseInt(this.system.carac.taille.value)) / 2);
+ let tailleData = RdDCarac.getCaracDerivee(bonusDomKey);
+ this.system.attributs.plusdom.value = tailleData.plusdom;
+
+ this.system.attributs.sconst.value = RdDCarac.calculSConst(this.system.carac.constitution.value);
+ this.system.attributs.sust.value = RdDCarac.getCaracDerivee(this.system.carac.taille.value).sust;
+
+ this.system.attributs.encombrement.value = (parseInt(this.system.carac.force.value) + parseInt(this.system.carac.taille.value)) / 2;
+ this.system.carac.melee.value = Math.floor((parseInt(this.system.carac.force.value) + parseInt(this.system.carac.agilite.value)) / 2);
+ this.system.carac.tir.value = Math.floor((parseInt(this.system.carac.vue.value) + parseInt(this.system.carac.dexterite.value)) / 2);
+ this.system.carac.lancer.value = Math.floor((parseInt(this.system.carac.tir.value) + parseInt(this.system.carac.force.value)) / 2);
+
+ this.system.sante.vie.max = Math.ceil((parseInt(this.system.carac.taille.value) + parseInt(this.system.carac.constitution.value)) / 2);
+
+ this.system.sante.vie.value = Math.min(this.system.sante.vie.value, this.system.sante.vie.max)
+ this.system.sante.endurance.max = Math.max(parseInt(this.system.carac.taille.value) + parseInt(this.system.carac.constitution.value), parseInt(this.system.sante.vie.max) + parseInt(this.system.carac.volonte.value));
+ this.system.sante.endurance.value = Math.min(this.system.sante.endurance.value, this.system.sante.endurance.max);
+ this.system.sante.fatigue.max = this.$getFatigueMax();
+ this.system.sante.fatigue.value = Math.min(this.system.sante.fatigue.value, this.system.sante.fatigue.max);
+
+ //Compteurs
+ this.system.reve.reve.max = this.system.carac.reve.value;
+ this.system.compteurs.chance.max = this.system.carac.chance.value;
}
canReceive(item) {
if (this.isCreature()) {
return item.type == TYPES.competencecreature || item.isInventaire();
}
- if (this.isEntite()) {
- return item.type == 'competencecreature';
- }
if (this.isPersonnage()) {
switch (item.type) {
case 'competencecreature': case 'tarot': case 'service':
@@ -121,27 +118,13 @@ export class RdDActor extends RdDBaseActor {
isHautRevant() {
return this.isPersonnage() && this.system.attributs.hautrevant.value != ""
}
- /* -------------------------------------------- */
- getFatigueActuelle() {
- if (ReglesOptionnelles.isUsing("appliquer-fatigue") && this.isPersonnage()) {
- return this.system.sante.fatigue?.value;
- }
- return 0;
- }
- /* -------------------------------------------- */
- getFatigueMax() {
- if (!this.isPersonnage()) {
- return 1;
- }
- return Misc.toInt(this.system.sante.fatigue?.max);
- }
+
/* -------------------------------------------- */
getReveActuel() {
switch (this.type) {
case 'personnage':
return Misc.toInt(this.system.reve?.reve?.value ?? this.carac.reve.value);
case 'creature':
- case 'entite':
return Misc.toInt(this.system.carac.reve?.value)
default:
return 0;
@@ -158,9 +141,6 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
getForce() {
- if (this.isEntite()) {
- return Misc.toInt(this.system.carac.reve?.value);
- }
return Misc.toInt(this.system.carac.force?.value);
}
/* -------------------------------------------- */
@@ -168,7 +148,6 @@ export class RdDActor extends RdDBaseActor {
switch (this.type) {
case 'personnage': return Misc.toInt(this.system.carac.agilite?.value);
case 'creature': return Misc.toInt(this.system.carac.force?.value);
- case 'entite': return Misc.toInt(this.system.carac.reve?.value);
}
return 10;
}
@@ -214,28 +193,6 @@ export class RdDActor extends RdDBaseActor {
return 0;
}
- /* -------------------------------------------- */
- getEncTotal() {
- return Math.floor(this.encTotal ?? 0);
- }
-
- /* -------------------------------------------- */
- getCompetence(idOrName, options = {}) {
- if (idOrName instanceof Item) {
- return idOrName.isCompetence() ? idOrName : undefined
- }
- return RdDItemCompetence.findCompetence(this.items, idOrName, options)
- }
-
- getCompetences(name) {
- return RdDItemCompetence.findCompetences(this.items, name)
- }
- getCompetenceCorpsACorps(options = {}) {
- return this.getCompetence("Corps à corps", options)
- }
- getCompetencesEsquive() {
- return this.getCompetences("esquive")
- }
/* -------------------------------------------- */
getTache(id) {
return this.findItemLike(id, 'tache');
@@ -285,15 +242,6 @@ export class RdDActor extends RdDBaseActor {
return draconics[0];
}
- getPossession(possessionId) {
- return this.itemTypes[TYPES.possession].find(it => it.system.possessionid == possessionId);
- }
- getPossessions() {
- return this.itemTypes[TYPES.possession];
- }
- getEmpoignades() {
- return this.itemTypes[TYPES.empoignade];
- }
getDemiReve() {
return this.system.reve.tmrpos.coord;
}
@@ -322,20 +270,6 @@ export class RdDActor extends RdDBaseActor {
}
}
- /* -------------------------------------------- */
- getSurprise(isCombat = undefined) {
- let niveauSurprise = this.getEffects()
- .map(effect => StatusEffects.valeurSurprise(effect, isCombat))
- .reduce(Misc.sum(), 0);
- if (niveauSurprise > 1) {
- return 'totale';
- }
- if (niveauSurprise == 1) {
- return 'demi';
- }
- return '';
- }
-
/* -------------------------------------------- */
hasArmeeMeleeEquipee() { // Return true si l'acteur possède au moins 1 arme de mêlée équipée
return this.itemTypes['arme'].find(it => it.system.equipe && it.system.competence != "")
@@ -351,7 +285,7 @@ export class RdDActor extends RdDBaseActor {
'chance-actuelle': this.getCaracChanceActuelle()
});
- await this._openRollDialog({
+ await this.openRollDialog({
name: `jet-${this.id}`,
label: `Jet de ${this.name}`,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll.html',
@@ -365,23 +299,6 @@ export class RdDActor extends RdDBaseActor {
});
}
- async _openRollDialog({ name, label, template, rollData, callbackAction }) {
- const dialog = await RdDRoll.create(this, rollData,
- { html: template, close: html => { this.tmrApp?.restoreTMRAfterAction() } },
- {
- name: name,
- label: label,
- callbacks: [
- this.createCallbackExperience(),
- this.createCallbackAppelAuMoral(),
- { action: callbackAction }
- ]
- });
- dialog.render(true);
- return dialog
- }
-
-
async prepareChateauDormant(consigne) {
if (consigne.ignorer) {
return;
@@ -606,9 +523,6 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async remiseANeuf() {
- if (this.isEntite([ENTITE_NONINCARNE])) {
- return;
- }
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: 'Remise à neuf de ' + this.name
@@ -701,7 +615,7 @@ export class RdDActor extends RdDBaseActor {
return 'eveil';
}
else {
- if (!ReglesOptionnelles.isUsing("recuperation-reve")) {
+ if (!ReglesOptionnelles.isUsing("recuperation-reve")) {
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `Pas de récupération de rêve (${reve} points ignorés)`
@@ -751,7 +665,7 @@ export class RdDActor extends RdDBaseActor {
async recupererFatigue(message) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
let fatigue = this.system.sante.fatigue.value;
- const fatigueMin = this._computeFatigueMin();
+ const fatigueMin = this.$getFatigueMin();
if (fatigue <= fatigueMin) {
return;
}
@@ -878,7 +792,7 @@ export class RdDActor extends RdDBaseActor {
this.setPointsDeChance(to);
}
}
- let selectedCarac = RdDActor._findCaracByName(this.system.carac, caracName);
+ let selectedCarac = RdDBaseActorVivant._findCaracByName(this.system.carac, caracName);
const from = selectedCarac.value
await this.update({ [`system.carac.${caracName}.value`]: to });
await ExperienceLog.add(this, XP_TOPIC.CARAC, from, to, caracName);
@@ -889,7 +803,7 @@ export class RdDActor extends RdDBaseActor {
if (caracName == 'Taille') {
return;
}
- let selectedCarac = RdDActor._findCaracByName(this.system.carac, caracName);
+ let selectedCarac = RdDBaseActorVivant._findCaracByName(this.system.carac, caracName);
if (!selectedCarac.derivee) {
const from = Number(selectedCarac.xp);
await this.update({ [`system.carac.${caracName}.xp`]: to });
@@ -903,7 +817,7 @@ export class RdDActor extends RdDBaseActor {
if (caracName == 'Taille') {
return;
}
- let carac = RdDActor._findCaracByName(this.system.carac, caracName);
+ let carac = RdDBaseActorVivant._findCaracByName(this.system.carac, caracName);
if (carac) {
carac = duplicate(carac);
const fromXp = Number(carac.xp);
@@ -1117,18 +1031,12 @@ export class RdDActor extends RdDBaseActor {
await this.update({ [`system.attributs.${fieldName}.value`]: fieldValue });
}
-
-
isSurenc() {
return this.isPersonnage() ? (this.computeMalusSurEncombrement() < 0) : false
}
/* -------------------------------------------- */
computeMalusSurEncombrement() {
- switch (this.type) {
- case 'entite':
- return 0;
- }
return Math.min(0, Math.floor(this.getEncombrementMax() - this.encTotal));
}
@@ -1138,12 +1046,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
getEncombrementMax() {
- switch (this.type) {
- case 'entite':
- return 0;
- default:
- return this.system.attributs.encombrement.value
- }
+ return this.system.attributs.encombrement.value
}
/* -------------------------------------------- */
@@ -1184,12 +1087,7 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
- recompute() {
- if (this.type == 'entite') {
- // Pas d'état général pour les entités forçage à 0
- this.system.compteurs.etat.value = 0;
- return
- }
+ async computeEtatGeneral() {
this.system.compteurs.etat.value = this.$malusVie() + this.$malusFatigue() + this.$malusEthylisme();
}
@@ -1202,19 +1100,42 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
- $malusFatigue() {
- if (ReglesOptionnelles.isUsing("appliquer-fatigue") && this.system.sante.fatigue) {
- const max = Math.max(1, Math.min(this.system.sante.endurance.max, 60));
+ getEnduranceActuelle() {
+ return Number(this.system.sante.endurance.value);
+ }
- let fatigueTab = RdDUtility.getSegmentsFatigue(max);
- let reste = Math.min(max * 2, Math.max(0, this.system.sante.fatigue.value));
- for (let idx = 0; idx < fatigueTab.length; idx++) {
- reste -= fatigueTab[idx];
- if (reste <= 0) {
- return fatigueMalus[idx];
- }
- }
- return -7; // This is the max !
+ getFatigueActuelle() {
+ if (ReglesOptionnelles.isUsing("appliquer-fatigue") && this.isPersonnage()) {
+ return Math.max(0, Math.min(this.$getFatigueMax(), this.system.sante.fatigue?.value));
+ }
+ return 0;
+ }
+
+ getFatigueRestante() {
+ return this.$getFatigueMax() - this.getFatigueActuelle();
+ }
+
+ getFatigueMax() {
+ return this.isPersonnage() ? this.$getFatigueMax() : 1;
+ }
+
+ $getFatigueMin() {
+ return this.system.sante.endurance.max - this.system.sante.endurance.value;
+ }
+
+ $getFatigueMax() {
+ return this.$getEnduranceMax() * 2;
+ }
+
+ $getEnduranceMax() {
+ return Math.max(1, Math.min(this.system.sante.endurance.max, MAX_ENDURANCE_FATIGUE));
+ }
+
+ $malusFatigue() {
+ if (ReglesOptionnelles.isUsing("appliquer-fatigue") && this.isPersonnage()) {
+ const fatigueMax = this.$getFatigueMax();
+ const fatigue = this.getFatigueActuelle();
+ return RdDUtility.calculMalusFatigue(fatigue, this.$getEnduranceMax())
}
return 0;
}
@@ -1436,9 +1357,6 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
async setSonne(sonne = true) {
- if (this.isEntite()) {
- return;
- }
if (!game.combat && sonne) {
ui.notifications.info("Le personnage est hors combat, il ne reste donc pas sonné");
return;
@@ -1448,9 +1366,6 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
getSConst() {
- if (this.isEntite()) {
- return 0;
- }
return RdDCarac.calculSConst(this.system.carac.constitution.value)
}
@@ -1554,7 +1469,7 @@ export class RdDActor extends RdDBaseActor {
result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max));
//console.log("New value ", inc, minValue, result.newValue);
let fatigue = 0;
- if (name == "endurance" && !this.isEntite()) {
+ if (name == "endurance") {
if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie
sante.vie.value--;
result.perteVie = true;
@@ -1580,23 +1495,18 @@ export class RdDActor extends RdDBaseActor {
compteur.value = result.newValue;
// If endurance lost, then the same amount of fatigue cannot be recovered
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
- sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this._computeFatigueMin());
+ sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this.$getFatigueMin());
}
await this.update({ "system.sante": sante })
if (this.isDead()) {
await this.setEffect(STATUSES.StatusComma, true);
}
- return result;
+ return result
}
isDead() {
- return !this.isEntite() && this.system.sante.vie.value < -this.getSConst()
- }
-
- /* -------------------------------------------- */
- _computeFatigueMin() {
- return this.system.sante.endurance.max - this.system.sante.endurance.value;
+ return this.system.sante.vie.value < -this.getSConst()
}
/* -------------------------------------------- */
@@ -2003,7 +1913,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async checkCaracXP(caracName, display = true) {
- let carac = RdDActor._findCaracByName(this.system.carac, caracName);
+ let carac = RdDBaseActorVivant._findCaracByName(this.system.carac, caracName);
if (carac && carac.xp > 0) {
const niveauSuivant = Number(carac.value) + 1;
let xpNeeded = RdDCarac.getCaracNextXp(niveauSuivant);
@@ -2152,7 +2062,7 @@ export class RdDActor extends RdDBaseActor {
const draconicList = this.computeDraconicAndSortIndex(sorts);
const reve = duplicate(this.system.carac.reve);
- const dialog = await this._openRollDialog({
+ const dialog = await this.openRollDialog({
name: 'lancer-un-sort',
label: 'Lancer un sort',
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
@@ -2196,7 +2106,7 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
- getTMRFatigue() { // Pour l'instant uniquement Inertie Draconique
+ getCoutFatigueTMR() { // Pour l'instant uniquement Inertie Draconique
let countInertieDraconique = EffetsDraconiques.countInertieDraconique(this);
if (countInertieDraconique > 0) {
ChatMessage.create({
@@ -2272,29 +2182,6 @@ export class RdDActor extends RdDBaseActor {
}
}
- /* -------------------------------------------- */
- async rollCarac(caracName, jetResistance = undefined) {
- RdDEmpoignade.checkEmpoignadeEnCours(this)
- let selectedCarac = this.getCaracByName(caracName)
- await this._openRollDialog({
- name: 'jet-' + caracName,
- label: 'Jet ' + Grammar.apostrophe('de', selectedCarac.label),
- template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
- rollData: {
- selectedCarac: selectedCarac,
- competences: this.itemTypes['competence'],
- jetResistance: jetResistance ? caracName : undefined
- },
- callbackAction: r => this.$onRollCaracResult(r)
- });
- }
-
- /* -------------------------------------------- */
- async $onRollCaracResult(rollData) {
- // Final chat message
- await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-general.html');
- }
-
/**
* Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
* @param {*} caracName
@@ -2340,45 +2227,6 @@ export class RdDActor extends RdDBaseActor {
}
}
- /* -------------------------------------------- */
- async rollCompetence(idOrName, options = { tryTarget: true }) {
- RdDEmpoignade.checkEmpoignadeEnCours(this)
- const competence = this.getCompetence(idOrName);
- let rollData = { carac: this.system.carac, competence: competence }
- if (competence.type == TYPES.competencecreature) {
- const arme = RdDItemCompetenceCreature.armeCreature(competence)
- if (arme && options.tryTarget && Targets.hasTargets()) {
- Targets.selectOneToken(target => {
- if (arme.action == "possession") {
- RdDPossession.onAttaquePossession(target, this, competence)
- }
- else {
- RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
- }
- });
- return;
- }
- // Transformer la competence de créature
- RdDItemCompetenceCreature.setRollDataCreature(rollData)
- }
-
- await this._openRollDialog({
- name: 'jet-competence',
- label: 'Jet ' + Grammar.apostrophe('de', competence.name),
- template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
- rollData: rollData,
- callbackAction: r => this.$onRollCompetence(r, options)
- });
- }
-
- /* -------------------------------------------- */
- async $onRollCompetence(rollData, options) {
- await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html')
- if (options?.onRollAutomate) {
- options.onRollAutomate(rollData);
- }
- }
-
/* -------------------------------------------- */
async creerTacheDepuisLivre(item, options = { renderSheet: true }) {
const nomTache = "Lire " + item.name;
@@ -2405,7 +2253,6 @@ export class RdDActor extends RdDBaseActor {
}
blessuresASoigner() {
- // TODO or not TODO: filtrer les blessures poour lesquels on ne peut plus faire de premiers soins?
return this.filterItems(it => it.system.gravite > 0 && it.system.gravite <= 6 && !(it.system.premierssoins.done && it.system.soinscomplets.done), 'blessure')
}
@@ -2423,7 +2270,7 @@ export class RdDActor extends RdDBaseActor {
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(compName);
- await this._openRollDialog({
+ await this.openRollDialog({
name: 'jet-competence',
label: 'Jet ' + Grammar.apostrophe('de', competence.name),
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
@@ -2447,7 +2294,7 @@ export class RdDActor extends RdDBaseActor {
const compData = this.getCompetence(tacheData.system.competence)
compData.system.defaut_carac = tacheData.system.carac; // Patch !
- await this._openRollDialog({
+ await this.openRollDialog({
name: 'jet-competence',
label: 'Jet de Tâche ' + tacheData.name,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
@@ -2510,7 +2357,7 @@ export class RdDActor extends RdDBaseActor {
artData.forceCarac[selected] = duplicate(this.system.carac[selected]);
}
- await this._openRollDialog({
+ await this.openRollDialog({
name: `jet-${artData.art}`,
label: `${artData.verbe} ${oeuvre.name}`,
template: `systems/foundryvtt-reve-de-dragon/templates/dialog-roll-${oeuvre.type}.html`,
@@ -2750,7 +2597,7 @@ export class RdDActor extends RdDBaseActor {
const dialog = await RdDRoll.create(this, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-signedraconique.html',
- close: html => { this.tmrApp?.restoreTMRAfterAction() }
+ close: async html => await this._onCloseRollDialog(html)
},
{
name: 'lire-signe-draconique',
@@ -2765,6 +2612,10 @@ export class RdDActor extends RdDBaseActor {
this.tmrApp?.setTMRPendingAction(dialog);
}
+ async _onCloseRollDialog(html) {
+ this.tmrApp?.restoreTMRAfterAction()
+ }
+
/* -------------------------------------------- */
async _rollLireSigneDraconique(rollData) {
const compData = rollData.competence;
@@ -2786,7 +2637,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async rollAppelChance(onSuccess = () => { }, onEchec = () => { }) {
- await this._openRollDialog({
+ await this.openRollDialog({
name: 'appelChance',
label: 'Appel à la chance',
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
@@ -2838,7 +2689,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
ajustementAstrologique() {
- if (this.isCreatureEntite()) {
+ if (this.isCreature()) {
return 0;
}
// selon l'heure de naissance...
@@ -2904,7 +2755,7 @@ export class RdDActor extends RdDBaseActor {
async _xpCarac(xpData) {
if (xpData.xpCarac > 0) {
let carac = duplicate(this.system.carac);
- let selectedCarac = RdDActor._findCaracByName(carac, xpData.caracName);
+ let selectedCarac = RdDBaseActorVivant._findCaracByName(carac, xpData.caracName);
if (!selectedCarac.derivee) {
const from = Number(selectedCarac.xp);
const to = from + xpData.xpCarac;
@@ -2957,51 +2808,6 @@ export class RdDActor extends RdDBaseActor {
await AppAstrologie.create(this);
}
- /* -------------------------------------------- */
- 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 RdDActor._findCaracByName(this.system.carac, name);
- }
-
- getCaracChanceActuelle() {
- return {
- label: 'Chance actuelle',
- value: this.getChanceActuel(),
- type: "number"
- };
- }
-
- getCaracReveActuel() {
- return {
- label: 'Rêve actuel',
- value: this.getReveActuel(),
- type: "number"
- };
- }
-
- /* -------------------------------------------- */
- static _findCaracByName(carac, name) {
- name = Grammar.toLowerCaseNoAccent(name);
- switch (name) {
- case 'reve-actuel': case 'reve actuel':
- return carac.reve;
- case 'chance-actuelle': case 'chance actuelle':
- return carac.chance;
- }
-
- 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;
- }
-
/* -------------------------------------------- */
countMonteeLaborieuse() { // Return +1 par queue/ombre/souffle Montée Laborieuse présente
let countMonteeLaborieuse = EffetsDraconiques.countMonteeLaborieuse(this);
@@ -3076,61 +2882,6 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
- getCompetenceArme(arme, competenceName) {
- switch (arme.type) {
- case TYPES.competencecreature:
- return arme.name
- case TYPES.arme:
- switch (competenceName) {
- case 'competence': return arme.system.competence;
- case 'unemain': return RdDItemArme.competence1Mains(arme);
- case 'deuxmains': return RdDItemArme.competence2Mains(arme);
- case 'tir': return arme.system.tir;
- case 'lancer': return arme.system.lancer;
- }
- }
- return undefined
- }
-
- /* -------------------------------------------- */
- /**
- *
- * @param {*} arme item d'arme/compétence de créature
- * @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
- * @returns
- */
- rollArme(arme, categorieArme = "competence") {
- let compToUse = this.getCompetenceArme(arme, categorieArme)
- if (!Targets.hasTargets()) {
- RdDConfirm.confirmer({
- settingConfirmer: "confirmer-combat-sans-cible",
- content: `
Voulez vous faire un jet de ${compToUse} sans choisir de cible valide?
-
Tous les jets de combats devront être gérés à la main
-
`,
- title: 'Ne pas utiliser les automatisation de combat',
- buttonLabel: "Pas d'automatisation",
- onAction: async () => {
- this.rollCompetence(compToUse, { tryTarget: false })
- }
- });
- return;
- }
-
- Targets.selectOneToken(target => {
- if (Targets.isTargetEntite(target)) {
- ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
- return;
- }
-
- const competence = this.getCompetence(compToUse)
- //console.log("RollArme", competence, arme)
- if (competence.isCompetencePossession()) {
- return RdDPossession.onAttaquePossession(target, this, competence);
- }
- RdDCombat.rddCombatTarget(target, this).attaque(competence, arme);
- })
- }
-
async rollSoins(blesse, blessureId) {
const blessure = blesse.blessuresASoigner().find(it => it.id == blessureId);
if (blessure) {
@@ -3207,12 +2958,6 @@ export class RdDActor extends RdDBaseActor {
RdDPossession.onConjurerPossession(this, possession)
}
- /* -------------------------------------------- */
- getArmeParade(armeParadeId) {
- const item = armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined;
- return RdDItemArme.getArme(item);
- }
-
/* -------------------------------------------- */
verifierForceMin(item) {
if (item.type == 'arme' && item.system.force > this.system.carac.force.value) {
@@ -3229,7 +2974,7 @@ export class RdDActor extends RdDBaseActor {
if (item?.isEquipable()) {
const isEquipe = !item.system.equipe;
await this.updateEmbeddedDocuments('Item', [{ _id: item.id, "system.equipe": isEquipe }]);
- this.computeEncTotal(); // Mise à jour encombrement
+ this.computeEncTotal();
if (isEquipe)
this.verifierForceMin(item);
}
@@ -3264,79 +3009,24 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
- async encaisser() {
- await RdDEncaisser.encaisser(this);
- }
-
- /* -------------------------------------------- */
- async encaisserDommages(rollData, attacker = undefined, show = undefined) {
- if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
- return;
- }
- const attackerId = attacker?.id;
- if (ReglesOptionnelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
- RdDBaseActor.remoteActorCall({
- tokenId: this.token?.id,
- actorId: this.id,
- method: 'appliquerEncaissement',
- args: [rollData, show, attackerId]
- });
- return;
- }
- await this.appliquerEncaissement(rollData, show, attackerId);
- }
-
- async appliquerEncaissement(rollData, show, attackerId) {
- const armure = await this.computeArmure(rollData);
- if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
- DialogValidationEncaissement.validerEncaissement(this, rollData, armure, show, attackerId, (encaissement, show, attackerId) => this._appliquerEncaissement(encaissement, show, attackerId));
- }
- else {
- let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
- await this._appliquerEncaissement(encaissement, show, attackerId);
- }
- }
-
- async _appliquerEncaissement(encaissement, show, attackedId) {
- const attacker = attackedId ? game.actors.get(attackedId) : undefined
- let santeOrig = duplicate(this.system.sante);
-
+ async onAppliquerJetEncaissement(encaissement, attacker) {
+ const santeOrig = duplicate(this.system.sante);
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table
- const perteVie = this.isEntite()
- ? { newValue: 0 }
- : await this.santeIncDec("vie", -encaissement.vie);
+ const perteVie = await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
mergeObject(encaissement, {
- alias: this.name,
- hasPlayerOwner: this.hasPlayerOwner,
- resteEndurance: this.system.sante.endurance.value,
+ resteEndurance: perteEndurance.newValue,
sonne: perteEndurance.sonne,
jetEndurance: perteEndurance.jetEndurance,
- endurance: santeOrig.endurance.value - perteEndurance.newValue,
- vie: this.isEntite() ? 0 : (santeOrig.vie.value - perteVie.newValue),
- blessure: blessure,
- show: show ?? {}
+ endurance: perteEndurance.perte,
+ vie: santeOrig.vie.value - perteVie.newValue,
+ blessure: blessure
});
-
- await ChatUtility.createChatWithRollMode(this.name, {
- roll: encaissement.roll,
- content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
- });
-
- if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
- encaissement = duplicate(encaissement);
- encaissement.isGM = true;
- ChatMessage.create({
- whisper: ChatMessage.getWhisperRecipients("GM"),
- content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
- });
- }
}
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attacker = undefined) {
- if (this.isEntite()) return; // Une entité n'a pas de blessures
if (encaissement.gravite < 0) return;
if (encaissement.gravite > 0) {
while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) {
@@ -3347,7 +3037,7 @@ export class RdDActor extends RdDBaseActor {
}
}
}
- const endActuelle = Number(this.system.sante.endurance.value);
+ const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg.loc.label, attacker);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle;
@@ -3392,71 +3082,6 @@ export class RdDActor extends RdDBaseActor {
return itemUse[itemId] ?? 0;
}
- /* -------------------------------------------- */
- /* -- entites -- */
- /* retourne true si on peut continuer, false si on ne peut pas continuer */
- async targetEntiteNonAccordee(target, when = 'avant-encaissement') {
- if (target) {
- return !await this.accorder(target.actor, when);
- }
- return false;
- }
-
- /* -------------------------------------------- */
- async accorder(entite, when = 'avant-encaissement') {
- if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
- || entite == undefined
- || !entite.isEntite([ENTITE_INCARNE])
- || entite.isEntiteAccordee(this)) {
- return true;
- }
- const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.system.carac.niveau.value));
- const rollData = {
- alias: this.name,
- rolled: rolled,
- entite: entite.name,
- selectedCarac: this.system.carac.reve
- };
-
- if (rolled.isSuccess) {
- await entite.setEntiteReveAccordee(this);
- }
-
- await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.html');
- if (rolled.isPart) {
- await this.appliquerAjoutExperience(rollData, true);
- }
- return rolled.isSuccess;
- }
-
- /* -------------------------------------------- */
- isEntite(typeentite = []) {
- return this.type == 'entite' && (typeentite.length == 0 || typeentite.includes(this.system.definition.typeentite));
- }
-
- /* -------------------------------------------- */
- isEntiteAccordee(attaquant) {
- if (!this.isEntite([ENTITE_INCARNE])) { return true; }
- let resonnance = this.system.sante.resonnance;
- return (resonnance.actors.find(it => it == attaquant.id));
- }
-
- /* -------------------------------------------- */
- async setEntiteReveAccordee(attaquant) {
- if (!this.isEntite([ENTITE_INCARNE])) {
- ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entite de cauchemer/rêve");
- return;
- }
- let resonnance = duplicate(this.system.sante.resonnance);
- if (resonnance.actors.find(it => it == attaquant.id)) {
- // déjà accordé
- return;
- }
- resonnance.actors.push(attaquant.id);
- await this.update({ "system.sante.resonnance": resonnance });
- return;
- }
-
/* -------------------------------------------- */
async effectuerTacheAlchimie(recetteId, tacheAlchimie, texteTache) {
let recetteData = this.findItemLike(recetteId, 'recettealchimique');
@@ -3760,43 +3385,8 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
- getEffects(filter = e => true) {
- return this.getEmbeddedCollection("ActiveEffect").filter(filter);
- }
-
- /* -------------------------------------------- */
- getEffect(statusId) {
- return this.getEmbeddedCollection("ActiveEffect").find(it => it.flags?.core?.statusId == statusId);
- }
-
- /* -------------------------------------------- */
- async setEffect(statusId, status) {
- if (this.isEntite()) {
- return;
- }
- console.log("setEffect", statusId, status)
- const effect = this.getEffect(statusId);
- if (!status && effect) {
- await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
- }
- if (status && !effect) {
- await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.status(statusId)]);
- }
- }
-
- async removeEffect(statusId) {
- const effect = this.getEffect(statusId);
- if (effect) {
- await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
- }
- }
-
- /* -------------------------------------------- */
- async removeEffects(filter = e => true) {
- if (game.user.isGM) {
- const ids = this.getEffects(filter).map(it => it.id);
- await this.deleteEmbeddedDocuments('ActiveEffect', ids);
- }
+ isEffectAllowed(statusId) {
+ return true
}
/* -------------------------------------------- */
diff --git a/module/actor/base-actor-reve-sheet.js b/module/actor/base-actor-reve-sheet.js
new file mode 100644
index 00000000..ea074ff1
--- /dev/null
+++ b/module/actor/base-actor-reve-sheet.js
@@ -0,0 +1,81 @@
+import { RdDSheetUtility } from "../rdd-sheet-utility.js";
+import { RdDBaseActorSheet } from "./base-actor-sheet.js";
+
+/* -------------------------------------------- */
+/**
+ * Extend the basic ActorSheet with some very simple modifications
+ * @extends {ActorSheet}
+ */
+export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
+
+ /* -------------------------------------------- */
+ /** @override */
+ activateListeners(html) {
+ super.activateListeners(html);
+
+ // Everything below here is only needed if the sheet is editable
+ if (!this.options.editable) return;
+
+ this.html.find('.item-action').click(async event => {
+ const item = RdDSheetUtility.getItem(event, this.actor);
+ item?.actionPrincipale(this.actor, async () => this.render())
+ });
+
+ this.html.find('.encaisser-direct').click(async event => {
+ this.actor.encaisser();
+ })
+ this.html.find('.remise-a-neuf').click(async event => {
+ if (game.user.isGM) {
+ this.actor.remiseANeuf();
+ }
+ });
+
+ this.html.find('.carac-label a').click(async event => {
+ let caracName = event.currentTarget.attributes.name.value;
+ this.actor.rollCarac(caracName.toLowerCase());
+ });
+
+ this.html.find('a.competence-label').click(async event => {
+ this.actor.rollCompetence(RdDSheetUtility.getItemId(event));
+ });
+
+ this.html.find('.delete-active-effect').click(async event => {
+ if (game.user.isGM) {
+ let effect = this.html.find(event.currentTarget).parents(".active-effect").data('effect');
+ this.actor.removeEffect(effect);
+ }
+ });
+ this.html.find('.enlever-tous-effets').click(async event => {
+ if (game.user.isGM) {
+ await this.actor.removeEffects();
+ }
+ });
+
+ if (this.options.vueDetaillee) {
+ // On carac change
+ this.html.find('.carac-value').change(async event => {
+ let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
+ this.actor.updateCarac(caracName, parseInt(event.target.value));
+ });
+ // On competence change
+ this.html.find('.competence-value').change(async event => {
+ let compName = event.currentTarget.attributes.compname.value;
+ //console.log("Competence changed :", compName);
+ this.actor.updateCompetence(compName, parseInt(event.target.value));
+ });
+ }
+
+ this.html.find('.vue-detaillee').click(async event => {
+ this.options.vueDetaillee = !this.options.vueDetaillee;
+ this.render(true);
+ });
+
+ this.html.find('.endurance-plus').click(async event => {
+ this.actor.santeIncDec("endurance", 1);
+ });
+ this.html.find('.endurance-moins').click(async event => {
+ this.actor.santeIncDec("endurance", -1);
+ });
+ }
+
+}
diff --git a/module/actor/base-actor-reve.js b/module/actor/base-actor-reve.js
new file mode 100644
index 00000000..3409253f
--- /dev/null
+++ b/module/actor/base-actor-reve.js
@@ -0,0 +1,509 @@
+import { ChatUtility } from "../chat-utility.js";
+import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
+import { Grammar } from "../grammar.js";
+import { RdDItemCompetence } from "../item-competence.js";
+import { Misc } from "../misc.js";
+import { RdDEmpoignade } from "../rdd-empoignade.js";
+import { RdDResolutionTable } from "../rdd-resolution-table.js";
+import { RdDEncaisser } from "../rdd-roll-encaisser.js";
+import { RdDRoll } from "../rdd-roll.js";
+import { RdDUtility } from "../rdd-utility.js";
+import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
+import { RdDBaseActor } from "./base-actor.js";
+import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
+import { StatusEffects } from "../settings/status-effects.js";
+import { TYPES } from "../item.js";
+import { Targets } from "../targets.js";
+import { RdDPossession } from "../rdd-possession.js";
+import { RdDCombat } from "../rdd-combat.js";
+import { RdDConfirm } from "../rdd-confirm.js";
+import { ENTITE_INCARNE, SYSTEM_RDD } from "../constants.js";
+import { RdDItemArme } from "../item-arme.js";
+
+const POSSESSION_SANS_DRACONIC = {
+ img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp',
+ name: 'Sans draconic',
+ system: {
+ niveau: 0,
+ defaut_carac: "reve-actuel",
+ }
+};
+
+/**
+ * Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
+ * - Entités de rêve
+ * - Créatures de "sang": créatures et humanoides
+ */
+export class RdDBaseActorReve extends RdDBaseActor {
+
+ getCaracChanceActuelle() {
+ return {
+ label: 'Chance actuelle',
+ value: this.getChanceActuel(),
+ type: "number"
+ };
+ }
+
+ getCaracReveActuel() {
+ return {
+ label: 'Rêve actuel',
+ value: this.getReveActuel(),
+ type: "number"
+ };
+ }
+
+ getReveActuel() { return this.getReve() }
+ getChanceActuel() { return this.getChance() }
+
+ getReve() { return Number(this.system.carac.reve?.value ?? 0) }
+ getForce() { return this.getReve() }
+ getTaille() { return Number(this.system.carac.taille?.value ?? 0) }
+ getAgilite() { return this.getForce() }
+ getChance() { return this.getReve() }
+ getMoralTotal() { return 0 }
+ getBonusDegat() { return Number(this.system.attributs?.plusdom?.value ?? 0) }
+ getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
+ getSConst() { return 0 }
+
+ /* -------------------------------------------- */
+ getEncombrementMax() { return 0 }
+ isSurenc() { return false }
+ computeMalusSurEncombrement() { return 0 }
+
+ ajustementAstrologique() { return 0 }
+ getMalusArmure() { return 0 }
+
+ getEnduranceActuelle() {
+ return Number(this.system.sante?.endurance?.value ?? 0);
+ }
+ async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } }
+ isDead() { return false }
+ blessuresASoigner() { return [] }
+ getEtatGeneral(options = { ethylisme: false }) { return 0 }
+
+ async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
+ async remiseANeuf() { }
+ async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
+
+ async santeIncDec(name, inc, isCritique = false) { }
+
+ async finDeRound(options = { terminer: false }) {
+ await this.$finDeRoundSuppressionEffetsTermines(options);
+ await this.finDeRoundBlessures();
+ await this.$finDeRoundSupprimerObsoletes();
+ await this.$finDeRoundEmpoignade();
+ }
+
+ async $finDeRoundSuppressionEffetsTermines(options) {
+ for (let effect of this.getEffects()) {
+ if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
+ await effect.delete();
+ ChatMessage.create({ content: `${this.name} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` });
+ }
+ }
+ }
+
+ async finDeRoundBlessures() {
+ }
+
+ async $finDeRoundSupprimerObsoletes() {
+ const obsoletes = []
+ .concat(this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
+ .concat(this.itemTypes[TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
+ .map(it => it.id);
+ await this.deleteEmbeddedDocuments('Item', obsoletes);
+ }
+
+ async $finDeRoundEmpoignade() {
+ const immobilisations = this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
+ immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
+ game.actors.get(emp.system.empoigneid),
+ emp
+ ))
+ }
+
+ async setSonne(sonne = true) { }
+
+ /* -------------------------------------------- */
+ getCompetence(idOrName, options = {}) {
+ if (idOrName instanceof Item) {
+ return idOrName.isCompetence() ? idOrName : undefined
+ }
+ return RdDItemCompetence.findCompetence(this.items, idOrName, options)
+ }
+ getCompetences(name) {
+ return RdDItemCompetence.findCompetences(this.items, name)
+ }
+ getCompetenceCorpsACorps(options = {}) {
+ return this.getCompetence("Corps à corps", options)
+ }
+ getCompetencesEsquive() {
+ return this.getCompetences("esquive")
+ }
+
+ getArmeParade(armeParadeId) {
+ const item = armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined;
+ return RdDItemArme.getArme(item);
+ }
+
+ getDraconicOuPossession() {
+ return POSSESSION_SANS_DRACONIC
+ }
+
+ getPossession(possessionId) {
+ return this.itemTypes[TYPES.possession].find(it => it.system.possessionid == possessionId);
+ }
+ getPossessions() {
+ return this.itemTypes[TYPES.possession];
+ }
+ getEmpoignades() {
+ return this.itemTypes[TYPES.empoignade];
+ }
+
+ /* -------------------------------------------- */
+ async updateCreatureCompetence(idOrName, fieldName, value) {
+ let competence = this.getCompetence(idOrName);
+ if (competence) {
+ function getFieldPath(fieldName) {
+ switch (fieldName) {
+ case "niveau": return 'system.niveau';
+ case "dommages": return 'system.dommages';
+ case "carac_value": return 'system.carac_value';
+ }
+ return undefined
+ }
+ const path = getFieldPath(fieldName);
+ if (path) {
+ await this.updateEmbeddedDocuments('Item', [{ _id: competence.id, [path]: value }]); // updates one EmbeddedEntity
+ }
+ }
+ }
+
+ /* -------------------------------------------- */
+ isEffectAllowed(statusId) { return true }
+
+ getEffects(filter = e => true) {
+ return this.getEmbeddedCollection("ActiveEffect").filter(filter);
+ }
+
+ getEffect(statusId) {
+ return this.getEmbeddedCollection("ActiveEffect").find(it => it.flags?.core?.statusId == statusId);
+ }
+
+ async setEffect(statusId, status) {
+ if (this.isEffectAllowed(statusId)) {
+ const effect = this.getEffect(statusId);
+ if (!status && effect) {
+ await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
+ }
+ if (status && !effect) {
+ await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.status(statusId)]);
+ }
+ }
+ }
+
+ async removeEffect(statusId) {
+ const effect = this.getEffect(statusId);
+ if (effect) {
+ await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
+ }
+ }
+
+ async removeEffects(filter = e => true) {
+ if (game.user.isGM) {
+ const ids = this.getEffects(filter).map(it => it.id);
+ await this.deleteEmbeddedDocuments('ActiveEffect', ids);
+ }
+ }
+
+ /* -------------------------------------------- */
+ getSurprise(isCombat = undefined) {
+ let niveauSurprise = this.getEffects()
+ .map(effect => StatusEffects.valeurSurprise(effect, isCombat))
+ .reduce(Misc.sum(), 0);
+ if (niveauSurprise > 1) {
+ return 'totale';
+ }
+ if (niveauSurprise == 1) {
+ return 'demi';
+ }
+ return '';
+ }
+
+ /* -------------------------------------------- */
+ async computeEtatGeneral() {
+ // Par défaut, on ne calcule pas d'état général, seuls les personnages/créatures sont affectés
+ this.system.compteurs.etat.value = 0;
+ }
+
+ /* -------------------------------------------- */
+ async openRollDialog({ name, label, template, rollData, callbackAction }) {
+ const dialog = await RdDRoll.create(this, rollData,
+ { html: template, close: async html => await this._onCloseRollDialog(html) },
+ {
+ name: name,
+ label: label,
+ callbacks: [
+ this.createCallbackExperience(),
+ this.createCallbackAppelAuMoral(),
+ { action: callbackAction }
+ ]
+ });
+ dialog.render(true);
+ return dialog
+ }
+
+ createEmptyCallback() {
+ return {
+ condition: r => false,
+ action: r => { }
+ };
+ }
+ createCallbackExperience() { return this.createEmptyCallback(); }
+ createCallbackAppelAuMoral() { return this.createEmptyCallback(); }
+ async _onCloseRollDialog(html) { }
+
+ /* -------------------------------------------- */
+ async roll() {
+ RdDEmpoignade.checkEmpoignadeEnCours(this)
+
+ const carac = this.getCarac()
+ const selectedCaracName = ['apparence', 'perception', 'force', 'reve'].find(it => carac[it] != undefined)
+
+ await this.openRollDialog({
+ name: `jet-${this.id}`,
+ label: `Jet de ${this.name}`,
+ template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll.html',
+ rollData: {
+ carac: carac,
+ selectedCarac: carac[selectedCaracName],
+ selectedCaracName: selectedCaracName,
+ competences: this.itemTypes['competence']
+ },
+ callbackAction: r => this.$onRollCaracResult(r)
+ });
+ }
+
+ getCarac() {
+ // TODO: le niveau d'une entité de cauchemar devrait être exclu...
+ const carac = mergeObject(duplicate(this.system.carac),
+ {
+ 'reve-actuel': this.getCaracReveActuel(),
+ 'chance-actuelle': this.getCaracChanceActuelle()
+ });
+ return carac;
+ }
+
+ /* -------------------------------------------- */
+ async rollCarac(caracName, jetResistance = undefined) {
+ RdDEmpoignade.checkEmpoignadeEnCours(this)
+ let selectedCarac = this.getCaracByName(caracName)
+ await this.openRollDialog({
+ name: 'jet-' + caracName,
+ label: 'Jet ' + Grammar.apostrophe('de', selectedCarac.label),
+ template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
+ rollData: {
+ selectedCarac: selectedCarac,
+ competences: this.itemTypes['competence'],
+ jetResistance: jetResistance ? caracName : undefined
+ },
+ callbackAction: r => this.$onRollCaracResult(r)
+ });
+ }
+
+ /* -------------------------------------------- */
+ async $onRollCaracResult(rollData) {
+ // Final chat message
+ await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-general.html');
+ }
+
+ /* -------------------------------------------- */
+ async rollCompetence(idOrName, options = { tryTarget: true }) {
+ RdDEmpoignade.checkEmpoignadeEnCours(this)
+ const competence = this.getCompetence(idOrName);
+ let rollData = { carac: this.system.carac, competence: competence }
+ if (competence.type == TYPES.competencecreature) {
+ const arme = RdDItemCompetenceCreature.armeCreature(competence)
+ if (arme && options.tryTarget && Targets.hasTargets()) {
+ Targets.selectOneToken(target => {
+ if (arme.action == "possession") {
+ RdDPossession.onAttaquePossession(target, this, competence)
+ }
+ else {
+ RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
+ }
+ });
+ return;
+ }
+ // Transformer la competence de créature
+ RdDItemCompetenceCreature.setRollDataCreature(rollData)
+ }
+
+ await this.openRollDialog({
+ name: 'jet-competence',
+ label: 'Jet ' + Grammar.apostrophe('de', competence.name),
+ template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
+ rollData: rollData,
+ callbackAction: r => this.$onRollCompetence(r, options)
+ });
+ }
+
+ async $onRollCompetence(rollData, options) {
+ await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html')
+ if (options?.onRollAutomate) {
+ options.onRollAutomate(rollData);
+ }
+ }
+
+ /** --------------------------------------------
+ * @param {*} arme item d'arme/compétence de créature
+ * @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
+ * @returns
+ */
+ rollArme(arme, categorieArme = "competence") {
+ let compToUse = this.$getCompetenceArme(arme, categorieArme)
+ if (!Targets.hasTargets()) {
+ RdDConfirm.confirmer({
+ settingConfirmer: "confirmer-combat-sans-cible",
+ content: `Voulez vous faire un jet de ${compToUse} sans choisir de cible valide?
+
Tous les jets de combats devront être gérés à la main
+
`,
+ title: 'Ne pas utiliser les automatisation de combat',
+ buttonLabel: "Pas d'automatisation",
+ onAction: async () => {
+ this.rollCompetence(compToUse, { tryTarget: false })
+ }
+ });
+ return;
+ }
+
+ Targets.selectOneToken(target => {
+ if (Targets.isTargetEntite(target)) {
+ ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
+ return;
+ }
+
+ const competence = this.getCompetence(compToUse)
+ //console.log("RollArme", competence, arme)
+ if (competence.isCompetencePossession()) {
+ return RdDPossession.onAttaquePossession(target, this, competence);
+ }
+ RdDCombat.rddCombatTarget(target, this).attaque(competence, arme);
+ })
+ }
+
+ $getCompetenceArme(arme, competenceName) {
+ switch (arme.type) {
+ case TYPES.competencecreature:
+ return arme.name
+ case TYPES.arme:
+ switch (competenceName) {
+ case 'competence': return arme.system.competence;
+ case 'unemain': return RdDItemArme.competence1Mains(arme);
+ case 'deuxmains': return RdDItemArme.competence2Mains(arme);
+ case 'tir': return arme.system.tir;
+ case 'lancer': return arme.system.lancer;
+ }
+ }
+ return undefined
+ }
+
+ verifierForceMin(item) {
+ }
+ /* -------------------------------------------- */
+ async resetItemUse() { }
+ async incDecItemUse(itemId, inc = 1) { }
+ getItemUse(itemId) { return 0; }
+
+ /* -------------------------------------------- */
+ async encaisser() { await RdDEncaisser.encaisser(this) }
+
+ async encaisserDommages(rollData, attacker = undefined, show = undefined) {
+ if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
+ return;
+ }
+ const attackerId = attacker?.id;
+ if (ReglesOptionnelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
+ RdDBaseActor.remoteActorCall({
+ tokenId: this.token?.id,
+ actorId: this.id,
+ method: 'appliquerEncaissement',
+ args: [rollData, show, attackerId]
+ });
+ return;
+ }
+
+ const armure = await this.computeArmure(rollData);
+ if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
+ DialogValidationEncaissement.validerEncaissement(this, rollData, armure,
+ jet => this.$onEncaissement(jet, show, attacker));
+ }
+ else {
+ const jet = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
+ await this.$onEncaissement(jet, show, attacker);
+ }
+ }
+
+ async $onEncaissement(jet, show, attacker) {
+ await this.onAppliquerJetEncaissement(jet, attacker);
+ await this.$afficherEncaissement(jet, show);
+ }
+
+ async onAppliquerJetEncaissement(encaissement, attacker) { }
+
+ async $afficherEncaissement(encaissement, show) {
+ mergeObject(encaissement, {
+ alias: this.name,
+ hasPlayerOwner: this.hasPlayerOwner,
+ show: show ?? {}
+ });
+
+ await ChatUtility.createChatWithRollMode(this.name, {
+ roll: encaissement.roll,
+ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
+ });
+
+ if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
+ encaissement = duplicate(encaissement);
+ encaissement.isGM = true;
+ ChatMessage.create({
+ whisper: ChatMessage.getWhisperRecipients("GM"),
+ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
+ });
+ }
+ }
+
+ /* -------------------------------------------- */
+ async accorder(entite, when = 'avant-encaissement') {
+ if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
+ || entite == undefined
+ || !entite.isEntite([ENTITE_INCARNE])
+ || entite.isEntiteAccordee(this)) {
+ return true;
+ }
+ const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.system.carac.niveau.value));
+ const rollData = {
+ alias: this.name,
+ rolled: rolled,
+ entite: entite.name,
+ selectedCarac: this.system.carac.reve
+ };
+
+ if (rolled.isSuccess) {
+ await entite.setEntiteReveAccordee(this);
+ }
+
+ await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.html');
+ if (rolled.isPart) {
+ await this.appliquerAjoutExperience(rollData, true);
+ }
+ return rolled.isSuccess;
+ }
+
+ isEntiteAccordee(attacker) { return true }
+
+ async setEntiteReveAccordee(attacker) {
+ ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entite de cauchemer/rêve");
+ }
+
+}
diff --git a/module/actor/base-actor-sheet.js b/module/actor/base-actor-sheet.js
index 6411a415..ef137193 100644
--- a/module/actor/base-actor-sheet.js
+++ b/module/actor/base-actor-sheet.js
@@ -31,7 +31,7 @@ export class RdDBaseActorSheet extends ActorSheet {
async getData() {
Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']);
- this.actor.recompute();
+ this.actor.computeEtatGeneral();
let formData = {
title: this.title,
id: this.actor.id,
diff --git a/module/actor/base-actor.js b/module/actor/base-actor.js
index c1fda4b4..c3bad622 100644
--- a/module/actor/base-actor.js
+++ b/module/actor/base-actor.js
@@ -1,5 +1,6 @@
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 { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
@@ -9,6 +10,33 @@ import { SystemCompendiums } from "../settings/system-compendiums.js";
import { APP_ASTROLOGIE_REFRESH } from "../sommeil/app-astrologie.js";
export class RdDBaseActor extends Actor {
+ /* -------------------------------------------- */
+ static _findCaracByName(carac, name) {
+ name = Grammar.toLowerCaseNoAccent(name);
+ switch (name) {
+ case 'reve-actuel': case 'reve actuel':
+ return carac.reve;
+ case 'chance-actuelle': case 'chance actuelle':
+ return carac.chance;
+ }
+
+ 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;
+ }
+
+ 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 RdDBaseActor._findCaracByName(this.system.carac, name);
+ }
static getDefaultImg(itemType) {
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
@@ -124,11 +152,24 @@ export class RdDBaseActor extends Actor {
}
/* -------------------------------------------- */
+ prepareData() {
+ super.prepareData()
+ this.prepareActorData()
+ this.cleanupConteneurs()
+ this.computeEtatGeneral()
+ this.computeEncTotal()
+ }
+
+ async prepareActorData() { }
+ async computeEtatGeneral() { }
+ /* -------------------------------------------- */
+
isCreatureEntite() { return this.type == 'creature' || this.type == 'entite'; }
isCreature() { return this.type == 'creature'; }
- isEntite() { return this.type == 'entite'; }
+ isEntite(typeentite = []) { return false }
isPersonnage() { return this.type == 'personnage'; }
isVehicule() { return this.type == 'vehicule'; }
+
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
@@ -145,12 +186,7 @@ export class RdDBaseActor extends Actor {
}
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
- getReveActuel() { return 0 }
- computeMalusSurEncombrement() { return 0 }
getEncombrementMax() { return 0 }
- recompute() { }
- async setEffect(statusId, status) { }
-
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
@@ -179,6 +215,16 @@ export class RdDBaseActor extends Actor {
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']);
@@ -399,6 +445,10 @@ export class RdDBaseActor extends Actor {
return 0;
}
+ getEncTotal() {
+ return Math.floor(this.encTotal ?? 0);
+ }
+
async createItem(type, name = undefined) {
if (!name) {
name = 'Nouveau ' + Misc.typeName('Item', type);
@@ -627,5 +677,12 @@ export class RdDBaseActor extends Actor {
.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") }
}
\ No newline at end of file
diff --git a/module/actor-entite-sheet.js b/module/actor/entite-sheet.js
similarity index 91%
rename from module/actor-entite-sheet.js
rename to module/actor/entite-sheet.js
index 50f505bf..975a2d10 100644
--- a/module/actor-entite-sheet.js
+++ b/module/actor/entite-sheet.js
@@ -1,8 +1,8 @@
-import { RdDActorSheet } from "./actor-sheet.js";
-import { RdDSheetUtility } from "./rdd-sheet-utility.js";
-import { RdDUtility } from "./rdd-utility.js";
+import { RdDBaseActorReveSheet } from "./base-actor-reve-sheet.js";
+import { RdDSheetUtility } from "../rdd-sheet-utility.js";
+import { RdDUtility } from "../rdd-utility.js";
-export class RdDActorEntiteSheet extends RdDActorSheet {
+export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
/** @override */
static get defaultOptions() {
diff --git a/module/actor/entite.js b/module/actor/entite.js
new file mode 100644
index 00000000..7e971249
--- /dev/null
+++ b/module/actor/entite.js
@@ -0,0 +1,110 @@
+import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
+import { TYPES } from "../item.js";
+import { Misc } from "../misc.js";
+import { RdDEncaisser } from "../rdd-roll-encaisser.js";
+import { STATUSES } from "../settings/status-effects.js";
+import { RdDBaseActorReve } from "./base-actor-reve.js";
+
+export class RdDEntite extends RdDBaseActorReve {
+
+ static get defaultIcon() {
+ return "systems/foundryvtt-reve-de-dragon/icons/entites/darquoine.webp";
+ }
+
+ canReceive(item) {
+ return item.type == TYPES.competencecreature
+ }
+
+ isEntite(typeentite = []) {
+ return (typeentite.length == 0 || typeentite.includes(this.system.definition.typeentite));
+ }
+ isNonIncarnee() { return this.isEntite([ENTITE_NONINCARNE]) }
+
+ getReveActuel() {
+ return Misc.toInt(this.system.carac.reve?.value)
+ }
+
+ getForce() { return this.getReve() }
+ getAgilite() { return this.getReve() }
+ getChance() { return this.getReve() }
+
+ getDraconicOuPossession() {
+ return this.itemTypes[TYPES.competencecreature]
+ .filter(it => it.system.categorie == 'possession')
+ .sort(Misc.descending(it => it.system.niveau))
+ .find(it => true);
+ }
+
+ async remiseANeuf() {
+ await this.removeEffects(e => true);
+ if (!this.isNonIncarnee()) {
+ await this.update({
+ 'system.sante.endurance.value': this.system.sante.endurance.max
+ });
+ }
+ }
+
+ isDead() {
+ return this.isNonIncarnee() ? false : this.system.sante.endurance.value <= 0
+ }
+
+ async santeIncDec(name, inc, isCritique = false) {
+ if (name == 'endurance' && !this.isNonIncarnee()) {
+ const oldValue = this.system.sante.endurance.value;
+ const endurance = Math.max(0,
+ Math.min(oldValue + inc,
+ this.system.sante.endurance.max));
+ await this.update({ "system.sante.endurance.value": endurance })
+ await this.setEffect(STATUSES.StatusComma, endurance <= 0);
+ return {
+ perte: oldValue - endurance,
+ newValue: endurance
+ }
+ }
+ return {}
+ }
+ async encaisser() {
+ if (this.isNonIncarnee()) {
+ return
+ }
+ await RdDEncaisser.encaisser(this)
+ }
+
+ isEffectAllowed(statusId) {
+ return [STATUSES.StatusComma].includes(statusId);
+ }
+
+ async onAppliquerJetEncaissement(encaissement, attacker) {
+ const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
+ mergeObject(encaissement, {
+ resteEndurance: perteEndurance.newValue,
+ endurance: perteEndurance.perte
+ });
+ }
+
+ isEntiteAccordee(attacker) {
+ if (this.isEntite([ENTITE_INCARNE])) {
+ let resonnance = this.system.sante.resonnance
+ return (resonnance.actors.find(it => it == attacker.id))
+ }
+ return true
+ }
+
+ /* -------------------------------------------- */
+ async setEntiteReveAccordee(attacker) {
+ if (this.isEntite([ENTITE_INCARNE])) {
+ let resonnance = duplicate(this.system.sante.resonnance);
+ if (resonnance.actors.find(it => it == attacker.id)) {
+ // déjà accordé
+ return;
+ }
+ resonnance.actors.push(attacker.id);
+ await this.update({ "system.sante.resonnance": resonnance });
+ }
+ else {
+ super.setEntiteReveAccordee(attacker)
+ }
+ }
+
+
+}
diff --git a/module/dialog-validation-encaissement.js b/module/dialog-validation-encaissement.js
index a8808466..d4c41f61 100644
--- a/module/dialog-validation-encaissement.js
+++ b/module/dialog-validation-encaissement.js
@@ -7,20 +7,19 @@ import { RdDUtility } from "./rdd-utility.js";
*/
export class DialogValidationEncaissement extends Dialog {
- static async validerEncaissement(actor, rollData, armure, show, attackerId, onEncaisser) {
+ static async validerEncaissement(actor, rollData, armure, onEncaisser) {
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: HIDE_DICE });
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.html', {
actor: actor,
rollData: rollData,
- encaissement: encaissement,
- show: show
+ encaissement: encaissement
});
- const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, show, attackerId, onEncaisser);
+ const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser);
dialog.render(true);
}
/* -------------------------------------------- */
- constructor(html, actor, rollData, armure, encaissement, show, attackerId, onEncaisser) {
+ constructor(html, actor, rollData, armure, encaissement, onEncaisser) {
// Common conf
let buttons = {
"valider": { label: "Valider", callback: html => this.onValider() },
@@ -47,8 +46,6 @@ export class DialogValidationEncaissement extends Dialog {
this.rollData = rollData;
this.armure = armure;
this.encaissement = encaissement;
- this.show = show;
- this.attackerId = attackerId;
this.onEncaisser = onEncaisser;
this.forceDiceResult = {total: encaissement.roll.result };
}
@@ -67,6 +64,6 @@ export class DialogValidationEncaissement extends Dialog {
async onValider() {
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
- this.onEncaisser(this.encaissement, this.show, this.attackerId)
+ this.onEncaisser(this.encaissement)
}
}
diff --git a/module/item-arme.js b/module/item-arme.js
index bbc87102..ae7b688e 100644
--- a/module/item-arme.js
+++ b/module/item-arme.js
@@ -20,7 +20,7 @@ const nomCategorieParade = {
export class RdDItemArme extends Item {
static isArme(item) {
- return RdDItemCompetenceCreature.getCategorieAttaque(item) || item.type == TYPES.arme;
+ return item.type == TYPES.arme || RdDItemCompetenceCreature.getCategorieAttaque(item);
}
/* -------------------------------------------- */
diff --git a/module/item-competencecreature.js b/module/item-competencecreature.js
index 85f3f291..c5dae365 100644
--- a/module/item-competencecreature.js
+++ b/module/item-competencecreature.js
@@ -55,6 +55,20 @@ export class RdDItemCompetenceCreature extends Item {
}
/* -------------------------------------------- */
+ static isCompetenceAttaque(item) {
+ if (item.type == TYPES.competencecreature) {
+ switch (item.system.categorie) {
+ case "melee":
+ case "tir":
+ case "lancer":
+ case "naturelle":
+ case "possession":
+ return true
+ }
+ }
+ return undefined
+ }
+
static getCategorieAttaque(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
@@ -63,6 +77,7 @@ export class RdDItemCompetenceCreature extends Item {
case "lancer":
case "naturelle":
case "possession":
+ case "parade":
return item.system.categorie
}
}
diff --git a/module/misc.js b/module/misc.js
index f055f4a0..e7ce38fe 100644
--- a/module/misc.js
+++ b/module/misc.js
@@ -63,8 +63,8 @@ export class Misc {
static keepDecimals(num, decimals) {
if (decimals <= 0 || decimals > 6) return num;
- const decimal = Math.pow(10, parseInt(decimals));
- return Math.round(num * decimal) / decimal;
+ const power10n = Math.pow(10, parseInt(decimals));
+ return Math.round(num * power10n) / power10n;
}
static getFractionHtml(diviseur) {
diff --git a/module/rdd-carac.js b/module/rdd-carac.js
index 10a23e5b..1defe187 100644
--- a/module/rdd-carac.js
+++ b/module/rdd-carac.js
@@ -1,7 +1,7 @@
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
-const tableCaracDerivee = {
+const TABLE_CARACTERISTIQUES_DERIVEES = {
// xp: coût pour passer du niveau inférieur à ce niveau
1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 },
2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 },
@@ -58,6 +58,10 @@ export class RdDCarac {
selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/);
}
+ static getCaracDerivee(value) {
+ return TABLE_CARACTERISTIQUES_DERIVEES[Math.min(Math.max(Number(value), 1), 32)];
+ }
+
static computeTotal(carac, beaute = undefined) {
const total = Object.values(carac ?? {}).filter(c => !c.derivee)
.map(it => parseInt(it.value))
@@ -73,7 +77,7 @@ export class RdDCarac {
/* -------------------------------------------- */
static calculSConst(constitution) {
- return Number(tableCaracDerivee[Number(constitution)].sconst);
+ return RdDCarac.getCaracDerivee(constitution).sconst;
}
/* -------------------------------------------- */
@@ -84,7 +88,7 @@ export class RdDCarac {
}
static getCaracXp(targetValue) {
- return tableCaracDerivee[targetValue]?.xp ?? 200;
+ return RdDCarac.getCaracDerivee(targetValue)?.xp ?? 200;
}
@@ -97,37 +101,4 @@ export class RdDCarac {
return Grammar.toLowerCaseNoAccent(selectedCarac?.label)?.match(/(apparence|force|agilite|dexterite|vue|ouie|odorat|empathie|melee|tir|lancer|derobee)/);
}
- /* -------------------------------------------- */
- static computeCarac(system) {
- system.carac.force.value = Math.min(system.carac.force.value, parseInt(system.carac.taille.value) + 4);
-
- system.carac.derobee.value = Math.floor(parseInt(((21 - system.carac.taille.value)) + parseInt(system.carac.agilite.value)) / 2);
- let bonusDomKey = Math.floor((parseInt(system.carac.force.value) + parseInt(system.carac.taille.value)) / 2);
- bonusDomKey = Math.min(Math.max(bonusDomKey, 0), 32); // Clamp de securite
-
- let tailleData = tableCaracDerivee[bonusDomKey];
- system.attributs.plusdom.value = tailleData.plusdom;
-
- system.attributs.sconst.value = RdDCarac.calculSConst(system.carac.constitution.value);
- system.attributs.sust.value = tableCaracDerivee[Number(system.carac.taille.value)].sust;
-
- system.attributs.encombrement.value = (parseInt(system.carac.force.value) + parseInt(system.carac.taille.value)) / 2;
- system.carac.melee.value = Math.floor((parseInt(system.carac.force.value) + parseInt(system.carac.agilite.value)) / 2);
- system.carac.tir.value = Math.floor((parseInt(system.carac.vue.value) + parseInt(system.carac.dexterite.value)) / 2);
- system.carac.lancer.value = Math.floor((parseInt(system.carac.tir.value) + parseInt(system.carac.force.value)) / 2);
-
- system.sante.vie.max = Math.ceil((parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value)) / 2);
-
- system.sante.vie.value = Math.min(system.sante.vie.value, system.sante.vie.max)
- system.sante.endurance.max = Math.max(parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value), parseInt(system.sante.vie.max) + parseInt(system.carac.volonte.value));
- system.sante.endurance.value = Math.min(system.sante.endurance.value, system.sante.endurance.max);
- system.sante.fatigue.max = system.sante.endurance.max * 2;
- system.sante.fatigue.value = Math.min(system.sante.fatigue.value, system.sante.fatigue.max);
-
- //Compteurs
- system.reve.reve.max = system.carac.reve.value;
- system.compteurs.chance.max = system.carac.chance.value;
- }
-
-
}
diff --git a/module/rdd-combat.js b/module/rdd-combat.js
index c0eea94c..363a0f43 100644
--- a/module/rdd-combat.js
+++ b/module/rdd-combat.js
@@ -87,7 +87,7 @@ export class RdDCombatManager extends Combat {
let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0);
if (!formula) {
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
- const competence = combatant.actor.items.find(it => RdDItemCompetenceCreature.getCategorieAttaque(it))
+ const competence = combatant.actor.items.find(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
if (competence) {
rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0);
}
@@ -229,7 +229,9 @@ export class RdDCombatManager extends Combat {
}
static listActionsCreature(competences) {
- return competences.map(it => RdDItemCompetenceCreature.armeCreature(it))
+ return competences
+ .filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
+ .map(it => RdDItemCompetenceCreature.armeCreature(it))
.filter(it => it != undefined);
}
diff --git a/module/rdd-main.js b/module/rdd-main.js
index 3f016727..56ba18a9 100644
--- a/module/rdd-main.js
+++ b/module/rdd-main.js
@@ -29,11 +29,12 @@ import { Environnement } from "./environnement.js";
import { RdDActor } from "./actor.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCommerce } from "./actor/commerce.js";
+import { RdDEntite } from "./actor/entite.js";
import { RdDVehicule } from "./actor/vehicule.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDCommerceSheet } from "./actor/commerce-sheet.js";
import { RdDActorCreatureSheet } from "./actor-creature-sheet.js";
-import { RdDActorEntiteSheet } from "./actor-entite-sheet.js";
+import { RdDActorEntiteSheet } from "./actor/entite-sheet.js";
import { RdDActorVehiculeSheet } from "./actor/vehicule-sheet.js";
import { RdDItem } from "./item.js";
@@ -93,7 +94,7 @@ export class SystemReveDeDragon {
this.actorClasses = {
commerce: RdDCommerce,
creature: RdDActor,
- entite: RdDActor,
+ entite: RdDEntite,
personnage: RdDActor,
vehicule: RdDVehicule,
}
diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js
index 60ba2114..3bc94254 100644
--- a/module/rdd-resolution-table.js
+++ b/module/rdd-resolution-table.js
@@ -28,7 +28,7 @@ const reussites = [
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
/* -------------------------------------------- */
-const caracMaximumResolution = 60;
+const CARAC_MAXIMUM_RESOLUTION = 40;
/* -------------------------------------------- */
export class RdDResolutionTable {
static resolutionTable = this.build()
@@ -36,7 +36,7 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static build() {
let table = []
- for (var caracValue = 0; caracValue <= caracMaximumResolution; caracValue++) {
+ for (var caracValue = 0; caracValue <= CARAC_MAXIMUM_RESOLUTION; caracValue++) {
table[caracValue] = this._computeRow(caracValue);
}
return table;
diff --git a/module/rdd-utility.js b/module/rdd-utility.js
index 4d625da0..0f8880e3 100644
--- a/module/rdd-utility.js
+++ b/module/rdd-utility.js
@@ -55,7 +55,8 @@ function _cumulSegmentsFatigue(matrix) {
}
/* -------------------------------------------- */
-export const fatigueMatrix = _buildAllSegmentsFatigue(60);
+export const MAX_ENDURANCE_FATIGUE = 60;
+const fatigueMatrix = _buildAllSegmentsFatigue(MAX_ENDURANCE_FATIGUE);
const cumulFatigueMatrix = _cumulSegmentsFatigue(fatigueMatrix);
const fatigueMalus = [0, 0, 0, -1, -1, -1, -2, -3, -4, -5, -6, -7]; // Provides the malus for each segment of fatigue
@@ -465,11 +466,10 @@ export class RdDUtility {
}
/* -------------------------------------------- */
- static calculMalusFatigue(fatigue, maxEnd) {
- maxEnd = Math.max(maxEnd, 1);
- maxEnd = Math.min(maxEnd, cumulFatigueMatrix.length);
- let segments = cumulFatigueMatrix[maxEnd];
- for (let i = 0; i < 12; i++) {
+ static calculMalusFatigue(fatigue, endurance) {
+ endurance = Math.min(Math.max(endurance, 1), cumulFatigueMatrix.length);
+ let segments = cumulFatigueMatrix[endurance];
+ for (let i = 0; i < segments.length; i++) {
if (fatigue <= segments[i]) {
return fatigueMalus[i]
}