Skip to content

Commit

Permalink
module dependant visibility hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
Eligarf committed Apr 3, 2024
1 parent 34ba49e commit fb919eb
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 109 deletions.
7 changes: 7 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
4 changes: 3 additions & 1 deletion languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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"
Expand Down
4 changes: 3 additions & 1 deletion languages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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"
Expand Down
4 changes: 3 additions & 1 deletion languages/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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"
Expand Down
19 changes: 1 addition & 18 deletions module.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -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"
Expand Down
90 changes: 28 additions & 62 deletions scripts/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down
12 changes: 0 additions & 12 deletions scripts/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
6 changes: 3 additions & 3 deletions scripts/systems/dnd4e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
42 changes: 37 additions & 5 deletions scripts/systems/dnd5e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
6 changes: 3 additions & 3 deletions scripts/systems/pf1.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions scripts/systems/pf2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit fb919eb

Please sign in to comment.