179 lines
6.3 KiB
JavaScript
179 lines
6.3 KiB
JavaScript
|
|
/**
|
||
|
|
* A special subclass of DataField used to reference an AbstractBaseShader definition.
|
||
|
|
*/
|
||
|
|
class ShaderField extends foundry.data.fields.DataField {
|
||
|
|
|
||
|
|
/** @inheritdoc */
|
||
|
|
static get _defaults() {
|
||
|
|
const defaults = super._defaults;
|
||
|
|
defaults.nullable = true;
|
||
|
|
defaults.initial = undefined;
|
||
|
|
return defaults;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @override */
|
||
|
|
_cast(value) {
|
||
|
|
if ( !foundry.utils.isSubclass(value, AbstractBaseShader) ) {
|
||
|
|
throw new Error("The value provided to a ShaderField must be an AbstractBaseShader subclass.");
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A Vision Mode which can be selected for use by a Token.
|
||
|
|
* The selected Vision Mode alters the appearance of various aspects of the canvas while that Token is the POV.
|
||
|
|
*/
|
||
|
|
class VisionMode extends foundry.abstract.DataModel {
|
||
|
|
/**
|
||
|
|
* Construct a Vision Mode using provided configuration parameters and callback functions.
|
||
|
|
* @param {object} data Data which fulfills the model defined by the VisionMode schema.
|
||
|
|
* @param {object} [options] Additional options passed to the DataModel constructor.
|
||
|
|
*/
|
||
|
|
constructor(data={}, options={}) {
|
||
|
|
super(data, options);
|
||
|
|
this.animated = options.animated ?? false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @inheritDoc */
|
||
|
|
static defineSchema() {
|
||
|
|
const fields = foundry.data.fields;
|
||
|
|
const shaderSchema = () => new fields.SchemaField({
|
||
|
|
shader: new ShaderField(),
|
||
|
|
uniforms: new fields.ObjectField()
|
||
|
|
});
|
||
|
|
const lightingSchema = () => new fields.SchemaField({
|
||
|
|
visibility: new fields.NumberField({
|
||
|
|
initial: this.LIGHTING_VISIBILITY.ENABLED,
|
||
|
|
choices: Object.values(this.LIGHTING_VISIBILITY)
|
||
|
|
}),
|
||
|
|
postProcessingModes: new fields.ArrayField(new fields.StringField()),
|
||
|
|
uniforms: new fields.ObjectField()
|
||
|
|
});
|
||
|
|
|
||
|
|
// Return model schema
|
||
|
|
return {
|
||
|
|
id: new fields.StringField({blank: false}),
|
||
|
|
label: new fields.StringField({blank: false}),
|
||
|
|
tokenConfig: new fields.BooleanField({initial: true}),
|
||
|
|
canvas: new fields.SchemaField({
|
||
|
|
shader: new ShaderField(),
|
||
|
|
uniforms: new fields.ObjectField()
|
||
|
|
}),
|
||
|
|
lighting: new fields.SchemaField({
|
||
|
|
background: lightingSchema(),
|
||
|
|
coloration: lightingSchema(),
|
||
|
|
illumination: lightingSchema(),
|
||
|
|
darkness: lightingSchema(),
|
||
|
|
levels: new fields.ObjectField({
|
||
|
|
validate: o => {
|
||
|
|
const values = Object.values(CONST.LIGHTING_LEVELS);
|
||
|
|
return Object.entries(o).every(([k, v]) => values.includes(Number(k)) && values.includes(v));
|
||
|
|
},
|
||
|
|
validationError: "may only contain a mapping of keys from VisionMode.LIGHTING_LEVELS"
|
||
|
|
}),
|
||
|
|
multipliers: new fields.ObjectField({
|
||
|
|
validate: o => {
|
||
|
|
const values = Object.values(CONST.LIGHTING_LEVELS);
|
||
|
|
return Object.entries(o).every(([k, v]) => values.includes(Number(k)) && Number.isFinite(v));
|
||
|
|
},
|
||
|
|
validationError: "must provide a mapping of keys from VisionMode.LIGHTING_LEVELS to numeric multiplier values"
|
||
|
|
})
|
||
|
|
}),
|
||
|
|
vision: new fields.SchemaField({
|
||
|
|
background: shaderSchema(),
|
||
|
|
coloration: shaderSchema(),
|
||
|
|
illumination: shaderSchema(),
|
||
|
|
darkness: new fields.SchemaField({
|
||
|
|
adaptive: new fields.BooleanField({initial: true})
|
||
|
|
}),
|
||
|
|
defaults: new fields.SchemaField({
|
||
|
|
color: new fields.ColorField({required: false, initial: undefined}),
|
||
|
|
attenuation: new fields.AlphaField({required: false, initial: undefined}),
|
||
|
|
brightness: new fields.NumberField({required: false, initial: undefined, nullable: false, min: -1, max: 1}),
|
||
|
|
saturation: new fields.NumberField({required: false, initial: undefined, nullable: false, min: -1, max: 1}),
|
||
|
|
contrast: new fields.NumberField({required: false, initial: undefined, nullable: false, min: -1, max: 1})
|
||
|
|
}),
|
||
|
|
preferred: new fields.BooleanField({initial: false})
|
||
|
|
})
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The lighting illumination levels which are supported.
|
||
|
|
* @enum {number}
|
||
|
|
*/
|
||
|
|
static LIGHTING_LEVELS = CONST.LIGHTING_LEVELS;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Flags for how each lighting channel should be rendered for the currently active vision modes:
|
||
|
|
* - Disabled: this lighting layer is not rendered, the shaders does not decide.
|
||
|
|
* - Enabled: this lighting layer is rendered normally, and the shaders can choose if they should be rendered or not.
|
||
|
|
* - Required: the lighting layer is rendered, the shaders does not decide.
|
||
|
|
* @enum {number}
|
||
|
|
*/
|
||
|
|
static LIGHTING_VISIBILITY = {
|
||
|
|
DISABLED: 0,
|
||
|
|
ENABLED: 1,
|
||
|
|
REQUIRED: 2
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A flag for whether this vision source is animated
|
||
|
|
* @type {boolean}
|
||
|
|
*/
|
||
|
|
animated = false;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Does this vision mode enable light sources?
|
||
|
|
* True unless it disables lighting entirely.
|
||
|
|
* @type {boolean}
|
||
|
|
*/
|
||
|
|
get perceivesLight() {
|
||
|
|
const {background, illumination, coloration} = this.lighting;
|
||
|
|
return !!(background.visibility || illumination.visibility || coloration.visibility);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Special activation handling that could be implemented by VisionMode subclasses
|
||
|
|
* @param {VisionSource} source Activate this VisionMode for a specific source
|
||
|
|
* @abstract
|
||
|
|
*/
|
||
|
|
_activate(source) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Special deactivation handling that could be implemented by VisionMode subclasses
|
||
|
|
* @param {VisionSource} source Deactivate this VisionMode for a specific source
|
||
|
|
* @abstract
|
||
|
|
*/
|
||
|
|
_deactivate(source) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Special handling which is needed when this Vision Mode is activated for a VisionSource.
|
||
|
|
* @param {VisionSource} source Activate this VisionMode for a specific source
|
||
|
|
*/
|
||
|
|
activate(source) {
|
||
|
|
if ( source._visionModeActivated ) return;
|
||
|
|
source._visionModeActivated = true;
|
||
|
|
this._activate(source);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Special handling which is needed when this Vision Mode is deactivated for a VisionSource.
|
||
|
|
* @param {VisionSource} source Deactivate this VisionMode for a specific source
|
||
|
|
*/
|
||
|
|
deactivate(source) {
|
||
|
|
if ( !source._visionModeActivated ) return;
|
||
|
|
source._visionModeActivated = false;
|
||
|
|
this._deactivate(source);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* An animation function which runs every frame while this Vision Mode is active.
|
||
|
|
* @param {number} dt The deltaTime passed by the PIXI Ticker
|
||
|
|
*/
|
||
|
|
animate(dt) {
|
||
|
|
return foundry.canvas.sources.PointVisionSource.prototype.animateTime.call(this, dt);
|
||
|
|
}
|
||
|
|
}
|