Game

Copyright(c) 2012 Stefano Balietti MIT Licensed

Wrapper class for a GameLoop object and functions to control the game flow

Defines a number of event listeners, diveded in

  • incoming,
  • outgoing,
  • internal

  
(function (exports, node) {
  

Global scope

  
var GameState = node.GameState,
  GameMsg = node.GameMsg,
  GameDB = node.GameDB,
  PlayerList = node.PlayerList,
  Player = node.Player,
  GameLoop = node.GameLoop,
  JSUS = node.JSUS;

var action = node.action;

exports.Game = Game;

var name,
  description,
  gameLoop,
  pl,
  ml;
  

Game constructor

Creates a new instance of Game

Params
settings object Optional. A configuration object
function Game (settings) {
  settings = settings || {};

Private properties

Game.name

The name of the game

API
private
  name = settings.name || 'A nodeGame game';
  
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'name', {
      value: name,
      enumerable: true
    });
  }
  else {
    this.name = name;
  }

Game.description

A text describing the game

API
private
  description = settings.description || 'No Description';
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'description', {
      value: description,
      enumerable: true
    });
  }
  else {
    this.description = description;
  }

Game.gameLoop

An object containing the game logic

See
GameLoop
API
private
  gameLoop = new GameLoop(settings.loop || settings.loops);
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'gameLoop', {
      value: gameLoop,
      enumerable: true
    });
  }
  else {
    this.gameLoop = gameLoop;
  }
  

Game.pl

The list of players connected to the game

The list may be empty, depending on the server settings

API
private
  pl = new PlayerList();
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'pl', {
      value: pl,
      enumerable: true,
      configurable: true,
      writable: true
    });
  }
  else {
    this.pl = pl;
  }

Game.pl

The list of monitor clients connected to the game

The list may be empty, depending on the server settings

API
private
  ml = new PlayerList();
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'ml', {
      value: ml,
      enumerable: true,
      configurable: true,
      writable: true
    });
  }
  else {
    this.ml = ml;
  }
  

Game.ready

If TRUE, the nodeGame engine is fully loaded

Shortcut to game.isReady

If the browser does not support the method object setters, this property is disabled, and Game.isReady() should be used instead.

See
Game.isReady();
API
private
  if (node.support.getter) {
    Object.defineProperty(this, 'ready', {
      set: function(){},
      get: this.isReady,
      enumerable: true
    });
  }
  else {
    this.ready = null;
  }

Public properties

Game.observer

If TRUE, silently observes the game. Defaults FALSE

An nodeGame observer will not send any automatic notification to the server, but it will just observe the game played by other clients.

  this.observer = ('undefined' !== typeof settings.observer) ? settings.observer 
                                : false;

Game.auto_step

If TRUE, automatically advances to the next state if all the players have completed the same state

After a successful STATEDONE event is fired, the client will automatically goes to the next function in the game-loop without waiting for a STATE message from the server.

Depending on the configuration settings, it can still perform additional checkings (e.g.wheter the mininum number of players is connected) before stepping to the next state.

Defaults: true

  this.auto_step = ('undefined' !== typeof settings.auto_step) ? settings.auto_step 
                               : true;

Game.auto_wait

If TRUE, fires a WAITING... event immediately after a successful DONE event

Under default settings, the WAITING... event temporarily prevents the user to access the screen and displays a message to the player.

Defaults: FALSE

  this.auto_wait = ('undefined' !== typeof settings.auto_wait) ? settings.auto_wait 
                                 : false; 

Game.solo_mode

If TRUE, automatically advances to the next state upon completion of a state

After a successful DONE event is fired, the client will automatically goes to the next function in the game-loop without waiting for a STATE message from the server, or checking the STATE of the other players.

Defaults: FALSE

  this.solo_mode = ('undefined' !== typeof settings.solo_mode) ? settings.solo_mode 
                               : false; 

TODO: check this

  this.minPlayers = settings.minPlayers || 1;
  this.maxPlayers = settings.maxPlayers || 1000;
  
  if (settings.init) {
    this.init = settings.init;
  }

Game.memory

A storage database for the game

In the server logic the content of SET messages are automatically inserted in this object

@see node.set

  this.memory = new GameDB();
  
  this.player = null; 
  this.state = new GameState();

} // <!-- ends constructor -->

Game methods

Game.init

Initialization function

This function is called as soon as the game is instantiated, i.e. at state 0.0.0. All event listeners declared here will stay valid throughout the game.

Game.prototype.init = function () {};

Game.start

Starts the game

Calls the init function, sets the state as node.is.LOADED and notifies the server.

Important: it does not use Game.publishState because that is just for change of state after the game has started

See
node.play
See
Game.publishState
Game.prototype.start = function() {

INIT the game

  this.init();
  this.updateState(new GameState("1.1.1"));
  

this.state.is = node.is.LOADED; node.socket.sendSTATE(node.action.SAY, node.game.state);

  
  node.log('game started');
};

