Séparation de l'Actor Entités

This commit is contained in:
2023-11-04 03:42:39 +01:00
parent d4be2957a3
commit 1d3ae9bb1a
18 changed files with 945 additions and 674 deletions

View File

@ -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: `<p>Voulez vous faire un jet de ${compToUse} sans choisir de cible valide?
<br>Tous les jets de combats devront être gérés à la main
</p>`,
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
}
/* -------------------------------------------- */