Lunar Passing C++ Objects to Lua
Posted: Mon Jul 19, 2010 12:50 am
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.)
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++.
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:
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:
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):
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.
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
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;
}
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
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;
}
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;
}
}