// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

// TODO: Convert this whole thing to use LuaWrapperGen or something.

#include <iostream>
using namespace std;

#include "../console/console.h"
using namespace Console;

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#include "../lua/luatableserializer.h"
#include "world.h"
#include "scriptentity.h"
#include "editorui.h"
#include "../malfont/fontmanager.h"
#include "../malfont/malfont.h"
#include "../image/image.h"

// This just creates a new table with the metatable set to
// __index = ourself. You can use it to create child classes, or even
// children of child classes and so on. It doesn't have to be redefined
// in child classes to get this ability.
int luaCB_Entity_createSubClass(lua_State *L) {
	assert(lua_gettop(L) == 1);

	// Create a new table.
	lua_newtable(L);
	
	// Set its metatable to { __index = self } (THIS one being self,
	// not the one we just created.)
	lua_newtable(L);
	lua_pushstring(L, "__index");
	lua_pushvalue(L, -4); // Push MY self onto the stack.
	lua_settable(L, -3);
	lua_setmetatable(L, -2);
	
	// Make a pointer we can use in C++ to find the original.
	lua_pushstring(L, "_userData");
	lua_pushlightuserdata(L, NULL);
	lua_settable(L, -3);
	
	return 1;
}

// This creates an actual instance of a class. It doesn't have to be
// redefined in child classes.
int luaCB_Entity_create(lua_State *L) {
	assert(lua_gettop(L) == 1 || lua_gettop(L) == 2);
	
	std::string customData;
	if(lua_gettop(L) == 2) {
		if(!lua_isnil(L, -1)) {
			customData = lua_tostring(L, -1);
		}
		lua_pop(L, 1);
	}

	ScriptEntity *ent = new ScriptEntity(activeWorld);
	
	// Get a new subclass onto the stack.
	luaCB_Entity_createSubClass(L);
	
	// Make a pointer we can use in C++ to find the original.
	lua_pushstring(L, "_userData");
	lua_pushlightuserdata(L, (void*)ent);
	lua_settable(L, -3);
	
	// Add this one to the global entitiesByName.
	lua_getfield(L, LUA_GLOBALSINDEX, "entitiesByName");
	lua_pushstring(L, ent->getName().c_str());
	lua_pushvalue(L, -3); // Copy the Entity table to the top.
	lua_settable(L, -3);
	lua_pop(L, 1); // Pop the entitiesByName back off.
	
	if(customData.size()) {
		// Shove in the customData.
		lua_pushstring(L, "customData");
		lua_pushstring(L, customData.c_str());
		lua_settable(L, -3);
	}
	
	// Call the initialization function.
	lua_getfield(L, -1, "init");
	lua_pushvalue(L, -2); // For the "self" variable...
	if(lua_pcall(L, 1, 0, 0)) {
		out("error") << "Lua exploded because: "  << lua_tostring(L, -1) << endl;
	}
	
	return 1;
}

void createScriptEntityWithXml(World *world, TiXmlElement *xml) {
	ScriptEntity *ent = new ScriptEntity(world, xml);
	lua_State *L = world->getLuaState();
	
	// This is mostly the same as above.
	// TODO: Combine both into one function?
	
	// Get a new subclass onto the stack.
	luaCB_Entity_createSubClass(L);
	
	// Make a pointer we can use in C++ to find the original.
	lua_pushstring(L, "_userData");
	lua_pushlightuserdata(L, (void*)ent);
	lua_settable(L, -3);
	
	// This will just let Spawners know not to screw up sprite definitions.
	lua_pushstring(L, "_loadedXML");
	lua_pushboolean(L, true);
	lua_settable(L, -3);
	
	// Add this one to the global entitiesByName.
	lua_getfield(L, LUA_GLOBALSINDEX, "entitiesByName");
	lua_pushstring(L, ent->getName().c_str());
	lua_pushvalue(L, -3); // Copy the Entity table to the top.
	lua_settable(L, -3);
	lua_pop(L, 1); // Pop the entitiesByName back off.
	
	// TODO: Remove this and use the normal save/load info.
	// Okay. Table's been created. Time to move in all the XML data.
	ent->fillTableData();
	
	// First try to find an initFromSave()...
	lua_getfield(L, -1, "initFromSave");
	if(!lua_isnil(L, -1)) {
		
		lua_pushvalue(L, -2); // For the "self" variable...
		
		LuaTableSerializer lts;
		lts.tableFromXml(L, xml->FirstChildElement("luaState"));
		//lua_newtable(L); // TODO: Replace this with the XML loaded table!
		
		if(lua_pcall(L, 2, 0, 0)) {
			out("error") << "Lua exploded because (initFromSave - " << ent->getName() << "): " << lua_tostring(L, -1) << endl;
		}
		
	} else {
		// No initFromSave means we just do the normal initialization.
		lua_pop(L, 1); // Pop off that nil.
	
		// Call the initialization function.
		lua_getfield(L, -1, "init");
		lua_pushvalue(L, -2); // For the "self" variable...
		if(lua_pcall(L, 1, 0, 0)) {
			out("error") << "Lua exploded because (initFromSave - " << ent->getName() << "): " << lua_tostring(L, -1) << endl;
		}
	}
	
	// The new script entity is LEFT ON THE STACK.
}

int luaCB_Entity_testFunc(lua_State *L) {
	// Get "self".
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) {
		out("error") << "Attempt to access non-existing Entity" << endl;
		return 0;
	}
	//cout << ent->getName() << endl;
	
	return 0;
}

