@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
252 lines (211 loc) • 6.76 kB
JavaScript
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;