/*
 * gTile.js
 *
 * Copyright (c) 2008 Tyler de Witt (tyler-dewitt.com)
 * Unauthorized reproduction or use of this code is not permitted.
 */

/* ------------------------------
   gTile is the base class for all game objects
   Functions to implement minimal behaviours
   Object game objects derive from gTile and over ride behavior functions
   
   Functions for serializing the object.
   -------------------------------*/

function gTile() {
  this.__proto__= Object.prototype;


  /* Note: this is required to work in IE */
  this.__proto__=gTile.prototype;

  /* This code gets executed when gTile(sub_type) is called
     as a constructor
     This will only ever be called from gGame.create_object

     Normally, we call g.create_object('gTile',sub_type);
     See gGame.create_object for details
  */

  if (arguments.length > 0) {
    this.load_proto(arguments[0]);
  } 
}

/* gTile class functions */
gTile.prototype.zIndex=10;
gTile.prototype.x=-1;
gTile.prototype.y=-1;
gTile.prototype.id=0;
gTile.prototype.object_type="gTile";
gTile.prototype.sub_type=null;
gTile.prototype.title=null;
gTile.prototype.onMap=false;
gTile.prototype.displayTile=true;
gTile.prototype.image='none.jpg';
gTile.prototype.tile_height=1;
gTile.prototype.tile_width=1;

gTile.prototype.walkable=true;
gTile.prototype.test_walk=true;

gTile.prototype.screenX=-1;
gTile.prototype.screenY=-1;
gTile.prototype.onScreen=false;

/* Behavior for what happens when character attempts to walk into this object
   returns: true if character can move here, false otherwise */
gTile.prototype.walk=function(who) {
  return(this.walkable);
}
    
gTile.prototype.move=function(xoffset,yoffset) {
  if(!this.onMap) return false;
    
  return g.gm.moveTile(this,xoffset,yoffset);
}


/* Recursive function for determining if a property is savable.  Checks
   the prototype.savedVars array of each class. */

gTile.prototype.isSavedVar=function(s) {
   var x=this;

    while (x.__proto__ != null ) {
      if (!(typeof x.__proto__.savedVars == "undefined")) {
	for (var i in x.__proto__.savedVars) {
	//window.console.log(x.__proto__.savedVars[i]);

	  if (s==x.__proto__.savedVars[i])
	    return true;
	}
      }
      if(typeof x.object_type != null && x.object_type=="gTile") break;
      x=x.__proto__;
    }
    return false;
}

/* Returns an object suitable for serialization.
   savedVars is a class variable array specifying which
   data is necessary to persist.  
   If derived classes override this function, they must
   call the parent gTile.save explicitely, prior to saving
   additional variables. 

   returns: object, the object to serialize */

gTile.prototype.save=function() {
  var o=new Array();
  /* For gTile base class */
  for (i in gTile.prototype.savedVars) { 
    var x=gTile.prototype.savedVars[i];
    if ((x=="image" /* || x=="descrip" || x=="photo" */) && this.same_as_proto(x)) {
      o.push("");
    } else {
      o.push(this[gTile.prototype.savedVars[i]]); 
    }
  }
  return o;
}

gTile.prototype.save_default=function(class_name) {
  /* For all other derived classes */
  var p=eval(class_name).prototype;
  o=p.save.call(this);
  for (i in p.savedVars) {o.push(this[p.savedVars[i]]); }
  return o;
}

/* Loads an object from the stored data persisted by gTile.save 
   If this class has a sub type, load those default variables before
   loading the specific data that was saved.
   Sub types allow for smaller save files
   o: object, the object to load from.
*/

gTile.prototype.load=function(o) {
  /* For gTile base class */
  //Load the proto, then the saved variables
  if (typeof o[1] == "string") this.load_proto(o[1]);
  for (i in gTile.prototype.savedVars) { 
    var val=o.shift();
    if(gTile.prototype.savedVars[i]=="image" && val=="") continue;
    /*
    if(gTile.prototype.savedVars[i]=="descrip" && val=="") continue;
    if(gTile.prototype.savedVars[i]=="photo" && val=="") continue;
    */
    this[gTile.prototype.savedVars[i]] = val;
  }
}