int luaCB_Entity_init(lua_State *L) {
	//ScriptEntity *ent = getEntityFromTable(L, -1);
	
	// TODO: Any extra stuff that we didn't handle in the constructor?
	
	return 0;
}

int luaCB_Entity_getName(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushstring(L, ent->getName().c_str());
	return 1;
}

int luaCB_Entity_setName(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setName(lua_tostring(L, -1));
	return 0;
}

int luaCB_Entity_setTexture(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setTexture(lua_tostring(L, -1));
	
	return 0;
}

int luaCB_Entity_getTexture(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushstring(L, ent->getTexture().c_str());
	
	return 1;
}

int luaCB_Entity_setSpriteFrame(lua_State *L) {
	assert(lua_gettop(L) == 5);
	
	ScriptEntity *ent = getEntityFromTable(L, -5);
	if(!ent) return 0;
	
	ent->setSpriteFrame(BoundingBox(
		Vector2D(
			lua_tonumber(L, -4),
			lua_tonumber(L, -3)),
		Vector2D(
			lua_tonumber(L, -2),
			lua_tonumber(L, -1))));
			
	return 0;
}

int luaCB_Entity_getSpriteFrame(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	BoundingBox box = ent->getSpriteFrame();
	lua_pushnumber(L, box.upperLeft.x);
	lua_pushnumber(L, box.upperLeft.y);
	lua_pushnumber(L, box.lowerRight.x);
	lua_pushnumber(L, box.lowerRight.y);
	return 4;
}

int luaCB_Entity_getSize(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	Vector2D size = ent->getSize();
	lua_pushnumber(L, size.x);
	lua_pushnumber(L, size.y);
	
	return 2;
}

int luaCB_Entity_setSize(lua_State *L) {
	assert(lua_gettop(L) == 3);
	
	ScriptEntity *ent = getEntityFromTable(L, -3);
	if(!ent) return 0;
	
	ent->setSize(Vector2D(lua_tonumber(L, -2), lua_tonumber(L, -1)));
	
	return 0;
}

int luaCB_Entity_setPos(lua_State *L) {
	assert(lua_gettop(L) == 3);
	
	ScriptEntity *ent = getEntityFromTable(L, -3);
	if(!ent) return 0;
	
	ent->setPos(Vector2D(lua_tonumber(L, -2), lua_tonumber(L, -1)));
	
	return 0;
}

int luaCB_Entity_getPos(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	Vector2D size = ent->getPos();
	lua_pushnumber(L, size.x);
	lua_pushnumber(L, size.y);
	
	return 2;
}

int luaCB_Entity_setVel(lua_State *L) {
	assert(lua_gettop(L) == 3);
	
	ScriptEntity *ent = getEntityFromTable(L, -3);
	if(!ent) return 0;
	
	ent->setVel(Vector2D(lua_tonumber(L, -2), lua_tonumber(L, -1)));
	
	return 0;
}

int luaCB_Entity_getVel(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	Vector2D size = ent->getVel();
	lua_pushnumber(L, size.x);
	lua_pushnumber(L, size.y);
	
	return 2;
}

int luaCB_Entity_getBlocker(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->blocker);
	
	return 1;
}

int luaCB_Entity_setBlocker(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->blocker = lua_toboolean(L, -1);
	
	return 0;
}

int luaCB_Entity_getBlockable(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->blockable);
	
	return 1;
}

int luaCB_Entity_setBlockable(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->blockable = lua_toboolean(L, -1);
	
	return 0;
}

int luaCB_Entity_die(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	ent->die();
	
	return 0;
}

int luaCB_Entity_getAge(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->getAge());
	
	return 1;
}

int luaCB_Entity_setGravityInfluence(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->gravityInfluence = lua_tonumber(L, -1);
	
	return 0;
}

int luaCB_Entity_getGravityInfluence(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->gravityInfluence);
	
	return 1;
}

int luaCB_Entity_setAlpha(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setAlpha(lua_tonumber(L, -1));
	
	return 0;
}

int luaCB_Entity_getAlpha(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->getAlpha());
	
	return 1;
}

int luaCB_Entity_setLayer(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setLayer(lua_tonumber(L, -1), false);
	
	return 0;
}

int luaCB_Entity_getLayer(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->getLayer());
	
	return 1;
}

int luaCB_Entity_setIgnoreBlockDown(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setIgnoreBlockDown(lua_toboolean(L, -1));
	
	return 0;
}

int luaCB_Entity_getIgnoreBlockDown(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->getIgnoreBlockDown());
	
	return 1;
}

int luaCB_Entity_setSpriteScale(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setSpriteScale(lua_tonumber(L, -1));
	
	return 0;
}

int luaCB_Entity_getSpriteScale(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->getSpriteScale());
	
	return 1;
}

int luaCB_Entity_setOnGround(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->onGround = lua_toboolean(L, -1);
	
	return 0;
}

int luaCB_Entity_getOnGround(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->onGround);
	
	return 1;
}

int luaCB_Entity_setBounciness(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->bounciness = lua_tonumber(L, -1);
	
	return 0;
}

int luaCB_Entity_getBounciness(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->bounciness);
	
	return 1;
}

