Răsfoiți Sursa

Script reworking continued.

sgrenier 11 ani în urmă
părinte
comite
6842e8c924

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -232,6 +232,7 @@
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\SceneLoader.cpp" />
     <ClCompile Include="src\ScreenDisplayer.cpp" />
+    <ClCompile Include="src\Script.cpp" />
     <ClCompile Include="src\ScriptController.cpp" />
     <ClCompile Include="src\ScriptTarget.cpp" />
     <ClCompile Include="src\Slider.cpp" />
@@ -468,6 +469,7 @@
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\SceneLoader.h" />
     <ClInclude Include="src\ScreenDisplayer.h" />
+    <ClInclude Include="src\Script.h" />
     <ClInclude Include="src\ScriptController.h" />
     <ClInclude Include="src\ScriptTarget.h" />
     <ClInclude Include="src\Slider.h" />

+ 4 - 4
gameplay/gameplay.vcxproj.filters

@@ -726,8 +726,8 @@
     <ClCompile Include="src\lua\lua_JoystickControl.cpp">
       <Filter>src\lua</Filter>
     </ClCompile>
-    <ClCompile Include="src\lua\lua_ContainerDirection.cpp">
-      <Filter>src\lua</Filter>
+    <ClCompile Include="src\Script.cpp">
+      <Filter>src</Filter>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
@@ -1442,8 +1442,8 @@
     <ClInclude Include="src\lua\lua_JoystickControl.h">
       <Filter>src\lua</Filter>
     </ClInclude>
-    <ClInclude Include="src\lua\lua_ContainerDirection.h">
-      <Filter>src\lua</Filter>
+    <ClInclude Include="src\Script.h">
+      <Filter>src</Filter>
     </ClInclude>
   </ItemGroup>
   <ItemGroup>

+ 0 - 0
gameplay/src/Script.cpp


+ 92 - 0
gameplay/src/Script.h

@@ -0,0 +1,92 @@
+#ifndef SCRIPT_H_
+#define SCRIPT_H_
+
+#include "Base.h"
+#include "Ref.h"
+
+namespace gameplay
+{
+
+/**
+ * Represents a script that has been loaded by the scripting system.
+ */
+class Script : public Ref
+{
+    friend class ScriptController;
+
+public:
+
+    /**
+     * Defines the scope of a script environment.
+     */
+    enum Scope
+    {
+        /**
+         * Global scripts execute in a single shared environment that is accessible to
+         * all other scripts in the system. Global scripts can use code from other global
+         * scripts and other scripts can use code from a global script.
+         *
+         * Scripts that execute in the global environment must be careful to not override
+         * other variables or functions that already exist in the global environment (unless
+         * this is intended). Because of this, script naming conventions and the use of 
+         * unique namespaces is important to prevent naming collisions with other global code.
+         */
+        GLOBAL,
+
+        /**
+         * Private scripts execute in a more limited sandbox environment that by default does
+         * not  allow other scripts to see their variables or functions. Variables and functions
+         * in a private script can be named the same as those in other scripts without 
+         * collision issues.
+         *
+         * Although global code cannot access private scripts, private scripts can acceess
+         * global code. Similarly, private scripts can expose variables and functions to the
+         * global environment using explicit notation, although the same precautions noted
+         * for the GLOBAL scope should be used when doing this, to prevent naming collisions.
+         *
+         * PRIVATE_SHARED scripts can have at most once instance of the script loaded into
+         * memory, with all objects referencing the script sharing it. This means that shared
+         * scripts may execute for multiple objects and therefore do not have any instance 
+         * information available to them other than what is passed through function callbacks.
+         * Similarly, shared scripts should not attempt to store any per-instance state.
+         */
+        PRIVATE_SHARED,
+
+        /**
+         * PRIVATE_INSTANCE scripts provide the same sandboxed envrionment that PRIVATE_SHARED
+         * scripts do. However, there may be multiple instances of the same PRIVATE_INSTANCE
+         * script loaded into memory at the same time: one for each object that references it.
+         * Because of this, instance scripts can be used to store per-instance state, since it
+         * will not be shared by multiple instances.
+         *
+         * In some cases, such as when a script is loaded by a ScriptTarget object, the script
+         * may have direct access to the owning object by using the "this" keyword.
+         *
+         * @see ScriptTarget
+         */
+        PRIVATE_INSTANCE
+    };
+
+protected:
+
+    /**
+     * Copy constructor (hidden).
+     */
+    Script(const Script& copy);
+
+    /**
+     * Destructor.
+     */
+    ~Script();
+
+private:
+
+    std::string _path;
+    Scope _scope;
+    int _env;
+
+};
+
+}
+
+#endif SCRIPT_H_

+ 525 - 560
gameplay/src/ScriptController.cpp

@@ -153,329 +153,15 @@ static bool getNestedVariable(lua_State* lua, const char* name, int script = 0)
     return false;
 }
 
