Status inconscious/prone/dead

inconscious: si endurance à 0
prone: si chute sur un recul
dead: lors de la deuxième critique, ou si la vie passe en dessous de
-SConst
This commit is contained in:
Vincent Vandemeulebrouck 2021-01-24 19:52:02 +01:00
parent 44e34af397
commit 56bb117a0c
6 changed files with 80 additions and 56 deletions

View File

@ -32,6 +32,7 @@ export class RdDActor extends Actor {
static init() { static init() {
Hooks.on("deleteActiveEffect", (actor, effect, options) => actor.onDeleteActiveEffect(effect, options)); Hooks.on("deleteActiveEffect", (actor, effect, options) => actor.onDeleteActiveEffect(effect, options));
Hooks.on("createActiveEffect", (actor, effect, options) => actor.onCreateActiveEffect(effect, options)); Hooks.on("createActiveEffect", (actor, effect, options) => actor.onCreateActiveEffect(effect, options));
Hooks.on("updateActor", (actor, update, options, actorId) => actor.onUpdateActor(update, options, actorId));
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -99,18 +100,18 @@ export class RdDActor extends Actor {
// Make separate methods for each Actor type (character, npc, etc.) to keep // Make separate methods for each Actor type (character, npc, etc.) to keep
// things organized. // things organized.
if (actorData.type === 'personnage') this._prepareCharacterData(actorData); if (actorData.type === 'personnage') this._prepareCharacterData(actorData);
if (actorData.type === 'creature') this.prepareCreatureData(actorData); if (actorData.type === 'creature') this._prepareCreatureData(actorData);
if (actorData.type === 'vehicule') this.prepareVehiculeData(actorData); if (actorData.type === 'vehicule') this._prepareVehiculeData(actorData);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
prepareCreatureData(actorData) { _prepareCreatureData(actorData) {
this.computeEncombrementTotalEtMalusArmure(); this.computeEncombrementTotalEtMalusArmure();
this.computeEtatGeneral(); this.computeEtatGeneral();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
prepareVehiculeData( actorData ) { _prepareVehiculeData( actorData ) {
this.computeEncombrementTotalEtMalusArmure(); this.computeEncombrementTotalEtMalusArmure();
} }
@ -1105,7 +1106,7 @@ export class RdDActor extends Actor {
if (this.isEntiteCauchemar()) { if (this.isEntiteCauchemar()) {
return 0; return 0;
} }
return this.data.data.attributs?.sconst?.value ?? 0; return RdDUtility.calculSConst(this.data.data.carac.constitution.value);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -1177,18 +1178,14 @@ export class RdDActor extends Actor {
sonne: false, sonne: false,
}; };
let minValue = 0; let minValue = name == "vie" ? -this.getSConst()-1 : 0;
if (this.type == 'personnage') {
// TODO: les animaux/humanoïdes on théoriquement aussi un sconst, mais la SPA n'est pas passé par là
minValue = name == "vie" ? -Number(this.data.data.attributs.sconst.value) : 0;
}
result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max)); result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max));
//console.log("New value ", inc, minValue, result.newValue); //console.log("New value ", inc, minValue, result.newValue);
let fatigue = 0; let fatigue = 0;
if (name == "endurance" && this.data.type != 'entite') { if (name == "endurance" && !this.isEntiteCauchemar()) {
if (!isCritique && result.newValue == 0 && inc < 0) { // perte endurance et endurance devient 0 -> -1 vie sauf si coup critique if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie
sante.vie.value = sante.vie.value - 1; sante.vie.value --;
} }
result.newValue = Math.max(0, result.newValue); result.newValue = Math.max(0, result.newValue);
if (inc > 0) { // le max d'endurance s'applique seulement à la récupération if (inc > 0) { // le max d'endurance s'applique seulement à la récupération
@ -1202,6 +1199,7 @@ export class RdDActor extends Actor {
result.jetEndurance = testIsSonne.roll.total; result.jetEndurance = testIsSonne.roll.total;
} else if (inc > 0) { } else if (inc > 0) {
await this.setSonne(false); await this.setSonne(false);
sante.sonne.value = false;
} }
if (sante.fatigue && inc < 0) { // Each endurance lost -> fatigue lost if (sante.fatigue && inc < 0) { // Each endurance lost -> fatigue lost
fatigue = perte; fatigue = perte;
@ -1213,6 +1211,9 @@ export class RdDActor extends Actor {
if (sante.fatigue && fatigue > 0) { if (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._computeFatigueMin());
} }
if (!this.isEntiteCauchemar() && sante.vie.value<-this.getSConst()) {
await this.addStatusEffectById('dead');
}
await this.update({ "data.sante": sante }); await this.update({ "data.sante": sante });
return result; return result;
@ -2366,8 +2367,11 @@ export class RdDActor extends Actor {
count--; count--;
} else { } else {
// TODO: status effect dead // TODO: status effect dead
ChatMessage.create({ content: `<strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>` }); this.addStatusEffectById('dead');
ChatMessage.create({ content: `<img class="chat-icon" src="icons/svg/skull.svg" alt="charge" height="32" width="32" />
<strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>` });
encaissement.critiques -= count; encaissement.critiques -= count;
encaissement.mort = true;
break; break;
} }
} }
@ -2639,6 +2643,13 @@ export class RdDActor extends Actor {
await this.update( { 'data.subacteurs.montures': newMontures }); await this.update( { 'data.subacteurs.montures': newMontures });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async onUpdateActor(update, options, actorId) {
const updatedEndurance = update?.data?.sante?.endurance;
if (updatedEndurance && options.diff) {
this.forceStatusEffectId('unconscious', updatedEndurance.value == 0);
}
}
/* -------------------------------------------- */
async onCreateActiveEffect(effect, options) { async onCreateActiveEffect(effect, options) {
switch (StatusEffects.statusId(effect)) { switch (StatusEffects.statusId(effect)) {
case 'sonne': case 'sonne':
@ -2646,7 +2657,7 @@ export class RdDActor extends Actor {
return; return;
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async onDeleteActiveEffect(effect, options) { async onDeleteActiveEffect(effect, options) {
switch (StatusEffects.statusId(effect)) { switch (StatusEffects.statusId(effect)) {
@ -2655,80 +2666,81 @@ export class RdDActor extends Actor {
return; return;
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
enleverTousLesEffets() { enleverTousLesEffets() {
this.deleteEmbeddedEntity('ActiveEffect', Array.from(this.effects?.keys() ?? [])); this.deleteEmbeddedEntity('ActiveEffect', Array.from(this.effects?.keys() ?? []));
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
listeEffets(matching = it => true) { listeEffets(matching = it => true) {
const all = Array.from(this.effects?.values() ?? []); const all = Array.from(this.effects?.values() ?? []);
const filtered = all.filter(it => matching(it.data)); const filtered = all.filter(it => matching(it.data));
return filtered; return filtered;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async setStatusDemiReve(status) { async setStatusDemiReve(status) {
const options = { renderSheet: true/*, noHook: from == 'hook' */ };
if (status) { if (status) {
await this.addEffect(StatusEffects.demiReve(), options) await this.addStatusEffect(StatusEffects.demiReve())
} }
else { else {
this.deleteEffect(StatusEffects.demiReve(), options) this.deleteStatusEffect(StatusEffects.demiReve())
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async setStatusSonne(sonne) { async setStatusSonne(sonne) {
if (this.isEntiteCauchemar()) { if (this.isEntiteCauchemar()) {
return; return;
} }
const id = 'sonne'; await this.forceStatusEffectId('sonne', sonne);
const options = { renderSheet: true/*, noHook: from == 'hook' */ }; }
/* -------------------------------------------- */
async forceStatusEffectId(statusId, sonne) {
if (sonne) { if (sonne) {
await this.addById(id, options); await this.addStatusEffectById(statusId);
} }
else /* if (!sonne)*/ { else {
this.deleteById(id, options) this.deleteStatusEffectById(statusId);
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
deleteById(id, options) { deleteStatusEffectById(id, options = { renderSheet: true}) {
const effects = Array.from(this.effects?.values()) const effects = Array.from(this.effects?.values())
.filter(it => it.data.flags.core?.statusId == id); .filter(it => it.data.flags.core?.statusId == id);
this._deleteAll(effects, options); this._deleteStatusEffects(effects, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
deleteEffect(effect, options) { deleteStatusEffect(effect, options = { renderSheet: true}) {
const toDelete = Array.from(this.effects?.values()) const toDelete = Array.from(this.effects?.values())
.filter(it => StatusEffects.statusId(it.data) == StatusEffects.statusId(effect)); .filter(it => StatusEffects.statusId(it.data) == StatusEffects.statusId(effect));
this._deleteAll(toDelete, options); this._deleteStatusEffects(toDelete, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_deleteAll(effects, options) { _deleteStatusEffects(effects, options) {
this._deleteAllIds(effects.map(it => it.id), options); this._deleteStatusEffectsByIds(effects.map(it => it.id), options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_deleteAllIds(effectIds, options) { _deleteStatusEffectsByIds(effectIds, options) {
this.deleteEmbeddedEntity('ActiveEffect', effectIds, options); this.deleteEmbeddedEntity('ActiveEffect', effectIds, options);
this.applyActiveEffects(); this.applyActiveEffects();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async addById(id, options) { async addStatusEffectById(id, options = { renderSheet: true}) {
const statusEffect = CONFIG.statusEffects.find(it => it.id == id); const statusEffect = CONFIG.statusEffects.find(it => it.id == id);
await this.addEffect(statusEffect, options); await this.addStatusEffect(statusEffect, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async addEffect(statusEffect, options) { async addStatusEffect(statusEffect, options = { renderSheet: true}) {
this.deleteById(statusEffect.id, options); this.deleteStatusEffectById(statusEffect.id, options);
const effet = duplicate(statusEffect); const effet = duplicate(statusEffect);
effet["flags.core.statusId"] = effet.id; effet["flags.core.statusId"] = effet.id;
await this.createEmbeddedEntity('ActiveEffect', effet, options); await this.createEmbeddedEntity('ActiveEffect', effet, options);

View File

@ -562,7 +562,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async choixParticuliere(rollData, choix) { async choixParticuliere(rollData, choix) {
console.log("RdDCombat.choixParticuliere >>>", rollData, choix); console.log("RdDCombat.choixParticuliere >>>", rollData, choix);
this.removeChatMessageActionsPasseArme(rollData.passeArme); this.removeChatMessageActionsPasseArme(rollData.passeArme);
rollData.particuliere = choix; rollData.particuliere = choix;
await this._onAttaqueNormale(rollData); await this._onAttaqueNormale(rollData);
@ -783,31 +783,38 @@ export class RdDCombat {
} }
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async computeRecul(defenderRoll) { // Calcul du recul (p. 132) async computeRecul(defenderRoll) { // Calcul du recul (p. 132)
const attackerRoll = defenderRoll.attackerRoll; const attackerRoll = defenderRoll.attackerRoll;
if (this._isAttaqueCauseRecul(attackerRoll)) { if (this._isAttaqueCauseRecul(attackerRoll)) {
const impact = this._computeImpactRecul(attackerRoll); const impact = this._computeImpactRecul(attackerRoll);
const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact }); const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact });
if (rollRecul.rolled.isSuccess) { if (rollRecul.rolled.isSuccess) {
defenderRoll.show.recul = 'encaisse'; defenderRoll.show.recul = 'encaisse';
} else if (rollRecul.rolled.isETotal) { } else if (rollRecul.rolled.isETotal || this._isReculCauseChute(impact)) {
defenderRoll.show.recul = 'chute'; defenderRoll.show.recul = 'chute';
await this.defender.addStatusEffectById('prone');
} }
else { else {
const agilite = this.defender.getAgilite(); defenderRoll.show.recul = 'recul';
const chute = await RdDResolutionTable.rollData({ caracValue: agilite, finalLevel: impact });
defenderRoll.show.recul = (chute.rolled.isSuccess) ? 'recul' : 'chute';
} }
} }
} }
/* -------------------------------------------- */
async _isReculCauseChute(impact) {
const agilite = this.defender.getAgilite();
const chute = await RdDResolutionTable.rollData({ caracValue: agilite, finalLevel: impact });
return chute.rolled.isEchec;
}
/* -------------------------------------------- */
_isAttaqueCauseRecul(attaque) { _isAttaqueCauseRecul(attaque) {
return attaque.particuliere == 'force' || attaque.tactique == 'charge'; return attaque.particuliere == 'force' || attaque.tactique == 'charge';
} }
/* -------------------------------------------- */
_computeImpactRecul(attaque) { _computeImpactRecul(attaque) {
const taille = this.defender.getTaille(); const taille = this.defender.getTaille();
const force = this.attacker.getForce(); const force = this.attacker.getForce();

View File

@ -468,7 +468,7 @@ export class RdDUtility {
let tailleData = tableCaracDerivee[bonusDomKey]; let tailleData = tableCaracDerivee[bonusDomKey];
data.attributs.plusdom.value = tailleData.plusdom; data.attributs.plusdom.value = tailleData.plusdom;
data.attributs.sconst.value = tableCaracDerivee[Number(data.carac.constitution.value)].sconst; data.attributs.sconst.value = RdDUtility.calculSConst(data.carac.constitution.value);
data.attributs.sust.value = tableCaracDerivee[Number(data.carac.taille.value)].sust; data.attributs.sust.value = tableCaracDerivee[Number(data.carac.taille.value)].sust;
data.attributs.encombrement.value = (parseInt(data.carac.force.value) + parseInt(data.carac.taille.value)) / 2; data.attributs.encombrement.value = (parseInt(data.carac.force.value) + parseInt(data.carac.taille.value)) / 2;
@ -489,6 +489,10 @@ export class RdDUtility {
data.compteurs.chance.max = data.carac.chance.value; data.compteurs.chance.max = data.carac.chance.value;
} }
static calculSConst(constitution) {
return Number(tableCaracDerivee[Number(constitution)].sconst);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static getSegmentsFatigue(maxEnd) { static getSegmentsFatigue(maxEnd) {
maxEnd = Math.max(maxEnd, 1); maxEnd = Math.max(maxEnd, 1);

View File

@ -5,7 +5,7 @@ const rddStatusEffects = [
demiReveStatusEffect demiReveStatusEffect
]; ];
const statusDemiSurprise = new Set(['sonne', 'prone', 'restrain']); const statusDemiSurprise = new Set(['sonne', 'prone', 'restrain']);
const statusSurpriseTotale = new Set(['unconscious', 'blind']); const statusSurpriseTotale = new Set(['unconscious', 'blind', 'dead']);
export class StatusEffects { export class StatusEffects {
static onReady() { static onReady() {

View File

@ -41,7 +41,7 @@
<li> <li>
<label class="ctn-sonne"> <label class="ctn-sonne">
Sonné : Sonné :
<input class="resource-content data-sante-sonne" type="checkbox" value="{{data.sante.sonne.value}}" {{#if data.sante.sonne.value}}checked{{/if}} /> <input class="resource-content data-sante-sonne" type="checkbox" {{#if data.sante.sonne.value}}checked{{/if}} />
</label> </label>
</li> </li>
<li> <li>

View File

@ -20,13 +20,14 @@
{{/unless}} {{/unless}}
{{/unless}}, total: <span class="rdd-roll-echec">{{total}}</span> {{/unless}}, total: <span class="rdd-roll-echec">{{total}}</span>
<br> <br>
{{alias}} subit {{alias}}
{{#if (eq dmg.mortalite 'cauchemar')}}le coup {{#if (eq dmg.mortalite 'cauchemar')}}subit le coup
{{else if (gt eraflures 0)}}une contusion {{else if eraflures}}subit une contusion
{{else if (gt legeres 0)}}une blessure légère {{else if legeres}}subit une blessure légère
{{else if (gt graves 0)}}une blessure grave {{else if graves}}subit une blessure grave
{{else if (gt critiques 0)}}une blessure critique {{else if critiques}}subit une blessure critique
{{else}}Rien du tout {{else if mort}}vient de mourir
{{else}}s'en sort sans une égratignure
{{/if}} {{/if}}
({{dmg.loc.label}}) ({{dmg.loc.label}})
{{#if (gt endurance 0)}} {{#if (gt endurance 0)}}