int luaCB_Entity_setSpriteOffset(lua_State *L) {
	assert(lua_gettop(L) == 3);
	
	ScriptEntity *ent = getEntityFromTable(L, -3);
	if(!ent) return 0;
	
	ent->setSpriteOffset(Vector2D(lua_tonumber(L, -2), lua_tonumber(L, -1)));
	
	return 0;
}

int luaCB_Entity_getSpriteOffset(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	Vector2D offset = ent->getSpriteOffset();
	lua_pushnumber(L, offset.x);
	lua_pushnumber(L, offset.y);
	
	return 2;
}

int luaCB_Entity_setSpriteBottomCenter(lua_State *L) {
	assert(lua_gettop(L) == 3);
	
	ScriptEntity *ent = getEntityFromTable(L, -3);
	if(!ent) return 0;
	
	ent->setSpriteBottomCenter(Vector2D(lua_tonumber(L, -2), lua_tonumber(L, -1)));
	
	return 0;
}

int luaCB_Entity_setForceUpdate(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setForceUpdate(lua_toboolean(L, -1));
	
	return 0;
}

int luaCB_Entity_getForceUpdate(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->getForceUpdate());
	
	return 1;
}

int luaCB_Entity_setFlashing(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->flashing = lua_toboolean(L, -1);
	
	return 0;
}

int luaCB_Entity_getFlashing(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->flashing);
	
	return 1;
}

int luaCB_Entity_getOnScreen(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	BoundingBox bb = ent->getBoundingBox();
	lua_pushboolean(L, ent->getWorld()->getVisibleArea(ent->getLayer()).overlap(bb));
	
	return 1;
}

int luaCB_Entity_setLabel(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setLabel(lua_tostring(L, -1));
	
	return 0;
}

int luaCB_Entity_setLabelFont(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setLabelFont(lua_tostring(L, -1));
	
	return 0;
}

int luaCB_Entity_getLabel(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushstring(L, ent->getLabel().c_str());
		
	return 1;
}

int luaCB_Entity_getLabelFont(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushstring(L, ent->getLabelFont().c_str());
	
	return 1;
}

int luaCB_Entity_setDepth(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setDepth(lua_tonumber(L, -1));
	
	return 0;
}

int luaCB_Entity_getDepth(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->getDepth());
		
	return 1;
}

int luaCB_Entity_setEditorVisibleOnly(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->editorVisibleOnly = lua_toboolean(L, -1);
	
	return 0;
}

int luaCB_Entity_setSpriteAngle(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setSpriteAngle(lua_tonumber(L, -1));
	
	return 0;
}

int luaCB_Entity_getSpriteAngle(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushnumber(L, ent->getSpriteAngle());
		
	return 1;
}

int luaCB_Entity_getEditorVisibleOnly(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->editorVisibleOnly);
	
	return 1;
}

int luaCB_Entity_setColor(lua_State *L) {
	assert(lua_gettop(L) == 4);
	
	ScriptEntity *ent = getEntityFromTable(L, -4);
	if(!ent) return 0;
	
	ent->setColor(
		lua_tonumber(L, -3),
		lua_tonumber(L, -2),
		lua_tonumber(L, -1));
	
	return 0;
}

int luaCB_Entity_getColor(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	float r, g, b;
	ent->getColor(&r, &g, &b);
	
	lua_pushnumber(L, r);
	lua_pushnumber(L, g);
	lua_pushnumber(L, b);
		
	return 3;
}

int luaCB_Entity_getDead(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	if(!getEntityTableIsValid(L, -1)) {

		// Something happened to this and it's no longer
		// around. That's basically like being dead.
		lua_pushboolean(L, true);
		return 1;
	}

	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->getDead());
	
	return 1;
}

int luaCB_Entity_getLabelFontHeight(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	string fontName = ent->getLabelFont();
	
	if(!fontName.size()) return 0;
	
	// Ugly hack alert!
	string fixedName = ent->getWorld()->getRealFile(string("fonts/") + fixDeprecatedPath(fontName));
	
	MalFont *font = ent->getWorld()->getFontManager()->getFont(fixedName);
	if(!font) return 0;
	
	lua_pushnumber(L, font->getOrigHeight());
	
	return 1;
}

int luaCB_Entity_getFlipHorizontal(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->getFlipHorizontal());
	
	return 1;
}

int luaCB_Entity_setFlipHorizontal(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setFlipHorizontal(lua_toboolean(L, -1));
	
	return 0;
}

int luaCB_Entity_setFlipVertical(lua_State *L) {
	assert(lua_gettop(L) == 2);
	
	ScriptEntity *ent = getEntityFromTable(L, -2);
	if(!ent) return 0;
	
	ent->setFlipVertical(lua_toboolean(L, -1));
	
	return 0;
}

int luaCB_Entity_getFlipVertical(lua_State *L) {
	assert(lua_gettop(L) == 1);
	
	ScriptEntity *ent = getEntityFromTable(L, -1);
	if(!ent) return 0;
	
	lua_pushboolean(L, ent->getFlipVertical());
	
	return 1;
}

int luaCB_Entity_setLinkTarget(lua_State *L) {
    assert(lua_gettop(L) == 2);

    ScriptEntity *ent = getEntityFromTable(L, -2);
    if(!ent) return 0;

    const char *str = lua_tostring(L, -1);
    if(str) {
        ent->setLinkTarget(str);
    } else {
        ent->setLinkTarget("");
    }

    return 0;
}

