diff options
author | Robert Vollmert <rvollmert@gmx.net> | 2009-10-19 22:45:04 +0200 |
---|---|---|
committer | Robert Vollmert <rvollmert@gmx.net> | 2009-10-19 22:46:37 +0200 |
commit | 49eb883334f3e7ab343251cdcd161b77cb3ba784 (patch) | |
tree | bb79ffcacdb208ff0a554413e5247ddcd9e08978 /crawl-ref/source/dlua.cc | |
parent | c0aa07cd0d0b527f9f37a48737880d9cf0bbf566 (diff) | |
download | crawl-ref-49eb883334f3e7ab343251cdcd161b77cb3ba784.tar.gz crawl-ref-49eb883334f3e7ab343251cdcd161b77cb3ba784.zip |
Split dgn_lib out of luadgn.cc; rename luadgn to dlua.
Diffstat (limited to 'crawl-ref/source/dlua.cc')
-rw-r--r-- | crawl-ref/source/dlua.cc | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/crawl-ref/source/dlua.cc b/crawl-ref/source/dlua.cc new file mode 100644 index 0000000000..7d26c87d24 --- /dev/null +++ b/crawl-ref/source/dlua.cc @@ -0,0 +1,916 @@ +/* + * File: luadgn.cc + * Summary: Dungeon-builder Lua interface. + * Created by: dshaligram on Sat Jun 23 20:02:09 2007 UTC + */ + +#include "AppHdr.h" + +#include <sstream> +#include <algorithm> +#include <memory> +#include <cmath> + +#include "branch.h" +#include "chardump.h" +#include "clua.h" +#include "cloud.h" +#include "describe.h" +#include "directn.h" +#include "dlua.h" +#include "dungeon.h" +#include "files.h" +#include "hiscores.h" +#include "initfile.h" +#include "items.h" +#include "l_los.h" +#include "los.h" +#include "mapdef.h" +#include "mapmark.h" +#include "maps.h" +#include "message.h" +#include "misc.h" +#include "mon-util.h" +#include "monplace.h" +#include "monstuff.h" +#include "place.h" +#include "spells3.h" +#include "spl-util.h" +#include "state.h" +#include "stuff.h" +#include "tags.h" +#include "terrain.h" +#include "view.h" + +#ifdef UNIX +#include <sys/time.h> +#include <time.h> +#endif + +template <typename list, typename lpush> +static int dlua_gentable(lua_State *ls, const list &strings, lpush push) +{ + lua_newtable(ls); + for (int i = 0, size = strings.size(); i < size; ++i) + { + push(ls, strings[i]); + lua_rawseti(ls, -2, i + 1); + } + return (1); +} + +inline static void dlua_pushcxxstring(lua_State *ls, const std::string &s) +{ + lua_pushstring(ls, s.c_str()); +} + +int dlua_stringtable(lua_State *ls, const std::vector<std::string> &s) +{ + return dlua_gentable(ls, s, dlua_pushcxxstring); +} + +static int dlua_compiled_chunk_writer(lua_State *ls, const void *p, + size_t sz, void *ud) +{ + std::ostringstream &out = *static_cast<std::ostringstream*>(ud); + out.write((const char *) p, sz); + return (0); +} + +/////////////////////////////////////////////////////////////////////////// +// dlua_chunk + +dlua_chunk::dlua_chunk(const std::string &_context) + : file(), chunk(), compiled(), context(_context), first(-1), + last(-1), error() +{ + clear(); +} + +// Initialises a chunk from the function on the top of stack. +// This function must not be a closure, i.e. must not have any upvalues. +dlua_chunk::dlua_chunk(lua_State *ls) + : file(), chunk(), compiled(), context(), first(-1), last(-1), error() +{ + clear(); + + lua_stack_cleaner cln(ls); + std::ostringstream out; + const int err = lua_dump(ls, dlua_compiled_chunk_writer, &out); + if (err) + { + const char *e = lua_tostring(ls, -1); + error = e? e : "Unknown error compiling chunk"; + } + compiled = out.str(); +} + +dlua_chunk dlua_chunk::precompiled(const std::string &chunk) +{ + dlua_chunk dchunk; + dchunk.compiled = chunk; + return (dchunk); +} + +void dlua_chunk::write(writer& outf) const +{ + if (empty()) + { + marshallByte(outf, CT_EMPTY); + return; + } + + if (!compiled.empty()) + { + marshallByte(outf, CT_COMPILED); + marshallString4(outf, compiled); + } + else + { + marshallByte(outf, CT_SOURCE); + marshallString4(outf, chunk); + } + + marshallString4(outf, file); + marshallLong(outf, first); +} + +void dlua_chunk::read(reader& inf) +{ + clear(); + chunk_t type = static_cast<chunk_t>(unmarshallByte(inf)); + switch (type) + { + case CT_EMPTY: + return; + case CT_SOURCE: + unmarshallString4(inf, chunk); + break; + case CT_COMPILED: + unmarshallString4(inf, compiled); + break; + } + unmarshallString4(inf, file); + first = unmarshallLong(inf); +} + +void dlua_chunk::clear() +{ + file.clear(); + chunk.clear(); + first = last = -1; + error.clear(); + compiled.clear(); +} + +void dlua_chunk::set_file(const std::string &s) +{ + file = s; +} + +void dlua_chunk::add(int line, const std::string &s) +{ + if (first == -1) + first = line; + + if (line != last && last != -1) + while (last++ < line) + chunk += '\n'; + + chunk += " "; + chunk += s; + last = line; +} + +void dlua_chunk::set_chunk(const std::string &s) +{ + chunk = s; +} + +int dlua_chunk::check_op(CLua &interp, int err) +{ + error = interp.error; + return (err); +} + +int dlua_chunk::load(CLua &interp) +{ + if (!compiled.empty()) + return check_op( interp, + interp.loadbuffer(compiled.c_str(), compiled.length(), + context.c_str()) ); + + if (empty()) + { + chunk.clear(); + return (-1000); + } + + int err = check_op( interp, + interp.loadstring(chunk.c_str(), context.c_str()) ); + if (err) + return (err); + std::ostringstream out; + err = lua_dump(interp, dlua_compiled_chunk_writer, &out); + if (err) + { + const char *e = lua_tostring(interp, -1); + error = e? e : "Unknown error compiling chunk"; + lua_pop(interp, 2); + } + compiled = out.str(); + chunk.clear(); + return (err); +} + +int dlua_chunk::run(CLua &interp) +{ + int err = load(interp); + if (err) + return (err); + // callfn returns true on success, but we want to return 0 on success. + return (check_op(interp, !interp.callfn(NULL, 0, 0))); +} + +int dlua_chunk::load_call(CLua &interp, const char *fn) +{ + int err = load(interp); + if (err == -1000) + return (0); + if (err) + return (err); + + return check_op(interp, !interp.callfn(fn, fn? 1 : 0, 0)); +} + +std::string dlua_chunk::orig_error() const +{ + rewrite_chunk_errors(error); + return (error); +} + +bool dlua_chunk::empty() const +{ + return compiled.empty() && trimmed_string(chunk).empty(); +} + +bool dlua_chunk::rewrite_chunk_errors(std::string &s) const +{ + const std::string contextm = "[string \"" + context + "\"]:"; + std::string::size_type dlwhere = s.find(contextm); + + if (dlwhere == std::string::npos) + return (false); + + if (!dlwhere) + { + s = rewrite_chunk_prefix(s); + return (true); + } + + // Our chunk is mentioned, go back through and rewrite lines. + std::vector<std::string> lines = split_string("\n", s); + std::string newmsg = lines[0]; + bool wrote_prefix = false; + for (int i = 2, size = lines.size() - 1; i < size; ++i) + { + const std::string &st = lines[i]; + if (st.find(context) != std::string::npos) + { + if (!wrote_prefix) + { + newmsg = get_chunk_prefix(st) + ": " + newmsg; + wrote_prefix = true; + } + else + newmsg += "\n" + rewrite_chunk_prefix(st); + } + } + s = newmsg; + return (true); +} + +std::string dlua_chunk::rewrite_chunk_prefix(const std::string &line, + bool skip_body) const +{ + std::string s = line; + const std::string contextm = "[string \"" + context + "\"]:"; + const std::string::size_type ps = s.find(contextm); + if (ps == std::string::npos) + return (s); + + const std::string::size_type lns = ps + contextm.length(); + std::string::size_type pe = s.find(':', ps + contextm.length()); + if (pe != std::string::npos) + { + const std::string line_num = s.substr(lns, pe - lns); + const int lnum = atoi(line_num.c_str()); + const std::string newlnum = make_stringf("%d", lnum + first - 1); + s = s.substr(0, lns) + newlnum + s.substr(pe); + pe = lns + newlnum.length(); + } + + return s.substr(0, ps) + (file.empty()? context : file) + ":" + + (skip_body? s.substr(lns, pe - lns) + : s.substr(lns)); +} + +std::string dlua_chunk::get_chunk_prefix(const std::string &sorig) const +{ + return rewrite_chunk_prefix(sorig, true); +} + +LUAFN(_crawl_args) +{ + return dlua_stringtable(ls, SysEnv.cmd_args); +} + +LUAFN(_crawl_milestone) +{ +#ifdef DGL_MILESTONES + mark_milestone(luaL_checkstring(ls, 1), + luaL_checkstring(ls, 2)); +#endif + return (0); +} + +LUAFN(_crawl_redraw_view) +{ + viewwindow(true, false); + return (0); +} + +#ifdef UNIX +LUAFN(_crawl_millis) +{ + struct timeval tv; + struct timezone tz; + const int error = gettimeofday(&tv, &tz); + if (error) + luaL_error(ls, make_stringf("Failed to get time: %s", + strerror(error)).c_str()); + + lua_pushnumber(ls, tv.tv_sec * 1000 + tv.tv_usec / 1000); + return (1); +} +#endif + +static const struct luaL_reg crawl_lib[] = +{ + { "args", _crawl_args }, + { "mark_milestone", _crawl_milestone }, + { "redraw_view", _crawl_redraw_view }, +#ifdef UNIX + { "millis", _crawl_millis }, +#endif + { NULL, NULL } +}; + +static int file_marshall(lua_State *ls) +{ + if (lua_gettop(ls) != 2) + luaL_error(ls, "Need two arguments: tag header and value"); + writer &th(*static_cast<writer*>( lua_touserdata(ls, 1) )); + if (lua_isnumber(ls, 2)) + marshallLong(th, luaL_checklong(ls, 2)); + else if (lua_isboolean(ls, 2)) + marshallByte(th, lua_toboolean(ls, 2)); + else if (lua_isstring(ls, 2)) + marshallString(th, lua_tostring(ls, 2)); + else if (lua_isfunction(ls, 2)) + { + dlua_chunk chunk(ls); + marshallString(th, chunk.compiled_chunk()); + } + return (0); +} + +static int file_unmarshall_boolean(lua_State *ls) +{ + if (lua_gettop(ls) != 1) + luaL_error(ls, "Need reader as one argument"); + reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) )); + lua_pushboolean(ls, unmarshallByte(th)); + return (1); +} + +static int file_unmarshall_number(lua_State *ls) +{ + if (lua_gettop(ls) != 1) + luaL_error(ls, "Need reader as one argument"); + reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) )); + lua_pushnumber(ls, unmarshallLong(th)); + return (1); +} + +static int file_unmarshall_string(lua_State *ls) +{ + if (lua_gettop(ls) != 1) + luaL_error(ls, "Need reader as one argument"); + reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) )); + lua_pushstring(ls, unmarshallString(th).c_str()); + return (1); +} + +static int file_unmarshall_fn(lua_State *ls) +{ + if (lua_gettop(ls) != 1) + luaL_error(ls, "Need reader as one argument"); + reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) )); + const std::string s(unmarshallString(th, LUA_CHUNK_MAX_SIZE)); + dlua_chunk chunk = dlua_chunk::precompiled(s); + if (chunk.load(dlua)) + lua_pushnil(ls); + return (1); +} + +enum lua_persist_type +{ + LPT_NONE, + LPT_NUMBER, + LPT_STRING, + LPT_FUNCTION, + LPT_NIL, + LPT_BOOLEAN +}; + +static int file_marshall_meta(lua_State *ls) +{ + if (lua_gettop(ls) != 2) + luaL_error(ls, "Need two arguments: tag header and value"); + + writer &th(*static_cast<writer*>( lua_touserdata(ls, 1) )); + + lua_persist_type ptype = LPT_NONE; + if (lua_isnumber(ls, 2)) + ptype = LPT_NUMBER; + else if (lua_isboolean(ls, 2)) + ptype = LPT_BOOLEAN; + else if (lua_isstring(ls, 2)) + ptype = LPT_STRING; + else if (lua_isfunction(ls, 2)) + ptype = LPT_FUNCTION; + else if (lua_isnil(ls, 2)) + ptype = LPT_NIL; + else + luaL_error(ls, + make_stringf("Cannot marshall %s", + lua_typename(ls, lua_type(ls, 2))).c_str()); + marshallByte(th, ptype); + if (ptype != LPT_NIL) + file_marshall(ls); + return (0); +} + +static int file_unmarshall_meta(lua_State *ls) +{ + reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) )); + const lua_persist_type ptype = + static_cast<lua_persist_type>(unmarshallByte(th)); + switch (ptype) + { + case LPT_BOOLEAN: + return file_unmarshall_boolean(ls); + case LPT_NUMBER: + return file_unmarshall_number(ls); + case LPT_STRING: + return file_unmarshall_string(ls); + case LPT_FUNCTION: + return file_unmarshall_fn(ls); + case LPT_NIL: + lua_pushnil(ls); + return (1); + default: + luaL_error(ls, "Unexpected type signature."); + } + // Never get here. + return (0); +} + +static const struct luaL_reg file_lib[] = +{ + { "marshall", file_marshall }, + { "marshall_meta", file_marshall_meta }, + { "unmarshall_meta", file_unmarshall_meta }, + { "unmarshall_number", file_unmarshall_number }, + { "unmarshall_string", file_unmarshall_string }, + { "unmarshall_fn", file_unmarshall_fn }, + { NULL, NULL } +}; + +LUARET1(you_can_hear_pos, boolean, + player_can_hear(coord_def(luaL_checkint(ls,1), luaL_checkint(ls, 2)))) +LUARET1(you_x_pos, number, you.pos().x) +LUARET1(you_y_pos, number, you.pos().y) +LUARET2(you_pos, number, you.pos().x, you.pos().y) + +// see_cell should not be exposed to user scripts. The game should +// never disclose grid coordinates to the player. Therefore we load it +// only into the core Lua interpreter (dlua), never into the user +// script interpreter (clua). +LUARET1(you_see_cell, boolean, + see_cell(luaL_checkint(ls, 1), luaL_checkint(ls, 2))) +LUARET1(you_see_cell_no_trans, boolean, + see_cell_no_trans(luaL_checkint(ls, 1), luaL_checkint(ls, 2))) + +LUAFN(you_moveto) +{ + const coord_def place(luaL_checkint(ls, 1), luaL_checkint(ls, 2)); + ASSERT(map_bounds(place)); + you.moveto(place); + return (0); +} + +LUAFN(you_random_teleport) +{ + you_teleport_now(false, false); + return (0); +} + +LUAFN(you_losight) +{ + calc_show_los(); + return (0); +} + +static const struct luaL_reg you_lib[] = +{ + { "hear_pos", you_can_hear_pos }, + { "x_pos", you_x_pos }, + { "y_pos", you_y_pos }, + { "pos", you_pos }, + { "moveto", you_moveto }, + { "see_cell", you_see_cell }, + { "see_cell_no_trans", you_see_cell_no_trans }, + { "random_teleport", you_random_teleport }, + { "losight", you_losight }, + { NULL, NULL } +}; + +static int dgnevent_type(lua_State *ls) +{ + DEVENT(ls, 1, dev); + PLUARET(number, dev->type); +} + +static int dgnevent_place(lua_State *ls) +{ + DEVENT(ls, 1, dev); + lua_pushnumber(ls, dev->place.x); + lua_pushnumber(ls, dev->place.y); + return (2); +} + +static int dgnevent_dest(lua_State *ls) +{ + DEVENT(ls, 1, dev); + lua_pushnumber(ls, dev->dest.x); + lua_pushnumber(ls, dev->dest.y); + return (2); +} + +static int dgnevent_ticks(lua_State *ls) +{ + DEVENT(ls, 1, dev); + PLUARET(number, dev->elapsed_ticks); +} + +static int dgnevent_arg1(lua_State *ls) +{ + DEVENT(ls, 1, dev); + PLUARET(number, dev->arg1); +} + +static int dgnevent_arg2(lua_State *ls) +{ + DEVENT(ls, 1, dev); + PLUARET(number, dev->arg2); +} + +static const struct luaL_reg dgnevent_lib[] = +{ + { "type", dgnevent_type }, + { "pos", dgnevent_place }, + { "dest", dgnevent_dest }, + { "ticks", dgnevent_ticks }, + { "arg1", dgnevent_arg1 }, + { "arg2", dgnevent_arg2 }, + { NULL, NULL } +}; + +void luaopen_setmeta(lua_State *ls, + const char *global, + const luaL_reg *lua_lib, + const char *meta) +{ + luaL_newmetatable(ls, meta); + lua_setglobal(ls, global); + + luaL_openlib(ls, global, lua_lib, 0); + + // Do <global>.__index = <global> + lua_pushstring(ls, "__index"); + lua_pushvalue(ls, -2); + lua_settable(ls, -3); +} + +static void luaopen_dgnevent(lua_State *ls) +{ + luaopen_setmeta(ls, "dgnevent", dgnevent_lib, DEVENT_METATABLE); +} + +static int mapmarker_pos(lua_State *ls) +{ + MAPMARKER(ls, 1, mark); + lua_pushnumber(ls, mark->pos.x); + lua_pushnumber(ls, mark->pos.y); + return (2); +} + +static int mapmarker_move(lua_State *ls) +{ + MAPMARKER(ls, 1, mark); + const coord_def dest( luaL_checkint(ls, 2), luaL_checkint(ls, 3) ); + env.markers.move_marker(mark, dest); + return (0); +} + +static const struct luaL_reg mapmarker_lib[] = +{ + { "pos", mapmarker_pos }, + { "move", mapmarker_move }, + { NULL, NULL } +}; + +static void luaopen_mapmarker(lua_State *ls) +{ + luaopen_setmeta(ls, "mapmarker", mapmarker_lib, MAPMARK_METATABLE); +} + + +// Return the integer stored in the table (on the stack) with the key name. +// If the key doesn't exist or the value is the wrong type, return defval. +static int _table_int(lua_State *ls, int idx, const char *name, int defval) +{ + lua_pushstring(ls, name); + lua_gettable(ls, idx < 0 ? idx - 1 : idx); + bool nil = lua_isnil(ls, idx); + bool valid = lua_isnumber(ls, idx); + if (!nil && !valid) + luaL_error(ls, "'%s' in table, but not an int.", name); + int ret = (!nil && valid ? lua_tonumber(ls, idx) : defval); + lua_pop(ls, 1); + return (ret); +} + +// Return the character stored in the table (on the stack) with the key name. +// If the key doesn't exist or the value is the wrong type, return defval. +static char _table_char(lua_State *ls, int idx, const char *name, char defval) +{ + lua_pushstring(ls, name); + lua_gettable(ls, idx < 0 ? idx - 1 : idx); + bool nil = lua_isnil(ls, idx); + bool valid = lua_isstring(ls, idx); + if (!nil && !valid) + luaL_error(ls, "'%s' in table, but not a string.", name); + + char ret = defval; + if (!nil && valid) + { + const char *str = lua_tostring(ls, idx); + if (str[0] && !str[1]) + ret = str[0]; + else + luaL_error(ls, "'%s' has more than one character.", name); + } + lua_pop(ls, 1); + return (ret); +} + +// Return the string stored in the table (on the stack) with the key name. +// If the key doesn't exist or the value is the wrong type, return defval. +static const char* _table_str(lua_State *ls, int idx, const char *name, const char *defval) +{ + lua_pushstring(ls, name); + lua_gettable(ls, idx < 0 ? idx - 1 : idx); + bool nil = lua_isnil(ls, idx); + bool valid = lua_isstring(ls, idx); + if (!nil && !valid) + luaL_error(ls, "'%s' in table, but not a string.", name); + const char *ret = (!nil && valid ? lua_tostring(ls, idx) : defval); + lua_pop(ls, 1); + return (ret); +} + +// Return the boolean stored in the table (on the stack) with the key name. +// If the key doesn't exist or the value is the wrong type, return defval. +static bool _table_bool(lua_State *ls, int idx, const char *name, bool defval) +{ + lua_pushstring(ls, name); + lua_gettable(ls, idx < 0 ? idx - 1 : idx); + bool nil = lua_isnil(ls, idx); + bool valid = lua_isboolean(ls, idx); + if (!nil && !valid) + luaL_error(ls, "'%s' in table, but not a bool.", name); + bool ret = (!nil && valid ? lua_toboolean(ls, idx) : defval); + lua_pop(ls, 1); + return (ret); +} + +#define BF_INT(ls, val, def) int val = _table_int(ls, -1, #val, def); +#define BF_CHAR(ls, val, def) char val = _table_char(ls, -1, #val, def); +#define BF_STR(ls, val, def) const char *val = _table_str(ls, -1, #val, def); +#define BF_BOOL(ls, val, def) bool val = _table_bool(ls, -1, #val, def); + +static void bf_octa_room(lua_State *ls, map_lines &lines) +{ + int default_oblique = std::min(lines.width(), lines.height()) / 2 - 1; + BF_INT(ls, oblique, default_oblique); + BF_CHAR(ls, outside, 'x'); + BF_CHAR(ls, inside, '.'); + BF_STR(ls, replace, "."); + + coord_def tl, br; + if (!lines.find_bounds(replace, tl, br)) + return; + + for (rectangle_iterator ri(tl, br); ri; ++ri) + { + const coord_def mc = *ri; + char glyph = lines(mc); + if (replace[0] && !strchr(replace, glyph)) + continue; + + int ob = 0; + ob += std::max(oblique + tl.x - mc.x, 0); + ob += std::max(oblique + mc.x - br.x, 0); + + bool is_inside = (mc.y >= tl.y + ob && mc.y <= br.y - ob); + lines(mc) = is_inside ? inside : outside; + } +} + +static void bf_smear(lua_State *ls, map_lines &lines) +{ + BF_INT(ls, iterations, 1); + BF_CHAR(ls, smear, 'x'); + BF_STR(ls, onto, "."); + BF_BOOL(ls, boxy, false); + + const int max_test_per_iteration = 10; + int sanity = 0; + int max_sanity = iterations * max_test_per_iteration; + + for (int i = 0; i < iterations; i++) + { + bool diagonals, straights; + coord_def mc; + + do + { + do + { + sanity++; + mc.x = random_range(1, lines.width() - 2); + mc.y = random_range(1, lines.height() - 2); + } + while (onto[0] && !strchr(onto, lines(mc))); + + // Prevent too many iterations. + if (sanity > max_sanity) + return; + + // Is there a "smear" feature along the diagonal from mc? + diagonals = lines(coord_def(mc.x+1, mc.y+1)) == smear || + lines(coord_def(mc.x-1, mc.y+1)) == smear || + lines(coord_def(mc.x-1, mc.y-1)) == smear || + lines(coord_def(mc.x+1, mc.y-1)) == smear; + + // Is there a "smear" feature up, down, left, or right from mc? + straights = lines(coord_def(mc.x+1, mc.y)) == smear || + lines(coord_def(mc.x-1, mc.y)) == smear || + lines(coord_def(mc.x, mc.y+1)) == smear || + lines(coord_def(mc.x, mc.y-1)) == smear; + } + while (!straights && (boxy || !diagonals)); + + lines(mc) = smear; + } +} + +static void bf_extend(lua_State *ls, map_lines &lines) +{ + BF_INT(ls, height, 1); + BF_INT(ls, width, 1); + BF_CHAR(ls, fill, 'x'); + + lines.extend(width, height, fill); +} + +typedef void (*bf_func)(lua_State *ls, map_lines &lines); +struct bf_entry +{ + const char* name; + bf_func func; +}; + +// Create a separate list of builder funcs so that we can automatically +// generate a list of closures for them, rather than individually +// and explicitly exposing them to the dgn namespace. +static struct bf_entry bf_map[] = +{ + { "map_octa_room", &bf_octa_room }, + { "map_smear", &bf_smear }, + { "map_extend", &bf_extend } +}; + +static int dgn_call_builder_func(lua_State *ls) +{ + // This function gets called for all the builder functions that + // operate on map_lines. + + MAP(ls, 1, map); + if (!lua_istable(ls, 2) && !lua_isfunction(ls, 2)) + return luaL_argerror(ls, 2, "Expected table"); + + bf_func *func = (bf_func *)lua_topointer(ls, lua_upvalueindex(1)); + if (!func) + return luaL_error(ls, "Expected C function in closure upval"); + + // Put the table on top. + lua_settop(ls, 2); + + // Call the builder func itself. + (*func)(ls, map->map); + + return (0); +} + +static void _register_builder_funcs(lua_State *ls) +{ + lua_getglobal(ls, "dgn"); + + const size_t num_entries = sizeof(bf_map) / sizeof(bf_entry); + for (size_t i = 0; i < num_entries; i++) + { + // Push a closure with the C function into the dgn table. + lua_pushlightuserdata(ls, &bf_map[i].func); + lua_pushcclosure(ls, &dgn_call_builder_func, 1); + lua_setfield(ls, -2, bf_map[i].name); + } + + lua_pop(ls, 1); +} + +void init_dungeon_lua() +{ + lua_stack_cleaner clean(dlua); + + luaL_openlib(dlua, "dgn", dgn_lib, 0); + // Add additional function to the Crawl module. + luaL_openlib(dlua, "crawl", crawl_lib, 0); + luaL_openlib(dlua, "file", file_lib, 0); + luaL_openlib(dlua, "you", you_lib, 0); + luaL_openlib(dlua, "los", los_lib, 0); + + dlua.execfile("clua/dungeon.lua", true, true); + dlua.execfile("clua/luamark.lua", true, true); + + lua_getglobal(dlua, "dgn_run_map"); + luaopen_debug(dlua); + luaL_newmetatable(dlua, MAP_METATABLE); + + luaopen_dgnevent(dlua); + luaopen_mapmarker(dlua); + luaopen_ray(dlua); + + _register_builder_funcs(dlua); + + register_mapdef_tables(dlua); +} + +// Can be called from within a debugger to look at the current Lua +// call stack. (Borrowed from ToME 3) +void print_dlua_stack(void) +{ + struct lua_Debug dbg; + int i = 0; + lua_State *L = dlua.state(); + + fprintf(stderr, EOL); + while (lua_getstack(L, i++, &dbg) == 1) + { + lua_getinfo(L, "lnuS", &dbg); + + char* file = strrchr(dbg.short_src, '/'); + if (file == NULL) + file = dbg.short_src; + else + file++; + + fprintf(stderr, "%s, function %s, line %d" EOL, file, + dbg.name, dbg.currentline); + } + + fprintf(stderr, EOL); +} |