Lunar Passing C++ Objects to Lua

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
dejai
Chaos Rift Junior
Chaos Rift Junior
Posts: 207
Joined: Fri Apr 11, 2008 8:44 pm

Lunar Passing C++ Objects to Lua

Post by dejai »

Lunar is a low level way to access lua whilst not being too much of a hassle it pretty much allows you to pass objects directly to lua have them modified and returned. This is really neat because you don't have to mess around with luabind or anything like that and it lets you specify your own lua api in a fairly straightforward way. Also it is just a single header file that requires liblua5.1.4 to run and that is it so it won't bother you that much on the dependency front. (Find Lunar Here: http://lua-users.org/wiki/CppBindingWithLunar ) Now for some examples of it in use.

Object.h this is our "Game Object" file for the NTEngine and allows us to create generic objects. These objects can be invisible and act as game triggers or they can be AI opponents. Their "Behaviour" is scripted in lua and their "Appearance" is written in XML (this format is fine for indie games especially when you have not standardized your file format it does not work so well on very large scale games due to its verbosity.)

Code: Select all

#ifndef OBJECT_H
#define OBJECT_H

#include <string>

#include "boost/function/function1.hpp"

#include "AnimSprite.h"
#include "InputHandler.h"
#include "Key.h"
#include "Lunar.h"

class State;

/*! \class Object
 * -Inherits from sf::Sprite\n
 * -Animates a spritesheet when given AnimData\n
*/
class Object : public AnimSprite {
 public:
  enum Dir { Up, Down, Left, Right };

  /// First constructor for registering to Lunar, second for loading in Objects
  Object( lua_State *L );
  Object( const std::string &filepath, int tileX, int tileY, int strip );
  ~Object();

  /// Handle events generated for Object
  void HandleEvents();

  /// Updates the Object's collision
  void UpdateCollision( Object *collisionObj );

  /// Updates the Object's movement
  void UpdateAI();

  /// Updates the Object's rendering
  void UpdateRendering();

  /// Returns collision box for  object
  const sf::FloatRect& GetCollisionBox() const;

  /// Returns whether Object blocks tile it is on,
  /// preventing other Objects from getting on it
  bool BlockingTile() const;

  /// Returns x-location of Object on  grid
  int GetTileX() const;

  /// Returns y-location of Object on  grid
  int GetTileY() const;

  /// Returns type of Object
  const std::string& GetType() const;

  /************************************************
  Lua Functions
  ************************************************/
	/// Allows user to tell Object to move from Lua.
	/// Returns true if Object can move in the direction it is facing,
	/// or is in motion. False if it can't move in the direction it is facing.
  int LuaMove( lua_State *L );

  /// Returns current animation ID
  int LuaGetAnimation( lua_State *L );

	/// Sets animation on sheet corresponding to index top to bottom
  int LuaSetAnimation( lua_State *L );

  /// Same as above, but animation is played in reverse
  int LuaSetAnimationReverse( lua_State *L );

	/// Returns whether Object is animating
  int LuaIsAnimating( lua_State *L );

  /// Returns whether Object is moving
  int LuaMoving( lua_State *L );

	/// Allows Lua to access type of Object
  int LuaGetType( lua_State *L );

  /// Wrap GetTileX and GetTileY to allow it to be exposed to Lua
  int LuaGetTile( lua_State *L );

  /// Takes boolean. True means Object should block tile it is on,
  /// false means it shouldn't.
  int LuaBlockTile( lua_State *L );

  /// Allows user to get and set the Object's direction from Lua
  int LuaGetDir( lua_State *L );
  int LuaSetDir( lua_State *L );

  /// Allows Lua to reverse direction of Object
  int LuaReverse( lua_State *L );

  /// Allows user to get the Object's table (to change variables in it, etc.)
  int LuaGetTable( lua_State *L );

  /// Sets the Object's `noClip` to true or false
  int LuaSetNoClip( lua_State *L );

  /// Sets the starting time to the current system time
  int LuaResetTimer( lua_State *L );

  /// Returns the elapsed time since timer was last reset in seconds
  /// Has floating point precision
  int LuaGetElapsedTime( lua_State *L );

  /// Returns speed of Object
  int LuaGetSpeed( lua_State *L );

  /// Sets speed to speed passed (float)
  int LuaSetSpeed( lua_State *L );

  /// Reduces speed by float given. Speed can't go below zero.
  int LuaSlowDown( lua_State *L );

  /// Increases speed by float given. Speed set to zero if greater than numeric
  /// limit.
  int LuaSpeedUp( lua_State *L );


  /// Necessities for Lunar
  static const char className[];
  static Lunar<Object>::RegType methods[];

 private:
  typedef std::pair< Key, std::string > KeyEntry;

  /// Restricts copy constructor and assignment.
  Object( const Object &object );
  Object& operator=( const Object &object );

	/// Loads Object given a path to an xml file,
  /// returning true if loading was successful
  bool LoadObjectData( const std::string &filepath );

	/// Loads Object's collision data given path to xml file,
  /// returning true if loading was successful
  bool LoadCollisionData( const std::string &filepath );

	/// Initializes the lua script
  void InitLua();

	///Updates the movement of Object
  void MovementUpdate();

  /// Corrects movement of object when it exceeds next grid location
  void CorrectMovement();

  /// Calls lua function named 'funcName' that is in this Object's script
  void CallScriptFunc( std::string &funcName );

  /// Function pointer registered to CallLuaFunc for use with InputHandler
  const boost::function1<void, std::string&> m_ptrCallScriptFunc;

  bool m_moving; // If true; keep moving in m_direction
  bool m_blockingTile; // True if object blocks other objects from accessing tile it is on
  bool m_noClip; // When true, allows object to pass through solid objects and tiles
  Dir m_direction; // Current direction  object is moving
  float m_distance; // Distance traveled from last grid location
  float m_speed; // m_speed at which object moves
  InputHandler m_input; // Handles input for this Object
  int m_id; // ID of object
  sf::Clock m_timer; // Timer that Object can use from its script
  sf::FloatRect m_collisionRect; // Object's collision box
  static State *m_state; // State that Object is in
  std::string m_luaScript; // Filepath to the lua script
  std::string m_type; // What type of  object (slime, kickle, etc.)
};

#endif
So from above what is important to note is:
static const char className[];
static Lunar<Object>::RegType methods[];
These are used to specify the API to lua you pretty much give a name "Move" and a function LuaMove. You will see how this works in the cpp file
It is also important to note we have a lot of methods with Lua prefixes these are going to be our interface between Lua and C++.

Code: Select all

#include "Object.h"

#include "boost/bind/bind.hpp"
extern "C" {
#include "lualib.h"
}

#include "App.h"
#include "State.h"
#include "TileManager.h"
#include "tinyxml.h"

/************************************************
Constant Members
************************************************/
State* Object::m_state = NULL;

/************************************************
Public Members
************************************************/
const char Object::className[] = "Object";
Lunar<Object>::RegType Object::methods[] = {
  { "Move", &Object::LuaMove },
  { "GetAnimation", &Object::LuaGetAnimation },
  { "SetAnimation", &Object::LuaSetAnimation },
  { "SetAnimationReverse", &Object::LuaSetAnimationReverse },
  { "IsAnimating", &Object::LuaIsAnimating },
  { "IsMoving", &Object::LuaMoving },
  { "GetType", &Object::LuaGetType },
  { "GetTile", &Object::LuaGetTile },
  { "BlockTile", &Object::LuaBlockTile },
  { "GetDir", &Object::LuaGetDir },
  { "SetDir", &Object::LuaSetDir },
  { "Reverse", &Object::LuaReverse },
  { "GetTable", &Object::LuaGetTable },
  { "SetNoClip", &Object::LuaSetNoClip },
  { "ResetTimer", &Object::LuaResetTimer },
  { "GetElapsedTime", &Object::LuaGetElapsedTime },
  { "GetSpeed", &Object::LuaGetSpeed },
  { "SetSpeed", &Object::LuaSetSpeed },
  { "SlowDown", &Object::LuaSlowDown },
  { "SpeedUp", &Object::LuaSpeedUp },
  { NULL, NULL }
};

/************************************************
 * Constructor and Destructor
************************************************/
Object::Object( lua_State *L )
 : m_ptrCallScriptFunc( boost::bind( &Object::CallScriptFunc, this, _1 )),
   m_moving( false ),
   m_blockingTile( false ),
   m_noClip( false ),
   m_direction( Up ),
   m_distance( 0.0f ),
   m_speed( 0.0f ),
   m_id( -1 ) {
  m_state = App::GetApp()->GetCurrentState();

  if( !lua_isstring( L, -1 ) ) {
    luaL_error( L, "Invalid argument for Object." );
    LogErr( "State not passed to Object." );
  }

  std::string filepath( lua_tostring( L, -1 ) );
  if( !( LoadObjectData( filepath ) &&
         LoadCollisionData( filepath ) ) ) {
    LogErr( "Object XML file " + filepath + " didn't load correctly." );
  }

  InitLua();
}


Object::Object(
  const std::string &filepath,
  int tileX,
  int tileY,
  int strip
)
 : m_ptrCallScriptFunc( boost::bind( &Object::CallScriptFunc, this, _1 )),
   m_moving( false ),
   m_blockingTile( false ),
   m_noClip( false ),
   m_direction( Up ),
   m_distance( 0.0f ),
   m_speed( 0.0f ),
   m_id( -1 ) {
  m_state = App::GetApp()->GetCurrentState();

  if( !LoadObjectData( filepath ) ) {
    LogErr( "Object XML file " + filepath + " didn't load correctly." );
  }

  //Calculate the float positions given tileX and tileY
  //Taking into account tile size, and max tiles across/down
  int tileDim = m_state->GetTileManager().GetTileDim();

  float x = static_cast<float>( tileDim *
     ( tileX % m_state->GetTileManager().GetMapWidth()));

  float y = static_cast<float>( tileDim *
     ( tileY % m_state->GetTileManager().GetMapHeight()));

  if( GetAnimData() ) {
    //Take into account the sprites that are taller than a normal tile
    y -= GetAnimData()->GetFrameHeight( GetAnimation() ) % tileDim;
  }
  SetPosition( x, y );
  SetAnimation( strip );

  if( !LoadCollisionData( filepath ) ) {
    LogErr( "Object XML file " + filepath + " didn't load correctly." );
  }

  InitLua();
}


Object::~Object() {
  lua_State *L = App::GetApp()->GetLuaState();
  if ( L ) {
    luaL_unref( L, LUA_REGISTRYINDEX, m_id );
  }
}


void Object::HandleEvents() {
  if ( !m_moving ) {
    m_input.ScanInput( m_ptrCallScriptFunc );
  }
}


void Object::UpdateCollision( Object *collisionObj ) {
  if( collisionObj ) {
    lua_State *L = App::GetApp()->GetLuaState();
    lua_rawgeti( L, LUA_REGISTRYINDEX, m_id );
    lua_getfield( L, -1, "HandleCollision" );
    if ( lua_isfunction( L, -1 ) ) {
      Lunar<Object>::push( L, this );
      Lunar<Object>::push( L, collisionObj );
      lua_call( L, 2, 0 );
    }
    lua_settop( L, 0 );
  }
}


void Object::UpdateAI() {
  if( m_moving ) {
    MovementUpdate();
  } else {
    lua_State *L = App::GetApp()->GetLuaState();
    lua_rawgeti( L, LUA_REGISTRYINDEX, m_id );
    lua_getfield( L, -1, "AI" );
    if ( lua_isfunction( L, -1 )) {
      Lunar<Object>::push( L, this );
      lua_call( L, 1, 0 );
    }
    lua_settop( L, 0 );
  }
}


void Object::UpdateRendering() {
  AnimSprite::Update();
}


const sf::FloatRect &Object::GetCollisionBox() const {
  return m_collisionRect;
}


bool Object::BlockingTile() const {
  return m_blockingTile;
}


int Object::GetTileX() const {
  int tileDim = m_state->GetTileManager().GetTileDim();
  return static_cast<int>(
    ( GetPosition().x + tileDim / 2 ) / tileDim );
}


int Object::GetTileY() const {
  int tileDim = m_state->GetTileManager().GetTileDim();
  return static_cast<int>(
    (( GetPosition().y +
       GetSubRect().GetHeight() % tileDim ) +
       tileDim / 2) / tileDim );
}


const std::string& Object::GetType() const {
  return m_type;
}


int Object::LuaMove( lua_State *L ) {
  if( !m_moving ) {
    int nextTileX = GetTileX();
    int nextTileY = GetTileY();

    switch ( m_direction ) {
      case Up: {
        nextTileY--;
        break;
      }
      case Down: {
        nextTileY++;
        break;
      }
      case Left: {
        nextTileX--;
        break;
      }
      case Right: {
        nextTileX++;
        break;
      }
      default: {}
    }

    if ( nextTileX >= 0 && nextTileY >= 0 ) {
      int x = nextTileX;
      int y = nextTileY;
      if (( m_noClip ) ||
        ( m_state->GetTileManager().TileIsCrossable( x, y ) &&
        !m_state->GetObjectManager().ObjectBlockingTile( x, y ))) {
        m_moving = true;
      }
    }
    lua_pushboolean( L, m_moving );
    return 1;
  }
  lua_pushboolean( L, true );
  return 1;
}


int Object::LuaGetAnimation( lua_State *L ) {
  lua_pushinteger( L, GetAnimation());
  return 1;
}


int Object::LuaSetAnimation( lua_State *L ) {
  if( !lua_isnumber( L, -1 )) {
    LogLuaErr( "Didn't pass number to SetAnimation in Object: " + m_type );
    return luaL_error( L, "Didn't pass number to SetAnimation" );
  }
  int animation = lua_tointeger( L, -1 );
  SetAnimation( animation );
  return 0;
}


int Object::LuaSetAnimationReverse( lua_State *L ) {
  if ( !lua_isnumber( L, -1 )) {
    LogLuaErr(
      "Didn't pass number to SetAnimationReverse in Object: " + m_type
    );
    return luaL_error( L, "Didn't pass number to SetAnimationReverse" );
  }
  int animation = lua_tointeger( L, -1 );
  SetAnimation( animation, true );
  return 0;
}


int Object::LuaIsAnimating( lua_State *L ) {
  lua_pushboolean( L, IsAnimating() );
  return 1;
}


int Object::LuaMoving( lua_State *L ) {
  lua_pushboolean( L, m_moving );
  return 1;
}


int Object::LuaGetType( lua_State *L ) {
  lua_pushstring( L, m_type.c_str() );
  return 1;
}


int Object::LuaGetTile( lua_State *L ) {
  lua_pushinteger( L, GetTileX() );
  lua_pushinteger( L, GetTileY() );
  return 2;
}


int Object::LuaBlockTile( lua_State *L ) {
  m_blockingTile = lua_toboolean( L, -1 );
  return 0;
}


int Object::LuaGetDir( lua_State *L ) {
  lua_pushinteger( L, m_direction );
  return 1;
}


int Object::LuaSetDir( lua_State *L ) {
  if( !lua_isnumber( L, -1 ) ) {
    LogLuaErr( "Didn't pass number to SetDir in Object: " + m_type );
    return luaL_error( L, "Didn't pass number to SetDir" );
  }
  m_direction = static_cast<Dir>( lua_tointeger( L, -1 ) );
  return 0;
}


int Object::LuaReverse( lua_State *L ) {
  m_distance = m_state->GetTileManager().GetTileDim() - m_distance;

  switch( m_direction ) {
    case Up: {
      m_direction = Down;
      break;
    }
    case Down: {
      m_direction = Up;
      break;
    }
    case Left: {
      m_direction = Right;
      break;
    }
    case Right: {
      m_direction = Left;
      break;
    }
    default: {
      LogLuaErr( "In Reverse, no direction for Object: " + m_type );
      return luaL_error( L, "In Reverse, no direction for Object" );
    }
  }

  m_moving = true;
  return 0;
}


int Object::LuaGetTable( lua_State *L ) {
  lua_rawgeti( L, LUA_REGISTRYINDEX, m_id );
  return 1;
}


int Object::LuaSetNoClip( lua_State *L ) {
  m_noClip = lua_toboolean( L, -1 );
  return 0;
}


int Object::LuaResetTimer( lua_State *L ) {
  m_timer.Reset();
  return 0;
}


int Object::LuaGetElapsedTime( lua_State *L ) {
  lua_pushnumber( L, m_timer.GetElapsedTime() );
  return 1;
}


int Object::LuaGetSpeed( lua_State *L ) {
  lua_pushnumber( L, m_speed );
  return 1;
}


int Object::LuaSetSpeed( lua_State *L ) {
  if ( !lua_isnumber( L, -1 )) {
    LogLuaErr( "Number not passed to SetSpeed for Object: " + m_type );
    return luaL_error( L, "Number not passed to SetSpeed for Object" );
  }
  m_speed = lua_tonumber( L, -1 );
  return 0;
}


int Object::LuaSlowDown( lua_State *L ) {
  if ( !lua_isnumber( L, -1 )) {
    LogLuaErr( "Number not passed to SlowDown for Object: " + m_type );
    return luaL_error( L, "Number not passed to SlowDown for Object" );
  }
  m_speed -= lua_tonumber( L, -1 );

  if ( m_speed < 0.f ) {
    m_speed = 0.f;
  }
  return 0;
}


int Object::LuaSpeedUp( lua_State *L ) {
  if ( !lua_isnumber( L, -1 )) {
    LogLuaErr( "Number not passed to SpeedUp for Object: " + m_type );
    return luaL_error( L, "Number not passed to SpeedUp for Object" );
  }
  m_speed += lua_tonumber( L, -1 );
  return 0;
}

/************************************************
Private Methods
************************************************/
void Object::InitLua() {
  lua_State *L = App::GetApp()->GetLuaState();
  luaL_dofile( L, m_luaScript.c_str() );
  if ( lua_istable( L, -1 )) {
    m_id = luaL_ref( L, LUA_REGISTRYINDEX );
  }
}


void Object::MovementUpdate() {
  m_distance += m_speed;

  switch( m_direction ) {
    case Up: {
      Move( 0.0f, -m_speed );
      m_collisionRect.Offset( 0.0f, -m_speed );
      break;
    }
    case Down: {
      Move( 0.0f, m_speed );
      m_collisionRect.Offset( 0.0f, m_speed );
      break;
    }
    case Left: {
      Move( -m_speed, 0.0f );
      m_collisionRect.Offset( -m_speed, 0.0f );
      break;
    }
    case Right: {
      Move( m_speed, 0.0f );
      m_collisionRect.Offset( m_speed, 0.0f );
      break;
    }
    default: {}
  }

  if( m_distance >= m_state->GetTileManager().GetTileDim()) {
    m_moving = false;
    CorrectMovement();
    m_distance = 0.0f;
  }
}


void Object::CorrectMovement() {
  static float diff = 0.0f;
  //Calculate the amount of distance to move back
  diff = m_distance - m_state->GetTileManager().GetTileDim();

  //Find the correct direction to move back
  switch( m_direction ) {
    case Up: {
      Move( 0.0f, diff );
      m_collisionRect.Offset( 0.0f, diff );
      break;
    }
    case Down: {
      Move( 0.0f, -diff );
      m_collisionRect.Offset( 0.0f, -diff );
      break;
    }
    case Left: {
      Move( diff, 0.0f );
      m_collisionRect.Offset( diff, 0.0f );
      break;
    }
    case Right: {
      Move( -diff, 0.0f );
      m_collisionRect.Offset( -diff, 0.0f );
      break;
    }
    default: {}
  }

  SetPosition( round( GetPosition().x ), round( GetPosition().y ) );
  m_collisionRect.Top = round( m_collisionRect.Top );
  m_collisionRect.Bottom = round( m_collisionRect.Bottom );
  m_collisionRect.Left = round( m_collisionRect.Left );
  m_collisionRect.Right = round( m_collisionRect.Right );
}


void Object::CallScriptFunc( std::string &funcName ) {
  lua_State *L = App::GetApp()->GetLuaState();
  lua_rawgeti( L, LUA_REGISTRYINDEX, m_id );
  lua_getfield( L, -1, funcName.c_str() );
  if( lua_isfunction( L, -1 ) ) {
    Lunar<Object>::push( L, this );
    lua_call( L, 1, 0 );
  }
  lua_settop( L, 0 );
}


bool Object::LoadCollisionData( const std::string &filepath ) {
  TiXmlDocument doc ( filepath.c_str() );

  if ( !doc.LoadFile() ) {
    return false;
  }

  TiXmlHandle handleDoc( &doc );
  TiXmlElement *root = handleDoc.FirstChildElement( "object" ).Element();

  TiXmlElement *rect = root->FirstChildElement( "rect" );
  if ( rect ) {
    rect->QueryFloatAttribute( "x", &m_collisionRect.Left );
    m_collisionRect.Left += GetPosition().x;

    rect->QueryFloatAttribute( "y", &m_collisionRect.Top );
    m_collisionRect.Top += GetPosition().y;

    rect->QueryFloatAttribute( "width", &m_collisionRect.Right );
    m_collisionRect.Right += m_collisionRect.Left;

    rect->QueryFloatAttribute( "height", &m_collisionRect.Bottom );
    m_collisionRect.Bottom += m_collisionRect.Top;
  }

  TiXmlElement *tile = root->FirstChildElement( "tile" );
  if ( tile ) {
    const char *blockingTile = tile->Attribute( "block" );
    if ( blockingTile ) {
      m_blockingTile = ( ToLowerCase( blockingTile ) == "true" );
    } else {
      LogErr( "No 'block' attribute specified for tile element in Object: " +
              filepath );
      return false;
    }
  }

  return true;
}


bool Object::LoadObjectData( const std::string &filepath ) {
  TiXmlDocument doc ( filepath.c_str() );

  if ( !doc.LoadFile() ) {
    LogErr( "Unable to load Object file: " + filepath );
    return false;
  }

  m_type = GetXmlFileName( filepath );

  TiXmlHandle handleDoc( &doc );
  TiXmlElement *root = handleDoc.FirstChildElement( "object" ).Element();

  TiXmlElement *animation = root->FirstChildElement( "animation" );
  if( animation ) {
    const char *animPath = animation->Attribute( "path" );
    if ( animPath ) {
      LoadAnimData( animPath );
    } else {
      LogErr( "No animation path specified in Object: " + filepath );
      return false;
    }
  }

  TiXmlElement *script = root->FirstChildElement( "script" );
  if ( script ) {
    const char *scriptPath = script->Attribute( "path" );
    if ( scriptPath ) {
      m_luaScript = scriptPath;
    } else {
      LogErr( "No script path specified in Object: " + filepath );
      return false;
    }
  }

  TiXmlElement *speed = root->FirstChildElement( "speed" );
  if ( speed ) {
    speed->QueryFloatAttribute( "ppf", &m_speed );
  }

  //Load input data if there is any
  const TiXmlElement *inputListRoot = root->FirstChildElement( "input_list" );
  if ( inputListRoot ) {
    if ( !m_input.LoadInputList( inputListRoot )) {
      LogErr( "Problem loading input list in Object: " + m_type );
      return false;
    }
  }

  return true;
}
Here we can see the registering of functions to lua and the ability to return by pushing integers, floats and we can also accept paramters from lua and check if they are correct. This is very powerful indeed. Before I end this small tutorial I would like to just show one more example which shows the lua implementation of the collision behaviors for our character kickle. Here you can start to see the real power of lunar kicking in:

Code: Select all

function Kickle.HandleCollision( self, other )

  otherType = other:GetType()

  -- Things that kill Kickle

	if ( otherType == "Slime" or otherType == "Penguin" or 

       otherType == "IceBlock" ) then

		Kickle.state = DYING

		self:SetAnimation( self:GetDir() + Kickle.state )

  

  elseif ( otherType == "DreamBag" and Kickle.state ~= DYING ) then

    State.DestroyObject( other )

  end

end
Take particular note of: State.DestroyObject( other )
We are actually able to pass an object as a parameter in lua that is a "C++" object. Now look how this translates into C++ code on the other side of the AP (ObjectManager.cpp)I:

Code: Select all

int ObjectManager::LuaDestroyObject( lua_State *L ) {
  Object *ObjectToDestroy = Lunar<Object>::check(L, 1);
  if ( ObjectToDestroy ) {
    lua_remove( L, 1 );
    Inst().RemoveObject( ObjectToDestroy );
  }
  return 0;
}
So we have taken an object which could have potentially be modified in lua and we are able to retrieve it back into C++ this is extremely powerful. I will leave you with a couple other method implementations which may get you thing how it could be useful (Take a look at LuaGetObjectOnTile):

Code: Select all

int ObjectManager::LuaCreateObject( lua_State *L ) {
  if( !lua_isstring( L, -3 )) {
    LogLuaErr( "String not passed for file path in CreateObject." );
    return luaL_error( L, "String not passed for file path in CreateObject." );
  }
  std::string path = lua_tostring( L, -3 );

  if( !lua_isnumber( L, -2 ) || !lua_isnumber( L, -1 )) {
    LogLuaErr( "Number not passed to tile location in CreateObject." );
    return luaL_error( L,
      "Number not passed to tile location  in CreateObject." );
  }
  int tileX = lua_tointeger( L, -2 );
  int tileY = lua_tointeger( L, -1 );

  if ( tileX >= 0 && tileY >= 0 ) {
    Object *newObject = new Object( path, tileX, tileY, 0 );
    Inst().AddObject( newObject );
    Lunar<Object>::push( L, newObject );
    return 1;
  } else {
    LogLuaErr( "Negative tile passed to CreateObject" );
    return 0;
  }
}


int ObjectManager::LuaDestroyObject( lua_State *L ) {
  Object *ObjectToDestroy = Lunar<Object>::check(L, 1);
  if ( ObjectToDestroy ) {
    lua_remove( L, 1 );
    Inst().RemoveObject( ObjectToDestroy );
  }
  return 0;
}


int ObjectManager::LuaGetObject( lua_State *L ) {
  if ( !lua_isstring( L, -1 ) ) {
    LogLuaErr( "String not passed for Object type in GetObject." );
    return luaL_error( L, "String not passed for Object type in GetObject." );
  }
  std::string ObjectType = lua_tostring( L, -1 );

  Lunar<Object>::push( L, Inst().GetObject( ObjectType ));
  return 1;
}


int ObjectManager::LuaGetObjects( lua_State *L ) {
  if ( !lua_isstring( L, -1 ) ) {
    LogLuaErr( "String not passed for Object type in GetObject." );
    return luaL_error( L, "String not passed for Object type in GetObject." );
  }
  std::string type = lua_tostring( L, -1 );

  lua_newtable( L );
  int newTable = lua_gettop( L );
  int index = 1; // Lua table indices start at 1

  std::list<Object*>::const_iterator obj = Inst().m_objects.begin();
  while ( obj != Inst().m_objects.end()) {
    if ( *obj && ( *obj )->GetType() == type ) {
      Lunar<Object>::push( L, *obj );
      lua_rawseti( L, newTable, index );
      ++index;
    }
    ++obj;
  }
  return 1;
}
int ObjectManager::LuaGetObjectOnTile( lua_State *L ) {
  if ( !lua_isnumber( L, -2 ) ) {
    LogLuaErr( "Number not passed to x position in GetObjectOnTile." );
    return luaL_error( L, "Number not passed to x position in GetObjectOnTile." );
  }
  int tileX = lua_tointeger( L, -2 );

  if ( !lua_isnumber( L, -1 ) ) {
    LogLuaErr( "Number not passed to y position in GetObjectOnTile." );
    return luaL_error( L, "Number not passed to y position in GetObjectOnTile." );
  }
  int tileY = lua_tointeger( L, -1 );

  if ( tileX >= 0 && tileY >= 0 ) {
    Lunar<Object>::push( L, Inst().ObjectOnTile( tileX, tileY ));
    return 1;
  } else {
    LogLuaErr( "Negative tile passed to GetObjectOnTile" );
    return 0;
  }
}


int ObjectManager::LuaObjectBlockingTile( lua_State *L ) {
  if ( !lua_isnumber( L, -2 ) ) {
    LogLuaErr( "Number not passed to x position in ObjectBlockingTile" );
    return luaL_error( L, "Number not passed to x position in ObjectBlockingTile." );
  }
  int tileX = lua_tointeger( L, -2 );

  if ( !lua_isnumber( L, -1 ) ) {
    LogLuaErr( "Number not passed to y position in ObjectBlockingTile." );
    return luaL_error( L, "Number not passed to y position in ObjectBlockingTile." );
  }
  int tileY = lua_tointeger( L, -1 );

  if ( tileX >= 0 && tileY >= 0 ) {
    lua_pushboolean( L, Inst().ObjectBlockingTile( tileX, tileY ));
    return 1;
  } else {
    LogLuaErr( "Negative tile passed to ObjectBlockingTile" );
    return 0;
  }
}

This code was written by the developers of NTEngine which is licensed under the MIT license agreement you can find more information in regards to that over at http://ntengine.sf.net please support us by downloading the first stage release of our game and commenting on it via our mailing list or the post I have made in the Game Development section.
Post Reply