Home Reference Source Test

src/manager/InputManager.js

import Manager from './Manager';
import DOMManager from './DOMManager';
import Messager from '../utils/Messager';
import KeyCode from '../const/KeyCode';
import { SceneManager, Point } from '../entry';

/**
 * A manager class intended to manage the javascript callback associated with the DOM.
 * The InputManager filters general DOM events such as key down, key up, left and right click.
 * The manager sorts events into referenceable lists for components to reference each game tick.
 * 
 * In result, the InputManager creates a bridge between the DOM and Engine. Components should use the 
 * `is` functions to determine if a specific character is down for that game tick. 
 * 
 * If you want to listen to all characters, for example a textbox input, use events instead. To listen to events, 
 * use InputManager.events to access the event. Events are defined in InputManager.EVENT. See Messager.js for more info
 * on listening to events.
 */
export class _InputManager extends Manager {
    /**
     * Constructor. Initializes Messager, event definitons, and 
     * key and mouse states.
     */
    constructor() {
        super();     
        this.events = new Messager();
        this.EVENT = {
            LEFT_PRESSED: 'mouseLeftPressed',
            LEFT_RELEASED: 'mouseLeftReleased',
            RIGHT_PRESSED: 'mouseRightPressed',
            RIGHT_RELEASED: 'mouseRightReleased',
            MOUSE_MOVE: 'mouseMove',
            KEY_DOWN: 'keyDown',
            KEY_UP: 'keyUp',
            KEY: 'keyDownRepeat',
        };
        this.KEY_DOWN = [];
        this.KEY_PRESSED = [];
        this.KEY_UP = [];
        this.LEFT_PRESSED = [];
        this.LEFT_RELEASED = [];
        this.RIGHT_PRESSED = [];
        this.RIGHT_RELEASED = [];

        const AllKeys = 256;
        for (let i = 0; i < AllKeys; i++) {
            this.KEY_DOWN[i] = false;
            this.KEY_PRESSED[i] = false;
            this.KEY_UP[i] = false;
        }
    }

    /**
     * Returns true if the key represented by this KeyCode is pressed down on 
     * this game tick. Does not repeat each game tick. Must be released to re-trigger.
     * The `isKeyPressed()` will also return true when this function does.
     * 
     * @param {KeyCode} KeyCode Keycode enum which represents a character by number.
     * 
     * @returns {boolean} Is this key down.
     */
    isKeyDown(KeyCode) {
        return this.KEY_DOWN[KeyCode];
    }

    /**
     * Returns true if the key represented by this KeyCode is held down on 
     * this game tick. Repeats each game tick it is held down.
     * 
     * @param {KeyCode} KeyCode Keycode enum which represents a character by number.
     * 
     * @returns {boolean} Is this key pressed (held down).
     */
    isKeyPressed(KeyCode) {
        return this.KEY_PRESSED[KeyCode];
    }

    /**
     * Returns true if the key represented by this KeyCode is released on 
     * this game tick.
     * 
     * @param {KeyCode} KeyCode Keycode enum which represents a character by number.
     * 
     * @returns {boolean} Is this key released.
     */
    isKeyUp(KeyCode) {
        return this.KEY_UP[KeyCode];
    }