int luaCB_Entity_addLinkTarget(lua_State *L) {
    assert(lua_gettop(L) == 2);

    ScriptEntity *ent = getEntityFromTable(L, -2);
    if(!ent) return 0;

    const char *str = lua_tostring(L, -1);
    if(str) {
        ent->addLinkTarget(str);
    }

    return 0;
}

int luaCB_Entity_getLinkTarget(lua_State *L) {
    assert(lua_gettop(L) == 1);

    ScriptEntity *ent = getEntityFromTable(L, -1);
    if(!ent) return 0;

    lua_pushstring(L, ent->getLinkTarget().c_str());

    return 1;
}

int luaCB_Entity_getAllLinkTargets(lua_State *L) {
    assert(lua_gettop(L) == 1);

    ScriptEntity *ent = getEntityFromTable(L, -1);
    if(!ent) return 0;

    vector<string> targets;
    ent->getAllLinkTargets(targets);

    lua_newtable(L);

    for(unsigned int i = 0; i < targets.size(); i++) {
        lua_pushnumber(L, i + 1);
        lua_pushstring(L, targets[i].c_str());
        lua_settable(L, -3);
    }

    return 1;
}

int luaCB_Entity_setIsLight(lua_State *L) {
    assert(lua_gettop(L) == 2);

    ScriptEntity *ent = getEntityFromTable(L, -2);
    if(!ent) return 0;

    ent->setIsLight(lua_toboolean(L, -1));

    return 0;
}

int luaCB_Entity_getIsLight(lua_State *L) {
    assert(lua_gettop(L) == 1);

    ScriptEntity *ent = getEntityFromTable(L, -1);
    if(!ent) return 0;

    lua_pushboolean(L, ent->getIsLight());

    return 1;
}

int luaCB_Entity_setFullbright(lua_State *L) {
    assert(lua_gettop(L) == 2);

    ScriptEntity *ent = getEntityFromTable(L, -2);
    if(!ent) return 0;

    ent->setFullbright(lua_toboolean(L, -1));

    return 0;
}

int luaCB_Entity_getFullbright(lua_State *L) {
    assert(lua_gettop(L) == 1);

    ScriptEntity *ent = getEntityFromTable(L, -1);
    if(!ent) return 0;

    lua_pushboolean(L, ent->getFullbright());

    return 1;
}

void setFunctionInTable(lua_State *L, const std::string &funcName, lua_CFunction func) {
	lua_pushstring(L, funcName.c_str());
	lua_pushcfunction(L, func);
	lua_settable(L, -3);
}