Game.pause

Experimental. Sets the game to pause

Game.prototype.pause = function () {
  this.state.paused = true;
};

Game.resume

Experimental. Resumes the game from a pause

Game.prototype.resume = function () {
  this.state.paused = false;
};

Game.next

Fetches a state from the game-loop N steps ahead

Optionally, a parameter can control the number of steps to take in the game-loop before returning the state

Params
N number Optional. The number of steps to take in the game-loop. Defaults 1
Returns
boolean GameState The next state, or FALSE if it does not exist @see GameState @see Game.gameLoop
Game.prototype.next = function (N) {
  if (!N) return this.gameLoop.next(this.state);
  return this.gameLoop.jumpTo(this.state, Math.abs(N));
};

Game.previous

Fetches a state from the game-loop N steps back

Optionally, a parameter can control the number of steps to take backward in the game-loop before returning the state

Params
times number Optional. The number of steps to take in the game-loop. Defaults 1
Returns
boolean GameState The previous state, or FALSE if it does not exist @see GameState @see Game.gameLoop
Game.prototype.previous = function (N) {
  if (!N) return this.gameLoop.previous(this.state);
  return this.gameLoop.jumpTo(this.state, -Math.abs(N));
};

Game.jumpTo

Moves the game forward or backward in the game-loop

Optionally, a parameter can control the number of steps to take in the game-loop before executing the next function. A negative value jumps backward in the game-loop, and a positive one jumps forward in the game-loop

Params
jump number The number of steps to take in the game-loop
Returns
boolean TRUE, if the game succesfully jumped to the desired state @see GameState @see Game.gameLoop
Game.prototype.jumpTo = function (jump) {
  if (!jump) return false;
  var gs = this.gameLoop.jumpTo(this.state, jump);
  if (!gs) return false;
  return this.updateState(gs);
};

Game.publishState

Notifies internal listeners, the server and other connected clients of the current game-state

If the observer flag is set, external notification is inhibited, but the STATECHANGE event is emitted anyway

See
GameState
See
Game.observer
Game.prototype.publishState = function() {
  if (!this.observer) {
    var stateEvent = node.OUT + action.SAY + '.STATE'; 
    node.emit(stateEvent, this.state, 'ALL');
  }
  
  node.emit('STATECHANGE');
  
  node.log('New State = ' + new GameState(this.state), 'DEBUG');
};

Game.updateState

Updates the game to the specified game-state

Params
state GameState The state to load and run
Game.prototype.updateState = function (state) {
  
  console.log('New state is going to be ' + new GameState(state), 'DEBUG');
  
  if (this.step(state) !== false) {
    this.paused = false;
    this.state.is =  node.is.LOADED;
    if (this.isReady()) {
      node.emit('LOADED');
    }
  }   
  else {
    console.log('Error in stepping', 'ERR');

TODO: implement sendERR

    node.emit('TXT','State was not updated');
  }
};

Game.step

Retrieves from the game-loop and executes the function for the specified game-state

Params
gameState GameState Optional. The GameState to run
Returns
Boolean FALSE, if the execution encountered an error @see Game.gameLoop @see GameState
Game.prototype.step = function (gameState) {
  
  gameState = gameState || this.next();
  if (gameState) {
    
    var func = this.gameLoop.getFunction(gameState);
    
    console.log('func found');
    console.log(func.toString());
    

Experimental: node.window should load the func as well if (node.window) { var frame = this.gameLoop.getAllParams(gameState).frame; node.window.loadFrame(frame); }

    
    
    
    if (func) {

Local Listeners from previous state are erased before proceeding to next one

      node.events.clearState(this.state);
      
      gameState.is = node.is.LOADING;
      this.state = gameState;
    

This could speed up the loading in other client, but now causes problems of multiple update

      this.publishState();
          
      return func.call(node.game);
    }
  }
  return false;
};

Game.isReady

Returns TRUE if the nodeGame engine is fully loaded

As soon as the nodegame-client library is loaded node.game.state is equal to 0.0.0. In this situation the game will be considered READY unless the nodegame-window says otherwise

During stepping between functions in the game-loop the flag is temporarily turned to FALSE, and all events are queued and fired only after nodeGame is ready to handle them again.

If the browser does not support the method object setters, this property is disabled, and Game.isReady() should be used instead.

See
Game.ready;
Game.prototype.isReady = function() {
  if (this.state.state !== 0 && this.state.is < node.is.LOADED) return false;
  

Check if there is a gameWindow obj and whether it is loading

  if (node.window) {  
    return (node.window.state >= node.is.LOADED) ? true : false;
  }
  return true;
};

Closure

})(
  'undefined' != typeof node ? node : module.exports
  , 'undefined' != typeof node ? node : module.parent.exports
);