-void ScriptUtil::registerLibrary(const char* name, const luaL_Reg* functions)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-    lua_newtable(sc->_lua);
-
-    // Go through the list of functions and add them to the table.
-    const luaL_Reg* iter = functions;
-    for (; iter && iter->name; iter++)
-    {
-        lua_pushcfunction(sc->_lua, iter->func);
-        lua_setfield(sc->_lua, -2, iter->name);
-    }
-
-    lua_setglobal(sc->_lua, name);
-}
-
-void ScriptUtil::registerConstantBool(const std::string& name, bool value, const std::vector<std::string>& scopePath)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-
-    // If the constant is within a scope, get the correct parent 
-    // table on the stack before setting its value.
-    if (!scopePath.empty())
-    {
-        lua_getglobal(sc->_lua, scopePath[0].c_str());
-        for (unsigned int i = 1; i < scopePath.size(); i++)
-        {
-            lua_pushstring(sc->_lua, scopePath[i].c_str());
-            lua_gettable(sc->_lua, -2);
-        }
-        
-        // Add the constant to the parent table.
-        lua_pushboolean(sc->_lua, value);
-        lua_setfield(sc->_lua, -2, name.c_str());
-
-        // Pop all the parent tables off the stack.
-        int size = (int)scopePath.size();
-        lua_pop(sc->_lua, size);
-    }
-    else
-    {
-        // TODO: Currently unsupported (we don't parse for this yet).
-        // If the constant is global, add it to the global table.
-        lua_pushboolean(sc->_lua, value);
-        lua_pushvalue(sc->_lua, -1);
-        lua_setglobal(sc->_lua, name.c_str());
-    }
-}
-
-void ScriptUtil::registerConstantNumber(const std::string& name, double value, const std::vector<std::string>& scopePath)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-
-    // If the constant is within a scope, get the correct parent 
-    // table on the stack before setting its value.
-    if (!scopePath.empty())
-    {
-        lua_getglobal(sc->_lua, scopePath[0].c_str());
-        for (unsigned int i = 1; i < scopePath.size(); i++)
-        {
-            lua_pushstring(sc->_lua, scopePath[i].c_str());
-            lua_gettable(sc->_lua, -2);
-        }
-        
-        // Add the constant to the parent table.
-        lua_pushnumber(sc->_lua, value);
-        lua_setfield(sc->_lua, -2, name.c_str());
-
-        // Pop all the parent tables off the stack.
-        int size = (int)scopePath.size();
-        lua_pop(sc->_lua, size);
-    }
-    else
-    {
-        // TODO: Currently unsupported (we don't parse for this yet).
-        // If the constant is global, add it to the global table.
-        lua_pushnumber(sc->_lua, value);
-        lua_pushvalue(sc->_lua, -1);
-        lua_setglobal(sc->_lua, name.c_str());
-    }
-}
-
-void ScriptUtil::registerConstantString(const std::string& name, const std::string& value, const std::vector<std::string>& scopePath)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-
-    // If the constant is within a scope, get the correct parent 
-    // table on the stack before setting its value.
-    if (!scopePath.empty())
-    {
-        lua_getglobal(sc->_lua, scopePath[0].c_str());
-        for (unsigned int i = 1; i < scopePath.size(); i++)
-        {
-            lua_pushstring(sc->_lua, scopePath[i].c_str());
-            lua_gettable(sc->_lua, -2);
-        }
-        
-        // Add the constant to the parent table.
-        lua_pushstring(sc->_lua, value.c_str());
-        lua_setfield(sc->_lua, -2, name.c_str());
-
-        // Pop all the parent tables off the stack.
-        int size = (int)scopePath.size();
-        lua_pop(sc->_lua, size);
-    }
-    else
-    {
-        // TODO: Currently unsupported (we don't parse for this yet).
-        // If the constant is global, add it to the global table.
-        lua_pushstring(sc->_lua, value.c_str());
-        lua_pushvalue(sc->_lua, -1);
-        lua_setglobal(sc->_lua, name.c_str());
-    }
-}
-
-void ScriptUtil::registerEnumValue(int enumValue, const std::string& enumValueString, const std::vector<std::string>& scopePath)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-
-    // If the constant is within a scope, get the correct parent 
-    // table on the stack before setting its value.
-    if (!scopePath.empty())
-    {
-        lua_getglobal(sc->_lua, scopePath[0].c_str());
-        for (unsigned int i = 1; i < scopePath.size(); i++)
-        {
-            lua_pushstring(sc->_lua, scopePath[i].c_str());
-            lua_gettable(sc->_lua, -2);
-        }
-
-        // Add the enum value to the parent table.
-        lua_pushnumber(sc->_lua, enumValue);
-        lua_setfield(sc->_lua, -2, enumValueString.c_str());
-
-        // Pop all the parent tables off the stack.
-        int size = (int)scopePath.size();
-        lua_pop(sc->_lua, size);
-    }
-    else
-    {
-        // TODO: Currently unsupported (we don't parse for this yet).
-        // If the constant is global, add it to the global table.
-        lua_pushnumber(sc->_lua, enumValue);
-        lua_pushvalue(sc->_lua, -1);
-        lua_setglobal(sc->_lua, enumValueString.c_str());
-    }
-}
-
-void ScriptUtil::registerClass(const char* name, const luaL_Reg* members, lua_CFunction newFunction, 
-    lua_CFunction deleteFunction, const luaL_Reg* statics,  const std::vector<std::string>& scopePath)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-
-    // If the type is an inner type, get the correct parent 
-    // table on the stack before creating the table for the class.
-    if (!scopePath.empty())
-    {
-        std::string tablename = name;
-
-        // Strip off the scope path part of the name.
-        lua_getglobal(sc->_lua, scopePath[0].c_str());
-        std::size_t index = tablename.find(scopePath[0]);
-        if (index != std::string::npos)
-            tablename = tablename.substr(index + scopePath[0].size());
-        
-        for (unsigned int i = 1; i < scopePath.size(); i++)
-        {
-            lua_pushstring(sc->_lua, scopePath[i].c_str());
-            lua_gettable(sc->_lua, -2);
-
-            index = tablename.find(scopePath[i]);
-            if (index != std::string::npos)
-                tablename = tablename.substr(index + scopePath[i].size());
-        }
-
-        lua_pushstring(sc->_lua, tablename.c_str());
-        lua_newtable(sc->_lua);
-    }
-    else
-    {
-        // If the type is not an inner type, set it as a global table.
-        lua_newtable(sc->_lua);
-        lua_pushvalue(sc->_lua, -1);
-        lua_setglobal(sc->_lua, name);
-    }
-    
-    // Create the metatable and populate it with the member functions.
-    lua_pushliteral(sc->_lua, "__metatable");
-    luaL_newmetatable(sc->_lua, name);
-    if (members)
-        luaL_setfuncs(sc->_lua, members, 0);
-    lua_pushstring(sc->_lua, "__index");
-    lua_pushvalue(sc->_lua, -2);
-    lua_settable(sc->_lua, -3);
-
-    // Add the delete function if it was specified.
-    if (deleteFunction)
-    {
-        lua_pushstring(sc->_lua, "__gc");
-        lua_pushcfunction(sc->_lua, deleteFunction);
-        lua_settable(sc->_lua, -3);
-    }
-
-    // Set the metatable on the main table.
-    lua_settable(sc->_lua, -3);
-    
-    // Populate the main table with the static functions.
-    if (statics)
-        luaL_setfuncs(sc->_lua, statics, 0);
-
-    // Set the new function(s) for the class.
-    if (newFunction)
-    {
-        lua_pushliteral(sc->_lua, "new");
-        lua_pushcfunction(sc->_lua, newFunction);
-        lua_settable(sc->_lua, -3);
-    }
-
-    // Set the table we just created within the correct parent table.
-    if (!scopePath.empty())
-    {
-        lua_settable(sc->_lua, -3);
-
-        // Pop all the parent tables off the stack.
-        int size = (int)scopePath.size();
-        lua_pop(sc->_lua, size);
-    }
-    else
-    {
-        // Pop the main table off the stack.
-        lua_pop(sc->_lua, 1);
-    }
-}
-
-void ScriptUtil::registerFunction(const char* luaFunction, lua_CFunction cppFunction)
-{
-    lua_pushcfunction(Game::getInstance()->getScriptController()->_lua, cppFunction);
-    lua_setglobal(Game::getInstance()->getScriptController()->_lua, luaFunction);
-}
-
-void ScriptUtil::setGlobalHierarchyPair(const std::string& base, const std::string& derived)
-{
-    Game::getInstance()->getScriptController()->_hierarchy[base].push_back(derived);
-}
-
-ScriptUtil::LuaArray<bool> ScriptUtil::getBoolPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(bool, luaCheckBool);
-}
-
-ScriptUtil::LuaArray<short> ScriptUtil::getShortPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(short, (short)luaL_checkint);
-}
-
-ScriptUtil::LuaArray<int> ScriptUtil::getIntPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(int, (int)luaL_checkint);
-}
-
-ScriptUtil::LuaArray<long> ScriptUtil::getLongPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(long, (long)luaL_checkint);
-}
-
-ScriptUtil::LuaArray<unsigned char> ScriptUtil::getUnsignedCharPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(unsigned char, (unsigned char)luaL_checkunsigned);
-}
-
-ScriptUtil::LuaArray<unsigned short> ScriptUtil::getUnsignedShortPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(unsigned short, (unsigned short)luaL_checkunsigned);
-}
-
-ScriptUtil::LuaArray<unsigned int> ScriptUtil::getUnsignedIntPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(unsigned int, (unsigned int)luaL_checkunsigned);
-}
-
-ScriptUtil::LuaArray<unsigned long> ScriptUtil::getUnsignedLongPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(unsigned long, (unsigned long)luaL_checkunsigned);
-}
-
-ScriptUtil::LuaArray<float> ScriptUtil::getFloatPointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(float, (float)luaL_checknumber);
-}
-
-ScriptUtil::LuaArray<double> ScriptUtil::getDoublePointer(int index)
-{
-    GENERATE_LUA_GET_POINTER(double, (double)luaL_checknumber);
-}
-
-const char* ScriptUtil::getString(int index, bool isStdString)
+Script* ScriptController::loadScript(const char* path, Script::Scope scope, bool forceReload)
 {
-    if (lua_type(Game::getInstance()->getScriptController()->_lua, index) == LUA_TSTRING)
-        return luaL_checkstring(Game::getInstance()->getScriptController()->_lua, index);
-    else if (lua_type(Game::getInstance()->getScriptController()->_lua, index) == LUA_TNIL && !isStdString)
-        return NULL;
-    else
-    {
-        GP_ERROR("Invalid string parameter (index = %d).", index);
-        return NULL;
-    }
-}
+    GP_ASSERT(path);
 
-bool ScriptUtil::luaCheckBool(lua_State* state, int n)
-{
-    if (!lua_isboolean(state, n))
+    if (scope == Script::GLOBAL || scope == Script::PRIVATE_SHARED)
     {
-        const char* msg = lua_pushfstring(state, "%s expected, got %s", lua_typename(state, LUA_TBOOLEAN), luaL_typename(state, n));
-        luaL_argerror(state, n, msg);
-        return false;
+        // Check if a script with the same path is already loaded 
     }
-    return (lua_toboolean(state, n) != 0);
-}
-
 
