Source: ActionEngine.js

Source: ActionEngine.js

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;
}