Files
Foundry-VTT-Docker/resources/app/client/pixi/layers/controls/door.js

216 lines
6.6 KiB
JavaScript
Raw Normal View History

2025-01-04 00:34:03 +01:00
/**
* An icon representing a Door Control
* @extends {PIXI.Container}
*/
class DoorControl extends PIXI.Container {
constructor(wall) {
super();
this.wall = wall;
this.visible = false; // Door controls are not visible by default
}
/* -------------------------------------------- */
/**
* The center of the wall which contains the door.
* @type {PIXI.Point}
*/
get center() {
return this.wall.center;
}
/* -------------------------------------------- */
/**
* Draw the DoorControl icon, displaying its icon texture and border
* @returns {Promise<DoorControl>}
*/
async draw() {
// Background
this.bg = this.bg || this.addChild(new PIXI.Graphics());
this.bg.clear().beginFill(0x000000, 1.0).drawRoundedRect(-2, -2, 44, 44, 5).endFill();
this.bg.alpha = 0;
// Control Icon
this.icon = this.icon || this.addChild(new PIXI.Sprite());
this.icon.width = this.icon.height = 40;
this.icon.alpha = 0.6;
this.icon.texture = this._getTexture();
// Border
this.border = this.border || this.addChild(new PIXI.Graphics());
this.border.clear().lineStyle(1, 0xFF5500, 0.8).drawRoundedRect(-2, -2, 44, 44, 5).endFill();
this.border.visible = false;
// Add control interactivity
this.eventMode = "static";
this.interactiveChildren = false;
this.hitArea = new PIXI.Rectangle(-2, -2, 44, 44);
this.cursor = "pointer";
// Set position
this.reposition();
this.alpha = 1.0;
// Activate listeners
this.removeAllListeners();
this.on("pointerover", this._onMouseOver).on("pointerout", this._onMouseOut)
.on("pointerdown", this._onMouseDown).on("rightdown", this._onRightDown);
return this;
}
/* -------------------------------------------- */
/**
* Get the icon texture to use for the Door Control icon based on the door state
* @returns {PIXI.Texture}
*/
_getTexture() {
// Determine displayed door state
const ds = CONST.WALL_DOOR_STATES;
let s = this.wall.document.ds;
if ( !game.user.isGM && (s === ds.LOCKED) ) s = ds.CLOSED;
// Determine texture path
const icons = CONFIG.controlIcons;
let path = {
[ds.LOCKED]: icons.doorLocked,
[ds.CLOSED]: icons.doorClosed,
[ds.OPEN]: icons.doorOpen
}[s] || icons.doorClosed;
if ( (s === ds.CLOSED) && (this.wall.document.door === CONST.WALL_DOOR_TYPES.SECRET) ) path = icons.doorSecret;
// Obtain the icon texture
return getTexture(path);
}
/* -------------------------------------------- */
reposition() {
let pos = this.wall.midpoint.map(p => p - 20);
this.position.set(...pos);
}
/* -------------------------------------------- */
/**
* Determine whether the DoorControl is visible to the calling user's perspective.
* The control is always visible if the user is a GM and no Tokens are controlled.
* @see {CanvasVisibility#testVisibility}
* @type {boolean}
*/
get isVisible() {
if ( !canvas.visibility.tokenVision ) return true;
// Hide secret doors from players
const w = this.wall;
if ( (w.document.door === CONST.WALL_DOOR_TYPES.SECRET) && !game.user.isGM ) return false;
// Test two points which are perpendicular to the door midpoint
const ray = this.wall.toRay();
const [x, y] = w.midpoint;
const [dx, dy] = [-ray.dy, ray.dx];
const t = 3 / (Math.abs(dx) + Math.abs(dy)); // Approximate with Manhattan distance for speed
const points = [
{x: x + (t * dx), y: y + (t * dy)},
{x: x - (t * dx), y: y - (t * dy)}
];
// Test each point for visibility
return points.some(p => {
return canvas.visibility.testVisibility(p, {object: this, tolerance: 0});
});
}
/* -------------------------------------------- */
/* Event Handlers */
/* -------------------------------------------- */
/**
* Handle mouse over events on a door control icon.
* @param {PIXI.FederatedEvent} event The originating interaction event
* @protected
*/
_onMouseOver(event) {
event.stopPropagation();
const canControl = game.user.can("WALL_DOORS");
const blockPaused = game.paused && !game.user.isGM;
if ( !canControl || blockPaused ) return false;
this.border.visible = true;
this.icon.alpha = 1.0;
this.bg.alpha = 0.25;
canvas.walls.hover = this.wall;
}
/* -------------------------------------------- */
/**
* Handle mouse out events on a door control icon.
* @param {PIXI.FederatedEvent} event The originating interaction event
* @protected
*/
_onMouseOut(event) {
event.stopPropagation();
if ( game.paused && !game.user.isGM ) return false;
this.border.visible = false;
this.icon.alpha = 0.6;
this.bg.alpha = 0;
canvas.walls.hover = null;
}
/* -------------------------------------------- */
/**
* Handle left mouse down events on a door control icon.
* This should only toggle between the OPEN and CLOSED states.
* @param {PIXI.FederatedEvent} event The originating interaction event
* @protected
*/
_onMouseDown(event) {
if ( event.button !== 0 ) return; // Only support standard left-click
event.stopPropagation();
const { ds } = this.wall.document;
const states = CONST.WALL_DOOR_STATES;
// Determine whether the player can control the door at this time
if ( !game.user.can("WALL_DOORS") ) return false;
if ( game.paused && !game.user.isGM ) {
ui.notifications.warn("GAME.PausedWarning", {localize: true});
return false;
}
const sound = !(game.user.isGM && game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.ALT));
// Play an audio cue for testing locked doors, only for the current client
if ( ds === states.LOCKED ) {
if ( sound ) this.wall._playDoorSound("test");
return false;
}
// Toggle between OPEN and CLOSED states
return this.wall.document.update({ds: ds === states.CLOSED ? states.OPEN : states.CLOSED}, {sound});
}
/* -------------------------------------------- */
/**
* Handle right mouse down events on a door control icon.
* This should toggle whether the door is LOCKED or CLOSED.
* @param {PIXI.FederatedEvent} event The originating interaction event
* @protected
*/
_onRightDown(event) {
event.stopPropagation();
if ( !game.user.isGM ) return;
let state = this.wall.document.ds;
const states = CONST.WALL_DOOR_STATES;
if ( state === states.OPEN ) return;
state = state === states.LOCKED ? states.CLOSED : states.LOCKED;
const sound = !(game.user.isGM && game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.ALT));
return this.wall.document.update({ds: state}, {sound});
}
}