2017-09-24 22:23:22 +00:00
|
|
|
module TK.SpaceTac.UI {
|
2017-10-01 20:52:50 +00:00
|
|
|
export type KeyPressedCallback = (key: string) => void
|
2015-04-23 22:36:57 +00:00
|
|
|
|
2017-05-03 18:12:13 +00:00
|
|
|
/**
|
|
|
|
* Manager for keyboard/mouse/touch events.
|
|
|
|
*/
|
|
|
|
export class InputManager {
|
2017-05-29 23:15:32 +00:00
|
|
|
private debug = false
|
2017-05-03 18:12:13 +00:00
|
|
|
private view: BaseView
|
|
|
|
private game: MainUI
|
|
|
|
private input: Phaser.Input
|
2015-04-23 22:36:57 +00:00
|
|
|
|
2017-05-03 18:12:13 +00:00
|
|
|
private cheats_allowed: boolean
|
|
|
|
private cheat: boolean
|
2015-04-23 22:36:57 +00:00
|
|
|
|
2017-10-01 20:52:50 +00:00
|
|
|
private hovered: Phaser.Button | null = null
|
|
|
|
|
2017-05-03 18:12:13 +00:00
|
|
|
private binds: { [key: string]: KeyPressedCallback } = {}
|
2015-04-23 22:36:57 +00:00
|
|
|
|
2017-05-03 18:12:13 +00:00
|
|
|
private keyboard_grabber: any = null
|
|
|
|
private keyboard_callback: KeyPressedCallback | null = null
|
2015-04-23 22:36:57 +00:00
|
|
|
|
|
|
|
constructor(view: BaseView) {
|
|
|
|
this.view = view;
|
|
|
|
this.game = view.gameui;
|
|
|
|
this.input = view.input;
|
2017-05-03 18:12:13 +00:00
|
|
|
this.cheats_allowed = true;
|
2015-04-23 22:36:57 +00:00
|
|
|
this.cheat = false;
|
|
|
|
|
2017-02-12 18:54:09 +00:00
|
|
|
this.input.reset(true);
|
|
|
|
|
2015-04-23 22:36:57 +00:00
|
|
|
// Default mappings
|
2017-05-03 18:12:13 +00:00
|
|
|
this.bind("s", "Quick save", () => {
|
2015-04-23 22:36:57 +00:00
|
|
|
this.game.saveGame();
|
|
|
|
});
|
2017-05-03 18:12:13 +00:00
|
|
|
this.bind("l", "Quick load", () => {
|
2015-04-23 22:36:57 +00:00
|
|
|
this.game.loadGame();
|
|
|
|
this.game.state.start("router");
|
|
|
|
});
|
2017-05-03 18:12:13 +00:00
|
|
|
this.bind("m", "Toggle sound", () => {
|
2017-06-08 17:32:57 +00:00
|
|
|
this.game.options.setNumberValue("mainvolume", this.game.options.getNumberValue("mainvolume") > 0 ? 0 : 1);
|
2015-04-23 22:36:57 +00:00
|
|
|
});
|
2017-05-15 23:15:07 +00:00
|
|
|
this.bind("f", "Toggle fullscreen", () => {
|
2017-06-08 17:32:57 +00:00
|
|
|
this.game.options.setBooleanValue("fullscreen", !this.game.options.getBooleanValue("fullscreen"));
|
2017-05-15 23:15:07 +00:00
|
|
|
});
|
2017-05-03 18:12:13 +00:00
|
|
|
this.bind("+", "", () => {
|
|
|
|
if (this.cheats_allowed) {
|
2015-04-23 22:36:57 +00:00
|
|
|
this.cheat = !this.cheat;
|
2017-02-12 18:54:09 +00:00
|
|
|
this.game.displayMessage(this.cheat ? "Cheats enabled" : "Cheats disabled");
|
2015-04-23 22:36:57 +00:00
|
|
|
}
|
|
|
|
});
|
2017-05-03 18:12:13 +00:00
|
|
|
|
2017-10-11 21:49:56 +00:00
|
|
|
if (!this.game.headless) {
|
|
|
|
this.input.keyboard.addCallbacks(this, undefined, (event: KeyboardEvent) => {
|
|
|
|
if (this.debug) {
|
|
|
|
console.log(event);
|
|
|
|
}
|
2017-05-29 23:15:32 +00:00
|
|
|
|
2017-10-11 21:49:56 +00:00
|
|
|
this.forceLeaveHovered();
|
2017-10-01 20:52:50 +00:00
|
|
|
|
2017-10-11 21:49:56 +00:00
|
|
|
if (!contains(["Control", "Shift", "Alt", "Meta"], event.key)) {
|
|
|
|
this.keyPress(event.key);
|
|
|
|
if (event.code != event.key) {
|
|
|
|
this.keyPress(event.code);
|
|
|
|
}
|
2017-05-03 18:12:13 +00:00
|
|
|
}
|
2017-10-11 21:49:56 +00:00
|
|
|
});
|
|
|
|
}
|
2015-04-23 22:36:57 +00:00
|
|
|
}
|
|
|
|
|
2017-05-03 18:12:13 +00:00
|
|
|
/**
|
|
|
|
* Bind a key to a specific action.
|
|
|
|
*/
|
|
|
|
bind(key: string, desc: string, action: Function): void {
|
|
|
|
this.binds[key] = (key) => action();
|
2015-04-23 22:36:57 +00:00
|
|
|
}
|
|
|
|
|
2017-05-03 18:12:13 +00:00
|
|
|
/**
|
|
|
|
* Bind a key to a cheat action.
|
|
|
|
*
|
|
|
|
* The action will only be applied if cheat mode is activated.
|
|
|
|
*/
|
|
|
|
bindCheat(key: string, desc: string, action: Function): void {
|
2017-03-09 17:11:00 +00:00
|
|
|
this.bind(key, `Cheat: ${desc}`, () => {
|
2015-04-23 22:36:57 +00:00
|
|
|
if (this.cheat) {
|
2017-03-09 17:11:00 +00:00
|
|
|
console.warn(`Cheat ! ${desc}`);
|
2015-04-23 22:36:57 +00:00
|
|
|
action();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-05-03 18:12:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply a key press
|
|
|
|
*/
|
|
|
|
keyPress(key: string): void {
|
|
|
|
if (this.keyboard_callback) {
|
|
|
|
this.keyboard_callback(key);
|
|
|
|
} else if (this.binds[key]) {
|
|
|
|
this.binds[key](key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Grab the keyboard to receive next key presses.
|
|
|
|
*
|
|
|
|
* Release will happen if another grab is made, or if releaseKeyboard is called.
|
|
|
|
*
|
|
|
|
* *handle* is used to identify the grabber.
|
|
|
|
*/
|
|
|
|
grabKeyboard(handle: any, callback: KeyPressedCallback): void {
|
|
|
|
this.keyboard_grabber = handle;
|
|
|
|
this.keyboard_callback = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release the keyboard.
|
|
|
|
*/
|
|
|
|
releaseKeyboard(handle: any): void {
|
|
|
|
if (handle === this.keyboard_grabber) {
|
|
|
|
this.keyboard_grabber = null;
|
|
|
|
this.keyboard_callback = null;
|
|
|
|
}
|
|
|
|
}
|
2017-10-01 20:52:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Force the cursor out of currently hovered object
|
|
|
|
*/
|
|
|
|
private forceLeaveHovered() {
|
|
|
|
if (this.hovered && this.hovered.data.hover_pointer) {
|
|
|
|
(<any>this.hovered.input)._pointerOutHandler(this.hovered.data.hover_pointer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-11 22:28:33 +00:00
|
|
|
/**
|
|
|
|
* Setup dragging on an UI component
|
|
|
|
*
|
|
|
|
* If no drag or drop function is defined, dragging is disabled
|
2017-10-12 23:16:33 +00:00
|
|
|
*
|
|
|
|
* If update function is defined, it will receive (a lot of) cursor moves while dragging
|
2017-10-11 22:28:33 +00:00
|
|
|
*/
|
2017-10-12 23:16:33 +00:00
|
|
|
setDragDrop(obj: Phaser.Button | Phaser.Image, drag?: Function, drop?: Function, update?: Function): void {
|
2017-10-12 19:11:58 +00:00
|
|
|
obj.events.onDragStart.removeAll();
|
|
|
|
obj.events.onDragStop.removeAll();
|
2017-10-12 23:16:33 +00:00
|
|
|
obj.events.onDragUpdate.removeAll();
|
2017-10-12 19:11:58 +00:00
|
|
|
|
2017-10-11 22:28:33 +00:00
|
|
|
if (drag && drop) {
|
|
|
|
obj.inputEnabled = true;
|
|
|
|
obj.input.enableDrag(false, true);
|
|
|
|
|
|
|
|
obj.events.onDragStart.add(() => {
|
|
|
|
this.forceLeaveHovered();
|
|
|
|
this.view.audio.playOnce("ui-drag");
|
|
|
|
drag();
|
|
|
|
});
|
2017-10-12 23:16:33 +00:00
|
|
|
|
2017-10-11 22:28:33 +00:00
|
|
|
obj.events.onDragStop.add(() => {
|
|
|
|
this.view.audio.playOnce("ui-drop");
|
|
|
|
drop();
|
|
|
|
});
|
2017-10-12 23:16:33 +00:00
|
|
|
|
|
|
|
if (update) {
|
|
|
|
obj.events.onDragUpdate.add(() => {
|
|
|
|
update();
|
|
|
|
});
|
|
|
|
}
|
2017-10-11 22:28:33 +00:00
|
|
|
} else {
|
|
|
|
obj.input.disableDrag();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-01 20:52:50 +00:00
|
|
|
/**
|
|
|
|
* Setup hover/click handlers on an UI element
|
|
|
|
*
|
|
|
|
* This is done in a way that should be compatible with touch-enabled screen
|
|
|
|
*
|
|
|
|
* Returns functions that may be used to force the behavior
|
|
|
|
*/
|
2017-10-29 21:08:55 +00:00
|
|
|
setHoverClick(obj: Phaser.Button, enter: Function = nop, leave: Function = nop, click: Function = nop, hovertime = 300, holdtime = 600) {
|
2017-10-26 21:47:13 +00:00
|
|
|
let holdstart = Timer.nowMs();
|
2017-10-01 20:52:50 +00:00
|
|
|
let enternext: Function | null = null;
|
|
|
|
let entercalled = false;
|
|
|
|
let cursorinside = false;
|
|
|
|
let destroyed = false;
|
|
|
|
|
|
|
|
obj.input.useHandCursor = true;
|
|
|
|
|
|
|
|
let prevententer = () => {
|
|
|
|
if (enternext != null) {
|
|
|
|
Timer.global.cancel(enternext);
|
|
|
|
enternext = null;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let effectiveenter = () => {
|
|
|
|
if (!destroyed) {
|
|
|
|
enternext = null;
|
|
|
|
entercalled = true;
|
|
|
|
enter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let effectiveleave = () => {
|
|
|
|
prevententer();
|
|
|
|
if (entercalled) {
|
|
|
|
entercalled = false;
|
|
|
|
leave();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj.events) {
|
|
|
|
obj.events.onDestroy.addOnce(() => {
|
|
|
|
destroyed = true;
|
|
|
|
effectiveleave();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
obj.onInputOver.add((_: any, pointer: Phaser.Pointer) => {
|
|
|
|
if (destroyed) return;
|
|
|
|
|
|
|
|
if (this.hovered) {
|
|
|
|
if (this.hovered === obj) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
this.forceLeaveHovered();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.hovered = obj;
|
|
|
|
this.hovered.data.hover_pointer = pointer;
|
|
|
|
|
|
|
|
if (obj.visible && obj.alpha) {
|
|
|
|
cursorinside = true;
|
|
|
|
enternext = Timer.global.schedule(hovertime, effectiveenter);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
obj.onInputOut.add(() => {
|
|
|
|
if (destroyed) return;
|
|
|
|
|
|
|
|
if (this.hovered === obj) {
|
|
|
|
this.hovered = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
cursorinside = false;
|
|
|
|
effectiveleave();
|
|
|
|
});
|
|
|
|
|
|
|
|
obj.onInputDown.add(() => {
|
|
|
|
if (destroyed) return;
|
|
|
|
|
|
|
|
if (obj.visible && obj.alpha) {
|
2017-10-26 21:47:13 +00:00
|
|
|
holdstart = Timer.nowMs();
|
2017-10-01 20:52:50 +00:00
|
|
|
if (!cursorinside && !enternext) {
|
|
|
|
enternext = Timer.global.schedule(holdtime, effectiveenter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
obj.onInputUp.add(() => {
|
|
|
|
if (destroyed) return;
|
|
|
|
|
|
|
|
if (!cursorinside) {
|
|
|
|
effectiveleave();
|
|
|
|
}
|
|
|
|
|
2017-10-26 21:47:13 +00:00
|
|
|
if (Timer.fromMs(holdstart) < holdtime) {
|
2017-10-01 20:52:50 +00:00
|
|
|
if (!cursorinside) {
|
|
|
|
effectiveenter();
|
|
|
|
}
|
|
|
|
click();
|
|
|
|
if (!cursorinside) {
|
|
|
|
effectiveleave();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-04-23 22:36:57 +00:00
|
|
|
}
|
|
|
|
}
|