216 lines
6.6 KiB
JavaScript
216 lines
6.6 KiB
JavaScript
/**
|
|
* 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});
|
|
}
|
|
}
|