#include "AppHdr.h"
#include "l_libs.h"
#include "cluautil.h"
#include <cmath>
#include "branch.h"
#include "cloud.h"
#include "colour.h"
#include "coord.h"
#include "directn.h"
#include "dungeon.h"
#include "env.h"
#include "flood_find.h"
#include "l_defs.h"
#include "mapmark.h"
#include "maps.h"
#include "random.h"
#include "shout.h"
#include "spl-util.h"
#include "state.h"
#ifdef USE_TILE
#include "tiledef-dngn.h"
#endif
#include "view.h"
const char *VAULT_PLACEMENT_METATABLE = "crawl.vault-placement";
///////////////////////////////////////////////////////////////////////////
// Lua dungeon bindings (in the dgn table).
static inline bool _lua_boolean(lua_State *ls, int ndx, bool defval)
{
return lua_isnone(ls, ndx)? defval : lua_toboolean(ls, ndx);
}
void dgn_reset_default_depth()
{
lc_default_depths.clear();
}
std::string dgn_set_default_depth(const std::string &s)
{
std::vector<std::string> 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<std::string> 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);
}
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_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<map_section_type>(i);
found = true;
break;
}
}
if (!found)
luaL_error(ls, ("Bad orient: " + orient).c_str());
}
}
PLUARET(string, map_section_name(map->orient));
}
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 clua_stringtable(ls, map->map.get_lines());
if (lua_isnil(ls, 2))
{
map->map.clear();
return (0);
}
// map(<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<std::string> &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->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->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->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->map.add_key_mask(luaL_checkstring(ls, 2));
if (!err.empty())
luaL_error(ls, err.c_str());
return (0);
}
static int dgn_kprop(lua_State *ls)
{
return dgn_map_add_transform(ls, &map_lines::add_fproperty);
}
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_subvault(lua_State *ls)
{
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->subvault_from_tagstring(luaL_checkstring(ls, i));
if (!err.empty())
luaL_error(ls, err.c_str());
}
}
return (0);
}
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);
}
typedef
flood_find<map_def::map_feature_finder, map_def::map_bounds_check>
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_coordinates(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_coordinates(ls, c);
return (2);
}
return (0);
}
static int dgn_gly_points(lua_State *ls)
{
MAP(ls, 1, map);
std::vector<coord_def> cs = map->find_glyph(*luaL_checkstring(ls, 2));
for (int i = 0, size = cs.size(); i < size; ++i)
dlua_push_coordinates(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(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(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());
}
static int dgn_register_listener(lua_State *ls)
{
unsigned mask = luaL_checkint(ls, 1);
MAPMARKER(ls, 2, mark);
map_lua_marker *listener = dynamic_cast<map_lua_marker*>(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<map_lua_marker*>(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<map_marker*> markers = env.markers.get_all(key, val);
PLUARET(number, markers.size());
}
static int dgn_terrain_changed(lua_State *ls)
{
dungeon_feature_type type = DNGN_UNSEEN;
if (lua_isnumber(ls, 3))
type = static_cast<dungeon_feature_type>(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_fprop_changed(lua_State *ls)
{
feature_property_type prop = FPROP_NONE;
if (lua_isnumber(ls, 3))
prop = static_cast<feature_property_type>(luaL_checkint(ls, 3));
else if (lua_isstring(ls, 3))
prop = str_to_fprop(lua_tostring(ls, 3));
coord_def pos = coord_def(luaL_checkint(ls, 1), luaL_checkint(ls, 2));
if (in_bounds(pos) && prop != FPROP_NONE)
{
if (testbits(env.pgrid(pos), prop))
{
env.pgrid(pos) &= ~prop;
lua_pushboolean(ls, true);
}
else if (!testbits(env.pgrid(pos), prop))
{
env.pgrid(pos) |= prop;
lua_pushboolean(ls, true);
}
else
lua_pushboolean(ls, false);
}
else
lua_pushboolean(ls, false);
return (1);
}
static int dgn_fprop_at (lua_State *ls)
{
feature_property_type prop = FPROP_NONE;
if (lua_isnumber(ls, 3))
prop = static_cast<feature_property_type>(luaL_checkint(ls, 3));
else if (lua_isstring(ls, 3))
prop = str_to_fprop(lua_tostring(ls, 3));
coord_def pos = coord_def(luaL_checkint(ls, 1), luaL_checkint(ls, 2));
if (in_bounds(pos) && prop != FPROP_NONE)
lua_pushboolean(ls, testbits(env.pgrid(pos), prop));
else
lua_pushboolean(ls, false);
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);
}
bool _valid_border_feat (dungeon_feature_type feat)
{
return ((feat <= DNGN_MAXWALL && feat >= DNGN_MINWALL)
|| (feat == DNGN_TREES || feat == DNGN_OPEN_SEA
|| feat == DNGN_LAVA || feat == DNGN_DEEP_WATER
|| feat == DNGN_SHALLOW_WATER || feat == DNGN_FLOOR));
}
// XXX: Currently, this is hacked so that map_def->border_fill_type is marsalled
// when the maps are stored. This relies on the individual map Lua prelude
// being executed whenever maps are loaded and verified, which means that
// the next time the map is loaded, border_fill_type is already stored.
static int lua_dgn_set_border_fill_type (lua_State *ls)
{
MAP(ls, 1, map);
if (lua_gettop(ls) != 2)
luaL_error(ls, "set_border_fill_type requires a feature.");
std::string fill_string = luaL_checkstring(ls, 2);
dungeon_feature_type fill_type = dungeon_feature_by_name(fill_string);
if (_valid_border_feat(fill_type))
map->border_fill_type = fill_type;
else
luaL_error(ls, ("set_border_fill_type cannot be the feature '" +
fill_string +"'.").c_str());
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);
}
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_coordinates(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<cloud_type>(i)) == name)
return static_cast<cloud_type>(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, int colour, std::string name,
std::string tile)
{
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, colour, name,
tile );
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;
const int colour = lua_isstring(ls, 10) ? str_to_colour(luaL_checkstring(ls, 10)) : -1;
std::string name = lua_isstring(ls, 11) ? luaL_checkstring(ls, 11) : "";
std::string tile = lua_isstring(ls, 12) ? luaL_checkstring(ls, 12) : "";
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, colour, name, tile);
return (0);
}
static int dgn_place_cloud(lua_State *ls)
{
const int x = luaL_checkint(ls, 1);
const int y = luaL_checkint(ls, 2);
const cloud_type ctype = dgn_cloud_name_to_type(luaL_checkstring(ls, 3));
const int cl_range = luaL_checkint(ls, 4);
const char* kname = lua_isstring(ls, 5) ? luaL_checkstring(ls, 5)
: "";
const kill_category kc = dgn_kill_name_to_category(kname);
const int spread_rate = lua_isnumber(ls, 6) ? luaL_checkint(ls, 6) : -1;
const int colour = lua_isstring(ls, 7) ? str_to_colour(luaL_checkstring(ls, 7)) : -1;
std::string name = lua_isstring(ls, 8) ? luaL_checkstring(ls, 8) : "";
std::string tile = lua_isstring(ls, 9) ? luaL_checkstring(ls, 9) : "";
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 (ctype == CLOUD_NONE)
{
std::string error = "Invalid cloud type '";
error += luaL_checkstring(ls, 3);
error += "'";
luaL_argerror(ls, 3, error.c_str());
return (0);
}
if (kc == KC_NCATEGORIES)
{
std::string error = "Invalid kill category '";
error += kname;
error += "'";
luaL_argerror(ls, 5, error.c_str());
return (0);
}
if (spread_rate < -1 || spread_rate > 100)
{
luaL_argerror(ls, 6, "spread_rate must be between -1 and 100,"
"inclusive");
return (0);
}
place_cloud(ctype, coord_def(x, y), cl_range, kc, spread_rate, colour, name, tile);
return (0);
}
// XXX: Doesn't allow for messages or specifying the noise source.
LUAFN(dgn_noisy)
{
const int loudness = luaL_checkint(ls, 1);
COORDS(pos, 2, 3);
noisy(loudness, pos);
return (0);
}
static int _dgn_is_passable(lua_State *ls)
{
COORDS(c, 1, 2);
lua_pushboolean(ls, dgn_square_travel_ok(c));
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 std::auto_ptr<lua_datum> _dgn_map_safe_bounds_fn;
static bool _lua_map_place_valid(const map_def &map,
const coord_def &c,
const coord_def &size)
{
dprf("lua_map_place_invalid: (%d,%d) (%d,%d)",
c.x, c.y, size.x, size.y);
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_def*>(&map));
dlua_push_coordinates(ls, c);
dlua_push_coordinates(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<map_place_check_t> 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<point_vector> 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);
}
static int _lua_push_map(lua_State *ls, const map_def *map)
{
if (map)
clua_push_map(ls, const_cast<map_def*>(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_by_name)
{
if (const char *name = luaL_checkstring(ls, 1))
return _lua_push_map(ls, find_map_by_name(name));
return (0);
}
LUAFN(dgn_map_in_depth)
{
const level_id lid = dlua_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 = dlua_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_map_parameters)
{
return clua_stringtable(ls, map_parameters);
}
int dgn_push_vault_placement(lua_State *ls, const vault_placement &vp)
{
return dlua_push_object_type(ls, VAULT_PLACEMENT_METATABLE, vp);
}
LUAFN(_dgn_maps_used_here)
{
return clua_gentable(ls, Level_Vaults, dgn_push_vault_placement);
}
LUAFN(_dgn_find_marker_position_by_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_position_by_prop(prop, value);
if (map_bounds(place))
dlua_push_coordinates(ls, place);
else
{
lua_pushnil(ls);
lua_pushnil(ls);
}
return (2);
}
LUAFN(_dgn_find_marker_positions_by_prop)
{
const char *prop = luaL_checkstring(ls, 1);
const std::string value(
lua_gettop(ls) >= 2 ? luaL_checkstring(ls, 2) : "");
const unsigned limit(lua_gettop(ls) >= 3 ? luaL_checkint(ls, 3) : 0);
const std::vector<coord_def> places =
find_marker_positions_by_prop(prop, value, limit);
clua_gentable(ls, places, clua_pushpoint);
return (1);
}
static int _push_mapmarker(lua_State *ls, map_marker *marker)
{
dlua_push_userdata(ls, marker, MAPMARK_METATABLE);
return (1);
}
LUAFN(_dgn_find_markers_by_prop)
{
const char *prop = luaL_checkstring(ls, 1);
const std::string value(
lua_gettop(ls) >= 2 ? luaL_checkstring(ls, 2) : "");
const unsigned limit(lua_gettop(ls) >= 3 ? luaL_checkint(ls, 3) : 0);
const std::vector<map_marker*> places =
find_markers_by_prop(prop, value, limit);
clua_gentable(ls, places, _push_mapmarker);
return (1);
}
LUAFN(_dgn_marker_at_pos)
{
const int x = luaL_checkint(ls, 1);
const int y = luaL_checkint(ls, 2);
coord_def p(x, y);
map_marker* marker = env.markers.find(p);
if (marker == NULL)
lua_pushnil(ls);
else
_push_mapmarker(ls, marker);
return (1);
}
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_coordinates(ls, lua_special_room_spec.tl);
dlua_push_coordinates(ls, lua_special_room_spec.br);
return (5);
}
LUAFN(dgn_is_validating)
{
MAP(ls, 1, map);
lua_pushboolean(ls, map->is_validating());
return (1);
}
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<vault_placement*>(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);
}
LUAWRAP(_dgn_reset_level, dgn_reset_level())
LUAFN(dgn_fill_grd_area)
{
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);
x1 = std::min(std::max(x1, X_BOUND_1+1), X_BOUND_2-1);
y1 = std::min(std::max(y1, Y_BOUND_1+1), Y_BOUND_2-1);
x2 = std::min(std::max(x2, X_BOUND_1+1), X_BOUND_2-1);
y2 = std::min(std::max(y2, Y_BOUND_1+1), Y_BOUND_2-1);
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);
}
const struct luaL_reg dgn_dlib[] =
{
{ "reset_level", _dgn_reset_level },
{ "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 },
{ "kprop", dgn_kprop },
{ "kmask", dgn_kmask },
{ "mapsize", dgn_map_size },
{ "subvault", dgn_subvault },
{ "colour_at", dgn_colour_at },
{ "fprop_at", dgn_fprop_at },
{ "terrain_changed", dgn_terrain_changed },
{ "fprop_changed", dgn_fprop_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 },
{ "register_listener", dgn_register_listener },
{ "remove_listener", dgn_remove_listener },
{ "remove_marker", dgn_remove_marker },
{ "num_matching_markers", dgn_num_matching_markers },
{ "change_level_flags", dgn_change_level_flags },
{ "change_branch_flags", dgn_change_branch_flags },
{ "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 },
{ "set_border_fill_type", lua_dgn_set_border_fill_type },
{ "fixup_stairs", dgn_fixup_stairs },
{ "floor_halo", dgn_floor_halo },
{ "random_walk", dgn_random_walk },
{ "apply_area_cloud", dgn_apply_area_cloud },
{ "place_cloud", dgn_place_cloud },
{ "noisy", dgn_noisy },
{ "is_passable", _dgn_is_passable },
{ "register_feature_marker", dgn_register_feature_marker },
{ "register_lua_marker", dgn_register_lua_marker },
{ "with_map_bounds_fn", dgn_with_map_bounds_fn },
{ "with_map_anchors", dgn_with_map_anchors },
{ "map_by_tag", dgn_map_by_tag },
{ "map_by_name", dgn_map_by_name },
{ "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 },
{ "map_parameters", _dgn_map_parameters },
{ "maps_used_here", _dgn_maps_used_here },
{ "find_marker_position_by_prop", _dgn_find_marker_position_by_prop },
{ "find_marker_positions_by_prop", _dgn_find_marker_positions_by_prop },
{ "find_markers_by_prop", _dgn_find_markers_by_prop },
{ "marker_at_pos", _dgn_marker_at_pos },
{ "get_special_room_info", dgn_get_special_room_info },
{ "is_validating", dgn_is_validating },
{ "fill_grd_area", dgn_fill_grd_area },
{ NULL, NULL }
};
#define VP(name) \
vault_placement &name = \
**clua_get_userdata<vault_placement*>( \
ls, VAULT_PLACEMENT_METATABLE)
LUAFN(_vp_pos)
{
VP(vp);
clua_pushpoint(ls, vp.pos);
return 1;
}
LUAFN(_vp_size)
{
VP(vp);
clua_pushpoint(ls, vp.size);
return 1;
}
LUAFN(_vp_orient)
{
VP(vp);
PLUARET(number, vp.orient)
}
LUAFN(_vp_map)
{
VP(vp);
clua_push_map(ls, &vp.map);
return 1;
}
LUAFN(_vp_exits)
{
VP(vp);
return clua_gentable(ls, vp.exits, clua_pushpoint);
}
LUAFN(_vp_level_number)
{
VP(vp);
PLUARET(number, vp.level_number)
}
LUAFN(_vp_num_runes)
{
VP(vp);
PLUARET(number, vp.num_runes)
}
LUAFN(_vp_rune_subst)
{
VP(vp);
PLUARET(number, vp.rune_subst)
}
static const luaL_reg dgn_vaultplacement_ops[] =
{
{ "pos", _vp_pos },
{ "size", _vp_size },
{ "orient", _vp_orient },
{ "map", _vp_map },
{ "exits", _vp_exits },
{ "level_number", _vp_level_number },
{ "num_runes", _vp_num_runes },
{ "rune_subst", _vp_rune_subst },
{ NULL, NULL }
};
static void _dgn_register_metatables(lua_State *ls)
{
clua_register_metatable(ls,
VAULT_PLACEMENT_METATABLE,
dgn_vaultplacement_ops,
lua_object_gc<vault_placement*>);
}
void dluaopen_dgn(lua_State *ls)
{
_dgn_register_metatables(ls);
luaL_openlib(ls, "dgn", dgn_dlib, 0);
luaL_openlib(ls, "dgn", dgn_build_dlib, 0);
luaL_openlib(ls, "dgn", dgn_event_dlib, 0);
luaL_openlib(ls, "dgn", dgn_grid_dlib, 0);
luaL_openlib(ls, "dgn", dgn_item_dlib, 0);
luaL_openlib(ls, "dgn", dgn_level_dlib, 0);
luaL_openlib(ls, "dgn", dgn_mons_dlib, 0);
luaL_openlib(ls, "dgn", dgn_subvault_dlib, 0);
luaL_openlib(ls, "dgn", dgn_tile_dlib, 0);
}