UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

252 lines (211 loc) • 6.76 kB
import { assert } from "../../../../core/assert.js"; import { resolvePath } from "../../../../core/json/resolvePath.js"; import ObservedValue from "../../../../core/model/ObservedValue.js"; import { System } from "../../../ecs/System.js"; import InputController from "../components/InputController.js"; /** * * @param {Object} object * @param {String} propertyName * @param {function} constructor * @returns {*} */ function getOrCreateProperty(object, propertyName, constructor) { assert.notEqual(object, undefined, `object must not be undefined, can't resolve property '${propertyName}`); assert.equal(typeof propertyName, "string", `propertyName must be of type 'string', instead was '${typeof propertyName}'`); assert.equal(typeof constructor, "function", `constructor must be of type 'function', instead was '${typeof constructor}'`); if (object.hasOwnProperty(propertyName)) { return object[propertyName]; } else { let value = constructor(); object[propertyName] = value; return value; } } /** * * @param {Object} proxies * @param {String} path * @param {Signal} signal * @returns {Proxy} */ function getOrCreateProxy(proxies, path, signal) { class Proxy { /** * * @param {Signal} signal * @constructor */ constructor(signal) { /** * @type {Signal} */ this.signal = signal; this.running = false; this.deferred = []; /** * * @type {InputControllerBinding[]} */ this.bindings = []; } handler() { const bindings = this.bindings; const l = bindings.length; for (let i = 0; i < l; i++) { /** * * @type {InputControllerBinding} */ const binding = bindings[i]; const listener = binding.listener; listener.apply(undefined, arguments); if (binding.exclusive) { break; } } this.__processDeferred(); } __processDeferred() { const deferred = this.deferred; const dl = deferred.length; for (let i = 0; i < dl; i++) { const binding = deferred[i]; this.registerBinding(binding); } this.deferred = []; } /** * * @param {InputControllerBinding} binding */ registerBinding(binding) { this.bindings.push(binding); this.bindings.sort((a, b) => { if (a.exclusive && !b.exclusive) { return -1; } else if (!a.exclusive && b.exclusive) { return 1; } else { return b.priotity - a.priority; } }); } start() { this.signal.add(this.handler, this); } stop() { this.signal.remove(this.handler, this); } /** * * @param {InputControllerBinding} binding */ add(binding) { //TODO handle case where binding is added mid-dispatch this.registerBinding(binding); } /** * * @param {InputControllerBinding} binding */ remove(binding) { const i_b = this.bindings.indexOf(binding); if (i_b !== -1) { this.bindings.splice(i_b, 1); } else { const i_d = this.deferred.indexOf(binding); this.deferred.splice(i_d, 1); } } } return getOrCreateProperty(proxies, path, function () { const proxy = new Proxy(signal); proxy.start(); return proxy; }); } /** * * @param {List.<InputControllerBinding>} mapping * @param devices * @param proxies */ function applyBindings(mapping, devices, proxies) { mapping.forEach(function (binding) { const path = binding.path; const signal = resolvePath(devices, path); const proxy = getOrCreateProxy(proxies, path, signal); proxy.add(binding); }); } /** * * @param {List.<InputControllerBinding>} mapping * @param devices * @param proxies */ function removeBindings(mapping, devices, proxies) { mapping.forEach(function (binding) { const path = binding.path; const signal = resolvePath(devices, path); const proxy = getOrCreateProxy(proxies, path, signal); proxy.remove(binding); }); } /** * Prefer to use {@link InputSystem} instead */ class InputControllerSystem extends System { constructor(devices) { super(); this.enabled = new ObservedValue(true); this.dependencies = [InputController]; this.devices = devices; // console.log("Input Controller System started. Devices: ", devices); const self = this; this.enabled.onChanged.add(function (v) { const entityManager = self.entityManager; const dataset = entityManager.dataset; if (dataset === null) { // do nothing return; } if (v) { dataset.traverseComponents(InputController, function (c) { applyBindings(c.mapping, self.devices, self.proxies); }); } else { dataset.traverseComponents(InputController, function (c) { removeBindings(c.mapping, self.devices, self.proxies); }); } }); } async startup(entityManager) { this.entityManager = entityManager; this.proxies = {}; } /** * * @param {InputController} component * @param entity */ link(component, entity) { const mapping = component.mapping; const devices = this.devices; applyBindings(mapping, devices, this.proxies); } /** * * @param {InputController} component * @param entity */ unlink(component, entity) { const mapping = component.mapping; const devices = this.devices; removeBindings(mapping, devices, this.proxies); component.on.unlinked.send1(entity); } } export default InputControllerSystem;