-bool ScriptController::loadScript(const char* path, bool forceReload)
-{
-    GP_ASSERT(path);
     std::set<std::string>::iterator iter = _loadedScripts.find(path);
     if (iter == _loadedScripts.end() || forceReload)
     {
@@ -581,46 +267,6 @@ void ScriptController::unloadScript(int id)
     // Can we test this with manual GC and breaking on gameplay object constructors that were delcared local (even global??) to the script?
 }
 
-std::string ScriptController::loadUrl(const char* url, int* scriptId)
-{
-    GP_ASSERT(url);
-
-    int sid = 0;
-
-    bool isolated = url[0] == '+';
-
-    std::string script;
-    std::string func;
-    parseUrl(isolated ? url + 1 : url, &script, &func);
-
-    // Ensure the script is loaded
-    if (script.length() > 0)
-    {
-        if (isolated)
-            sid = Game::getInstance()->getScriptController()->loadScriptIsolated(script.c_str());
-        else
-            sid = Game::getInstance()->getScriptController()->loadScript(script.c_str()) ? 0 : -1;
-    }
-
-    if (scriptId)
-        *scriptId = sid;
-
-    // Return the function name.
-    return func;
-}
-
-void ScriptController::parseUrl(const char* url, std::string* script, std::string* function)
-{
-    std::string p1, p2;
-    splitURL(url, &p1, &p2);
-    if (function->length() == 0)
-    {
-        // The url doesn't reference a script, only a function
-        *function = *script;
-        *script = "";
-    }
-}
-
 bool ScriptController::getBool(const char* name, bool defaultValue, int script)
 {
     PUSH_NESTED_VARIABLE(name, defaultValue, script);
@@ -1159,309 +805,628 @@ void ScriptController::executeFunctionHelper(int resultCount, const char* func,
                 break;
             }
 
-            argumentCount++;
-            luaL_checkstack(_lua, 1, "Too many arguments.");
-        }
-    }
+            argumentCount++;
+            luaL_checkstack(_lua, 1, "Too many arguments.");
+        }
+    }
+
+    _envStack.push_back(script);
+
+    // Perform the function call.
+    if (lua_pcall(_lua, argumentCount, resultCount, 0) != 0)
+        GP_WARN("Failed to call function '%s' with error '%s'.", func, lua_tostring(_lua, -1));
+
+    _envStack.pop_back();
+}
+
+int ScriptController::convert(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if (lua_type(state, 1) == LUA_TUSERDATA && lua_type(state, 2) == LUA_TSTRING )
+            {
+                // Get parameter 2
+                const char* param2 = ScriptUtil::getString(2, false);
+                if (param2 != NULL)
+                {
+                    luaL_getmetatable(state, param2);
+                    lua_setmetatable(state, -3);
+                }
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_convert - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
+// Helper macros.
+#define SCRIPT_EXECUTE_FUNCTION_NO_PARAM(type, checkfunc) \
+    int top = lua_gettop(_lua); \
+    executeFunctionHelper(1, func, NULL, NULL); \
+    type value = (type)checkfunc(_lua, -1); \
+    lua_pop(_lua, -1); \
+    lua_settop(_lua, top); \
+    return value;
+
+#define SCRIPT_EXECUTE_FUNCTION_PARAM(type, checkfunc) \
+    int top = lua_gettop(_lua); \
+    va_list list; \
+    va_start(list, args); \
+    executeFunctionHelper(1, func, args, &list); \
+    type value = (type)checkfunc(_lua, -1); \
+    lua_pop(_lua, -1); \
+    va_end(list); \
+    lua_settop(_lua, top); \
+    return value;
+
+#define SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(type, checkfunc) \
+    int top = lua_gettop(_lua); \
+    executeFunctionHelper(1, func, args, list, script); \
+    type value = (type)checkfunc(_lua, -1); \
+    lua_pop(_lua, -1); \
+    lua_settop(_lua, top); \
+    return value;
+
+template<> void ScriptController::executeFunction<void>(const char* func)
+{
+    int top = lua_gettop(_lua);
+    executeFunctionHelper(0, func, NULL, NULL);
+    lua_settop(_lua, top);
+}
+
+template<> bool ScriptController::executeFunction<bool>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(bool, ScriptUtil::luaCheckBool);
+}
+
+template<> char ScriptController::executeFunction<char>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(char, luaL_checkint);
+}
+
+template<> short ScriptController::executeFunction<short>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(short, luaL_checkint);
+}
+
+template<> int ScriptController::executeFunction<int>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(int, luaL_checkint);
+}
+
+template<> long ScriptController::executeFunction<long>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(long, luaL_checklong);
+}
+
+template<> unsigned char ScriptController::executeFunction<unsigned char>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned char, luaL_checkunsigned);
+}
+
+template<> unsigned short ScriptController::executeFunction<unsigned short>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned short, luaL_checkunsigned);
+}
+
+template<> unsigned int ScriptController::executeFunction<unsigned int>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned int, luaL_checkunsigned);
+}
+
+template<> unsigned long ScriptController::executeFunction<unsigned long>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned long, luaL_checkunsigned);
+}
+
+template<> float ScriptController::executeFunction<float>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(float, luaL_checknumber);
+}
+
+template<> double ScriptController::executeFunction<double>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(double, luaL_checknumber);
+}
+
+template<> std::string ScriptController::executeFunction<std::string>(const char* func)
+{
+    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(std::string, luaL_checkstring);
+}
+
+/** Template specialization. */
+template<> void ScriptController::executeFunction<void>(const char* func, const char* args, ...)
+{
+    int top = lua_gettop(_lua);
+    va_list list;
+    va_start(list, args);
+    executeFunctionHelper(0, func, args, &list);
+    va_end(list);
+    lua_settop(_lua, top);
+}
+
+/** Template specialization. */
+template<> bool ScriptController::executeFunction<bool>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(bool, ScriptUtil::luaCheckBool);
+}
+
+/** Template specialization. */
+template<> char ScriptController::executeFunction<char>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(char, luaL_checkint);
+}
+
+/** Template specialization. */
+template<> short ScriptController::executeFunction<short>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(short, luaL_checkint);
+}
+
+/** Template specialization. */
+template<> int ScriptController::executeFunction<int>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(int, luaL_checkint);
+}
+
+/** Template specialization. */
+template<> long ScriptController::executeFunction<long>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(long, luaL_checklong);
+}
+
+/** Template specialization. */
+template<> unsigned char ScriptController::executeFunction<unsigned char>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned char, luaL_checkunsigned);
+}
+
+/** Template specialization. */
+template<> unsigned short ScriptController::executeFunction<unsigned short>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned short, luaL_checkunsigned);
+}
+
+/** Template specialization. */
+template<> unsigned int ScriptController::executeFunction<unsigned int>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned int, luaL_checkunsigned);
+}
+
+/** Template specialization. */
+template<> unsigned long ScriptController::executeFunction<unsigned long>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned long, luaL_checkunsigned);
+}
+
+/** Template specialization. */
+template<> float ScriptController::executeFunction<float>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(float, luaL_checknumber);
+}
 
-    _envStack.push_back(script);
+/** Template specialization. */
+template<> double ScriptController::executeFunction<double>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(double, luaL_checknumber);
+}
 
-    // Perform the function call.
-    if (lua_pcall(_lua, argumentCount, resultCount, 0) != 0)
-        GP_WARN("Failed to call function '%s' with error '%s'.", func, lua_tostring(_lua, -1));
+/** Template specialization. */
+template<> std::string ScriptController::executeFunction<std::string>(const char* func, const char* args, ...)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM(std::string, luaL_checkstring);
+}
 
-    _envStack.pop_back();
+/** Template specialization. */
+template<> void ScriptController::executeFunction<void>(const char* func, const char* args, va_list* list, int script)
+{
+    executeFunctionHelper(0, func, args, list, script);
 }
 
-int ScriptController::convert(lua_State* state)
+/** Template specialization. */
+template<> bool ScriptController::executeFunction<bool>(const char* func, const char* args, va_list* list, int script)
 {
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 2:
-        {
-            if (lua_type(state, 1) == LUA_TUSERDATA && lua_type(state, 2) == LUA_TSTRING )
-            {
-                // Get parameter 2
-                const char* param2 = ScriptUtil::getString(2, false);
-                if (param2 != NULL)
-                {
-                    luaL_getmetatable(state, param2);
-                    lua_setmetatable(state, -3);
-                }
-                return 0;
-            }
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(bool, ScriptUtil::luaCheckBool);
+}
 
-            lua_pushstring(state, "lua_convert - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 2).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
+/** Template specialization. */
+template<> char ScriptController::executeFunction<char>(const char* func, const char* args, va_list* list, int script)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(char, luaL_checkint);
 }
 
-// Helper macros.
-#define SCRIPT_EXECUTE_FUNCTION_NO_PARAM(type, checkfunc) \
-    int top = lua_gettop(_lua); \
-    executeFunctionHelper(1, func, NULL, NULL); \
-    type value = (type)checkfunc(_lua, -1); \
-    lua_pop(_lua, -1); \
-    lua_settop(_lua, top); \
-    return value;
+/** Template specialization. */
+template<> short ScriptController::executeFunction<short>(const char* func, const char* args, va_list* list, int script)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(short, luaL_checkint);
+}
 
-#define SCRIPT_EXECUTE_FUNCTION_PARAM(type, checkfunc) \
-    int top = lua_gettop(_lua); \
-    va_list list; \
-    va_start(list, args); \
-    executeFunctionHelper(1, func, args, &list); \
-    type value = (type)checkfunc(_lua, -1); \
-    lua_pop(_lua, -1); \
-    va_end(list); \
-    lua_settop(_lua, top); \
-    return value;
+/** Template specialization. */
+template<> int ScriptController::executeFunction<int>(const char* func, const char* args, va_list* list, int script)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(int, luaL_checkint);
+}
 
-#define SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(type, checkfunc) \
-    int top = lua_gettop(_lua); \
-    executeFunctionHelper(1, func, args, list, script); \
-    type value = (type)checkfunc(_lua, -1); \
-    lua_pop(_lua, -1); \
-    lua_settop(_lua, top); \
-    return value;
+/** Template specialization. */
+template<> long ScriptController::executeFunction<long>(const char* func, const char* args, va_list* list, int script)
+{
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(long, luaL_checklong);
+}
 
-template<> void ScriptController::executeFunction<void>(const char* func)
+/** Template specialization. */
+template<> unsigned char ScriptController::executeFunction<unsigned char>(const char* func, const char* args, va_list* list, int script)
 {
-    int top = lua_gettop(_lua);
-    executeFunctionHelper(0, func, NULL, NULL);
-    lua_settop(_lua, top);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned char, luaL_checkunsigned);
 }
 
