var Engine = require('./Engine');
/**
* Represents an action based game engine.
* It's like a normal ECS with Action.
* @constructor
* @extends Engine
* @param {Boolean} [isServer=false] - Whether if it's a server or not
* @see Action
*/
function ActionEngine(isServer) {
Engine.call(this);
/**
* A boolean contains whether if it's a server or not.
* @var {Boolean}
*/
this.isServer = isServer || false;
this._actions = {};
this._actionQueue = [];
}
ActionEngine.prototype = Object.create(Engine.prototype);
ActionEngine.prototype.constructor = ActionEngine;
/*
- Define Action in Engine
engine.a('add', action)
- Create Action
engine.a('add', player, entity, options)
- Run Action
engine.a(action)
*/
ActionEngine.prototype.a = function(name, entity, player, options) {
if(typeof name !== 'string') {
return this.runAction(name);
}
if(arguments.length == 2 && typeof entity == 'function') {
return this.defineAction(name, entity);
}
return this.createAction(name, entity, player, options);
}
ActionEngine.prototype.aa = function(name, entity, player, options) {
return this.runAction(this.createAction(name, entity, player, options));
}
/**
* Defines an {@link Action} type to the Engine.
* @param name {String} - The name of the Action.
* @param constructor {Function} - The constructor of the Action.
*/
ActionEngine.prototype.defineAction = function(name, constructor) {
this._actions[name] = constructor;
// TODO This isn't good way to do this, should be changed
constructor.prototype.name = name;
}
/**
* Returns the {@link Action}'s construtor with specified name.
* @param name {String} - The name of the Action.
* @returns {Function} The constructor of the Action.
*/
ActionEngine.prototype.getActionConstructor = function(name) {
return this._actions[name];
}
/**
* Creates a new {@link Action} using registered constructor.
* @param name {String} - The name of the Action.
* @param {Entity} entity - The entity associated with the action.
* @param {Entity} player - The player who requested the action.
* @param {Object} options - The arguments associated with the action.
* @returns {Action} The created Action.
*/
ActionEngine.prototype.createAction = function(name, entity, player, options) {
return new (this.getActionConstructor(name))(this, entity, player, options);
}
ActionEngine.prototype.getTurn = function() {
return null;
}
/**
* Runs the Action.
* Action shouldn't have run yet if {@link ActionEngine#isServer} is true,
* but it should have run on server and have {@link Action#result} if not.
* @param action {Action} - The Action to run.
* @fires ActionEngine#action
* @fires ActionEngine#preAction
*/
ActionEngine.prototype.runAction = function(action) {
var turn = this.getTurn();
if(this.isServer) {
if(action.result) {
throw new Error('Action has already run');
}
} else {
if(!action.result) {
var handled = false;
this.systems.forEach(function(system) {
if(system.sendAction) {
if(system.sendAction(turn, action, this)) handled = true;
}
}, this);
if(!handled) throw new Error('Action hasn\'t run on server yet');
return;
}
}
var firstCall = this._actionQueue.length == 0;
/**
* This event is fired before the action executes.
* @event ActionEngine#preAction
* @property {Action} 1 - The Action object.
*/
this.emit('preAction', turn, action, this);
this.systems.forEach(function(system) {
if(system.preAction) {
system.preAction(turn, action, this);
}
}, this);
if(turn) {
turn.addAction(action);
}
this._actionQueue.push([turn, action, this]);
try {
action.run(this);
if(action.result == null) {
throw new Error('Action should set result after running');
}
} finally {
while(firstCall && this._actionQueue.length > 0) {
var obj = this._actionQueue.shift();
/**
* This event is fired when the action executes.
* @event TurnEngine#action
* @property {Turn} 0 - The current Turn.
* @property {Action} 1 - The Action object.
*/
this.emit('action', obj[0], obj[1], obj[2]);
this.systems.forEach(function(system) {
if(system.action) {
system.action(obj[0], obj[1], obj[2]);
}
}, this);
}
}
return action;
}
if(typeof module !== 'undefined') {
module.exports = ActionEngine;
}