Lua (Lua code can be embedded with {{ <lua code here> }} constructs) that is run to produce the final map.
Some maps may be broken, this is untested, lots more work needs to be done.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1629 c06c8d41-db1a-0410-9941-cceddc491573
W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC
HNXKX6ZDQJV33E7UKZOLBYWJMRZ4QLEMXVXJZNRCTOIG2KVRTIEAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
KXUQB3WNWC5IFL6VFWADEPQMMU3VV3NDI5FLA666PGOEWFYUHCLQC
JQFQX7IWSJ4TYWVUVXAFMCPSAN67PRMNECDQI5WMON2JFMQVVUEQC
34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC
WKTZHLOJ65WSK6FR5MF7RWGSMZ22T2D6LHB66FV3IPGXIBLYHHNAC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
AUYQDKLMOG4FGH2T3LACX4TH632DPKVXBNV5VU6PIVUEWSIO4LQQC
RIRJ746W5ESARX4HUEA4JRVAKXXF3WYVXUCFFONPJMMKWHQAGI2AC
A3CO4KBFTFU3ZSHWRY2OPPX3MMTFV7OUCZGL7Q4Y2FU7JO4AP7MAC
JR2RAQ523LOWNDYJNK6AZVKI6WVMI622PIV72XWOVZYPXPUKSQWAC
7NDXS36TE7QVXTXJWMYSVG5UHCCLPIO4VL6NXFGTDK3ZNKE3A2IAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
KFM2ARORBIJ6BGX456VFW7EAVRIYBVFUV53JH63GSKNOKVPJWQ2QC
SIKFXNXSAMU6IYRGDG6SWP3LOX6SEE7PDFA7RVQAGG2SLWQ72D2QC
ILN2K6ASDZSMEHOPJ22IZLZJUO6DDGZTKAKXM3YXG6JZZHJNLX4AC
KFJEFN377VIZ7OH2XCYOGCELNEGO4CIOOP7DNXEMX3LFKIKWXVTAC
5E7K2S4F4QDZC5UH3JVMYQ5SFJIKIATZII4EJL7IOT66ZBJ5GUPQC
MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
YE7M665QKDGI7Y5WMERCWJNDZ4FUZ6GRUCK4E6GZH4SWCUM6RWLAC
%token <i> NAME DEPTH ORIENT PLACE CHANCE FLAGS MONS ITEM
%token <i> ROOT_DEPTH ENTRY_MSG EXIT_MSG
%token <i> ROCK_COLOUR FLOOR_COLOUR
%token <i> ENCOMPASS FLOAT
%token <i> NORTH EAST SOUTH WEST
%token <i> NORTHEAST SOUTHEAST SOUTHWEST NORTHWEST
%token <i> LEVEL END PVAULT PMINIVAULT MONSTERS ENDMONSTERS
%token <i> NAME DEPTH ORIENT PLACE CHANCE MONS ITEM
%token <i> PRELUDE MAIN
defdepth : DEFAULT_DEPTH
{ lc_default_depths.clear(); }
default_depth_ranges
defdepth : DEFAULT_DEPTH STRING
{
dgn_reset_default_depth();
std::string err = dgn_set_default_depth($2);
if (!err.empty())
yyerror(make_stringf("Bad default-depth: %s (%s)",
$2, err.c_str()).c_str());
}
if (lc_map.orient == MAP_FLOAT
|| lc_map.is_minivault())
{
if (lc_map.map.width() > GXM - MAPGEN_BORDER * 2
|| lc_map.map.height() > GYM - MAPGEN_BORDER * 2)
{
char buf[300];
snprintf(buf, sizeof buf,
"%s is too big: %dx%d - max %dx%d",
lc_map.is_minivault()? "Minivault" : "Float",
lc_map.map.width(), lc_map.map.height(),
GXM - MAPGEN_BORDER * 2,
GYM - MAPGEN_BORDER * 2);
yyerror(buf);
}
}
else
{
if (lc_map.map.width() > GXM
|| lc_map.map.height() > GYM)
{
char buf[300];
snprintf(buf, sizeof buf,
"Map is too big: %dx%d - max %dx%d",
lc_map.map.width(), lc_map.map.height(),
GXM, GYM);
yyerror(buf);
}
}
if (lc_map.map.height() == 0)
yyerror("Must define map.");
std::string err = lc_map.validate();
if (!err.empty())
yyerror(err.c_str());
main_lua : MAIN main_lua_lines { }
main_lua_lines : /* empty */ { }
| main_lua_lines main_lua_line { }
;
main_lua_line : LUA_LINE
{
lc_map.main.add(yylineno, $1);
}
prelude_lua : PRELUDE prelude_lua_lines { }
prelude_lua_lines : /* empty */ { }
| prelude_lua_lines prelude_lua_line { }
;
prelude_lua_line : LUA_LINE
{
lc_map.prelude.add(yylineno, $1);
}
std::string err = lc_map.map.add_shuffle($1);
if (!err.empty())
yyerror(
make_stringf(
"Bad shuffle argument: '%s' (%s)",
$1, err.c_str() ).c_str() );
lc_map.main.add(
yylineno,
make_stringf("shuffle(\"%s\")",
quote_lua_string($1).c_str()));
std::string err = lc_map.map.add_subst($1);
if (!err.empty())
yyerror(
make_stringf(
"Bad SUBST argument: '%s' (%s)",
$1, err.c_str() ).c_str() );
lc_map.main.add(
yylineno,
make_stringf("subst(\"%s\")",
quote_lua_string($1).c_str()));
std::string error = lc_map.items.add_item($1);
if (error.size())
{
char errbuf[300];
snprintf(errbuf, sizeof errbuf,
"Invalid item descriptor: '%s' (%s)",
$1, error.c_str());
yyerror(errbuf);
}
if (lc_map.items.size() > 8)
yyerror("Too many items specified (max 8)");
lc_map.main.add(
yylineno,
make_stringf("item(\"%s\")",
quote_lua_string($1).c_str()));
std::string err = lc_map.mons.add_mons($1);
if (!err.empty())
{
char buf[300];
snprintf(buf, sizeof buf,
"bad monster spec '%s' (%s)",
$1, err.c_str());
yyerror(buf);
}
if (lc_map.mons.size() > 7)
yyerror("Too many monsters specified (max 7)");
lc_map.main.add(
yylineno,
make_stringf("mons(\"%s\")",
quote_lua_string($1).c_str()));
| DEPTH extended_depth_ranges {}
;
default_depth_ranges :
ext_range
{
lc_default_depths.push_back($1);
}
| default_depth_ranges COMMA ext_range
{
lc_default_depths.push_back($3);
}
;
extended_depth_ranges :
ext_range
{
lc_map.add_depth($1);
}
| extended_depth_ranges COMMA ext_range
{
lc_map.add_depth($3);
}
;
ext_range : lev_range { $$ = $1; }
| NOT lev_range { $$ = $2; $$.deny = true; }
;
lev_range : IDENTIFIER
{
$$ = set_range($1, 1, 100);
}
| IDENTIFIER COLON STAR
{
$$ = set_range($1, 1, 100);
}
| IDENTIFIER COLON INTEGER DASH INTEGER
{
$$ = set_range($1, $3, $5);
}
| IDENTIFIER COLON INTEGER
| DEPTH STRING
$$ = set_range($1, $3, $3);
}
| INTEGER DASH INTEGER
{
$$ = set_range("Any", $1, $3);
}
| INTEGER
{
$$ = set_range("Any", $1, $1);
lc_map.main.add(
yylineno,
make_stringf("depth(\"%s\")",
quote_lua_string($2).c_str()));
| ORIENT orient_name
{
lc_map.orient = (map_section_type) $2;
}
;
orient_name : ENCOMPASS { $$ = MAP_ENCOMPASS; }
| NORTH { $$ = MAP_NORTH; }
| EAST { $$ = MAP_EAST; }
| SOUTH { $$ = MAP_SOUTH; }
| WEST { $$ = MAP_WEST; }
| NORTHEAST { $$ = MAP_NORTHEAST; }
| SOUTHEAST { $$ = MAP_SOUTHEAST; }
| SOUTHWEST { $$ = MAP_SOUTHWEST; }
| NORTHWEST { $$ = MAP_NORTHWEST; }
| FLOAT { $$ = MAP_FLOAT; }
;
flags : FLAGS flagnames {}
;
flagnames : /* empty */
| flagname flagnames
| ORIENT STRING
switch ($1) {
case NO_HMIRROR:
lc_map.flags &= ~MAPF_MIRROR_HORIZONTAL;
break;
case NO_VMIRROR:
lc_map.flags &= ~MAPF_MIRROR_VERTICAL;
break;
case NO_ROTATE:
lc_map.flags &= ~MAPF_ROTATE;
break;
}
lc_map.main.add(
yylineno,
make_stringf("orient(\"%s\")",
quote_lua_string($2).c_str()));
BRANCHDEF: return BRANCH;
DEFAULT return DEFAULT;
DESC: return DESC;
BRANCH: return BRANCH;
ROOT_DEPTH: return ROOT_DEPTH;
FLOOR_COLOUR: return FLOOR_COLOUR;
ROCK_COLOUR: return ROCK_COLOUR;
LEVEL return LEVEL;
END return END;
PVAULT: return PVAULT;
PMINIVAULT: return PMINIVAULT;
ENTRY_MSG: { BEGIN(ARGUMENT); return ENTRY_MSG; }
EXIT_MSG: { BEGIN(ARGUMENT); return EXIT_MSG; }
MONSTERS return MONSTERS;
ENDMONSTERS return ENDMONSTERS;
pandemonic return PANDEMONIC;
no_hmirror return NO_HMIRROR;
no_vmirror return NO_VMIRROR;
no_rotate return NO_ROTATE;
encompass return ENCOMPASS;
north return NORTH;
south return SOUTH;
east return EAST;
west return WEST;
northeast return NORTHEAST;
northwest return NORTHWEST;
southeast return SOUTHEAST;
southwest return SOUTHWEST;
float return FLOAT;
parse_maps( lc_desfile = datafile_path( "splev.des" ) );
parse_maps( lc_desfile = datafile_path( "vaults.des" ) );
parse_maps( lc_desfile = datafile_path( "entry.des" ) );
parse_maps( lc_desfile = datafile_path( "ebranch.des" ) );
static const char *map_files[] =
{
"entry.des", "splev.des", "vaults.des", "ebranch.des"
};
for (unsigned i = 0; i < ARRAYSIZE(map_files); ++i)
parse_maps( lc_desfile = datafile_path( map_files[i] ) );
enum map_flags
{
MAPF_PANDEMONIUM_VAULT = 0x01, // A pandemonium minivault.
MAPF_MIRROR_VERTICAL = 0x10, // The map may be mirrored vertically
MAPF_MIRROR_HORIZONTAL = 0x20, // may be mirrored horizontally.
MAPF_ROTATE = 0x40 // may be rotated
};
level_range level_range::parse(std::string s) throw (std::string)
{
level_range lr;
trim_string(s);
if (s[0] == '!')
{
lr.deny = true;
s = trimmed_string(s.substr(1));
}
std::string::size_type cpos = s.find(':');
if (cpos == std::string::npos)
parse_partial(lr, s);
else
{
std::string branch = trimmed_string(s.substr(0, cpos));
std::string depth = trimmed_string(s.substr(cpos + 1));
parse_depth_range(depth, &lr.shallowest, &lr.deepest);
lr.set(branch, lr.shallowest, lr.deepest);
}
return (lr);
}
void level_range::parse_partial(level_range &lr, const std::string &s)
throw (std::string)
{
if (isdigit(s[0]))
{
lr.branch = NUM_BRANCHES;
parse_depth_range(s, &lr.shallowest, &lr.deepest);
}
else
lr.set(s, 1, 100);
}
void level_range::parse_depth_range(const std::string &s, int *l, int *h)
throw (std::string)
{
if (s == "*")
{
*l = 1;
*h = 100;
return;
}
std::string::size_type hy = s.find('-');
if (hy == std::string::npos)
{
*l = *h = atoi(s.c_str());
if (!*l)
throw std::string("Bad depth: ") + s;
}
else
{
*l = atoi(s.substr(0, hy).c_str());
std::string tail = s.substr(hy + 1);
if (tail.empty())
*h = 100;
else
*h = atoi(tail.c_str());
if (!*l || !*h || *l > *h)
throw std::string("Bad depth: ") + s;
}
}
void map_lines::remove_shuffle(const std::string &raw)
{
std::string s = raw;
const std::string err = check_shuffle(s);
if (err.empty())
{
const shuffle_spec ss(s);
for (int i = 0, size = transforms.size(); i < size; ++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;
}
}
}
}
}
void map_lines::remove_subst(const std::string &raw)
{
// 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, size = transforms.size(); i < size; ++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;
}
}
}
}
}
void map_lines::clear_transforms(map_transformer::transform_type tt)
{
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 );
}
}
void map_lines::clear_shuffles()
{
clear_transforms(map_transformer::TT_SHUFFLE);
}
void map_lines::clear_substs()
{
clear_transforms(map_transformer::TT_SUBST);
}
}
std::vector<std::string> map_lines::get_shuffle_strings() const
{
std::vector<std::string> shuffles;
for (int i = 0, size = transforms.size(); i < size; ++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, size = transforms.size(); i < size; ++i)
if (transforms[i]->type() == map_transformer::TT_SUBST)
substs.push_back( transforms[i]->describe() );
return (substs);
void map_def::set_file(const std::string &s)
{
prelude.set_file(s);
main.set_file(s);
}
void map_def::run_strip_prelude()
{
run_lua(false);
prelude.clear();
}
void map_def::strip_lua()
{
prelude.clear();
main.clear();
}
std::string map_def::run_lua(bool run_main)
{
dlua.callfn("dgn_set_map", "m", this);
int err = prelude.load(&dlua);
if (err == -1000)
lua_pushnil(dlua);
else if (err)
return (prelude.orig_error());
if (!run_main)
lua_pushnil(dlua);
else
{
err = main.load(&dlua);
if (err == -1000)
lua_pushnil(dlua);
else if (err)
return (main.orig_error());
}
if (!dlua.callfn("dgn_run_map", 2, 0))
return (dlua.error);
// Clear the map setting.
dlua.callfn("dgn_set_map", 0, 0);
return (dlua.error);
}
std::string map_def::validate()
{
std::string err = run_lua(true);
if (!err.empty())
return (err);
if (orient == MAP_FLOAT || is_minivault())
{
if (map.width() > GXM - MAPGEN_BORDER * 2
|| map.height() > GYM - MAPGEN_BORDER * 2)
{
return make_stringf(
"%s is too big: %dx%d - max %dx%d",
is_minivault()? "Minivault" : "Float",
map.width(), map.height(),
GXM - MAPGEN_BORDER * 2,
GYM - MAPGEN_BORDER * 2);
}
}
else
{
if (map.width() > GXM
|| map.height() > GYM)
{
return make_stringf(
"Map is too big: %dx%d - max %dx%d",
map.width(), map.height(),
GXM, GYM);
}
}
if (map.height() == 0)
return ("Must define map.");
return ("");
}
}
std::string item_list::set_item(int index, const std::string &spec)
{
error.clear();
if (index < 0)
return (error = make_stringf("Index %d out of range", index));
item_spec_slot sp = parse_item_spec(spec);
if (error.empty())
{
if (index >= (int) items.size())
{
items.reserve(index + 1);
items.resize(index + 1, item_spec_slot());
}
items.push_back(sp);
}
return (error);
}
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);
------------------------------------------------------------------------------
-- dungeon.lua:
-- Dungeoneering functions.
------------------------------------------------------------------------------
-- Given an object and a table (dgn), returns a table with functions
-- that translate into method calls on the object. This table is
-- suitable for setfenv() on a function that expects to directly
-- address a map object.
function dgn_map_meta_wrap(obj, tab)
local meta = { }
for fn, val in pairs(tab) do
meta[fn] = function (...)
return val(obj, ...)
end
end
meta.wrapped_instance = obj
local meta_meta = { __index = _G }
setmetatable(meta, meta_meta)
return meta
end
function dgn_set_map(map)
g_dgn_curr_map = map
end
function dgn_run_map(prelude, main)
if prelude or main then
env = dgn_map_meta_wrap(g_dgn_curr_map, dgn)
if prelude then
setfenv(prelude, env)()
end
if main then
setfenv(main, env)()
end
-- Return the environment in case we want to chain further
-- calls.
return env
end
end
#define LUAWRAP(name, wrapexpr) \
static int name(lua_State *ls) \
{ \
wrapexpr; \
return (0); \
}
#define PLUARET(type, val) \
lua_push##type(ls, val); \
return (1);
#define LUARET1(name, type, val) \
static int name(lua_State *ls) \
{ \
lua_push##type(ls, val); \
return (1); \
}
#define LUARET2(name, type, val1, val2) \
static int name(lua_State *ls) \
{ \
lua_push##type(ls, val1); \
lua_push##type(ls, val2); \
return (2); \
}
template <class T>
inline static T *util_get_userdata(lua_State *ls, int ndx)
{
return (lua_islightuserdata(ls, ndx))?
static_cast<T *>( lua_touserdata(ls, ndx) )
: NULL;
}
template <class T>
inline static T *clua_get_userdata(lua_State *ls, const char *mt)
{
return static_cast<T*>( luaL_checkudata( ls, 1, mt ) );
}
std::string quote_lua_string(const std::string &s);
#define MAP_METATABLE "dgn.mtmap"
extern void lua_open_kills(lua_State *ls);
void lua_open_you(lua_State *ls);
void lua_open_item(lua_State *ls);
void lua_open_food(lua_State *ls);
void lua_open_crawl(lua_State *ls);
void lua_open_file(lua_State *ls);
void lua_open_options(lua_State *ls);
void lua_open_monsters(lua_State *ls);
void lua_open_globals(lua_State *ls);
extern void luaopen_kills(lua_State *ls);
lua_open_kills(_state);
lua_open_you(_state);
lua_open_item(_state);
lua_open_food(_state);
lua_open_crawl(_state);
lua_open_file(_state);
lua_open_options(_state);
lua_open_monsters(_state);
luaopen_kills(_state);
luaopen_you(_state);
luaopen_item(_state);
luaopen_food(_state);
luaopen_crawl(_state);
luaopen_file(_state);
luaopen_options(_state);
luaopen_monsters(_state);
#define LUAWRAP(name, wrapexpr) \
static int name(lua_State *ls) \
{ \
wrapexpr; \
return (0); \
}
#define LUARET1(name, type, val) \
static int name(lua_State *ls) \
{ \
lua_push##type(ls, val); \
return (1); \
}
#define LUARET2(name, type, val1, val2) \
static int name(lua_State *ls) \
{ \
lua_push##type(ls, val1); \
lua_push##type(ls, val2); \
return (2); \
}
template <class T> T *util_get_userdata(lua_State *ls, int ndx)
{
return (lua_islightuserdata(ls, ndx))?
static_cast<T *>( lua_touserdata(ls, ndx) )
: NULL;
}
template <class T> T *clua_get_userdata(lua_State *ls, const char *mt)
{
return static_cast<T*>( luaL_checkudata( ls, 1, mt ) );
}
std::string quote_lua_string(const std::string &s)
{
return replace_all_of(replace_all_of(s, "\\", "\\\\"), "\"", "\\\"");
}