2021-06-29 05:27:27 +02:00
|
|
|
// SPDX-FileCopyrightText: 2021 Johannes Loher
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import logger from './logger';
|
|
|
|
import { libWrapper } from './shims/libWrapperShim';
|
|
|
|
import notifications from './notifications';
|
2021-06-29 19:10:55 +02:00
|
|
|
import { DarknessDependentVisionConfig } from './darkness-dependent-vision-config';
|
2021-06-29 05:27:27 +02:00
|
|
|
|
|
|
|
const packageName = 'darkness-dependent-vision';
|
|
|
|
|
|
|
|
Hooks.once('init', async () => {
|
|
|
|
logger.info(`Initializing ${packageName}`);
|
|
|
|
|
|
|
|
try {
|
2021-06-29 19:10:55 +02:00
|
|
|
libWrapper.register(packageName, 'CONFIG.Token.objectClass.prototype.dimRadius', getDimRadius, 'OVERRIDE');
|
2021-06-29 05:27:27 +02:00
|
|
|
} catch (e) {
|
2021-06-29 19:10:55 +02:00
|
|
|
notifications.warn(
|
|
|
|
game.i18n.format('DDV.ErrorFailedToOverride', { target: 'CONFIG.Token.documentClass.prototype.dimRadius' }),
|
|
|
|
{
|
|
|
|
log: true,
|
|
|
|
},
|
|
|
|
);
|
2021-06-29 05:27:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2021-06-29 19:10:55 +02:00
|
|
|
libWrapper.register(packageName, 'CONFIG.Token.objectClass.prototype.brightRadius', getBrightRadius, 'OVERRIDE');
|
2021-06-29 05:27:27 +02:00
|
|
|
} catch (e) {
|
2021-06-29 19:10:55 +02:00
|
|
|
notifications.warn(
|
|
|
|
game.i18n.format('DDV.ErrorFailedToOverride', { target: 'CONFIG.Token.documentClass.prototype.brightRadius' }),
|
|
|
|
{
|
|
|
|
log: true,
|
|
|
|
},
|
|
|
|
);
|
2021-06-29 05:27:27 +02:00
|
|
|
}
|
|
|
|
|
2021-06-29 19:10:55 +02:00
|
|
|
libWrapper.register(packageName, 'CONFIG.Token.objectClass.prototype.updateSource', updateSource, 'WRAPPER');
|
|
|
|
|
|
|
|
libWrapper.register(packageName, 'CONFIG.Token.sheetClass.prototype._getHeaderButtons', getHeaderButtons, 'WRAPPER');
|
2021-06-29 05:27:27 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does this {@link Token} have dim vision, considering the darkness level of
|
|
|
|
* its containing {@link Scene}?
|
|
|
|
*/
|
|
|
|
function hasDimVision() {
|
2021-06-29 19:10:55 +02:00
|
|
|
const dimVisionDarknessMin = this.document.getFlag(packageName, 'dimVisionDarknessMin') ?? 0;
|
|
|
|
const dimVisionDarknessMax = this.document.getFlag(packageName, 'dimVisionDarknessMax') ?? 1;
|
|
|
|
const darkness = this.document.parent.data.darkness;
|
|
|
|
return dimVisionDarknessMin <= darkness && darkness <= dimVisionDarknessMax;
|
2021-06-29 05:27:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does this {@link Token} have bright vision, considering the darkness level of
|
|
|
|
* its containing {@link Scene}?
|
|
|
|
*/
|
|
|
|
function hasBrightVision() {
|
2021-06-29 19:10:55 +02:00
|
|
|
const brightVisionDarknessMin = this.document.getFlag(packageName, 'brightVisionDarknessMin') ?? 0;
|
|
|
|
const brightVisionDarknessMax = this.document.getFlag(packageName, 'brightVisionDarknessMax') ?? 1;
|
|
|
|
const darkness = this.document.parent.data.darkness;
|
|
|
|
return brightVisionDarknessMin <= darkness && darkness <= brightVisionDarknessMax;
|
2021-06-29 05:27:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get this {@link Token}'s dim vision distance of in grid units, considering
|
|
|
|
* the darkness level of its containing {@link Scene}.
|
|
|
|
*
|
|
|
|
* @returns {number} The the number of grid units that this {@link Token} has
|
|
|
|
* dim vision
|
|
|
|
*/
|
|
|
|
function getDimVision() {
|
|
|
|
return hasDimVision.call(this) ? this.data.dimSight : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get this {@link Token}'s bright vision distance in grid units, considering
|
|
|
|
* the darkness level of its containing {@link Scene}.
|
|
|
|
*
|
|
|
|
* @returns {number} The the number of grid units that this {@link Token} has
|
|
|
|
* bright vision
|
|
|
|
*/
|
|
|
|
function getBrightVision() {
|
|
|
|
return hasBrightVision.call(this) ? this.data.brightSight : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Translate the token's sight distance in units into a radius in pixels.
|
|
|
|
* @return {number} The sight radius in pixels
|
|
|
|
*/
|
|
|
|
function getDimRadius() {
|
|
|
|
const dimSight = getDimVision.call(this);
|
|
|
|
let r = Math.abs(this.data.dimLight) > Math.abs(dimSight) ? this.data.dimLight : dimSight;
|
|
|
|
return this.getLightRadius(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Translate the token's bright light distance in units into a radius in pixels.
|
|
|
|
* @return {number} The bright radius in pixels
|
|
|
|
*/
|
|
|
|
function getBrightRadius() {
|
|
|
|
const brightSight = getBrightVision.call(this);
|
|
|
|
let r = Math.abs(this.data.brightLight) > Math.abs(brightSight) ? this.data.brightLight : brightSight;
|
|
|
|
return this.getLightRadius(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the light and vision source objects associated with this Token
|
2021-06-29 19:10:55 +02:00
|
|
|
* @typedef {({defer, deleted, noUpdateFog}?: {defer?: boolean, deleted?: boolean, noUpdateFog?: boolean}) => void} UpdateSourceFunction
|
2021-06-29 05:27:27 +02:00
|
|
|
* @param {UpdateSourceFunction} wrapped The function that is wrapped by this function and needs to be called next in the chain
|
|
|
|
* @param {boolean} [defer] Defer refreshing the SightLayer to manually call that refresh later.
|
|
|
|
* @param {boolean} [deleted] Indicate that this light source has been deleted.
|
|
|
|
* @param {boolean} [noUpdateFog] Never update the Fog exploration progress for this update.
|
|
|
|
*/
|
|
|
|
function updateSource(wrapped, { defer = false, deleted = false, noUpdateFog = false } = {}) {
|
|
|
|
wrapped({ defer, deleted, noUpdateFog });
|
|
|
|
|
|
|
|
// Prepare some common data
|
|
|
|
const origin = this.getSightOrigin();
|
|
|
|
const sourceId = this.sourceId;
|
|
|
|
const d = canvas.dimensions;
|
|
|
|
|
|
|
|
// Update vision source
|
|
|
|
const isVisionSource = this._isVisionSource();
|
|
|
|
if (isVisionSource && !deleted) {
|
|
|
|
const dimSight = getDimVision.call(this);
|
|
|
|
const brightSight = getBrightVision.call(this);
|
|
|
|
let dim = Math.min(this.getLightRadius(dimSight), d.maxR);
|
|
|
|
const bright = Math.min(this.getLightRadius(brightSight), d.maxR);
|
|
|
|
this.vision.initialize({
|
|
|
|
x: origin.x,
|
|
|
|
y: origin.y,
|
|
|
|
dim: dim,
|
|
|
|
bright: bright,
|
|
|
|
angle: this.data.sightAngle,
|
|
|
|
rotation: this.data.rotation,
|
|
|
|
});
|
|
|
|
canvas.sight.sources.set(sourceId, this.vision);
|
|
|
|
if (!defer) {
|
|
|
|
this.vision.drawLight();
|
|
|
|
canvas.sight.refresh({ noUpdateFog });
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
canvas.sight.sources.delete(sourceId);
|
|
|
|
if (isVisionSource && !defer) canvas.sight.refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-29 19:10:55 +02:00
|
|
|
/**
|
|
|
|
* Specify the set of config buttons which should appear in the Application header.
|
|
|
|
* Buttons should be returned as an Array of objects.
|
|
|
|
* The header buttons which are added to the application can be modified by the getApplicationHeaderButtons hook.
|
|
|
|
* @typedef {{label: string, class: string, icon: string, onclick: Function|null}} ApplicationHeaderButton
|
|
|
|
* @param {() => ApplicationHeaderButton[]} wrapped The function that is wrapped by this function and needs to be called next in the chain
|
|
|
|
* @fires Application#hook:getApplicationHeaderButtons
|
|
|
|
* @returns {ApplicationHeaderButton[]}
|
|
|
|
*/
|
|
|
|
function getHeaderButtons(wrapped) {
|
|
|
|
const buttons = wrapped();
|
|
|
|
const button = {
|
|
|
|
label: 'DDV.TokenConfigHeaderButtonLabel',
|
|
|
|
class: 'configure-darkness-dependent-vision',
|
|
|
|
icon: 'fas fa-eye',
|
|
|
|
onclick: async () => {
|
|
|
|
return new DarknessDependentVisionConfig(this.object).render(true);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return [button, ...buttons];
|
|
|
|
}
|
|
|
|
|
2021-06-29 05:27:27 +02:00
|
|
|
Hooks.on('updateScene', (scene, change) => {
|
|
|
|
if (change.darkness != null) {
|
|
|
|
scene.getEmbeddedCollection('Token').forEach((tokenDocument) => tokenDocument.object.updateSource());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Hooks.on('updateToken', (token, change) => {
|
|
|
|
const shouldUpdateSource = [
|
2021-06-29 19:10:55 +02:00
|
|
|
'dimVisionDarknessMin',
|
|
|
|
'dimVisionDarknessMax',
|
|
|
|
'brightVisionDarknessMin',
|
|
|
|
'brightVisionDarknessMax',
|
2021-06-29 05:27:27 +02:00
|
|
|
].some((flagKey) => `flags.darkness-dependent-vision.${flagKey}` in foundry.utils.flattenObject(change));
|
|
|
|
if (shouldUpdateSource) {
|
|
|
|
token.object.updateSource();
|
|
|
|
}
|
|
|
|
});
|