From a0213fb552dc40631de59563c24e15e4902f0575 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Wed, 13 Jan 2021 03:42:13 +0100 Subject: [PATCH] #101 Gestion des status de surprise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La demi-surprise ou surprise dépend des états: - les TMRs sont ouvertes (sauf visu) - le personnage est sonné - un status parmi: prone, restrain - si inconscient ou aveugle, surprise totale Ajout de la possibilité de filtrer les status --- module/actor-sheet.js | 11 +- module/actor.js | 156 ++++++++++++++++++---- module/rdd-bonus.js | 4 +- module/rdd-combat.js | 58 ++++---- module/rdd-main.js | 30 ++--- module/rdd-resolution-table.js | 4 +- module/rdd-roll.js | 6 +- module/rdd-tmr-dialog.js | 5 +- module/rolldata-ajustements.js | 7 +- module/status-effects.js | 128 ++++++++++++++++++ templates/actor-sheet.html | 22 ++- templates/chat-resultat-attaque.html | 4 +- templates/chat-resultat-encaissement.html | 2 +- templates/dialog-competence.html | 5 +- templates/dialog-roll-ajustements.html | 9 +- templates/status-effects-settings.html | 16 +++ 16 files changed, 371 insertions(+), 96 deletions(-) create mode 100644 module/status-effects.js create mode 100644 templates/status-effects-settings.html diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 1cca5819..c72a189b 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -8,6 +8,7 @@ import { HtmlUtility } from "./html-utility.js"; import { RdDItem } from "./item.js"; import { RdDItemArme } from "./item-arme.js"; import { RdDItemCompetence } from "./item-competence.js"; +import { RdDBonus } from "./rdd-bonus.js"; /* -------------------------------------------- */ export class RdDActorSheet extends ActorSheet { @@ -108,6 +109,7 @@ export class RdDActorSheet extends ActorSheet { // Common data data.data.competenceByCategory = data.competenceByCategory; data.data.encTotal = this.actor.encTotal; + data.data.surprise = RdDBonus.find(this.actor.getSurprise(false)).descr; data.data.isGM = game.user.isGM; data.ajustementsConditions = CONFIG.RDD.ajustementsConditions; data.difficultesLibres = CONFIG.RDD.difficultesLibres; @@ -126,7 +128,6 @@ export class RdDActorSheet extends ActorSheet { data.data.vehiculesList = this.actor.buildVehiculesList(); data.data.monturesList = this.actor.buildMonturesList(); data.data.suivantsList = this.actor.buildSuivantsList(); - return data; } @@ -332,7 +333,9 @@ export class RdDActorSheet extends ActorSheet { html.find('#dormir-chateau-dormant').click((event) => { this.actor.dormirChateauDormant(); }); - + html.find('#enlever-tous-effets').click((event) => { + this.actor.enleverTousLesEffets(); + }); // Display info about queue html.find('.queuesouffle-label a').click((event) => { let myID = event.currentTarget.attributes['data-item-id'].value; @@ -483,6 +486,10 @@ export class RdDActorSheet extends ActorSheet { this.actor.santeIncDec("endurance", -1); this.render(true); }); + html.find('.data-sante-sonne').click((event) => { + this.actor.setSonne(event.currentTarget.checked); + this.render(true); + }); html.find('#ptreve-actuel-plus').click((event) => { this.actor.reveActuelIncDec(1); this.render(true); diff --git a/module/actor.js b/module/actor.js index 9d8c0d55..f931bb56 100644 --- a/module/actor.js +++ b/module/actor.js @@ -18,6 +18,8 @@ import { RdDAudio } from "./rdd-audio.js"; import { RdDItemCompetence } from "./item-competence.js"; import { RdDItemArme } from "./item-arme.js"; import { RdDAlchimie } from "./rdd-alchimie.js"; +import { StatusEffects } from "./status-effects.js"; + /* -------------------------------------------- */ /** @@ -27,7 +29,11 @@ import { RdDAlchimie } from "./rdd-alchimie.js"; 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("updateToken", (scene, token, data, options) => { RdDActor.onUpdateToken(scene, token, data, options) }); } + /* -------------------------------------------- */ /** * Override the create() function to provide additional RdD functionality. @@ -249,24 +255,19 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - getSurprise() { - // TODO: gérer une liste de flags demi-surprise (avec icône sur le token)? - if (this.getSonne()) { + getSurprise(isCombat = true) { + let niveauSurprise = Array.from(this.effects?.values() ?? []) + .map(effect => StatusEffects.valeurSurprise(effect.data, isCombat)) + .reduce((a,b)=> a+b, 0); + if (niveauSurprise>1) { + return 'totale'; + } + if (niveauSurprise==1 || this.getSonne()) { return 'demi'; } return ''; } - /* -------------------------------------------- */ - isDemiSurprise() { - return this.getSurprise() == 'demi'; - } - - /* -------------------------------------------- */ - isSurpriseTotale() { - return this.getSurprise() == 'totale'; - } - /* -------------------------------------------- */ async dormirChateauDormant() { let message = { @@ -1059,17 +1060,31 @@ export class RdDActor extends Actor { return !this.isEntiteCauchemar() && (this.data.data.sante.sonne?.value ?? false); } - /* -------------------------------------------- */ - getSConst() { - - if (!this.isEntiteCauchemar() && this.data.data.attributs) { - return this.data.data.attributs.sconst.value; + async setSonne(sonne = true) { + if (this.isEntiteCauchemar()) { + return; } - return 0; + await this.setStatusSonne(sonne); + await this.setStateSonne(sonne); + } + + async setStateSonne(sonne) { + if (this.isEntiteCauchemar()) { + return; + } + await this.update({ "data.sante.sonne.value": sonne }); } /* -------------------------------------------- */ - testSiSonne(sante, endurance) { + getSConst() { + if (this.isEntiteCauchemar()) { + return 0; + } + return this.data.data.attributs?.sconst?.value ?? 0; + } + + /* -------------------------------------------- */ + async testSiSonne(sante, endurance) { const roll = new Roll("1d20").roll(); roll.showDice = true; RdDDice.show(roll); @@ -1084,6 +1099,7 @@ export class RdDActor extends Actor { } if (result.sonne) { // 20 is always a failure + await this.setSonne(); sante.sonne.value = true; } return result; @@ -1156,7 +1172,7 @@ export class RdDActor extends Actor { const perte = compteur.value - result.newValue; if (perte > 1) { // Peut-être sonné si 2 points d'endurance perdus d'un coup - const testIsSonne = this.testSiSonne(sante, result.newValue); + const testIsSonne = await this.testSiSonne(sante, result.newValue); result.sonne = testIsSonne.sonne; result.jetEndurance = testIsSonne.roll.total; } else if (inc > 0) { @@ -2589,6 +2605,100 @@ export class RdDActor extends Actor { await this.update( { 'data.subacteurs.suivants': newSuivants }); await this.update( { 'data.subacteurs.montures': newMontures }); } + /* -------------------------------------------- */ + async onCreateActiveEffect(effect, options) { + switch (StatusEffects.statusId(effect)) { + case 'sonne': + await this.setStateSonne(true); + return; + } + } + + /* -------------------------------------------- */ + async onDeleteActiveEffect(effect, options) { + switch (StatusEffects.statusId(effect)) { + case 'sonne': + await this.setStateSonne(false); + 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) + } + else { + this.deleteEffect(StatusEffects.demiReve(), options) + } + } + + /* -------------------------------------------- */ + async setStatusSonne(sonne) { + if (this.isEntiteCauchemar()) { + return; + } + const id = 'sonne'; + const options = { renderSheet: true/*, noHook: from == 'hook' */ }; + + if (sonne) { + await this.addById(id, options); + } + else /* if (!sonne)*/ { + this.deleteById(id, options) + } + } + + /* -------------------------------------------- */ + deleteById(id, options) { + const effects = Array.from(this.effects?.values()) + .filter(it => it.data.flags.core?.statusId == id); + this._deleteAll(effects, options); + } + + /* -------------------------------------------- */ + deleteEffect(effect, options) { + const toDelete = Array.from(this.effects?.values()) + .filter(it => StatusEffects.statusId(it.data) == StatusEffects.statusId(effect)); + this._deleteAll(toDelete, options); + } + + /* -------------------------------------------- */ + _deleteAll(effects, options) { + this._deleteAllIds(effects.map(it => it.id), options); + } + + /* -------------------------------------------- */ + _deleteAllIds(effectIds, options) { + this.deleteEmbeddedEntity('ActiveEffect', effectIds, options); + this.applyActiveEffects(); + } + + /* -------------------------------------------- */ + async addById(id, options) { + const statusEffect = CONFIG.statusEffects.find(it => it.id == id); + await this.addEffect(statusEffect, options); + } + + /* -------------------------------------------- */ + async addEffect(statusEffect, options) { + this.deleteById(statusEffect.id, options); + const effet = duplicate(statusEffect); + effet["flags.core.statusId"] = effet.id; + await this.createEmbeddedEntity('ActiveEffect', effet, options); + this.applyActiveEffects(); + } } - - diff --git a/module/rdd-bonus.js b/module/rdd-bonus.js index a0d21036..bce20f3d 100644 --- a/module/rdd-bonus.js +++ b/module/rdd-bonus.js @@ -36,9 +36,9 @@ export class RdDBonus { } else { dmg.dmgArme = RdDBonus._dmgArme(rollData); dmg.penetration = RdDBonus._peneration(rollData); - dmg.dmgTactique = RdDBonus.dmgBonus(rollData.ajustements?.tactique); + dmg.dmgTactique = RdDBonus.dmgBonus(rollData.tactique); dmg.dmgParticuliere = RdDBonus._dmgParticuliere(rollData); - dmg.dmgSurprise = RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris); + dmg.dmgSurprise = RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used); dmg.dmgActor = rollData.selectedCarac ? RdDBonus._dmgPerso(dmgActor, rollData.selectedCarac.label, dmg.dmgArme) : 0; dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere; dmg.mortalite = RdDBonus._calculMortalite(rollData, isCauchemar) diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 427030de..43cd3efa 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -387,10 +387,11 @@ export class RdDCombat { passeArme: randomID(16), coupsNonMortels: false, competence: competence, - surprise: this.attacker.getSurprise(), - surpriseDefenseur: this.defender.getSurprise(), + surprise: this.attacker.getSurprise(true), + surpriseDefenseur: this.defender.getSurprise(true), essais: {} }; + rollData.diviseurSignificative = this._getDiviseurSignificative(rollData); if (this.attacker.isCreature()) { RdDItemCompetence.setRollDataCreature(rollData); @@ -456,7 +457,8 @@ export class RdDCombat { const paramDemandeDefense = { passeArme: attackerRoll.passeArme, essais: attackerRoll.essais, - surprise: this.defender.getSurprise(), + // surprise: this.defender.getSurprise(true), + // surprise: attackerRoll.ajustements.attaqueDefenseurSurpris.used, defender: this.defender, attackerId: this.attackerId, defenderTokenId: this.defenderTokenId, @@ -557,10 +559,9 @@ export class RdDCombat { } _prepareParade(attackerRoll, armeParade) { - const isCreature = this.defender.isCreature(); const compName = armeParade.data.competence; const armeAttaque = attackerRoll.arme; - + let rollData = { passeArme: attackerRoll.passeArme, forceValue: this.defender.getForceValue(), @@ -568,14 +569,15 @@ export class RdDCombat { attackerRoll: attackerRoll, competence: this.defender.getCompetence(compName), arme: armeParade, - surprise: this.defender.getSurprise(), + surprise: this.defender.getSurprise(true), needParadeSignificative: RdDItemArme.needParadeSignificative(armeAttaque, armeParade), needResist: RdDItemArme.needArmeResist(armeAttaque, armeParade), carac: this.defender.data.data.carac, show: {} }; - rollData.diviseur = this._getDiviseurSignificative(rollData); - if (isCreature) { + rollData.diviseurSignificative = this._getDiviseurSignificative(rollData); + + if (this.defender.isCreature()) { RdDItemCompetence.setRollDataCreature(rollData); } return rollData; @@ -583,7 +585,13 @@ export class RdDCombat { /* -------------------------------------------- */ _getDiviseurSignificative(defenderRoll) { - let facteurSign = (this.defender.isDemiSurprise() || defenderRoll.needParadeSignificative) ? 2 : 1; + let facteurSign = 1; + if (defenderRoll.surprise == 'demi'){ + facteurSign *= 2; + } + if (defenderRoll.needParadeSignificative) { + facteurSign *= 2; + } if (RdDBonus.isDefenseAttaqueFinesse(defenderRoll)) { facteurSign *= 2; } @@ -656,12 +664,12 @@ export class RdDCombat { diffLibre: attackerRoll.diffLibre, attackerRoll: attackerRoll, competence: competence, - surprise: this.defender.getSurprise(), - surpriseDefenseur: this.defender.getSurprise(), + surprise: this.defender.getSurprise(true), + surpriseDefenseur: this.defender.getSurprise(true), carac: this.defender.data.data.carac, show: {} }; - rollData.diviseur = this._getDiviseurSignificative(rollData); + rollData.diviseurSignificative = this._getDiviseurSignificative(rollData); if (this.defender.isCreature()) { RdDItemCompetence.setRollDataCreature(rollData); @@ -678,21 +686,21 @@ export class RdDCombat { } /* -------------------------------------------- */ - async _onEsquiveNormale(rollData) { - console.log("RdDCombat._onEsquiveNormal >>>", rollData); - this._consumeDefense(rollData.passeArme); - await RdDResolutionTable.displayRollData(rollData, this.defender, 'chat-resultat-esquive.html'); + async _onEsquiveNormale(defenderRoll) { + console.log("RdDCombat._onEsquiveNormal >>>", defenderRoll); + this._consumeDefense(defenderRoll.passeArme); + await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html'); } /* -------------------------------------------- */ - async _onEsquiveEchec(rollData) { - console.log("RdDCombat._onEsquiveEchec >>>", rollData); + async _onEsquiveEchec(defenderRoll) { + console.log("RdDCombat._onEsquiveEchec >>>", defenderRoll); - await RdDResolutionTable.displayRollData(rollData, this.defender, 'chat-resultat-esquive.html'); + await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html'); - this.removeChatMessageActionsPasseArme(rollData.passeArme); - this._sendMessageDefense(rollData.attackerRoll, { defense: true }) - this._storeDefense(rollData); + this.removeChatMessageActionsPasseArme(defenderRoll.passeArme); + this._sendMessageDefense(defenderRoll.attackerRoll, { defense: true }) + this._storeDefense(defenderRoll); } /* -------------------------------------------- */ @@ -776,12 +784,6 @@ export class RdDCombat { return Misc.toInt(this.defender.data.data.carac.taille.value) - (attaque.forceValue + attaque.arme.data.dommagesReels); } - /* -------------------------------------------- */ - _sendMessageEncaisser(rollData) { - let message = "" + this.defender.name + " doit:" + this._buildMessageEncaisser(rollData); - RdDCombat._sendRollMessage(this.attacker, this.defender, this.defenderTokenId, "msg_encaisser", message, rollData); - } - /* -------------------------------------------- */ async encaisser(attackerRoll, defenderTokenId) { defenderTokenId = defenderTokenId || this.defenderTokenId; diff --git a/module/rdd-main.js b/module/rdd-main.js index 80ed4442..fc513fa7 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -23,6 +23,7 @@ import { RdDCommands } from "./rdd-commands.js"; import { RdDCombat } from "./rdd-combat.js"; import { ChatUtility } from "./chat-utility.js"; import { RdDItemCompetence } from "./item-competence.js"; +import { StatusEffects } from "./status-effects.js"; /* -------------------------------------------- */ /* Foundry VTT Initialization */ @@ -104,9 +105,7 @@ Hooks.once("init", async function () { // preload handlebars templates RdDUtility.preloadHandlebarsTemplates(); // Create useful storage space - game.system.rdd = { - TMRUtility: TMRUtility - } + game.system.rdd = { TMRUtility: TMRUtility } /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar", { @@ -180,7 +179,7 @@ Hooks.once("init", async function () { // Set an initiative formula for the system CONFIG.Combat.initiative = { formula: "1d20", - decimals: 2 + decimals: 0 }; /* -------------------------------------------- */ @@ -202,23 +201,10 @@ Hooks.once("init", async function () { /* -------------------------------------------- */ // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorSheet, { - types: ["personnage"], - makeDefault: true - } - ); - Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorCreatureSheet, { - types: ["creature"], - makeDefault: true - }); - Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorVehiculeSheet, { - types: ["vehicule"], - makeDefault: true - }); - Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorEntiteSheet, { - types: ["entite"], - makeDefault: true - }); + Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorSheet, { types: ["personnage"], makeDefault: true }); + Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorCreatureSheet, { types: ["creature"], makeDefault: true }); + Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true }); + Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorEntiteSheet, { types: ["entite"], makeDefault: true }); Items.unregisterSheet("core", ItemSheet); Items.registerSheet("foundryvtt-reve-de-dragon", RdDItemSheet, { makeDefault: true }); @@ -232,6 +218,8 @@ Hooks.once("init", async function () { RdDCommands.init(); RdDCombat.init(); RdDTokenHud.init(); + RdDActor.init(); + StatusEffects.init(); }); /* -------------------------------------------- */ diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js index fd516b21..16c2a806 100644 --- a/module/rdd-resolution-table.js +++ b/module/rdd-resolution-table.js @@ -82,7 +82,7 @@ export class RdDResolutionTable { static explain(rolled) { let message = "
Jet : " + rolled.roll + " sur " + rolled.score + "% "; if (rolled.caracValue != null && rolled.finalLevel != null) { - message += (rolled.diviseur > 1 ? `(1/${rolled.diviseur} de ` : "(") + message += (rolled.diviseurSignificative > 1 ? `(1/${rolled.diviseurSignificative} de ` : "(") + rolled.caracValue + " à " + Misc.toSignedString(rolled.finalLevel) + ") "; } message += '' + rolled.quality + '' @@ -104,7 +104,7 @@ export class RdDResolutionTable { /* -------------------------------------------- */ static async rollData(rollData) { - rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData.bonus, rollData.diviseur, rollData.showDice); + rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData.bonus, rollData.diviseurSignificative, rollData.showDice); return rollData; } diff --git a/module/rdd-roll.js b/module/rdd-roll.js index e24ff8e9..6b998e44 100644 --- a/module/rdd-roll.js +++ b/module/rdd-roll.js @@ -40,7 +40,7 @@ export class RdDRoll extends Dialog { moral: actor.getMoralTotal(), carac: actor.data.data.carac, finalLevel: 0, - diffConditions: rollData.arme ? RdDBonus.bonusAttaque(rollData.surpriseDefenseur) : 0, + diffConditions: 0, diffLibre: rollData.competence?.data.default_diffLibre ?? 0, editLibre: true, editConditions: true, @@ -54,7 +54,7 @@ export class RdDRoll extends Dialog { useMalusEncTotal: false, encTotal: actor.getEncTotal(), ajustementAstrologique: actor.ajustementAstrologique(), - surprise: actor.getSurprise(), + surprise: actor.getSurprise(false), } mergeObject(rollData, defaultRollData, { recursive: true, overwrite: false }); RollDataAjustements.calcul(rollData, actor); @@ -225,7 +225,7 @@ export class RdDRoll extends Dialog { $("#compdialogTitle").text(this._getTitle(rollData)); $('#coupsNonMortels').prop('checked', rollData.coupsNonMortels); $("#dmg-arme-actor").text(dmgText); - $("#defenseur-surprise").text(RdDBonus.description(rollData.surpriseDefenseur)); +// $("#defenseur-surprise").text(RdDBonus.description(rollData.ajustements.attaqueDefenseurSurpris.descr)); $('.table-ajustement').remove(); $(".table-resolution").remove(); $(".table-proba-reussite").remove(); diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index ebef73c7..4e827961 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -51,14 +51,17 @@ export class RdDTMRDialog extends Dialog { this.rencontreState = 'aucune'; this.pixiApp = new PIXI.Application({ width: 720, height: 860 }); if (!this.viewOnly){ + this.actor.setStatusDemiReve(true); this._tellToGM(this.actor.name + " monte dans les terres médianes (" + mode + ")"); } } - + /* -------------------------------------------- */ close() { this.actor.santeIncDec("fatigue", this.nbFatigue).then(super.close()); // moving 1 cell costs 1 fatigue this.actor.tmrApp = undefined; // Cleanup reference + this.actor.setStatusDemiReve(false); + this._tellToGM(this.actor.name + " a quitté les terres médianes") } diff --git a/module/rolldata-ajustements.js b/module/rolldata-ajustements.js index 5139a5da..f3bf16b2 100644 --- a/module/rolldata-ajustements.js +++ b/module/rolldata-ajustements.js @@ -88,8 +88,9 @@ export const referenceAjustements = { getValue: (rollData, actor) => actor.ajustementAstrologique() }, facteurSign: { - isUsed: (rollData, actor) => rollData.diviseur > 1, - getDescr: (rollData, actor) => rollData.diviseur > 1 ? `Facteur significative ×${Misc.getFractionHtml(rollData.diviseur)}` : '' + isUsed: (rollData, actor) => rollData.diviseurSignificative > 1, + getLabel: (rollData, actor) => Misc.getFractionHtml(rollData.diviseurSignificative), + getDescr: (rollData, actor) => rollData.diviseurSignificative > 1 ? `Facteur significative ×${Misc.getFractionHtml(rollData.diviseurSignificative)}` : '' }, finesse: { isUsed: (rollData, actor) => RdDBonus.isDefenseAttaqueFinesse(rollData), @@ -100,7 +101,7 @@ export const referenceAjustements = { getDescr: (rollData, actor) => rollData.attackerRoll && rollData.arme? `${RdDItemArme.getNomCategorieParade(rollData.attackerRoll?.arme)} vs ${RdDItemArme.getNomCategorieParade(rollData.arme)}`: '' }, surprise: { - isUsed: (rollData, actor) => actor.getSurprise(), + isUsed: (rollData, actor) => actor.getSurprise(rollData.passeArme), getDescr: (rollData, actor) => RdDBonus.find(actor.getSurprise()).descr }, bonusCase: { diff --git a/module/status-effects.js b/module/status-effects.js new file mode 100644 index 00000000..cf420867 --- /dev/null +++ b/module/status-effects.js @@ -0,0 +1,128 @@ + +const rddStatusEffects = [ + { id: 'sonne', rdd: true, label: 'Sonné', icon: 'icons/svg/stoned.svg' }, +]; +const demiReveStatusEffect = { id: 'demi-reve', rdd: true, label: 'Demi-rêve', icon: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg' }; +const rddPrivateStatusEffects = [demiReveStatusEffect,]; +const statusDemiSurpriseCombat = new Set(['sonne', 'demi-reve', 'prone', 'restrain']); +const statusDemiSurprise = new Set(['sonne', 'prone', 'restrain']); +const statusSurpriseTotale = new Set(['unconscious', 'blind']); + +export class StatusEffects { + static init() { + StatusEffects.setCoreStatusId(rddPrivateStatusEffects); + StatusEffects.setCoreStatusId(rddStatusEffects); + const defaultUseStatusEffect = CONFIG.statusEffects.map(it => it.id).join(); + game.settings.register("foundryvtt-reve-de-dragon", "use-status-effects", { + name: "use-status-effects", + scope: "world", + config: false, + default: defaultUseStatusEffect, + type: String + }); + + game.settings.registerMenu("foundryvtt-reve-de-dragon", "select-status-effect", { + name: "Choisir les effets disponibles", + label: "Choix des effets", + hint: "Ouvre la fenêtre de sélection des effets/status appliqués aux acteurs", + icon: "fas fa-bars", + type: StatusEffectsSettings, + restricted: true + }); + CONFIG.RDD.allEffects = rddStatusEffects.concat(CONFIG.statusEffects); + + StatusEffects._setUseStatusEffects(StatusEffects._getUseStatusEffects()); + + console.log('statusEffects', CONFIG.statusEffects); + } + + static valeurSurprise(effect, isCombat) { + const id = StatusEffects.statusId(effect); + if (statusSurpriseTotale.has(id)) { + return 2; + } + const status = (isCombat ? statusDemiSurpriseCombat : statusDemiSurprise); + return status.has(id) ? 1 : 0; + } + + static statusId(effectData) { + return effectData.flags?.core?.statusId ?? effectData["flags.core.statusId"]; + } + + static setCoreStatusId(list) { + list.forEach(it => { + it.flags = { core: { statusId: it.id } }; + it["flags.core.statusId"] = it.id; + }); + } + + static _getUseStatusEffects() { + const setting = game.settings.get("foundryvtt-reve-de-dragon", "use-status-effects"); + return setting ? new Set(setting.split(',')) : new Set(); + } + + static _setUseStatusEffects(useStatusEffects) { + game.settings.set("foundryvtt-reve-de-dragon", "use-status-effects", StatusEffects._toSetting(useStatusEffects)); + + for (let effect of CONFIG.RDD.allEffects) { + effect.active = effect.rdd || useStatusEffects.has(effect.id); + } + CONFIG.statusEffects = CONFIG.RDD.allEffects.filter(it => it.active); + } + + static _toSetting(useStatusEffects) { + return Array.from(useStatusEffects).join(); + } + + static demiReve() { + return demiReveStatusEffect; + } +} + +class StatusEffectsSettings extends FormApplication { + constructor(...args) { + super(...args); + } + + static get defaultOptions() { + const options = super.defaultOptions; + mergeObject(options, { + id: "status-effects-settings", + template: "systems/foundryvtt-reve-de-dragon/templates/status-effects-settings.html", + height: "auto", + width: 350, + minimizable: false, + closeOnSubmit: true, + title: "Choix des status/effets" + }); + return options; + } + + getData() { + let data = super.getData(); + data.effects = CONFIG.RDD.allEffects; + return data; + } + + activateListeners(html) { + html.find(".select-effect").click((event) => { + let id = event.currentTarget.attributes.name?.value; + if (id) { + let selected = StatusEffects._getUseStatusEffects(); + let isChecked = event.currentTarget.checked; + if (isChecked) { + selected.add(id); + } + else { + selected.delete(id); + } + StatusEffects._setUseStatusEffects(selected); + } + }); + } + + async _updateObject(event, formData) { + this.close(); + } +} + diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index 27eab69d..439a78c8 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -1,3 +1,4 @@ +{{log "handlebar actor-sheet" this}}
{{!-- Sheet Header --}} @@ -40,7 +41,7 @@
  • @@ -71,10 +72,23 @@ {{data.blessures.resume}}
    - {{data.compteurs.etat.label}}: {{data.compteurs.etat.value}} + {{data.compteurs.etat.label}}: {{data.compteurs.etat.value}} + {{data.compteurs.surenc.label}}: {{data.compteurs.surenc.value}}
    -
    - {{data.compteurs.surenc.label}}: {{data.compteurs.surenc.value}} +
    + {{#if actor.effects}} + {{data.surprise}}! + {{#each actor.effects as |effect key|}} + + {{effect.label}} + + {{/each}} + {{#if data.isGM}} + (enlever tout) + {{/if}} + {{else}} + Aucun effet actif + {{/if}}
    diff --git a/templates/chat-resultat-attaque.html b/templates/chat-resultat-attaque.html index 577e07f9..498a04ae 100644 --- a/templates/chat-resultat-attaque.html +++ b/templates/chat-resultat-attaque.html @@ -1,5 +1,5 @@ -

    {{alias}} attaque: {{arme.name}}

    -
    {{selectedCarac.label}}{{#unless (eq selectedCarac.label competence.name)}} / {{competence.name}}{{/unless}}, difficulté {{diffLibre}}
    +

    {{alias}} attaque à {{diffLibre}}: {{arme.name}}

    +
    {{selectedCarac.label}}{{#unless (eq selectedCarac.label competence.name)}} / {{competence.name}}{{/unless}}
    {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
    {{#if tactique}} diff --git a/templates/chat-resultat-encaissement.html b/templates/chat-resultat-encaissement.html index 8413dd3e..086e2c41 100644 --- a/templates/chat-resultat-encaissement.html +++ b/templates/chat-resultat-encaissement.html @@ -24,7 +24,7 @@ {{#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 critique 0)}}une blessure critique + {{else if (gt critiques 0)}}une blessure critique {{else}}Rien du tout {{/if}} diff --git a/templates/dialog-competence.html b/templates/dialog-competence.html index 5b56c11e..1d8b4025 100644 --- a/templates/dialog-competence.html +++ b/templates/dialog-competence.html @@ -1,3 +1,4 @@ +{{log "handlebar dialog-competence" this}}

    @@ -54,8 +55,8 @@ {{/if}} - {{#if ajustements.attaqueDefenseurSurpris}} - + {{#if ajustements.attaqueDefenseurSurpris.used}} + {{/if}}
    {{/if}} diff --git a/templates/dialog-roll-ajustements.html b/templates/dialog-roll-ajustements.html index ffb2ec8f..e555db73 100644 --- a/templates/dialog-roll-ajustements.html +++ b/templates/dialog-roll-ajustements.html @@ -1,5 +1,5 @@
    - + + {{#if ajustements.facteurSign.used}} + + Significative requise ×{{{ajustements.facteurSign.label}}}! + + {{/if}}
    \ No newline at end of file diff --git a/templates/status-effects-settings.html b/templates/status-effects-settings.html new file mode 100644 index 00000000..a6862f12 --- /dev/null +++ b/templates/status-effects-settings.html @@ -0,0 +1,16 @@ + + +
    +