phaser3-rex-plugins
Version:
596 lines (485 loc) • 17 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.rexstatemanagerplugin = factory());
})(this, (function () { 'use strict';
var EventEmitterMethods = {
setEventEmitter(eventEmitter, EventEmitterClass) {
if (EventEmitterClass === undefined) {
EventEmitterClass = Phaser.Events.EventEmitter; // Use built-in EventEmitter class by default
}
this._privateEE = (eventEmitter === true) || (eventEmitter === undefined);
this._eventEmitter = (this._privateEE) ? (new EventEmitterClass()) : eventEmitter;
return this;
},
destroyEventEmitter() {
if (this._eventEmitter && this._privateEE) {
this._eventEmitter.shutdown();
}
return this;
},
getEventEmitter() {
return this._eventEmitter;
},
on() {
if (this._eventEmitter) {
this._eventEmitter.on.apply(this._eventEmitter, arguments);
}
return this;
},
once() {
if (this._eventEmitter) {
this._eventEmitter.once.apply(this._eventEmitter, arguments);
}
return this;
},
off() {
if (this._eventEmitter) {
this._eventEmitter.off.apply(this._eventEmitter, arguments);
}
return this;
},
emit(event) {
if (this._eventEmitter && event) {
this._eventEmitter.emit.apply(this._eventEmitter, arguments);
}
return this;
},
addListener() {
if (this._eventEmitter) {
this._eventEmitter.addListener.apply(this._eventEmitter, arguments);
}
return this;
},
removeListener() {
if (this._eventEmitter) {
this._eventEmitter.removeListener.apply(this._eventEmitter, arguments);
}
return this;
},
removeAllListeners() {
if (this._eventEmitter) {
this._eventEmitter.removeAllListeners.apply(this._eventEmitter, arguments);
}
return this;
},
listenerCount() {
if (this._eventEmitter) {
return this._eventEmitter.listenerCount.apply(this._eventEmitter, arguments);
}
return 0;
},
listeners() {
if (this._eventEmitter) {
return this._eventEmitter.listeners.apply(this._eventEmitter, arguments);
}
return [];
},
eventNames() {
if (this._eventEmitter) {
return this._eventEmitter.eventNames.apply(this._eventEmitter, arguments);
}
return [];
},
};
var GetValue = function (source, key, defaultValue, altSource) {
var isValidSource = source && (typeof source === 'object' || typeof source === 'function');
var isValidAltSource = altSource && (typeof altSource === 'object' || typeof altSource === 'function');
if (!isValidSource && !isValidAltSource) {
return defaultValue;
}
var keyPath = String(key);
// Shortcut:
// If obj[keyPath] can be read (including prototype chain), return it directly.
// This also supports literal keys like "a.b".
if (isValidSource && (keyPath in source)) {
return source[keyPath];
}
if (isValidAltSource && (keyPath in altSource)) {
return altSource[keyPath];
}
// If there is no dot, we already know it's missing.
if (keyPath.indexOf('.') === -1) {
return defaultValue;
}
var keys = keyPath.split('.');
// 1) Try source path first
if (isValidSource) {
var sourceResult = WalkPath(source, keys, defaultValue);
if (sourceResult.found) {
return sourceResult.value;
}
}
// 2) Then try altSource path
if (isValidAltSource) {
var altSourceResult = WalkPath(altSource, keys, defaultValue);
if (altSourceResult.found) {
return altSourceResult.value;
}
}
return defaultValue;
};
var WalkPath = function (source, keys, defaultValue) {
var parent = source;
var value = defaultValue;
var found;
for (var index = 0, cnt = keys.length; index < cnt; index++) {
var partKey = keys[index];
if (parent && (typeof parent === 'object' || typeof parent === 'function')) {
found = (partKey in parent);
} else {
found = false;
}
if (!found) {
WalkPathResult.found = false;
return WalkPathResult;
}
value = parent[partKey];
parent = value;
}
WalkPathResult.found = true;
WalkPathResult.value = value;
return WalkPathResult;
};
var WalkPathResult = {};
class StateManagerBase {
constructor(config) {
this._states = {};
this._stateLock = false;
this.enable = true;
this._start = undefined;
this._state = undefined;
this._prevState = undefined;
// Event emitter
var eventEmitter = GetValue(config, 'eventEmitter', undefined);
var EventEmitterClass = GetValue(config, 'EventEmitterClass', undefined);
this.setEventEmitter(eventEmitter, EventEmitterClass);
}
shutdown() {
this.destroyEventEmitter();
}
destroy() {
this.shutdown();
}
toJSON() {
return {
curState: this.state,
prevState: this.prevState,
enable: this.enable,
start: this._start
};
}
setEnable(e) {
if (e === undefined) {
e = true;
}
this.enable = e;
return this;
}
toggleEnable() {
this.setEnable(!this.enable);
return this;
}
getState(name) {
return this._states[name];
}
addState(name, state) {
if (typeof (name) !== 'string') {
state = name;
name = state.name;
}
this._states[name] = state;
return this;
}
addStates(states) {
if (Array.isArray(states)) {
for (var i = 0, cnt = states.length; i < cnt; i++) {
this.addState(states[i]);
}
} else {
for (var name in states) {
this.addState(name, states[name]);
}
}
return this;
}
removeState(name) {
if (this._states.hasOwnProperty(name)) {
delete this._states[name];
}
return this;
}
removeAllStates() {
for (var name in this._states) {
delete this._states[name];
}
return this;
}
set state(newState) {
if (!this.enable || this._stateLock) {
return;
}
if (this._state === newState) {
return;
}
this._prevState = this._state;
this._state = newState;
this._stateLock = true; // Lock state
this.emit('statechange', this);
if (this._prevState != null) {
var state = this.getState(this._prevState);
if (state && state.exit) {
state.exit(this);
}
this.emit(`exit_${this._prevState}`, this);
}
this._stateLock = false;
if (this._state != null) {
var state = this.getState(this._state);
if (state && state.enter) {
state.enter(this);
}
this.emit(`enter_${this._state}`, this);
}
}
get state() {
return this._state;
}
get prevState() {
return this._prevState;
}
get stateList() {
return Object.keys(this._states);
}
start(state) {
this._start = state;
this._prevState = undefined;
this._state = state; // Won't fire statechange events
return this;
}
goto(nextState) {
if (nextState != null) {
this.state = nextState;
}
return this;
}
next() {
var state = this.getState(this.state);
if (!state || !state.next) {
return this;
}
var nextState;
if (typeof (state.next) === 'string') {
nextState = state.next;
} else {
nextState = state.next(this);
}
this.goto(nextState);
return this;
}
runMethod(methodName, a1, a2, a3, a4, a5) {
var state = this.getState(this.state);
if (!state) {
return undefined;
}
var fn = state[methodName];
if (!fn) {
return undefined;
}
// Copy from eventemitter3
var len = arguments.length;
switch (len) {
case 1: return fn(this);
case 2: return fn(this, a1);
case 3: return fn(this, a1, a2);
case 4: return fn(this, a1, a2, a3);
case 5: return fn(this, a1, a2, a3, a4);
case 6: return fn(this, a1, a2, a3, a4, a5);
}
var args = Array.prototype.slice.call(arguments);
args[0] = this;
return fn.apply(undefined, args);
}
}
Object.assign(
StateManagerBase.prototype,
EventEmitterMethods
);
var HasListener = function (eventEmitter, eventName, fn, context, once) {
if (once === undefined) {
once = false;
}
var listeners = eventEmitter._events[eventName];
if (!listeners) {
return false;
}
for (var i = 0, cnt = listeners.length; i < cnt; i++) {
var listener = listeners[i];
if ((listener.fn === fn) &&
(listener.context === context) &&
(listener.once === once)
) {
return true;
}
}
return false;
};
class StateManager extends StateManagerBase {
constructor(config) {
super(config);
this._scene = GetValue(config, 'scene', undefined);
}
shutdown() {
this.stopUpdate();
this.stopPreUpdate();
this.stopPostUpdate();
this._scene = undefined;
super.shutdown();
}
getScene() {
return this._scene;
}
update(time, delta) {
this.runMethod('update', time, delta);
}
preupdate(time, delta) {
this.runMethod('preupdate', time, delta);
}
postupdate(time, delta) {
this.runMethod('postupdate', time, delta);
}
startUpdate(scene) {
if (!scene) {
scene = this._scene;
}
var eventEmitter = scene.sys.events;
if (HasListener(eventEmitter, 'update', this.update, this)) {
return this;
}
this._scene = scene;
eventEmitter.on('update', this.update, this);
return this;
}
stopUpdate() {
if (!this._scene) {
return this;
}
this._scene.sys.events.off('update', this.update, this);
return this;
}
startPreUpdate(scene) {
if (!scene) {
scene = this._scene;
}
var eventEmitter = scene.sys.events;
if (HasListener(eventEmitter, 'preupdate', this.preupdate, this)) {
return this;
}
this._scene = scene;
eventEmitter.on('preupdate', this.preupdate, this);
return this;
}
stopOreUpdate() {
if (!this._scene) {
return this;
}
this._scene.sys.events.off('preupdate', this.preupdate, this);
return this;
}
startPostUpdate(scene) {
if (!scene) {
scene = this._scene;
}
var eventEmitter = scene.sys.events;
if (HasListener(eventEmitter, 'postupdate', this.postupdate, this)) {
return this;
}
this._scene = scene;
eventEmitter.on('postupdate', this.postupdate, this);
return this;
}
stopPostUpdate() {
if (!this._scene) {
return this;
}
this._scene.sys.events.off('postupdate', this.postupdate, this);
return this;
}
}
var IsNil = function (value) {
return value === null || value === undefined;
};
var IsObjectLike = function (value) {
return value !== null && typeof value === 'object';
};
var NormalizePath = function (path, delimiter) {
if (Array.isArray(path)) ; else if (typeof path !== 'string') {
path = [];
} else if (path.trim() === '') {
path = [];
} else {
path = path.split(delimiter).filter(Boolean);
}
return path;
};
/**
* Set a nested value into target by path (mutates target).
*
* - If keys is a string and does NOT contain delimiter, write directly.
* - Intermediate non-plain-object values are always overwritten with {}.
*
* @param {object} target
* @param {string|string[]} keys
* @param {*} value
* @param {string} [delimiter='.']
* @returns {object} the same target reference
*/
var SetValue = function (target, keys, value, delimiter = '.') {
if (!IsObjectLike(target)) {
return target;
}
// Invalid key: no-op; don't replace root
if (IsNil(keys) || keys === '' || (Array.isArray(keys) && keys.length === 0)) {
return target;
}
// Fast path: single key
if (
(typeof keys === 'string' && keys.indexOf(delimiter) === -1) ||
(typeof keys === 'number')
) {
target[keys] = value;
return target;
}
var pathSegments = NormalizePath(keys, delimiter);
if (pathSegments.length === 0) {
return target;
}
var cursor = target;
var pathSegmentsCount = pathSegments.length;
for (var index = 0; index < pathSegmentsCount - 1; index++) {
var segment = pathSegments[index];
var next = cursor[segment];
if (!IsObjectLike(next)) {
// Force overwrite intermediates
cursor[segment] = {};
}
cursor = cursor[segment];
}
cursor[pathSegments[pathSegmentsCount - 1]] = value;
return target;
};
class StateManagerPlugin extends Phaser.Plugins.BasePlugin {
constructor(pluginManager) {
super(pluginManager);
}
start() {
var eventEmitter = this.game.events;
eventEmitter.on('destroy', this.destroy, this);
}
add(config) {
return new StateManager(config);
}
}
SetValue(window, 'RexPlugins.StateManager', StateManager);
return StateManagerPlugin;
}));