    /**
     * Called by the EngineManager. Not intended to be referenced. 
     * Sets up event listeners on the DOM.
     */
    start() {
        DOMManager.canvas.addEventListener('mouseup', (ev) => {
            ev.preventDefault();
            let event = {
                x: ev.offsetX * DOMManager.canvasDPIWidth,
                y: ev.offsetY * DOMManager.canvasDPIHeight,
                shiftHeld: ev.shiftKey,
                ctrlHeld: ev.ctrlKey,
            };
            let curScene = SceneManager.getCurrentScene();
            if (curScene != null) {
                for (let i = 0; i < curScene.viewports.length; i++) {                        
                    if (curScene.viewports[i].getBounds().isInBounds(new Point(event.x, event.y))) {
                        let relEvent = Object.assign({}, event);
                        relEvent.x -= curScene.viewports[i].getBounds().p1.x;
                        relEvent.y -= curScene.viewports[i].getBounds().p1.y;
                        if (ev.button == 0) {
                            this.LEFT_RELEASED[i].push(relEvent);
                        } else if (ev.button == 2) {
                            this.RIGHT_RELEASED[i].push(relEvent);
                        }
                    }
                }
            }
            if (ev.button == 0) {
                this.events.set(this.EVENT.LEFT_RELEASED, event);
            } else if (ev.button == 2) {
                this.events.set(this.EVENT.RIGHT_RELEASED, event);
            }
        })
        DOMManager.canvas.addEventListener('mousedown', (ev) => {
            ev.preventDefault();
            let event = {
                x: ev.offsetX * DOMManager.canvasDPIWidth,
                y: ev.offsetY * DOMManager.canvasDPIHeight,
                shiftHeld: ev.shiftKey,
                ctrlHeld: ev.ctrlKey,
            };
            let curScene = SceneManager.getCurrentScene();
            if (curScene != null) {
                for (let i = 0; i < curScene.viewports.length; i++) {                        
                    if (curScene.viewports[i].getBounds().isInBounds(new Point(event.x, event.y))) {
                        let relEvent = Object.assign({}, event);
                        relEvent.x -= curScene.viewports[i].getBounds().p1.x;
                        relEvent.y -= curScene.viewports[i].getBounds().p1.y;
                        if (ev.button == 0) {
                            this.LEFT_PRESSED[i].push(relEvent);
                        } else if (ev.button == 2) {
                            this.RIGHT_PRESSED[i].push(relEvent);
                        }
                    }
                }
            }
            if (ev.button == 0) {
                this.events.set(this.EVENT.LEFT_PRESSED, event);
            } else if (ev.button == 2) {
                this.events.set(this.EVENT.RIGHT_PRESSED, event);
            }
        })

        DOMManager.canvas.addEventListener('mousemove', (ev) => {
            ev.preventDefault();
            let event = {
                x: ev.offsetX * DOMManager.canvasDPIWidth,
                y: ev.offsetY * DOMManager.canvasDPIHeight,
                shiftHeld: ev.shiftKey,
                ctrlHeld: ev.ctrlKey,
            };
            let curScene = SceneManager.getCurrentScene();
            if (curScene != null) {
                for (let i = 0; i < curScene.viewports.length; i++) {                        
                    if (curScene.viewports[i].getBounds().isInBounds(new Point(event.x, event.y))) {
                        event.x -= curScene.viewports[i].getBounds().p1.x;
                        event.y -= curScene.viewports[i].getBounds().p1.y;
                        event.viewport = i;
                        break;
                    }
                }
            }
            this.events.set(this.EVENT.MOUSE_MOVE, event);
        })

        //right click manager
        DOMManager.canvas.oncontextmenu = (ev) => {
            ev.preventDefault();
        };

        //Key down manager
        document.addEventListener('keydown', (event) => {
            event.preventDefault();
            let code = event.which || event.keyCode;
            this.KEY_PRESSED[code] = true;
            if (!event.repeat) {
                this.KEY_DOWN[code] = true;
                this.events.set(this.EVENT.KEY_DOWN, {
                    key: event.key,
                    code,
                    shiftHeld: event.shiftKey,
                    ctrlHeld: event.ctrlKey,
                    repeat: event.repeat,
                });
            }
            this.events.set(this.EVENT.KEY, {
                key: event.key,
                code,
                shiftHeld: event.shiftKey,
                ctrlHeld: event.ctrlKey,
                repeat: event.repeat,
            });
        });

        //Key up manager
        document.addEventListener('keyup', (event) => {
            event.preventDefault();
            let code = event.which || event.keyCode;
            this.KEY_DOWN[code] = false;
            this.KEY_PRESSED[code] = false;
            this.KEY_UP[code] = true;
            this.events.set(this.EVENT.KEY_UP, {
                key: event.key,
                code,
                shiftHeld: event.shiftKey,
                ctrlHeld: event.ctrlKey,
                repeat: event.repeat,
            });
        });
    }

    /**
     * Managed by the EngineManager. Do not call directly. Updates character and mouse
     * inputs.
     */
    update() {
        this.KEY_DOWN = [];
        this.KEY_UP = [];
        this.LEFT_PRESSED = [];
        this.LEFT_RELEASED = [];
        this.RIGHT_PRESSED = [];
        this.RIGHT_RELEASED = [];

        let scene = null;
        if ((scene = SceneManager.getCurrentScene()) == null)
            return;

        for (let i = 0; i < scene.viewports.length; i++) {
            this.LEFT_PRESSED.push([]);
            this.LEFT_RELEASED.push([]);
            this.RIGHT_PRESSED.push([]);
            this.RIGHT_RELEASED.push([]);
        }
    }
}

/**
 * Singleton reference to the Input Manager.
 */
const InputManager = new _InputManager(); 
export default InputManager;