void setupLuaBaseClass(lua_State *L) {
	// Make the base class table.
	lua_newtable(L);
	
	// Creation functions
	setFunctionInTable(L, "create", luaCB_Entity_create);
	setFunctionInTable(L, "createSubClass", luaCB_Entity_createSubClass);
	setFunctionInTable(L, "init", luaCB_Entity_init);
	setFunctionInTable(L, "getName", luaCB_Entity_getName);
	setFunctionInTable(L, "setName", luaCB_Entity_setName);

	// Test stuff	
	setFunctionInTable(L, "testFunc", luaCB_Entity_testFunc);
	
	// Graphics
	setFunctionInTable(L, "setTexture", luaCB_Entity_setTexture);
	setFunctionInTable(L, "getTexture", luaCB_Entity_getTexture);
	setFunctionInTable(L, "setSpriteFrame", luaCB_Entity_setSpriteFrame);
	setFunctionInTable(L, "getSpriteFrame", luaCB_Entity_getSpriteFrame);
	setFunctionInTable(L, "setAlpha", luaCB_Entity_setAlpha);
	setFunctionInTable(L, "getAlpha", luaCB_Entity_getAlpha);
	setFunctionInTable(L, "setLayer", luaCB_Entity_setLayer);
	setFunctionInTable(L, "getLayer", luaCB_Entity_getLayer);
	setFunctionInTable(L, "setSpriteScale", luaCB_Entity_setSpriteScale);
	setFunctionInTable(L, "getSpriteScale", luaCB_Entity_getSpriteScale);
	setFunctionInTable(L, "setSpriteAngle", luaCB_Entity_setSpriteAngle);
	setFunctionInTable(L, "getSpriteAngle", luaCB_Entity_getSpriteAngle);
	setFunctionInTable(L, "setSpriteOffset", luaCB_Entity_setSpriteOffset);
	setFunctionInTable(L, "getSpriteOffset", luaCB_Entity_getSpriteOffset);
	setFunctionInTable(L, "setSpriteBottomCenter", luaCB_Entity_setSpriteBottomCenter);
	setFunctionInTable(L, "setFlashing", luaCB_Entity_setFlashing);
	setFunctionInTable(L, "getFlashing", luaCB_Entity_getFlashing);
	
	setFunctionInTable(L, "setDepth", luaCB_Entity_setDepth);
	setFunctionInTable(L, "getDepth", luaCB_Entity_getDepth);
	
	setFunctionInTable(L, "setLabel", luaCB_Entity_setLabel);
	setFunctionInTable(L, "getLabel", luaCB_Entity_getLabel);
	setFunctionInTable(L, "setLabelFont", luaCB_Entity_setLabelFont);
	setFunctionInTable(L, "getLabelFont", luaCB_Entity_getLabelFont);
	
	setFunctionInTable(L, "setFlipHorizontal", luaCB_Entity_setFlipHorizontal);
	setFunctionInTable(L, "setFlipVertical", luaCB_Entity_setFlipVertical);
	setFunctionInTable(L, "getFlipHorizontal", luaCB_Entity_getFlipHorizontal);
	setFunctionInTable(L, "getFlipVertical", luaCB_Entity_getFlipVertical);

    setFunctionInTable(L, "getIsLight", luaCB_Entity_getIsLight);
    setFunctionInTable(L, "setIsLight", luaCB_Entity_setIsLight);
    setFunctionInTable(L, "getFullbright", luaCB_Entity_getFullbright);
    setFunctionInTable(L, "setFullbright", luaCB_Entity_setFullbright);

	// Size
	setFunctionInTable(L, "getSize", luaCB_Entity_getSize);
	setFunctionInTable(L, "setSize", luaCB_Entity_setSize);
	
	// Position
	setFunctionInTable(L, "getPos", luaCB_Entity_getPos);
	setFunctionInTable(L, "setPos", luaCB_Entity_setPos);
	
	// Blocking
	setFunctionInTable(L, "getBlocker", luaCB_Entity_getBlocker);
	setFunctionInTable(L, "getBlockable", luaCB_Entity_getBlockable);
	setFunctionInTable(L, "setBlocker", luaCB_Entity_setBlocker);
	setFunctionInTable(L, "setBlockable", luaCB_Entity_setBlockable);
	
	// Actions
	setFunctionInTable(L, "die", luaCB_Entity_die);
	
	// Velocity
	setFunctionInTable(L, "getVel", luaCB_Entity_getVel);
	setFunctionInTable(L, "setVel", luaCB_Entity_setVel);
	
	// Information
	setFunctionInTable(L, "getAge", luaCB_Entity_getAge);
	setFunctionInTable(L, "getDead", luaCB_Entity_getDead);
	
	// Gravity
	setFunctionInTable(L, "getGravityInfluence", luaCB_Entity_getGravityInfluence);
	setFunctionInTable(L, "setGravityInfluence", luaCB_Entity_setGravityInfluence);
	
	setFunctionInTable(L, "setIgnoreBlockDown", luaCB_Entity_setIgnoreBlockDown);
	setFunctionInTable(L, "getIgnoreBlockDown", luaCB_Entity_getIgnoreBlockDown);
	
	setFunctionInTable(L, "setOnGround", luaCB_Entity_setOnGround);
	setFunctionInTable(L, "getOnGround", luaCB_Entity_getOnGround);
	
	setFunctionInTable(L, "setBounciness", luaCB_Entity_setBounciness);
	setFunctionInTable(L, "getBounciness", luaCB_Entity_getBounciness);

	// Functionality
	setFunctionInTable(L, "setForceUpdate", luaCB_Entity_setForceUpdate);
	setFunctionInTable(L, "getForceUpdate", luaCB_Entity_getForceUpdate);

	setFunctionInTable(L, "getOnScreen", luaCB_Entity_getOnScreen);

	setFunctionInTable(L, "getEditorVisibleOnly", luaCB_Entity_getEditorVisibleOnly);
	setFunctionInTable(L, "setEditorVisibleOnly", luaCB_Entity_setEditorVisibleOnly);

	setFunctionInTable(L, "setColor", luaCB_Entity_setColor);
	setFunctionInTable(L, "getColor", luaCB_Entity_getColor);
	
	setFunctionInTable(L, "getLabelFontHeight", luaCB_Entity_getLabelFontHeight);

	setFunctionInTable(L, "getLinkTarget", luaCB_Entity_getLinkTarget);
	setFunctionInTable(L, "setLinkTarget", luaCB_Entity_setLinkTarget);
	setFunctionInTable(L, "getAllLinkTargets", luaCB_Entity_getAllLinkTargets);
	setFunctionInTable(L, "addLinkTarget", luaCB_Entity_addLinkTarget);

	// Set it as a global ("Entity").
	lua_setglobal(L, "Entity");
}

ScriptEntity *getEntityFromTable(lua_State *L, int index) {
	lua_pushstring(L, "_userData");
	lua_gettable(L, index - 1);
	ScriptEntity *ent = (ScriptEntity*)lua_touserdata(L, -1);
	lua_pop(L, 1);
	
	if(!ent) {
		out("error") << "Attempting to access a non-existing Entity" << endl;
	}
	
	return ent;
}

bool getEntityTableIsValid(lua_State *L, int index) {
	lua_pushstring(L, "_userData");
	lua_gettable(L, index - 1);
	ScriptEntity *ent = (ScriptEntity*)lua_touserdata(L, -1);
	lua_pop(L, 1);
	
	if(!ent) {
		return false;
	}
	
	return true;
}

void ScriptEntity::pushTable(void) const {
	lua_State *L = getWorld()->getLuaState();
	
	// Get the entities table.
	lua_getfield(L, LUA_GLOBALSINDEX, "entitiesByName");
	
	// Push our name onto the stack.
	lua_pushstring(L, getName().c_str());
	
	// Get the table.
	lua_gettable(L, -2);
	
	// Now remove the entities table from the stack.
	lua_remove(L, -2);
}

