diff --git a/lang/fr.json b/lang/fr.json index 6d5cbe9b..94d60c4a 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -40,5 +40,19 @@ "TypeOmbre": "Ombre de Thanatos", "TypeSouffle": "Souffle de Dragon", "TypeTete": "Tête de Dragon" + }, + "EFFECT": { + "StatusStunned": "Sonné", + "StatusUnconscious": "Inconscient", + "StatusBlind": "Aveugle", + "StatusBleeding": "Saigne", + "StatusProne": "Au sol", + "StatusUnarmed": "Désarmé", + "StatusGrappling": "Empoignade", + "StatusGrappled": "Empoigné", + "StatusRestrained": "Immobilisé", + "StatusComma": "Comma", + "StatusDead": "Mort", + "StatusDemiReve": "Demi-rêve" } -} +} \ No newline at end of file diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 8ebde729..5f02626f 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -103,7 +103,7 @@ export class RdDActorSheet extends ActorSheet { formData.difficultesLibres = CONFIG.RDD.difficultesLibres; formData.hautreve = { - isDemiReve: this.actor.listeEffets( it => it.label == "Demi-rêve").length > 0, + isDemiReve: this.actor.getEffectByLabel("Demi-rêve"), sortsReserve: formData.data.reve.reserve.list, rencontres: duplicate(formData.data.reve.rencontre.list), casesTmr: formData.itemsByType.casetmr, @@ -384,6 +384,10 @@ export class RdDActorSheet extends ActorSheet { html.find('.repos').click(async event => { await DialogRepos.create(this.actor); }); + html.find('.delete-active-effect').click(async event => { + let id = $(event.currentTarget).parents(".active-effect").data('id'); + this.actor.enleverActiveEffectById(id); + }); html.find('.enlever-tous-effets').click(async event => { this.actor.enleverTousLesEffets(); }); diff --git a/module/actor.js b/module/actor.js index 14654f01..d52d29eb 100644 --- a/module/actor.js +++ b/module/actor.js @@ -32,7 +32,6 @@ import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js"; import { RollDataAjustements } from "./rolldata-ajustements.js"; import { DialogItemAchat } from "./dialog-item-achat.js"; import { RdDItem } from "./item.js"; - /* -------------------------------------------- */ /** * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. @@ -42,7 +41,6 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ static init() { Hooks.on("deleteActiveEffect", (effect, options, userId) => RdDActor.getParentActor(effect)?.onDeleteActiveEffect(effect, options)); - Hooks.on("createActiveEffect", (effect, options, userId) => RdDActor.getParentActor(effect)?.onCreateActiveEffect(effect, options)); Hooks.on("preUpdateItem", (item, change, options, id) => RdDActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id)); Hooks.on("createItem", (item, options, id) => RdDActor.getParentActor(item)?.onCreateItem(item, options, id)); @@ -424,7 +422,7 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ getSurprise(isCombat = undefined) { - let niveauSurprise = Array.from(this.effects?.values() ?? []) + let niveauSurprise = this.getActiveEffects() .map(effect => StatusEffects.valeurSurprise(effect.data, isCombat)) .reduce(Misc.sum(), 0); if (niveauSurprise > 1) { @@ -1507,13 +1505,12 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ getSonne() { - let data = Misc.templateData(this); - return !this.isEntiteCauchemar() && (data.sante?.sonne?.value ?? false); + return this.getEffectByLabel("EFFECT.StatusStunned"); } /* -------------------------------------------- */ getSonneRound() { - return !this.isEntiteCauchemar() && (Misc.templateData(this).sante.sonne?.round ?? false); + return Misc.templateData(this).sante.sonne?.round ?? -1; } /* -------------------------------------------- */ @@ -1528,23 +1525,12 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ async setSonne(sonne = true) { - if (this.isEntiteCauchemar()) { + if (!game.combat || this.isEntiteCauchemar()) { return; } - let round = (sonne && game.combat) ? game.combat.current.round : -1; // Sauvegarde du round de sonné en cas de combat - await this.setStatusSonne(sonne); - await this.setStateSonne(sonne, round); - } - - /* -------------------------------------------- */ - async setStateSonne(sonne, round = -1) { - if (this.isEntiteCauchemar()) { - return; - } - let sonneData = duplicate(Misc.templateData(this).sante.sonne); - sonneData.value = sonne; - sonneData.round = round; - await this.update({ "data.sante.sonne": sonneData }); + await this.setStatusEffect("EFFECT.StatusStunned", sonne, { + duration: (sonne && game.combat) ? { rounds: 2 } : undefined + }); } /* -------------------------------------------- */ @@ -1695,7 +1681,7 @@ export class RdDActor extends Actor { } await this.update({ "data.sante": sante }); if (this.isDead()) { - await this.addStatusEffectById('dead'); + await this.setStatusEffect("EFFECT.StatusComma", true); } return result; } @@ -3078,7 +3064,7 @@ export class RdDActor extends Actor { ui.notifications.warn("Vous êtes déja dans les TMR...."); return } - let demiReve = this.listeEffets(it => it.label == "Demi-rêve"); + let demiReve = this.getActiveEffects(it => it.data.label == "Demi-rêve"); if (mode != 'visu' && demiReve.length > 0) { ui.notifications.warn("Le joueur ou le MJ est déja dans les Terres Médianes avec ce personnage ! Visualisation uniquement"); mode = "visu"; // bascule le mode en visu automatiquement @@ -3094,7 +3080,7 @@ export class RdDActor extends Actor { }); return; } - await this.setStatusDemiReve(true); + await this.setStatusEffect("EFFECT.StatusDemiReve", true); } const actorData = Misc.data(this); @@ -3351,7 +3337,7 @@ export class RdDActor extends Actor { count--; } else { // TODO: status effect dead - this.addStatusEffectById('dead'); + this.setStatusEffect("EFFECT.StatusComma", true); ChatMessage.create({ content: `charge ${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !` @@ -3990,109 +3976,64 @@ export class RdDActor extends Actor { 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': - await this.setStateSonne(true); - return; + await this.setStatusEffect("EFFECT.StatusUnconscious", updatedEndurance.value == 0); } } /* -------------------------------------------- */ async onDeleteActiveEffect(effect, options) { - switch (StatusEffects.statusId(effect)) { - case 'sonne': - await this.setStateSonne(false); + switch (effect.label) { + case 'EFFECT.StatusStunned': return; } } /* -------------------------------------------- */ - enleverTousLesEffets() { - const ids = Array.from(this.effects?.keys() ?? []); - this.deleteEmbeddedDocuments('ActiveEffect', ids); + getActiveEffects(matching = it => true) { + return Array.from(this.getEmbeddedCollection("ActiveEffect").values()).filter(it => matching(it)); } /* -------------------------------------------- */ - listeEffets(matching = it => true) { - const all = Array.from(this.effects?.values() ?? []); - const filtered = all.filter(it => matching(it.data)); - return filtered; + getEffectByLabel(label) { + return this.getActiveEffects().find(it => it.data.label == label); } /* -------------------------------------------- */ - async setStatusDemiReve(status) { - const demiReve = StatusEffects.demiReve(); - if (status) { - await this.addStatusEffect(demiReve) - } else { - await this.deleteStatusEffect(demiReve) - } + getEffectById(id) { + return this.getActiveEffects().find(it => it.id == id); } /* -------------------------------------------- */ - async setStatusSonne(sonne) { - if (this.isEntiteCauchemar()) { + async setStatusEffect(label, status, updates = {}) { + if (this.isEntiteCauchemar() || this.data.type == 'vehicule') { return; } - await this.forceStatusEffectId('sonne', sonne); - } - - /* -------------------------------------------- */ - async forceStatusEffectId(statusId, isSet) { - if (isSet) { - await this.addStatusEffectById(statusId); + console.log("setStatusEffect", label, status, updates) + const existing = this.getEffectByLabel(label); + if (existing) { + existing.delete(); } - else { - await this.deleteStatusEffectById(statusId); + if (status) { + const statusEffect = mergeObject(duplicate(StatusEffects.status(label)), updates); + await this.createEmbeddedDocuments("ActiveEffect", [statusEffect]); } } - /* -------------------------------------------- */ - async deleteStatusEffectById(id) { - - const ids = Array.from(this.effects?.values()) - .filter(it => it.data.flags.core?.statusId == id) - .map(it => it.id); - console.log("Delete effect IDS1: ", this.effects, ids); - if (ids.length > 0) { - await this.deleteEmbeddedDocuments('ActiveEffect', ids); + enleverActiveEffectById(id) { + if (game.user.isGM){ + const existing = this.getEffectById(id); + if (existing) { + existing.delete(); + } } } - /* -------------------------------------------- */ - async deleteStatusEffect(effect) { - const ids = Array.from(this.effects?.values()) - .filter(it => StatusEffects.statusId(it.data) == StatusEffects.statusId(effect)) - .map(it => it.id); - console.log("Delete effect 1: ", this.effects, ids); - if (ids.length > 0) { - await this.deleteEmbeddedDocuments('ActiveEffect', ids); + enleverTousLesEffets() { + if (game.user.isGM){ + this.deleteEmbeddedDocuments('ActiveEffect', this.getActiveEffects().map(it => it.id)); } } - /* -------------------------------------------- */ - async addStatusEffectById(id) { - const statusEffect = CONFIG.statusEffects.find(it => it.id == id); - await this.addStatusEffect(statusEffect); - } - - /* -------------------------------------------- */ - async addStatusEffect(statusEffect) { - const effet = Misc.data(statusEffect); - await this.deleteStatusEffectById(effet.id); - effet.flags = effet.flags ?? { core: {} }; - effet.flags.core.statusId = effet.id; - let effectArray = await this.createEmbeddedDocuments('ActiveEffect', [effet]); - //if (effectArray[0]) { - //await effectArray[0].setFlag('core', 'statusId', effet.id); - //} - } - /* -------------------------------------------- */ async onPreUpdateItem(item, change, options, id) { const itemData = Misc.data(item); diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 7b5f7ccd..b7904ac6 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -1217,7 +1217,7 @@ export class RdDCombat { defenderRoll.show.recul = 'encaisse'; } else if (rollRecul.rolled.isETotal || this._isReculCauseChute(impact)) { defenderRoll.show.recul = 'chute'; - await this.defender.addStatusEffectById('prone'); + await this.defender.setStatusEffect("EFFECT.StatusProne", true); } else { defenderRoll.show.recul = 'recul'; diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index 48770b48..41c3ac73 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -274,7 +274,7 @@ export class RdDTMRDialog extends Dialog { if ( this.actor.tmrApp ) { this.actor.tmrApp = undefined; // Cleanup reference if ( !this.viewOnly ) { - this.actor.setStatusDemiReve(false); + this.actor.setStatusEffect("EFFECT.StatusDemiReve", false); this._tellToGM(this.actor.name + " a quitté les terres médianes"); } this.actor.santeIncDec("fatigue", this.cumulFatigue).then(super.close()); // moving 1 cell costs 1 fatigue diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 6de5f96c..40f64b44 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -115,6 +115,7 @@ export class RdDUtility { 'systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.html', 'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-competence-partial.html', 'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-categorie-competences-partial.html', + 'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-effects-partial.html', 'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-oeuvre-partial.html', 'systems/foundryvtt-reve-de-dragon/templates/actor-liste-blessures-partial.html', 'systems/foundryvtt-reve-de-dragon/templates/actor-blessure-partial.html', diff --git a/module/status-effects.js b/module/status-effects.js index 5b05324b..59319ab1 100644 --- a/module/status-effects.js +++ b/module/status-effects.js @@ -1,23 +1,31 @@ -const demiReveStatusEffect = { id: 'demi-reve', rdd: true, label: 'Demi-rêve', icon: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg' }; const rddStatusEffects = [ - { id: 'sonne', rdd: true, label: 'Sonné', icon: 'icons/svg/stoned.svg' }, - demiReveStatusEffect + { rdd: true, id: 'stun', label: 'EFFECT.StatusStunned', icon: 'icons/svg/stoned.svg' }, + { rdd: true, id: 'bleeding', label: 'EFFECT.StatusBleeding', icon: 'icons/svg/blood.svg' }, + { rdd: true, id: 'prone', label: 'EFFECT.StatusProne', icon: 'icons/svg/falling.svg' }, + { rdd: true, id: 'grappling', tint: '#33cc33', label: 'EFFECT.StatusGrappling', icon: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp' }, + { rdd: true, id: 'grappled', tint: '#ff9900', label: 'EFFECT.StatusGrappled', icon: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp' }, + { rdd: true, id: 'restrain', label: 'EFFECT.StatusRestrained', icon: 'icons/svg/net.svg' }, + { rdd: true, id: 'unconscious', label: 'EFFECT.StatusUnconscious', icon: 'icons/svg/unconscious.svg' }, + { rdd: true, id: 'blind', label: 'EFFECT.StatusBlind', icon: 'icons/svg/blind.svg' }, + { rdd: true, id: 'comma', label: 'EFFECT.StatusComma', icon: 'icons/svg/skull.svg' }, + { rdd: true, id: 'dead', label: 'EFFECT.StatusDead', icon: 'icons/svg/skull.svg' }, + { rdd: true, id: 'demi-reve', label: 'EFFECT.StatusDemiReve', icon: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg' } ]; -const statusDemiSurprise = new Set(['sonne', 'prone', 'restrain']); -const statusSurpriseTotale = new Set(['unconscious', 'blind', 'dead']); +const demiReveStatusEffect = rddStatusEffects.find(it => it.label == 'EFFECT.StatusDemiReve'); + +const statusDemiSurprise = new Set(['EFFECT.StatusStunned', 'EFFECT.StatusProne', 'EFFECT.StatusRestrain']); +const statusSurpriseTotale = new Set(['EFFECT.StatusUnconscious', 'EFFECT.StatusBlind', 'EFFECT.StatusComma']); export class StatusEffects { static onReady() { - StatusEffects.setCoreStatusId([demiReveStatusEffect]); - StatusEffects.setCoreStatusId(rddStatusEffects); - StatusEffects.setMandatoryRdd(); - const defaultUseStatusEffect = CONFIG.statusEffects.map(it => it.id).join(); + const rddStatusIds = rddStatusEffects.map(it => it.id); + const defaultStatusEffectIds = CONFIG.statusEffects.map(it => it.id); game.settings.register("foundryvtt-reve-de-dragon", "use-status-effects", { name: "use-status-effects", scope: "world", config: false, - default: defaultUseStatusEffect, + default: defaultStatusEffectIds.join(), type: String }); @@ -29,30 +37,21 @@ export class StatusEffects { type: StatusEffectsSettings, restricted: true }); - CONFIG.RDD.allEffects = rddStatusEffects.concat(CONFIG.statusEffects); - + + CONFIG.RDD.allEffects = rddStatusEffects.concat(CONFIG.statusEffects.filter(it => !rddStatusIds.includes(it.id))); + StatusEffects._setUseStatusEffects(StatusEffects._getUseStatusEffects()); console.log('statusEffects', CONFIG.statusEffects); } static valeurSurprise(effect, isCombat) { - const id = StatusEffects.statusId(effect); - if (statusSurpriseTotale.has(id)) { + // const id = StatusEffects.statusId(effect); + if (statusSurpriseTotale.has(effect.label)) { return 2; } - return statusDemiSurprise.has(id) || (isCombat && id == demiReveStatusEffect.id) ? 1 : 0; + return statusDemiSurprise.has(effect.label) || (isCombat && effect.label == demiReveStatusEffect.label) ? 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 setMandatoryRdd() { CONFIG.statusEffects.filter(it => statusDemiSurprise.has(it.id) || statusSurpriseTotale.has(it.id)) .forEach(it => it.rdd = true); @@ -78,6 +77,9 @@ export class StatusEffects { return Array.from(useStatusEffects).join(); } + static status(label) { + return rddStatusEffects.find(it => it.label == label) ?? { label: label }; + } static demiReve() { return demiReveStatusEffect; } diff --git a/styles/simple.css b/styles/simple.css index ff9b32a0..e925f788 100644 --- a/styles/simple.css +++ b/styles/simple.css @@ -271,6 +271,11 @@ table {border: 1px solid #7a7971;} height: 16; border-width: 0; } +.button-effect-img:hover { + color: rgba(255, 255, 128, 0.7); + border: 1px solid rgba(255, 128, 0, 0.8); + cursor: pointer; +} .small-button-container { height: 16px; diff --git a/templates/actor-creature-sheet.html b/templates/actor-creature-sheet.html index 70b233a5..c7f2395e 100644 --- a/templates/actor-creature-sheet.html +++ b/templates/actor-creature-sheet.html @@ -18,19 +18,7 @@ {{calc.resumeBlessures}}
- {{#if calc.surprise}}{{calc.surprise}}! {{/if}} - {{#if effects}} - {{#each effects as |effect key|}} - - {{effect.label}} - - {{/each}} - {{#if options.isGM}} - (enlever tout) - {{/if}} - {{else}} - Aucun effet actif - {{/if}} + {{>"systems/foundryvtt-reve-de-dragon/templates/actor-sheet-effects-partial.html"}}
diff --git a/templates/actor-sheet-effects-partial.html b/templates/actor-sheet-effects-partial.html new file mode 100644 index 00000000..3e4ada8f --- /dev/null +++ b/templates/actor-sheet-effects-partial.html @@ -0,0 +1,13 @@ +{{#if calc.surprise}}{{calc.surprise}}! {{/if}} +{{#if effects}} +{{#each effects as |effect key|}} + + {{localize effect.label}} + +{{/each}} +{{#if options.isGM}} +(enlever tout) +{{/if}} +{{else}} +Aucun effet actif +{{/if}} diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index e6da1fc8..a02afb10 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -74,20 +74,8 @@ {{data.compteurs.etat.label}}: {{data.compteurs.etat.value}} {{data.compteurs.surenc.label}}: {{data.compteurs.surenc.value}} -
- {{#if calc.surprise}}{{calc.surprise}}! {{/if}} - {{#if effects}} - {{#each effects as |effect key|}} - - {{effect.label}} - - {{/each}} - {{#if options.isGM}} - (enlever tout) - {{/if}} - {{else}} - Aucun effet actif - {{/if}} +
+ {{>"systems/foundryvtt-reve-de-dragon/templates/actor-sheet-effects-partial.html"}}
diff --git a/templates/status-effects-settings.html b/templates/status-effects-settings.html index a6862f12..d1f0a757 100644 --- a/templates/status-effects-settings.html +++ b/templates/status-effects-settings.html @@ -8,7 +8,7 @@ {{/if}} {{ localize  effect.label}} - + {{/each}}