Files
2025-01-04 00:34:03 +01:00

176 lines
5.1 KiB
JavaScript

/**
* The Lighting Layer which ambient light sources as part of the CanvasEffectsGroup.
* @category - Canvas
*/
class LightingLayer extends PlaceablesLayer {
/** @inheritdoc */
static documentName = "AmbientLight";
/** @inheritdoc */
static get layerOptions() {
return foundry.utils.mergeObject(super.layerOptions, {
name: "lighting",
rotatableObjects: true,
zIndex: 900
});
}
/**
* Darkness change event handler function.
* @type {_onDarknessChange}
*/
#onDarknessChange;
/* -------------------------------------------- */
/** @inheritdoc */
get hookName() {
return LightingLayer.name;
}
/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */
/** @inheritDoc */
async _draw(options) {
await super._draw(options);
this.#onDarknessChange = this._onDarknessChange.bind(this);
canvas.environment.addEventListener("darknessChange", this.#onDarknessChange);
}
/* -------------------------------------------- */
/** @inheritDoc */
async _tearDown(options) {
canvas.environment.removeEventListener("darknessChange", this.#onDarknessChange);
this.#onDarknessChange = undefined;
return super._tearDown(options);
}
/* -------------------------------------------- */
/* Methods */
/* -------------------------------------------- */
/**
* Refresh the fields of all the ambient lights on this scene.
*/
refreshFields() {
if ( !this.active ) return;
for ( const ambientLight of this.placeables ) {
ambientLight.renderFlags.set({refreshField: true});
}
}
/* -------------------------------------------- */
/** @override */
_activate() {
super._activate();
for ( const p of this.placeables ) p.renderFlags.set({refreshField: true});
}
/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */
/** @inheritDoc */
_canDragLeftStart(user, event) {
// Prevent creating a new light if currently previewing one.
if ( this.preview.children.length ) {
ui.notifications.warn("CONTROLS.ObjectConfigured", { localize: true });
return false;
}
return super._canDragLeftStart(user, event);
}
/* -------------------------------------------- */
/** @override */
_onDragLeftStart(event) {
super._onDragLeftStart(event);
const interaction = event.interactionData;
// Snap the origin to the grid
if ( !event.shiftKey ) interaction.origin = this.getSnappedPoint(interaction.origin);
// Create a pending AmbientLightDocument
const cls = getDocumentClass("AmbientLight");
const doc = new cls(interaction.origin, {parent: canvas.scene});
// Create the preview AmbientLight object
const preview = new this.constructor.placeableClass(doc);
// Updating interaction data
interaction.preview = this.preview.addChild(preview);
interaction.lightsState = 1;
// Prepare to draw the preview
preview.draw();
}
/* -------------------------------------------- */
/** @override */
_onDragLeftMove(event) {
const {destination, lightsState, preview, origin} = event.interactionData;
if ( lightsState === 0 ) return;
// Update the light radius
const radius = Math.hypot(destination.x - origin.x, destination.y - origin.y);
// Update the preview object data
preview.document.config.dim = radius * (canvas.dimensions.distance / canvas.dimensions.size);
preview.document.config.bright = preview.document.config.dim / 2;
// Refresh the layer display
preview.initializeLightSource();
preview.renderFlags.set({refreshState: true});
// Confirm the creation state
event.interactionData.lightsState = 2;
}
/* -------------------------------------------- */
/** @override */
_onDragLeftCancel(event) {
super._onDragLeftCancel(event);
canvas.effects.refreshLighting();
event.interactionData.lightsState = 0;
}
/* -------------------------------------------- */
/** @override */
_onMouseWheel(event) {
// Identify the hovered light source
const light = this.hover;
if ( !light || light.isPreview || (light.document.config.angle === 360) ) return;
// Determine the incremental angle of rotation from event data
const snap = event.shiftKey ? 15 : 3;
const delta = snap * Math.sign(event.delta);
return light.rotate(light.document.rotation + delta, snap);
}
/* -------------------------------------------- */
/**
* Actions to take when the darkness level of the Scene is changed
* @param {PIXI.FederatedEvent} event
* @internal
*/
_onDarknessChange(event) {
const {darknessLevel, priorDarknessLevel} = event.environmentData;
for ( const light of this.placeables ) {
const {min, max} = light.document.config.darkness;
if ( darknessLevel.between(min, max) === priorDarknessLevel.between(min, max) ) continue;
light.initializeLightSource();
if ( this.active ) light.renderFlags.set({refreshState: true});
}
}
}