Vincent Vandemeulebrouck
582df7e290
Les messages dans les TMRs sont envoyés au GM Simplification des messages de tchat liés à un actor: on peut utiliser les Owners (car les GMs sont owner). Au lieu de passer le name de l'Actor (qui peut être incorrect si deux actors ont le même, mais pas les mêmes propriétaires), on passe directement l'actor pour déterminer mles destinataires de messages
297 lines
11 KiB
JavaScript
297 lines
11 KiB
JavaScript
import { MAX_ENDURANCE_FATIGUE, RdDUtility } from "../rdd-utility.js";
|
|
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
|
|
import { STATUSES } from "../settings/status-effects.js";
|
|
import { ITEM_TYPES } from "../item.js";
|
|
import { RdDBaseActorReve } from "./base-actor-reve.js";
|
|
import { RdDDice } from "../rdd-dice.js";
|
|
import { RdDItemBlessure } from "../item/blessure.js";
|
|
import { ChatUtility } from "../chat-utility.js";
|
|
|
|
/**
|
|
* Classe de base pour les acteurs qui peuvent subir des blessures
|
|
* - créatures
|
|
* - humanoides
|
|
*/
|
|
export class RdDBaseActorSang extends RdDBaseActorReve {
|
|
|
|
|
|
getForce() { return Number(this.system.carac.force?.value ?? 0) }
|
|
|
|
getBonusDegat() { return Number(this.system.attributs?.plusdom?.value ?? 0) }
|
|
getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
|
|
getSConst() { return 0 }
|
|
|
|
getEnduranceMax() { return Math.max(1, Math.min(this.system.sante.endurance.max, MAX_ENDURANCE_FATIGUE)) }
|
|
|
|
getFatigueActuelle() {
|
|
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
|
|
return Math.max(0, Math.min(this.getFatigueMax(), this.system.sante.fatigue?.value ?? 0));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
getFatigueRestante() { return this.getFatigueMax() - this.getFatigueActuelle() }
|
|
getFatigueMin() { return this.system.sante.endurance.max - this.system.sante.endurance.value }
|
|
getFatigueMax() { return this.getEnduranceMax() * 2 }
|
|
|
|
malusFatigue() {
|
|
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
|
|
return RdDUtility.calculMalusFatigue(this.getFatigueActuelle(), this.getEnduranceMax())
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getEncombrementMax() { return Number(this.system.attributs?.encombrement?.value ?? 0) }
|
|
isSurenc() { return this.computeMalusSurEncombrement() < 0 }
|
|
|
|
computeMalusSurEncombrement() {
|
|
return Math.min(0, Math.floor(this.getEncombrementMax() - this.encTotal));
|
|
}
|
|
|
|
isDead() { return this.system.sante.vie.value < -this.getSConst() }
|
|
|
|
nbBlessuresLegeres() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isLegere()).length }
|
|
nbBlessuresGraves() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isGrave()).length }
|
|
nbBlessuresCritiques() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isCritique()).length }
|
|
|
|
/* -------------------------------------------- */
|
|
computeResumeBlessure() {
|
|
const nbLegeres = this.nbBlessuresLegeres()
|
|
const nbGraves = this.nbBlessuresGraves()
|
|
const nbCritiques = this.nbBlessuresCritiques()
|
|
|
|
if (nbLegeres + nbGraves + nbCritiques == 0) {
|
|
return "Aucune blessure";
|
|
}
|
|
let resume = "Blessures:";
|
|
if (nbLegeres > 0) {
|
|
resume += " " + nbLegeres + " légère" + (nbLegeres > 1 ? "s" : "");
|
|
}
|
|
if (nbGraves > 0) {
|
|
if (nbLegeres > 0)
|
|
resume += ",";
|
|
resume += " " + nbGraves + " grave" + (nbGraves > 1 ? "s" : "");
|
|
}
|
|
if (nbCritiques > 0) {
|
|
if (nbGraves > 0 || nbLegeres > 0)
|
|
resume += ",";
|
|
resume += " une CRITIQUE !";
|
|
}
|
|
return resume;
|
|
}
|
|
|
|
blessuresASoigner() { return [] }
|
|
|
|
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
|
|
async remiseANeuf() { }
|
|
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
async onAppliquerJetEncaissement(encaissement, attacker) {
|
|
const santeOrig = foundry.utils.duplicate(this.system.sante);
|
|
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table
|
|
const perteVie = await this.santeIncDec("vie", -encaissement.vie);
|
|
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
|
|
|
|
foundry.utils.mergeObject(encaissement, {
|
|
resteEndurance: perteEndurance.newValue,
|
|
sonne: perteEndurance.sonne,
|
|
jetEndurance: perteEndurance.jetEndurance,
|
|
endurance: perteEndurance.perte,
|
|
vie: santeOrig.vie.value - perteVie.newValue,
|
|
blessure: blessure
|
|
});
|
|
}
|
|
/* -------------------------------------------- */
|
|
async santeIncDec(name, inc, isCritique = false) {
|
|
if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) {
|
|
return;
|
|
}
|
|
const sante = foundry.utils.duplicate(this.system.sante)
|
|
let compteur = sante[name];
|
|
if (!compteur) {
|
|
return;
|
|
}
|
|
let result = {
|
|
sonne: false,
|
|
};
|
|
|
|
let minValue = name == "vie" ? -this.getSConst() - 1 : 0;
|
|
|
|
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") {
|
|
if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie
|
|
sante.vie.value--;
|
|
result.perteVie = true;
|
|
}
|
|
result.newValue = Math.max(0, result.newValue);
|
|
if (inc > 0) { // le max d'endurance s'applique seulement à la récupération
|
|
result.newValue = Math.min(result.newValue, this._computeEnduranceMax())
|
|
}
|
|
const perte = compteur.value - result.newValue;
|
|
result.perte = perte;
|
|
if (perte > 1) {
|
|
// Peut-être sonné si 2 points d'endurance perdus d'un coup
|
|
foundry.utils.mergeObject(result, await this.jetEndurance(result.newValue));
|
|
} else if (inc > 0) {
|
|
await this.setSonne(false);
|
|
}
|
|
if (sante.fatigue && inc < 0) { // Each endurance lost -> fatigue lost
|
|
fatigue = perte;
|
|
}
|
|
}
|
|
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.getFatigueMin());
|
|
}
|
|
await this.update({ "system.sante": sante })
|
|
if (this.isDead()) {
|
|
await this.setEffect(STATUSES.StatusComma, true);
|
|
}
|
|
return result
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_computeEnduranceMax() {
|
|
const diffVie = this.system.sante.vie.max - this.system.sante.vie.value;
|
|
const maxEndVie = this.system.sante.endurance.max - (diffVie * 2);
|
|
const nbGraves = this.countBlessures(it => it.isGrave()) > 0
|
|
const nbCritiques = this.countBlessures(it => it.isCritique()) > 0
|
|
const maxEndGraves = Math.floor(this.system.sante.endurance.max / (2 * nbGraves));
|
|
const maxEndCritiques = nbCritiques > 0 ? 1 : this.system.sante.endurance.max;
|
|
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
|
|
}
|
|
|
|
|
|
/* -------------------------------------------- */
|
|
async ajouterBlessure(encaissement, attacker = undefined) {
|
|
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) {
|
|
// Aggravation
|
|
encaissement.gravite += 2
|
|
if (encaissement.gravite > 2) {
|
|
encaissement.vie += 2;
|
|
}
|
|
}
|
|
}
|
|
const endActuelle = this.getEnduranceActuelle();
|
|
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attacker);
|
|
if (blessure.isCritique()) {
|
|
encaissement.endurance = endActuelle;
|
|
}
|
|
|
|
if (blessure.isMort()) {
|
|
this.setEffect(STATUSES.StatusComma, true);
|
|
encaissement.mort = true;
|
|
ChatMessage.create({
|
|
content: `<img class="chat-icon" src="icons/svg/skull.svg" data-tooltip="charge" />
|
|
<strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>`
|
|
});
|
|
}
|
|
return blessure;
|
|
}
|
|
|
|
async supprimerBlessure({ gravite }) {
|
|
const toDelete = this.itemTypes[ITEM_TYPES.blessure].find(it => it.system.gravite == gravite)?.id
|
|
if (toDelete) {
|
|
await this.deleteEmbeddedDocuments('Item', [toDelete]);
|
|
}
|
|
}
|
|
|
|
async supprimerBlessures(filterToDelete) {
|
|
const toDelete = this.filterItems(filterToDelete, ITEM_TYPES.blessure)
|
|
.map(it => it.id);
|
|
await this.deleteEmbeddedDocuments('Item', toDelete);
|
|
}
|
|
|
|
countBlessures(filter = it => !it.isContusion()) {
|
|
return this.filterItems(filter, ITEM_TYPES.blessure).length
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async jetDeVie() {
|
|
if (this.isDead()) {
|
|
ChatMessage.create({
|
|
content: `Jet de Vie: ${this.name} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`,
|
|
whisper: ChatUtility.getOwners(this)
|
|
})
|
|
return
|
|
}
|
|
const jetDeVie = await RdDDice.roll("1d20");
|
|
|
|
const sConst = this.getSConst();
|
|
const vie = this.system.sante.vie.value;
|
|
const isCritique = this.nbBlessuresCritiques() > 0;
|
|
const isGrave = this.nbBlessuresGraves();
|
|
const isEchecTotal = jetDeVie.total == 20;
|
|
const isSuccess = jetDeVie.total == 1 || jetDeVie.total <= vie;
|
|
const perte = isSuccess ? 0 : 1 + (isEchecTotal ? vie + sConst : 0)
|
|
const prochainJet = (jetDeVie.total == 1 && vie > 0 ? 20 : 1) * (isCritique ? 1 : isGrave > 0 ? sConst : 0)
|
|
|
|
let msgText = `Jet de Vie: <strong>${jetDeVie.total} / ${vie}</strong>`
|
|
if (isSuccess) {
|
|
msgText += "<br>Réussi, pas de perte de point de vie."
|
|
} else {
|
|
msgText += `<br>Echoué, perte ${perte} point de vie`;
|
|
await this.santeIncDec("vie", -perte);
|
|
}
|
|
if (this.isDead()) {
|
|
msgText += `<br><strong>${this.name} est mort !!!!</strong>`;
|
|
}
|
|
else if (prochainJet > 0) {
|
|
msgText += `<br>Prochain jet de vie dans ${prochainJet} ${isCritique ? 'round' : 'minute'}${prochainJet > 1 ? 's' : ''} ${isCritique ? '(état critique)' : '(état grave)'}`
|
|
}
|
|
ChatMessage.create({
|
|
content: msgText,
|
|
whisper: ChatUtility.getOwners(this)
|
|
});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async jetEndurance(resteEndurance = undefined) {
|
|
const jetEndurance = (await RdDDice.roll("1d20")).total;
|
|
const sonne = jetEndurance == 20 || jetEndurance > (resteEndurance ?? this.system.sante.endurance.value)
|
|
if (sonne) {
|
|
await this.setSonne();
|
|
}
|
|
return { jetEndurance, sonne }
|
|
}
|
|
|
|
async finDeRoundBlessures() {
|
|
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length;
|
|
if (nbGraves > 0) {
|
|
// Gestion blessure graves : -1 pt endurance par blessure grave
|
|
await this.santeIncDec("endurance", -nbGraves);
|
|
}
|
|
}
|
|
|
|
async setSonne(sonne = true) {
|
|
if (!game.combat && sonne) {
|
|
ui.notifications.info(`${this.name} est hors combat, il ne reste donc pas sonné`);
|
|
return;
|
|
}
|
|
await this.setEffect(STATUSES.StatusStunned, sonne);
|
|
}
|
|
|
|
getSonne() {
|
|
return this.getEffect(STATUSES.StatusStunned);
|
|
}
|
|
|
|
isEffectAllowed(effectId) { return true }
|
|
|
|
/* -------------------------------------------- */
|
|
async computeEtatGeneral() { this.system.compteurs.etat.value = this.malusVie() + this.malusFatigue() + this.malusEthylisme() }
|
|
getEtatGeneral(options = { ethylisme: false }) { return this.system.compteurs.etat.value }
|
|
|
|
malusVie() { return Math.min(this.system.sante.vie.value - this.system.sante.vie.max, 0) }
|
|
malusEthylisme() { return 0 }
|
|
|
|
|
|
}
|