diff --git a/ChangeLog.md b/ChangeLog.md index dbbf6ff..7d14371 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,6 @@ +# v4.9.0 +* Add a setting to specify additional words to recognize as 'hidden'. Useful in mixed language sessions. + # v4.8.0 * dnd5e: compatible with 3.x and 4.x diff --git a/README.md b/README.md index e1e6ec4..03b0de1 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,11 @@ A module that adds perception vs stealth testing to Foundry's visibility tests. It filters out any objects with the Hidden condition if the viewing Perception value fails to beat the object's Stealth value. [Stealthy Wiki](https://github.com/Eligarf/stealthy/wiki) ---- + # Features ## Stealth and Perception rolls are banked + The last stealth and perception rolls for each token or actor is recorded (banked) and used to control token visibility on the canvas. The roll results are displayed in the token HUD for GMs to see as token buttons with an input box on the bottom: perception is on the left, stealth is on the right. Changing the values in these input boxes will alter the stored results for any future visibility tests while that roll remains active. Perception banking has an overall token control *Bank perception rolls* which the GM uses to control when perception check banking is enabled. Toggling it off will also clear out all banked perception rolls for the current scene. @@ -23,16 +24,18 @@ Perception banking has an overall token control *Bank perception rolls* which th ![override](https://user-images.githubusercontent.com/16523503/213258088-73098735-321f-4542-9c8a-433be26cd014.gif) ![control](https://github.com/Eligarf/avoid-notice/assets/16523503/38d512f0-27dc-4eda-9e59-4a14078ba3f4) -## Rolls banked in token or actor +## Rolls banked in token or actor A game setting individually controls whether stealth or perception roll results are banked in the actor or token. ### Token + * Default for perception * Banked rolls are deleted by deleting the value in the token button * No icons are added to the token for a cleaner look ### Actor + * Default for stealth * Banked rolls are actually stored in an effect or item on the actor. * Banked rolls are deleted by deleting the effect they are banked in. @@ -43,15 +46,18 @@ A game setting individually controls whether stealth or perception roll results ![perception](https://user-images.githubusercontent.com/16523503/213257350-e382f584-1c5c-41a8-bf00-60705ec89bd0.gif) ## GM Configures which detection modes can be countered by stealth + A GM can select which detection modes should be able to be affected by stealth checks. If the *Vision5e* module is active, the configuration table will include all of the additional detection modes that *Vision5e* provides. ![menu-image](https://github.com/Eligarf/stealthy/assets/16523503/01030283-781c-4cbf-8eaf-07f306a10c2e) ## Friendly tokens can still be viewed + The GM has options for allowing stealthy tokens to be seen by other tokens of the same disposition. ## Automatic Hidden Door detection -Doors can have a detection range that will hide the door control until the viewing token is within the given range. Doors can also have an optional stealth value; tokens with a sufficiently high perception effect will be able to see a hidden door if it beats that door's stealth. + +Doors can have a detection range that will hide the door control until the viewing token is within the given range. Doors can also have an optional stealth value; tokens with a sufficiently high perception effect will be able to see a hidden door if it beats that door's stealth. **THIS DOES NOT APPLY TO FOUNDRY'S SECRET DOORS!!!** I tried and failed to to get the secret doors to play nice - it turns out to be way easier to conditionally hide a regular door from players than to conditionally show a secret door to them. @@ -59,26 +65,34 @@ Doors can have a detection range that will hide the door control until the viewi ![hidden-door](https://user-images.githubusercontent.com/16523503/217671740-41aa7832-d495-49da-a149-948ebb6ccb2a.PNG) # End Turn keybinding + It doesn't really belong in this module but I want to be able to press the *End* key to end my turn, and so I added an editable keybinding that will allow owners of the current combatant to do so. # Systems + Stealthy supports the following systems (specific notes about a given system are in the [Wiki](https://github.com/Eligarf/stealthy/wiki)): -- [dnd5e](https://github.com/Eligarf/stealthy/wiki/D&D-5e) -- dnd4e -- [pf1](https://github.com/Eligarf/stealthy/wiki/Pathfinder-1e) -I've abandoned trying to get this to work on PF2e. Instead, I use *PF2e Perception* and built a new module to help with the stealth-as-initiative vs perception checks: [PF2e Avoid Notice](https://foundryvtt.com/packages/pf2e-avoid-notice) +* [dnd5e](https://github.com/Eligarf/stealthy/wiki/D&D-5e) +* dnd4e +* [pf1](https://github.com/Eligarf/stealthy/wiki/Pathfinder-1e) + +I've abandoned trying to get this to work on PF2e. Instead, I use *PF2e Perception* and built a new module to help with the stealth-as-initiative vs perception checks: [PF2e Avoid Notice](https://foundryvtt.com/packages/pf2e-avoid-notice) # Limitations ## Handling Hidden removal -Stealthy will not automatically remove a banked stealth roll - the dnd5e [Skulker](https://www.dndbeyond.com/feats/skulker) feat demonstrates why removing Hidden gets complicated without heavier automation support provided by modules like the excellent [Midi-QOL](https://foundryvtt.com/packages/midi-qol) which handles this for my games. I suggest [Visual Active Effects](https://foundryvtt.com/packages/visual-active-effects) as an easier way to manually remove it, especially for low automation level games. + +Stealthy will not automatically remove a banked stealth roll - the dnd5e [Skulker](https://www.dndbeyond.com/feats/skulker) feat demonstrates why removing Hidden gets complicated without heavier automation support provided by modules like the excellent [Midi-QOL](https://foundryvtt.com/packages/midi-qol) which handles this for my games. I suggest [Visual Active Effects](https://foundryvtt.com/packages/visual-active-effects) as an easier way to manually remove it, especially for low automation level games. # Required modules + * [lib-wrapper](https://foundryvtt.com/packages/lib-wrapper) * [socketlib](https://github.com/manuelVo/foundryvtt-socketlib) + ## Optional modules -Stealthy will adapt to the presence of any of the following modules should they be active + +Stealthy will adapt to the presence of any of the following modules should they be active: + * [Active Token Effects](https://foundryvtt.com/packages/ATL) * [Condition Lab & Triggler](https://foundryvtt.com/packages/condition-lab-triggler) * [DFreds Convenient Effects](https://foundryvtt.com/packages/dfreds-convenient-effects) diff --git a/languages/en.json b/languages/en.json index 648ee5f..b168674 100644 --- a/languages/en.json +++ b/languages/en.json @@ -9,7 +9,9 @@ "hint": "This string will be sent through i18n.localize(). Changing these may affect overall automation of Stealthy's effects." }, "icon": "Hidden icon", - "iconhint": "Default icon for Hidden effect" + "iconhint": "Default icon for Hidden effect", + "aliases": "Other names to recognize as hidden", + "aliasesHint": "Separate names with semicolons. This is useful in mixed-language situations" }, "spot": { "name": "Spot", diff --git a/languages/fr.json b/languages/fr.json index 2638436..e46a1f2 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -9,7 +9,9 @@ "hint": "Cette chaîne sera envoyée via i18n.localize(). Changing these may affect overall automation of Stealthy's effects." }, "icon": "Icône cachée", - "iconhint": "Icône par défaut pour l'effet masqué" + "iconhint": "Icône par défaut pour l'effet masqué", + "aliases": "Other names to recognize as hidden", + "aliasesHint": "Separate names with semicolons. This is useful in mixed-language situations" }, "spot": { "name": "Détection", diff --git a/languages/pt-BR.json b/languages/pt-BR.json index 1d48605..75de1b6 100644 --- a/languages/pt-BR.json +++ b/languages/pt-BR.json @@ -9,7 +9,9 @@ "hint": "Esta string será enviada através de i18n.localize(). Mudar isso pode afetar a automação geral dos efeitos do Stealthy." }, "icon": "Ícone Escondido", - "iconhint": "Ícone padrão para o efeito Escondido" + "iconhint": "Ícone padrão para o efeito Escondido", + "aliases": "Other names to recognize as hidden", + "aliasesHint": "Separate names with semicolons. This is useful in mixed-language situations" }, "spot": { "name": "Percebendo", diff --git a/languages/ru.json b/languages/ru.json index 1189564..0f09d54 100644 --- a/languages/ru.json +++ b/languages/ru.json @@ -9,7 +9,9 @@ "hint": "Эта строка будет обработана через i18n.localize. Изменение может повлиять на общую автоматизацию эффектов модуля." }, "icon": "Иконка Статуса Стелс", - "iconhint": "Значек по умолчанию для статуса Стелс" + "iconhint": "Значек по умолчанию для статуса Стелс", + "aliases": "Other names to recognize as hidden", + "aliasesHint": "Separate names with semicolons. This is useful in mixed-language situations" }, "spot": { "name": "Поиск", diff --git a/scripts/engine.js b/scripts/engine.js index 6bdfff0..770d9e7 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -91,6 +91,7 @@ export default class Engine { game.settings.register(Stealthy.MODULE_ID, 'exposure', settings.exposure); game.settings.register(Stealthy.MODULE_ID, 'gIDimThreshold', settings.gIDimThreshold); game.settings.register(Stealthy.MODULE_ID, 'spotSecretDoors', settings.spotSecretDoors); + game.settings.register(Stealthy.MODULE_ID, 'hiddenAliases', settings.hiddenAliases); game.settings.register(Stealthy.MODULE_ID, 'stealthToActor', settings.stealthToActor); game.settings.register(Stealthy.MODULE_ID, 'perceptionToActor', settings.perceptionToActor); @@ -119,8 +120,13 @@ export default class Engine { setup() { this.hiddenName = game.i18n.localize(game.settings.get(Stealthy.MODULE_ID, 'hiddenLabel')); + this.hiddenAliases = [this.hiddenName]; this.spotName = game.i18n.localize(game.settings.get(Stealthy.MODULE_ID, 'spotLabel')); + const aliases = game.settings.get(Stealthy.MODULE_ID, 'hiddenAliases'); + if (aliases.length > 0) + this.hiddenAliases.push(...aliases.split(";")); Stealthy.log(`hiddenName='${this.hiddenName}', spotName='${this.spotName}'`); + Stealthy.log(`hiddenAliases = `, this.hiddenAliases); if (game.settings.get(Stealthy.MODULE_ID, 'spotSecretDoors')) { Doors.setup(); } @@ -289,6 +295,15 @@ export default class Engine { type: Boolean, default: false, }, + hiddenAliases: { + name: "stealthy.hidden.aliases", + hint: "stealthy.hidden.aliasesHint", + scope: 'world', + requiresReload: true, + config: true, + type: String, + default: '' + }, hiddenSource: { name: "stealthy.hidden.source", hint: "stealthy.source.hint", @@ -554,7 +569,7 @@ export default class Engine { findPerceptionEffect(actor) { const beforeV11 = Math.floor(game.version) < 11; - return actor?.effects.find((e) => !e.disabled && this.spotName === (beforeV11 ? e.label : e.name)); + return actor?.effects.find((e) => !e.disabled && this.hiddenAliases.includes(beforeV11 ? e.label : e.name)); } makeStealthEffectMaker(name) {