diff --git a/README.md b/README.md index b609fb5..5c53307 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ The following vision/detection modes are included. - _Note: Restricted by any sight-blocking wall, because there's no way to know what material the wall is made of or how thick it is._ - **Devil's Sight** - _Note: You need the [Vision Limitation]() module for magical darkness._ +- **Divine Sense** - **Echolocation** - Detects the same things Blindsight does but doesn't work while deafened. - **Ghostly Gaze** @@ -56,6 +57,7 @@ A token gains ... - _Detect Thoughts_ if the actor has an active effect with the name `Detect Thoughts`. - _Devil's Sight_ if the actor has an active effect with the name `Devil's Sight`. - _Note: Remove the effect that gives 120 feet Darkvision if it exists: Devil's Sight doesn't increase the range of Darkvision._ +- _Divine Sense_ if the actor has an active effect with the name `Divine Sense`. - _Ghostly Gaze_ if the actor has an active effect with the name `Ghostly Gaze`. - _See Invisibility_ if the actor has an active effect with the name `See Invisibility`. diff --git a/lang/en.json b/lang/en.json index 2fbfd63..101f54b 100644 --- a/lang/en.json +++ b/lang/en.json @@ -5,6 +5,7 @@ "VISION5E.DetectPoisonAndDisease": "Detect Poison and Disease", "VISION5E.DetectThoughts": "Detect Thoughts", "VISION5E.DevilsSight": "Devil's Sight", + "VISION5E.DivineSense": "Divine Sense", "VISION5E.Echolocation": "Echolocation", "VISION5E.GhostlyGaze": "Ghostly Gaze", "VISION5E.Hearing": "Hearing", diff --git a/module.json b/module.json index 87218c6..e3b31f5 100644 --- a/module.json +++ b/module.json @@ -8,7 +8,7 @@ "email": "dev7355608@gmail.com" } ], - "version": "1.5.1", + "version": "1.6.0", "compatibility": { "minimum": "11.299", "verified": "11.304" @@ -47,8 +47,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/v1.5.1/module.zip", - "changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v1.5.1", + "download": "https://github.com/dev7355608/vision-5e/releases/download/v1.6.0/module.zip", + "changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v1.6.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/automation.mjs b/scripts/automation.mjs index 4d6503f..58c7c90 100644 --- a/scripts/automation.mjs +++ b/scripts/automation.mjs @@ -220,6 +220,16 @@ Hooks.once("i18nInit", () => { }); } + for (const name of [ + "Divine Sense", + game.i18n.localize("VISION5E.DivineSense") + ]) { + effectMapping.set(name, { + id: "divineSense", + range: 60 + }); + } + for (const name of [ "Echolocation", game.i18n.localize("VISION5E.Echolocation") diff --git a/scripts/config.mjs b/scripts/config.mjs index 225c7f8..39106e8 100644 --- a/scripts/config.mjs +++ b/scripts/config.mjs @@ -5,6 +5,7 @@ import { DetectionModeDetectMagic } from "./detection-modes/detect-magic.mjs"; import { DetectionModeDetectPoisonAndDisease } from "./detection-modes/detect-poison-and-disease.mjs"; 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 { DetectionModeEcholocation } from "./detection-modes/echolocation.mjs"; import { DetectionModeGhostlyGaze } from "./detection-modes/ghostly-gaze.mjs"; import { DetectionModeHearing } from "./detection-modes/hearing.mjs"; @@ -18,6 +19,7 @@ import { VisionModeDetectMagic } from "./vision-modes/detect-magic.mjs"; import { VisionModeDetectPoisonAndDisease } from "./vision-modes/detect-poison-and-disease.mjs"; import { VisionModeDetectThoughts } from "./vision-modes/detect-thoughts.mjs"; import { VisionModeDevilsSight } from "./vision-modes/devils-sight.mjs"; +import { VisionModeDivineSense } from "./vision-modes/divine-sense.mjs"; import { VisionModeEcholocation } from "./vision-modes/echolocation.mjs"; import { VisionModeGhostlyGaze } from "./vision-modes/ghostly-gaze.mjs"; import { VisionModeTremorsense } from "./vision-modes/tremorsense.mjs"; @@ -83,6 +85,7 @@ Hooks.once("init", () => { registerDetectionMode(new DetectionModeDetectPoisonAndDisease()); registerDetectionMode(new DetectionModeDetectThoughts()); registerDetectionMode(new DetectionModeDevilsSight()); + registerDetectionMode(new DetectionModeDivineSense()); registerDetectionMode(new DetectionModeEcholocation()); registerDetectionMode(new DetectionModeGhostlyGaze()); registerDetectionMode(new DetectionModeHearing()); @@ -99,6 +102,7 @@ Hooks.once("init", () => { registerVisionMode(new VisionModeDetectPoisonAndDisease()); registerVisionMode(new VisionModeDetectThoughts()); registerVisionMode(new VisionModeDevilsSight()); + registerVisionMode(new VisionModeDivineSense()); registerVisionMode(new VisionModeEcholocation()); registerVisionMode(new VisionModeGhostlyGaze()); registerVisionMode(new VisionModeTremorsense()); diff --git a/scripts/detection-modes/divine-sense.mjs b/scripts/detection-modes/divine-sense.mjs new file mode 100644 index 0000000..6645d1c --- /dev/null +++ b/scripts/detection-modes/divine-sense.mjs @@ -0,0 +1,57 @@ +/** + * The detection mode for Divine Sense. + */ +export class DetectionModeDivineSense extends DetectionMode { + sourceType = "sight"; + wallDirectionMode = PointSourcePolygon.WALL_DIRECTION_MODES.NORMAL; + useThreshold = false; + imprecise = true; + important = true; + priority = -3000; + + constructor() { + super({ + id: "divineSense", + label: "VISION5E.DivineSense", + type: DetectionMode.DETECTION_TYPES.OTHER, + walls: true, + angle: false + }); + } + + /** @override */ + static getDetectionFilter() { + return this._detectionFilter ??= GlowOverlayFilter.create({ + glowColor: [1, 1, 0, 1] + }); + } + + /** @override */ + _canDetect(visionSource, target) { + if (!(target instanceof Token)) return false; + if (target.document.hasStatusEffect(CONFIG.specialStatusEffects.BURROW)) return true; + const actor = target.actor; + if (!(actor && actor.type === "npc")) return false; + const type = actor.system.details.type.value; + return type === "celestial" + || type === "fiend" + || type === "undead"; + } + + /** @override */ + _testLOS(visionSource, mode, target, test) { + if (!this._testAngle(visionSource, mode, target, test)) return false; + if (!this.walls) return true; + return !CONFIG.Canvas.polygonBackends.sight.testCollision( + { x: visionSource.x, y: visionSource.y }, + test.point, + { + type: "sight", + mode: "any", + source: visionSource, + wallDirectionMode: this.wallDirectionMode, + useThreshold: this.useThreshold + } + ); + } +} diff --git a/scripts/hud.mjs b/scripts/hud.mjs index 6bcbca6..99b3273 100644 --- a/scripts/hud.mjs +++ b/scripts/hud.mjs @@ -16,6 +16,7 @@ Hooks.on("renderTokenHUD", (hud, html) => { "detectPoisonAndDisease", "detectThoughts", "devilsSight", + "divineSense", "echolocation", "ghostlyGaze", "tremorsense", diff --git a/scripts/vision-modes/divine-sense.mjs b/scripts/vision-modes/divine-sense.mjs new file mode 100644 index 0000000..046ce46 --- /dev/null +++ b/scripts/vision-modes/divine-sense.mjs @@ -0,0 +1,35 @@ +import { DetectColorationVisionShader } from "./shaders/detect.mjs"; +import { VoidBackgroundVisionShader, VoidIlluminationVisionShader, VoidSamplerShader } from "./shaders/void.mjs"; + +/** + * The vision mode for Divine Sense. + */ +export class VisionModeDivineSense extends VisionMode { + detectionMode = "divineSense" + + constructor() { + super({ + id: "divineSense", + label: "VISION5E.DivineSense", + canvas: { + shader: VoidSamplerShader, + uniforms: { contrast: 0, saturation: 0, exposure: 0 } + }, + lighting: { + background: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, + illumination: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED }, + coloration: { visibility: VisionMode.LIGHTING_VISIBILITY.DISABLED } + }, + vision: { + darkness: { adaptive: false }, + defaults: { attenuation: 1, contrast: 0, saturation: 0, brightness: 0 }, + background: { shader: VoidBackgroundVisionShader }, + illumination: { shader: VoidIlluminationVisionShader }, + coloration: { + shader: DetectColorationVisionShader, + uniforms: { colorDetection: [1, 1, 0] } + } + } + }, { animated: true }); + } +}