S3rgeus
Emperor
Some context of what I'm trying to achieve: I want a unit to be able to detect some other units at a distance beyond its normal sight range. This will be showed to the human as a highlight off in the fog of war while the unit with said detection ability is selected.
The issue I'm running into is how I communicate with the UI Lua code where the highlights should be. Or, more accurately, how the UI should ask the game core "which units are currently detected by pUnit?" (where pUnit is sensibly defined elsewhere).
To all the C++ modders out there, has anyone had any experience returning collections of values from the DLL into Lua? Is there a simple way to push a collection (like an array or std::vector) in the form of a table, as the return value of a function? I'm hoping to be able to push a collection of playerID/unitID pairs to the Lua when it asks for a list of detected units.
From what I can see Firaxis has two primary approaches for returning data that's more complex than a single value to Lua: scoped instances (like Unit and Player objects) and iterator-like closures (such as Player:Units() and Player:Cities()). The latter has some crossover with the former in that those iterators are returning scoped instances on each iteration.
The iterator-like approach seems sensible for what I'm trying to achieve (though may be overkill - if there's a simpler way, do tell!). It would mean you could write something like this:
This leads me to looking into Firaxis's implementation of the Units iterator on the player. This is defined in CvLuaPlayer.cpp:
I'm relatively sure I understand the second function - it creates a new table that the caller (in Lua) will be looping through and pushes the values onto it. lUnitsAux, on the other hand, makes use of a lot of lua functionality that I'm unfamiliar with. The general gist I'm getting is that it's wrapping the firstUnit and nextUnit iterator pattern used elsewhere to loop over players' units in C++. It also appears to be pulling down the desired index from Lua, meaning there's a context switch between Lua and C++ after every iteration - I'm thinking lUnitsAux gets called once each at that time.
I could probably make this work by copy and paste with some tweaks (which I will try shortly), but I would like to gain some understanding of why it's structured how it is. The following few lines are the most opaque to me - does anyone know what these do?
lua_rawgeti looks to be paired with lua_rawseti after the unit has been grabbed - is that changing the index of the loop in Lua?
lua_gettop looks like it's changing the table and/or value that's being operated on by subsequent lua operations? (Hence why there's a lua_pop before it pushes any of the next iteration's data?)
The issue I'm running into is how I communicate with the UI Lua code where the highlights should be. Or, more accurately, how the UI should ask the game core "which units are currently detected by pUnit?" (where pUnit is sensibly defined elsewhere).
To all the C++ modders out there, has anyone had any experience returning collections of values from the DLL into Lua? Is there a simple way to push a collection (like an array or std::vector) in the form of a table, as the return value of a function? I'm hoping to be able to push a collection of playerID/unitID pairs to the Lua when it asks for a list of detected units.
From what I can see Firaxis has two primary approaches for returning data that's more complex than a single value to Lua: scoped instances (like Unit and Player objects) and iterator-like closures (such as Player:Units() and Player:Cities()). The latter has some crossover with the former in that those iterators are returning scoped instances on each iteration.
The iterator-like approach seems sensible for what I'm trying to achieve (though may be overkill - if there's a simpler way, do tell!). It would mean you could write something like this:
Code:
for pDetectedUnit in pUnit:DetectedUnits() do
-- highlight pDetectedUnit's hex here
end
This leads me to looking into Firaxis's implementation of the Units iterator on the player. This is defined in CvLuaPlayer.cpp:
Code:
// Aux Method used by lUnits.
int CvLuaPlayer::lUnitsAux(lua_State* L)
{
CvPlayerAI* pkPlayer = GetInstance(L);
CvUnit* pkUnit = NULL;
lua_pushvalue(L, lua_upvalueindex(1));
int t = lua_gettop(L);
lua_rawgeti(L, t, 1);
int i = -1;
if(!lua_isnil(L, -1))
{
i = lua_tointeger(L, -1);
}
lua_pop(L, 1);
pkUnit = (i == -1)? pkPlayer->firstUnit(&i) : pkPlayer->nextUnit(&i);
lua_pushinteger(L, i);
lua_rawseti(L, t, 1);
if(pkUnit)
{
CvLuaUnit::Push(L, pkUnit);
return 1;
}
return 0;
}
//------------------------------------------------------------------------------
// Method for iterating through units (behaves like pairs)
int CvLuaPlayer::lUnits(lua_State* L)
{
lua_createtable(L, 1, 0);
lua_pushcclosure(L, lUnitsAux, 1); /* generator, */
lua_pushvalue(L, 1); /* state (self)*/
return 2;
}
I'm relatively sure I understand the second function - it creates a new table that the caller (in Lua) will be looping through and pushes the values onto it. lUnitsAux, on the other hand, makes use of a lot of lua functionality that I'm unfamiliar with. The general gist I'm getting is that it's wrapping the firstUnit and nextUnit iterator pattern used elsewhere to loop over players' units in C++. It also appears to be pulling down the desired index from Lua, meaning there's a context switch between Lua and C++ after every iteration - I'm thinking lUnitsAux gets called once each at that time.
I could probably make this work by copy and paste with some tweaks (which I will try shortly), but I would like to gain some understanding of why it's structured how it is. The following few lines are the most opaque to me - does anyone know what these do?
Code:
lua_pushvalue(L, lua_upvalueindex(1));
int t = lua_gettop(L);
lua_rawgeti(L, t, 1);
lua_rawgeti looks to be paired with lua_rawseti after the unit has been grabbed - is that changing the index of the loop in Lua?
lua_gettop looks like it's changing the table and/or value that's being operated on by subsequent lua operations? (Hence why there's a lua_pop before it pushes any of the next iteration's data?)