The previous dungeon layout.des functions all apply directly to the grid and so are unsuitable for use in vaults. This aims to correct that by providing lua functions that can manipulate maps (of glyphs).
grd[x][y] in a .des file can now be used to get and set glyphs in the current map. This should allow for less cumbersome map variations than what you can do with just SUBST and SHUFFLE.
To support that, map_def no longer batches up transforms–it applies them all immediately. This resulted in a good bit of refactoring.
FTILE/RTILE map commands now support setting the tile for multiple features at once.
There are also a small number of new lua functions that apply to maps (map_octa_room, map_smear, and map_extend). Ideally, these will eventually replace the existing builder funcs that work on grd.
YFJLINBBEHE7RBETTARSYNWSO6QJ6MSPXFMSPSH2742QFC3L55SQC
GURIAIJVY6LEADNBTZF3SFBLODYJHWD6OGWFMUGEDI4L5KYO7UFAC
GTXLAKI6NJWAEVR4GVLG3DL22OGYLTZOPD54IJEQDRSMOHQBK2EQC
3QDFCGPKRLFO4DQNM4JWNRVO6BHB3KH2AT2AX3I5USPLV2R6MCXQC
SL2AT5HPB66PZJI53XLTIG5B6ZHGEHOD2BXJYNJRFXKOIGFXMD5AC
7NROUYQFX5ZRHHNGZ5DMVBNQP6H3LWDRVP5WIGG6YS6LDKOJXR3AC
KZQ6TWWRS5CSU44RKQLS7XWZJ24ILWQBLDWYPRPCNHQUXDLLB5XAC
T7CCGLOZ25B7BQKKGR6IA6LWBRKUWTXLTIRXUQ4YKQRVAA7AHZKQC
ST6AUNAYYEJU3R6TALOBXQDMN552NJU3K3U4JFSGAAFDTJ4BCYRQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
RGHXFBNIULRVRYLBGG5JZDMYVM2E2JJ2Y5KQPMU6PUS3V26G6ZXQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
W52PTX4I7TUG2LS36FJHLZ72DWCVXGUCTUIKPI3IGEYO5KWL6OVAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
ANBVGN4RZOMY5LI4QSHOV2477FN55H353ZYLSVCPTXC7AWWSQZBAC
KFJEFN377VIZ7OH2XCYOGCELNEGO4CIOOP7DNXEMX3LFKIKWXVTAC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
2ZZD6EYMSPVCXZTICL4VGYGGQRRWDLZ24KBCDBVIYC54OZ4C6GGAC
JT672SIJK4BOIUAGL2WQ6NR2NF4PSWP3BT6Q4HMNRF25UN6JQ2MAC
JUCA26S3JSQFMZTBL7566ITHCOXXEFTFT7KDOWSUXA5JLZK5QJWAC
3UKFCWWS5BLFQWZRB5FUA46CE2XGX5VRCEWC3K3XH5RCGQK64N2AC
JQFQX7IWSJ4TYWVUVXAFMCPSAN67PRMNECDQI5WMON2JFMQVVUEQC
DKRSOHZXL6EPSLKOKHF7GJXSZEJVY7CXGACSHWLM5B5FTRETWWCAC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
6X36L3WFJHVRAZ2KD6VQMG7O2BBTYKHVU7EMIRHUFUNE7VDMXA7AC
WKTZHLOJ65WSK6FR5MF7RWGSMZ22T2D6LHB66FV3IPGXIBLYHHNAC
OYTCBRC7LE44EUVRZVYTOOVKQWJ6P6YE3FXTOGUTNKEMLNWPHKSQC
ZJLJGSB2XSBQU42OFQMXL3EG4CXAQGOYAU6YTV2SAWZEJIPFH2CAC
RLUXNEFLEICA7ERDVUOIJZJJSTPY2QLEXYN2KDEVWZSKVIV4CKPQC
77MES6I23O3FEVX45F5L7LGABFIDYZA2CLESQBXALSVIHS5E2G6AC
KTRWLM6WDL4GAAJZNLQEEA3TGY2OKYZZUAAT4TCAVKRTR2NQBWUQC
LAGWU7UFZNUXOUC72BTQ7UU4Z7VHKBRGUUSJCH4C4J4OGLXS4F3QC
SVUM62ARSXH6RUBFRWS6KAQC7PTNTMGSV2GPZJQQJ4GNEML2HBVQC
JTTHP2BEYEPBQMSDM7IKANTMKRPY6ACGL2JN4D3OBZ7HFXKAYEGQC
B3SRWSFITQMJRVEBHGQQJARETYPSSDV6XKMQSSUTXEHTXRZKIQJQC
H3552BCIAVBLKAYKE4DHFLBLFW5RGRMYBMRRYHYEB5IPIJRUVU5QC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
PRG7UT7G56GT4W3FQ3KG5JRPGMKKJBFDLVHDLYFQK6IZW25JQLBQC
EERRG6FVPJDZD6UNX2W4UXG6HU7XAU7TDNDHMRCXTA4NN4VHFSAAC
TLA5UN6LZPXGKERI27EFY4HKIIU3VU5Y7ZU54WXL6ANBUV2VOTMQC
SM6YRPYZS6LMDQA6X3VAOK2PGMUFKPD7JMWJISOQSMX2CBR4ISPAC
IYC344M4KGTM7ONOZJEYDKISOXEXAWENOGU5CZTW3LAATYV5QUEAC
KFM2ARORBIJ6BGX456VFW7EAVRIYBVFUV53JH63GSKNOKVPJWQ2QC
JYNLSDNQDNOBCHDPQLJP6TNOJQ2IB4V7JHBYLL6UAUCTZ7DUZAWQC
BDXBM356LZKCLL7BSVY646QNZCIXOYNPRVJHZLGHNGPOMU4QJTAQC
TBXIAHQZ3KQVBPVEP5CMXJLLCQY46S52BEMJRR2EFIVTXALHM4WAC
W5WCC5J7PL6IXZ5LPSUWGY3IVQBCTK3FKOD5ADYRWWH2N425NEAAC
6BGEFSWH4L63JGWICMNFWRLQQP6PNWQBLPWNTYTLWX4OKAV222EQC
RRADDS444JWSL4KOJKNZFAIMWMZRLFR4KZPC2MJBCJPEPINC5CPQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
R6XS2HO5QX2FJUGL5UQQRNETKCMYWTUFPHPPS5SYWK3OQA4UDUQQC
WE3JT43OR4L6675GINGU4B3YDBMURJZHDDYY3VLHUJEBAKH2HYEAC
class map_transformer
{
public:
enum transform_type
{
TT_SHUFFLE,
TT_SUBST,
TT_NSUBST,
TT_MARKER,
TT_COLOUR,
TT_ROCKTILE,
TT_FLOORTILE
};
public:
virtual ~map_transformer() = 0;
virtual std::string apply_transform(map_lines &map) = 0;
virtual transform_type type() const = 0;
virtual std::string describe() const = 0;
};
class subst_spec : public map_transformer
class subst_spec
public:
std::string shuffle;
shuffle_spec(const std::string &spec)
: shuffle(spec)
{
}
std::string apply_transform(map_lines &map);
transform_type type() const;
std::string describe() const;
bool operator == (const shuffle_spec &other) const
{
return (shuffle == other.shuffle);
}
};
class map_marker_spec : public map_transformer
{
// Find rectangular bounds (inclusive) for uses of the glyph in the map.
// Returns false if glyph could not be found.
bool find_bounds(int glyph, coord_def &tl, coord_def &br) const;
// Same as above, but for any of the glyphs in glyph_str.
bool find_bounds(const char *glyph_str, coord_def &tl, coord_def &br) const;
int map_lines::operator () (const coord_def &c) const
rectangle_iterator map_lines::get_iter() const
{
ASSERT(width() > 0);
ASSERT(height() > 0);
coord_def tl(0, 0);
coord_def br(width() - 1, height() - 1);
return rectangle_iterator(tl, br);
}
char map_lines::operator () (const coord_def &c) const
{
return lines[c.y][c.x];
}
char& map_lines::operator () (const coord_def &c)
std::string s = raw;
const std::string err = check_shuffle(s);
if (err.empty())
{
const shuffle_spec ss(s);
for (int i = 0, vsize = transforms.size(); i < vsize; ++i)
{
if (transforms[i]->type() == map_transformer::TT_SHUFFLE)
{
const shuffle_spec *other =
dynamic_cast<shuffle_spec*>(transforms[i]);
if (ss == *other)
{
delete transforms[i];
transforms.erase( transforms.begin() + i );
return;
}
}
}
}
return map_width;
// Parsing subst specs is a pain, so we just let add_subst do the
// work, then pop the subst off the end of the vector.
if (add_subst(raw).empty())
{
map_transformer *sub = *transforms.rbegin();
subst_spec spec = *dynamic_cast<subst_spec*>(sub);
delete sub;
transforms.pop_back();
for (int i = 0, vsize = transforms.size(); i < vsize; ++i)
{
if (transforms[i]->type() == map_transformer::TT_SUBST)
{
subst_spec *cand = dynamic_cast<subst_spec*>(transforms[i]);
if (spec == *cand)
{
delete cand;
transforms.erase( transforms.begin() + i );
return;
}
}
}
}
return lines.size();
if (transforms.empty())
return;
for (int i = transforms.size() - 1; i >= 0; --i)
if (transforms[i]->type() == tt)
{
delete transforms[i];
transforms.erase( transforms.begin() + i );
}
}
min_width = std::max(1, min_width);
min_height = std::max(1, min_height);
#ifdef USE_TILE
void map_lines::clear_rocktiles()
{
clear_transforms(map_transformer::TT_ROCKTILE);
}
if (static_cast<int>(lines.size()) < min_height)
{
dirty = true;
while (static_cast<int>(lines.size()) < min_height)
add_line(std::string(min_width, fill));
}
void map_lines::clear_substs()
{
clear_transforms(map_transformer::TT_SUBST);
}
// Extend overlay matrix as well.
if (overlay.get())
{
std::auto_ptr<overlay_matrix> new_overlay(
new overlay_matrix(width(), height()));
std::string map_lines::apply_transforms()
{
std::string error;
for (int i = 0, vsize = transforms.size(); i < vsize; ++i)
{
error = transforms[i]->apply_transform(*this);
if (!error.empty())
return (error);
}
// Release the transforms so we don't try them again.
clear_transforms();
return ("");
}
}
std::vector<std::string> map_lines::get_shuffle_strings() const
{
std::vector<std::string> shuffles;
for (int i = 0, vsize = transforms.size(); i < vsize; ++i)
if (transforms[i]->type() == map_transformer::TT_SHUFFLE)
shuffles.push_back( transforms[i]->describe() );
return (shuffles);
std::vector<std::string> map_lines::get_subst_strings() const
{
std::vector<std::string> substs;
for (int i = 0, vsize = transforms.size(); i < vsize; ++i)
if (transforms[i]->type() == map_transformer::TT_SUBST)
substs.push_back( transforms[i]->describe() );
return (substs);
}
}
bool map_lines::find_bounds(int gly, coord_def &tl, coord_def &br) const
{
tl = coord_def(width(), height());
br = coord_def(-1, -1);
if (width() == 0 || height() == 0)
return false;
for (rectangle_iterator ri(get_iter()); ri; ++ri)
{
const coord_def mc = *ri;
if ((*this)(mc) != gly)
continue;
tl.x = std::min(tl.x, mc.x);
tl.y = std::min(tl.y, mc.y);
br.x = std::max(br.x, mc.x);
br.y = std::max(br.y, mc.y);
}
return (br.x >= 0);
for (rectangle_iterator ri(get_iter()); ri; ++ri)
{
ASSERT(ri);
const coord_def &mc = *ri;
const size_t len = strlen(str);
for (size_t i = 0; i < len; ++i)
{
if ((*this)(mc) == str[i])
{
tl.x = std::min(tl.x, mc.x);
tl.y = std::min(tl.y, mc.y);
br.x = std::max(br.x, mc.x);
br.y = std::max(br.y, mc.y);
break;
}
}
}
return (br.x >= 0);
}
std::vector<std::string> map_def::get_shuffle_strings() const
{
return map.get_shuffle_strings();
}
std::vector<std::string> map_def::get_subst_strings() const
{
return map.get_subst_strings();
}
}
std::string subst_spec::apply_transform(map_lines &map)
{
map.subst(*this);
return ("");
}
map_transformer::transform_type subst_spec::type() const
{
return (TT_SUBST);
}
std::string subst_spec::describe() const
{
std::string subst(1, foo);
subst += std::string(" ") + (fix? ':' : '=');
for (int i = 0, size = repl.size(); i < size; ++i)
{
const glyph_weighted_replacement_t &gly = repl[i];
subst += " ";
subst += static_cast<char>(gly.first);
if (gly.second != 10)
subst += make_stringf(":%d", gly.second);
}
return (subst);
}
bool subst_spec::operator == (const subst_spec &other) const
{
if (foo != other.foo || fix != other.fix)
return (false);
if (repl.size() != other.repl.size())
return (false);
for (int i = 0, size = repl.size(); i < size; ++i)
{
if (repl[i] != other.repl[i])
return (false);
}
return (true);
std::string nsubst_spec::apply_transform(map_lines &map)
{
map.nsubst(*this);
return ("");
}
std::string nsubst_spec::describe() const
{
return ("");
}
// shuffle_spec
std::string shuffle_spec::apply_transform(map_lines &map)
{
map.resolve_shuffle(shuffle);
return ("");
}
map_transformer::transform_type shuffle_spec::type() const
{
return (TT_SHUFFLE);
}
std::string shuffle_spec::describe() const
{
return (shuffle);
}
//////////////////////////////////////////////////////////////////////////
}
map_transformer::transform_type map_marker_spec::type() const
{
return (TT_MARKER);
}
std::string map_marker_spec::describe() const
{
return ("unimplemented");
static int dgn_map_add_transform(
lua_State *ls,
std::string (map_lines::*add)(const std::string &s),
void (map_lines::*erase)())
{
MAP(ls, 1, map);
for (int i = 2, size = lua_gettop(ls); i <= size; ++i)
{
if (lua_isnil(ls, i))
(map->map.*erase)();
else
{
std::string err = (map->map.*add)(luaL_checkstring(ls, i));
if (!err.empty())
luaL_error(ls, err.c_str());
}
}
// 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<map_def *>(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);
}
}
// Return the integer stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static int _table_int(lua_State *ls, int idx, const char *name, int defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isnumber(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not an int.", name);
int ret = (!nil && valid ? lua_tonumber(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
// Return the character stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static char _table_char(lua_State *ls, int idx, const char *name, char defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isstring(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a string.", name);
char ret = defval;
if (!nil && valid)
{
const char *str = lua_tostring(ls, idx);
if (str[0] && !str[1])
ret = str[0];
else
luaL_error(ls, "'%s' has more than one character.", name);
}
lua_pop(ls, 1);
return (ret);
}
// Return the string stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static const char* _table_str(lua_State *ls, int idx, const char *name, const char *defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isstring(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a string.", name);
const char *ret = (!nil && valid ? lua_tostring(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
// Return the boolean stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static bool _table_bool(lua_State *ls, int idx, const char *name, bool defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isboolean(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a bool.", name);
bool ret = (!nil && valid ? lua_toboolean(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
#define BF_INT(ls, val, def) int val = _table_int(ls, -1, #val, def);
#define BF_CHAR(ls, val, def) char val = _table_char(ls, -1, #val, def);
#define BF_STR(ls, val, def) const char *val = _table_str(ls, -1, #val, def);
#define BF_BOOL(ls, val, def) bool val = _table_bool(ls, -1, #val, def);
static void bf_octa_room(lua_State *ls, map_lines &lines)
{
int default_oblique = std::min(lines.width(), lines.height()) / 2 - 1;
BF_INT(ls, oblique, default_oblique);
BF_CHAR(ls, outside, 'x');
BF_CHAR(ls, inside, '.');
BF_STR(ls, replace, ".");
coord_def tl, br;
if (!lines.find_bounds(replace, tl, br))
return;
for (rectangle_iterator ri(tl, br); ri; ++ri)
{
const coord_def mc = *ri;
char glyph = lines(mc);
if (replace[0] && !strchr(replace, glyph))
continue;
int ob = 0;
ob += std::max(oblique + tl.x - mc.x, 0);
ob += std::max(oblique + mc.x - br.x, 0);
bool is_inside = (mc.y >= tl.y + ob && mc.y <= br.y - ob);
lines(mc) = is_inside ? inside : outside;
}
}
static void bf_smear(lua_State *ls, map_lines &lines)
{
BF_INT(ls, iterations, 1);
BF_CHAR(ls, smear, 'x');
BF_STR(ls, onto, ".");
BF_BOOL(ls, boxy, false);
const int max_test_per_iteration = 10;
int sanity = 0;
int max_sanity = iterations * max_test_per_iteration;
for (int i = 0; i < iterations; i++)
{
bool diagonals, straights;
coord_def mc;
do
{
do
{
sanity++;
mc.x = random_range(1, lines.width() - 2);
mc.y = random_range(1, lines.height() - 2);
}
while (onto[0] && !strchr(onto, lines(mc)));
// Prevent too many iterations.
if (sanity > max_sanity)
return;
// Is there a "smear" feature along the diagonal from mc?
diagonals = lines(coord_def(mc.x+1, mc.y+1)) == smear ||
lines(coord_def(mc.x-1, mc.y+1)) == smear ||
lines(coord_def(mc.x-1, mc.y-1)) == smear ||
lines(coord_def(mc.x+1, mc.y-1)) == smear;
// Is there a "smear" feature up, down, left, or right from mc?
straights = lines(coord_def(mc.x+1, mc.y)) == smear ||
lines(coord_def(mc.x-1, mc.y)) == smear ||
lines(coord_def(mc.x, mc.y+1)) == smear ||
lines(coord_def(mc.x, mc.y-1)) == smear;
}
while (!straights && (boxy || !diagonals));
lines(mc) = smear;
}
}
static void bf_extend(lua_State *ls, map_lines &lines)
{
BF_INT(ls, height, 1);
BF_INT(ls, width, 1);
BF_CHAR(ls, fill, 'x');
lines.extend(width, height, fill);
}
typedef void (*bf_func)(lua_State *ls, map_lines &lines);
struct bf_entry
{
const char* name;
bf_func func;
};
// Create a separate list of builder funcs so that we can automatically
// generate a list of closures for them, rather than individually
// and explicitly exposing them to the dgn namespace.
static struct bf_entry bf_map[] =
{
{ "map_octa_room", &bf_octa_room },
{ "map_smear", &bf_smear },
{ "map_extend", &bf_extend }
};
static int dgn_call_builder_func(lua_State *ls)
{
// This function gets called for all the builder functions that
// operate on map_lines.
MAP(ls, 1, map);
if (!lua_istable(ls, 2) && !lua_isfunction(ls, 2))
return luaL_argerror(ls, 2, "Expected table");
bf_func *func = (bf_func *)lua_topointer(ls, lua_upvalueindex(1));
if (!func)
return luaL_error(ls, "Expected C function in closure upval");
// Put the table on top.
lua_settop(ls, 2);
// Call the builder func itself.
(*func)(ls, map->map);
return (0);
}
static void _register_builder_funcs(lua_State *ls)
{
lua_getglobal(ls, "dgn");
const size_t num_entries = sizeof(bf_map) / sizeof(bf_entry);
for (size_t i = 0; i < num_entries; i++)
{
// Push a closure with the C function into the dgn table.
lua_pushlightuserdata(ls, &bf_map[i].func);
lua_pushcclosure(ls, &dgn_call_builder_func, 1);
lua_setfield(ls, -2, bf_map[i].name);
}
lua_pop(ls, 1);
}
FTILE: 1=floor_dirt, 2=floor_dirt, 3=floor_dirt, 4=floor_dirt, +=floor_dirt
FTILE: A=floor_dirt, B=floor_dirt, C=floor_dirt, D=floor_dirt, E=floor_dirt
FTILE: a=floor_dirt, b=floor_dirt, c=floor_dirt, d=floor_dirt, e=floor_dirt
FTILE: 1234+ABCDEabcde = floor_dirt
ENDMAP
###########################################################
# Basic island, with variations
#
NAME: enne_octagon_isle
ORIENT: float
TAGS: entry
FTILE: '[({ = floor_sand_stone
SUBST: ' = .
{{
map_octa_room({
oblique = crawl.random_range(3,7),
replace = '.',
outside = 'W',
inside = '.'
})
if crawl.coinflip() then
map_smear({iterations=20, smear='x', onto='.W', boxy = true})
end
}}
SUBST: W:xw
MAP
xxxxxxxxxxxxx
x.............x
x.............x
x.............x
x.............x
......www......
.....w[''w.....
@....w'{'w....@
.....w''(w.....
......www......
x.............x
x.............x
x.............x
x.............x
xxxxxxxxxxxxx
static int grd_get(lua_State *ls)
{
// Return a metatable for this column in the map grid.
map_def *map = *(map_def **) luaL_checkudata(ls, 1, GRD_METATABLE);
int column = luaL_checkint(ls, 2);
mapcolumn *mapref = clua_new_userdata<mapcolumn>(ls, GRD_COL_METATABLE);
mapref->map = map;
mapref->col = column;
return (1);
}
static int grd_set(lua_State *ls)
{
return (luaL_error(ls, "%s", "Cannot assign to read-only table."));
}
static char* grd_glyph(lua_State *ls, int &col, int &row)
{
mapcolumn *mapc = (mapcolumn *)luaL_checkudata(ls, 1, GRD_COL_METATABLE);
row = luaL_checkint(ls, 2);
col = mapc->col;
map_lines &lines = mapc->map->map;
if (row < 1 || col < 1 || col > lines.width() || row > lines.height())
{
return (NULL);
}
coord_def mc(col - 1, row - 1);
return (&lines(mc));
}
static int grd_col_get(lua_State *ls)
{
int col, row;
char *gly = grd_glyph(ls, col, row);
if (!gly)
return (luaL_error(ls, "Invalid coords: %d, %d", col, row));
char buf[2];
buf[0] = *gly;
buf[1] = '\0';
lua_pushstring(ls, buf);
return (1);
}
static int grd_col_set(lua_State *ls)
{
int col, row;
char *gly = grd_glyph(ls, col, row);
if (!gly)
return (luaL_error(ls, "Invalid coords: %d, %d", col, row));
const char *str = luaL_checkstring(ls, 3);
if (!str[0] || str[1])
return (luaL_error(ls, "%s", "grd must be set to a single char."));
(*gly) = str[0];
return (0);
}
void luaopen_grd(lua_State *ls)
{
// grd table
luaL_newmetatable(ls, GRD_METATABLE);
lua_pushstring(ls, "__index");
lua_pushcfunction(ls, grd_get);
lua_settable(ls, -3);
lua_pushstring(ls, "__newindex");
lua_pushcfunction(ls, grd_set);
lua_settable(ls, -3);
lua_pop(ls, 1);
// grd col table
luaL_newmetatable(ls, GRD_COL_METATABLE);
lua_pushstring(ls, "__index");
lua_pushcfunction(ls, grd_col_get);
lua_settable(ls, -3);
lua_pushstring(ls, "__newindex");
lua_pushcfunction(ls, grd_col_set);
lua_settable(ls, -3);
lua_pop(ls, 1);
}