Wrap MaterialInfo for lua.

develop
Alexander Gavrilov 2012-04-06 19:56:19 +04:00
parent 2640addf49
commit 0daafef690
7 changed files with 357 additions and 9 deletions

@ -498,6 +498,59 @@ and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O. functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead. However, currently every entry has a 180+-byte dead-weight overhead.
Material info lookup
====================
A material info record has fields:
* ``type``, ``index``, ``material``
DF material code pair, and a reference to the material object.
* ``mode``
One of ``'builtin'``, ``'inorganic'``, ``'plant'``, ``'creature'``.
* ``inorganic``, ``plant``, ``creature``
If the material is of the matching type, contains a reference to the raw object.
* ``figure``
For a specific creature material contains a ref to the historical figure.
Functions:
* ``dfhack.matinfo.decode(type,index)``
Looks up material info for the given number pair; if not found, returs *nil*.
* ``....decode(matinfo)``, ``....decode(item)``, ``....decode(obj)``
Uses ``matinfo.type``/``matinfo.index``, item getter vmethods,
or ``obj.mat_type``/``obj.mat_index`` to get the code pair.
* ``dfhack.matinfo.find(token[,token...])``
Looks up material by a token string, or a pre-split string token sequence.
* ``dfhack.matinfo.getToken(...)``, ``info:getToken()``
Applies ``decode`` and constructs a string token.
* ``info:toString([temperature[,named]])``
Returns the human-readable name at the given temperature.
* ``info:getCraftClass()``
Returns the classification used for craft skills.
* ``info:matches(obj)``
Checks if the material matches job_material_category or job_item.
Accept dfhack_material_category auto-assign table.
C++ function wrappers C++ function wrappers
===================== =====================

