From 49eb883334f3e7ab343251cdcd161b77cb3ba784 Mon Sep 17 00:00:00 2001 From: Robert Vollmert Date: Mon, 19 Oct 2009 22:45:04 +0200 Subject: Split dgn_lib out of luadgn.cc; rename luadgn to dlua. --- crawl-ref/source/l_dgn.cc | 2893 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2893 insertions(+) create mode 100644 crawl-ref/source/l_dgn.cc (limited to 'crawl-ref/source/l_dgn.cc') diff --git a/crawl-ref/source/l_dgn.cc b/crawl-ref/source/l_dgn.cc new file mode 100644 index 0000000000..a6e05cf589 --- /dev/null +++ b/crawl-ref/source/l_dgn.cc @@ -0,0 +1,2893 @@ +#include "AppHdr.h" + +#include "dlua.h" + +#include + +#include "branch.h" +#include "chardump.h" +#include "cloud.h" +#include "initfile.h" +#include "items.h" +#include "mapmark.h" +#include "maps.h" +#include "message.h" +#include "mon-util.h" +#include "monplace.h" +#include "monstuff.h" +#include "place.h" +#include "spl-util.h" +#include "view.h" + +#define MONSLIST_METATABLE "crawldgn.monster_list" +#define ITEMLIST_METATABLE "crawldgn.item_list" + +static mons_list _lua_get_mlist(lua_State *ls, int ndx) +{ + if (lua_isstring(ls, ndx)) + { + const char *spec = lua_tostring(ls, ndx); + mons_list mlist; + const std::string err = mlist.add_mons(spec); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (mlist); + } + else + { + mons_list **mlist = + clua_get_userdata(ls, MONSLIST_METATABLE, ndx); + if (mlist) + return (**mlist); + + luaL_argerror(ls, ndx, "Expected monster list object or string"); + return mons_list(); + } +} + +static item_list _lua_get_ilist(lua_State *ls, int ndx) +{ + if (lua_isstring(ls, ndx)) + { + const char *spec = lua_tostring(ls, ndx); + + item_list ilist; + const std::string err = ilist.add_item(spec); + if (!err.empty()) + luaL_error(ls, err.c_str()); + + return (ilist); + } + else + { + item_list **ilist = + clua_get_userdata(ls, ITEMLIST_METATABLE, ndx); + if (ilist) + return (**ilist); + + luaL_argerror(ls, ndx, "Expected item list object or string"); + return item_list(); + } +} + +void register_mapdef_tables(lua_State *ls) +{ + clua_register_metatable(ls, MONSLIST_METATABLE, NULL, + lua_object_gc); + clua_register_metatable(ls, ITEMLIST_METATABLE, NULL, + lua_object_gc); +} + +template +static void _push_object_type(lua_State *ls, const char *meta, const T &data) +{ + T **ptr = clua_new_userdata(ls, meta); + *ptr = new T(data); +} + +/////////////////////////////////////////////////////////////////////////// +// Lua dungeon bindings (in the dgn table). + +static dungeon_feature_type _get_lua_feature(lua_State *ls, int idx) +{ + dungeon_feature_type feat = (dungeon_feature_type)0; + if (lua_isnumber(ls, idx)) + feat = (dungeon_feature_type)luaL_checkint(ls, idx); + else if (lua_isstring(ls, idx)) + feat = dungeon_feature_by_name(luaL_checkstring(ls, idx)); + else + luaL_argerror(ls, idx, "Feature must be a string or a feature index."); + + return feat; +} + +static dungeon_feature_type _check_lua_feature(lua_State *ls, int idx) +{ + const dungeon_feature_type f = _get_lua_feature(ls, idx); + if (!f) + luaL_argerror(ls, idx, "Invalid dungeon feature"); + return (f); +} + +static inline bool _lua_boolean(lua_State *ls, int ndx, bool defval) +{ + return lua_isnone(ls, ndx)? defval : lua_toboolean(ls, ndx); +} + +#define FEAT(f, pos) \ +dungeon_feature_type f = _check_lua_feature(ls, pos) + +void dgn_reset_default_depth() +{ + lc_default_depths.clear(); +} + +std::string dgn_set_default_depth(const std::string &s) +{ + std::vector frags = split_string(",", s); + for (int i = 0, size = frags.size(); i < size; ++i) + { + try + { + lc_default_depths.push_back( level_range::parse(frags[i]) ); + } + catch (const std::string &error) + { + return (error); + } + } + return (""); +} + +static void dgn_add_depths(depth_ranges &drs, lua_State *ls, int s, int e) +{ + for (int i = s; i <= e; ++i) + { + const char *depth = luaL_checkstring(ls, i); + std::vector frags = split_string(",", depth); + for (int j = 0, size = frags.size(); j < size; ++j) + { + try + { + drs.push_back( level_range::parse(frags[j]) ); + } + catch (const std::string &error) + { + luaL_error(ls, error.c_str()); + } + } + } +} + +static std::string dgn_depth_list_string(const depth_ranges &drs) +{ + return (comma_separated_line(drs.begin(), drs.end(), ", ", ", ")); +} + +static int dgn_depth_proc(lua_State *ls, depth_ranges &dr, int s) +{ + if (lua_gettop(ls) < s) + { + PLUARET(string, dgn_depth_list_string(dr).c_str()); + } + + if (lua_isnil(ls, s)) + { + dr.clear(); + return (0); + } + + dr.clear(); + dgn_add_depths(dr, ls, s, lua_gettop(ls)); + return (0); +} + +static int dgn_default_depth(lua_State *ls) +{ + return dgn_depth_proc(ls, lc_default_depths, 1); +} + +static int dgn_depth(lua_State *ls) +{ + MAP(ls, 1, map); + return dgn_depth_proc(ls, map->depths, 2); +} + +// WARNING: This is a very low-level call. +LUAFN(dgn_dbg_goto_place) +{ + try + { + const level_id id = level_id::parse_level_id(luaL_checkstring(ls, 1)); + you.level_type = id.level_type; + if (id.level_type == LEVEL_DUNGEON) + { + you.where_are_you = static_cast(id.branch); + you.your_level = absdungeon_depth(id.branch, id.depth); + } + } + catch (const std::string &err) + { + luaL_error(ls, err.c_str()); + } + return (0); +} + +LUAFN(dgn_dbg_flush_map_memory) +{ + dgn_flush_map_memory(); + init_level_connectivity(); + return (0); +} + +LUAFN(dgn_dbg_generate_level) +{ + no_messages mx; + env.show.init(0); + env.map.init(map_cell()); +#ifdef USE_TILE + tile_init_default_flavour(); + tile_clear_flavour(); + TileNewLevel(true); +#endif + builder(you.your_level, you.level_type); + return (0); +} + +static int dgn_place(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) > 1) + { + if (lua_isnil(ls, 2)) + map->place.clear(); + else + { + try + { + map->place = level_id::parse_level_id(luaL_checkstring(ls, 2)); + } + catch (const std::string &err) + { + luaL_error(ls, err.c_str()); + } + } + } + PLUARET(string, map->place.describe().c_str()); +} + +static int dgn_tags(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) > 1) + { + if (lua_isnil(ls, 2)) + map->tags.clear(); + else + { + const char *s = luaL_checkstring(ls, 2); + map->tags += " " + trimmed_string(s) + " "; + } + } + PLUARET(string, map->tags.c_str()); +} + +static int dgn_tags_remove(lua_State *ls) +{ + MAP(ls, 1, map); + + const int top = lua_gettop(ls); + for (int i = 2; i <= top; ++i) + { + const std::string axee = luaL_checkstring(ls, i); + const std::string::size_type pos = map->tags.find(axee); + if (pos != std::string::npos) + map->tags = + map->tags.substr(0, pos) + + map->tags.substr(pos + axee.length()); + } + PLUARET(string, map->tags.c_str()); +} + +static const std::string level_flag_names[] = +{"no_tele_control", "not_mappable", "no_magic_map", ""}; + +static int dgn_lflags(lua_State *ls) +{ + MAP(ls, 1, map); + + try + { + map->level_flags = map_flags::parse(level_flag_names, + luaL_checkstring(ls, 2)); + } + catch (const std::string &error) + { + luaL_argerror(ls, 2, error.c_str()); + } + + return (0); +} + +static int dgn_change_level_flags(lua_State *ls) +{ + map_flags flags; + + try { + flags = map_flags::parse(level_flag_names, + luaL_checkstring(ls, 1)); + } + catch (const std::string &error) + { + luaL_argerror(ls, 2, error.c_str()); + lua_pushboolean(ls, false); + return (1); + } + + bool silent = lua_toboolean(ls, 2); + + bool changed1 = set_level_flags(flags.flags_set, silent); + bool changed2 = unset_level_flags(flags.flags_unset, silent); + + lua_pushboolean(ls, changed1 || changed2); + + return (1); +} + +static const std::string branch_flag_names[] = +{"no_tele_control", "not_mappable", "no_magic_map", ""}; + +static int dgn_bflags(lua_State *ls) +{ + MAP(ls, 1, map); + + try { + map->branch_flags = map_flags::parse(branch_flag_names, + luaL_checkstring(ls, 2)); + } + catch (const std::string &error) + { + luaL_argerror(ls, 2, error.c_str()); + } + + return (0); +} + +static int dgn_change_branch_flags(lua_State *ls) +{ + map_flags flags; + + try { + flags = map_flags::parse(branch_flag_names, + luaL_checkstring(ls, 1)); + } + catch (const std::string &error) + { + luaL_argerror(ls, 2, error.c_str()); + lua_pushboolean(ls, false); + return (1); + } + + bool silent = lua_toboolean(ls, 2); + + bool changed1 = set_branch_flags(flags.flags_set, silent); + bool changed2 = unset_branch_flags(flags.flags_unset, silent); + + lua_pushboolean(ls, changed1 || changed2); + + return (1); +} + +static int dgn_set_random_mon_list(lua_State *ls) +{ + // Don't complain if we're being called when the map is being loaded + // and validated. + if (you.level_type != LEVEL_PORTAL_VAULT && + !(you.start_time == 0 && !you.entering_level && !Generating_Level)) + { + luaL_error(ls, "Can only be used in portal vaults."); + return (0); + } + + const int nargs = lua_gettop(ls); + + map_def *map = NULL; + if (nargs > 2) + { + luaL_error(ls, "Too many arguments."); + return (0); + } + else if (nargs == 0) + { + luaL_error(ls, "Too few arguments."); + return (0); + } + else if (nargs == 2) + { + map_def **_map = + clua_get_userdata(ls, MAP_METATABLE, 1); + map = *_map; + } + + if (map) + { + if (map->orient != MAP_ENCOMPASS || map->place.is_valid() + || !map->depths.empty()) + { + luaL_error(ls, "Can only be used in portal vaults."); + return (0); + } + } + + int list_pos = (map != NULL) ? 2 : 1; + mons_list mlist = _lua_get_mlist(ls, list_pos); + + if (mlist.size() == 0) + { + luaL_argerror(ls, list_pos, "Mon list is empty."); + return (0); + } + + if (mlist.size() > 1) + { + luaL_argerror(ls, list_pos, "Mon list must contain only one slot."); + return (0); + } + + const int num_mons = mlist.slot_size(0); + + if (num_mons == 0) + { + luaL_argerror(ls, list_pos, "Mon list is empty."); + return (0); + } + + std::vector mons; + int num_lords = 0; + for (int i = 0; i < num_mons; i++) + { + mons_spec mon = mlist.get_monster(0, i); + + // Pandemonium lords are pseudo-unique, so don't randomly generate + // them. + if (mon.mid == MONS_PANDEMONIUM_DEMON) + { + num_lords++; + continue; + } + + std::string name; + if (mon.place.is_valid()) + { + if (mon.place.level_type == LEVEL_LABYRINTH + || mon.place.level_type == LEVEL_PORTAL_VAULT) + { + std::string err; + err = make_stringf("mon #%d: Can't use Lab or Portal as a " + "monster place.", i + 1); + luaL_argerror(ls, list_pos, err.c_str()); + return(0); + } + name = mon.place.describe(); + } + else + { + if (mon.mid == RANDOM_MONSTER || mon.monbase == RANDOM_MONSTER) + { + std::string err; + err = make_stringf("mon #%d: can't use random monster in " + "list specifying random monsters", i + 1); + luaL_argerror(ls, list_pos, err.c_str()); + return(0); + } + if (mon.mid == -1) + mon.mid = MONS_PROGRAM_BUG; + name = mons_type_name(mon.mid, DESC_PLAIN); + } + + mons.push_back(mon); + + if (mon.number != 0) + mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : number for %s " + "being discarded.", + name.c_str()); + + if (mon.band) + mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : band request for " + "%s being ignored.", + name.c_str()); + + if (mon.colour != BLACK) + mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : colour for " + "%s being ignored.", + name.c_str()); + + if (mon.items.size() > 0) + mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : items for " + "%s being ignored.", + name.c_str()); + } // for (int i = 0; i < num_mons; i++) + + if (mons.size() == 0 && num_lords > 0) + { + luaL_argerror(ls, list_pos, + "Mon list contains only pandemonium lords."); + return (0); + } + + if (map) + map->random_mons = mons; + else + set_vault_mon_list(mons); + + return (0); +} + +static int dgn_chance(lua_State *ls) +{ + MAP(ls, 1, map); + if (!lua_isnil(ls, 2) && !lua_isnil(ls, 3)) + { + const int chance_priority = luaL_checkint(ls, 2); + const int chance = luaL_checkint(ls, 3); + if (chance < 0 || chance > CHANCE_ROLL) + luaL_argerror(ls, 2, + make_stringf("Chance must be in the range [0,%d]", + CHANCE_ROLL).c_str()); + + map->chance_priority = chance_priority; + map->chance = chance; + } + PLUARET(number, map->chance); +} + +static int dgn_weight(lua_State *ls) +{ + MAP(ls, 1, map); + if (!lua_isnil(ls, 2)) + map->weight = luaL_checkint(ls, 2); + PLUARET(number, map->weight); +} + +static int dgn_orient(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) > 1) + { + if (lua_isnil(ls, 2)) + map->orient = MAP_NONE; + else + { + const std::string orient = luaL_checkstring(ls, 2); + bool found = false; + // Note: Empty string is intentionally mapped to MAP_NONE! + for (int i = MAP_NONE; i < MAP_NUM_SECTION_TYPES; ++i) + { + if (orient == map_section_name(i)) + { + map->orient = static_cast(i); + found = true; + break; + } + } + if (!found) + luaL_error(ls, ("Bad orient: " + orient).c_str()); + } + } + PLUARET(string, map_section_name(map->orient)); +} + +static int dgn_map_add_transform( + lua_State *ls, + std::string (map_lines::*add)(const std::string &s)) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) == 1) + luaL_error(ls, "Expected args, got none."); + + for (int i = 2, size = lua_gettop(ls); i <= size; ++i) + { + if (lua_isnil(ls, i)) + { + luaL_error(ls, "Unexpected nil."); + } + else + { + std::string err = (map->map.*add)(luaL_checkstring(ls, i)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + } + } + + return (0); +} + +static int dgn_shuffle(lua_State *ls) +{ + return dgn_map_add_transform(ls, &map_lines::add_shuffle); +} + +static int dgn_subst(lua_State *ls) +{ + return dgn_map_add_transform(ls, &map_lines::add_subst); +} + +static int dgn_nsubst(lua_State *ls) +{ + return dgn_map_add_transform(ls, &map_lines::add_nsubst); +} + +static int dgn_colour(lua_State *ls) +{ + return dgn_map_add_transform(ls, &map_lines::add_colour); +} + +static int dgn_normalise(lua_State *ls) +{ + MAP(ls, 1, map); + map->map.normalise(); + return (0); +} + +static int dgn_map(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) == 1) + return dlua_stringtable(ls, map->map.get_lines()); + + if (lua_isnil(ls, 2)) + { + map->map.clear(); + return (0); + } + + // map(, x, y) = glyph at (x,y), subject to map being + // resolved and normalised. + if (lua_gettop(ls) == 3 && lua_isnumber(ls, 2) && lua_isnumber(ls, 3)) + { + const int gly = map->map.glyph(luaL_checkint(ls, 2), + luaL_checkint(ls, 3)); + char buf[2] = ""; + buf[0] = gly; + lua_pushstring(ls, buf); + return (1); + } + + if (lua_isstring(ls, 2)) + { + map->map.add_line(luaL_checkstring(ls, 2)); + return (0); + } + + std::vector &lines = map->map.get_lines(); + int which_line = luaL_checkint(ls, 2); + if (which_line < 0) + which_line += (int) lines.size(); + if (lua_gettop(ls) == 2) + { + if (which_line < 0 || which_line >= (int) lines.size()) + { + luaL_error(ls, + lines.empty()? "Map is empty" + : make_stringf("Line %d out of range (0-%u)", + which_line, + lines.size() - 1).c_str()); + } + PLUARET(string, lines[which_line].c_str()); + } + + if (lua_isnil(ls, 3)) + { + if (which_line >= 0 && which_line < (int) lines.size()) + { + lines.erase(lines.begin() + which_line); + PLUARET(boolean, true); + } + return (0); + } + + const std::string newline = luaL_checkstring(ls, 3); + if (which_line < 0) + luaL_error(ls, + make_stringf("Index %d out of range", which_line).c_str()); + + if (which_line < (int) lines.size()) + { + lines[which_line] = newline; + return (0); + } + + lines.reserve(which_line + 1); + lines.resize(which_line + 1, ""); + lines[which_line] = newline; + return (0); +} + +static int dgn_mons(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) == 1) + return (0); + + if (lua_isnil(ls, 2)) + { + map->mons.clear(); + return (0); + } + + if (lua_isstring(ls, 2)) + { + std::string err = map->mons.add_mons(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); + } + + const int index = luaL_checkint(ls, 2); + std::string err = map->mons.set_mons(index, luaL_checkstring(ls, 3)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); +} + +static int dgn_item(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) == 1) + return (0); + + if (lua_isnil(ls, 2)) + { + map->items.clear(); + return (0); + } + + if (lua_isstring(ls, 2)) + { + std::string err = map->items.add_item(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); + } + + const int index = luaL_checkint(ls, 2); + std::string err = map->items.set_item(index, luaL_checkstring(ls, 3)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); +} + +static int dgn_lua_marker(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) != 3 || !lua_isstring(ls, 2) + || (!lua_isfunction(ls, 3) && !lua_istable(ls, 3))) + { + luaL_error(ls, "Expected marker key and marker function/table."); + } + + CLua &lvm(CLua::get_vm(ls)); + std::string key = lua_tostring(ls, 2); + lua_datum function(lvm, 3, false); + + const std::string err = map->map.add_lua_marker(key, function); + if (!err.empty()) + luaL_error(ls, err.c_str()); + + return (0); +} + +static int dgn_marker(lua_State *ls) +{ + MAP(ls, 1, map); + if (lua_gettop(ls) == 1) + return (0); + if (lua_isnil(ls, 2)) + { + map->map.clear_markers(); + return (0); + } + + if (lua_isstring(ls, 2)) + { + std::string err = map->map.add_feature_marker(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + } + return (0); +} + +static int dgn_kfeat(lua_State *ls) +{ + MAP(ls, 1, map); + std::string err = map->add_key_feat(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); +} + +static int dgn_kmons(lua_State *ls) +{ + MAP(ls, 1, map); + std::string err = map->add_key_mons(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); +} + +static int dgn_kitem(lua_State *ls) +{ + MAP(ls, 1, map); + std::string err = map->add_key_item(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); +} + +static int dgn_kmask(lua_State *ls) +{ + MAP(ls, 1, map); + std::string err = map->add_key_mask(luaL_checkstring(ls, 2)); + if (!err.empty()) + luaL_error(ls, err.c_str()); + return (0); +} + +static int dgn_map_size(lua_State *ls) +{ + MAP(ls, 1, map); + lua_pushnumber(ls, map->map.width()); + lua_pushnumber(ls, map->map.height()); + return (2); +} + +static int dgn_name(lua_State *ls) +{ + MAP(ls, 1, map); + PLUARET(string, map->name.c_str()); +} + +static int dgn_welcome(lua_State *ls) +{ + MAP(ls, 1, map); + map->welcome_messages.push_back(luaL_checkstring(ls, 2)); + return (0); +} + +static int dgn_grid(lua_State *ls) +{ + GETCOORD(c, 1, 2, map_bounds); + + if (!lua_isnone(ls, 3)) + { + const dungeon_feature_type feat = _get_lua_feature(ls, 3); + if (feat) + grd(c) = feat; + } + PLUARET(number, grd(c)); +} + +LUARET1(_dgn_is_wall, boolean, + feat_is_wall(static_cast(luaL_checkint(ls, 1)))) + +static int dgn_max_bounds(lua_State *ls) +{ + lua_pushnumber(ls, GXM); + lua_pushnumber(ls, GYM); + return (2); +} + +typedef + flood_find + map_flood_finder; + +static int dgn_map_pathfind(lua_State *ls, int minargs, + bool (map_flood_finder::*f)(const coord_def &)) +{ + MAP(ls, 1, map); + const int nargs = lua_gettop(ls); + if (nargs < minargs) + return luaL_error + (ls, + make_stringf("Not enough points to test connectedness " + "(need at least %d)", minargs / 2).c_str()); + + map_def::map_feature_finder feat_finder(*map); + map_def::map_bounds_check bounds_checker(*map); + map_flood_finder finder(feat_finder, bounds_checker); + + for (int i = 4; i < nargs; i += 2) + { + const coord_def c(luaL_checkint(ls, i), + luaL_checkint(ls, i + 1)); + finder.add_point(c); + } + + const coord_def pos(luaL_checkint(ls, 2), luaL_checkint(ls, 3)); + PLUARET(boolean, (finder.*f)(pos)); +} + +static int dgn_points_connected(lua_State *ls) +{ + return dgn_map_pathfind(ls, 5, &map_flood_finder::points_connected_from); +} + +static int dgn_any_point_connected(lua_State *ls) +{ + return dgn_map_pathfind(ls, 5, &map_flood_finder::any_point_connected_from); +} + +static int dgn_has_exit_from(lua_State *ls) +{ + return dgn_map_pathfind(ls, 3, &map_flood_finder::has_exit_from); +} + +static void dlua_push_coord(lua_State *ls, const coord_def &c) +{ + lua_pushnumber(ls, c.x); + lua_pushnumber(ls, c.y); +} + +static int dgn_gly_point(lua_State *ls) +{ + MAP(ls, 1, map); + coord_def c = map->find_first_glyph(*luaL_checkstring(ls, 2)); + if (c.x != -1 && c.y != -1) + { + dlua_push_coord(ls, c); + return (2); + } + return (0); +} + +static int dgn_gly_points(lua_State *ls) +{ + MAP(ls, 1, map); + std::vector cs = map->find_glyph(*luaL_checkstring(ls, 2)); + + for (int i = 0, size = cs.size(); i < size; ++i) + dlua_push_coord(ls, cs[i]); + return (cs.size() * 2); +} + +static int dgn_original_map(lua_State *ls) +{ + MAP(ls, 1, map); + if (map->original) + clua_push_map(ls, map->original); + else + lua_pushnil(ls); + return (1); +} + +static int dgn_load_des_file(lua_State *ls) +{ + const std::string &file = luaL_checkstring(ls, 1); + if (!file.empty()) + read_map(file); + return (0); +} + +static int dgn_lfloorcol(lua_State *ls) +{ + MAP(ls, 1, map); + + if (!lua_isnone(ls, 2)) + { + const char *s = luaL_checkstring(ls, 2); + int colour = str_to_colour(s); + + if (colour < 0 || colour == BLACK) + { + std::string error; + + if (colour == BLACK) + { + error = "Can't set floor to black."; + } + else + { + error = "No such colour as '"; + error += s; + error += "'"; + } + + luaL_argerror(ls, 2, error.c_str()); + + return (0); + } + map->floor_colour = (unsigned char) colour; + } + PLUARET(string, colour_to_str(map->floor_colour).c_str()); +} + +static int dgn_lrockcol(lua_State *ls) +{ + MAP(ls, 1, map); + + if (!lua_isnone(ls, 2)) + { + const char *s = luaL_checkstring(ls, 2); + int colour = str_to_colour(s); + + if (colour < 0 || colour == BLACK) + { + std::string error; + + if (colour == BLACK) + { + error = "Can't set rock to black."; + } + else + { + error = "No such colour as '"; + error += s; + error += "'"; + } + + luaL_argerror(ls, 2, error.c_str()); + + return (0); + } + + map->rock_colour = (unsigned char) colour; + } + PLUARET(string, colour_to_str(map->rock_colour).c_str()); +} + +static int dgn_get_floor_colour(lua_State *ls) +{ + PLUARET(string, colour_to_str(env.floor_colour).c_str()); +} + +static int dgn_get_rock_colour(lua_State *ls) +{ + PLUARET(string, colour_to_str(env.rock_colour).c_str()); +} + +static int _lua_colour(lua_State *ls, int ndx, + int forbidden_colour = -1) +{ + if (lua_isnumber(ls, ndx)) + return lua_tointeger(ls, ndx); + else if (const char *s = luaL_checkstring(ls, ndx)) + { + const int colour = str_to_colour(s); + + if (colour < 0 || colour == forbidden_colour) + { + std::string error; + if (colour == forbidden_colour) + error = std::string("Can't set floor to ") + s; + else + error = std::string("Unknown colour: '") + s + "'"; + return luaL_argerror(ls, 1, error.c_str()); + } + return (colour); + } + return luaL_argerror(ls, ndx, "Expected colour name or number"); +} + +static int dgn_change_floor_colour(lua_State *ls) +{ + const int colour = _lua_colour(ls, 1, BLACK); + const bool update_now = _lua_boolean(ls, 2, false); + + env.floor_colour = (unsigned char) colour; + + if (crawl_state.need_save && update_now) + viewwindow(true, false); + return (0); +} + +static int dgn_change_rock_colour(lua_State *ls) +{ + const int colour = _lua_colour(ls, 1, BLACK); + const bool update_now = _lua_boolean(ls, 2, false); + + env.rock_colour = (unsigned char) colour; + + if (crawl_state.need_save && update_now) + viewwindow(true, false); + return (0); +} + +static int dgn_colour_at(lua_State *ls) +{ + COORDS(c, 1, 2); + if (!lua_isnone(ls, 3)) + env.grid_colours(c) = _lua_colour(ls, 3); + PLUARET(string, colour_to_str(env.grid_colours(c)).c_str()); +} + +const char *dngn_feature_names[] = +{ +"unseen", "closed_door", "detected_secret_door", "secret_door", +"wax_wall", "metal_wall", "green_crystal_wall", "rock_wall", "stone_wall", +"permarock_wall", +"clear_rock_wall", "clear_stone_wall", "clear_permarock_wall", "trees", +"open_sea", "orcish_idol", "", "", "", "", "", +"granite_statue", "statue_reserved_1", "statue_reserved_2", +"", "", "", "", "", "", "", "", +"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", +"", "", "", "", "", "", "", "", "", "", "", "", "", "lava", +"deep_water", "", "", "shallow_water", "water_stuck", "floor", +"floor_special", "floor_reserved", "exit_hell", "enter_hell", +"open_door", "", "", "trap_mechanical", "trap_magical", "trap_natural", +"undiscovered_trap", "", "enter_shop", "enter_labyrinth", +"stone_stairs_down_i", "stone_stairs_down_ii", +"stone_stairs_down_iii", "escape_hatch_down", "stone_stairs_up_i", +"stone_stairs_up_ii", "stone_stairs_up_iii", "escape_hatch_up", "", +"", "enter_dis", "enter_gehenna", "enter_cocytus", +"enter_tartarus", "enter_abyss", "exit_abyss", "stone_arch", +"enter_pandemonium", "exit_pandemonium", "transit_pandemonium", +"", "", "", "builder_special_wall", "builder_special_floor", "", +"", "", "enter_orcish_mines", "enter_hive", "enter_lair", +"enter_slime_pits", "enter_vaults", "enter_crypt", +"enter_hall_of_blades", "enter_zot", "enter_temple", +"enter_snake_pit", "enter_elven_halls", "enter_tomb", +"enter_swamp", "enter_shoals", "enter_reserved_2", +"enter_reserved_3", "enter_reserved_4", "", "", "", +"return_from_orcish_mines", "return_from_hive", +"return_from_lair", "return_from_slime_pits", +"return_from_vaults", "return_from_crypt", +"return_from_hall_of_blades", "return_from_zot", +"return_from_temple", "return_from_snake_pit", +"return_from_elven_halls", "return_from_tomb", +"return_from_swamp", "return_from_shoals", "return_reserved_2", +"return_reserved_3", "return_reserved_4", "", "", "", "", "", "", +"", "", "", "", "", "", "", "enter_portal_vault", "exit_portal_vault", +"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", +"", "", "altar_zin", "altar_shining_one", "altar_kikubaaqudgha", +"altar_yredelemnul", "altar_xom", "altar_vehumet", +"altar_okawaru", "altar_makhleb", "altar_sif_muna", "altar_trog", +"altar_nemelex_xobeh", "altar_elyvilon", "altar_lugonu", +"altar_beogh", "altar_jiyva", "altar_feawn", "", "", "", "", +"fountain_blue", "fountain_sparkling", "fountain_blood", +"dry_fountain_blue", "dry_fountain_sparkling", "dry_fountain_blood", +"permadry_fountain", "abandoned_shop" +}; + +dungeon_feature_type dungeon_feature_by_name(const std::string &name) +{ + COMPILE_CHECK(ARRAYSZ(dngn_feature_names) == NUM_REAL_FEATURES, c1); + if (name.empty()) + return (DNGN_UNSEEN); + + for (unsigned i = 0; i < ARRAYSZ(dngn_feature_names); ++i) + if (dngn_feature_names[i] == name) + return static_cast(i); + + return (DNGN_UNSEEN); +} + +std::vector dungeon_feature_matches(const std::string &name) +{ + std::vector matches; + + COMPILE_CHECK(ARRAYSZ(dngn_feature_names) == NUM_REAL_FEATURES, c1); + if (name.empty()) + return (matches); + + for (unsigned i = 0; i < ARRAYSZ(dngn_feature_names); ++i) + if (strstr(dngn_feature_names[i], name.c_str())) + matches.push_back(dngn_feature_names[i]); + + return (matches); +} + +const char *dungeon_feature_name(dungeon_feature_type rfeat) +{ + const unsigned feat = rfeat; + + if (feat >= ARRAYSZ(dngn_feature_names)) + return (NULL); + + return dngn_feature_names[feat]; +} + +static int dgn_feature_number(lua_State *ls) +{ + const std::string &name = luaL_checkstring(ls, 1); + PLUARET(number, dungeon_feature_by_name(name)); +} + +static int dgn_feature_name(lua_State *ls) +{ + const unsigned feat = luaL_checkint(ls, 1); + PLUARET(string, + dungeon_feature_name(static_cast(feat))); +} + +static const char *dgn_event_type_names[] = +{ +"none", "turn", "mons_move", "player_move", "leave_level", +"entering_level", "entered_level", "player_los", "player_climb", +"monster_dies", "item_pickup", "item_moved", "feat_change", +"wall_hit" +}; + +static dgn_event_type dgn_event_type_by_name(const std::string &name) +{ + for (unsigned i = 0; i < ARRAYSZ(dgn_event_type_names); ++i) + if (dgn_event_type_names[i] == name) + return static_cast(i? 1 << (i - 1) : 0); + return (DET_NONE); +} + +static const char *dgn_event_type_name(unsigned evmask) +{ + if (evmask == 0) + return (dgn_event_type_names[0]); + + for (unsigned i = 1; i < ARRAYSZ(dgn_event_type_names); ++i) + if (evmask & (1 << (i - 1))) + return (dgn_event_type_names[i]); + + return (dgn_event_type_names[0]); +} + +static void dgn_push_event_type(lua_State *ls, int n) +{ + if (lua_isstring(ls, n)) + lua_pushnumber(ls, dgn_event_type_by_name(lua_tostring(ls, n))); + else if (lua_isnumber(ls, n)) + lua_pushstring(ls, dgn_event_type_name(luaL_checkint(ls, n))); + else + lua_pushnil(ls); +} + +static int dgn_dgn_event(lua_State *ls) +{ + const int start = lua_isuserdata(ls, 1)? 2 : 1; + int retvals = 0; + for (int i = start, nargs = lua_gettop(ls); i <= nargs; ++i) + { + dgn_push_event_type(ls, i); + retvals++; + } + return (retvals); +} + +static int dgn_register_listener(lua_State *ls) +{ + unsigned mask = luaL_checkint(ls, 1); + MAPMARKER(ls, 2, mark); + map_lua_marker *listener = dynamic_cast(mark); + coord_def pos; + // Was a position supplied? + if (lua_gettop(ls) == 4) + { + pos.x = luaL_checkint(ls, 3); + pos.y = luaL_checkint(ls, 4); + } + + dungeon_events.register_listener(mask, listener, pos); + return (0); +} + +static int dgn_remove_listener(lua_State *ls) +{ + MAPMARKER(ls, 1, mark); + map_lua_marker *listener = dynamic_cast(mark); + coord_def pos; + // Was a position supplied? + if (lua_gettop(ls) == 3) + { + pos.x = luaL_checkint(ls, 2); + pos.y = luaL_checkint(ls, 3); + } + dungeon_events.remove_listener(listener, pos); + return (0); +} + +static int dgn_remove_marker(lua_State *ls) +{ + MAPMARKER(ls, 1, mark); + env.markers.remove(mark); + return (0); +} + +static int dgn_num_matching_markers(lua_State *ls) +{ + const char* key = luaL_checkstring(ls, 1); + const char* val_ptr = lua_tostring(ls, 2); + const char* val; + + if (val_ptr == NULL) + val = ""; + else + val = val_ptr; + + std::vector markers = env.markers.get_all(key, val); + + PLUARET(number, markers.size()); +} + +static int dgn_feature_desc(lua_State *ls) +{ + const dungeon_feature_type feat = + static_cast(luaL_checkint(ls, 1)); + const description_level_type dtype = + lua_isnumber(ls, 2)? + static_cast(luaL_checkint(ls, 2)) : + description_type_by_name(lua_tostring(ls, 2)); + const bool need_stop = lua_isboolean(ls, 3)? lua_toboolean(ls, 3) : false; + const std::string s = + feature_description(feat, NUM_TRAPS, false, dtype, need_stop); + lua_pushstring(ls, s.c_str()); + return (1); +} + +static int dgn_feature_desc_at(lua_State *ls) +{ + const description_level_type dtype = + lua_isnumber(ls, 3)? + static_cast(luaL_checkint(ls, 3)) : + description_type_by_name(lua_tostring(ls, 3)); + const bool need_stop = lua_isboolean(ls, 4)? lua_toboolean(ls, 4) : false; + const std::string s = + feature_description(coord_def(luaL_checkint(ls, 1), + luaL_checkint(ls, 2)), + false, dtype, need_stop); + lua_pushstring(ls, s.c_str()); + return (1); +} + +static int dgn_set_feature_desc_short(lua_State *ls) +{ + const std::string base_name = luaL_checkstring(ls, 1); + const std::string desc = luaL_checkstring(ls, 2); + + if (base_name.empty()) + { + luaL_argerror(ls, 1, "Base name can't be empty"); + return (0); + } + + set_feature_desc_short(base_name, desc); + + return (0); +} + +static int dgn_set_feature_desc_long(lua_State *ls) +{ + const std::string raw_name = luaL_checkstring(ls, 1); + const std::string desc = luaL_checkstring(ls, 2); + + if (raw_name.empty()) + { + luaL_argerror(ls, 1, "Raw name can't be empty"); + return (0); + } + + set_feature_desc_long(raw_name, desc); + + return (0); +} + +static int dgn_terrain_changed(lua_State *ls) +{ + dungeon_feature_type type = DNGN_UNSEEN; + if (lua_isnumber(ls, 3)) + type = static_cast(luaL_checkint(ls, 3)); + else if (lua_isstring(ls, 3)) + type = dungeon_feature_by_name(lua_tostring(ls, 3)); + const bool affect_player = + lua_isboolean(ls, 4)? lua_toboolean(ls, 4) : true; + const bool preserve_features = + lua_isboolean(ls, 5)? lua_toboolean(ls, 5) : true; + const bool preserve_items = + lua_isboolean(ls, 6)? lua_toboolean(ls, 6) : true; + dungeon_terrain_changed( coord_def( luaL_checkint(ls, 1), + luaL_checkint(ls, 2) ), + type, affect_player, + preserve_features, preserve_items ); + return (0); +} + +static int dgn_item_from_index(lua_State *ls) +{ + const int index = luaL_checkint(ls, 1); + + item_def *item = &mitm[index]; + + if (is_valid_item(*item)) + lua_pushlightuserdata(ls, item); + else + lua_pushnil(ls); + + return (1); +} + +static int dgn_mons_from_index(lua_State *ls) +{ + const int index = luaL_checkint(ls, 1); + + monsters *mons = &menv[index]; + + if (mons->type != -1) + push_monster(ls, mons); + else + lua_pushnil(ls); + + return (1); +} + +static int dgn_mons_at(lua_State *ls) +{ + COORDS(c, 1, 2); + + monsters *mon = monster_at(c); + if (mon && mon->alive()) + push_monster(ls, mon); + else + lua_pushnil(ls); + return (1); +} + +static int dgn_items_at(lua_State *ls) +{ + COORDS(c, 1, 2); + lua_push_items(ls, env.igrid(c)); + return (1); +} + +static int lua_dgn_set_lt_callback(lua_State *ls) +{ + const char *level_type = luaL_checkstring(ls, 1); + + if (level_type == NULL || strlen(level_type) == 0) + return (0); + + const char *callback_name = luaL_checkstring(ls, 2); + + if (callback_name == NULL || strlen(callback_name) == 0) + return (0); + + dgn_set_lt_callback(level_type, callback_name); + + return (0); +} + +static int dgn_fixup_stairs(lua_State *ls) +{ + const dungeon_feature_type up_feat = + dungeon_feature_by_name(luaL_checkstring(ls, 1)); + + const dungeon_feature_type down_feat = + dungeon_feature_by_name(luaL_checkstring(ls, 2)); + + if (up_feat == DNGN_UNSEEN && down_feat == DNGN_UNSEEN) + return(0); + + for (int y = 0; y < GYM; ++y) + { + for (int x = 0; x < GXM; ++x) + { + const dungeon_feature_type feat = grd[x][y]; + if (feat_is_stone_stair(feat) || feat_is_escape_hatch(feat)) + { + dungeon_feature_type new_feat = DNGN_UNSEEN; + + if (feat_stair_direction(feat) == CMD_GO_DOWNSTAIRS) + new_feat = down_feat; + else + new_feat = up_feat; + + if (new_feat != DNGN_UNSEEN) + { + grd[x][y] = new_feat; + env.markers.add( + new map_feature_marker( + coord_def(x, y), + new_feat)); + } + } + } + } + + return (0); +} + +#ifdef USE_TILE +static unsigned int _get_tile_idx(lua_State *ls, int arg) +{ + if (!lua_isstring(ls, arg)) + { + luaL_argerror(ls, arg, "Expected string for tile name"); + return 0; + } + + const char *tile_name = luaL_checkstring(ls, arg); + + unsigned int idx; + if (!tile_dngn_index(tile_name, idx)) + { + std::string error = "Couldn't find tile '"; + error += tile_name; + error += "'"; + luaL_argerror(ls, arg, error.c_str()); + return 0; + } + + return idx; +} +#endif + +static int dgn_floor_halo(lua_State *ls) +{ + std::string error = ""; + + const char *s1 = luaL_checkstring(ls, 1); + const dungeon_feature_type target = dungeon_feature_by_name(s1); + + if (target == DNGN_UNSEEN) + { + error += "No such dungeon feature as '"; + error += s1; + error += "'. "; + } + + const char *s2 = luaL_checkstring(ls, 2); + short colour = str_to_colour(s2); + + if (colour == -1) + { + error += "No such colour as '"; + error += s2; + error += "'."; + } + else if (colour == BLACK) + error += "Can't set floor colour to black."; + + if (!error.empty()) + { + luaL_argerror(ls, 2, error.c_str()); + return(0); + } + + for (int y = 0; y < GYM; ++y) + for (int x = 0; x < GXM; ++x) + { + const dungeon_feature_type feat = grd[x][y]; + if (feat == target) + { + + for (int i = -1; i <= 1; i++) + for (int j = -1; j <= 1; j++) + { + if (!map_bounds(x+i, y+j)) + continue; + + const dungeon_feature_type feat2 = grd[x+i][y+j]; + + if (feat2 == DNGN_FLOOR + || feat2 == DNGN_UNDISCOVERED_TRAP) + { + env.grid_colours[x+i][y+j] = colour; + } + } + } + } + +#ifdef USE_TILE + unsigned int tile = _get_tile_idx(ls, 3); + if (!tile) + return (0); + if (tile_dngn_count(tile) != 9) + { + error += "'"; + error += luaL_checkstring(ls, 3); + error += "' is not a valid halo tile. It has "; + error += tile_dngn_count(tile); + error += " variations, but needs exactly 9."; + luaL_argerror(ls, 3, error.c_str()); + return (0); + } + + tile_floor_halo(target, tile); +#endif + + return (0); +} + +#define SQRT_2 1.41421356237309504880 + +static int dgn_random_walk(lua_State *ls) +{ + const int x = luaL_checkint(ls, 1); + const int y = luaL_checkint(ls, 2); + const int dist = luaL_checkint(ls, 3); + + // Fourth param being true means that we can move past + // statues. + const dungeon_feature_type minmove = + lua_isnil(ls, 4) ? DNGN_MINMOVE : DNGN_ORCISH_IDOL; + + if (!in_bounds(x, y)) + { + char buf[80]; + sprintf(buf, "Point (%d,%d) isn't in bounds.", x, y); + luaL_argerror(ls, 1, buf); + return (0); + } + if (dist < 1) + { + luaL_argerror(ls, 3, "Distance must be positive."); + return (0); + } + + float dist_left = dist; + // Allow movement to all 8 adjacent squares if distance is 1 + // (needed since diagonal moves are distance sqrt(2)) + if (dist == 1) + dist_left = (float)SQRT_2; + + int moves_left = dist; + coord_def pos(x, y); + while (dist_left >= 1.0 && moves_left-- > 0) + { + int okay_dirs = 0; + int dir = -1; + for (int j = 0; j < 8; j++) + { + const coord_def new_pos = pos + Compass[j]; + const float move_dist = (j % 2 == 0) ? 1.0 : SQRT_2; + + if (in_bounds(new_pos) && grd(new_pos) >= minmove + && move_dist <= dist_left) + { + if (one_chance_in(++okay_dirs)) + dir = j; + } + } + + if (okay_dirs == 0) + break; + + if (one_chance_in(++okay_dirs)) + continue; + + pos += Compass[dir]; + dist_left -= (dir % 2 == 0) ? 1.0 : SQRT_2; + } + + dlua_push_coord(ls, pos); + + return (2); +} + +static cloud_type dgn_cloud_name_to_type(std::string name) +{ + lowercase(name); + + if (name == "random") + return (CLOUD_RANDOM); + else if (name == "debugging") + return (CLOUD_DEBUGGING); + + for (int i = CLOUD_NONE; i < CLOUD_RANDOM; i++) + if (cloud_name(static_cast(i)) == name) + return static_cast(i); + + return (CLOUD_NONE); +} + +static kill_category dgn_kill_name_to_category(std::string name) +{ + if (name.empty()) + return KC_OTHER; + + lowercase(name); + + if (name == "you") + return KC_YOU; + else if (name == "friendly") + return KC_FRIENDLY; + else if (name == "other") + return KC_OTHER; + else + return KC_NCATEGORIES; +} + +static int lua_cloud_pow_min; +static int lua_cloud_pow_max; +static int lua_cloud_pow_rolls; + +static int make_a_lua_cloud(coord_def where, int garbage, int spread_rate, + cloud_type ctype, kill_category whose, + killer_type killer) +{ + UNUSED( garbage ); + + const int pow = random_range(lua_cloud_pow_min, + lua_cloud_pow_max, + lua_cloud_pow_rolls); + place_cloud( ctype, where, pow, whose, killer, spread_rate ); + return 1; +} + +static int dgn_apply_area_cloud(lua_State *ls) +{ + const int x = luaL_checkint(ls, 1); + const int y = luaL_checkint(ls, 2); + lua_cloud_pow_min = luaL_checkint(ls, 3); + lua_cloud_pow_max = luaL_checkint(ls, 4); + lua_cloud_pow_rolls = luaL_checkint(ls, 5); + const int size = luaL_checkint(ls, 6); + + const cloud_type ctype = dgn_cloud_name_to_type(luaL_checkstring(ls, 7)); + const char* kname = lua_isstring(ls, 8) ? luaL_checkstring(ls, 8) + : ""; + const kill_category kc = dgn_kill_name_to_category(kname); + + const int spread_rate = lua_isnumber(ls, 9) ? luaL_checkint(ls, 9) : -1; + + if (!in_bounds(x, y)) + { + char buf[80]; + sprintf(buf, "Point (%d,%d) isn't in bounds.", x, y); + luaL_argerror(ls, 1, buf); + return (0); + } + + if (lua_cloud_pow_min < 0) + { + luaL_argerror(ls, 4, "pow_min must be non-negative"); + return (0); + } + + if (lua_cloud_pow_max < lua_cloud_pow_min) + { + luaL_argerror(ls, 5, "pow_max must not be less than pow_min"); + return (0); + } + + if (lua_cloud_pow_max == 0) + { + luaL_argerror(ls, 5, "pow_max must be positive"); + return (0); + } + + if (lua_cloud_pow_rolls <= 0) + { + luaL_argerror(ls, 6, "pow_rolls must be positive"); + return (0); + } + + if (size < 1) + { + luaL_argerror(ls, 4, "size must be positive."); + return (0); + } + + if (ctype == CLOUD_NONE) + { + std::string error = "Invalid cloud type '"; + error += luaL_checkstring(ls, 7); + error += "'"; + luaL_argerror(ls, 7, error.c_str()); + return (0); + } + + if (kc == KC_NCATEGORIES) + { + std::string error = "Invalid kill category '"; + error += kname; + error += "'"; + luaL_argerror(ls, 8, error.c_str()); + return (0); + } + + if (spread_rate < -1 || spread_rate > 100) + { + luaL_argerror(ls, 9, "spread_rate must be between -1 and 100," + "inclusive"); + return (0); + } + + apply_area_cloud(make_a_lua_cloud, coord_def(x, y), 0, size, + ctype, kc, cloud_struct::whose_to_killer(kc), + spread_rate); + + return (0); +} + +static void _clamp_to_bounds(int &x, int &y, bool edge_ok = false) +{ + const int edge_offset = edge_ok ? 0 : 1; + x = std::min(std::max(x, X_BOUND_1 + edge_offset), X_BOUND_2 - edge_offset); + y = std::min(std::max(y, Y_BOUND_1 + edge_offset), Y_BOUND_2 - edge_offset); +} + +// Return a metatable for a point on the map_lines grid. +static int dgn_grd_table(lua_State *ls) +{ + MAP(ls, 1, map); + + map_def **mapref = clua_new_userdata(ls, GRD_METATABLE); + *mapref = map; + + return (1); +} + +static int dgn_width(lua_State *ls) +{ + MAP(ls, 1, map); + + lua_pushnumber(ls, map->map.width()); + return (1); +} + +static int dgn_height(lua_State *ls) +{ + MAP(ls, 1, map); + + lua_pushnumber(ls, map->map.height()); + return (1); +} + +static int dgn_fill_area(lua_State *ls) +{ + int x1 = luaL_checkint(ls, 1); + int y1 = luaL_checkint(ls, 2); + int x2 = luaL_checkint(ls, 3); + int y2 = luaL_checkint(ls, 4); + dungeon_feature_type feat = _check_lua_feature(ls, 5); + + _clamp_to_bounds(x1, y1); + _clamp_to_bounds(x2, y2); + if (x2 < x1) + std::swap(x1, x2); + if (y2 < y1) + std::swap(y1, y2); + + for (int y = y1; y <= y2; y++) + for (int x = x1; x <= x2; x++) + grd[x][y] = feat; + + return 0; +} + +static int dgn_replace_area(lua_State *ls) +{ + int x1 = luaL_checkint(ls, 1); + int y1 = luaL_checkint(ls, 2); + int x2 = luaL_checkint(ls, 3); + int y2 = luaL_checkint(ls, 4); + dungeon_feature_type search = _check_lua_feature(ls, 5); + dungeon_feature_type replace = _check_lua_feature(ls, 6); + + // gracefully handle out of bound areas by truncating them. + _clamp_to_bounds(x1, y1); + _clamp_to_bounds(x2, y2); + if (x2 < x1) + std::swap(x1, x2); + if (y2 < y1) + std::swap(y1, y2); + + for (int y = y1; y <= y2; y++) + for (int x = x1; x <= x2; x++) + if (grd[x][y] == search) + grd[x][y] = replace; + + return 0; +} + +static int dgn_octa_room(lua_State *ls) +{ + int x1 = luaL_checkint(ls, 1); + int y1 = luaL_checkint(ls, 2); + int x2 = luaL_checkint(ls, 3); + int y2 = luaL_checkint(ls, 4); + int oblique = luaL_checkint(ls, 5); + dungeon_feature_type fill = _check_lua_feature(ls, 6); + + spec_room sr; + sr.tl.x = x1; + sr.br.x = x2; + sr.tl.y = y1; + sr.br.y = y2; + + octa_room(sr, oblique, fill); + + return 0; +} + +static int dgn_make_pillars(lua_State *ls) +{ + int center_x = luaL_checkint(ls, 1); + int center_y = luaL_checkint(ls, 2); + int num = luaL_checkint(ls, 3); + int scale_x = luaL_checkint(ls, 4); + int big_radius = luaL_checkint(ls, 5); + int pillar_radius = luaL_checkint(ls, 6); + dungeon_feature_type fill = _check_lua_feature(ls, 8); + + // [enne] The underscore is for DJGPP's brain damage. + const float _PI = 3.14159265f; + for (int n = 0; n < num; n++) + { + float angle = n * 2 * _PI / (float)num; + int x = (int)std::floor(std::cos(angle) * big_radius * scale_x + 0.5f); + int y = (int)std::floor(std::sin(angle) * big_radius + 0.5f); + + lua_pushvalue(ls, 7); + lua_pushnumber(ls, center_x + x); + lua_pushnumber(ls, center_y + y); + lua_pushnumber(ls, pillar_radius); + lua_pushnumber(ls, fill); + + lua_call(ls, 4, 0); + } + + return 0; +} + +static int dgn_make_square(lua_State *ls) +{ + int center_x = luaL_checkint(ls, 1); + int center_y = luaL_checkint(ls, 2); + int radius = std::abs(luaL_checkint(ls, 3)); + dungeon_feature_type fill = _check_lua_feature(ls, 4); + + for (int x = -radius; x <= radius; x++) + for (int y = -radius; y <= radius; y++) + grd[center_x + x][center_y + y] = fill; + + return 0; +} + +static int dgn_make_rounded_square(lua_State *ls) +{ + int center_x = luaL_checkint(ls, 1); + int center_y = luaL_checkint(ls, 2); + int radius = std::abs(luaL_checkint(ls, 3)); + dungeon_feature_type fill = _check_lua_feature(ls, 4); + + for (int x = -radius; x <= radius; x++) + for (int y = -radius; y <= radius; y++) + if (std::abs(x) != radius || std::abs(y) != radius) + grd[center_x + x][center_y + y] = fill; + + return 0; +} + +static int dgn_make_circle(lua_State *ls) +{ + int center_x = luaL_checkint(ls, 1); + int center_y = luaL_checkint(ls, 2); + int radius = std::abs(luaL_checkint(ls, 3)); + dungeon_feature_type fill = _check_lua_feature(ls, 4); + + for (int x = -radius; x <= radius; x++) + for (int y = -radius; y <= radius; y++) + if (x * x + y * y < radius * radius) + grd[center_x + x][center_y + y] = fill; + + return 0; +} + +static int dgn_in_bounds(lua_State *ls) +{ + int x = luaL_checkint(ls, 1); + int y = luaL_checkint(ls, 2); + + lua_pushboolean(ls, in_bounds(x, y)); + return 1; +} + +static int dgn_replace_first(lua_State *ls) +{ + int x = luaL_checkint(ls, 1); + int y = luaL_checkint(ls, 2); + int dx = luaL_checkint(ls, 3); + int dy = luaL_checkint(ls, 4); + dungeon_feature_type search = _check_lua_feature(ls, 5); + dungeon_feature_type replace = _check_lua_feature(ls, 6); + + _clamp_to_bounds(x, y); + bool found = false; + while (in_bounds(x, y)) + { + if (grd[x][y] == search) + { + grd[x][y] = replace; + found = true; + break; + } + + x += dx; + y += dy; + } + + lua_pushboolean(ls, found); + return 1; +} + +static int dgn_replace_random(lua_State *ls) +{ + dungeon_feature_type search = _check_lua_feature(ls, 1); + dungeon_feature_type replace = _check_lua_feature(ls, 2); + + int x, y; + do + { + x = random2(GXM); + y = random2(GYM); + } + while (grd[x][y] != search); + + grd[x][y] = replace; + + return 0; +} + +static int dgn_spotty_level(lua_State *ls) +{ + bool seeded = lua_toboolean(ls, 1); + int iterations = luaL_checkint(ls, 2); + bool boxy = lua_toboolean(ls, 3); + + spotty_level(seeded, iterations, boxy); + return 0; +} + +static int dgn_smear_feature(lua_State *ls) +{ + int iterations = luaL_checkint(ls, 1); + bool boxy = lua_toboolean(ls, 2); + dungeon_feature_type feat = _check_lua_feature(ls, 3); + + int x1 = luaL_checkint(ls, 4); + int y1 = luaL_checkint(ls, 5); + int x2 = luaL_checkint(ls, 6); + int y2 = luaL_checkint(ls, 7); + + _clamp_to_bounds(x1, y1, true); + _clamp_to_bounds(x2, y2, true); + + smear_feature(iterations, boxy, feat, x1, y1, x2, y2); + + return 0; +} + +static int dgn_count_feature_in_box(lua_State *ls) +{ + int x1 = luaL_checkint(ls, 1); + int y1 = luaL_checkint(ls, 2); + int x2 = luaL_checkint(ls, 3); + int y2 = luaL_checkint(ls, 4); + dungeon_feature_type feat = _check_lua_feature(ls, 5); + + lua_pushnumber(ls, count_feature_in_box(x1, y1, x2, y2, feat)); + return 1; +} + +static int dgn_count_antifeature_in_box(lua_State *ls) +{ + int x1 = luaL_checkint(ls, 1); + int y1 = luaL_checkint(ls, 2); + int x2 = luaL_checkint(ls, 3); + int y2 = luaL_checkint(ls, 4); + dungeon_feature_type feat = _check_lua_feature(ls, 5); + + lua_pushnumber(ls, count_antifeature_in_box(x1, y1, x2, y2, feat)); + return 1; +} + +static int dgn_count_neighbours(lua_State *ls) +{ + int x = luaL_checkint(ls, 1); + int y = luaL_checkint(ls, 2); + dungeon_feature_type feat = _check_lua_feature(ls, 3); + + lua_pushnumber(ls, count_neighbours(x, y, feat)); + return 1; +} + +static int dgn_join_the_dots(lua_State *ls) +{ + int from_x = luaL_checkint(ls, 1); + int from_y = luaL_checkint(ls, 2); + int to_x = luaL_checkint(ls, 3); + int to_y = luaL_checkint(ls, 4); + // TODO enne - push map masks to lua? + unsigned map_mask = MMT_VAULT; + bool early_exit = lua_toboolean(ls, 5); + + coord_def from(from_x, from_y); + coord_def to(to_x, to_y); + + bool ret = join_the_dots(from, to, map_mask, early_exit); + lua_pushboolean(ls, ret); + + return 1; +} + +static int dgn_fill_disconnected_zones(lua_State *ls) +{ + int from_x = luaL_checkint(ls, 1); + int from_y = luaL_checkint(ls, 2); + int to_x = luaL_checkint(ls, 3); + int to_y = luaL_checkint(ls, 4); + + dungeon_feature_type feat = _check_lua_feature(ls, 5); + + process_disconnected_zones(from_x, from_y, to_x, to_y, true, feat); + + return 0; +} + +LUAFN(_dgn_is_opaque) +{ + COORDS(c, 1, 2); + lua_pushboolean(ls, feat_is_opaque(grd(c))); + return (1); +} + +static int _dgn_is_passable(lua_State *ls) +{ + COORDS(c, 1, 2); + lua_pushboolean(ls, is_travelsafe_square(c, false, true)); + return (1); +} + +static int dgn_register_feature_marker(lua_State *ls) +{ + COORDS(c, 1, 2); + FEAT(feat, 3); + env.markers.add( new map_feature_marker(c, feat) ); + return (0); +} + +static int dgn_register_lua_marker(lua_State *ls) +{ + COORDS(c, 1, 2); + if (!lua_istable(ls, 3) && !lua_isfunction(ls, 3)) + return luaL_argerror(ls, 3, "Expected marker table or function"); + + lua_datum table(CLua::get_vm(ls), 3, false); + map_marker *marker = new map_lua_marker(table); + marker->pos = c; + env.markers.add(marker); + return (0); +} + +static int dgn_create_monster(lua_State *ls) +{ + COORDS(c, 1, 2); + + mons_list mlist = _lua_get_mlist(ls, 3); + for (int i = 0, size = mlist.size(); i < size; ++i) + { + mons_spec mspec = mlist.get_monster(i); + const int mid = dgn_place_monster(mspec, you.your_level, c, + false, false, false); + if (mid != -1) + { + push_monster(ls, &menv[mid]); + return (1); + } + } + lua_pushnil(ls); + return (1); +} + +static int _dgn_monster_spec(lua_State *ls) +{ + const mons_list mlist = _lua_get_mlist(ls, 1); + _push_object_type(ls, MONSLIST_METATABLE, mlist); + return (1); +} + +static int _dgn_item_spec(lua_State *ls) +{ + const item_list ilist = _lua_get_ilist(ls, 1); + _push_object_type(ls, ITEMLIST_METATABLE, ilist); + return (1); +} + +LUARET1(_dgn_max_monsters, number, MAX_MONSTERS) + +static int dgn_create_item(lua_State *ls) +{ + COORDS(c, 1, 2); + + item_list ilist = _lua_get_ilist(ls, 3); + const int level = + lua_isnumber(ls, 4) ? lua_tointeger(ls, 4) : you.your_level; + + dgn_place_multiple_items(ilist, c, level); + link_items(); + return (0); +} + +static std::auto_ptr _dgn_map_safe_bounds_fn; + +static bool _lua_map_place_valid(const map_def &map, + const coord_def &c, + const coord_def &size) +{ +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "lua_map_place_invalid: (%d,%d) (%d,%d)", + c.x, c.y, size.x, size.y); +#endif + + lua_stack_cleaner clean(_dgn_map_safe_bounds_fn->lua); + + // Push the Lua function onto the stack. + _dgn_map_safe_bounds_fn->push(); + + lua_State *ls = _dgn_map_safe_bounds_fn->lua; + + // Push map, pos.x, pos.y, size.x, size.y + clua_push_map(ls, const_cast(&map)); + clua_push_coord(ls, c); + clua_push_coord(ls, size); + + const int err = lua_pcall(ls, 5, 1, 0); + + // Lua error invalidates place. + if (err) + { + mprf(MSGCH_ERROR, "Lua error: %s", lua_tostring(ls, -1)); + return (true); + } + + return (lua_toboolean(ls, -1)); +} + +LUAFN(dgn_with_map_bounds_fn) +{ + CLua &vm(CLua::get_vm(ls)); + if (lua_gettop(ls) != 2 || !lua_isfunction(ls, 1) || !lua_isfunction(ls, 2)) + luaL_error(ls, "Expected map-bounds check fn and action fn."); + + _dgn_map_safe_bounds_fn.reset(new lua_datum(vm, 1, false)); + + int err = 0; + { + unwind_var mpc(map_place_valid, + _lua_map_place_valid); + + // All set, call our friend, the second function. + ASSERT(lua_isfunction(ls, -1)); + + // Copy the function since pcall will pop it off. + lua_pushvalue(ls, -1); + + // Use pcall to catch the error here, else unwind_var won't + // happen when lua_call does its longjmp. + err = lua_pcall(ls, 0, 1, 0); + + _dgn_map_safe_bounds_fn.reset(NULL); + } + + if (err) + lua_error(ls); + + return (1); +} + +// Accepts any number of point coordinates and a function, binds the +// points as anchors that floating vaults must match and calls the +// function, returning the return value of the function. +LUAFN(dgn_with_map_anchors) +{ + const int top = lua_gettop(ls); + int err = 0; + { + unwind_var uanchor(map_anchor_points); + + map_anchor_points.clear(); + + int i; + for (i = 1; i < top; i += 2) + { + if (lua_isnumber(ls, i) && lua_isnumber(ls, i + 1)) + map_anchor_points.push_back( + coord_def( lua_tointeger(ls, i), + lua_tointeger(ls, i + 1) ) ); + } + + ASSERT(lua_isfunction(ls, -1)); + + lua_pushvalue(ls, -1); + err = lua_pcall(ls, 0, 1, 0); + } + if (err) + lua_error(ls); + return (1); +} + +#define BRANCH(br, pos) \ +const char *branch_name = luaL_checkstring(ls, pos); \ +branch_type req_branch_type = str_to_branch(branch_name); \ +if (req_branch_type == NUM_BRANCHES) \ +luaL_error(ls, "Expected branch name"); \ +Branch &br = branches[req_branch_type] + +#define BRANCHFN(name, type, expr) \ +LUAFN(dgn_br_##name) { \ +BRANCH(br, 1); \ +PLUARET(type, expr); \ +} + +BRANCHFN(floorcol, number, br.floor_colour) +BRANCHFN(rockcol, number, br.rock_colour) +BRANCHFN(has_shops, boolean, br.has_shops) +BRANCHFN(has_uniques, boolean, br.has_uniques) +BRANCHFN(parent_branch, string, + br.parent_branch == NUM_BRANCHES ? "" + : branches[br.parent_branch].abbrevname) + +#define LEVEL(lev, br, pos) \ +const char *level_name = luaL_checkstring(ls, pos); \ +level_area_type lev = str_to_level_area_type(level_name); \ +if (lev == NUM_LEVEL_AREA_TYPES) \ +luaL_error(ls, "Expected level name"); \ +const char *branch_name = luaL_checkstring(ls, pos); \ +branch_type br = str_to_branch(branch_name); \ +if (lev == LEVEL_DUNGEON && br == NUM_BRANCHES) \ +luaL_error(ls, "Expected branch name"); + +static void push_level_id(lua_State *ls, const level_id &lid) +{ + // We're skipping the constructor; naughty, but level_id has no + // virtual methods and no dynamically allocated memory. + level_id *nlev = + static_cast(lua_newuserdata(ls, sizeof(level_id))); + *nlev = lid; +} + +static level_id _lua_level_id(lua_State *ls, int ndx) +{ + if (lua_isstring(ls, ndx)) + { + const char *s = lua_tostring(ls, 1); + try + { + return level_id::parse_level_id(s); + } + catch (const std::string &err) + { + luaL_error(ls, err.c_str()); + } + } + else if (lua_isuserdata(ls, ndx)) + { + const level_id *lid = static_cast(lua_touserdata(ls, ndx)); + return (*lid); + } + + luaL_argerror(ls, ndx, "Expected level_id"); + // Never gets here. + return level_id(); +} + +LUAFN(dgn_level_id) +{ + const int nargs = lua_gettop(ls); + if (!nargs) + push_level_id(ls, level_id::current()); + else if (nargs == 1) + push_level_id(ls, _lua_level_id(ls, 1)); + return (1); +} + +LUAFN(dgn_level_name) +{ + const level_id lid(_lua_level_id(ls, 1)); + lua_pushstring(ls, lid.describe().c_str()); + return (1); +} + +LUAFN(dgn_set_level_type_name) +{ + if (you.level_type != LEVEL_PORTAL_VAULT) + { + luaL_error(ls, "Can only set level type name on portal vaults"); + return(0); + } + + if (!lua_isstring(ls, 1)) + { + luaL_argerror(ls, 1, "Expected string for level type name"); + return(0); + } + + you.level_type_name = luaL_checkstring(ls, 1); + + return(0); +} + +LUAFN(dgn_set_level_type_name_abbrev) +{ + if (you.level_type != LEVEL_PORTAL_VAULT) + { + luaL_error(ls, "Can only set level type name abbreviation on " + "portal vaults"); + return(0); + } + + if (!lua_isstring(ls, 1)) + { + luaL_argerror(ls, 1, "Expected string for level type name " + "abbreviation"); + return(0); + } + + you.level_type_name_abbrev = luaL_checkstring(ls, 1); + + return(0); +} + +LUAFN(dgn_set_level_type_origin) +{ + if (you.level_type != LEVEL_PORTAL_VAULT) + { + luaL_error(ls, "Can only set level type origin on portal vaults"); + return(0); + } + + if (!lua_isstring(ls, 1)) + { + luaL_argerror(ls, 1, "Expected string for level type origin"); + return(0); + } + + you.level_type_origin = luaL_checkstring(ls, 1); + + return(0); +} + +static int _lua_push_map(lua_State *ls, const map_def *map) +{ + if (map) + clua_push_map(ls, const_cast(map)); + else + lua_pushnil(ls); + return (1); +} + +LUAFN(dgn_map_by_tag) +{ + if (const char *tag = luaL_checkstring(ls, 1)) + { + const bool check_depth = _lua_boolean(ls, 3, true); + return _lua_push_map(ls, random_map_for_tag(tag, check_depth)); + } + return (0); +} + +LUAFN(dgn_map_in_depth) +{ + const level_id lid = _lua_level_id(ls, 1); + const bool mini = _lua_boolean(ls, 2, true); + return _lua_push_map(ls, random_map_in_depth(lid, mini)); +} + +LUAFN(dgn_map_by_place) +{ + const level_id lid = _lua_level_id(ls, 1); + const bool mini = _lua_boolean(ls, 2, false); + return _lua_push_map(ls, random_map_for_place(lid, mini)); +} + +LUAFN(_dgn_place_map) +{ + MAP(ls, 1, map); + const bool clobber = _lua_boolean(ls, 2, false); + const bool no_exits = _lua_boolean(ls, 3, false); + coord_def where(-1, -1); + if (lua_isnumber(ls, 4) && lua_isnumber(ls, 5)) + { + COORDS(c, 4, 5); + where = c; + } + if (dgn_place_map(map, clobber, no_exits, where) && !Level_Vaults.empty()) + lua_pushlightuserdata(ls, &Level_Vaults[Level_Vaults.size() - 1]); + else + lua_pushnil(ls); + return (1); +} + +LUAFN(_dgn_in_vault) +{ + GETCOORD(c, 1, 2, map_bounds); + const int mask = lua_isnone(ls, 3) ? MMT_VAULT : lua_tointeger(ls, 3); + lua_pushboolean(ls, dgn_Map_Mask(c) & mask); + return (1); +} + +LUAFN(_dgn_find_marker_prop) +{ + const char *prop = luaL_checkstring(ls, 1); + const std::string value( + lua_gettop(ls) >= 2 ? luaL_checkstring(ls, 2) : ""); + const coord_def place = find_marker_prop(prop, value); + if (map_bounds(place)) + clua_push_coord(ls, place); + else + { + lua_pushnil(ls); + lua_pushnil(ls); + } + return (2); +} + +extern spec_room lua_special_room_spec; +extern int lua_special_room_level; + +LUAFN(dgn_get_special_room_info) +{ + if (!lua_special_room_spec.created || !in_bounds(lua_special_room_spec.tl) + || lua_special_room_level == -1) + { + return (0); + } + + lua_pushnumber(ls, lua_special_room_level); + dlua_push_coord(ls, lua_special_room_spec.tl); + dlua_push_coord(ls, lua_special_room_spec.br); + + return (5); +} + +LUAFN(_dgn_resolve_map) +{ + if (lua_isnil(ls, 1)) + { + lua_pushnil(ls); + return (1); + } + + MAP(ls, 1, map); + const bool check_collisions = _lua_boolean(ls, 2, true); + + // Save the vault_placement into Temp_Vaults because the map_def + // will need to be alive through to the end of dungeon gen. + Temp_Vaults.push_back(vault_placement()); + + vault_placement &place(Temp_Vaults[Temp_Vaults.size() - 1]); + + if (vault_main(place, map, check_collisions) != MAP_NONE) + { + clua_push_map(ls, &place.map); + lua_pushlightuserdata(ls, &place); + } + else + { + lua_pushnil(ls); + lua_pushnil(ls); + } + return (2); +} + +LUAFN(_dgn_reuse_map) +{ + if (!lua_isuserdata(ls, 1)) + luaL_argerror(ls, 1, "Expected vault_placement"); + + vault_placement &vp( + *static_cast(lua_touserdata(ls, 1))); + + COORDS(place, 2, 3); + + const bool flip_horiz = _lua_boolean(ls, 4, false); + const bool flip_vert = _lua_boolean(ls, 5, false); + + // 1 for clockwise, -1 for anticlockwise, 0 for no rotation. + const int rotate_dir = lua_isnone(ls, 6) ? 0 : luaL_checkint(ls, 6); + + const bool register_place = _lua_boolean(ls, 7, true); + const bool register_vault = register_place && _lua_boolean(ls, 8, false); + + if (flip_horiz) + vp.map.hmirror(); + if (flip_vert) + vp.map.vmirror(); + if (rotate_dir) + vp.map.rotate(rotate_dir == 1); + + vp.size = vp.map.map.size(); + + // draw_at changes vault_placement. + vp.draw_at(place); + + if (register_place) + dgn_register_place(vp, register_vault); + + return (0); +} + +LUAFN(dgn_lev_floortile) +{ +#ifdef USE_TILE + LEVEL(lev, br, 1); + + tile_flavour flv; + tile_default_flv(lev, br, flv); + + const char *tile_name = tile_dngn_name(flv.floor); + PLUARET(string, tile_name); +#else + PLUARET(string, "invalid"); +#endif +} + +LUAFN(dgn_lev_rocktile) +{ +#ifdef USE_TILE + LEVEL(lev, br, 1); + + tile_flavour flv; + tile_default_flv(lev, br, flv); + + const char *tile_name = tile_dngn_name(flv.wall); + PLUARET(string, tile_name); +#else + PLUARET(string, "invalid"); +#endif +} + +LUAFN(dgn_lrocktile) +{ + MAP(ls, 1, map); + +#ifdef USE_TILE + unsigned short tile = _get_tile_idx(ls, 2); + map->rock_tile = tile; + + const char *tile_name = tile_dngn_name(tile); + PLUARET(string, tile_name); +#else + UNUSED(map); + PLUARET(string, "invalid"); +#endif +} + +LUAFN(dgn_lfloortile) +{ + MAP(ls, 1, map); + +#ifdef USE_TILE + unsigned short tile = _get_tile_idx(ls, 2); + map->floor_tile = tile; + + const char *tile_name = tile_dngn_name(tile); + PLUARET(string, tile_name); +#else + UNUSED(map); + PLUARET(string, "invalid"); +#endif +} + +LUAFN(dgn_change_rock_tile) +{ +#ifdef USE_TILE + unsigned short tile = _get_tile_idx(ls, 1); + if (tile) + env.tile_default.wall = tile; + + const char *tile_name = tile_dngn_name(tile); + PLUARET(string, tile_name); +#else + PLUARET(string, "invalid"); +#endif +} + +LUAFN(dgn_change_floor_tile) +{ +#ifdef USE_TILE + unsigned short tile = _get_tile_idx(ls, 1); + if (tile) + env.tile_default.floor = tile; + + const char *tile_name = tile_dngn_name(tile); + PLUARET(string, tile_name); +#else + PLUARET(string, "invalid"); +#endif +} + +LUAFN(dgn_ftile) +{ +#ifdef USE_TILE + return dgn_map_add_transform(ls, &map_lines::add_floortile); +#else + return 0; +#endif +} + +LUAFN(dgn_rtile) +{ +#ifdef USE_TILE + return dgn_map_add_transform(ls, &map_lines::add_rocktile); +#else + return 0; +#endif +} + +LUAFN(dgn_dbg_dump_map) +{ + const int pos = lua_isuserdata(ls, 1) ? 2 : 1; + if (lua_isstring(ls, pos)) + dump_map(lua_tostring(ls, pos), true); + return (0); +} + +LUAFN(dgn_dbg_test_explore) +{ +#ifdef WIZARD + debug_test_explore(); +#endif + return (0); +} + +LUAFN(dgn_dismiss_monsters) +{ + PLUARET(number, + dismiss_monsters(lua_gettop(ls) == 0 ? "" : + luaL_checkstring(ls, 1))); +} + +LUAWRAP(_dgn_reset_level, dgn_reset_level()) + +const struct luaL_reg dgn_lib[] = +{ +{ "dbg_goto_place", dgn_dbg_goto_place }, +{ "dbg_flush_map_memory", dgn_dbg_flush_map_memory }, +{ "dbg_generate_level", dgn_dbg_generate_level }, +{ "dbg_dump_map", dgn_dbg_dump_map }, +{ "dbg_test_explore", dgn_dbg_test_explore }, + +{ "reset_level", _dgn_reset_level }, +{ "dismiss_monsters", dgn_dismiss_monsters }, + +{ "default_depth", dgn_default_depth }, +{ "name", dgn_name }, +{ "depth", dgn_depth }, +{ "place", dgn_place }, +{ "tags", dgn_tags }, +{ "tags_remove", dgn_tags_remove }, +{ "lflags", dgn_lflags }, +{ "bflags", dgn_bflags }, +{ "chance", dgn_chance }, +{ "weight", dgn_weight }, +{ "welcome", dgn_welcome }, +{ "orient", dgn_orient }, +{ "shuffle", dgn_shuffle }, +{ "subst", dgn_subst }, +{ "nsubst", dgn_nsubst }, +{ "colour", dgn_colour }, +{ "lfloorcol", dgn_lfloorcol}, +{ "lrockcol", dgn_lrockcol}, +{ "normalise", dgn_normalise }, +{ "map", dgn_map }, +{ "mons", dgn_mons }, +{ "item", dgn_item }, +{ "marker", dgn_marker }, +{ "lua_marker", dgn_lua_marker }, +{ "kfeat", dgn_kfeat }, +{ "kitem", dgn_kitem }, +{ "kmons", dgn_kmons }, +{ "kmask", dgn_kmask }, +{ "mapsize", dgn_map_size }, + +{ "grid", dgn_grid }, +{ "is_wall", _dgn_is_wall }, +{ "max_bounds", dgn_max_bounds }, +{ "colour_at", dgn_colour_at }, + +{ "terrain_changed", dgn_terrain_changed }, +{ "points_connected", dgn_points_connected }, +{ "any_point_connected", dgn_any_point_connected }, +{ "has_exit_from", dgn_has_exit_from }, +{ "gly_point", dgn_gly_point }, +{ "gly_points", dgn_gly_points }, +{ "original_map", dgn_original_map }, +{ "load_des_file", dgn_load_des_file }, +{ "feature_number", dgn_feature_number }, +{ "feature_name", dgn_feature_name }, +{ "dgn_event_type", dgn_dgn_event }, +{ "register_listener", dgn_register_listener }, +{ "remove_listener", dgn_remove_listener }, +{ "remove_marker", dgn_remove_marker }, +{ "num_matching_markers", dgn_num_matching_markers }, +{ "feature_desc", dgn_feature_desc }, +{ "feature_desc_at", dgn_feature_desc_at }, +{ "set_feature_desc_short", dgn_set_feature_desc_short }, +{ "set_feature_desc_long", dgn_set_feature_desc_long }, +{ "item_from_index", dgn_item_from_index }, +{ "mons_from_index", dgn_mons_from_index }, +{ "mons_at", dgn_mons_at }, +{ "items_at", dgn_items_at }, +{ "change_level_flags", dgn_change_level_flags }, +{ "change_branch_flags", dgn_change_branch_flags }, +{ "set_random_mon_list", dgn_set_random_mon_list }, +{ "get_floor_colour", dgn_get_floor_colour }, +{ "get_rock_colour", dgn_get_rock_colour }, +{ "change_floor_colour", dgn_change_floor_colour }, +{ "change_rock_colour", dgn_change_rock_colour }, +{ "set_lt_callback", lua_dgn_set_lt_callback }, +{ "fixup_stairs", dgn_fixup_stairs }, +{ "floor_halo", dgn_floor_halo }, +{ "random_walk", dgn_random_walk }, +{ "apply_area_cloud", dgn_apply_area_cloud }, + +// building routines +{ "grd_table", dgn_grd_table }, +{ "width", dgn_width }, +{ "height", dgn_height }, +{ "fill_area", dgn_fill_area }, +{ "replace_area", dgn_replace_area }, +{ "octa_room", dgn_octa_room }, +{ "make_pillars", dgn_make_pillars }, +{ "make_square", dgn_make_square }, +{ "make_rounded_square", dgn_make_rounded_square }, +{ "make_circle", dgn_make_circle }, +{ "in_bounds", dgn_in_bounds }, +{ "replace_first", dgn_replace_first }, +{ "replace_random", dgn_replace_random }, +{ "spotty_level", dgn_spotty_level }, +{ "smear_feature", dgn_smear_feature }, +{ "count_feature_in_box", dgn_count_feature_in_box }, +{ "count_antifeature_in_box", dgn_count_antifeature_in_box }, +{ "count_neighbours", dgn_count_neighbours }, +{ "join_the_dots", dgn_join_the_dots }, +{ "fill_disconnected_zones", dgn_fill_disconnected_zones }, + +{ "is_opaque", _dgn_is_opaque }, +{ "is_passable", _dgn_is_passable }, + +{ "register_feature_marker", dgn_register_feature_marker }, +{ "register_lua_marker", dgn_register_lua_marker }, + +{ "create_monster", dgn_create_monster }, +{ "create_item", dgn_create_item }, + +{ "monster_spec", _dgn_monster_spec }, +{ "item_spec", _dgn_item_spec }, + +{ "max_monsters", _dgn_max_monsters }, + +{ "with_map_bounds_fn", dgn_with_map_bounds_fn }, +{ "with_map_anchors", dgn_with_map_anchors }, + +{ "br_floorcol", dgn_br_floorcol }, +{ "br_rockcol", dgn_br_rockcol }, +{ "br_has_shops", dgn_br_has_shops }, +{ "br_has_uniques", dgn_br_has_uniques }, +{ "br_parent_branch", dgn_br_parent_branch }, + +{ "level_id", dgn_level_id }, +{ "level_name", dgn_level_name }, +{ "set_level_type_name", dgn_set_level_type_name }, +{ "set_level_type_name_abbrev", dgn_set_level_type_name_abbrev }, +{ "set_level_type_origin", dgn_set_level_type_origin }, +{ "map_by_tag", dgn_map_by_tag }, +{ "map_in_depth", dgn_map_in_depth }, +{ "map_by_place", dgn_map_by_place }, +{ "place_map", _dgn_place_map }, +{ "reuse_map", _dgn_reuse_map }, +{ "resolve_map", _dgn_resolve_map }, +{ "in_vault", _dgn_in_vault }, + +{ "find_marker_prop", _dgn_find_marker_prop }, + +{ "get_special_room_info", dgn_get_special_room_info }, + +{ "lrocktile", dgn_lrocktile }, +{ "lfloortile", dgn_lfloortile }, +{ "rtile", dgn_rtile }, +{ "ftile", dgn_ftile }, +{ "change_rock_tile", dgn_change_rock_tile }, +{ "change_floor_tile", dgn_change_floor_tile }, +{ "lev_floortile", dgn_lev_floortile }, +{ "lev_rocktile", dgn_lev_rocktile }, + +{ NULL, NULL } +}; -- cgit v1.2.3-54-g00ecf