gTile.prototype.load_default=function(o,class_name) {
  /* For all other derived classes */


  var p=eval(class_name).prototype;
  p.load.call(this,o);
  for (i in p.savedVars) {this[p.savedVars[i]] = o.shift(); }
}


/* To dynamically determine if an object inheriting from gTile implements an interface or property */

gTile.prototype.has_func=function(f) {  return (typeof eval("this." + f) == "function"); }
gTile.prototype.has_prop=function(f) {  return (typeof eval("this." + f) != "undefined" && typeof eval("this." + f) != "function"); }
gTile.prototype.same_as_proto=function(f) {
  if (this.sub_type==null) return false;

  var p=eval(this.object_type + "_protos." + this.sub_type);
  if (typeof p == "undefined") { return false;}
  return (this[f]==p[f]);
}

/* gTile_protos is an array of sub types for the gTile class
   load_proto sets up an object as a sub type after it has been
   newly instantiated.

   s: string, object sub_type, is an index into gTile_protos array 
   return: true on sucess, false otherwise */

gTile.prototype.load_proto=function(s) {
  if (typeof eval(this.object_type + "_protos") == "undefined")
  return false;
  
  var plist=eval(this.object_type + "_protos");
  if (plist[s] == null)
  return false;

  this.sub_type=s;
  for (i in plist[s]) {
    //implement one level cloning TODO this should be recursive
    if (typeof plist[s][i]=="object") {
      if (plist[s][i].constructor==Array)
	this[i]=new Array();
      if (plist[s][i].constructor==Object)
	this[i]=new Object();

      for (var k in plist[s][i]) { this[i][k]=plist[s][i][k]; }
    } else {
      this[i]=plist[s][i];
    }
  }
  if (this.title==null) this.title=this.sub_type;
  return true;
}


/* The sub type definitions for gTile
   Sub types keep the class hiearchy manageable, and also minimize the amount
   of data that needs to be serialized.
   Drawback: we can't serialize for example the 'image' or 'walkable' variables.
   Instead, a sub class will need to be derived in this case to over ride gTile.save */
gTile_protos = {
  trap_floor: {  title: 'floor', image: 'floor.jpg', walk: function(who) { if (!g.gs.vars.trapped) { g.gs.vars.trapped=1; msg("You're trapped!"); } else { g.gs.vars.trapped=false; msg("You're safe"); }  return true;} },
  floor: {  descrip: 'cold stone floor', image: 'floor.jpg' },
  floor2: {  descrip: 'bricked stone floor', image: 'floor2.jpg' },
  floor4: {  descrip: 'moldy stone floor', image: 'floor4.jpg' },
  grass: {  image: 'grass.jpg' },
  grass2: {  title: 'grass', image: 'grass2.jpg' },
  path: {  image: 'path.jpg' },
  trees: {  zIndex: 20, image: 'trees.jpg', walkable: false},
  wall: { descrip: 'a cold stone wall', image: 'wall.jpg', zIndex: 20, walkable: false, test_walk: false },
  walltorch: { descrip: 'a torch lit wall', image: 'walltorch.jpg', zIndex: 20, walkable: false, test_walk: false },
  closet: { image: 'gloset.jpg', zIndex: 20, walkable: false },
  gblock: { title: 'stone wall', image: 't_8_13.jpg', zIndex: 20, walkable: false, test_walk: false },
  bed: { image: 'bed.jpg', zIndex: 20, walkable: false },
  locked: { descrip: 'a locked door', title: 'door (locked)', image: 'door2.jpg', zIndex: 20, walkable: false },
  fireplace: { image: 'fireplace.jpg', look_str: "You are mesmerized by the dancing flames", zIndex: 20, walkable: false }
};

/* List of variables that get saved when we call gTile.save 
   derived classes will not have to save these variables, but will need
   to call their parent gTile.save before saving their own data */
//gTile.prototype.savedVars= {object_type:0, id:1, sub_type:2, onMap:3, displayTile:4, x: 5, y: 6};
gTile.prototype.savedVars= ["object_type","sub_type","id","onMap","displayTile","x","y","image","tile_height","tile_width"];