@ -335,10 +335,11 @@ ul.auto-toc {
</li> </li>
<li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul> <li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li> <li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#c-function-wrappers" id="id12">C++ function wrappers</a><ul> <li><a class="reference internal" href="#material-info-lookup" id="id12">Material info lookup</a></li>
<li><a class="reference internal" href="#gui-module" id="id13">Gui module</a></li> <li><a class="reference internal" href="#c-function-wrappers" id="id13">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#job-module" id="id14">Job module</a></li> <li><a class="reference internal" href="#gui-module" id="id14">Gui module</a></li>
<li><a class="reference internal" href="#units-module" id="id15">Units module</a></li> <li><a class="reference internal" href="#job-module" id="id15">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id16">Units module</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -759,8 +760,51 @@ and automatically stored in the save game, these save and retrieval
functions can just copy values in memory without doing any actual I/O. functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p> However, currently every entry has a 180+-byte dead-weight overhead.</p>
</div> </div>
<div class="section" id="material-info-lookup">
<h2><a class="toc-backref" href="#id12">Material info lookup</a></h2>
<p>A material info record has fields:</p>
<ul>
<li><p class="first"><tt class="docutils literal">type</tt>, <tt class="docutils literal">index</tt>, <tt class="docutils literal">material</tt></p>
<p>DF material code pair, and a reference to the material object.</p>
</li>
<li><p class="first"><tt class="docutils literal">mode</tt></p>
<p>One of <tt class="docutils literal">'builtin'</tt>, <tt class="docutils literal">'inorganic'</tt>, <tt class="docutils literal">'plant'</tt>, <tt class="docutils literal">'creature'</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">inorganic</tt>, <tt class="docutils literal">plant</tt>, <tt class="docutils literal">creature</tt></p>
<p>If the material is of the matching type, contains a reference to the raw object.</p>
</li>
<li><p class="first"><tt class="docutils literal">figure</tt></p>
<p>For a specific creature material contains a ref to the historical figure.</p>
</li>
</ul>
<p>Functions:</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.matinfo.decode(type,index)</tt></p>
<p>Looks up material info for the given number pair; if not found, returs <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">....decode(matinfo)</span></tt>, <tt class="docutils literal"><span class="pre">....decode(item)</span></tt>, <tt class="docutils literal"><span class="pre">....decode(obj)</span></tt></p>
<p>Uses <tt class="docutils literal">matinfo.type</tt>/<tt class="docutils literal">matinfo.index</tt>, item getter vmethods,
or <tt class="docutils literal">obj.mat_type</tt>/<tt class="docutils literal">obj.mat_index</tt> to get the code pair.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.matinfo.find(token[,token...])</span></tt></p>
<p>Looks up material by a token string, or a pre-split string token sequence.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.matinfo.getToken(...)</span></tt>, <tt class="docutils literal">info:getToken()</tt></p>
<p>Applies <tt class="docutils literal">decode</tt> and constructs a string token.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">info:toString([temperature[,named]])</span></tt></p>
<p>Returns the human-readable name at the given temperature.</p>
</li>
<li><p class="first"><tt class="docutils literal">info:getCraftClass()</tt></p>
<p>Returns the classification used for craft skills.</p>
</li>
<li><p class="first"><tt class="docutils literal">info:matches(obj)</tt></p>
<p>Checks if the material matches job_material_category or job_item.</p>
</li>
</ul>
</div>
<div class="section" id="c-function-wrappers"> <div class="section" id="c-function-wrappers">
<h2><a class="toc-backref" href="#id12">C++ function wrappers</a></h2> <h2><a class="toc-backref" href="#id13">C++ function wrappers</a></h2>
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods.</p> <p>Thin wrappers around C++ functions, similar to the ones for virtual methods.</p>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.TranslateName(name,in_english,only_last_name)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.TranslateName(name,in_english,only_last_name)</tt></p>
@ -768,7 +812,7 @@ However, currently every entry has a 180+-byte dead-weight overhead.</p>
</li> </li>
</ul> </ul>
<div class="section" id="gui-module"> <div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id13">Gui module</a></h3> <h3><a class="toc-backref" href="#id14">Gui module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedWorkshopJob(silent)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.gui.getSelectedWorkshopJob(silent)</tt></p>
<p>When a job is selected in <em>'q'</em> mode, returns the job, else <p>When a job is selected in <em>'q'</em> mode, returns the job, else
@ -797,7 +841,7 @@ The is_bright boolean actually seems to invert the brightness.</p>
</ul> </ul>
</div> </div>
<div class="section" id="job-module"> <div class="section" id="job-module">
<h3><a class="toc-backref" href="#id14">Job module</a></h3> <h3><a class="toc-backref" href="#id15">Job module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
<p>Creates a deep copy of the given job.</p> <p>Creates a deep copy of the given job.</p>
@ -817,7 +861,7 @@ The is_bright boolean actually seems to invert the brightness.</p>
</ul> </ul>
</div> </div>
<div class="section" id="units-module"> <div class="section" id="units-module">
<h3><a class="toc-backref" href="#id15">Units module</a></h3> <h3><a class="toc-backref" href="#id16">Units module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.units.getVisibleName(unit)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.units.getVisibleName(unit)</tt></p>
<p>Returns the name visible in game, accounting for false identities.</p> <p>Returns the name visible in game, accounting for false identities.</p>

@ -42,6 +42,7 @@ distribution.
#include "modules/Job.h" #include "modules/Job.h"
#include "modules/Translation.h" #include "modules/Translation.h"
#include "modules/Units.h" #include "modules/Units.h"
#include "modules/Materials.h"
#include "LuaWrapper.h" #include "LuaWrapper.h"
#include "LuaTools.h" #include "LuaTools.h"
@ -53,6 +54,13 @@ distribution.
#include "df/building.h" #include "df/building.h"
#include "df/unit.h" #include "df/unit.h"
#include "df/item.h" #include "df/item.h"
#include "df/material.h"
#include "df/historical_figure.h"
#include "df/plant_raw.h"
#include "df/creature_raw.h"
#include "df/inorganic_raw.h"
#include "df/dfhack_material_category.h"
#include "df/job_material_category.h"
#include <lua.h> #include <lua.h>
#include <lauxlib.h> #include <lauxlib.h>
@ -280,6 +288,230 @@ static void OpenPersistent(lua_State *state)
lua_pop(state, 1); lua_pop(state, 1);
} }
/************************
* Material info lookup *
************************/
static void push_matinfo(lua_State *state, MaterialInfo &info)
{
if (!info.isValid())
{
lua_pushnil(state);
return;
}
lua_newtable(state);
lua_pushvalue(state, lua_upvalueindex(1));
lua_setmetatable(state, -2);
lua_pushinteger(state, info.type);
lua_setfield(state, -2, "type");
lua_pushinteger(state, info.index);
lua_setfield(state, -2, "index");
#define SETOBJ(name) { \
Lua::PushDFObject(state, info.name); \
lua_setfield(state, -2, #name); \
}
SETOBJ(material);
if (info.plant) SETOBJ(plant);
if (info.creature) SETOBJ(creature);
if (info.inorganic) SETOBJ(inorganic);
if (info.figure) SETOBJ(figure);
#undef SETOBJ
if (info.mode != MaterialInfo::Builtin)
{
lua_pushinteger(state, info.subtype);
lua_setfield(state, -2, "subtype");
}
const char *id = "builtin";
switch (info.mode)
{
case MaterialInfo::Plant: id = "plant"; break;
case MaterialInfo::Creature: id = "creature"; break;
case MaterialInfo::Inorganic: id = "inorganic"; break;
}
lua_pushstring(state, id);
lua_setfield(state, -2, "mode");
}
static int dfhack_matinfo_find(lua_State *state)
{
MaterialInfo info;
int argc = lua_gettop(state);
if (argc == 1)
info.find(luaL_checkstring(state, 1));
else
{
std::vector<std::string> tokens;
for (int i = 1; i < argc; i++)
tokens.push_back(luaL_checkstring(state, i));
info.find(tokens);
}
push_matinfo(state, info);
return 1;
}
static bool decode_matinfo(lua_State *state, MaterialInfo *info, bool numpair = false)
{
int curtop = lua_gettop(state);
luaL_checkany(state, 1);
if (!lua_isnumber(state, 1))
{
if (lua_isnil(state, 1))
return false;
if (lua_getmetatable(state, 1))
{
if (lua_rawequal(state, -1, lua_upvalueindex(1)))
{
lua_getfield(state, 1, "type");
lua_getfield(state, 1, "index");
goto int_pair;
}
lua_pop(state, 1);
}
if (lua_isuserdata(state, 1))
{
if (auto item = Lua::GetDFObject<df::item>(state, 1))
return info->decode(item);
if (auto mvec = Lua::GetDFObject<df::material_vec_ref>(state, 1))
return info->decode(*mvec, luaL_checkint(state, 2));
}
lua_getfield(state, 1, "mat_type");
lua_getfield(state, 1, "mat_index");
goto int_pair;
}
else
{
if (!numpair)
luaL_argerror(state, 1, "material info object expected");
if (curtop < 2)
lua_settop(state, 2);
}
int_pair:
{
int ok;
int type = lua_tointegerx(state, -2, &ok);
if (!ok)
luaL_argerror(state, 1, "material id is not a number");
int index = lua_tointegerx(state, -1, &ok);
if (!ok)
index = -1;
lua_settop(state, curtop);
return info->decode(type, index);
}
}
static int dfhack_matinfo_decode(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info, true);
push_matinfo(state, info);
return 1;
}
static int dfhack_matinfo_getToken(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info, true);
auto str = info.getToken();
lua_pushstring(state, str.c_str());
return 1;
}
static int dfhack_matinfo_toString(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info);
lua_settop(state, 3);
auto str = info.toString(luaL_optint(state, 2, 10015), lua_toboolean(state, 3));
lua_pushstring(state, str.c_str());
return 1;
}
static int dfhack_matinfo_getCraftClass(lua_State *state)
{
MaterialInfo info;
if (decode_matinfo(state, &info, true))
lua_pushinteger(state, info.getCraftClass());
else
lua_pushnil(state);
return 1;
}
static int dfhack_matinfo_matches(lua_State *state)
{
MaterialInfo info;
if (!decode_matinfo(state, &info))
luaL_argerror(state, 1, "material info object expected");
luaL_checkany(state, 2);
if (lua_isuserdata(state, 2))
{
if (auto mc = Lua::GetDFObject<df::job_material_category>(state, 2))
lua_pushboolean(state, info.matches(*mc));
else if (auto mc = Lua::GetDFObject<df::dfhack_material_category>(state, 2))
lua_pushboolean(state, info.matches(*mc));
else if (auto mc = Lua::GetDFObject<df::job_item>(state, 2))
lua_pushboolean(state, info.matches(*mc));
else
luaL_argerror(state, 2, "material category object expected");
}
else if (lua_istable(state, 2))
{
df::dfhack_material_category tmp;
if (!Lua::AssignDFObject(*Lua::GetOutput(state), state, &tmp, 2, false))
lua_error(state);
lua_pushboolean(state, info.matches(tmp));
}
else
luaL_argerror(state, 2, "material category object expected");
return 1;
}
static const luaL_Reg dfhack_matinfo_funcs[] = {
{ "find", dfhack_matinfo_find },
{ "decode", dfhack_matinfo_decode },
{ "getToken", dfhack_matinfo_getToken },
{ "toString", dfhack_matinfo_toString },
{ "getCraftClass", dfhack_matinfo_getCraftClass },
{ "matches", dfhack_matinfo_matches },
{ NULL, NULL }
};
static void OpenMatinfo(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "matinfo");
lua_dup(state);
luaL_setfuncs(state, dfhack_matinfo_funcs, 1);
lua_dup(state);
lua_setfield(state, -2, "__index");
lua_pop(state, 1);
}
/************************ /************************
* Wrappers for C++ API * * Wrappers for C++ API *
************************/ ************************/
@ -337,6 +569,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
void OpenDFHackApi(lua_State *state) void OpenDFHackApi(lua_State *state)
{ {
OpenPersistent(state); OpenPersistent(state);
OpenMatinfo(state);
LuaWrapper::SetFunctionWrappers(state, dfhack_module); LuaWrapper::SetFunctionWrappers(state, dfhack_module);
OpenModule(state, "gui", dfhack_gui_module); OpenModule(state, "gui", dfhack_gui_module);

@ -699,6 +699,9 @@ static int meta_assign(lua_State *state)
{ {
type_identity *id = get_object_identity(state, 1, "df.assign()", false); type_identity *id = get_object_identity(state, 1, "df.assign()", false);
if (lua_getmetatable(state, 2))
luaL_error(state, "cannot use lua tables with metatable in df.assign()");
int base = lua_gettop(state); int base = lua_gettop(state);
// x:assign{ assign = foo } => x:assign(foo) // x:assign{ assign = foo } => x:assign(foo)

@ -114,6 +114,7 @@ namespace DFHack
} }
bool find(const std::string &token); bool find(const std::string &token);
bool find(const std::vector<std::string> &tokens);
bool findBuiltin(const std::string &token); bool findBuiltin(const std::string &token);
bool findInorganic(const std::string &token); bool findInorganic(const std::string &token);

@ -70,5 +70,9 @@ function dfhack.persistent:__tostring()
..self.value.."\":"..table.concat(self.ints,",")..">" ..self.value.."\":"..table.concat(self.ints,",")..">"
end end
function dfhack.matinfo:__tostring()
return "<material "..self.type..":"..self.index.." "..self:getToken()..">"
end
-- Feed the table back to the require() mechanism. -- Feed the table back to the require() mechanism.
return dfhack return dfhack

@ -164,6 +164,13 @@ bool MaterialInfo::find(const std::string &token)
{ {
std::vector<std::string> items; std::vector<std::string> items;
split_string(&items, token, ":"); split_string(&items, token, ":");
return find(items);
}
bool MaterialInfo::find(const std::vector<std::string> &items)
{
if (items.empty())
return false;
if (items[0] == "INORGANIC" && items.size() > 1) if (items[0] == "INORGANIC" && items.size() > 1)
return findInorganic(vector_get(items,1)); return findInorganic(vector_get(items,1));
@ -204,8 +211,11 @@ bool MaterialInfo::findBuiltin(const std::string &token)
df::world_raws &raws = world->raws; df::world_raws &raws = world->raws;
for (int i = 1; i < NUM_BUILTIN; i++) for (int i = 1; i < NUM_BUILTIN; i++)
if (raws.mat_table.builtin[i]->id == token) {
auto obj = raws.mat_table.builtin[i];
if (obj && obj->id == token)
return decode(i, -1); return decode(i, -1);
}
return decode(-1); return decode(-1);
} }