From 56bb117a0c76eeb9a6ecd7fcf3756dc3bec36c7d Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Sun, 24 Jan 2021 19:52:02 +0100 Subject: [PATCH] Status inconscious/prone/dead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- module/actor.js | 90 +++++++++++++---------- module/rdd-combat.js | 21 ++++-- module/rdd-utility.js | 6 +- module/status-effects.js | 2 +- templates/actor-sheet.html | 2 +- templates/chat-resultat-encaissement.html | 15 ++-- 6 files changed, 80 insertions(+), 56 deletions(-) diff --git a/module/actor.js b/module/actor.js index cf6b7268..d0c77749 100644 --- a/module/actor.js +++ b/module/actor.js @@ -32,6 +32,7 @@ export class RdDActor extends Actor { static init() { Hooks.on("deleteActiveEffect", (actor, effect, options) => actor.onDeleteActiveEffect(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 // things organized. if (actorData.type === 'personnage') this._prepareCharacterData(actorData); - if (actorData.type === 'creature') this.prepareCreatureData(actorData); - if (actorData.type === 'vehicule') this.prepareVehiculeData(actorData); + if (actorData.type === 'creature') this._prepareCreatureData(actorData); + if (actorData.type === 'vehicule') this._prepareVehiculeData(actorData); } /* -------------------------------------------- */ - prepareCreatureData(actorData) { + _prepareCreatureData(actorData) { this.computeEncombrementTotalEtMalusArmure(); this.computeEtatGeneral(); } /* -------------------------------------------- */ - prepareVehiculeData( actorData ) { + _prepareVehiculeData( actorData ) { this.computeEncombrementTotalEtMalusArmure(); } @@ -1105,7 +1106,7 @@ export class RdDActor extends Actor { if (this.isEntiteCauchemar()) { 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, }; - let minValue = 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; - } + 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" && this.data.type != 'entite') { - if (!isCritique && result.newValue == 0 && inc < 0) { // perte endurance et endurance devient 0 -> -1 vie sauf si coup critique - sante.vie.value = sante.vie.value - 1; + if (name == "endurance" && !this.isEntiteCauchemar()) { + if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie + sante.vie.value --; } result.newValue = Math.max(0, result.newValue); 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; } else if (inc > 0) { await this.setSonne(false); + sante.sonne.value = false; } if (sante.fatigue && inc < 0) { // Each endurance lost -> fatigue lost fatigue = perte; @@ -1213,6 +1211,9 @@ export class RdDActor extends Actor { if (sante.fatigue && fatigue > 0) { 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 }); return result; @@ -2366,8 +2367,11 @@ export class RdDActor extends Actor { count--; } else { // TODO: status effect dead - ChatMessage.create({ content: `${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !` }); + this.addStatusEffectById('dead'); + ChatMessage.create({ content: `charge + ${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !` }); encaissement.critiques -= count; + encaissement.mort = true; break; } } @@ -2639,6 +2643,13 @@ export class RdDActor extends Actor { 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) { switch (StatusEffects.statusId(effect)) { case 'sonne': @@ -2646,7 +2657,7 @@ export class RdDActor extends Actor { return; } } - + /* -------------------------------------------- */ async onDeleteActiveEffect(effect, options) { switch (StatusEffects.statusId(effect)) { @@ -2655,80 +2666,81 @@ export class RdDActor extends Actor { return; } } - + /* -------------------------------------------- */ enleverTousLesEffets() { this.deleteEmbeddedEntity('ActiveEffect', Array.from(this.effects?.keys() ?? [])); } - + /* -------------------------------------------- */ listeEffets(matching = it => true) { const all = Array.from(this.effects?.values() ?? []); const filtered = all.filter(it => matching(it.data)); return filtered; } - + /* -------------------------------------------- */ async setStatusDemiReve(status) { - const options = { renderSheet: true/*, noHook: from == 'hook' */ }; if (status) { - await this.addEffect(StatusEffects.demiReve(), options) + await this.addStatusEffect(StatusEffects.demiReve()) } else { - this.deleteEffect(StatusEffects.demiReve(), options) + this.deleteStatusEffect(StatusEffects.demiReve()) } } - + /* -------------------------------------------- */ async setStatusSonne(sonne) { if (this.isEntiteCauchemar()) { return; } - const id = 'sonne'; - const options = { renderSheet: true/*, noHook: from == 'hook' */ }; - + await this.forceStatusEffectId('sonne', sonne); + } + + /* -------------------------------------------- */ + async forceStatusEffectId(statusId, sonne) { if (sonne) { - await this.addById(id, options); + await this.addStatusEffectById(statusId); } - else /* if (!sonne)*/ { - this.deleteById(id, options) + else { + this.deleteStatusEffectById(statusId); } } /* -------------------------------------------- */ - deleteById(id, options) { + deleteStatusEffectById(id, options = { renderSheet: true}) { const effects = Array.from(this.effects?.values()) .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()) .filter(it => StatusEffects.statusId(it.data) == StatusEffects.statusId(effect)); - this._deleteAll(toDelete, options); + this._deleteStatusEffects(toDelete, options); } /* -------------------------------------------- */ - _deleteAll(effects, options) { - this._deleteAllIds(effects.map(it => it.id), options); + _deleteStatusEffects(effects, options) { + this._deleteStatusEffectsByIds(effects.map(it => it.id), options); } /* -------------------------------------------- */ - _deleteAllIds(effectIds, options) { + _deleteStatusEffectsByIds(effectIds, options) { this.deleteEmbeddedEntity('ActiveEffect', effectIds, options); this.applyActiveEffects(); } /* -------------------------------------------- */ - async addById(id, options) { + async addStatusEffectById(id, options = { renderSheet: true}) { const statusEffect = CONFIG.statusEffects.find(it => it.id == id); - await this.addEffect(statusEffect, options); + await this.addStatusEffect(statusEffect, options); } /* -------------------------------------------- */ - async addEffect(statusEffect, options) { - this.deleteById(statusEffect.id, options); + async addStatusEffect(statusEffect, options = { renderSheet: true}) { + this.deleteStatusEffectById(statusEffect.id, options); const effet = duplicate(statusEffect); effet["flags.core.statusId"] = effet.id; await this.createEmbeddedEntity('ActiveEffect', effet, options); diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 4191f302..6df4d093 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -562,7 +562,7 @@ export class RdDCombat { /* -------------------------------------------- */ async choixParticuliere(rollData, choix) { console.log("RdDCombat.choixParticuliere >>>", rollData, choix); - + this.removeChatMessageActionsPasseArme(rollData.passeArme); rollData.particuliere = choix; await this._onAttaqueNormale(rollData); @@ -783,31 +783,38 @@ export class RdDCombat { } } } + /* -------------------------------------------- */ async computeRecul(defenderRoll) { // Calcul du recul (p. 132) const attackerRoll = defenderRoll.attackerRoll; if (this._isAttaqueCauseRecul(attackerRoll)) { - const impact = this._computeImpactRecul(attackerRoll); const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact }); - if (rollRecul.rolled.isSuccess) { defenderRoll.show.recul = 'encaisse'; - } else if (rollRecul.rolled.isETotal) { + } else if (rollRecul.rolled.isETotal || this._isReculCauseChute(impact)) { defenderRoll.show.recul = 'chute'; + await this.defender.addStatusEffectById('prone'); } else { - const agilite = this.defender.getAgilite(); - const chute = await RdDResolutionTable.rollData({ caracValue: agilite, finalLevel: impact }); - defenderRoll.show.recul = (chute.rolled.isSuccess) ? 'recul' : 'chute'; + defenderRoll.show.recul = 'recul'; } } } + /* -------------------------------------------- */ + async _isReculCauseChute(impact) { + const agilite = this.defender.getAgilite(); + const chute = await RdDResolutionTable.rollData({ caracValue: agilite, finalLevel: impact }); + return chute.rolled.isEchec; + } + + /* -------------------------------------------- */ _isAttaqueCauseRecul(attaque) { return attaque.particuliere == 'force' || attaque.tactique == 'charge'; } + /* -------------------------------------------- */ _computeImpactRecul(attaque) { const taille = this.defender.getTaille(); const force = this.attacker.getForce(); diff --git a/module/rdd-utility.js b/module/rdd-utility.js index da714c1f..0e11e0bb 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -468,7 +468,7 @@ export class RdDUtility { let tailleData = tableCaracDerivee[bonusDomKey]; 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.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; } + static calculSConst(constitution) { + return Number(tableCaracDerivee[Number(constitution)].sconst); + } + /* -------------------------------------------- */ static getSegmentsFatigue(maxEnd) { maxEnd = Math.max(maxEnd, 1); diff --git a/module/status-effects.js b/module/status-effects.js index 57d2b315..a5efcf6e 100644 --- a/module/status-effects.js +++ b/module/status-effects.js @@ -5,7 +5,7 @@ const rddStatusEffects = [ demiReveStatusEffect ]; const statusDemiSurprise = new Set(['sonne', 'prone', 'restrain']); -const statusSurpriseTotale = new Set(['unconscious', 'blind']); +const statusSurpriseTotale = new Set(['unconscious', 'blind', 'dead']); export class StatusEffects { static onReady() { diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index 5597990d..e6d3a159 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -41,7 +41,7 @@
  • diff --git a/templates/chat-resultat-encaissement.html b/templates/chat-resultat-encaissement.html index 10a7ab0e..86fa9811 100644 --- a/templates/chat-resultat-encaissement.html +++ b/templates/chat-resultat-encaissement.html @@ -20,13 +20,14 @@ {{/unless}} {{/unless}}, total: {{total}}
    - {{alias}} subit - {{#if (eq dmg.mortalite 'cauchemar')}}le coup - {{else if (gt eraflures 0)}}une contusion - {{else if (gt legeres 0)}}une blessure légère - {{else if (gt graves 0)}}une blessure grave - {{else if (gt critiques 0)}}une blessure critique - {{else}}Rien du tout + {{alias}} + {{#if (eq dmg.mortalite 'cauchemar')}}subit le coup + {{else if eraflures}}subit une contusion + {{else if legeres}}subit une blessure légère + {{else if graves}}subit une blessure grave + {{else if critiques}}subit une blessure critique + {{else if mort}}vient de mourir + {{else}}s'en sort sans une égratignure {{/if}} ({{dmg.loc.label}}) {{#if (gt endurance 0)}}