From db53cdb0b85d5977e6f12cf9313268e869b6fe3a Mon Sep 17 00:00:00 2001 From: dev7355608 Date: Tue, 24 Sep 2024 02:06:06 +0200 Subject: [PATCH] Update to PHB'24 rules --- README.md | 64 ++--- module.json | 6 +- scripts/actor.mjs | 331 +++++++++++++----------- scripts/detection-modes/tremorsense.mjs | 1 + scripts/detection-modes/witch-sight.mjs | 2 +- scripts/main.mjs | 39 ++- scripts/vision-modes/devils-sight.mjs | 9 +- 7 files changed, 259 insertions(+), 193 deletions(-) diff --git a/README.md b/README.md index d16f632..31be421 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,17 @@ The automation detects relevant feats and effects that affect the creatures sens ### Detection Modes -- **Blindsense** _(Class Feature: Rogue, SRD 5.1 / PHB14 94)_ +- **Blindsense _(Legacy)_** _(Class Feature: Rogue, SRD 5.1 / PHB'14 94)_ - Detects PC/NPC actors that are not objects (e.g. [Item Piles](https://foundryvtt.com/packages/item-piles)). - Cannot detect actors that are _burrowing_, _defeated (dead)_, _ethereal_ (from the the material plane), or _petrified_. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _deafened_, _defeated_ (_dead_), _petrified_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically for PC actors with the `Blindsense`[\*](#translations) feat. -- **Blindsight** _(Sense, SRD 5.1 / PHB14 183)_ +- **Blindsight** _(Sense, SRD 5.1 / PHB'14 183 / PHB'24 361)_ - Detects tokens, notes, and door controls. - - Cannot detect actors that are _burrowing_ or _ethereal_ (from the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise). - - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. Also disabled if _deafened_ and the actor is a NPC and has the `Echolocation`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 318)_ or `Blind Senses`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 175)_ feat. + - Cannot detect actors that are _burrowing_ or _ethereal_ (from the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise). + - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. Also disabled if _deafened_ and the actor is a NPC and has the `Echolocation`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 318)_ or `Blind Senses`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 175)_ feat. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically from the actor's _Blindsight_. - **Blood Sense** _(Homebrew)_ @@ -33,63 +33,63 @@ The automation detects relevant feats and effects that affect the creatures sens - Disabled while _defeated_ (_dead_), _petrified_, or _unconscious_. - Is not restricted by walls or blocked by darkness sources. - Configured automatically for NPC actors that have the `Blood Sense`[\*](#translations) feat. -- **Darkvision** _(Sense, SRD 5.1 / PHB14 183)_ +- **Darkvision** _(Sense, SRD 5.1 / PHB'14 183 / PHB'24 260)_ - Detects tokens, notes, and door controls. - - Cannot detect actors that are _burrowing_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise), or _invisible_. Also cannot detect PC actors with the `Umbral Sight`[\*](#translations) _(Subclass Feature: Gloom Stalker Ranger, XGE 41)_ feat. + - Cannot detect actors that are _burrowing_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise), or _invisible_. Also cannot detect PC actors with the `Umbral Sight`[\*](#translations) _(Subclass Feature: Gloom Stalker Ranger, XGE 41 / PHB'24 126)_ feat. - Disabled while _blinded_, _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - - Is restricted by sight-blocking walls and is blocked by darkness sources unless the actor in a NPC with the `Devil's Sight`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 70)_ feat. + - Is restricted by sight-blocking walls and is blocked by darkness sources unless the actor in a NPC with the `Devil's Sight`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 70)_ feat. - Configured automatically from the actor's _Darkvision_. -- **Detect Evil and Good** _(Spell, SRD 5.1 / PHB14 231)_ +- **Detect Evil and Good** _(Spell, SRD 5.1 / PHB'14 231 / PHB'24 261)_ - Detects aberrations, celestials, elementals, feys, fiends, undeads, and PC actors with the `Hollow One`[\*](#translations) _(Character Creation Option, EGW 182)_, `Supernatural Gift: Hollow One`, or `Supernatural Gifts: Hollow One` feat. - - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB14 259)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB14 263)_ effect. + - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 259 / PHB'24 298)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 263 / PHB'24 302)_ effect. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically for actors that have the `Detect Evil and Good`[\*](#translations) effect. -- **Detect Magic** _(Spell, SRD 5.1 / PHB14 231)_ +- **Detect Magic** _(Spell, SRD 5.1 / PHB'14 231 / PHB'24 262)_ - Detects actors that carry a magic item or are affected by a spell effect. - - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB14 259)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB14 263)_ effect. + - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 259 / PHB'24 298)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 263 / PHB'24 302)_ effect. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - - Configured automatically for actors with the `Detect Magic`[\*](#translations) effect, PC actors with the `Magic Awareness`[\*](#translations) _(Subclass Feature: Wild Magic Barbarian, TCE 25)_ feat, and NPC actors with the `Sense Magic`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 40)_ feat. -- **Detect Poison and Disease** _(Spell, SRD 5.1 / PHB14 231)_ + - Configured automatically for actors with the `Detect Magic`[\*](#translations) effect, PC actors with the `Magic Awareness`[\*](#translations) _(Subclass Feature: Wild Magic Barbarian, TCE 25)_ feat, and NPC actors with the `Sense Magic`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 40)_ feat. +- **Detect Poison and Disease** _(Spell, SRD 5.1 / PHB'14 231 / PHB'24 262)_ - Detects all actors that have a poisonous natural weapon attack or have the _poisoned_ or _diseased_ status effect. - - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB14 259)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB14 263)_ effect. + - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 259 / PHB'24 298)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 263 / PHB'24 302)_ effect. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically for actors that have the `Detect Poison and Disease`[\*](#translations) effect. - _Note: Some poisonous creatures might not be detected, because the Versatile Damage of the natural weapon attack is missing the `[poison]` flavor, which you'll need to fix yourself._ -- **Detect Thoughts** _(Spell, SRD 5.1 / PHB14 231)_ - - Detects all PC/NPC actors that have an Intelligence of 4 or higher and speak at least one language and are not an object (e.g. [Item Piles](https://foundryvtt.com/packages/item-piles)). - - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB14 259)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB14 263)_ effect. +- **Detect Thoughts** _(Spell, SRD 5.1 / PHB'14 231 / PHB'24 262)_ + - Detects all PC/NPC actors that speak at least one language or are telepathic (or, if legacy rules, have an Intelligence of 4 or higher and speak at least one language) and are not an object (e.g. [Item Piles](https://foundryvtt.com/packages/item-piles)). + - Cannot detect actors that are _burrowing_ or _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise). Also cannot detect actors with the `Mind Blank`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 259 / PHB'24 298)_ or `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 263 / PHB'24 302)_ effect. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically for actors that have the `Detect Thoughts`[\*](#translations) effect. -- **Devil's Sight** _(Eldritch Invocation, SRD 5.1 / PHB14 110)_ +- **Devil's Sight** _(Eldritch Invocation, SRD 5.1 / PHB'14 110 / PHB'24 155)_ - Can detect tokens, notes, and door controls. - - Cannot detect actors that are _burrowing_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise), or _invisible_. + - Cannot detect actors that are _burrowing_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise), or _invisible_. - Disabled while _blinded_, _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically for PC actors that have the `Devil's Sight`[\*](#translations), `Invocation: Devil's Sight`, `Invocations: Devil's Sight`, `Eldritch Invocation: Devil's Sight`, `Eldritch Invocations: Devil's Sight`, or `Eldritch Adept: Devil's Sight` feat. - _Note: Remove the active effect that gives 120 feet Darkvision from the Invocation: Devil's Sight feat if it exists: Devil's Sight doesn't increase the range of Darkvision._ -- **Divine Sense** _(Class Feature: Paladin, SRD 5.1 / PHB14 82)_ +- **Divine Sense** _(Class Feature: Paladin, SRD 5.1 / PHB'14 82 / PHB'24 110)_ - Detects celestials, fiends, undead, and PC actors with the `Hollow One`[\*](#translations) _(Character Creation Option, EGW 182)_, `Supernatural Gift: Hollow One`, or `Supernatural Gifts: Hollow One` feat. - - Cannot detect actors that are _burrowing_, _ethereal_ (from the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise), or _petrified_. + - Cannot detect actors that are _burrowing_, _ethereal_ (from the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise), or _petrified_. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically for PC actors with the `Divine Sense`[\*](#translations) effect. -- **Ethereal Sight** _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ +- **Ethereal Sight** _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ - Allows detection _ethereal_ actors. - Cannot detect anything on its own and requires another sense to see the target without the _ethereal_ status. - Is not restricted by walls or blocked by darkness sources. - Configured automatically for NPC actors that have the `Ethereal Sight`[\*](#translations) feat and PC actors with the `The Third Eye: Ethereal Sight`[\*](#translations) feat. - **Eyes of the Grave** _(Class Feature: Cleric, XGE 19)_ - Detects undead, and PC actors with the `Hollow One`[\*](#translations) _(Character Creation Option, EGW 182)_, `Supernatural Gift: Hollow One`, or `Supernatural Gifts: Hollow One` feat. - - Cannot detect actors that are _burrowing_, _ethereal_ (from the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise), or _petrified_. Also cannot detect actors with the `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB14 263)_ effect. + - Cannot detect actors that are _burrowing_, _ethereal_ (from the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise), or _petrified_. Also cannot detect actors with the `Nondetection`[\*](#translations) _(Spell, SRD 5.1 / PHB'14 263)_ effect. - Does not reveal the identity of detected tokens. - Disabled while _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. @@ -100,39 +100,39 @@ The automation detects relevant feats and effects that affect the creatures sens - Disabled while _deafened_, _defeated_ (_dead_), _petrified_, or _unconscious_. - Is restricted by sound-blocking walls with reversed direction. - By default all tokens have hearing range of 15 + 2.5 * (*Passive Perception* - 10) feet (`15 + 2.5 * (@skills.prc.passive - 10)`). The default hearing range can be configured in the module settings. -- **Life Sense** _(Monster Feature, e.g. MPMM14 250 / MTF 246)_ +- **Life Sense** _(Monster Feature, e.g. MPMM'14 250 / MTF 246)_ - Detects PC/NPC actors that are not undead, constructs, or objects (e.g. [Item Piles](https://foundryvtt.com/packages/item-piles)). - - Cannot detect actors that are _defeated (dead)_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise), or _petrified_. + - Cannot detect actors that are _defeated (dead)_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise), or _petrified_. - Does not reveal the identity of detected tokens. - Disabled while _defeated_ (_dead_), _petrified_, or _unconscious_. - Is not restricted by walls or blocked by darkness sources. - Configured automatically for NPC actors that have the `Life Sense`[\*](#translations) feat. - **Light Perception** - Detects tokens, notes, and door controls that are illuminated a light source or are _burning_. - - Cannot detect actors that are _burrowing_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 147)_ NPC feat says otherwise), or _invisible_. + - Cannot detect actors that are _burrowing_, _ethereal_ (from the the material plane unless the `Etherealness`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 147)_ NPC feat says otherwise), or _invisible_. - Disabled while _blinded_, _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and is blocked by darkness sources. - Infinite range by default. -- **See Invisibility** _(Spell, SRD 5.1 / PHB14 274)_ +- **See Invisibility** _(Spell, SRD 5.1 / PHB'14 274 / PHB'24 314)_ - Allows detection of _invisible_ and _ethereal_ actors. - Cannot detect anything on its own and requires another sense to see the target without the _ethereal_ and _invisible_ status. - Is not restricted by walls or blocked by darkness sources. - - Configured automatically for actors with the `See Invisibility`[\*](#translations) effect and PC actors with the `The Third Eye: See Invisibility`[\*](#translations) _(Class Feature: Wizard, PHB14 116)_ feat. -- **Tremorsense** _(Sense, SRD 5.1 / MM14 9)_ + - Configured automatically for actors with the `See Invisibility`[\*](#translations) effect and PC actors with the `The Third Eye: See Invisibility`[\*](#translations) _(Class Feature: Wizard, PHB'14 116)_ feat. +- **Tremorsense** _(Sense, SRD 5.1 / MM'14 9 / PHB'24 377)_ - Detects PC/NPC actors that are not objects (e.g. [Item Piles](https://foundryvtt.com/packages/item-piles)). - Cannot detect actors that are _burrowing_, _defeated (dead)_, _ethereal_ (from the the material plane), _flying_, _hovering_, or _petrified_. - Does not reveal the identity of detected tokens. - Disabled while _defeated_ (_dead_), _flying_, _hovering_, _petrified_, or _unconscious_. - Is not restricted by walls or blocked by darkness sources. - Configured automatically from the actor's _Tremorsense_. -- **Truesight** _(Sense, SRD 5.1 / PHB14 183)_ +- **Truesight** _(Sense, SRD 5.1 / PHB'14 183 / PHB'24 377)_ - Detects tokens, notes, and door controls. - Cannot detect actors that are _burrowing_. - Disabled while _blinded_, _burrowing_, _defeated_ (_dead_), _petrified_, _sleeping_, or _unconscious_. - Is restricted by sight-blocking walls and isn't blocked by darkness sources. - Configured automatically from the actor's _Truesight_. -- **Witch Sight** _(Eldritch Invocation, SRD 5.1 / PHB14 111)_ - - Allows detection of PC/NPC actors that have has the `Shapechanger`[\*](#translations) creature subtype and NPC actors with the `Shapechanger`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM14 220)_ feat. +- **Witch Sight _(Legacy)_** _(Eldritch Invocation, SRD 5.1 / PHB'14 111)_ + - Allows detection of PC/NPC actors that have has the `Shapechanger`[\*](#translations) creature subtype and NPC actors with the `Shapechanger`[\*](#translations) _(Monster Feature, e.g. SRD 5.1 / MM'14 220)_ feat. - Cannot detect anything on its own and requires another sense to see the target. - Is not restricted by walls or blocked by darkness sources. - Configured automatically for PC actor that have the `Witch Sight`[\*](#translations), `Invocation: Witch Sight`, `Invocations: Witch Sight`, `Eldritch Invocation: Witch Sight`, `Eldritch Invocations: Witch Sight`, or `Eldritch Adept: Witch Sight` feat. diff --git a/module.json b/module.json index 613fc12..127f82a 100644 --- a/module.json +++ b/module.json @@ -8,7 +8,7 @@ "email": "dev7355608@gmail.com" } ], - "version": "2.4.2", + "version": "2.5.0", "compatibility": { "minimum": "12", "verified": "12" @@ -60,8 +60,8 @@ }, "url": "https://github.com/dev7355608/vision-5e", "manifest": "https://github.com/dev7355608/vision-5e/releases/latest/download/module.json", - "download": "https://github.com/dev7355608/vision-5e/releases/download/v2.4.2/module.zip", - "changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v2.4.2", + "download": "https://github.com/dev7355608/vision-5e/releases/download/v2.5.0/module.zip", + "changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v2.5.0", "bugs": "https://github.com/dev7355608/vision-5e/issues", "readme": "https://raw.githubusercontent.com/dev7355608/vision-5e/main/README.md", "license": "https://raw.githubusercontent.com/dev7355608/vision-5e/main/LICENSE" diff --git a/scripts/actor.mjs b/scripts/actor.mjs index f682ea2..e237f10 100644 --- a/scripts/actor.mjs +++ b/scripts/actor.mjs @@ -60,7 +60,8 @@ export default (Actor) => class extends Actor { this.statuses.add(CONFIG.specialStatusEffects.OBJECT); } - if (/(?<=^|[\s,;])(?:Shapechanger|Gestaltwandler|Métamorphe|Cambiaformas|Metamorfo)(?=$|[\s,;])/i.test(this.system.details?.type?.subtype ?? "")) { + if (CONFIG.specialStatusEffects.SHAPECHANGER + && /(?<=^|[\s,;])(?:Shapechanger|Gestaltwandler|Métamorphe|Cambiaformas|Metamorfo)(?=$|[\s,;])/i.test(this.system.details?.type?.subtype ?? "")) { this.statuses.add(CONFIG.specialStatusEffects.SHAPECHANGER); } @@ -235,35 +236,8 @@ function isPoisonous(actor) { return actor.items.some(isPoisonousNaturalWeapon); } -Hooks.once("init", () => { - if (!foundry.utils.isNewerVersion("4.0.0", game.system.version)) { - return; - } - - isPoisonous = function (actor) { - if (actor.statuses.has(CONFIG.specialStatusEffects.OBJECT) - || actor.statuses.has(CONFIG.specialStatusEffects.PETRIFIED)) { - return false; - } - - for (const item of actor.items) { - if (item.type === "weapon" && item.system.type.value === "natural" - && (item.system.damage.parts.some(part => part[1] === "poison") - || [ - item.system.critical.damage, - item.system.damage.versatile, - item.system.formula - ].some(formula => /\[poison\]/i.test(formula)))) { - return true; - } - } - - return false; - } -}); - /** - * A thinking creature is a creature that has an Intelligence of 4 or higher and speaks at least one language. + * A thinking creature is a creature that speaks at least one language or is telepathic. * @param {Actor} actor * @returns {boolean} */ @@ -274,7 +248,6 @@ function isThinking(actor) { } return (actor.type === "character" || actor.type === "npc") - && actor.system.abilities.int.value > 3 && (actor.system.traits.languages.value.size > 0 || !!actor.system.traits.languages.custom); } @@ -287,18 +260,12 @@ function isThinking(actor) { /** @type {Registry} */ const FEAT_REGISTRY = { character: { - blindsense(item) { - upgradeDetectionMode(this, "blindsense", 10, "ft"); - }, hollowOne(item) { this.statuses.add(CONFIG.specialStatusEffects.REVENANCE); }, invocationDevilsSight(item) { upgradeDetectionMode(this, "devilsSight", 120, "ft"); }, - invocationWitchSight(item) { - upgradeDetectionMode(this, "witchSight", 30, "ft"); - }, umbralSight(item) { this.statuses.add(CONFIG.specialStatusEffects.UMBRAL_SIGHT); }, @@ -332,9 +299,6 @@ const FEAT_REGISTRY = { senseMagic(item) { upgradeDetectionMode(this, "detectMagic", findRange(item.system.description.value, this.system.attributes.senses.units)); }, - shapechanger(item) { - this.statuses.add(CONFIG.specialStatusEffects.SHAPECHANGER); - }, } }; @@ -375,12 +339,6 @@ const EFFECT_REGISTRY = { seeInvisibility(effect) { upgradeDetectionMode(this, "seeInvisibility", null); }, - theThirdEyeEtherealSight(effect) { - upgradeDetectionMode(this, "etherealSight", 60, "ft"); - }, - theThirdEyeSeeInvisibility(effect) { - upgradeDetectionMode(this, "seeInvisibility", 10, "ft"); - }, }, npc: { detectEvilAndGood(effect) { @@ -407,15 +365,12 @@ const EFFECT_REGISTRY = { } }; -/** @type {Record} */ -const DATABASE = Object.values({ +/** @type {Record<"en" | "de" | "fr" | "es" | "pt-BR", Record>} */ +const DATABASE = { en: { blindSenses: [ "Blind Senses", ], - blindsense: [ - "Blindsense", - ], bloodSense: [ "Blood Sense", ], @@ -463,11 +418,6 @@ const DATABASE = Object.values({ "Eldritch Adept: Ghostly Gaze", "Ghostly Gaze", ], - invocationWitchSight: [ - [["Eldritch ", ""], "Invocation", ["s", ""], ": Witch Sight"], - "Eldritch Adept: Witch Sight", - "Witch Sight", - ], lifeSense: [ "Life Sense", ], @@ -486,15 +436,6 @@ const DATABASE = Object.values({ senseMagic: [ "Sense Magic", ], - shapechanger: [ - "Shapechanger", - ], - theThirdEyeEtherealSight: [ - "The Third Eye: Ethereal Sight", - ], - theThirdEyeSeeInvisibility: [ - "The Third Eye: See Invisibility", - ], umbralSight: [ "Umbral Sight", ], @@ -503,9 +444,6 @@ const DATABASE = Object.values({ blindSenses: [ "Blinde Sinne", ], - blindsense: [ - "Blindgespür", - ], bloodSense: [ "Blutgespür", ], @@ -553,11 +491,6 @@ const DATABASE = Object.values({ "Schauerlicher Adept: Geisterhafter Blick", "Geisterhafter Blick", ], - invocationWitchSight: [ - [["Schauerliche ", ""], "Anrufung", ["en", ""], ": Hexensicht"], - "Schauerlicher Adept: Hexensicht", - "Hexensicht", - ], lifeSense: [ "Lebensgespür", ], @@ -576,15 +509,6 @@ const DATABASE = Object.values({ senseMagic: [ "Magie spüren", ], - shapechanger: [ - "Gestaltwandler", - ], - theThirdEyeEtherealSight: [ - "Das dritte Auge: Ätherische Sicht", - ], - theThirdEyeSeeInvisibility: [ - "Das dritte Auge: Unsichtbares sehen", - ], umbralSight: [ "Düstersicht", ], @@ -593,9 +517,6 @@ const DATABASE = Object.values({ blindSenses: [ "Sens aveugles", ], - blindsense: [ - "Perception aveugle", - ], bloodSense: [ "Perception du sang", ], @@ -646,12 +567,6 @@ const DATABASE = Object.values({ ["Adepte occulte: Regard fantomatique"], ["Regard fantomatique"], ], - invocationWitchSight: [ - [["Invocation", "Manifestation"], [" occulte", ""], [": ", " : "], ["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], - [["Invocations", "Manifestations"], [" occultes", ""], [": ", " : "], ["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], - ["Adepte occulte", [": ", " : "], ["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], - [["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], - ], lifeSense: [ "Perception de la vie", ], @@ -670,15 +585,6 @@ const DATABASE = Object.values({ senseMagic: [ [["Détection", "Perception"], " de la magie"], ], - shapechanger: [ - "Métamorphe", - ], - theThirdEyeEtherealSight: [ - ["Troisième œil", [": ", " : "], "Vision éthérée"], - ], - theThirdEyeSeeInvisibility: [ - ["Troisième œil", [": ", " : "], "Voir l", ["'", "’"], "invisible"], - ], umbralSight: [ "Vision des ombres", ], @@ -687,9 +593,6 @@ const DATABASE = Object.values({ blindSenses: [ "Sentidos de Ciego", ], - blindsense: [ - "Sentir sin Ver", - ], bloodSense: [ "Percepción de Sangre", ], @@ -739,12 +642,6 @@ const DATABASE = Object.values({ "Adepto Sobrenatural: Mirada Fantasmal", "Mirada Fantasmal", ], - invocationWitchSight: [ - ["Invocación", [" Sobrenatural", ""], ": Visión Bruja"], - ["Invocaciones", [" Sobrenaturales", ""], ": Visión Bruja"], - "Adepto Sobrenatural: Visión Bruja", - "Visión Bruja", - ], lifeSense: [ "Percepción de la Vida", ], @@ -763,15 +660,6 @@ const DATABASE = Object.values({ senseMagic: [ "Sentir Magia", ], - shapechanger: [ - "Cambiaformas", - ], - theThirdEyeEtherealSight: [ - "El Tercer Ojo: Visión Etérea", - ], - theThirdEyeSeeInvisibility: [ - "El Tercer Ojo: Ver Invisibilidad", - ], umbralSight: [ "Visión en la Umbra", ], @@ -780,9 +668,6 @@ const DATABASE = Object.values({ blindSenses: [ "Sentido Cego", ], - blindsense: [ - "Sentido Cego", - ], bloodSense: [ "Percepção do Sangue", ], @@ -834,12 +719,6 @@ const DATABASE = Object.values({ "Adepto Místico: Olhar Fantasmagórico", "Olhar Fantasmagórico", ], - invocationWitchSight: [ - ["Invocação", [" Mística", ""], ": Visão da Bruxa"], - ["Invocações", [" Místicas", ""], ": Visão da Bruxa"], - "Adepto Místico: Visão da Bruxa", - "Visão da Bruxa", - ], lifeSense: [ "Percepção da Vida", ], @@ -858,37 +737,30 @@ const DATABASE = Object.values({ senseMagic: [ "Sentir Magia", ], - shapechanger: [ - "Metamorfo", - ], - theThirdEyeEtherealSight: [ - "O Terceiro Olho: Visão Etérea", - ], - theThirdEyeSeeInvisibility: [ - "O Terceiro Olho: Ver Invisibilidade", - ], umbralSight: [ "Visão Umbral", ], }, -}).reduce((object, current) => { - for (const key in current) { - object[key] = (object[key] ?? []).concat(current[key]); - } - - return object; -}); +}; /** * @param {Registry<*>} registry * @returns {{ character: Matcher, npc: Matcher }} */ function createMatchers(registry) { + const database = Object.values(DATABASE).reduce((object, current) => { + for (const key in current) { + object[key] = (object[key] ?? []).concat(current[key]); + } + + return object; + }); + return Object.fromEntries( Object.entries(registry).map(([type, methods]) => [ type, new Matcher(Object.keys(methods).reduce((object, name) => { - object[name] = DATABASE[name]; + object[name] = database[name]; return object; }, {})) @@ -897,7 +769,176 @@ function createMatchers(registry) { } /** @type {{ character: Matcher, npc: Matcher }} */ -const FEAT_MATCHERS = createMatchers(FEAT_REGISTRY); +let FEAT_MATCHERS = createMatchers(FEAT_REGISTRY); /** @type {{ character: Matcher, npc: Matcher }} */ -const EFFECT_MATCHERS = createMatchers(EFFECT_REGISTRY); +let EFFECT_MATCHERS = createMatchers(EFFECT_REGISTRY); + +Hooks.once("init", () => { + if (foundry.utils.isNewerVersion("4.0.0", game.system.version)) { + isPoisonous = function (actor) { + if (actor.statuses.has(CONFIG.specialStatusEffects.OBJECT) + || actor.statuses.has(CONFIG.specialStatusEffects.PETRIFIED)) { + return false; + } + + for (const item of actor.items) { + if (item.type === "weapon" && item.system.type.value === "natural" + && (item.system.damage.parts.some(part => part[1] === "poison") + || [ + item.system.critical.damage, + item.system.damage.versatile, + item.system.formula + ].some(formula => /\[poison\]/i.test(formula)))) { + return true; + } + } + + return false; + }; + + return; + } + + if (game.settings.get("dnd5e", "rulesVersion") !== "legacy") { + return; + } + + // Detect Thoughts in PHB'14 requires an Intelligence of 4 or higher. + isThinking = function (actor) { + if (actor.statuses.has(CONFIG.specialStatusEffects.OBJECT) + || actor.statuses.has(CONFIG.specialStatusEffects.PETRIFIED)) { + return false; + } + + return (actor.type === "character" || actor.type === "npc") + && actor.system.abilities.int.value > 3 + && (actor.system.traits.languages.value.size > 0 + || !!actor.system.traits.languages.custom); + }; + + // Invocation: Witch Sight and The Third Eye have changed in PHB'24. + Object.assign(FEAT_REGISTRY.character, { + blindsense(item) { + upgradeDetectionMode(this, "blindsense", 10, "ft"); + }, + invocationWitchSight(item) { + upgradeDetectionMode(this, "witchSight", 30, "ft"); + }, + }); + Object.assign(FEAT_REGISTRY.npc, { + shapechanger(item) { + this.statuses.add(CONFIG.specialStatusEffects.SHAPECHANGER); + }, + }); + Object.assign(EFFECT_REGISTRY.character, { + theThirdEyeEtherealSight(effect) { + upgradeDetectionMode(this, "etherealSight", 60, "ft"); + }, + theThirdEyeSeeInvisibility(effect) { + upgradeDetectionMode(this, "seeInvisibility", 10, "ft"); + }, + }); + + Object.assign(DATABASE.en, { + blindsense: [ + "Blindsense", + ], + invocationWitchSight: [ + [["Eldritch ", ""], "Invocation", ["s", ""], ": Witch Sight"], + "Eldritch Adept: Witch Sight", + "Witch Sight", + ], + shapechanger: [ + "Shapechanger", + ], + theThirdEyeEtherealSight: [ + "The Third Eye: Ethereal Sight", + ], + theThirdEyeSeeInvisibility: [ + "The Third Eye: See Invisibility", + ], + }); + Object.assign(DATABASE.de, { + blindsense: [ + "Blindgespür", + ], + invocationWitchSight: [ + [["Schauerliche ", ""], "Anrufung", ["en", ""], ": Hexensicht"], + "Schauerlicher Adept: Hexensicht", + "Hexensicht", + ], + shapechanger: [ + "Gestaltwandler", + ], + theThirdEyeEtherealSight: [ + "Das dritte Auge: Ätherische Sicht", + ], + theThirdEyeSeeInvisibility: [ + "Das dritte Auge: Unsichtbares sehen", + ], + }); + Object.assign(DATABASE.fr, { + blindsense: [ + "Perception aveugle", + ], + invocationWitchSight: [ + [["Invocation", "Manifestation"], [" occulte", ""], [": ", " : "], ["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], + [["Invocations", "Manifestations"], [" occultes", ""], [": ", " : "], ["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], + ["Adepte occulte", [": ", " : "], ["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], + [["Vision", "Vue"], " ", ["de sorcier", "sorcière"]], + ], + shapechanger: [ + "Métamorphe", + ], + theThirdEyeEtherealSight: [ + ["Troisième œil", [": ", " : "], "Vision éthérée"], + ], + theThirdEyeSeeInvisibility: [ + ["Troisième œil", [": ", " : "], "Voir l", ["'", "’"], "invisible"], + ], + }); + Object.assign(DATABASE.es, { + blindsense: [ + "Sentir sin Ver", + ], + invocationWitchSight: [ + ["Invocación", [" Sobrenatural", ""], ": Visión Bruja"], + ["Invocaciones", [" Sobrenaturales", ""], ": Visión Bruja"], + "Adepto Sobrenatural: Visión Bruja", + "Visión Bruja", + ], + shapechanger: [ + "Cambiaformas", + ], + theThirdEyeEtherealSight: [ + "El Tercer Ojo: Visión Etérea", + ], + theThirdEyeSeeInvisibility: [ + "El Tercer Ojo: Ver Invisibilidad", + ], + }); + Object.assign(DATABASE["pt-BR"], { + blindsense: [ + "Sentido Cego", + ], + invocationWitchSight: [ + ["Invocação", [" Mística", ""], ": Visão da Bruxa"], + ["Invocações", [" Místicas", ""], ": Visão da Bruxa"], + "Adepto Místico: Visão da Bruxa", + "Visão da Bruxa", + ], + shapechanger: [ + "Metamorfo", + ], + theThirdEyeEtherealSight: [ + "O Terceiro Olho: Visão Etérea", + ], + theThirdEyeSeeInvisibility: [ + "O Terceiro Olho: Ver Invisibilidade", + ], + }); + + FEAT_MATCHERS = createMatchers(FEAT_REGISTRY); + EFFECT_MATCHERS = createMatchers(EFFECT_REGISTRY); +}); diff --git a/scripts/detection-modes/tremorsense.mjs b/scripts/detection-modes/tremorsense.mjs index 8b06df7..32a23f2 100644 --- a/scripts/detection-modes/tremorsense.mjs +++ b/scripts/detection-modes/tremorsense.mjs @@ -26,6 +26,7 @@ export default class DetectionModeTremorsense extends DetectionMode { _canDetect(visionSource, target) { const source = visionSource.object; + // TODO: incorporeal creatures should not be detected if (!(target instanceof Token) || target.document.hasStatusEffect(CONFIG.specialStatusEffects.DEFEATED) || target.document.hasStatusEffect(CONFIG.specialStatusEffects.ETHEREAL) diff --git a/scripts/detection-modes/witch-sight.mjs b/scripts/detection-modes/witch-sight.mjs index 1e637c7..9757f2c 100644 --- a/scripts/detection-modes/witch-sight.mjs +++ b/scripts/detection-modes/witch-sight.mjs @@ -4,7 +4,7 @@ import { DETECTION_LEVELS } from "../const.mjs"; /** * The detection mode for Witch Sight. */ -export default class DetectionModeDetectWitchSight extends DetectionMode { +export default class DetectionModeWitchSight extends DetectionMode { constructor() { super({ diff --git a/scripts/main.mjs b/scripts/main.mjs index 05f3809..18fbd90 100644 --- a/scripts/main.mjs +++ b/scripts/main.mjs @@ -23,7 +23,7 @@ import DetectionModeLightPerception from "./detection-modes/light-perception.mjs import DetectionModeSeeInvisibility from "./detection-modes/see-invisibility.mjs"; import DetectionModeTremorsense from "./detection-modes/tremorsense.mjs"; import DetectionModeTruesight from "./detection-modes/truesight.mjs"; -import DetectionModeDetectWitchSight from "./detection-modes/witch-sight.mjs"; +import DetectionModeWitchSight from "./detection-modes/witch-sight.mjs"; import VisionModeBlindsight from "./vision-modes/blindsight.mjs"; import VisionModeDarkvision from "./vision-modes/darkvision.mjs"; import VisionModeDevilsSight from "./vision-modes/devils-sight.mjs"; @@ -76,12 +76,16 @@ Hooks.once("init", () => { CONFIG.specialStatusEffects.POISONED = "poisoned"; CONFIG.specialStatusEffects.POISONOUS = "poisonous"; CONFIG.specialStatusEffects.REVENANCE = "revenance"; - CONFIG.specialStatusEffects.SHAPECHANGER = "shapechanger"; CONFIG.specialStatusEffects.SLEEPING = "sleeping"; CONFIG.specialStatusEffects.THINKING = "thinking"; CONFIG.specialStatusEffects.UMBRAL_SIGHT = "umbralSight"; CONFIG.specialStatusEffects.UNCONSCIOUS = "unconscious"; + // Shapechanger detection is not needed in PHB'24 at the momement, because Witch Sight has been changed + if (game.settings.get("dnd5e", "rulesVersion") === "legacy") { + CONFIG.specialStatusEffects.SHAPECHANGER = "shapechanger"; + } + // Create aliases for core special status effects Object.defineProperties(CONFIG.specialStatusEffects, { BLIND: { get() { return this.BLINDED }, set(id) { this.BLINDED = id; }, configurable: true, enumerable: false }, @@ -92,7 +96,6 @@ Hooks.once("init", () => { // Register detection modes for (const detectionModeClass of [ - DetectionModeBlindsense, DetectionModeBlindsight, DetectionModeBloodSense, DetectionModeDarkvision, @@ -110,13 +113,24 @@ Hooks.once("init", () => { DetectionModeSeeInvisibility, DetectionModeTremorsense, DetectionModeTruesight, - DetectionModeDetectWitchSight, ]) { const mode = new detectionModeClass(); CONFIG.Canvas.detectionModes[mode.id] = mode; } + // Register legacy detection modes + if (game.settings.get("dnd5e", "rulesVersion") === "legacy") { + for (const detectionModeClass of [ + DetectionModeBlindsense, + DetectionModeWitchSight, + ]) { + const mode = new detectionModeClass(); + + CONFIG.Canvas.detectionModes[mode.id] = mode; + } + } + // Remove core detection modes that do not exist in D&D 5e delete CONFIG.Canvas.detectionModes.senseAll; delete CONFIG.Canvas.detectionModes.senseInvisibility; @@ -145,6 +159,23 @@ Hooks.once("init", () => { // we currently cannot prevent FOV from exploring the fog delete CONFIG.Canvas.visionModes.tremorsense; + // Legacy Devil's Sight + if (game.settings.get("dnd5e", "rulesVersion") === "legacy") { + CONFIG.Canvas.visionModes.devilsSight.updateSource({ + canvas: { + shader: ColorAdjustmentsSamplerShader, + uniforms: { contrast: -0.15, saturation: 0, exposure: 0 } + }, + lighting: { + background: { visibility: VisionMode.LIGHTING_VISIBILITY.REQUIRED } + }, + vision: { + darkness: { adaptive: false }, + defaults: { contrast: -0.15, saturation: 0, brightness: 0.5 }, + } + }); + } + // Patch visiblity testing if (game.modules.get("lib-wrapper")?.active) { libWrapper.register( diff --git a/scripts/vision-modes/devils-sight.mjs b/scripts/vision-modes/devils-sight.mjs index 9fb8c7a..08bc4ba 100644 --- a/scripts/vision-modes/devils-sight.mjs +++ b/scripts/vision-modes/devils-sight.mjs @@ -7,16 +7,9 @@ export default class VisionModeDevilsSight extends VisionMode { super({ id: "devilsSight", label: "VISION5E.DevilsSight", - canvas: { - shader: ColorAdjustmentsSamplerShader, - uniforms: { contrast: -0.15, saturation: 0, exposure: 0 } - }, - lighting: { - background: { visibility: VisionMode.LIGHTING_VISIBILITY.REQUIRED } - }, vision: { darkness: { adaptive: false }, - defaults: { contrast: -0.15, saturation: 0, brightness: 0.5 }, + defaults: { contrast: 0, saturation: 0, brightness: 1 }, } }); }