Initial
This commit is contained in:
215
resources/app/client/pixi/layers/controls/door.js
Normal file
215
resources/app/client/pixi/layers/controls/door.js
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* 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});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user