TiXmlElement *ScriptEntity::toXml(void) const {
	TiXmlElement *xml = Entity::toXml();
	
	// Spawner-specific stuff saved here.
	// Non-spawners won't get saved to XML anyway.
	lua_State *L = getWorld()->getLuaState();
	pushTable(); // Push our table

	xml->SetValue("ScriptEntity");

	string metatableName = findGlobalNameForMetaTable(L);
	if(metatableName.size()) {
		xml->SetAttribute("Metatable", metatableName.c_str());
	} else {
		assert(0);
	}

	if(getIsSpawner()) {
		// TODO: Remove this and use the new save system.
		lua_getfield(L, -1, "baseClassName");
		if(!lua_isnil(L, -1)) {
			xml->SetAttribute("baseClassName", lua_tostring(L, -1));
		}
		lua_pop(L, 1);
		
		// TODO: Remove this and use the new save system.
		lua_getfield(L, -1, "customName");
		if(!lua_isnil(L, -1)) {
			xml->SetAttribute("customName", lua_tostring(L, -1));
		}
		lua_pop(L, 1);

		// TODO: customData is a stupid hack. Remove it.
		lua_getfield(L, -1, "customData");
		if(!lua_isnil(L, -1)) {
			xml->SetAttribute("customData", lua_tostring(L, -1));
		}
		lua_pop(L, 1);
	}
	
	// Get the list of editor properties.
	lua_getfield(L, -1, "editorProperties");
	if(!lua_isnil(L, -1)) {
		lua_pushnil(L);
		while(lua_next(L, -2)) {
			if(lua_type(L, -2) == LUA_TSTRING) {
				string propName(lua_tostring(L, -2));
				
				TiXmlElement *prop = new TiXmlElement("attribute");
				prop->SetAttribute("name", propName.c_str());
				
				switch(lua_type(L, -1)) {
					case LUA_TSTRING:
						prop->SetAttribute("type", "string");
						prop->SetAttribute("value", lua_tostring(L, -1));
						break;
					case LUA_TBOOLEAN:
						prop->SetAttribute("type", "boolean");
						prop->SetAttribute("value", lua_toboolean(L, -1) ? "1" : "0" );
						break;
					case LUA_TNUMBER:
						prop->SetAttribute("type", "number");
						prop->SetAttribute("value", lua_tonumber(L, -1));
						break;
				}
				
				xml->LinkEndChild(prop);
			}
			lua_pop(L, 1);
		}
	}
	// Pop the editorProperties table (or the nil if there was none).
	lua_pop(L, 1);
	
	lua_getfield(L, -1, "save");
	if(!lua_isnil(L, -1)) {
		lua_pushvalue(L, -2); // Push the class back as the "self" parameter.
		
		if(lua_pcall(L, 1, 1, 0)) {
			out("error") <<  "Lua exploded during saving " << getName() << ": " << lua_tostring(L, -1) << endl;
		}
		
	} else {
		lua_pop(L, 1);
		lua_newtable(L);
	}	
	
	LuaTableSerializer lts;
	TiXmlElement *luaStateXml = lts.tableToXml(L);
	xml->LinkEndChild(luaStateXml);
	
	// Pop the save data table.
	lua_pop(L, 1);
	
	// Pop our table.
	lua_pop(L, 1);
	
	return xml;
}

ScriptEntity::ScriptEntity(World *world, TiXmlElement *xml) : Entity(world, xml) {
	// For spawners, we need to get the class associated with the
	// base class name, and link it back to the spawner.
	if(xml->Attribute("baseClassName")) {
		tableData["baseClassName"] = xml->Attribute("baseClassName");
	}
	
	if(xml->Attribute("customData")) {
		tableData["customData"] = xml->Attribute("customData");
	}
	
	if(xml->Attribute("customName")) {
		tableData["customName"] = xml->Attribute("customName");
	}
	
	for(TiXmlElement *e = xml->FirstChildElement("attribute"); e; e = e->NextSiblingElement("attribute")) {
		editorProperties[e->Attribute("name")] = LuaValue(e);
	}
	
}

ScriptEntity::ScriptEntity(World *world) : Entity(world) {
}
	
ScriptEntity::~ScriptEntity(void) {
	lua_State *L = getWorld()->getLuaState();
	
	// Kill the "_userData" value in the table so we don't have a
	// dangling pointer to this.
	pushTable();
	lua_pushstring(L, "_userData");
	lua_pushnil(L);
	lua_settable(L, -3);
	lua_pop(L, 1);
	
	// Remove me from the global objects list.
	lua_getfield(L, LUA_GLOBALSINDEX, "entitiesByName");
	lua_pushstring(L, getName().c_str());
	lua_pushnil(L);
	lua_settable(L, -3);
	lua_pop(L, 1); // Pop the entitiesByName back off.

}

void ScriptEntity::update(void) {
	lua_State *L = getWorld()->getLuaState();
	
	// Get the table from the global Entities table by name.
	pushTable();
	
	// Get the update() from that.
	lua_getfield(L, -1, "update");
	
	// Copy the table back to the top.
	lua_pushvalue(L, -2);
	
	if(lua_isnil(L, -2)) {
		//cout << "Error: " << getName() << " has no update()!" << endl;
		lua_pop(L, 2);
	} else {
		if(lua_pcall(L, 1, 0, 0)) {
			out("error") << "Lua exploded because (update - " << getName() << "): " << lua_tostring(L, -1) << endl;
		}
	}
	
	// Pop the table.
	lua_pop(L, 1);
	
	// Run a normal update (physics, etc)
	Entity::update();
}