-template<> bool ScriptController::executeFunction<bool>(const char* func)
+/** Template specialization. */
+template<> unsigned short ScriptController::executeFunction<unsigned short>(const char* func, const char* args, va_list* list, int script)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(bool, ScriptUtil::luaCheckBool);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned short, luaL_checkunsigned);
 }
 
-template<> char ScriptController::executeFunction<char>(const char* func)
+/** Template specialization. */
+template<> unsigned int ScriptController::executeFunction<unsigned int>(const char* func, const char* args, va_list* list, int script)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(char, luaL_checkint);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned int, luaL_checkunsigned);
 }
 
-template<> short ScriptController::executeFunction<short>(const char* func)
+/** Template specialization. */
+template<> unsigned long ScriptController::executeFunction<unsigned long>(const char* func, const char* args, va_list* list, int script)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(short, luaL_checkint);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned long, luaL_checkunsigned);
 }
 
-template<> int ScriptController::executeFunction<int>(const char* func)
+/** Template specialization. */
+template<> float ScriptController::executeFunction<float>(const char* func, const char* args, va_list* list, int script)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(int, luaL_checkint);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(float, luaL_checknumber);
 }
 
-template<> long ScriptController::executeFunction<long>(const char* func)
+/** Template specialization. */
+template<> double ScriptController::executeFunction<double>(const char* func, const char* args, va_list* list, int script)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(long, luaL_checklong);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(double, luaL_checknumber);
 }
 
-template<> unsigned char ScriptController::executeFunction<unsigned char>(const char* func)
+/** Template specialization. */
+template<> std::string ScriptController::executeFunction<std::string>(const char* func, const char* args, va_list* list, int script)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned char, luaL_checkunsigned);
+    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(std::string, luaL_checkstring);
 }
 
-template<> unsigned short ScriptController::executeFunction<unsigned short>(const char* func)
+void ScriptUtil::registerLibrary(const char* name, const luaL_Reg* functions)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned short, luaL_checkunsigned);
+    ScriptController* sc = Game::getInstance()->getScriptController();
+    lua_newtable(sc->_lua);
+
+    // Go through the list of functions and add them to the table.
+    const luaL_Reg* iter = functions;
+    for (; iter && iter->name; iter++)
+    {
+        lua_pushcfunction(sc->_lua, iter->func);
+        lua_setfield(sc->_lua, -2, iter->name);
+    }
+
+    lua_setglobal(sc->_lua, name);
 }
 
-template<> unsigned int ScriptController::executeFunction<unsigned int>(const char* func)
+void ScriptUtil::registerConstantBool(const std::string& name, bool value, const std::vector<std::string>& scopePath)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned int, luaL_checkunsigned);
+    ScriptController* sc = Game::getInstance()->getScriptController();
+
+    // If the constant is within a scope, get the correct parent 
+    // table on the stack before setting its value.
+    if (!scopePath.empty())
+    {
+        lua_getglobal(sc->_lua, scopePath[0].c_str());
+        for (unsigned int i = 1; i < scopePath.size(); i++)
+        {
+            lua_pushstring(sc->_lua, scopePath[i].c_str());
+            lua_gettable(sc->_lua, -2);
+        }
+
+        // Add the constant to the parent table.
+        lua_pushboolean(sc->_lua, value);
+        lua_setfield(sc->_lua, -2, name.c_str());
+
+        // Pop all the parent tables off the stack.
+        int size = (int)scopePath.size();
+        lua_pop(sc->_lua, size);
+    }
+    else
+    {
+        // TODO: Currently unsupported (we don't parse for this yet).
+        // If the constant is global, add it to the global table.
+        lua_pushboolean(sc->_lua, value);
+        lua_pushvalue(sc->_lua, -1);
+        lua_setglobal(sc->_lua, name.c_str());
+    }
 }
 
-template<> unsigned long ScriptController::executeFunction<unsigned long>(const char* func)
+void ScriptUtil::registerConstantNumber(const std::string& name, double value, const std::vector<std::string>& scopePath)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(unsigned long, luaL_checkunsigned);
+    ScriptController* sc = Game::getInstance()->getScriptController();
+
+    // If the constant is within a scope, get the correct parent 
+    // table on the stack before setting its value.
+    if (!scopePath.empty())
+    {
+        lua_getglobal(sc->_lua, scopePath[0].c_str());
+        for (unsigned int i = 1; i < scopePath.size(); i++)
+        {
+            lua_pushstring(sc->_lua, scopePath[i].c_str());
+            lua_gettable(sc->_lua, -2);
+        }
+
+        // Add the constant to the parent table.
+        lua_pushnumber(sc->_lua, value);
+        lua_setfield(sc->_lua, -2, name.c_str());
+
+        // Pop all the parent tables off the stack.
+        int size = (int)scopePath.size();
+        lua_pop(sc->_lua, size);
+    }
+    else
+    {
+        // TODO: Currently unsupported (we don't parse for this yet).
+        // If the constant is global, add it to the global table.
+        lua_pushnumber(sc->_lua, value);
+        lua_pushvalue(sc->_lua, -1);
+        lua_setglobal(sc->_lua, name.c_str());
+    }
+}
+
+void ScriptUtil::registerConstantString(const std::string& name, const std::string& value, const std::vector<std::string>& scopePath)
+{
+    ScriptController* sc = Game::getInstance()->getScriptController();
+
+    // If the constant is within a scope, get the correct parent 
+    // table on the stack before setting its value.
+    if (!scopePath.empty())
+    {
+        lua_getglobal(sc->_lua, scopePath[0].c_str());
+        for (unsigned int i = 1; i < scopePath.size(); i++)
+        {
+            lua_pushstring(sc->_lua, scopePath[i].c_str());
+            lua_gettable(sc->_lua, -2);
+        }
+
+        // Add the constant to the parent table.
+        lua_pushstring(sc->_lua, value.c_str());
+        lua_setfield(sc->_lua, -2, name.c_str());
+
+        // Pop all the parent tables off the stack.
+        int size = (int)scopePath.size();
+        lua_pop(sc->_lua, size);
+    }
+    else
+    {
+        // TODO: Currently unsupported (we don't parse for this yet).
+        // If the constant is global, add it to the global table.
+        lua_pushstring(sc->_lua, value.c_str());
+        lua_pushvalue(sc->_lua, -1);
+        lua_setglobal(sc->_lua, name.c_str());
+    }
 }
 
-template<> float ScriptController::executeFunction<float>(const char* func)
+void ScriptUtil::registerEnumValue(int enumValue, const std::string& enumValueString, const std::vector<std::string>& scopePath)
 {
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(float, luaL_checknumber);
-}
+    ScriptController* sc = Game::getInstance()->getScriptController();
 
-template<> double ScriptController::executeFunction<double>(const char* func)
-{
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(double, luaL_checknumber);
-}
+    // If the constant is within a scope, get the correct parent 
+    // table on the stack before setting its value.
+    if (!scopePath.empty())
+    {
+        lua_getglobal(sc->_lua, scopePath[0].c_str());
+        for (unsigned int i = 1; i < scopePath.size(); i++)
+        {
+            lua_pushstring(sc->_lua, scopePath[i].c_str());
+            lua_gettable(sc->_lua, -2);
+        }
 
-template<> std::string ScriptController::executeFunction<std::string>(const char* func)
-{
-    SCRIPT_EXECUTE_FUNCTION_NO_PARAM(std::string, luaL_checkstring);
-}
+        // Add the enum value to the parent table.
+        lua_pushnumber(sc->_lua, enumValue);
+        lua_setfield(sc->_lua, -2, enumValueString.c_str());
 
-/** Template specialization. */
-template<> void ScriptController::executeFunction<void>(const char* func, const char* args, ...)
-{
-    int top = lua_gettop(_lua);
-    va_list list;
-    va_start(list, args);
-    executeFunctionHelper(0, func, args, &list);
-    va_end(list);
-    lua_settop(_lua, top);
+        // Pop all the parent tables off the stack.
+        int size = (int)scopePath.size();
+        lua_pop(sc->_lua, size);
+    }
+    else
+    {
+        // TODO: Currently unsupported (we don't parse for this yet).
+        // If the constant is global, add it to the global table.
+        lua_pushnumber(sc->_lua, enumValue);
+        lua_pushvalue(sc->_lua, -1);
+        lua_setglobal(sc->_lua, enumValueString.c_str());
+    }
 }
 
-/** Template specialization. */
-template<> bool ScriptController::executeFunction<bool>(const char* func, const char* args, ...)
+void ScriptUtil::registerClass(const char* name, const luaL_Reg* members, lua_CFunction newFunction,
+    lua_CFunction deleteFunction, const luaL_Reg* statics, const std::vector<std::string>& scopePath)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM(bool, ScriptUtil::luaCheckBool);
-}
+    ScriptController* sc = Game::getInstance()->getScriptController();
 
