diff --git a/README.md b/README.md index af13768..d51b03f 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,13 @@ The automation detects relevant feats and effects that affect the creatures sens - 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 / MM 147)_ NPC feat says otherwise), or _petrified_. Also cannot detect actors with the `Nondetection`[\*](#translations) _(Spell, SRD / PHB 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. + - Configured automatically for PC actors with the `Eyes of the Grave`[\*](#translations) effect. - **Hearing** - Detects PC/NPC actors that are not objects (e.g. [Item Piles](https://foundryvtt.com/packages/item-piles)). - Cannot detect actors that are _defeated (dead)_, _ethereal_ (from the the material plane), _inaudible_, or _petrified_. @@ -161,6 +168,7 @@ While a player does have any owned nonhidden tokens with vision that are _defeat | Devil's Sight | `ATL.detectionModes.devilsSight.range` | | Divine Sense | `ATL.detectionModes.divineSense.range` | | Ethereal Sight | `ATL.detectionModes.etherealSight.range` | +| Eyes of the Grave | `ATL.detectionModes.eyesOfTheGrave.range` | | Hearing | `ATL.detectionModes.hearing.range` | | Life Sense | `ATL.detectionModes.lifeSense.range` | | Light Perception | `ATL.detectionModes.lightPerception.range` | @@ -190,6 +198,7 @@ _Note: Attribute keys starting with `ATL.` require the [Active Token Effects](ht | `Eldritch Adept` | _Feat_ | `Schauerlicher Adept` | `Adepte occulte` | `Adepto sobrenatural` | `Adepto Místico` | | `Eldritch Invocation` | _Warlock feat_ | `Schauerliche Anrufung` | `Invocation occulte` / `Manifestation occulte` | `Invocación sobrenatural` | `Invocação Mística` | | `Ethereal Sight` | _Monster feat_ | `Ätherische Sicht` | `Vision éthérée` / `Vue éthérée` | `Visión etérea` | `Visão Etérea` | +| `Eyes of the Grave` | _Cleric (Grave Domain) feat_ | `Grabesblick` | `Œil de la tombe` | `Ojos de la Tumba` | `Olhos da Sepultura` | | `Ghostly Gaze` | _Warlock invocation_ | `Geisterhafter Blick` | `Regard fantomatique` | `Mirada fantasmal` | `Olhar Fantasmagórico` | | `Hollow One` | _Supernatural Gift_ | `Leerwandler` | `Celui-qui-est-creux` | `Aquel que está vacío` | `Oco` | | `Life Sense` | _Monster feat_ | `Lebensgespür` | `Perception de la vie` | `Percepción de la Vida` | `Percepção da Vida` | diff --git a/lang/de.json b/lang/de.json index ac57637..ecb20ec 100644 --- a/lang/de.json +++ b/lang/de.json @@ -8,6 +8,7 @@ "VISION5E.DevilsSight": "Teufelssicht", "VISION5E.DivineSense": "Göttliches Gespür", "VISION5E.EtherealSight": "Ätherische Sicht", + "VISION5E.EyesOfTheGrave": "Grabesblick", "VISION5E.Hearing": "Hören", "VISION5E.Inaudible": "Lautlos", "VISION5E.LifeSense": "Lebensgespür", diff --git a/lang/en.json b/lang/en.json index 15b703a..ea6e290 100644 --- a/lang/en.json +++ b/lang/en.json @@ -8,6 +8,7 @@ "VISION5E.DevilsSight": "Devil's Sight", "VISION5E.DivineSense": "Divine Sense", "VISION5E.EtherealSight": "Ethereal Sight", + "VISION5E.EyesOfTheGrave": "Eyes of the Grave", "VISION5E.Hearing": "Hearing", "VISION5E.Inaudible": "Inaudible", "VISION5E.LifeSense": "Life Sense", diff --git a/lang/es.json b/lang/es.json index 17a3a7a..6bb0a70 100644 --- a/lang/es.json +++ b/lang/es.json @@ -8,6 +8,7 @@ "VISION5E.DevilsSight": "Vista del Diablo", "VISION5E.DivineSense": "Sentidos Divinos", "VISION5E.EtherealSight": "Visión Etérea", + "VISION5E.EyesOfTheGrave": "Ojos de la Tumba", "VISION5E.Hearing": "Audición", "VISION5E.Inaudible": "Inaudible", "VISION5E.LifeSense": "Percepción de la Vida", diff --git a/lang/fr.json b/lang/fr.json index d88bee2..7d65fb6 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -8,6 +8,7 @@ "VISION5E.DevilsSight": "Vision du diable", "VISION5E.DivineSense": "Perception divine", "VISION5E.EtherealSight": "Vision éthérée", + "VISION5E.EyesOfTheGrave": "Œil de la tombe", "VISION5E.Hearing": "Audition", "VISION5E.Inaudible": "Inaudible", "VISION5E.LifeSense": "Perception de la vie", diff --git a/lang/pt-BR.json b/lang/pt-BR.json index 815b1d2..d017ab7 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -8,6 +8,7 @@ "VISION5E.DevilsSight": "Visão Diabólica", "VISION5E.DivineSense": "Sentido Divino", "VISION5E.EtherealSight": "Visão Etérea", + "VISION5E.EyesOfTheGrave": "Olhos da Sepultura", "VISION5E.Hearing": "Audição", "VISION5E.Inaudible": "Inaudível", "VISION5E.LifeSense": "Percepção da Vida", diff --git a/module.json b/module.json index 7930e62..769fe41 100644 --- a/module.json +++ b/module.json @@ -8,7 +8,7 @@ "email": "dev7355608@gmail.com" } ], - "version": "2.3.4", + "version": "2.4.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.3.4/module.zip", - "changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v2.3.4", + "download": "https://github.com/dev7355608/vision-5e/releases/download/v2.4.0/module.zip", + "changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v2.4.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 092d229..5ba20bf 100644 --- a/scripts/actor.mjs +++ b/scripts/actor.mjs @@ -314,6 +314,9 @@ const EFFECT_REGISTRY = { divineSense(effect) { upgradeDetectionMode(this, "divineSense", 60, "ft"); }, + eyesOfTheGrave(item) { + upgradeDetectionMode(this, "eyesOfTheGrave", 60, "ft"); + }, invocationGhostlyGaze(effect) { upgradeDetectionMode(this, "basicSight", 30, "ft"); this.statuses.add(CONFIG.specialStatusEffects.GHOSTLY_GAZE); @@ -401,6 +404,9 @@ const DATABASE = Object.values({ etherealness: [ "Etherealness", ], + eyesOfTheGrave: [ + "Eyes of the Grave", + ], hollowOne: [ [["Supernatural Gift"], ["s", ""], ": Hollow One"], "Hollow One", @@ -488,6 +494,9 @@ const DATABASE = Object.values({ etherealness: [ "Körperlosigkeit", ], + eyesOfTheGrave: [ + "Grabesblick", + ], hollowOne: [ [["Übernatürliche Gabe"], ["n", ""], ": ", ["Leerwandler", "Hollow One"]], "Leerwandler", @@ -576,6 +585,9 @@ const DATABASE = Object.values({ etherealness: [ "Forme éthérée", ], + eyesOfTheGrave: [ + "Œil de la tombe", + ], hollowOne: [ [["Don surnaturel", "Dons surnaturels"], [": ", " : "], ["Celui-qui-est-creux", "Hollow One"]], "Celui-qui-est-creux", @@ -666,6 +678,9 @@ const DATABASE = Object.values({ etherealness: [ "Excursión Etérea", ], + eyesOfTheGrave: [ + "Ojos de la Tumba", + ], hollowOne: [ [["Don Supernatural", "Dones Supernaturales"], ": ", ["Aquel que está vacío", "Hollow One"]], "Aquel que está vacío", @@ -757,6 +772,9 @@ const DATABASE = Object.values({ etherealness: [ "Forma Etérea", ], + eyesOfTheGrave: [ + "Olhos da Sepultura", + ], hollowOne: [ [["Dom Sobrenatural", "Dons Sobrenaturais"], ": ", ["Oco", "Hollow One"]], "Oco", diff --git a/scripts/detection-modes/eyes-of-the-grave.mjs b/scripts/detection-modes/eyes-of-the-grave.mjs new file mode 100644 index 0000000..8beeb13 --- /dev/null +++ b/scripts/detection-modes/eyes-of-the-grave.mjs @@ -0,0 +1,70 @@ +import DetectionMode from "./base.mjs"; + +/** + * The detection mode for Eyes of the Grave. + */ +export default class DetectionModeEyesOfTheGrave extends DetectionMode { + imprecise = true; + important = true; + + constructor() { + super({ + id: "eyesOfTheGrave", + label: "VISION5E.EyesOfTheGrave", + type: DetectionMode.DETECTION_TYPES.OTHER, + angle: false, + important: true, + imprecise: true, + }); + } + + /** @override */ + static getDetectionFilter() { + return this._detectionFilter ??= CONFIG.Canvas.detectionModes.detectEvilAndGood.constructor.getDetectionFilter(); + } + + /** @override */ + _canDetect(visionSource, target) { + const source = visionSource.object; + + if (!(target instanceof Token) + || !target.actor?.system.details?.type + || target.document.hasStatusEffect(CONFIG.specialStatusEffects.BURROWING) + || target.document.hasStatusEffect(CONFIG.specialStatusEffects.ETHEREAL) + && !source.document.hasStatusEffect(CONFIG.specialStatusEffects.ETHEREAL) + && !target.document.hasStatusEffect(CONFIG.specialStatusEffects.MATERIAL) + || target.document.hasStatusEffect(CONFIG.specialStatusEffects.NONDETECTION) + || target.document.hasStatusEffect(CONFIG.specialStatusEffects.OBJECT) + || target.document.hasStatusEffect(CONFIG.specialStatusEffects.PETRIFIED)) { + return false; + } + + if (source.document.hasStatusEffect(CONFIG.specialStatusEffects.BURROWING) + || source.document.hasStatusEffect(CONFIG.specialStatusEffects.DEFEATED) + || source.document.hasStatusEffect(CONFIG.specialStatusEffects.PETRIFIED) + || source.document.hasStatusEffect(CONFIG.specialStatusEffects.SLEEPING) + || source.document.hasStatusEffect(CONFIG.specialStatusEffects.UNCONSCIOUS)) { + return false; + } + + if (target.document.hasStatusEffect(CONFIG.specialStatusEffects.REVENANCE)) { + return true; + } + + return target.actor.system.details.type.value === "undead"; + } + + /** @override */ + _testLOS(visionSource, mode, target, test) { + return !CONFIG.Canvas.polygonBackends.sight.testCollision( + { x: visionSource.x, y: visionSource.y }, + test.point, + { + type: "sight", + mode: "any", + source: visionSource, + useThreshold: true + } + ); + } +} diff --git a/scripts/main.mjs b/scripts/main.mjs index a78a8f1..15738eb 100644 --- a/scripts/main.mjs +++ b/scripts/main.mjs @@ -16,6 +16,7 @@ import DetectionModeDetectThoughts from "./detection-modes/detect-thoughts.mjs"; import DetectionModeDevilsSight from "./detection-modes/devils-sight.mjs"; import DetectionModeDivineSense from "./detection-modes/divine-sense.mjs"; import DetectionModeEtherealSight from "./detection-modes/ethereral-sight.mjs"; +import DetectionModeEyesOfTheGrave from "./detection-modes/eyes-of-the-grave.mjs"; import DetectionModeHearing from "./detection-modes/hearing.mjs"; import DetectionModeLifeSense from "./detection-modes/life-sense.mjs"; import DetectionModeLightPerception from "./detection-modes/light-perception.mjs"; @@ -101,6 +102,7 @@ Hooks.once("init", () => { DetectionModeDevilsSight, DetectionModeDivineSense, DetectionModeEtherealSight, + DetectionModeEyesOfTheGrave, DetectionModeHearing, DetectionModeLifeSense, DetectionModeLightPerception,