From fb919eb007324cb80ca9f70d82aa4bd75cb5b3e2 Mon Sep 17 00:00:00 2001 From: Eligarf Date: Wed, 3 Apr 2024 15:45:43 -0700 Subject: [PATCH] module dependant visibility hooks --- ChangeLog.md | 7 ++++ languages/en.json | 4 +- languages/fr.json | 4 +- languages/pt-BR.json | 4 +- module.json | 19 +-------- scripts/engine.js | 90 +++++++++++++--------------------------- scripts/hooks.js | 12 ------ scripts/systems/dnd4e.js | 6 +-- scripts/systems/dnd5e.js | 42 ++++++++++++++++--- scripts/systems/pf1.js | 6 +-- scripts/systems/pf2e.js | 6 +-- 11 files changed, 91 insertions(+), 109 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 875dc10..095e7cf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,10 @@ +# v3.14.2 +* Dnd5e: Temporary workaround for vision-5e hooks while I work on properly hooking up Stealthy to it. +* Dnd5e: Updated pt-BR translations (Thanks Kharmans) +* Dnd5e: Added both name and label fields to the language .json files for Dim/Dark +* PF1: Disabled Stealthy until I can figure out a better way to capture skill rolls +* Dnd4e: Disabled Stealthy until I can figure out a better way to capture skill rolls + # v3.14.1 * Dnd5e: Adding a setting to select old reliable way or new experimental way of hooking the visibility tests. * PF2e: disabled for the time being, need a better way of hooking hide/seek actions. diff --git a/languages/en.json b/languages/en.json index ddabd8c..710c3f1 100644 --- a/languages/en.json +++ b/languages/en.json @@ -71,10 +71,12 @@ }, "dark": { "label": "Dark", + "name": "Dark", "key": "Localization key for Dark label" }, "dim": { "label": "Dim", + "name": "Dim", "key": "Localization key for Dim label" }, "spotPair": { @@ -90,7 +92,7 @@ "hint": "Enables a homebrew option to ignore the passive perception value when rolling an active perception check" }, "friendlyUmbralSight": { - "name": "How to treat friendly tokens with Umbral Sight", + "name": "DEPRECATED - How to treat friendly tokens with Umbral Sight", "allow": "Umbral Sight is hidden to Darkvision", "inCombat": "Ignore Umbral Sight unless in combat", "ignore": "Ignore Umbral Sight" diff --git a/languages/fr.json b/languages/fr.json index f06e0ec..41299da 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -71,10 +71,12 @@ }, "dark": { "label": "Dark", + "name": "Dark", "key": "Clé de localisation pour Dark" }, "dim": { "label": "Faible", + "name": "Faible", "key": "Clé de localisation pour Faible" }, "spotPair": { @@ -90,7 +92,7 @@ "hint": "Permet à une option homebrew d'ignorer la valeur de perception passive lors d'un test de perception active" }, "friendlyUmbralSight": { - "name": "Comment traiter les tokens alliés avec Vision des ombres", + "name": "DEPRECATED - Comment traiter les tokens alliés avec Vision des ombres", "allow": "Alliés avec Vision des ombres sont cachés", "inCombat": "Ignore Vision des ombres sauf en combat", "ignore": "Ignorer Vision des ombres" diff --git a/languages/pt-BR.json b/languages/pt-BR.json index 6e9d010..750eabc 100644 --- a/languages/pt-BR.json +++ b/languages/pt-BR.json @@ -71,10 +71,12 @@ }, "dark": { "label": "Escuridão", + "name": "Escuridão", "key": "Chave de localização para rótulo Escuridão." }, "dim": { "label": "Penumbra", + "name": "Penumbra", "key": "Chave de localização para rótulo Penumbra." }, "spotPair": { @@ -90,7 +92,7 @@ "hint": "Ativa uma opção de homebrew para ignorar o valor de Percepção Passiva ao realizar um teste de Percepção Ativa." }, "friendlyUmbralSight": { - "name": "Como tratar fichas amigas com Visão Umbral", + "name": "DEPRECATED - Como tratar fichas amigas com Visão Umbral", "allow": "Visão Umbral está escondida para Darkvision", "inCombat": "Ignore Visão Umbral a menos que esteja em combate", "ignore": "Ignorar Visão Umbral" diff --git a/module.json b/module.json index 5a151c1..0781dad 100644 --- a/module.json +++ b/module.json @@ -15,26 +15,12 @@ }, "relationships": { "systems": [ - { - "id": "dnd4e", - "type": "system", - "compatibility": { - "verified": "0.4.21" - } - }, { "id": "dnd5e", "type": "system", "compatibility": { "verified": "3.0.4" } - }, - { - "id": "pf1", - "type": "system", - "compatibility": { - "verified": "0.9.4" - } } ], "requires": [ @@ -55,10 +41,7 @@ "esmodules": [ "scripts/stealthy.js", "scripts/hooks.js", - "scripts/systems/dnd4e.js", - "scripts/systems/dnd5e.js", - "scripts/systems/pf1.js", - "scripts/systems/pf2e.js" + "scripts/systems/dnd5e.js" ], "styles": [ "styles/stealthy.css" diff --git a/scripts/engine.js b/scripts/engine.js index 5e58891..fccd0a6 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -23,76 +23,42 @@ export default class Engine { } patchFoundry() { - // Detection mode patching - if (game.settings.get(Stealthy.MODULE_ID, 'useCanDetect')) { - libWrapper.register( - Stealthy.MODULE_ID, - 'DetectionMode.prototype._canDetect', - function (wrapped, visionSource, target) { - switch (this.type) { - case DetectionMode.DETECTION_TYPES.SIGHT: - case DetectionMode.DETECTION_TYPES.SOUND: - const src = visionSource.object.document; - if (src instanceof TokenDocument) { - const tgt = target?.document; - if (tgt instanceof TokenDocument) { - const engine = stealthy.engine; - if (engine.isHidden(visionSource, tgt)) return false; - } - } - } - return wrapped(visionSource, target); - }, - libWrapper.MIXED, - { perf_mode: libWrapper.PERF_FAST } - ); - } - else { - libWrapper.register( - Stealthy.MODULE_ID, - 'DetectionModeBasicSight.prototype.testVisibility', - function (wrapped, visionSource, mode, config = {}) { - const engine = stealthy.engine; - if (engine.isHidden(visionSource, config.object)) return false; - return wrapped(visionSource, mode, config); - }, - libWrapper.MIXED, - { perf_mode: libWrapper.PERF_FAST } - ); - - libWrapper.register( - Stealthy.MODULE_ID, - 'DetectionModeInvisibility.prototype.testVisibility', - function (wrapped, visionSource, mode, config = {}) { - const engine = stealthy.engine; - if (engine.isHidden(visionSource, config.object)) return false; - return wrapped(visionSource, mode, config); - }, - libWrapper.MIXED, - { perf_mode: libWrapper.PERF_FAST } - ); - } + // Generic Detection mode patching + Stealthy.log('Engine.patchFoundry'); + libWrapper.register( + Stealthy.MODULE_ID, + 'DetectionMode.prototype._canDetect', + function (wrapped, visionSource, target) { + switch (this.type) { + case DetectionMode.DETECTION_TYPES.SIGHT: + case DetectionMode.DETECTION_TYPES.SOUND: + const srcToken = visionSource.object.document; + if (!(srcToken instanceof TokenDocument)) break; + const tgtToken = target?.document; + if (!(tgtToken instanceof TokenDocument)) break; + const engine = stealthy.engine; + if (engine.isHidden(visionSource, tgtToken)) return false; + } + return wrapped(visionSource, target); + }, + libWrapper.MIXED, + { perf_mode: libWrapper.PERF_FAST } + ); if (game.settings.get(Stealthy.MODULE_ID, 'spotSecretDoors')) { StealthyDoors.initialize(); } } - isHidden(visionSource, target) { + isHidden(visionSource, tgtToken) { const friendlyStealth = game.settings.get(Stealthy.MODULE_ID, 'friendlyStealth'); - let ignoreFriendlyStealth = friendlyStealth === 'ignore' || !game.combat && friendlyStealth === 'inCombat'; - ignoreFriendlyStealth = - ignoreFriendlyStealth && - target.document?.disposition === visionSource.object.document?.disposition; + const ignoreFriendlyStealth = friendlyStealth === 'ignore' || !game.combat && friendlyStealth === 'inCombat'; + if (ignoreFriendlyStealth && tgtToken?.disposition === visionSource.object.document?.disposition) return false; - if (!ignoreFriendlyStealth) { - const hiddenEffect = this.findHiddenEffect(target?.actor); - if (hiddenEffect) { - return !this.canSpotTarget(visionSource, hiddenEffect, target); - } - } + const hiddenEffect = this.findHiddenEffect(tgtToken?.actor); + if (!hiddenEffect) return false; - return false; + return !this.canDetectHidden(visionSource, hiddenEffect, tgtToken); } findHiddenEffect(actor) { @@ -105,7 +71,7 @@ export default class Engine { return actor?.effects.find(e => (v10 ? e.label : e.name) === this.spotLabel && !e.disabled); } - canSpotTarget(visionSource, hiddenEffect, target) { + canDetectHidden(visionSource, hiddenEffect, target) { // Implement your system's method for testing spot data vs hidden data // This should would in the absence of a spot effect on the viewer, using // a passive or default value as necessary diff --git a/scripts/hooks.js b/scripts/hooks.js index 28387d1..9dd7144 100644 --- a/scripts/hooks.js +++ b/scripts/hooks.js @@ -81,18 +81,6 @@ Hooks.once('setup', () => { } }); - game.settings.register(Stealthy.MODULE_ID, 'useCanDetect', { - name: 'EXPERIMENTAL - use _canDetect instead of TestVisibility', - hint: 'This only works on some installations', - scope: 'world', - config: true, - type: Boolean, - default: false, - onChange: value => { - debouncedReload(); - }, - }); - game.settings.register(Stealthy.MODULE_ID, 'logLevel', { name: game.i18n.localize("stealthy.logLevel.name"), scope: 'client', diff --git a/scripts/systems/dnd4e.js b/scripts/systems/dnd4e.js index 45a6332..a95cd15 100644 --- a/scripts/systems/dnd4e.js +++ b/scripts/systems/dnd4e.js @@ -20,16 +20,16 @@ class Engine4e extends Engine { }); } - canSpotTarget(visionSource, hiddenEffect, target) { + canDetectHidden(visionSource, hiddenEffect, tgtToken) { // Never gets called, neither do the patches for the v10 vision modes // dead in the water const source = visionSource.object?.actor; - const stealth = hiddenEffect.flags.stealthy?.hidden ?? (10 + target.actor.system.skills.stl.total); + const stealth = hiddenEffect.flags.stealthy?.hidden ?? (10 + tgtToken.actor.system.skills.stl.total); const spotEffect = this.findSpotEffect(source); const perception = spotEffect?.flags.stealthy?.spot ?? (10 + source.system.skills.prc.total); if (perception <= stealth) { - Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${target.name}'s ${stealth}`); + Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${tgtToken.name}'s ${stealth}`); return false; } return true; diff --git a/scripts/systems/dnd5e.js b/scripts/systems/dnd5e.js index 9189aab..c18be9d 100644 --- a/scripts/systems/dnd5e.js +++ b/scripts/systems/dnd5e.js @@ -64,7 +64,7 @@ class Engine5e extends Engine { type: Boolean, default: false, }); - + game.settings.register(Stealthy.MODULE_ID, 'friendlyUmbralSight', { name: game.i18n.localize("stealthy.dnd5e.friendlyUmbralSight.name"), scope: 'world', @@ -100,9 +100,41 @@ class Engine5e extends Engine { }); } + patchFoundry() { + if (!game.modules.get("vision-5e")?.active) { + super.patchFoundry(); + return; + } + + Stealthy.log('Dnd5e.patchFoundry'); + // Detection mode patching + libWrapper.register( + Stealthy.MODULE_ID, + 'CONFIG.Canvas.detectionModes.basicSight._canDetect', + function (wrapped, visionSource, target) { + switch (this.type) { + case DetectionMode.DETECTION_TYPES.SIGHT: + const srcToken = visionSource.object.document; + if (!(srcToken instanceof TokenDocument)) break; + const tgtToken = target?.document; + if (!(tgtToken instanceof TokenDocument)) break; + const engine = stealthy.engine; + if (engine.isHidden(visionSource, tgtToken)) return false; + } + return wrapped(visionSource, target); + }, + libWrapper.MIXED, + { perf_mode: libWrapper.PERF_FAST } + ); + + if (game.settings.get(Stealthy.MODULE_ID, 'spotSecretDoors')) { + StealthyDoors.initialize(); + } + } + static LIGHT_LABELS = ['dark', 'dim', 'bright']; - canSpotTarget(visionSource, hiddenEffect, targetToken) { + canDetectHidden(visionSource, hiddenEffect, tgtToken) { const srcToken = visionSource.object.document; const source = srcToken?.actor; const stealth = hiddenEffect.flags.stealthy?.hidden ?? target.actor.system.skills.ste.passive; @@ -115,14 +147,14 @@ class Engine5e extends Engine { let perception; if (game.settings.get(Stealthy.MODULE_ID, 'tokenLighting')) { - perception = this.adjustForLightingConditions(spotPair, visionSource, source, targetToken.actor); + perception = this.adjustForLightingConditions(spotPair, visionSource, source, tgtToken.actor); } else { - perception = this.adjustForDefaultConditions(spotPair, visionSource, source, targetToken.actor); + perception = this.adjustForDefaultConditions(spotPair, visionSource, source, tgtToken.actor); } if (perception <= stealth) { - Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${targetToken.name}'s ${stealth}`); + Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${tgtToken.name}'s ${stealth}`); return false; } diff --git a/scripts/systems/pf1.js b/scripts/systems/pf1.js index 9d586aa..9804338 100644 --- a/scripts/systems/pf1.js +++ b/scripts/systems/pf1.js @@ -40,16 +40,16 @@ export class EnginePF1 extends Engine { return actor?.items.find(i => i.name === 'Spot' && i.system.active); } - canSpotTarget(visionSource, hiddenEffect, target) { + canDetectHidden(visionSource, hiddenEffect, tgtToken) { const source = visionSource.object?.actor; - const stealth = hiddenEffect.flags.stealthy?.hidden ?? (10 + target.actor.system.skills.ste.mod); + const stealth = hiddenEffect.flags.stealthy?.hidden ?? (10 + tgtToken.actor.system.skills.ste.mod); const spotEffect = this.findSpotEffect(source); const spotTake10 = game.settings.get(Stealthy.MODULE_ID, 'spotTake10'); const perception = spotEffect?.flags.stealthy?.spot ?? (spotTake10 ? 10 + source.system.skills.per.mod : undefined); if (perception === undefined || perception <= stealth) { - Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${target.name}'s ${stealth}`); + Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${tgtToken.name}'s ${stealth}`); return false; } return true; diff --git a/scripts/systems/pf2e.js b/scripts/systems/pf2e.js index e4666c6..04b562b 100644 --- a/scripts/systems/pf2e.js +++ b/scripts/systems/pf2e.js @@ -44,14 +44,14 @@ export class EnginePF2e extends Engine { return actor?.items.find(i => i.name === 'Seeking'); } - canSpotTarget(visionSource, hiddenEffect, target) { - const stealth = hiddenEffect?.flags?.stealthy?.hidden ?? (10 + target.actor.system.skills.ste.value); + canDetectHidden(visionSource, hiddenEffect, tgtToken) { + const stealth = hiddenEffect?.flags?.stealthy?.hidden ?? (10 + tgtToken.actor.system.skills.ste.value); const source = visionSource.object?.actor; let seeking = this.findSpotEffect(source); const perception = seeking?.flags?.stealthy?.spot ?? 10 + source.system.attributes.perception?.value; if (perception < stealth) { - Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${target.name}'s ${stealth}`); + Stealthy.log(`${visionSource.object.name}'s ${perception} can't detect ${tgtToken.name}'s ${stealth}`); return false; } return true;