void ScriptEntity::postUpdate(void) {
	lua_State *L = getWorld()->getLuaState();
	
	// Get the table from the global Entities table by name.
	pushTable();
	
	// Get the postUpdate() from that.
	lua_getfield(L, -1, "postUpdate");
	
	// Copy the table back to the top.
	lua_pushvalue(L, -2);
	
	if(lua_isnil(L, -2)) {
		// postUpdate() is optional, so no error here.
		lua_pop(L, 2);
	} else {
		if(lua_pcall(L, 1, 0, 0)) {
			out("error") << "Lua exploded because (postUpdate - " << getName() << "): " << lua_tostring(L, -1) << endl;
		}
	}
	
	// Pop the table.
	lua_pop(L, 1);
	
	// Run a normal post update.
	Entity::postUpdate();
}

void ScriptEntity::setName(const std::string &name) {
	lua_State *L = getWorld()->getLuaState();

	// Add the ent table to the stack for after we change the name.
	pushTable();
	
	// Remove us from entitiesByName.
	lua_getfield(L, LUA_GLOBALSINDEX, "entitiesByName");
	lua_pushstring(L, getName().c_str());
	lua_pushnil(L);
	lua_settable(L, -3);

	// Call the normal entity name change.
	Entity::setName(name);

	// Reinsert us.
	lua_pushstring(L, getName().c_str());
	lua_pushvalue(L, -3); // Copy the Entity table to the top.
	lua_settable(L, -3);
	lua_pop(L, 2); // Pop the entitiesByName back off.

}

bool ScriptEntity::getIsSpawner(void) const {
	lua_State *L = getWorld()->getLuaState();
	
	pushTable();
	
	lua_getfield(L, -1, "isSpawner");
	bool ret = false;
	if(lua_toboolean(L, -1)) {
		ret = true;
	}
	lua_pop(L, 2);
	return ret;
}

void ScriptEntity::fillTableData(void) {
	lua_State *L = getWorld()->getLuaState();
	
	pushTable();
	
	for(std::map<std::string, std::string>::iterator i = tableData.begin(); i != tableData.end(); i++) {
		lua_pushstring(L, i->first.c_str());
		lua_pushstring(L, i->second.c_str());
		lua_settable(L, -3);
	}
	
	if(editorProperties.size()) {
		lua_pushstring(L, "editorProperties");
		lua_newtable(L);
		for(std::map<std::string, LuaValue>::iterator i = editorProperties.begin(); i != editorProperties.end(); i++) {
			lua_pushstring(L, i->first.c_str());
			i->second.push(L);
			lua_settable(L, -3);
		}
		lua_settable(L, -3);
		editorProperties.clear();
	}
		
	// Pop the main table.
	lua_pop(L, 1);
}

void ScriptEntity::updateEditor(EditorUI *ui) {
	
	Entity::updateEditor(ui);
	
	ui->junkInterface.addAttribute("ScriptEntity attributes", INSPECTOR_DIVIDER);
	
	// Get some info straight from Lua.
	lua_State *L = getWorld()->getLuaState();
	pushTable(); // Push our table
	
	ui->junkInterface.addAttribute("Class", INSPECTOR_READONLYSTRING);
	ui->junkInterface.setStringAttribute("Class", findGlobalNameForMetaTable(L));
	
	// TODO: Ditch spawner bullshit.
	if(getIsSpawner()) {
		
		ui->junkInterface.addAttribute("Spawner attributes", INSPECTOR_DIVIDER);

		ui->junkInterface.addAttribute("baseClassName", INSPECTOR_READONLYSTRING);
		
		ui->junkInterface.addAttribute("customData", INSPECTOR_STRING);
		ui->junkInterface.setStringAttribute("customData", tableData["customData"]);
		
		ui->junkInterface.addAttribute("customName", INSPECTOR_STRING);
		ui->junkInterface.setStringAttribute("customName", tableData["customName"]);
		
		// Get the name.
		lua_getfield(L, -1, "baseClassName");
		if(!lua_isnil(L, -1)) {
			ui->junkInterface.setStringAttribute("baseClassName", lua_tostring(L, -1));
		}
		lua_pop(L, 1);
		
	}
	
	// Get the list of editable properties defined in the Class.
	lua_getfield(L, -1, "editorProperties");
	
	if(!lua_isnil(L, -1)) {
		
		ui->junkInterface.addAttribute("Class specific attributes", INSPECTOR_DIVIDER);
		
		lua_pushnil(L);
		
		while(lua_next(L, -2)) {
			
			if(lua_type(L, -2) == LUA_TSTRING) {
				
				string propName(lua_tostring(L, -2));
				
				switch(lua_type(L, -1)) {
					
					case LUA_TSTRING:
						ui->junkInterface.addAttribute(propName, INSPECTOR_STRING);
						ui->junkInterface.setStringAttribute(propName, lua_tostring(L, -1));
						break;
						
					case LUA_TBOOLEAN:
						ui->junkInterface.addAttribute(propName, INSPECTOR_BOOL);
						ui->junkInterface.setBoolAttribute(propName, lua_toboolean(L, -1));
						break;
						
					case LUA_TNUMBER:
						ui->junkInterface.addAttribute(propName, INSPECTOR_DOUBLE);
						ui->junkInterface.setDoubleAttribute(propName, lua_tonumber(L, -1));
						break;
				}
			}
			
			lua_pop(L, 1);
		}
	}
	
	// Pop properties table.
	lua_pop(L, 1);
	
	// Pop the main table.
	lua_pop(L, 1);
	
}

void ScriptEntity::updateFromEditor(EditorUI *ui) {
	
	Entity::updateFromEditor(ui);
	
	// TODO: Ditch spawner stuff.
	
	if(getIsSpawner()) {
		
		tableData["customData"] = ui->junkInterface.getStringAttribute("customData");
		tableData["customName"] = ui->junkInterface.getStringAttribute("customName");
		fillTableData();
	}
		
	// Update the Lua table directly...
	lua_State *L = getWorld()->getLuaState();
	pushTable(); // Push our table
	
	// Iterate through the list of properties, updating as needed.
	lua_getfield(L, -1, "editorProperties");
	if(!lua_isnil(L, -1)) {
		
		lua_pushnil(L);
		while(lua_next(L, -2)) {
			
			// At this point the stack should be:
			//   Table, Key, Value
			
			if(lua_type(L, -2) == LUA_TSTRING) {
				string propName(lua_tostring(L, -2));
				
				switch(lua_type(L, -1)) {
					
					case LUA_TSTRING:
						lua_pop(L, 1); // Pop old value.
						lua_pushstring(L, ui->junkInterface.getStringAttribute(propName).c_str());
						lua_settable(L, -3);
						break;
						
					case LUA_TBOOLEAN:
						lua_pop(L, 1); // Pop old value.
						lua_pushboolean(L, ui->junkInterface.getBoolAttribute(propName));
						lua_settable(L, -3);
						break;
						
					case LUA_TNUMBER:
						lua_pop(L, 1); // Pop old value.
						lua_pushnumber(L, ui->junkInterface.getDoubleAttribute(propName));
						lua_settable(L, -3);
						break;
						
					default:
						// We just need stack behavior consistency
						// with the values we ARE dealing with and
						// lua_settable pops the value and key from
						// the stack.
						lua_pop(L, 2);
				}
				
				// We need the value back on the stack, so we know
				// where to keep going from.
				lua_pushstring(L, propName.c_str());
			}

		}
	}
	// Pop properties table.
	lua_pop(L, 1);
	
	// Pop the main table.
	lua_pop(L, 1);
	
}

void ScriptEntity::collided(Entity *otherEnt) {
	lua_State *L = getWorld()->getLuaState();
	
	// Get the table from the global Entities table by name.
	pushTable();
	
	// Get the collided() from that.
	lua_getfield(L, -1, "collided");
	
	if(lua_isnil(L, -1)) {
		// Oh. No collision function.
		lua_pop(L, 2);
		return;
	}
	
	// Copy the table back to the top (self).
	lua_pushvalue(L, -2);
	
	// Push the name of what we bumped into.
	lua_pushstring(L, otherEnt->getName().c_str());
	
	// Call the function.
	if(lua_pcall(L, 2, 0, 0)) {
		out("error") << "Lua exploded because (collided - " << getName() << ": " << lua_tostring(L, -1) << endl;
	}
	
	// Pop the table.
	lua_pop(L, 1);	
}

void ScriptEntity::collidedWithWorld(void) {
	
	lua_State *L = getWorld()->getLuaState();
	
	// Get the table from the global Entities table by name.
	pushTable();
	
	// Get the collided() from that.
	lua_getfield(L, -1, "collidedWithWorld");
	
	if(lua_isnil(L, -1)) {
		// Oh. No collision function.
		lua_pop(L, 2);
		return;
	}
	
	// Copy the table back to the top (self).
	lua_pushvalue(L, -2);
	
	// Call the function.
	if(lua_pcall(L, 1, 0, 0)) {
		out("error") << "Lua exploded because (collidedWithWorld - " << getName() << ": " << lua_tostring(L, -1) << endl;
	}
	
	// Pop the table.
	lua_pop(L, 1);	
}

void ScriptEntity::die(void) {
	
	bool alreadyDead = dead;
	
	Entity::die();
	
	if(!alreadyDead) {
		lua_State *L = getWorld()->getLuaState();
		
		// Get the table from the global Entities table by name.
		pushTable();
		
		// Get the destruct() from that.
		lua_getfield(L, -1, "destruct");
		
		if(lua_isnil(L, -1)) {
			
			// Oh. No destruct function.
			lua_pop(L, 2);
			
		} else {
		
			// Copy the table back to the top (self).
			lua_pushvalue(L, -2);
			
			// Call the function.
			if(lua_pcall(L, 1, 0, 0)) {
				out("error") << "Lua exploded because (die - " << getName() << ": " << lua_tostring(L, -1) << endl;
			}
			
			// Pop the table.
			lua_pop(L, 1);	
		}		
	}
}

bool ScriptEntity::isSavable(void) {
	
	bool ret = false;
	
	lua_State *L = getWorld()->getLuaState();
	pushTable();
	
	lua_getfield(L, -1, "save");
	
	if(lua_isfunction(L, -1)) {
		ret = true;
	}
	
	lua_pop(L, 2); // Pop save and table.
	return ret;
}

bool ScriptEntity::getIsPermanent(void) {
	
	bool ret = false;
	
	lua_State *L = getWorld()->getLuaState();
	pushTable();
	
	lua_getfield(L, -1, "isPermanent");
	
	if(lua_isboolean(L, -1)) {
		ret = lua_toboolean(L, -1);
	}
	
	lua_pop(L, 2); // Pop isPermanent and table.
	return ret;
}
