Skip to content

Commit

Permalink
Always apply filters of imporant detection modes
Browse files Browse the repository at this point in the history
  • Loading branch information
dev7355608 committed Jun 27, 2023
1 parent 825704c commit 946c82c
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 75 deletions.
8 changes: 8 additions & 0 deletions PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ class DetectionMode extends foundry.abstract.DataModel {
*/
imprecise;

/**
* Important detection modes always add their detection filter to token if they detect it.
* A token can have multiple detection filters: they are blended in a special filter.
* @type {boolean}
* @default false
*/
important;

/**
* The ID of the light perception mode (`DetectionModeLightPerception`).
* @type {string}
Expand Down
6 changes: 3 additions & 3 deletions module.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"email": "[email protected]"
}
],
"version": "1.4.0",
"version": "1.5.0",
"compatibility": {
"minimum": "11.299",
"verified": "11.302"
Expand Down Expand Up @@ -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.4.0/module.zip",
"changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v1.4.0",
"download": "https://github.com/dev7355608/vision-5e/releases/download/v1.5.0/module.zip",
"changelog": "https://github.com/dev7355608/vision-5e/releases/tag/v1.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"
Expand Down
3 changes: 2 additions & 1 deletion scripts/detection-modes/detect-evil-and-good.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*/
export class DetectionModeDetectEvilAndGood extends DetectionMode {
imprecise = true;
priority = -2000;
important = true;
priority = -3000;

constructor() {
super({
Expand Down
3 changes: 2 additions & 1 deletion scripts/detection-modes/detect-magic.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*/
export class DetectionModeDetectMagic extends DetectionMode {
imprecise = true;
priority = -2000;
important = true;
priority = -3000;

constructor() {
super({
Expand Down
3 changes: 2 additions & 1 deletion scripts/detection-modes/detect-poison-and-disease.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*/
export class DetectionModeDetectPoisonAndDisease extends DetectionMode {
imprecise = true;
priority = -2000;
important = true;
priority = -3000;

constructor() {
super({
Expand Down
3 changes: 2 additions & 1 deletion scripts/detection-modes/detect-thoughts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*/
export class DetectionModeDetectThoughts extends DetectionMode {
imprecise = true;
priority = -2000;
important = true;
priority = -3000;

constructor() {
super({
Expand Down
2 changes: 1 addition & 1 deletion scripts/detection-modes/hearing.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
export class DetectionModeHearing extends DetectionMode {
imprecise = true;
priority = -3000;
priority = -2000;

constructor() {
super({
Expand Down
238 changes: 171 additions & 67 deletions scripts/patches.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ Object.defineProperties(DetectionMode.prototype, {
enumerable: false,
writable: true
},
important: {
value: false,
configurable: true,
enumerable: false,
writable: true
},
_testPoint: {
value: function (visionSource, mode, target, test) {
if (!this._testRange(visionSource, mode, target, test)) return false;
Expand Down Expand Up @@ -288,92 +294,190 @@ Object.defineProperties(CanvasVisibility.prototype, {
}
});

CanvasVisibility.prototype.testVisibility = function (point, options = {}) {
const object = options.object;
CanvasVisibility.prototype.testVisibility = (() => {
class MultiDetectionFilter extends PIXI.Filter {
#filters;

if (object instanceof Token) {
object.detectionFilter = undefined;
object.impreciseVisible = false;
}
constructor(filters) {
super();
this.#filters = filters;
}

// If no vision sources are present, the visibility is dependant of the type of user
if (!canvas.effects.visionSources.some(s => s.active)) return game.user.isGM;
get autoFit() {
let autoFit = true;
for (let i = 0; i < this.#filters.length; i++) {
autoFit &&= this.#filters[i].autoFit;
}
return autoFit;
}

// Prepare an array of test points depending on the requested tolerance
const config = this._createTestConfig(point, options);
set autoFit(value) { }

// First test basic detection for light sources which specifically provide vision
for (const lightSource of canvas.effects.lightSources) {
if (!lightSource.data.vision || !lightSource.active) continue;
const result = lightSource.testVisibility(config);
if (result === true) return true;
}
get padding() {
let padding = 0;
for (let i = 0; i < this.#filters.length; i++) {
padding = Math.max(padding, this.#filters[i].padding);
}
return padding;
}

// Get scene rect to test that some points are not detected into the padding
const sr = canvas.dimensions.sceneRect;
const inBuffer = !sr.contains(point.x, point.y);
// Skip sources that are not both inside the scene or both inside the buffer
const activeVisionSources = canvas.effects.visionSources.filter(s => s.active && inBuffer !== sr.contains(s.x, s.y));
const modes = CONFIG.Canvas.detectionModes;

// Second test basic detection tests for vision sources
for (const visionSource of activeVisionSources) {
const token = visionSource.object.document;
const mode = token.detectionModes.find(m => m.id === visionSource.detectionMode.id);
const dm = modes[mode?.id];
const result = dm?.testVisibility(visionSource, mode, config);
if (result === true) {
if (object instanceof Token) {
if (!(dm.imprecise && object.impreciseVisible)) {
object.detectionFilter = dm.constructor.getDetectionFilter(true);
}
object.impreciseVisible = dm.imprecise;
set padding(value) { }

get resolution() {
const renderer = canvas.app.renderer;
const renderTextureSystem = renderer.renderTexture;
if (renderTextureSystem.current) {
return renderTextureSystem.current.resolution;
}
if (!dm.imprecise) return true;
return renderer.resolution;
}
}

const basicVisible = object instanceof Token && object.impreciseVisible;

// Third test light perception for vision sources
for (const visionSource of activeVisionSources) {
const token = visionSource.object.document;
const mode = token.detectionModes.find(m => m.id === DetectionMode.LIGHT_MODE_ID);
const dm = modes[mode?.id];
const result = dm?.testVisibility(visionSource, mode, config);
if (result === true) {
if (object instanceof Token) {
if (!basicVisible) {
object.detectionFilter = dm.constructor.getDetectionFilter(visionSource.visionMode.perceivesLight);
set resolution(value) { }

get multisample() {
const renderer = canvas.app.renderer;
const renderTextureSystem = renderer.renderTexture;
if (renderTextureSystem.current) {
return renderTextureSystem.current.multisample;
}
return renderer.multisample;
}

set multisample(value) { }

apply(filterManager, input, output, clearMode, currentState) {
for (let i = 0; i < this.#filters.length; i++) {
this.#filters[i].apply(filterManager, input, output, i === 0 ? clearMode : PIXI.CLEAR_MODES.BLEND, currentState);
}
}
};

return function (point, options = {}) {
const object = options.object;

if (object instanceof Token) {
object.detectionFilter = undefined;
object.impreciseVisible = false;
}

// If no vision sources are present, the visibility is dependant of the type of user
if (!canvas.effects.visionSources.some(s => s.active)) return game.user.isGM;

// Prepare an array of test points depending on the requested tolerance
const config = this._createTestConfig(point, options);
let preciseVisible = false;

// First test basic detection for light sources which specifically provide vision
for (const lightSource of canvas.effects.lightSources) {
if (!lightSource.data.vision || !lightSource.active) continue;
const result = lightSource.testVisibility(config);
if (result) {
preciseVisible = true;
break;
}
}

// Get scene rect to test that some points are not detected into the padding
const sr = canvas.dimensions.sceneRect;
const inBuffer = !sr.contains(point.x, point.y);
// Skip sources that are not both inside the scene or both inside the buffer
const activeVisionSources = canvas.effects.visionSources.filter(s => s.active && inBuffer !== sr.contains(s.x, s.y));
const modes = CONFIG.Canvas.detectionModes;
let importantModes;

// Second test basic detection tests for vision sources
for (const visionSource of activeVisionSources) {
const token = visionSource.object.document;
const mode = token.detectionModes.find(m => m.id === visionSource.detectionMode.id);
const dm = modes[mode?.id];
if (!dm || preciseVisible && !dm.important) continue;
const result = dm.testVisibility(visionSource, mode, config);
if (result === true) {
if (object instanceof Token) {
if (dm.important) {
importantModes ??= new Set();
importantModes.add(dm);
}
if (!preciseVisible && !(dm.imprecise && object.impreciseVisible)) {
if (!dm.important) object.detectionFilter = dm.constructor.getDetectionFilter(true);
object.impreciseVisible = dm.imprecise;
}
}
if (!dm.imprecise) {
preciseVisible = true;
}
}
return true;
}
}

if (!(object instanceof Token)) return false; // Special detection modes can only detect tokens
const basicVisible = preciseVisible || object instanceof Token && object.impreciseVisible;

// Lastly test special detection modes for vision sources
for (const visionSource of activeVisionSources) {
const token = visionSource.object.document;
for (const mode of token.detectionModes) {
if (mode.id === DetectionMode.LIGHT_MODE_ID || mode.id === visionSource.detectionMode.id) continue;
const dm = modes[mode.id];
const result = dm?.testVisibility(visionSource, mode, config);
// Third test light perception for vision sources
for (const visionSource of activeVisionSources) {
const token = visionSource.object.document;
const mode = token.detectionModes.find(m => m.id === DetectionMode.LIGHT_MODE_ID);
const dm = modes[mode?.id];
if (!dm || preciseVisible && !dm.important) continue;
const result = dm.testVisibility(visionSource, mode, config);
if (result === true) {
if (!(dm.imprecise && object.impreciseVisible)) {
if (!basicVisible) {
object.detectionFilter = dm.constructor.getDetectionFilter(false);
if (object instanceof Token) {
if (dm.important) {
importantModes ??= new Set();
importantModes.add(dm);
}
if (!preciseVisible && !basicVisible && !(dm.imprecise && object.impreciseVisible)) {
if (!dm.important) object.detectionFilter = dm.constructor.getDetectionFilter(visionSource.visionMode.perceivesLight);
object.impreciseVisible = dm.imprecise;
}
}
object.impreciseVisible = dm.imprecise;
if (!dm.imprecise) return true;
if (!dm.imprecise) {
preciseVisible = true;
}
}
}
}

return false;
};
if (!(object instanceof Token)) return preciseVisible; // Special detection modes can only detect tokens

// Lastly test special detection modes for vision sources
for (const visionSource of activeVisionSources) {
const token = visionSource.object.document;
for (const mode of token.detectionModes) {
if (mode.id === DetectionMode.LIGHT_MODE_ID || mode.id === visionSource.detectionMode.id) continue;
const dm = modes[mode.id];
if (!dm || preciseVisible && !dm.important) continue;
const result = dm.testVisibility(visionSource, mode, config);
if (result === true) {
if (dm.important) {
importantModes ??= new Set();
importantModes.add(dm);
}
if (!preciseVisible && !basicVisible && !(dm.imprecise && object.impreciseVisible)) {
if (!dm.important) object.detectionFilter = dm.constructor.getDetectionFilter(false);
object.impreciseVisible = dm.imprecise;
}
if (!dm.imprecise) {
preciseVisible = true;
}
}
}
}

if (object instanceof Token) {
if (preciseVisible) {
object.impreciseVisible = false;
}

if (importantModes) {
const dmfs = object.detectionFilter ? [object.detectionFilter] : [];
for (const dm of importantModes) {
dmfs.push(dm.constructor.getDetectionFilter(false));
}
object.detectionFilter = new MultiDetectionFilter(dmfs);
}
}

return preciseVisible;
};
})();

CanvasVisibility.prototype.restrictVisibility = ((restrictVisibility) => function () {
for (const token of canvas.tokens.placeables) {
Expand Down

0 comments on commit 946c82c

Please sign in to comment.