-/** Template specialization. */
-template<> char ScriptController::executeFunction<char>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(char, luaL_checkint);
-}
+    // If the type is an inner type, get the correct parent 
+    // table on the stack before creating the table for the class.
+    if (!scopePath.empty())
+    {
+        std::string tablename = name;
 
-/** Template specialization. */
-template<> short ScriptController::executeFunction<short>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(short, luaL_checkint);
-}
+        // Strip off the scope path part of the name.
+        lua_getglobal(sc->_lua, scopePath[0].c_str());
+        std::size_t index = tablename.find(scopePath[0]);
+        if (index != std::string::npos)
+            tablename = tablename.substr(index + scopePath[0].size());
 
-/** Template specialization. */
-template<> int ScriptController::executeFunction<int>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(int, luaL_checkint);
-}
+        for (unsigned int i = 1; i < scopePath.size(); i++)
+        {
+            lua_pushstring(sc->_lua, scopePath[i].c_str());
+            lua_gettable(sc->_lua, -2);
 
-/** Template specialization. */
-template<> long ScriptController::executeFunction<long>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(long, luaL_checklong);
-}
+            index = tablename.find(scopePath[i]);
+            if (index != std::string::npos)
+                tablename = tablename.substr(index + scopePath[i].size());
+        }
 
-/** Template specialization. */
-template<> unsigned char ScriptController::executeFunction<unsigned char>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned char, luaL_checkunsigned);
-}
+        lua_pushstring(sc->_lua, tablename.c_str());
+        lua_newtable(sc->_lua);
+    }
+    else
+    {
+        // If the type is not an inner type, set it as a global table.
+        lua_newtable(sc->_lua);
+        lua_pushvalue(sc->_lua, -1);
+        lua_setglobal(sc->_lua, name);
+    }
 
-/** Template specialization. */
-template<> unsigned short ScriptController::executeFunction<unsigned short>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned short, luaL_checkunsigned);
-}
+    // Create the metatable and populate it with the member functions.
+    lua_pushliteral(sc->_lua, "__metatable");
+    luaL_newmetatable(sc->_lua, name);
+    if (members)
+        luaL_setfuncs(sc->_lua, members, 0);
+    lua_pushstring(sc->_lua, "__index");
+    lua_pushvalue(sc->_lua, -2);
+    lua_settable(sc->_lua, -3);
 
-/** Template specialization. */
-template<> unsigned int ScriptController::executeFunction<unsigned int>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned int, luaL_checkunsigned);
-}
+    // Add the delete function if it was specified.
+    if (deleteFunction)
+    {
+        lua_pushstring(sc->_lua, "__gc");
+        lua_pushcfunction(sc->_lua, deleteFunction);
+        lua_settable(sc->_lua, -3);
+    }
 
-/** Template specialization. */
-template<> unsigned long ScriptController::executeFunction<unsigned long>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(unsigned long, luaL_checkunsigned);
-}
+    // Set the metatable on the main table.
+    lua_settable(sc->_lua, -3);
 
-/** Template specialization. */
-template<> float ScriptController::executeFunction<float>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(float, luaL_checknumber);
-}
+    // Populate the main table with the static functions.
+    if (statics)
+        luaL_setfuncs(sc->_lua, statics, 0);
 
-/** Template specialization. */
-template<> double ScriptController::executeFunction<double>(const char* func, const char* args, ...)
-{
-    SCRIPT_EXECUTE_FUNCTION_PARAM(double, luaL_checknumber);
+    // Set the new function(s) for the class.
+    if (newFunction)
+    {
+        lua_pushliteral(sc->_lua, "new");
+        lua_pushcfunction(sc->_lua, newFunction);
+        lua_settable(sc->_lua, -3);
+    }
+
+    // Set the table we just created within the correct parent table.
+    if (!scopePath.empty())
+    {
+        lua_settable(sc->_lua, -3);
+
+        // Pop all the parent tables off the stack.
+        int size = (int)scopePath.size();
+        lua_pop(sc->_lua, size);
+    }
+    else
+    {
+        // Pop the main table off the stack.
+        lua_pop(sc->_lua, 1);
+    }
 }
 
-/** Template specialization. */
-template<> std::string ScriptController::executeFunction<std::string>(const char* func, const char* args, ...)
+void ScriptUtil::registerFunction(const char* luaFunction, lua_CFunction cppFunction)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM(std::string, luaL_checkstring);
+    lua_pushcfunction(Game::getInstance()->getScriptController()->_lua, cppFunction);
+    lua_setglobal(Game::getInstance()->getScriptController()->_lua, luaFunction);
 }
 
-/** Template specialization. */
-template<> void ScriptController::executeFunction<void>(const char* func, const char* args, va_list* list, int script)
+void ScriptUtil::setGlobalHierarchyPair(const std::string& base, const std::string& derived)
 {
-    executeFunctionHelper(0, func, args, list, script);
+    Game::getInstance()->getScriptController()->_hierarchy[base].push_back(derived);
 }
 
-/** Template specialization. */
-template<> bool ScriptController::executeFunction<bool>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<bool> ScriptUtil::getBoolPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(bool, ScriptUtil::luaCheckBool);
+    GENERATE_LUA_GET_POINTER(bool, luaCheckBool);
 }
 
-/** Template specialization. */
-template<> char ScriptController::executeFunction<char>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<short> ScriptUtil::getShortPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(char, luaL_checkint);
+    GENERATE_LUA_GET_POINTER(short, (short)luaL_checkint);
 }
 
-/** Template specialization. */
-template<> short ScriptController::executeFunction<short>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<int> ScriptUtil::getIntPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(short, luaL_checkint);
+    GENERATE_LUA_GET_POINTER(int, (int)luaL_checkint);
 }
 
-/** Template specialization. */
-template<> int ScriptController::executeFunction<int>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<long> ScriptUtil::getLongPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(int, luaL_checkint);
+    GENERATE_LUA_GET_POINTER(long, (long)luaL_checkint);
 }
 
-/** Template specialization. */
-template<> long ScriptController::executeFunction<long>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<unsigned char> ScriptUtil::getUnsignedCharPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(long, luaL_checklong);
+    GENERATE_LUA_GET_POINTER(unsigned char, (unsigned char)luaL_checkunsigned);
 }
 
-/** Template specialization. */
-template<> unsigned char ScriptController::executeFunction<unsigned char>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<unsigned short> ScriptUtil::getUnsignedShortPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned char, luaL_checkunsigned);
+    GENERATE_LUA_GET_POINTER(unsigned short, (unsigned short)luaL_checkunsigned);
 }
 
-/** Template specialization. */
-template<> unsigned short ScriptController::executeFunction<unsigned short>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<unsigned int> ScriptUtil::getUnsignedIntPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned short, luaL_checkunsigned);
+    GENERATE_LUA_GET_POINTER(unsigned int, (unsigned int)luaL_checkunsigned);
 }
 
-/** Template specialization. */
-template<> unsigned int ScriptController::executeFunction<unsigned int>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<unsigned long> ScriptUtil::getUnsignedLongPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned int, luaL_checkunsigned);
+    GENERATE_LUA_GET_POINTER(unsigned long, (unsigned long)luaL_checkunsigned);
 }
 
-/** Template specialization. */
-template<> unsigned long ScriptController::executeFunction<unsigned long>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<float> ScriptUtil::getFloatPointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(unsigned long, luaL_checkunsigned);
+    GENERATE_LUA_GET_POINTER(float, (float)luaL_checknumber);
 }
 
-/** Template specialization. */
-template<> float ScriptController::executeFunction<float>(const char* func, const char* args, va_list* list, int script)
+ScriptUtil::LuaArray<double> ScriptUtil::getDoublePointer(int index)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(float, luaL_checknumber);
+    GENERATE_LUA_GET_POINTER(double, (double)luaL_checknumber);
 }
 
-/** Template specialization. */
-template<> double ScriptController::executeFunction<double>(const char* func, const char* args, va_list* list, int script)
+const char* ScriptUtil::getString(int index, bool isStdString)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(double, luaL_checknumber);
+    if (lua_type(Game::getInstance()->getScriptController()->_lua, index) == LUA_TSTRING)
+        return luaL_checkstring(Game::getInstance()->getScriptController()->_lua, index);
+    else if (lua_type(Game::getInstance()->getScriptController()->_lua, index) == LUA_TNIL && !isStdString)
+        return NULL;
+    else
+    {
+        GP_ERROR("Invalid string parameter (index = %d).", index);
+        return NULL;
+    }
 }
 
-/** Template specialization. */
-template<> std::string ScriptController::executeFunction<std::string>(const char* func, const char* args, va_list* list, int script)
+bool ScriptUtil::luaCheckBool(lua_State* state, int n)
 {
-    SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(std::string, luaL_checkstring);
+    if (!lua_isboolean(state, n))
+    {
+        const char* msg = lua_pushfstring(state, "%s expected, got %s", lua_typename(state, LUA_TBOOLEAN), luaL_typename(state, n));
+        luaL_argerror(state, n, msg);
+        return false;
+    }
+    return (lua_toboolean(state, n) != 0);
 }
 
 }

+ 322 - 417
gameplay/src/ScriptController.h

@@ -1,344 +1,13 @@
 #ifndef SCRIPTCONTROLLER_H_
 #define SCRIPTCONTROLLER_H_
 
-#include "Base.h"
+#include "Script.h"
 #include "Game.h"
 #include "Control.h"
 
 namespace gameplay
 {
 
-/**
- * Functions and structures used by the generated Lua script bindings.
- */
-namespace ScriptUtil
-{
-
-/**
- * Represents a C++ object from within Lua.
- * 
- * @script{ignore}
- */
-struct LuaObject
-{
-    /** The actual object instance. */
-    void* instance;
-    /** Whether object is owned by Lua. */
-    bool owns;
-};
-
-/**
- * Stores a Lua parameter of an array/pointer type that is passed from Lua to C.
- * Handles automatic cleanup of any temporary memory associated with the array.
- * 
- * @script{ignore}
- */
-template <typename T>
-class LuaArray
-{
-public:
-
-    /**
-     * Creates a LuaArray to store a single pointer value.
-     */
-    LuaArray(T* param);
-
-    /**
-     * Allocates a LuaArray to store an array of values.
-     *
-     * Individual items in the array can be set using the 
-     * set(unsigned int, const T&) method.
-     * 
-     * @param count Number of elements to store in the parameter.
-     */
-    LuaArray(int count);
-
-    /**
-     * Copy constructor.
-     */
-    LuaArray(const LuaArray<T>& copy);
-
-    /**
-     * Destructor.
-     */
-    ~LuaArray();
-
-    /**
-     * Assignment operator.
-     */
-    LuaArray<T>& operator = (const LuaArray<T>& p);
-
-    /**
-     * Copies the value of the object pointed to by itemPtr into the specified
-     * index of this LuaArray's array.
-     */
-    void set(unsigned int index, const T* itemPtr);
-
-    /**
-     * Conversion operator from LuaArray to T*.
-     */
-    operator T* () const;
-
-    /**
-     * Overloads [] operator to get/set item value at index.
-     */
-    T& operator[] (int index);
-
-private:
-
-    struct Data
-    {
-        Data() : value(NULL), refCount(0) { }
-        T* value;
-        int refCount;
-    };
-
-    Data* _data;
-};
-
-/**
- * Registers the given library with Lua.
- * 
- * @param name The name of the library from within Lua.
- * @param functions The library function mapping (Lua function names to C++ functions).
- * 
- * @script{ignore}
- */
-void registerLibrary(const char* name, const luaL_Reg* functions);
-
-/**
- * Registers the given boolean constant as valid for the given scope path.
- * 
- * @param name The name of the constant (what the user would use from Lua).
- * @param value The constant's value.
- * @param scopePath The list of containing classes, going inward from the most outer class.
- * 
- * @script{ignore}
- */
-void registerConstantBool(const std::string& name, bool value, const std::vector<std::string>& scopePath);
-
-/**
- * Registers the given number constant as valid for the given scope path.
- * 
- * @param name The name of the constant (what the user would use from Lua).
- * @param value The constant's value.
- * @param scopePath The list of containing classes, going inward from the most outer class.
- * 
- * @script{ignore}
- */
-void registerConstantNumber(const std::string& name, double value, const std::vector<std::string>& scopePath);
-
-/**
- * Registers the given string constant as valid for the given scope path.
- * 
- * @param name The name of the constant (what the user would use from Lua).
- * @param value The constant's value.
- * @param scopePath The list of containing classes, going inward from the most outer class.
- * 
- * @script{ignore}
- */
-void registerConstantString(const std::string& name, const std::string& value, const std::vector<std::string>& scopePath);
-
-/**
- * Registers the given enum value as valid for the given scope path.
- *
- * @param enumValue The enumeration value, expressed as an integer.
- * @param enumValueString The string representation of the enum value (what the user would use from Lua).
- * @param scopePath The list of containing classes, going inward from the most outer class.
- *
- * @script{ignore}
- */
-void registerEnumValue(int enumValue, const std::string& enumValueString, const std::vector<std::string>& scopePath);
-
-/**
- * Registers the given class type with Lua.
- * 
- * @param name The name of the class from within Lua.
- * @param members The library function mapping for all the member functions (Lua function names to C++ functions).
- * @param newFunction The function to call that creates an instance of the class.
- * @param deleteFunction The function to call that destroys an instance of the class.
- * @param statics The library function mapping for all the static functions (Lua function names to C++ functions).
- * @param scopePath For an inner class, this is a list of its containing classes, going inward from the most outer class.
- * 
- * @script{ignore}
- */
-void registerClass(const char* name, const luaL_Reg* members, lua_CFunction newFunction, lua_CFunction deleteFunction, const luaL_Reg* statics,
-                   const std::vector<std::string>& scopePath);
-
-/**
- * Register a function with Lua.
- * 
- * @param luaFunction The name of the function from within Lua.
- * @param cppFunction The C++ function pointer.
- * 
- * @script{ignore}
- */
-void registerFunction(const char* luaFunction, lua_CFunction cppFunction);
-
-/**
- * Sets an inheritance pair within the global inheritance hierarchy (base, derived).
- * 
- * @param base The base class of the inheritance pair.
- * @param derived The derived class of the inheritance pair.
- * 
- * @script{ignore}
- */
-void setGlobalHierarchyPair(const std::string& base, const std::string& derived);
-
-/**
- * Gets a pointer to a bool (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<bool> getBoolPointer(int index);
-
-/**
- * Gets a pointer to a short (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<short> getShortPointer(int index);
-
-/**
- * Gets a pointer to an int (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<int> getIntPointer(int index);
-
-/**
- * Gets a pointer to a long (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<long> getLongPointer(int index);
-
-/**
- * Gets a pointer to an unsigned char (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<unsigned char> getUnsignedCharPointer(int index);
-
-/**
- * Gets a pointer to an unsigned short (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<unsigned short> getUnsignedShortPointer(int index);
-
-/**
- * Gets a pointer to an unsigned int (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<unsigned int> getUnsignedIntPointer(int index);
-
-/**
- * Gets a pointer to an unsigned long (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<unsigned long> getUnsignedLongPointer(int index);
-
-/**
- * Gets a pointer to a float (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<float> getFloatPointer(int index);
-
-/**
- * Gets a pointer to a double (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
- * 
- * @param index The stack index.
- * 
- * @return The pointer.
- * 
- * @script{ignore}
- */
-LuaArray<double> getDoublePointer(int index);
-
-/**
- * Gets an object pointer of the given type for the given stack index.
- * 
- * @param type The type of object pointer to retrieve.
- * @param index The stack index.
- * @param nonNull Whether the pointer must be non-null (e.g. if the parameter we 
- *      are retrieving is actually a reference or by-value parameter).
- * @param success An out parameter that is set to true if the Lua parameter was successfully
- *      converted to a valid object, or false if it was unable to perform a valid conversion.
- * 
- * @return The object pointer or <code>NULL</code> if the data at the stack index
- *      is not an object or if the object is not derived from the given type.
- * 
- * @script{ignore}
- */
-template <typename T>
-LuaArray<T> getObjectPointer(int index, const char* type, bool nonNull, bool* success);
-
-/**
- * Gets a string for the given stack index.
- * 
- * @param index The stack index.
- * @param isStdString Whether the string being retrieved is a std::string object or not.
- * 
- * @return The string or <code>NULL</code>.
- * 
- * @script{ignore}
- */
-const char* getString(int index, bool isStdString);
-
-/**
- * Checks that the parameter at the given stack position is a boolean and returns it.
- * 
- * @param state The Lua state.
- * @param n The stack index.
- * 
- * @return The boolean (if successful; otherwise it logs an error).
- * 
- * @script{ignore}
- */
-bool luaCheckBool(lua_State* state, int n);
-
-}
-
 /**
  * Controls and manages all scripts.
  */
@@ -346,77 +15,28 @@ class ScriptController
 {
     friend class Game;
     friend class Platform;
+    friend class ScriptUtil;
 
 public:
 
     /**
-     * Loads the given script file and executes its global code.
-     *
-     * The script is loaded into the global script environment, where any
-     * non-local functions and variables in the script will override those
-     * in the global environment.
+     * Loads the given script file and executes its code (if it is not
+     * alreay loaded).
      *
-     * Scripts loaded into the global environment cannot be explicitly unloaded.
-     * Once all data within the script is no longer referenced, it will be
-     * garbage collected through normal means.
+     * The script is loaded into an environment that is defined by the scope 
+     * parameter. If the script scope is GLOBAL or PRIVATE_SHARED and if the
+     * forceReload parameter is false, a previously-loaded script object may
+     * be returned. PRIVATE_INSTANCE scope always results in a new script being
+     * loaded and executed.
      * 
      * @param path The path to the script.
-     * @param forceReload Whether the script should be reloaded if it has already been loaded.
-     */
-    bool loadScript(const char* path, bool forceReload = false);
-
-    /**
-     * Loads the given script into its own isolated environment and executes its code.
-     *
-     * The script is loaded into a new isolated script environment that is separate from
-     * the global environment. Non-local functions and variables do not overlap with
-     * those in the global table. However, the script does still have access to functions
-     * and variables in the global table (unless they are re-declared in the script).
-     *
-     * Since this function installs the script into an isolated envrionment, the 
-     * script is guaranteed to be newly loaded (even if the same script was previously
-     * loaded) and any global code within it is executed.
+     * @param scope The scope for the script to be executed in.
+     * @param forceReload Whether the script should be reloaded if it has already been loaded
+     *      (applicable for GLOBAL and PRIVATE_SHARED scripts only).
      *
-     * The script may later be unloaded by calling unloadScript(int).
-     *
-     * @param path The path of the script.
-     * @return A unique ID for the script, which may be passed to unloadScript(int),
-     *      or -1 if the script could not be loaded.
+     * @return The loaded script, or NULL if the script could not be loaded.
      */
-    int loadScriptIsolated(const char* path);
-
-    /**
-     * Attempts to unload an isolated script with the given ID.
-     *
-     * Unloading a script causes the non-global data within the script to be
-     * released such that a reference is no longer held to it. It will then
-     * be garbage collected at some point in the future. If the script stored
-     * any data in the global environment or if any other code still references
-     * data from the script, that data will not be freed.
-     *
-     * @param id The unique ID of the isolated script, as returned from loadScriptIsolated(const char*).
-     */
-    void unloadScript(int id);
-
-    /**
-     * Given a URL, loads the referenced file and returns the referenced function name.
-     * 
-     * @param url The url to load.
-     * @param scriptId Optional integer pointer that is populated with 
-     * @return The function that the URL references.
-     */
-    std::string loadUrl(const char* url, int* scriptId = NULL);
-
-    /**
-     * Parses the given url into a separate script filename and function name.
-     *
-     * @param url Url to parse.
-     * @param script Populated with the path of the script, or an empty string if the URL does not include a script.
-     * @param function Populated with the function name.
-     *
-     * @script{ignore}
-     */
-    void parseUrl(const char* url, std::string* script, std::string* function);
+    Script* loadScript(const char* path, Script::Scope scope = Script::GLOBAL, bool forceReload = false);
 
     /**
      * Calls the specified no-parameter global Lua function.
@@ -853,6 +473,19 @@ private:
      */
     void finalize();
 
+    /**
+     * Attempts to unload a script with the given ID.
+     *
+     * Unloading a script causes the non-global data within the script to be
+     * released such that a reference is no longer held to it. It will then
+     * be garbage collected at some point in the future. If the script stored
+     * any data in the global environment or if any other code still references
+     * data from the script, that data will not be freed.
+     *
+     * @param id The ID of the script environment.
+     */
+    void unloadScript(int id);
+
     /**
      * Calls the specified Lua function using the given parameters.
      * 
@@ -906,29 +539,6 @@ private:
      */
     static int convert(lua_State* state);
 
-    // Friend functions (used by Lua script bindings).
-    friend void ScriptUtil::registerLibrary(const char* name, const luaL_Reg* functions);
-    friend void ScriptUtil::registerConstantBool(const std::string& name, bool value, const std::vector<std::string>& scopePath);
-    friend void ScriptUtil::registerConstantNumber(const std::string& name, double value, const std::vector<std::string>& scopePath);
-    friend void ScriptUtil::registerConstantString(const std::string& name, const std::string& value, const std::vector<std::string>& scopePath);
-    friend void ScriptUtil::registerEnumValue(int enumValue, const std::string& enumValueString, const std::vector<std::string>& scopePath);
-    friend void ScriptUtil::registerClass(const char* name, const luaL_Reg* members, lua_CFunction newFunction,
-        lua_CFunction deleteFunction, const luaL_Reg* statics, const std::vector<std::string>& scopePath);
-    friend void ScriptUtil::registerFunction(const char* luaFunction, lua_CFunction cppFunction);
-    friend void ScriptUtil::setGlobalHierarchyPair(const std::string& base, const std::string& derived);
-    friend ScriptUtil::LuaArray<bool> ScriptUtil::getBoolPointer(int index);
-    friend ScriptUtil::LuaArray<short> ScriptUtil::getShortPointer(int index);
-    friend ScriptUtil::LuaArray<int> ScriptUtil::getIntPointer(int index);
-    friend ScriptUtil::LuaArray<long> ScriptUtil::getLongPointer(int index);
-    friend ScriptUtil::LuaArray<unsigned char> ScriptUtil::getUnsignedCharPointer(int index);
-    friend ScriptUtil::LuaArray<unsigned short> ScriptUtil::getUnsignedShortPointer(int index);
-    friend ScriptUtil::LuaArray<unsigned int> ScriptUtil::getUnsignedIntPointer(int index);
-    friend ScriptUtil::LuaArray<unsigned long> ScriptUtil::getUnsignedLongPointer(int index);
-    friend ScriptUtil::LuaArray<float> ScriptUtil::getFloatPointer(int index);
-    friend ScriptUtil::LuaArray<double> ScriptUtil::getDoublePointer(int index);
-    template<typename T> friend ScriptUtil::LuaArray<T> ScriptUtil::getObjectPointer(int index, const char* type, bool nonNull, bool* success);
-    friend const char* ScriptUtil::getString(int index, bool isStdString);
-
     lua_State* _lua;
     unsigned int _returnCount;
     std::map<std::string, std::vector<std::string> > _hierarchy;
@@ -1018,6 +628,301 @@ template<> double ScriptController::executeFunction<double>(const char* func, co
 /** Template specialization. */
 template<> std::string ScriptController::executeFunction<std::string>(const char* func, const char* args, va_list* list, int script);
 
+/**
+ * Functions and structures used by the generated Lua script bindings.
+ *
+ * This class is used internally by the generated script bindings and is not intended
+ * to be used directly.
+ *
+ * @script{ignore}
+ */
+class ScriptUtil
+{
+public:
+
+    /**
+     * Represents a C++ object from within Lua.
+     * 
+     * @script{ignore}
+     */
+    struct LuaObject
+    {
+        /** The actual object instance. */
+        void* instance;
+        /** Whether object is owned by Lua. */
+        bool owns;
+    };
+
+    /**
+     * Stores a Lua parameter of an array/pointer type that is passed from Lua to C.
+     * Handles automatic cleanup of any temporary memory associated with the array.
+     * 
+     * @script{ignore}
+     */
+    template <typename T>
+    class LuaArray
+    {
+    public:
+
+        /**
+         * Creates a LuaArray to store a single pointer value.
+         */
+        LuaArray(T* param);
+
+        /**
+         * Allocates a LuaArray to store an array of values.
+         *
+         * Individual items in the array can be set using the 
+         * set(unsigned int, const T&) method.
+         * 
+         * @param count Number of elements to store in the parameter.
+         */
+        LuaArray(int count);
+
+        /**
+         * Copy constructor.
+         */
+        LuaArray(const LuaArray<T>& copy);
+
+        /**
+         * Destructor.
+         */
+        ~LuaArray();
+
+        /**
+         * Assignment operator.
+         */
+        LuaArray<T>& operator = (const LuaArray<T>& p);
+
+        /**
+         * Copies the value of the object pointed to by itemPtr into the specified
+         * index of this LuaArray's array.
+         */
+        void set(unsigned int index, const T* itemPtr);
+
+        /**
+         * Conversion operator from LuaArray to T*.
+         */
+        operator T* () const;
+
+        /**
+         * Overloads [] operator to get/set item value at index.
+         */
+        T& operator[] (int index);
+
+    private:
+
+        struct Data
+        {
+            Data() : value(NULL), refCount(0) { }
+            T* value;
+            int refCount;
+        };
+
+        Data* _data;
+    };
+
+    /**
+     * Registers the given library with Lua.
+     * 
+     * @param name The name of the library from within Lua.
+     * @param functions The library function mapping (Lua function names to C++ functions).
+     */
+    static void registerLibrary(const char* name, const luaL_Reg* functions);
+
+    /**
+     * Registers the given boolean constant as valid for the given scope path.
+     * 
+     * @param name The name of the constant (what the user would use from Lua).
+     * @param value The constant's value.
+     * @param scopePath The list of containing classes, going inward from the most outer class.
+     */
+    static void registerConstantBool(const std::string& name, bool value, const std::vector<std::string>& scopePath);
+
+    /**
+     * Registers the given number constant as valid for the given scope path.
+     * 
+     * @param name The name of the constant (what the user would use from Lua).
+     * @param value The constant's value.
+     * @param scopePath The list of containing classes, going inward from the most outer class.
+     */
+    static void registerConstantNumber(const std::string& name, double value, const std::vector<std::string>& scopePath);
+
+    /**
+     * Registers the given string constant as valid for the given scope path.
+     * 
+     * @param name The name of the constant (what the user would use from Lua).
+     * @param value The constant's value.
+     * @param scopePath The list of containing classes, going inward from the most outer class.
+     */
+    static void registerConstantString(const std::string& name, const std::string& value, const std::vector<std::string>& scopePath);
+
+    /**
+     * Registers the given enum value as valid for the given scope path.
+     *
+     * @param enumValue The enumeration value, expressed as an integer.
+     * @param enumValueString The string representation of the enum value (what the user would use from Lua).
+     * @param scopePath The list of containing classes, going inward from the most outer class.
+     */
+    static void registerEnumValue(int enumValue, const std::string& enumValueString, const std::vector<std::string>& scopePath);
+
+    /**
+     * Registers the given class type with Lua.
+     * 
+     * @param name The name of the class from within Lua.
+     * @param members The library function mapping for all the member functions (Lua function names to C++ functions).
+     * @param newFunction The function to call that creates an instance of the class.
+     * @param deleteFunction The function to call that destroys an instance of the class.
+     * @param statics The library function mapping for all the static functions (Lua function names to C++ functions).
+     * @param scopePath For an inner class, this is a list of its containing classes, going inward from the most outer class.
+     */
+    static void registerClass(const char* name, const luaL_Reg* members, lua_CFunction newFunction, lua_CFunction deleteFunction, const luaL_Reg* statics,
+                       const std::vector<std::string>& scopePath);
+
+    /**
+     * Register a function with Lua.
+     * 
+     * @param luaFunction The name of the function from within Lua.
+     * @param cppFunction The C++ function pointer.
+     */
+    static void registerFunction(const char* luaFunction, lua_CFunction cppFunction);
+
+    /**
+     * Sets an inheritance pair within the global inheritance hierarchy (base, derived).
+     * 
+     * @param base The base class of the inheritance pair.
+     * @param derived The derived class of the inheritance pair.
+     */
+    static void setGlobalHierarchyPair(const std::string& base, const std::string& derived);
+
+    /**
+     * Gets a pointer to a bool (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<bool> getBoolPointer(int index);
+
+    /**
+     * Gets a pointer to a short (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<short> getShortPointer(int index);
+
+    /**
+     * Gets a pointer to an int (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<int> getIntPointer(int index);
+
+    /**
+     * Gets a pointer to a long (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<long> getLongPointer(int index);
+
+    /**
+     * Gets a pointer to an unsigned char (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<unsigned char> getUnsignedCharPointer(int index);
+
+    /**
+     * Gets a pointer to an unsigned short (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<unsigned short> getUnsignedShortPointer(int index);
+
+    /**
+     * Gets a pointer to an unsigned int (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<unsigned int> getUnsignedIntPointer(int index);
+
+    /**
+     * Gets a pointer to an unsigned long (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<unsigned long> getUnsignedLongPointer(int index);
+
+    /**
+     * Gets a pointer to a float (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<float> getFloatPointer(int index);
+
+    /**
+     * Gets a pointer to a double (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
+     * 
+     * @param index The stack index.
+     * 
+     * @return The pointer.
+     */
+    static LuaArray<double> getDoublePointer(int index);
+
+    /**
+     * Gets an object pointer of the given type for the given stack index.
+     * 
+     * @param type The type of object pointer to retrieve.
+     * @param index The stack index.
+     * @param nonNull Whether the pointer must be non-null (e.g. if the parameter we 
+     *      are retrieving is actually a reference or by-value parameter).
+     * @param success An out parameter that is set to true if the Lua parameter was successfully
+     *      converted to a valid object, or false if it was unable to perform a valid conversion.
+     * 
+     * @return The object pointer or <code>NULL</code> if the data at the stack index
+     *      is not an object or if the object is not derived from the given type.
+     */
+    template <typename T>
+    static LuaArray<T> getObjectPointer(int index, const char* type, bool nonNull, bool* success);
+
+    /**
+     * Gets a string for the given stack index.
+     * 
+     * @param index The stack index.
+     * @param isStdString Whether the string being retrieved is a std::string object or not.
+     * 
+     * @return The string or <code>NULL</code>.
+     */
+    static const char* getString(int index, bool isStdString);
+
+    /**
+     * Checks that the parameter at the given stack position is a boolean and returns it.
+     * 
+     * @param state The Lua state.
+     * @param n The stack index.
+     * 
+     * @return The boolean (if successful; otherwise it logs an error).
+     */
+    static bool luaCheckBool(lua_State* state, int n);
+
+};
+
 }
 
 #include "ScriptController.inl"

+ 1 - 1
gameplay/src/ScriptController.inl

@@ -21,7 +21,7 @@ ScriptUtil::LuaArray<T>::LuaArray(int count)
     // Allocate a chunk of memory to store 'count' number of T.
     // Use new instead of malloc since we track memory allocations
     // int DebugMem configurations.
-    _data->value = (T*)new unsigned char[sizeof(T) * count];
+    _data->value = (T*)new unsigned char[sizeof(T)* count];
 
     // Positive ref count means we automatically cleanup memory
     _data->refCount = 1;

+ 30 - 1
gameplay/src/ScriptTarget.cpp

@@ -9,6 +9,8 @@ namespace gameplay
 // TODO: Should eventCallbacks store a vector of Event pointers instead of std::string (so we only have to do pointer comparisons instead of string comparisons)? What if Events are destroyed?
 // TODO: Should isolated scripts be cached (not currently), or a new script loaded for every ScriptTarget instance?
 
+extern void splitURL(const std::string& url, std::string* file, std::string* id);
+
 ScriptTarget::EventRegistry::EventRegistry()
 {
 }
@@ -86,7 +88,7 @@ void ScriptTarget::registerEvents(EventRegistry* registry)
     _events->push_back(registry);
 }
 
-int ScriptTarget::addScript(const char* path)
+void ScriptTarget::addScript(const char* path, Script::Scope scope)
 {
     // Load the script into an isolated environment
     ScriptController* sc = Game::getInstance()->getScriptController();
@@ -165,6 +167,33 @@ void ScriptTarget::removeScript(Script* script)
 
     // Free the script object
     SAFE_DELETE(script);
+
+    // TODO: Remove callbacks
+}
+
+
+void ScriptTarget::addScriptCallback(const char* eventName, const char* function)
+{
+    std::string script, func;
+    splitURL(function, &script, &func);
+    if (func.length() == 0)
+    {
+        // The url doesn't reference a script, only a function
+        func = script;
+        script = "";
+    }
+
+    // Ensure the script is loaded
+    if (script.length() > 0)
+    {
+        if (!Game::getInstance()->getScriptController()->loadScript(script.c_str()))
+            return "";
+    }
+}
+
+void ScriptTarget::removeScriptCallback(const char* eventName, const char* function)
+{
+    // TODO
 }
 
 void ScriptTarget::clearScripts()

+ 32 - 14
gameplay/src/ScriptTarget.h

@@ -146,9 +146,9 @@ public:
      * Attaches a script to this object.
      *
      * @param path Path to the script.
-     * @return The ID of the successfully loaded script, or zero if unsuccessful.
+     * @return A pointer to the successfully loaded script, or NULL if unsuccessful.
      */
-    int addScript(const char* path);
+    Script* addScript(const char* path);
 
     /**
      * Removes a previously attached script from this object.
@@ -159,7 +159,25 @@ public:
     bool removeScript(const char* path);
 
     /**
-     *  Removes all scripts from this object.
+     * Adds the given global script function as a callback for the given event.
+     * 
+     * @param eventName The name of the event.
+     * @param function The name of the script function to call when the event is fired; can either be
+     *  just the name of a function (if the function's script file has already been loaded), or can be
+     *  a URL of the form scriptFile.lua#functionName.
+     */
+    void addScriptCallback(const char* eventName, const char* function);
+
+    /**
+     * Removes the given script function as a callback for the given event.
+     * 
+     * @param eventName The name of the event.
+     * @param function The name of the script function.
+     */
+    void removeScriptCallback(const char* eventName, const char* function);
+
+    /**
+     *  Removes all scripts and callbacks from this object.
      */
     void clearScripts();
 
@@ -178,24 +196,24 @@ public:
 protected:
 
     /**
-     * Stores an attached script.
+     * Stores a registered script callback.
      */
-    struct Script
+    struct Callback
     {
-        // The ID of the script
-        int id;
+        // The script the callback belongs to
+        Script* script;
 
-        // The path the script was loaded from
-        std::string path;
+        // The function within the script to call
+        std::string function;
 
-        // Event callback functions available to be called for this script
-        std::vector<const EventRegistry::Event*> eventCallbacks;
+        // The script event this callback is for
+        const EventRegistry::Event* event;
 
         // Linked list info
-        Script* next;
-        Script* prev;
+        Callback* next;
+        Callback* prev;
 
-        Script() : id(0), next(NULL), prev(NULL) { }
+        Callback() : script(NULL), event(NULL), next(NULL), prev(NULL) { }
     };
 
     /**