KFEAT: feature names are now as in the dungeon_feature_type enum.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1930 c06c8d41-db1a-0410-9941-cceddc491573
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
QQJQXDH2ABPJREQBK3ITJYPX3V3VQNC47UIYQGBSPBMR5CHJ56UQC
ITQ2EXDFLN3EHCURKYHZYVWFACD4KW7NA6CMHNKGWTDKHCHQAE6AC
AYYQVALBAAKFK5U52WGHWE2N7LCVZM3BGRADPVJQWOFLS2TXK6QAC
RDIHBEN2QCXK7AESR3GT7A7D3A2W3AXR5GTYEOJ2AA75ONXFQQTQC
QIDA6PWWBPR6IJOXFCSPCGWV45PVRZ4TPWSL2TDATNVTYLDMVESQC
AFE345BJ7IX2YYYX3I5I6RYLXNWJCFE4WMH6F5JMIR6X7WUP75CAC
F4WAQJD7KLOXHZOTOT6WG26PVIDHYNVS3AV6ZNXGAS7PDR4HBWQAC
NDTQUANX3GZ6HZP5FONYNJUYPD3R2P6SGRC3ICKJ7ZWF3KO23LTAC
WT66JDIRTLLP37SHTV4GI3V64JFJ4D25LNRLGCHFG6CLEFKJ3QGQC
TAHSTXR7ROOMDFUSBUU4ZAIEWQLAS5CIRCTARLD4Q2BGNLSL7E5QC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
RC6L3CIBLJEH4GWRFD7UQNGI6PZT74FRUVOYHSAN2XCC74NZUASQC
ANJ4QWXY2LDCFJRUSYFOR56CYBNCFZOVYYXWCZIQIBOEHYLUDM3QC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
AUYQDKLMOG4FGH2T3LACX4TH632DPKVXBNV5VU6PIVUEWSIO4LQQC
W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC
34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC
3PY3L3A4QRW3Z5Y7SHO4TMVOOP2VNCO27X2MX4DTOP2SADLBQUOAC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
EOMCPVNQLX3IMLC46EAO67DPBH5KEG2FQTPBLGU62HIRWA3UQ7XQC
CEK6M777MI5JVDC3KHE3JD3FETVSJ4VN6DGATBI5P3O6P5XHY4DAC
5ASC3STDYCNLZFEBN6UTMUCGDETHBR2OCBZCF5VIAZ5RRWLOTDYQC
PJDC24L7LXX6NZ7J5P7MNJJHAKQDJQEBV4CMDR4VSXLMWRIBMNKQC
GACH6PWPGGUBEE7PFEPQMOZKSR7HTQGL2WLGF2AQPJD3FCCSKZNQC
PTB7I4WQ3NTF7BE3O6WKXDSJD6QRWSZIEWPBSZGCJJZVNKT4OL5AC
5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC
T4IH76FA5TWHFOZUJFHLQXQJENJHWTUZZP4EGNA7D4GTZY7D4ZKAC
QDWDUURSNLMT6AXNNJ3DEQCWAKCAIHV6MP5F7QGIBGXOG2BI2NPQC
NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
ZP2KE7A2LE7Z2S7AC45WE4CXDSEVDTWIMV2EM4IBUKXYJIDU6R7QC
A3CO4KBFTFU3ZSHWRY2OPPX3MMTFV7OUCZGL7Q4Y2FU7JO4AP7MAC
KFJEFN377VIZ7OH2XCYOGCELNEGO4CIOOP7DNXEMX3LFKIKWXVTAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
B3SRWSFITQMJRVEBHGQQJARETYPSSDV6XKMQSSUTXEHTXRZKIQJQC
OYTCBRC7LE44EUVRZVYTOOVKQWJ6P6YE3FXTOGUTNKEMLNWPHKSQC
H3552BCIAVBLKAYKE4DHFLBLFW5RGRMYBMRRYHYEB5IPIJRUVU5QC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
GRH4XPIYHDOXXF3R3QPMZTFHGLO2OJAZS4FLNBBXG3DHTQQM7RDQC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
JTTHP2BEYEPBQMSDM7IKANTMKRPY6ACGL2JN4D3OBZ7HFXKAYEGQC
RGHXFBNIULRVRYLBGG5JZDMYVM2E2JJ2Y5KQPMU6PUS3V26G6ZXQC
JDPJS5SNW6ZTY5DYV5QUBG2WVJ45FJ4CAOK6KDJKPEI2HLIHVLRQC
UU5EKED2RA2U3CFZ3UEJQEWSWHQPEU7ZD4KH3I22IIVZFHD4Y67QC
IPXXB4VRVZWOU5DKQ5ZTD37LS3QNK2R6APNZUO672YEEJT6OFAYQC
YHSVOROKPYS33Y4RYZRVZTE3G5LXOFX52HEDNLV6HIXOJYNOKH3QC
YATODO2EN3AW7IXW5PL25HHK34YHF6Y32QBMOUZXEATZZ4YQQWZQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
2G55UEHQ7554OPNSZVTUCZTWSHIFKGT56QEGSYFKCTX547I4AL3AC
T6YW32VLZX2KKDMIS4G562ER73VHCAQZOQ6P4LSLVGVN25UD5UJQC
IGHO5UHUXYBLIHLUMLZ672YHAYUSK4FSFX7SA42XARIKLIMCVLUAC
CI5VTLSMB2L5W5ZVKDZEJFUARGSZP2FUSTRFV3MG6U44TDDUYH5AC
HNXKX6ZDQJV33E7UKZOLBYWJMRZ4QLEMXVXJZNRCTOIG2KVRTIEAC
YE7M665QKDGI7Y5WMERCWJNDZ4FUZ6GRUCK4E6GZH4SWCUM6RWLAC
Q2FZIIGQECGP2FKKWLGDBBQVGCFZ4JTY53PFPJP6X7YKC23AQGFQC
AVSMB4Y6F6ZMMNNPOAQQZ34OWC6N5JOURTEWFTUKDWMIIMLWWJUAC
5RRCORYDFW2N2YPUM4PDYZGI6RY7YWVMGWTY5ZIDVO6JRD5R5GAQC
WE3JT43OR4L6675GINGU4B3YDBMURJZHDDYY3VLHUJEBAKH2HYEAC
CIPVRZGLOZHCERK6YPOBV3P2E4IAB4H6D5EHLRQE2O5E4P4VCBUAC
2ZZD6EYMSPVCXZTICL4VGYGGQRRWDLZ24KBCDBVIYC54OZ4C6GGAC
R6XS2HO5QX2FJUGL5UQQRNETKCMYWTUFPHPPS5SYWK3OQA4UDUQQC
static void seen_staircase(unsigned char which_staircase,const coord_def& pos);
static void seen_other_thing(unsigned char which_thing, const coord_def& pos);
static void seen_staircase(dungeon_feature_type which_staircase,
const coord_def& pos);
static void seen_other_thing(dungeon_feature_type which_thing,
const coord_def& pos);
level_pos curpos(level_id::current());
// XXX Is there really no better way to do this?
curpos.pos.x = you.x_pos;
curpos.pos.y = you.y_pos;
if ( portals_present.find(curpos) != portals_present.end() )
portals_present.erase(curpos);
else
mprf(MSGCH_DIAGNOSTICS, "Oops - tried to unnotice bad portal.");
if (map.find(k) != map.end())
{
map.erase(k);
return (true);
}
return (false);
}
static bool unnotice_portal(const level_pos &pos)
{
return find_erase(portals_present, pos);
}
static bool unnotice_altar(const level_pos &pos)
{
return find_erase(altars_present, pos);
}
static bool unnotice_shop(const level_pos &pos)
{
return find_erase(shops_present, pos);
}
static bool unnotice_stair(const level_pos &pos)
{
const dungeon_feature_type feat = grd(pos.pos);
if (grid_is_branch_stairs(feat))
{
for (int i = 0; i < NUM_BRANCHES; ++i)
{
if (branches[i].entry_stairs == feat)
{
const branch_type br = static_cast<branch_type>(i);
return (find_erase(stair_level, br));
}
}
}
return (false);
}
bool unnotice_feature(const level_pos &pos)
{
return (unnotice_portal(pos)
|| unnotice_altar(pos)
|| unnotice_shop(pos)
|| unnotice_stair(pos));
if (stair_find == DNGN_ENTER_LABYRINTH || stair_find == DNGN_ENTER_BAZAAR)
{
// no longer a feature
if (stair_find == DNGN_ENTER_LABYRINTH)
unnotice_labyrinth_portal();
grd[you.x_pos][you.y_pos] = DNGN_FLOOR;
// remove any markers that were going to expire this labyrinth.
if (map_marker *marker = env_find_marker(you.pos(), MAT_TIMED_FEATURE))
dynamic_cast<map_timed_feature_marker*>(marker)->timeout(false);
}
if (stair_find == DNGN_ENTER_LABYRINTH)
dungeon_terrain_changed(you.pos(), DNGN_FLOOR);
const char *bell_urgency(int ticks) const;
const char *noise_maker(int ticks) const;
public:
// Ticks are a tenth of a turn.
int duration_ticks;
int warn_threshold;
bool initialised;
private:
void check_register_table();
bool get_table() const;
void push_fn_args(const char *fn) const;
bool callfn(const char *fn, bool warn_err = false) const;
map_timed_feature_marker::map_timed_feature_marker(
const coord_def &_pos,
int duration_turns,
dungeon_feature_type _feat)
: map_feature_marker(_pos, _feat), duration_ticks(duration_turns * 10),
warn_threshold(-1000)
map_lua_marker::map_lua_marker()
: map_marker(MAT_LUA_MARKER, coord_def()), initialised(false)
dungeon_events.register_listener(DET_TURN_ELAPSED, this);
lua_stack_cleaner clean(dlua);
if (dlua.loadstring(("return " + s).c_str(), "lua_marker"))
mprf(MSGCH_WARN, "lua_marker load error: %s", dlua.error.c_str());
if (!dlua.callfn("dgn_run_map", 1, 1))
mprf(MSGCH_WARN, "lua_marker exec error: %s", dlua.error.c_str());
check_register_table();
}
const dungeon_feature_type ft = grd(pos);
switch (ft)
{
case DNGN_ENTER_BAZAAR:
mprf(MSGCH_SOUND, "You %shear coins being counted.",
duration_ticks < 1000? "can faintly " : "");
break;
case DNGN_ENTER_LABYRINTH:
mprf(MSGCH_SOUND, "You hear a faint echoing snort.");
break;
default:
break;
}
lua_pushlightuserdata(dlua, this);
lua_pushnil(dlua);
lua_settable(dlua, LUA_REGISTRYINDEX);
std::string map_timed_feature_marker::describe() const
{
return make_stringf("timer: %d ticks (%s)",
duration_ticks, dungeon_feature_name(feat));
// Key is this.
lua_pushlightuserdata(dlua, this);
// Move key before value.
lua_insert(dlua, -2);
lua_settable(dlua, LUA_REGISTRYINDEX);
initialised = true;
if (ticks > 5000)
return "stately ";
else if (ticks > 4000)
return "";
else if (ticks > 2500)
return "brisk ";
else if (ticks > 1500)
return "urgent ";
else if (ticks > 0)
return "frantic ";
else
return "last, dying notes of the ";
// First save the unmarshall Lua function.
lua_pushlightuserdata(dlua, const_cast<map_lua_marker*>(this));
lua_gettable(dlua, LUA_REGISTRYINDEX);
return (lua_istable(dlua, -1));
case DNGN_ENTER_LABYRINTH:
return (ticks > 0? "tolling of a bell" : "bell");
case DNGN_ENTER_BAZAAR:
return (ticks > 0? "ticking of an ancient clock" : "clock");
default:
return (ticks > 0?
"trickling of a stream filled with giant, killer bugs."
: "stream");
mprf(MSGCH_WARN, "Couldn't find table.");
init = false;
marshallByte(th, init);
if (!init)
return;
// Call dlua_marker_function(table, 'read')
lua_pushstring(dlua, "read");
if (!dlua.callfn("dlua_marker_function", 2, 1))
end(1, false, "lua_marker: write error: %s", dlua.error.c_str());
// Right, what's on top should be a function. Save it.
dlua_chunk reader(dlua);
if (!reader.error.empty())
end(1, false, "lua_marker: couldn't save read function: %s",
reader.error.c_str());
marshallString(th, reader.compiled_chunk());
// Okay, saved the reader. Now ask the writer to do its thing.
// Call: dlua_marker_method(table, fname, marker)
get_table();
lua_pushstring(dlua, "write");
lua_pushlightuserdata(dlua, const_cast<map_lua_marker*>(this));
lua_pushlightuserdata(dlua, &th);
if (!dlua.callfn("dlua_marker_method", 4))
end(1, false, "lua_marker::write error: %s", dlua.error.c_str());
if (warn_threshold == -1000)
warn_threshold = std::max(50, duration_ticks - 500);
duration_ticks -= e.elapsed_ticks;
lua_stack_cleaner cln(dlua);
// Read the Lua chunk we saved.
const std::string compiled = unmarshallString(th, LUA_CHUNK_MAX_SIZE);
if (duration_ticks < warn_threshold || duration_ticks <= 0)
{
if (duration_ticks > 900)
warn_threshold = duration_ticks - 500;
else
warn_threshold = duration_ticks - 250;
if (duration_ticks > 0 && player_can_hear(pos))
mprf(MSGCH_SOUND, "You hear the %s%s.",
bell_urgency(duration_ticks),
noise_maker(duration_ticks));
dlua_chunk chunk = dlua_chunk::precompiled(compiled);
if (chunk.load(dlua))
end(1, false, "lua_marker::read error: %s", chunk.error.c_str());
dlua_push_userdata(dlua, this, MAPMARK_METATABLE);
lua_pushlightuserdata(dlua, &th);
if (!dlua.callfn("dlua_marker_read", 3, 1))
end(1, false, "lua_marker::read error: %s", dlua.error.c_str());
if (verbose)
{
if (see_grid(pos))
mprf("%s disappears!",
feature_description(grd(pos), NUM_TRAPS, false,
DESC_CAP_THE, false).c_str());
else
mpr("The walls and floor vibrate strangely for a moment.");
}
map_marker *marker = new map_lua_marker;
marker->read(th);
return (marker);
}
dungeon_terrain_changed(pos);
bool map_lua_marker::callfn(const char *fn, bool warn_err) const
{
const int top = lua_gettop(dlua);
push_fn_args(fn);
const bool res =
dlua.callfn("dlua_marker_method", lua_gettop(dlua) - top, 1);
if (!res && warn_err)
mprf(MSGCH_WARN, "mlua error: %s", dlua.error.c_str());
return (res);
}
// Kill this marker.
env_remove_marker(this);
void map_lua_marker::notify_dgn_event(const dgn_event &e)
{
lua_stack_cleaner clean(dlua);
push_fn_args("event");
clua_push_dgn_event(dlua, &e);
if (!dlua.callfn("dlua_marker_method", 4, 0))
mprf(MSGCH_WARN, "notify_dgn_event: Lua error: %s",
dlua.error.c_str());
map_marker *mt = new map_timed_feature_marker();
mt->read(th);
return (mt);
lua_stack_cleaner cln(dlua);
if (!callfn("describe"))
return make_stringf("error: %s", dlua.error.c_str());
std::string desc;
if (lua_isstring(dlua, -1))
desc = lua_tostring(dlua, -1);
return desc;
strip_tag(raw, "timer:", true);
int navg = strip_number_tag(raw, "avg:");
if (navg == TAG_UNFOUND)
navg = 1;
if (navg < 1 || navg > 20)
throw make_stringf("Bad marker spec '%s' (avg out of bounds)",
s.c_str());
dungeon_feature_type feat = DNGN_FLOOR;
std::string fname = strip_tag_prefix(raw, "feat:");
if (!fname.empty()
&& (feat = dungeon_feature_by_name(fname)) == DNGN_UNSEEN)
strip_tag(raw, "lua:");
map_lua_marker *mark = new map_lua_marker(raw, ctx);
if (!mark->initialised)
std::vector<std::string> limits = split_string("-", raw);
const int nlims = limits.size();
if (nlims < 1 || nlims > 2)
throw make_stringf("Malformed turn range (%s) in marker '%s'",
raw.c_str(), s.c_str());
const int low = atoi(limits[0].c_str());
const int high = nlims == 1? low : atoi(limits[1].c_str());
if (low == 0 || high < low)
throw make_stringf("Malformed turn range (%s) in marker '%s'",
raw.c_str(), s.c_str());
const int duration = low == high? low : random_range(low, high, navg);
return new map_timed_feature_marker(coord_def(0, 0),
duration, feat);
return (mark);
: transforms(), markers(), lines(), map_width(0), solid_north(false),
solid_east(false), solid_south(false), solid_west(false),
solid_checked(false)
: transforms(), markers(), lines(), map_width(0),
solid_north(false), solid_east(false), solid_south(false),
solid_west(false), solid_checked(false)
map(), mons(), items(), keyspecs(), prelude("dlprelude"), main("dlmain"),
validate("dlvalidate"), veto("dlveto"), index_only(false),
cache_offset(0L)
map(), mons(), items(), keyspecs(), prelude("dlprelude"),
main("dlmain"), validate("dlvalidate"), veto("dlveto"),
index_only(false), cache_offset(0L)
}
// Initialises a chunk from the function on the top of stack.
// This function must not be a closure, i.e. must not have any upvalues.
dlua_chunk::dlua_chunk(lua_State *ls)
: file(), chunk(), compiled(), context(), first(-1), last(-1), error()
{
clear();
lua_stack_cleaner cln(ls);
std::ostringstream out;
const int err = lua_dump(ls, dlua_compiled_chunk_writer, &out);
if (err)
{
const char *e = lua_tostring(ls, -1);
error = e? e : "Unknown error compiling chunk";
}
compiled = out.str();
}
dlua_chunk dlua_chunk::precompiled(const std::string &chunk)
{
dlua_chunk dchunk;
dchunk.compiled = chunk;
return (dchunk);
}
static const char *dgn_event_type_names[] =
{
"none", "turn", "mons_move", "player_move", "leave_level", "enter_level",
"player_los", "player_climb"
};
static dgn_event_type dgn_event_type_by_name(const std::string &name)
{
for (unsigned i = 0; i < ARRAYSIZE(dgn_event_type_names); ++i)
if (dgn_event_type_names[i] == name)
return static_cast<dgn_event_type>(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 < ARRAYSIZE(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<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_remove_marker(mark);
return (0);
}
static int dgn_feature_desc(lua_State *ls)
{
const dungeon_feature_type feat =
static_cast<dungeon_feature_type>(luaL_checkint(ls, 1));
const description_level_type dtype =
lua_isnumber(ls, 2)?
static_cast<description_level_type>(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_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;
dungeon_terrain_changed( coord_def( luaL_checkint(ls, 1),
luaL_checkint(ls, 2) ),
type, affect_player );
return (0);
static int file_marshall(lua_State *ls)
{
if (lua_gettop(ls) != 2)
luaL_error(ls, "Need two arguments: tag header and value");
tagHeader &th(*static_cast<tagHeader*>( lua_touserdata(ls, 1) ));
if (lua_isnumber(ls, 2))
marshallLong(th, luaL_checklong(ls, 2));
else if (lua_isstring(ls, 2))
marshallString(th, lua_tostring(ls, 2));
else if (lua_isfunction(ls, 2))
{
dlua_chunk chunk(ls);
marshallString(th, chunk.compiled_chunk());
}
return (0);
}
static int file_unmarshall_number(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need tag header as one argument");
tagHeader &th(*static_cast<tagHeader*>( lua_touserdata(ls, 1) ));
lua_pushnumber(ls, unmarshallLong(th));
return (1);
}
static int file_unmarshall_string(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need tag header as one argument");
tagHeader &th(*static_cast<tagHeader*>( lua_touserdata(ls, 1) ));
lua_pushstring(ls, unmarshallString(th).c_str());
return (1);
}
static int file_unmarshall_fn(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need tag header as one argument");
tagHeader &th(*static_cast<tagHeader*>( lua_touserdata(ls, 1) ));
const std::string s(unmarshallString(th, LUA_CHUNK_MAX_SIZE));
dlua_chunk chunk = dlua_chunk::precompiled(s);
if (chunk.load(dlua))
lua_pushnil(ls);
return (1);
}
enum lua_persist_type
{
LPT_NONE,
LPT_NUMBER,
LPT_STRING,
LPT_FUNCTION
};
static int file_marshall_meta(lua_State *ls)
{
if (lua_gettop(ls) != 2)
luaL_error(ls, "Need two arguments: tag header and value");
tagHeader &th(*static_cast<tagHeader*>( lua_touserdata(ls, 1) ));
lua_persist_type ptype = LPT_NONE;
if (lua_isnumber(ls, 2))
ptype = LPT_NUMBER;
else if (lua_isstring(ls, 2))
ptype = LPT_STRING;
else if (lua_isfunction(ls, 2))
ptype = LPT_FUNCTION;
else
luaL_error(ls, "Can marshall only numbers, strings and functions.");
marshallByte(th, ptype);
file_marshall(ls);
return (0);
}
static int file_unmarshall_meta(lua_State *ls)
{
tagHeader &th(*static_cast<tagHeader*>( lua_touserdata(ls, 1) ));
const lua_persist_type ptype =
static_cast<lua_persist_type>(unmarshallByte(th));
switch (ptype)
{
case LPT_NUMBER:
return file_unmarshall_number(ls);
case LPT_STRING:
return file_unmarshall_string(ls);
case LPT_FUNCTION:
return file_unmarshall_fn(ls);
default:
luaL_error(ls, "Unexpected type signature.");
}
// Never get here.
return (0);
}
static const struct luaL_reg file_lib[] =
{
{ "marshall", file_marshall },
{ "marshall_meta", file_marshall_meta },
{ "unmarshall_meta", file_unmarshall_meta },
{ "unmarshall_number", file_unmarshall_number },
{ "unmarshall_string", file_unmarshall_string },
{ "unmarshall_fn", file_unmarshall_fn },
{ NULL, NULL }
};
LUARET1(you_can_hear_pos, boolean,
player_can_hear(luaL_checkint(ls,1), luaL_checkint(ls, 2)))
static const struct luaL_reg you_lib[] =
{
{ "hear_pos", you_can_hear_pos },
{ NULL, NULL }
};
static int dgnevent_type(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->type);
}
static int dgnevent_place(lua_State *ls)
{
DEVENT(ls, 1, dev);
lua_pushnumber(ls, dev->place.x);
lua_pushnumber(ls, dev->place.y);
return (2);
}
static int dgnevent_ticks(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->elapsed_ticks);
}
static const struct luaL_reg dgnevent_lib[] =
{
{ "type", dgnevent_type },
{ "pos", dgnevent_place },
{ "ticks", dgnevent_ticks },
{ NULL, NULL }
};
static void luaopen_setmeta(lua_State *ls,
const char *global,
const luaL_reg *lua_lib,
const char *meta)
{
luaL_newmetatable(ls, meta);
lua_setglobal(ls, global);
luaL_openlib(ls, global, lua_lib, 0);
// Do <global>.__index = <global>
lua_pushstring(ls, "__index");
lua_pushvalue(ls, -2);
lua_settable(ls, -3);
}
static void luaopen_dgnevent(lua_State *ls)
{
luaopen_setmeta(ls, "dgnevent", dgnevent_lib, DEVENT_METATABLE);
}
static int mapmarker_pos(lua_State *ls)
{
MAPMARKER(ls, 1, mark);
lua_pushnumber(ls, mark->pos.x);
lua_pushnumber(ls, mark->pos.y);
return (2);
}
static const struct luaL_reg mapmarker_lib[] =
{
{ "pos", mapmarker_pos },
{ NULL, NULL }
};
static void luaopen_mapmarker(lua_State *ls)
{
luaopen_setmeta(ls, "mapmarker", mapmarker_lib, MAPMARK_METATABLE);
}
description_level_type description_type_by_name(const char *desc)
{
if (!desc)
return DESC_PLAIN;
if (!strcmp("The", desc))
return DESC_CAP_THE;
else if (!strcmp("the", desc))
return DESC_NOCAP_THE;
else if (!strcmp("A", desc))
return DESC_CAP_A;
else if (!strcmp("a", desc))
return DESC_NOCAP_A;
else if (!strcmp("Your", desc))
return DESC_CAP_YOUR;
else if (!strcmp("your", desc))
return DESC_NOCAP_YOUR;
else if (!strcmp("its", desc))
return DESC_NOCAP_ITS;
else if (!strcmp("worn", desc))
return DESC_INVENTORY_EQUIP;
else if (!strcmp("inv", desc))
return DESC_INVENTORY;
return DESC_PLAIN;
}
KFEAT: D = altar of Trog
KFEAT: E = altar of Makhleb
KFEAT: F = altar of Okawaru
KFEAT: G = altar of The Shining One
KFEAT: H = altar of Zin
KFEAT: I = altar of Elyvilon
KFEAT: J = altar of Sif Muna
KFEAT: K = altar of Vehumet
KFEAT: L = altar of Kikubaaqudgha
KFEAT: M = altar of Yredelemnul
KFEAT: N = altar of Xom
KFEAT: O = altar of Nemelex Xobeh
KFEAT: D = altar_trog
KFEAT: E = altar_makhleb
KFEAT: F = altar_okawaru
KFEAT: G = altar_shining_one
KFEAT: H = altar_zin
KFEAT: I = altar_elyvilon
KFEAT: J = altar_sif_muna
KFEAT: K = altar_vehumet
KFEAT: L = altar_kikubaaqudgha
KFEAT: M = altar_yredelemnul
KFEAT: N = altar_xom
KFEAT: O = altar_nemelex_xobeh
------------------------------------------------------------------------------
-- luamark.lua:
-- Lua map marker handling.
------------------------------------------------------------------------------
dofile('clua/lm_timed.lua')
dofile('clua/lm_1way.lua')
function dlua_marker_function(table, name)
return table[name]
end
function dlua_marker_method(table, name, marker, ...)
if table[name] then
return table[name](table, marker, ...)
end
end
function dlua_marker_read(fn, marker, th)
return fn(marker, th)
end
function timed_marker(pars)
return TimedMarker.new(pars)
end
lmark = { }
-- Marshalls a table comprising of keys that are strings or numbers only,
-- and values that are strings, numbers, functions, or tables only. The table
-- cannot have cycles, and the table's metatable is not preserved.
function lmark.marshall_table(th, table)
if not table then
file.marshall(th, -1)
return
end
-- Count the number of elements first (ugh)
local nsize = 0
local tsize = 0
for _, v in pairs(table) do
if type(v) == 'table' then
tsize = tsize + 1
else
nsize = nsize + 1
end
end
file.marshall(th, nsize)
for key, value in pairs(table) do
if type(value) ~= 'table' then
file.marshall_meta(th, key)
file.marshall_meta(th, value)
end
end
file.marshall(th, tsize)
for key, value in pairs(table) do
if type(value) == 'table' then
file.marshall_meta(th, key)
lmark.marshall_table(th, value)
end
end
end
-- Unmarshals a table marshaled by marshall_table.
function lmark.unmarshall_table(th)
local nsize = file.unmarshall_number(th)
if nsize == -1 then
return nil
end
local ret = { }
for i = 1, nsize do
local key = file.unmarshall_meta(th)
local val = file.unmarshall_meta(th)
ret[key] = val
end
local tsize = file.unmarshall_number(th)
for i = 1, tsize do
local key = file.unmarshall_meta(th)
local val = lmark.unmarshall_table(th)
ret[key] = val
end
return ret
end
------------------------------------------------------------------------------
-- lm_tmsg.lua:
-- Messaging for timed Lua markers.
------------------------------------------------------------------------------
TimedMessaging = { }
TimedMessaging.__index = TimedMessaging
function TimedMessaging._new()
local m = { }
setmetatable(m, TimedMessaging)
return m
end
function TimedMessaging.new(pars)
pars = pars or { }
local m = TimedMessaging._new()
m.noisemaker = pars.noisemaker
m.verb = pars.verb
m.finalmsg = pars.finalmsg
m.ranges = pars.ranges
m.initmsg = pars.initmsg or ''
return m
end
function TimedMessaging:init(tmarker, cm)
local lab = dgn.grid(cm:pos()) == dgn.feature_number('enter_labyrinth')
if not self.noisemaker then
self.noisemaker = lab and "an ancient clock" or "a massive bell"
end
self.verb = self.verb or (lab and 'ticking' or 'tolling')
if not self.finalmsg then
self.finalmsg = lab and "last, dying ticks of the clock"
or "last, dying notes of the bell"
end
if not self.ranges then
self.ranges = { { 5000, 'stately ' }, { 4000, '' },
{ 2500, 'brisk ' }, { 1500, 'urgent ' },
{ 0, 'frantic ' } }
end
self.check = self.check or tmarker.dur - 500
if self.check < 50 then
self.check = 50
end
if #self.initmsg > 0 and you.hear_pos(cm:pos()) then
crawl.mpr(self.initmsg, "sound")
end
end
function TimedMessaging:say_message(dur)
self.sound_channel = self.sound_channel or crawl.msgch_num('sound')
if dur <= 0 then
crawl.mpr("You hear the " .. self.finalmsg .. ".", self.sound_channel)
return
end
for _, chk in ipairs(self.ranges) do
if dur > chk[1] then
crawl.mpr("You hear the " .. chk[2] .. self.verb
.. " of " .. self.noisemaker .. ".",
self.sound_channel)
break
end
end
end
function TimedMessaging:event(luamark, cmarker, event)
if luamark.dur < self.check or luamark.dur <= 0 then
self.check = luamark.dur - 250
if luamark.dur > 900 then
self.check = self.check - 250
end
if you.hear_pos(cmarker:pos()) then
self:say_message(luamark.dur)
end
end
end
function TimedMessaging:write(th)
file.marshall(th, self.check)
file.marshall(th, self.noisemaker)
file.marshall(th, self.verb)
file.marshall(th, self.initmsg)
file.marshall(th, self.finalmsg)
lmark.marshall_table(th, self.ranges)
end
function TimedMessaging.read(th)
local tm = TimedMessaging._new()
tm.check = file.unmarshall_number(th)
tm.noisemaker = file.unmarshall_string(th)
tm.verb = file.unmarshall_string(th)
tm.initmsg = file.unmarshall_string(th)
tm.finalmsg = file.unmarshall_string(th)
tm.ranges = lmark.unmarshall_table(th)
return tm
end
function bell_clock_msg(pars)
return TimedMessaging.new(pars)
end
------------------------------------------------------------------------------
-- lm_timed.lua:
-- Lua timed map feature markers.
------------------------------------------------------------------------------
dofile('clua/lm_tmsg.lua')
TimedMarker = { }
TimedMarker.__index = TimedMarker
function TimedMarker._new()
local marker = { }
setmetatable(marker, TimedMarker)
return marker
end
function TimedMarker.new(pars)
pars = pars or { }
if not pars.msg then
error("No messaging object provided (msg = nil)")
end
pars.high = pars.high or pars.low or pars.turns or 1
pars.low = pars.low or pars.high or pars.turns or 1
local dur = crawl.random_range(pars.low, pars.high, pars.navg or 1)
local feat = pars.feat or 'floor'
local fnum = dgn.feature_number(feat)
if fnum == dgn.feature_number('unseen') then
error("Bad feature name: " .. feat)
end
local tmarker = TimedMarker._new()
tmarker.dur = dur * 10
tmarker.fnum = fnum
tmarker.feat = feat
tmarker.msg = pars.msg
return tmarker
end
function TimedMarker:activate(marker)
self.msg:init(self, marker)
dgn.register_listener(dgn.dgn_event_type('turn'), marker)
dgn.register_listener(dgn.dgn_event_type('player_climb'),
marker, marker:pos())
end
function TimedMarker:timeout(marker, verbose, affect_player)
local x, y = marker:pos()
if verbose then
if you.see_grid(marker:pos()) then
crawl.mpr(dgn.feature_desc(dgn.grid(x, y), "The") ..
" disappears!")
else
crawl.mpr("The walls and floor vibrate strangely for a moment.")
end
end
dgn.terrain_changed(x, y, self.fnum, affect_player)
dgn.remove_listener(marker)
dgn.remove_listener(marker, marker:pos())
dgn.remove_marker(marker)
end
function TimedMarker:event(marker, ev)
self.ticktype = self.ticktype or dgn.dgn_event_type('turn')
self.stairtype = self.stairtype or dgn.dgn_event_type('player_climb')
if ev:type() == self.stairtype then
local mx, my = marker:pos()
local ex, ey = ev:pos()
if mx == ex and my == ey then
self:timeout(marker, false, false)
end
return
end
if ev:type() ~= self.ticktype then
return
end
self.dur = self.dur - ev:ticks()
self.msg:event(self, marker, ev)
if self.dur <= 0 then
self:timeout(marker, true, true)
end
end
function TimedMarker:describe(marker)
return self.feat .. "/" .. tostring(self.dur)
end
function TimedMarker.read(marker, th)
local marker = TimedMarker._new()
marker.dur = file.unmarshall_number(th)
marker.fnum = file.unmarshall_number(th)
marker.feat = file.unmarshall_string(th)
marker.msg = file.unmarshall_fn(th)(th)
return marker
end
function TimedMarker:write(marker, th)
file.marshall(th, self.dur)
file.marshall(th, self.fnum)
file.marshall(th, self.feat)
file.marshall(th, self.msg.read)
self.msg:write(th)
end
------------------------------------------------------------------------------
-- lm_1way.lua:
-- One-way stair marker.
------------------------------------------------------------------------------
OneWayStair = { }
OneWayStair.__index = OneWayStair
function OneWayStair.new()
local ows = { }
setmetatable(ows, OneWayStair)
return ows
end
function OneWayStair:activate(marker)
local ev = dgn.dgn_event_type('player_climb')
dgn.register_listener(ev, marker, marker:pos())
end
function OneWayStair:event(marker, ev)
if ev:type() == dgn.dgn_event_type('player_climb') then
local x, y = ev:pos()
dgn.terrain_changed(x, y, 'floor', false)
dgn.remove_listener(marker, ev:pos())
dgn.remove_marker(marker)
end
end
function OneWayStair.read(marker, th)
return OneWayStair.new()
end
function one_way_stair()
return OneWayStair.new()
end
lua_settop(_state, 1);
lua_pushboolean(_state, managed_vm);
setregistry("lua_vm_is_managed");
}
bool CLua::is_managed_vm(lua_State *ls)
{
lua_stack_cleaner clean(ls);
lua_pushstring(ls, "lua_vm_is_managed");
lua_gettable(ls, LUA_REGISTRYINDEX);
return (lua_toboolean(ls, -1));
}
static description_level_type desc_code(const char *desc)
{
if (!desc)
return DESC_PLAIN;
if (!strcmp("The", desc))
return DESC_CAP_THE;
else if (!strcmp("the", desc))
return DESC_NOCAP_THE;
else if (!strcmp("A", desc))
return DESC_CAP_A;
else if (!strcmp("a", desc))
return DESC_NOCAP_A;
else if (!strcmp("Your", desc))
return DESC_CAP_YOUR;
else if (!strcmp("your", desc))
return DESC_NOCAP_YOUR;
else if (!strcmp("its", desc))
return DESC_NOCAP_ITS;
else if (!strcmp("worn", desc))
return DESC_INVENTORY_EQUIP;
else if (!strcmp("inv", desc))
return DESC_INVENTORY;
return DESC_PLAIN;
KFEAT features are specified as a unique substring of the in-game
description of the feature; simple * and ? wildcards can also be
used. For instance, to place a portal to the Abyss, you can use:
KFEAT features are specified as a feature name (see section I
for a full list of feature names). As another example, you can
place a portal to the Abyss as: