Modified ActorFrame:GetChildren and GetChild to return a table in cases where multiple children have the same name. This table acts as a pass through layer for function calls so that old code should be unaffected.

This commit is contained in:
Kyzentun
2014-04-29 15:08:34 -06:00
parent 6b17fb043d
commit 587aa38854
2 changed files with 129 additions and 9 deletions
+128 -9
View File
@@ -289,14 +289,137 @@ void ActorFrame::EndDraw()
Actor::EndDraw();
}
// This exists solely as a helper for children table.
// This applies the desired function to te first child in the table.
static int IdenticalChildrenSingleApplier(lua_State* L)
{
// First arg is the table of items, get the last object.
// It is the one that would have been in the children table in the old version.
// The other args are meant for the function.
// The upvalue for this function is the function the theme tried to call.
lua_rawgeti(L, 1, lua_objlen(L, 1)); // stack: table, args, obj
lua_insert(L, 2); // stack: table, obj, args
lua_pushvalue(L, lua_upvalueindex(1)); // stack: table, obj, args, func
lua_insert(L, 2); // stack: table, func, obj, args
int args_count= lua_gettop(L) - 2;
lua_call(L, args_count, LUA_MULTRET); // stack: table, return_values
return lua_gettop(L) - 1;
}
// This exists solely as a helper for children table.
// This is the __index function for the table of all children with the same name.
static int IdenticalChildrenIndexLayer(lua_State* L)
{
if(lua_isnumber(L, 2))
{
lua_gettable(L, 1);
}
else
{
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pop(L, 2);
// Get the last object in the table.
// Its meta table contains the function the theme wanted to run.
// The function is then pushed as an upvalue for ICSA as a closure.
// The closure is then returned so that when the function call is performed, ICSA is actually called.
lua_pushnumber(L, lua_objlen(L, 1)); // stack: 1
lua_gettable(L, 1); // stack: object
lua_getmetatable(L, -1); // stack: object, obj_meta
lua_getfield(L, -1, "__index"); // stack: object, obj_meta, obj_index
lua_pushvalue(L, 2); // stack: object, obj_meta, obj_index, func_name
lua_gettable(L, -2); // stack: object, obj_meta, obj_index, obj_function
lua_pushcclosure(L, IdenticalChildrenSingleApplier, 1); // stack: object, obj_meta, obj_index, closure
lua_insert(L, -4); // stack: closure, object, obj_meta, obj_index
lua_pop(L, 3); // stack: closure
}
return 1;
}
static void CreateChildTable(lua_State* L, Actor* a)
{
// Old PushChildrenTable assumed that all children had unique names, so only the last one of a name ended up in the table that was returned.
// Create a table that will hold all the children that have this name and act as a pass through layer for function calls.
// stack: old_entry
lua_createtable(L, 0, 0); // stack: old_entry, table_entry
lua_insert(L, -2); // stack: table_entry, old_entry
lua_rawseti(L, -2, 1); // stack: table_entry
a->PushSelf(L); // stack: table_entry, new_entry
lua_rawseti(L, -2, 2); // stack: table_entry
lua_createtable(L, 0, 1); // stack: table_entry, table_meta
lua_pushcfunction(L, IdenticalChildrenIndexLayer); // stack: table_entry, table_meta, ICIL
lua_setfield(L, -2, "__index"); // stack: table_entry, table_meta
lua_setmetatable(L, -2); // stack: table_entry
}
static void AddToChildTable(lua_State* L, Actor* a)
{
// stack: table_entry
int next_index= lua_objlen(L, -1) + 1;
a->PushSelf(L); // stack: table_entry, actor
lua_rawseti(L, -2, next_index); // stack: table_entry
}
void ActorFrame::PushChildrenTable( lua_State *L )
{
lua_newtable( L );
lua_newtable( L ); // stack: all_actors
FOREACH( Actor*, m_SubActors, a )
{
LuaHelpers::Push( L, (*a)->GetName() );
(*a)->PushSelf( L );
lua_rawset( L, -3 );
LuaHelpers::Push( L, (*a)->GetName() ); // stack: all_actors, name
lua_gettable(L, -2); // stack: all_actors, entry
if(lua_isnil(L, -1))
{
lua_pop(L, 1); // stack: all_actors
LuaHelpers::Push( L, (*a)->GetName() ); // stack: all_actors, name
(*a)->PushSelf( L ); // stack: all_actors, name, actor
lua_rawset( L, -3 ); // stack: all_actors
}
else
{
// Fun fact: PushSelf pushes a table.
if(lua_objlen(L, -1) > 0)
{
// stack: all_actors, table_entry
AddToChildTable(L, *a); // stack: all_actors, table_entry
lua_pop(L, 1); // stack: all_actors
}
else
{
// stack: all_actors, old_entry
CreateChildTable(L, *a); // stack: all_actors, table_entry
LuaHelpers::Push(L, (*a)->GetName()); // stack: all_actors, table_entry, name
lua_insert(L, -2); // stack: all_actors, name, table_entry
lua_rawset(L, -3); // stack: all_actors
}
}
}
}
void ActorFrame::PushChildTable(lua_State* L, const RString &sName)
{
int found= 0;
FOREACH(Actor*, m_SubActors, a)
{
if((*a)->GetName() == sName)
{
switch(found)
{
case 0:
(*a)->PushSelf(L);
break;
case 1:
CreateChildTable(L, *a);
break;
default:
AddToChildTable(L, *a);
break;
}
++found;
}
}
if(!found)
{
lua_pushnil(L);
}
}
@@ -498,11 +621,7 @@ public:
static int vanishpoint( T* p, lua_State *L ) { p->SetVanishPoint( FArg(1), FArg(2) ); return 0; }
static int GetChild( T* p, lua_State *L )
{
Actor *pChild = p->GetChild( SArg(1) );
if( pChild )
pChild->PushSelf( L );
else
lua_pushnil( L );
p->PushChildTable(L, SArg(1));
return 1;
}
static int GetChildren( T* p, lua_State *L )