From f2a19d9971bb076f4a80a9e41ba601bc862d0fdf Mon Sep 17 00:00:00 2001 From: dshaligram Date: Thu, 26 Jul 2007 11:06:09 +0000 Subject: Replaced timed markers with Lua markers. Breaks save compatibility. 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 --- crawl-ref/docs/level-design.txt | 14 +- crawl-ref/source/clua.cc | 96 ++++----- crawl-ref/source/clua.h | 31 ++- crawl-ref/source/dat/bazaar.des | 8 +- crawl-ref/source/dat/clua/lm_1way.lua | 35 ++++ crawl-ref/source/dat/clua/lm_timed.lua | 105 ++++++++++ crawl-ref/source/dat/clua/lm_tmsg.lua | 106 ++++++++++ crawl-ref/source/dat/clua/luamark.lua | 88 ++++++++ crawl-ref/source/dat/float.des | 2 +- crawl-ref/source/dat/hells.des | 4 +- crawl-ref/source/dat/lab.des | 7 +- crawl-ref/source/dat/large.des | 2 +- crawl-ref/source/dat/mini.des | 22 +- crawl-ref/source/dat/orc.des | 6 +- crawl-ref/source/dat/temple.des | 24 +-- crawl-ref/source/dgnevent.h | 5 +- crawl-ref/source/direct.cc | 25 ++- crawl-ref/source/dungeon.cc | 1 + crawl-ref/source/externs.h | 1 + crawl-ref/source/files.cc | 1 + crawl-ref/source/libutil.cc | 27 +++ crawl-ref/source/libutil.h | 3 + crawl-ref/source/luadgn.cc | 367 ++++++++++++++++++++++++++++++++- crawl-ref/source/luadgn.h | 13 ++ crawl-ref/source/mapdef.cc | 40 ++-- crawl-ref/source/mapdef.h | 2 + crawl-ref/source/mapmark.cc | 298 +++++++++++++------------- crawl-ref/source/mapmark.h | 48 +++-- crawl-ref/source/message.cc | 6 + crawl-ref/source/misc.cc | 36 ++-- crawl-ref/source/misc.h | 4 +- crawl-ref/source/mon-util.cc | 12 ++ crawl-ref/source/monstuff.cc | 16 +- crawl-ref/source/mstuff2.cc | 6 +- crawl-ref/source/overmap.cc | 72 +++++-- crawl-ref/source/overmap.h | 3 +- crawl-ref/source/player.cc | 9 +- crawl-ref/source/tags.cc | 9 +- crawl-ref/source/tags.h | 2 +- crawl-ref/source/util/levcomp.lpp | 26 ++- crawl-ref/source/util/levcomp.ypp | 27 ++- crawl-ref/source/view.cc | 5 + crawl-ref/source/view.h | 1 + 43 files changed, 1267 insertions(+), 348 deletions(-) create mode 100644 crawl-ref/source/dat/clua/lm_1way.lua create mode 100644 crawl-ref/source/dat/clua/lm_timed.lua create mode 100644 crawl-ref/source/dat/clua/lm_tmsg.lua create mode 100644 crawl-ref/source/dat/clua/luamark.lua (limited to 'crawl-ref') diff --git a/crawl-ref/docs/level-design.txt b/crawl-ref/docs/level-design.txt index 2c42429363..33d52687c2 100644 --- a/crawl-ref/docs/level-design.txt +++ b/crawl-ref/docs/level-design.txt @@ -517,7 +517,7 @@ NSUBST: ? = 3:w / *:l omit the initial N: or N=, then 1= is assumed, except for the last spec where *= is assumed. -KFEAT: Z = C / needle trap / antique armour shop / altar of Zin +KFEAT: Z = C / needle trap / antique armour shop / altar_zin The KFEAT: directive allows you to specify a placeholder symbol that is replaced with another symbol, named feature, trap, or shop. For example, the line above will replace occurrences of Z @@ -530,11 +530,11 @@ KFEAT: Z = C / needle trap / antique armour shop / altar of Zin You'll notice that 'Z' is the symbol of the Orb of Zot. Kxxx directives allow you to assign arbitrary definitions to any symbol. - 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: - KFEAT: A = gate to the * Abyss + KFEAT: A = enter_abyss If you want no feature as an option in a KFEAT line, use 'floor'. If you do not want to specify the type of shop, use 'any shop' or @@ -611,8 +611,8 @@ MARKER: A = feat: or timer: feature to deep water at the end of it. Feature names used in markers must be names matching the - names in the source code, not the descriptions as used in - KFEAT. There's a full list of feature names at the end of this + names in the source code. There's a full list of feature + names in section I (Feature names) at the end of this document. diff --git a/crawl-ref/source/clua.cc b/crawl-ref/source/clua.cc index d16c2bf31d..717707c1cc 100644 --- a/crawl-ref/source/clua.cc +++ b/crawl-ref/source/clua.cc @@ -11,6 +11,7 @@ #include "chardump.h" #include "cio.h" #include "delay.h" +#include "dgnevent.h" #include "files.h" #include "food.h" #include "invent.h" @@ -32,6 +33,7 @@ #include "spl-util.h" #include "stuff.h" #include "travel.h" +#include "view.h" #include #include @@ -215,7 +217,7 @@ int CLua::loadstring(const char *s, const char *context) return loadbuffer(s, strlen(s), context); } -int CLua::execstring(const char *s, const char *context) +int CLua::execstring(const char *s, const char *context, int nresults) { int err = 0; if ((err = loadstring(s, context))) @@ -223,7 +225,7 @@ int CLua::execstring(const char *s, const char *context) lua_State *ls = state(); lua_call_throttle strangler(this); - err = lua_pcall(ls, 0, 0, 0); + err = lua_pcall(ls, 0, nresults, 0); set_error(err, ls); return err; } @@ -266,6 +268,9 @@ int CLua::execfile(const char *filename, bool trusted, bool die_on_fail) if (!err) err = lua_pcall(ls, 0, 0, 0); set_error(err); + if (die_on_fail && !error.empty()) + end(1, false, "Lua execfile error (%s): %s", + filename, dlua.error.c_str()); return (err); } @@ -411,6 +416,9 @@ int CLua::push_args(lua_State *ls, const char *format, va_list args, case 'b': lua_pushboolean(ls, va_arg(args, int)); break; + case 'D': + clua_push_dgn_event(ls, va_arg(args, const dgn_event *)); + break; case 'm': clua_push_map(ls, va_arg(args, map_def *)); break; @@ -579,6 +587,8 @@ void CLua::init_lua() if (!_state) return; + lua_stack_cleaner clean(_state); + lua_atpanic(_state, clua_panic); luaopen_base(_state); @@ -601,16 +611,25 @@ void CLua::init_lua() load_cmacro(); load_chooks(); + lua_register(_state, "loadfile", clua_loadfile); + lua_register(_state, "dofile", clua_dofile); + if (managed_vm) { lua_register(_state, "pcall", clua_guarded_pcall); - lua_register(_state, "loadfile", clua_loadfile); - lua_register(_state, "dofile", clua_dofile); - execfile("clua/userbase.lua", true, true); - } + } - 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)); } void CLua::load_chooks() @@ -652,15 +671,6 @@ static void clua_register_metatable(lua_State *ls, const char *tn, lua_settop(ls, top); } -template T *clua_new_userdata( - lua_State *ls, const char *mt) -{ - void *udata = lua_newuserdata( ls, sizeof(T) ); - luaL_getmetatable(ls, mt); - lua_setmetatable(ls, -2); - return static_cast( udata ); -} - ///////////////////////////////////////////////////////////////////// // Bindings to get information on the player // @@ -724,6 +734,9 @@ LUARET1(you_branch, string, level_id::current().describe(false, false).c_str()) LUARET1(you_subdepth, number, level_id::current().depth) LUARET1(you_absdepth, number, you.your_level + 1) LUAWRAP(you_stop_activity, interrupt_activity(AI_FORCE_INTERRUPT)) +LUARET1(you_turns, number, you.num_turns) +LUARET1(you_see_grid, boolean, + see_grid(luaL_checkint(ls, 1), luaL_checkint(ls, 2))) void lua_push_floor_items(lua_State *ls); static int you_floor_items(lua_State *ls) @@ -764,6 +777,7 @@ static int l_you_abils(lua_State *ls) static const struct luaL_reg you_lib[] = { { "turn_is_over", you_turn_is_over }, + { "turns" , you_turns }, { "spells" , l_you_spells }, { "abilities" , l_you_abils }, { "name" , you_name }, @@ -800,6 +814,9 @@ static const struct luaL_reg you_lib[] = { "branch", you_branch }, { "subdepth", you_subdepth }, { "absdepth", you_absdepth }, + + { "see_grid", you_see_grid }, + { NULL, NULL }, }; @@ -1268,33 +1285,6 @@ static int l_item_worn(lua_State *ls) return (2); } -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; -} - static int l_item_name(lua_State *ls) { LUA_ITEM(item, 1); @@ -1302,7 +1292,9 @@ static int l_item_name(lua_State *ls) { description_level_type ndesc = DESC_PLAIN; if (lua_isstring(ls, 2)) - ndesc = desc_code(lua_tostring(ls, 2)); + ndesc = description_type_by_name(lua_tostring(ls, 2)); + else if (lua_isnumber(ls, 2)) + ndesc = static_cast(luaL_checkint(ls, 2)); bool terse = lua_toboolean(ls, 3); lua_pushstring(ls, item->name(ndesc, terse).c_str()); } @@ -1916,6 +1908,9 @@ LUARET1(crawl_random2, number, random2( luaL_checkint(ls, 1) )) LUARET1(crawl_one_chance_in, boolean, one_chance_in( luaL_checkint(ls, 1) )) LUARET1(crawl_random2avg, number, random2avg( luaL_checkint(ls, 1), luaL_checkint(ls, 2) )) +LUARET1(crawl_random_range, number, + random_range( luaL_checkint(ls, 1), luaL_checkint(ls, 2), + lua_isnumber(ls, 3)? luaL_checkint(ls, 3) : 1 )) LUARET1(crawl_coinflip, boolean, coinflip()) static int crawl_err_trace(lua_State *ls) @@ -1959,6 +1954,7 @@ static const struct luaL_reg crawl_lib[] = { "one_chance_in", crawl_one_chance_in }, { "random2avg" , crawl_random2avg }, { "coinflip", crawl_coinflip }, + { "random_range", crawl_random_range }, { "redraw_screen", crawl_redraw_screen }, { "input_line", crawl_input_line }, { "c_input_line", crawl_c_input_line}, @@ -1996,7 +1992,6 @@ void luaopen_crawl(lua_State *ls) /////////////////////////////////////////////////////////// // File operations - static const struct luaL_reg file_lib[] = { { "write", CLua::file_write }, @@ -2262,6 +2257,13 @@ void clua_push_map(lua_State *ls, map_def *map) *mapref = map; } +void clua_push_dgn_event(lua_State *ls, const dgn_event *devent) +{ + const dgn_event **de = + clua_new_userdata(ls, DEVENT_METATABLE); + *de = devent; +} + void luaopen_monsters(lua_State *ls) { luaL_newmetatable(ls, MONS_METATABLE); @@ -2626,7 +2628,7 @@ static int clua_loadfile(lua_State *ls) if (!file) return (0); - const int err = CLua::loadfile(ls, file); + const int err = CLua::loadfile(ls, file, !CLua::is_managed_vm(ls)); if (err) { const int place = lua_gettop(ls); @@ -2643,7 +2645,7 @@ static int clua_dofile(lua_State *ls) if (!file) return (0); - const int err = CLua::loadfile(ls, file); + const int err = CLua::loadfile(ls, file, !CLua::is_managed_vm(ls)); if (err) return (lua_error(ls)); diff --git a/crawl-ref/source/clua.h b/crawl-ref/source/clua.h index 44da65b057..99a4e24bc3 100644 --- a/crawl-ref/source/clua.h +++ b/crawl-ref/source/clua.h @@ -23,6 +23,17 @@ extern "C" { #endif class CLua; + +class lua_stack_cleaner +{ +public: + lua_stack_cleaner(lua_State *_ls) : ls(_ls), top(lua_gettop(_ls)) { } + ~lua_stack_cleaner() { lua_settop(ls, top); } +private: + lua_State *ls; + int top; +}; + class lua_call_throttle { public: @@ -55,7 +66,7 @@ public: void setglobal(const char *name); void getglobal(const char *name); - + // Assigns the value on top of the stack to a unique name in the registry // and returns the name. std::string setuniqregistry(); @@ -65,7 +76,8 @@ public: int loadbuffer(const char *buf, size_t size, const char *context); int loadstring(const char *str, const char *context); - int execstring(const char *str, const char *context = "init.txt"); + int execstring(const char *str, const char *context = "init.txt", + int nresults = 0); int execfile(const char *filename, bool trusted = false, bool die_on_fail = false); @@ -80,6 +92,8 @@ public: bool trusted = false, bool die_on_fail = false); static bool is_path_safe(std::string file, bool trusted = false); + static bool is_managed_vm(lua_State *ls); + public: std::string error; @@ -208,8 +222,21 @@ inline static T *clua_get_userdata(lua_State *ls, const char *mt) std::string quote_lua_string(const std::string &s); class map_def; +class dgn_event; void clua_push_map(lua_State *ls, map_def *map); +void clua_push_dgn_event(lua_State *ls, const dgn_event *devent); + +template T *clua_new_userdata( + lua_State *ls, const char *mt) +{ + void *udata = lua_newuserdata( ls, sizeof(T) ); + luaL_getmetatable(ls, mt); + lua_setmetatable(ls, -2); + return static_cast( udata ); +} #define MAP_METATABLE "dgn.mtmap" +#define DEVENT_METATABLE "dgn.devent" +#define MAPMARK_METATABLE "dgn.mapmark" #endif diff --git a/crawl-ref/source/dat/bazaar.des b/crawl-ref/source/dat/bazaar.des index 618509f125..cc1cb17d0b 100644 --- a/crawl-ref/source/dat/bazaar.des +++ b/crawl-ref/source/dat/bazaar.des @@ -9,8 +9,14 @@ lua {{ function check_expire_marker(e) + e.messager = + bell_clock_msg { initmsg="You hear coins being counted." } if not crawl.one_chance_in(3) then - e.marker("O = timer: 1000-1500") + e.marker( [[ O = lua: timed_marker { + low=1000, high=1500, msg=messager } + ]] ) + else + e.marker("O = lua: one_way_stair()") end end }} diff --git a/crawl-ref/source/dat/clua/lm_1way.lua b/crawl-ref/source/dat/clua/lm_1way.lua new file mode 100644 index 0000000000..6ce1e6d722 --- /dev/null +++ b/crawl-ref/source/dat/clua/lm_1way.lua @@ -0,0 +1,35 @@ +------------------------------------------------------------------------------ +-- 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 diff --git a/crawl-ref/source/dat/clua/lm_timed.lua b/crawl-ref/source/dat/clua/lm_timed.lua new file mode 100644 index 0000000000..8b9f1aaf26 --- /dev/null +++ b/crawl-ref/source/dat/clua/lm_timed.lua @@ -0,0 +1,105 @@ +------------------------------------------------------------------------------ +-- 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 diff --git a/crawl-ref/source/dat/clua/lm_tmsg.lua b/crawl-ref/source/dat/clua/lm_tmsg.lua new file mode 100644 index 0000000000..1448e7ade0 --- /dev/null +++ b/crawl-ref/source/dat/clua/lm_tmsg.lua @@ -0,0 +1,106 @@ +------------------------------------------------------------------------------ +-- 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 diff --git a/crawl-ref/source/dat/clua/luamark.lua b/crawl-ref/source/dat/clua/luamark.lua new file mode 100644 index 0000000000..d2d12ce34e --- /dev/null +++ b/crawl-ref/source/dat/clua/luamark.lua @@ -0,0 +1,88 @@ +------------------------------------------------------------------------------ +-- 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 diff --git a/crawl-ref/source/dat/float.des b/crawl-ref/source/dat/float.des index 4922553783..175ef6ee52 100644 --- a/crawl-ref/source/dat/float.des +++ b/crawl-ref/source/dat/float.des @@ -283,7 +283,7 @@ MONS: rat/grey rat/w:6 green rat/w:3 orange rat/w:20 giant bat MONS: flying skull, human zombie MONS: vampire, vampire mage, vampire knight KITEM: > = any good_item -KFEAT: ; = altar of Yredelemnul/altar of Kikubaaqudgha +KFEAT: ; = altar_yredelemnul/altar_kikubaaqudgha SHUFFLE: defg SHUFFLE: *s/!? SUBST: d = =, e = x, f = x, g = x diff --git a/crawl-ref/source/dat/hells.des b/crawl-ref/source/dat/hells.des index 05e7d75db8..a98f785006 100644 --- a/crawl-ref/source/dat/hells.des +++ b/crawl-ref/source/dat/hells.des @@ -111,7 +111,9 @@ ENDMAP NAME: vestibule_of_hell PLACE: Hell ORIENT: encompass -MARKER: D=feat:enter_dis, G=feat:enter_gehenna, C=feat:enter_cocytus +MARKER: D=feat:enter_dis +MARKER: G=feat:enter_gehenna +MARKER: C=feat:enter_cocytus MARKER: T=feat:enter_tartarus SUBST: D=A, G=A, C=A, T=A diff --git a/crawl-ref/source/dat/lab.des b/crawl-ref/source/dat/lab.des index a5a4e68cec..72056aa1d4 100644 --- a/crawl-ref/source/dat/lab.des +++ b/crawl-ref/source/dat/lab.des @@ -12,7 +12,10 @@ NAME: lab_entry_generic TAGS: lab_entry transparent DEPTH: 12-26 ORIENT: float -MARKER: O = timer:400-600 +: messager = bell_clock_msg { initmsg="You hear a distant snort." } +MARKER: O = lua: timed_marker { \ + low=400, high=600, msg=messager \ + } MAP O ENDMAP @@ -344,7 +347,7 @@ ENDMAP # Teaser: fake exit NAME: labyrinth_fake_exit TAGS: lab -KFEAT: X = gate * Abyss +KFEAT: X = enter_abyss MAP ........ .vvvvvv. diff --git a/crawl-ref/source/dat/large.des b/crawl-ref/source/dat/large.des index 3261f5b342..e34102e181 100644 --- a/crawl-ref/source/dat/large.des +++ b/crawl-ref/source/dat/large.des @@ -781,7 +781,7 @@ FLAGS: no_rotate DEPTH: D:14-26, Orc MONS: orc warlord, orc priest, orc high priest, orc warrior, orc wizard MONS: orc knight, orc sorcerer -KFEAT: C = altar of Beogh +KFEAT: C = altar_beogh MAP xxxxxxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx diff --git a/crawl-ref/source/dat/mini.des b/crawl-ref/source/dat/mini.des index bb0b679b8d..97472702c6 100644 --- a/crawl-ref/source/dat/mini.des +++ b/crawl-ref/source/dat/mini.des @@ -398,7 +398,7 @@ NAME: david_defended_altar DEPTH: 7-20, !Lair, !Swamp, !Shoal SHUFFLE: 1ID/2TC MONS: orc priest, deep elf priest / w:30 nothing -KFEAT: D = altar of Beogh / w:2 altar of Okawaru / w:2 altar of Makhleb +KFEAT: D = altar_beogh / w:2 altar_okawaru / w:2 altar_makhleb MAP ccccc... c1..c... @@ -1106,11 +1106,11 @@ SHUFFLE: !; SUBST: " = .:80 W:30 SUBST: ; = .:80 W:30 SUBST: ! = w:20 W ? c -KFEAT: 3 = deep water -KFEAT: 2 = shallow water / . -KFEAT: 1 = shallow water / . w:30 -KFEAT: d = shallow water / . -KFEAT: e = shallow water / . +KFEAT: 3 = w +KFEAT: 2 = W / . +KFEAT: 1 = W / . w:30 +KFEAT: d = W / . +KFEAT: e = W / . KFEAT: ? = spear trap / blade trap MAP ......... @@ -1135,11 +1135,11 @@ MONS: yaktaur captain, grey snake, storm dragon ITEM: potion of cure mutation/potion of gain dexterity/potion of gain strength ITEM: potion of gain intelligence/potion of experience/w:40 potion of heal wound SHUFFLE: de, !; -KFEAT: 3 = deep water / . -KFEAT: | = shallow water / . -KFEAT: d = shallow water / . -KFEAT: e = shallow water / . -KFEAT: 2 = shallow water +KFEAT: 3 = w / . +KFEAT: | = W / . +KFEAT: d = W / . +KFEAT: e = W / . +KFEAT: 2 = W SUBST: ; = .:80 W:30 SUBST: " = .:80 W:30 SUBST: ! = w:20 W ? c diff --git a/crawl-ref/source/dat/orc.des b/crawl-ref/source/dat/orc.des index b541934960..f170b12959 100644 --- a/crawl-ref/source/dat/orc.des +++ b/crawl-ref/source/dat/orc.des @@ -148,7 +148,7 @@ ENDMAP NAME: david_orc_5 TAGS: orc_entry ORIENT: float -KFEAT: C = altar of Beogh w:70 / altar of Yredelemnul / altar of Makhleb / altar of Trog +KFEAT: C = altar_beogh w:70 / altar_yredelemnul / altar_makhleb / altar_trog SHUFFLE: EFGH SUBST: E=3 SUBST: F=1, G=1, H=1 @@ -478,7 +478,7 @@ ENDMAP ################################### # Beogh 1 NAME: mines1_david -KFEAT: C = altar of Beogh +KFEAT: C = altar_beogh MAP ........... .xxxx.xxxx. @@ -496,7 +496,7 @@ ENDMAP ################################### # Beogh 2 NAME: mines2_david -KFEAT: C = altar of Beogh +KFEAT: C = altar_beogh MONS: orc / orc warrior w:1 SUBST: . = . 1:2 MAP diff --git a/crawl-ref/source/dat/temple.des b/crawl-ref/source/dat/temple.des index e958377c35..a64b684bde 100644 --- a/crawl-ref/source/dat/temple.des +++ b/crawl-ref/source/dat/temple.des @@ -458,18 +458,18 @@ ORIENT: encompass SHUFFLE: ABC/DEF/GHI/JKL/MNO SHUFFLE: ABC, DEF, GHI, JKL, MNO SUBST: A=[, B=(, C={ -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 SUBST: X : GUT MAP xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/crawl-ref/source/dgnevent.h b/crawl-ref/source/dgnevent.h index 9b22d8c477..efc5cdd59a 100644 --- a/crawl-ref/source/dgnevent.h +++ b/crawl-ref/source/dgnevent.h @@ -12,6 +12,7 @@ #include "externs.h" #include +// Keep event names in luadgn.cc in sync. enum dgn_event_type { DET_NONE = 0x0000, @@ -20,7 +21,9 @@ enum dgn_event_type DET_MONSTER_MOVED = 0x0002, DET_PLAYER_MOVED = 0x0004, DET_LEAVING_LEVEL = 0x0008, - DET_ENTERING_LEVEL = 0x0010 + DET_ENTERING_LEVEL = 0x0010, + DET_PLAYER_IN_LOS = 0x0020, // Player just entered LOS. + DET_PLAYER_CLIMBS = 0x0040 // Player climbing stairs. }; class dgn_event diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 21ffebe163..675e214549 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -1211,7 +1211,20 @@ std::string feature_description(dungeon_feature_type grid, if (add_stop) desc += "."; if (dtype == DESC_PLAIN || (!grid_is_trap(grid) && isupper(desc[0]))) + { + if (isupper(desc[0])) + { + switch (dtype) + { + case DESC_PLAIN: case DESC_NOCAP_THE: case DESC_NOCAP_A: + desc[0] = tolower(desc[0]); + break; + default: + break; + } + } return (desc); + } switch (dtype) { @@ -1465,10 +1478,7 @@ std::string feature_description(int mx, int my, description_level_type dtype, case DNGN_ENTER_SHOP: return (shop_name(mx, my, add_stop)); default: - return (feature_description( - grid, NUM_TRAPS, - env_find_marker(coord_def(mx, my), MAT_TIMED_FEATURE), - dtype, add_stop)); + return (feature_description(grid, NUM_TRAPS, false, dtype, add_stop)); } } @@ -1703,7 +1713,12 @@ static void describe_cell(int mx, int my) #ifdef DEBUG_DIAGNOSTICS std::string marker; if (map_marker *mark = env_find_marker(coord_def(mx, my), MAT_ANY)) - marker = " (" + mark->describe() + ")"; + { + std::string desc = mark->describe(); + if (desc.empty()) + desc = "?"; + marker = " (" + desc + ")"; + } const std::string traveldest = stair_destination_description(coord_def(mx, my)); mprf("(%d,%d): %s - %s%s%s", mx, my, diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index 7919634957..5a0f9e20b4 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -4433,6 +4433,7 @@ static void place_shops(int level_number, int nshops) { place_specific_stair(DNGN_ENTER_BAZAAR, "bzr_entry", level_number, true); + allow_bazaars = false; } else { diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index ceb0f5c0bc..ade4bf5014 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -1027,6 +1027,7 @@ public: public: bool has_action_energy() const; void check_redraw(const coord_def &oldpos) const; + void apply_location_effects(); kill_category kill_alignment() const; diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index 2b64064856..e7e6762ec2 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -931,6 +931,7 @@ bool load( dungeon_feature_type stair_taken, load_mode_type load_mode, sanity_test_monster_inventory(); + dungeon_events.fire_event(DET_ENTERING_LEVEL); // Things to update for player entering level if (load_mode == LOAD_ENTER_LEVEL) { diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc index e1cca6a43e..845f96b380 100644 --- a/crawl-ref/source/libutil.cc +++ b/crawl-ref/source/libutil.cc @@ -46,6 +46,33 @@ #include #endif +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; +} + // Should return true if the filename contains nothing that // the shell can do damage with. bool shell_safe(const char *file) diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h index 9af70c60ad..183f6fac2f 100644 --- a/crawl-ref/source/libutil.h +++ b/crawl-ref/source/libutil.h @@ -15,12 +15,15 @@ #include "AppHdr.h" #include "defines.h" +#include "enum.h" #include #include #include extern const char *standard_plural_qualifiers[]; +description_level_type description_type_by_name(const char *desc); + std::string lowercase_string(std::string s); std::string &lowercase(std::string &s); std::string &uppercase(std::string &s); diff --git a/crawl-ref/source/luadgn.cc b/crawl-ref/source/luadgn.cc index 2cb194a44e..041b0460ad 100644 --- a/crawl-ref/source/luadgn.cc +++ b/crawl-ref/source/luadgn.cc @@ -7,12 +7,14 @@ #include "AppHdr.h" #include "clua.h" +#include "direct.h" #include "files.h" #include "luadgn.h" #include "mapdef.h" #include "mapmark.h" #include "maps.h" #include "stuff.h" +#include "tags.h" #include "dungeon.h" #include @@ -41,6 +43,14 @@ int dlua_stringtable(lua_State *ls, const std::vector &s) return dlua_gentable(ls, s, dlua_pushcxxstring); } +static int dlua_compiled_chunk_writer(lua_State *ls, const void *p, + size_t sz, void *ud) +{ + std::ostringstream &out = *static_cast(ud); + out.write((const char *) p, sz); + return (0); +} + /////////////////////////////////////////////////////////////////////////// // dlua_chunk @@ -51,6 +61,31 @@ dlua_chunk::dlua_chunk(const std::string &_context) clear(); } +// 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); +} + void dlua_chunk::write(FILE *outf) const { if (empty()) @@ -132,14 +167,6 @@ int dlua_chunk::check_op(CLua &interp, int err) return (err); } -static int dlua_compiled_chunk_writer(lua_State *ls, const void *p, - size_t sz, void *ud) -{ - std::ostringstream &out = *static_cast(ud); - out.write((const char *) p, sz); - return (0); -} - int dlua_chunk::load(CLua &interp) { if (!compiled.empty()) @@ -263,6 +290,10 @@ std::string dlua_chunk::get_chunk_prefix(const std::string &sorig) const #define MAP(ls, n, var) \ map_def *var = *(map_def **) luaL_checkudata(ls, n, MAP_METATABLE) +#define DEVENT(ls, n, var) \ + dgn_event *var = *(dgn_event **) luaL_checkudata(ls, n, DEVENT_METATABLE) +#define MAPMARKER(ls, n, var) \ + map_marker *var = *(map_marker **) luaL_checkudata(ls, n, MAPMARK_METATABLE) void dgn_reset_default_depth() { @@ -859,6 +890,123 @@ static int dgn_feature_name(lua_State *ls) dungeon_feature_name(static_cast(feat))); } +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(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(mark); + coord_def pos; + // Was a position supplied? + if (lua_gettop(ls) == 4) + { + pos.x = luaL_checkint(ls, 3); + pos.y = luaL_checkint(ls, 4); + } + + dungeon_events.register_listener(mask, listener, pos); + return (0); +} + +static int dgn_remove_listener(lua_State *ls) +{ + MAPMARKER(ls, 1, mark); + map_lua_marker *listener = dynamic_cast(mark); + coord_def pos; + // Was a position supplied? + if (lua_gettop(ls) == 3) + { + pos.x = luaL_checkint(ls, 2); + pos.y = luaL_checkint(ls, 3); + } + dungeon_events.remove_listener(listener, pos); + return (0); +} + +static int dgn_remove_marker(lua_State *ls) +{ + MAPMARKER(ls, 1, mark); + env_remove_marker(mark); + return (0); +} + +static int dgn_feature_desc(lua_State *ls) +{ + const dungeon_feature_type feat = + static_cast(luaL_checkint(ls, 1)); + const description_level_type dtype = + lua_isnumber(ls, 2)? + static_cast(luaL_checkint(ls, 2)) : + description_type_by_name(lua_tostring(ls, 2)); + const bool need_stop = lua_isboolean(ls, 3)? lua_toboolean(ls, 3) : false; + const std::string s = + feature_description(feat, NUM_TRAPS, false, dtype, need_stop); + lua_pushstring(ls, s.c_str()); + return (1); +} + +static int dgn_terrain_changed(lua_State *ls) +{ + dungeon_feature_type type = DNGN_UNSEEN; + if (lua_isnumber(ls, 3)) + type = static_cast(luaL_checkint(ls, 3)); + else if (lua_isstring(ls, 3)) + type = dungeon_feature_by_name(lua_tostring(ls, 3)); + const bool affect_player = + lua_isboolean(ls, 4)? lua_toboolean(ls, 4) : true; + dungeon_terrain_changed( coord_def( luaL_checkint(ls, 1), + luaL_checkint(ls, 2) ), + type, affect_player ); + return (0); +} + static const struct luaL_reg dgn_lib[] = { { "default_depth", dgn_default_depth }, @@ -883,6 +1031,7 @@ static const struct luaL_reg dgn_lib[] = { "kitem", dgn_kitem }, { "kmons", dgn_kmons }, { "grid", dgn_grid }, + { "terrain_changed", dgn_terrain_changed }, { "points_connected", dgn_points_connected }, { "any_point_connected", dgn_any_point_connected }, { "has_exit_from", dgn_has_exit_from }, @@ -892,6 +1041,11 @@ static const struct luaL_reg dgn_lib[] = { "load_des_file", dgn_load_des_file }, { "feature_number", dgn_feature_number }, { "feature_name", dgn_feature_name }, + { "dgn_event_type", dgn_dgn_event }, + { "register_listener", dgn_register_listener }, + { "remove_listener", dgn_remove_listener }, + { "remove_marker", dgn_remove_marker }, + { "feature_desc", dgn_feature_desc }, { NULL, NULL } }; @@ -906,16 +1060,207 @@ static const struct luaL_reg crawl_lib[] = { NULL, NULL } }; +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( 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( 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( 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( 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( 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( lua_touserdata(ls, 1) )); + const lua_persist_type ptype = + static_cast(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 .__index = + 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); +} + void init_dungeon_lua() { + lua_stack_cleaner clean(dlua); + luaL_openlib(dlua, "dgn", dgn_lib, 0); // Add additional function to the Crawl module. luaL_openlib(dlua, "crawl", crawl_lib, 0); + luaL_openlib(dlua, "file", file_lib, 0); + luaL_openlib(dlua, "you", you_lib, 0); + dlua.execfile("clua/dungeon.lua", true, true); - if (!dlua.error.empty()) - end(1, false, "Lua error: %s", dlua.error.c_str()); + dlua.execfile("clua/luamark.lua", true, true); + lua_getglobal(dlua, "dgn_run_map"); luaopen_debug(dlua); luaL_newmetatable(dlua, MAP_METATABLE); - lua_settop(dlua, 1); + + luaopen_dgnevent(dlua); + luaopen_mapmarker(dlua); } diff --git a/crawl-ref/source/luadgn.h b/crawl-ref/source/luadgn.h index d717f38f5f..04e85c2de0 100644 --- a/crawl-ref/source/luadgn.h +++ b/crawl-ref/source/luadgn.h @@ -43,6 +43,10 @@ public: public: dlua_chunk(const std::string &_context = "dlua_chunk"); + dlua_chunk(lua_State *ls); + + static dlua_chunk precompiled(const std::string &compiled); + void clear(); void add(int line, const std::string &line); void set_chunk(const std::string &s); @@ -57,6 +61,8 @@ public: bool empty() const; + const std::string &compiled_chunk() const { return compiled; } + void write(FILE *) const; void read(FILE *); }; @@ -69,6 +75,13 @@ int dlua_stringtable(lua_State *ls, const std::vector &s); dungeon_feature_type dungeon_feature_by_name(const std::string &name); const char *dungeon_feature_name(dungeon_feature_type feat); +template +inline void dlua_push_userdata(lua_State *ls, T udata, const char *meta) +{ + T *de = clua_new_userdata(ls, meta); + *de = udata; +} + ////////////////////////////////////////////////////////////////////////// #endif diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc index a5854ae236..6e5e5c7c8d 100644 --- a/crawl-ref/source/mapdef.cc +++ b/crawl-ref/source/mapdef.cc @@ -302,9 +302,9 @@ map_transformer::~map_transformer() // map_lines map_lines::map_lines() - : 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) { } @@ -1023,9 +1023,9 @@ dlua_set_map::~dlua_set_map() map_def::map_def() : name(), tags(), place(), depths(), orient(), chance(), - 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) { init(); } @@ -1330,6 +1330,7 @@ std::string map_def::validate_map_def() break; } + dlua_set_map dl(this); return (map.apply_transforms()); } @@ -1574,6 +1575,7 @@ void map_def::normalise() std::string map_def::resolve() { + dlua_set_map dl(this); return map.apply_transforms(); } @@ -2306,27 +2308,27 @@ std::string shuffle_spec::describe() const std::string map_marker_spec::apply_transform(map_lines &map) { std::vector positions = map.find_glyph(key); - if (positions.size() == 1) + if (positions.empty()) + return make_stringf("cant find key '%c' for marker", key); + + for (int i = 0, size = positions.size(); i < size; ++i) { try { - map_marker *mark = map_marker::parse_marker(marker); + map_marker *mark = + map_marker::parse_marker(marker); if (!mark) return make_stringf("Unable to parse marker from %s", marker.c_str()); - mark->pos = positions[0]; + mark->pos = positions[i]; map.add_marker(mark); - return (""); } catch (const std::string &err) { return (err); } } - else if (positions.empty()) - return make_stringf("cant find key '%c' for marker", key); - else - return make_stringf("too many matches for key '%c' for marker", key); + return (""); } map_transformer::transform_type map_marker_spec::type() const @@ -2436,15 +2438,13 @@ feature_spec_list keyed_mapspec::parse_feature(const std::string &str) list.push_back( parse_shop(s, weight) ); return (list); } - - std::vector feats = - features_by_desc( glob_pattern(s, true) ); - if (!feats.empty()) - list.push_back( feature_spec(feats[0], weight) ); - if (feats.empty()) + const dungeon_feature_type ftype = dungeon_feature_by_name(s); + if (ftype == DNGN_UNSEEN) err = make_stringf("no features matching \"%s\"", str.c_str()); + else + list.push_back( feature_spec( ftype, weight ) ); return (list); } diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h index 9ff325bcca..d1a0316aad 100644 --- a/crawl-ref/source/mapdef.h +++ b/crawl-ref/source/mapdef.h @@ -167,6 +167,7 @@ struct map_marker_spec : public map_transformer map_transformer *clone() const; }; +class map_def; class map_lines { public: @@ -260,6 +261,7 @@ private: friend class subst_spec; friend class nsubst_spec; friend class shuffle_spec; + friend class map_marker_spec; private: std::vector transforms; diff --git a/crawl-ref/source/mapmark.cc b/crawl-ref/source/mapmark.cc index 696f042b4c..4867225072 100644 --- a/crawl-ref/source/mapmark.cc +++ b/crawl-ref/source/mapmark.cc @@ -9,6 +9,7 @@ #include "AppHdr.h" #include "mapmark.h" +#include "clua.h" #include "direct.h" #include "libutil.h" #include "luadgn.h" @@ -22,13 +23,13 @@ map_marker::marker_reader map_marker::readers[NUM_MAP_MARKER_TYPES] = { &map_feature_marker::read, - &map_timed_feature_marker::read, + &map_lua_marker::read, }; map_marker::marker_parser map_marker::parsers[NUM_MAP_MARKER_TYPES] = { &map_feature_marker::parse, - &map_timed_feature_marker::parse, + &map_lua_marker::parse, }; map_marker::map_marker(map_marker_type t, const coord_def &p) @@ -64,13 +65,14 @@ map_marker *map_marker::read_marker(tagHeader &inf) return readers[type]? (*readers[type])(inf, type) : NULL; } -map_marker *map_marker::parse_marker(const std::string &s) throw (std::string) +map_marker *map_marker::parse_marker( + const std::string &s, const std::string &ctx) throw (std::string) { for (int i = 0; i < NUM_MAP_MARKER_TYPES; ++i) { if (parsers[i]) { - if (map_marker *m = parsers[i](s)) + if (map_marker *m = parsers[i](s, ctx)) return (m); } } @@ -112,7 +114,8 @@ map_marker *map_feature_marker::read(tagHeader &inf, map_marker_type) return (mapf); } -map_marker *map_feature_marker::parse(const std::string &s) throw (std::string) +map_marker *map_feature_marker::parse( + const std::string &s, const std::string &) throw (std::string) { if (s.find("feat:") != 0) return (NULL); @@ -132,190 +135,195 @@ std::string map_feature_marker::describe() const } //////////////////////////////////////////////////////////////////////////// -// map_feature_marker +// map_lua_marker -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) { - type = MAT_TIMED_FEATURE; } -void map_timed_feature_marker::activate() +map_lua_marker::map_lua_marker(const std::string &s, const std::string &) + : 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(); +} - if (player_can_hear(pos)) +map_lua_marker::~map_lua_marker() +{ + // Remove the Lua marker table from the registry. + if (initialised) { - 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); } } -void map_timed_feature_marker::write(tagHeader &th) const +void map_lua_marker::check_register_table() { - map_feature_marker::write(th); - marshallShort(th, duration_ticks); - marshallShort(th, warn_threshold); -} + if (!lua_istable(dlua, -1)) + { + mprf(MSGCH_WARN, "lua_marker: Expected table, didn't get it."); + initialised = false; + return; + } -void map_timed_feature_marker::read(tagHeader &th) -{ - map_feature_marker::read(th); - duration_ticks = unmarshallShort(th); - warn_threshold = unmarshallShort(th); -} + // Got a table. Save it in the registry. -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; } -const char *map_timed_feature_marker::bell_urgency(int ticks) const +bool map_lua_marker::get_table() const { - 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(this)); + lua_gettable(dlua, LUA_REGISTRYINDEX); + return (lua_istable(dlua, -1)); } -const char *map_timed_feature_marker::noise_maker(int ticks) const +void map_lua_marker::write(tagHeader &th) const { - switch (grd(pos)) + map_marker::write(th); + + lua_stack_cleaner clean(dlua); + bool init = initialised; + if (!get_table()) { - 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(this)); + lua_pushlightuserdata(dlua, &th); + + if (!dlua.callfn("dlua_marker_method", 4)) + end(1, false, "lua_marker::write error: %s", dlua.error.c_str()); } -void map_timed_feature_marker::notify_dgn_event(const dgn_event &e) +void map_lua_marker::read(tagHeader &th) { - if (!e.elapsed_ticks || e.type != DET_TURN_ELAPSED) + map_marker::read(th); + + if (!(initialised = unmarshallByte(th))) return; - 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)); - - if (duration_ticks <= 0) - timeout(true); - } + 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()); + + // Right, what's on top had better be a table. + check_register_table(); } -void map_timed_feature_marker::timeout(bool verbose) +map_marker *map_lua_marker::read(tagHeader &th, map_marker_type) { - 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); +} - // And it's gone forever. - grd(pos) = feat; +void map_lua_marker::push_fn_args(const char *fn) const +{ + get_table(); + lua_pushstring(dlua, fn); + dlua_push_userdata(dlua, this, MAPMARK_METATABLE); +} - 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); +} - // Stop listening for further ticks. - dungeon_events.remove_listener(this); +void map_lua_marker::activate() +{ + lua_stack_cleaner clean(dlua); + callfn("activate", true); +} - // 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 *map_timed_feature_marker::read(tagHeader &th, map_marker_type) +std::string map_lua_marker::describe() const { - 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; } -map_marker *map_timed_feature_marker::parse(const std::string &s) - throw (std::string) +map_marker *map_lua_marker::parse( + const std::string &s, const std::string &ctx) throw (std::string) { - if (s.find("timer:") != 0) + if (s.find("lua:") != 0) return (NULL); std::string raw = s; - 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) { - throw make_stringf("Bad feature name (%s) in marker spec '%s'", - fname.c_str(), s.c_str()); + delete mark; + throw make_stringf("Unable to initialise Lua marker from '%s'", + raw.c_str()); } - - std::vector 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); } ////////////////////////////////////////////////////////////////////////// diff --git a/crawl-ref/source/mapmark.h b/crawl-ref/source/mapmark.h index 31d0b74e29..eacddcf266 100644 --- a/crawl-ref/source/mapmark.h +++ b/crawl-ref/source/mapmark.h @@ -3,6 +3,8 @@ #include "dungeon.h" #include "dgnevent.h" +#include "clua.h" +#include "luadgn.h" ////////////////////////////////////////////////////////////////////////// // Map markers @@ -11,7 +13,7 @@ enum map_marker_type { MAT_FEATURE, // Stock marker. - MAT_TIMED_FEATURE, + MAT_LUA_MARKER, NUM_MAP_MARKER_TYPES, MAT_ANY }; @@ -30,7 +32,8 @@ public: virtual std::string describe() const = 0; static map_marker *read_marker(tagHeader&); - static map_marker *parse_marker(const std::string &text) + static map_marker *parse_marker(const std::string &text, + const std::string &ctx = "") throw (std::string); public: @@ -40,7 +43,8 @@ protected: map_marker_type type; typedef map_marker *(*marker_reader)(tagHeader &, map_marker_type); - typedef map_marker *(*marker_parser)(const std::string &); + typedef map_marker *(*marker_parser)(const std::string &, + const std::string &); static marker_reader readers[NUM_MAP_MARKER_TYPES]; static marker_parser parsers[NUM_MAP_MARKER_TYPES]; }; @@ -55,39 +59,39 @@ public: void read(tagHeader &); std::string describe() const; static map_marker *read(tagHeader &, map_marker_type); - static map_marker *parse(const std::string &s) throw (std::string); + static map_marker *parse(const std::string &s, const std::string &) + throw (std::string); public: dungeon_feature_type feat; }; -class map_timed_feature_marker : public map_feature_marker, dgn_event_listener +// A marker powered by Lua. +class map_lua_marker : public map_marker, public dgn_event_listener { public: - map_timed_feature_marker(const coord_def &pos = coord_def(), - int duration_turns = 0, - dungeon_feature_type feat = DNGN_FLOOR); + map_lua_marker(); + map_lua_marker(const std::string &s, const std::string &ctx); + ~map_lua_marker(); + void activate(); + void write(tagHeader &) const; void read(tagHeader &); std::string describe() const; - + void notify_dgn_event(const dgn_event &e); - - // Expires this marker *now* and cleans it up. - void timeout(bool verbose); - + static map_marker *read(tagHeader &, map_marker_type); - static map_marker *parse(const std::string &s) throw (std::string); - + static map_marker *parse(const std::string &s, const std::string &) + throw (std::string); private: - 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; }; void env_activate_markers(); diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index 9789881fbb..bef5843dca 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -372,6 +372,12 @@ void mprf( const char *format, ... ) void mpr(const char *inf, int channel, int param) { + if (!crawl_state.io_inited) + { + if (channel == MSGCH_WARN) + fprintf(stderr, "%s\n", inf); + return; + } char mbuf[400]; size_t i = 0; const int stepsize = get_number_of_cols() - 1; diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 94bf165bf4..f94624ff39 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -535,10 +535,20 @@ static void dgn_check_terrain_monsters(const coord_def &pos) } } -void dungeon_terrain_changed(const coord_def &pos) +void dungeon_terrain_changed(const coord_def &pos, + dungeon_feature_type nfeat, + bool affect_player) { + if (nfeat != DNGN_UNSEEN) + { + unnotice_feature(level_pos(level_id::current(), pos)); + grd(pos) = nfeat; + if (is_notable_terrain(nfeat) && see_grid(pos)) + seen_notable_thing(nfeat, pos.x, pos.y); + } + dgn_check_terrain_items(pos); - if (pos == you.pos()) + if (affect_player && pos == you.pos()) { if (!grid_is_solid(grd(pos))) { @@ -776,6 +786,12 @@ static void climb_message(dungeon_feature_type stair, bool going_up) mpr(going_up? "You climb upwards." : "You climb downwards."); } +static void leaving_level_now() +{ + dungeon_events.fire_position_event(DET_PLAYER_CLIMBS, you.pos()); + dungeon_events.fire_event(DET_LEAVING_LEVEL); +} + void up_stairs(void) { dungeon_feature_type stair_find = grd[you.x_pos][you.y_pos]; @@ -828,6 +844,7 @@ void up_stairs(void) } // Checks are done, the character is committed to moving between levels. + leaving_level_now(); exit_stair_message(stair_find, true); int old_level = you.your_level; @@ -1028,6 +1045,9 @@ void down_stairs( int old_level, dungeon_feature_type force_stair ) } // All checks are done, the player is on the move now. + + // Fire level-leaving trigger. + leaving_level_now(); exit_stair_message(stair_find, false); #ifdef DGL_MILESTONES @@ -1132,16 +1152,8 @@ void down_stairs( int old_level, dungeon_feature_type force_stair ) } } - 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(marker)->timeout(false); - } + if (stair_find == DNGN_ENTER_LABYRINTH) + dungeon_terrain_changed(you.pos(), DNGN_FLOOR); if (stair_find == DNGN_ENTER_LABYRINTH) { diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 37477745fd..7bbfd4a602 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -171,7 +171,9 @@ void setup_environment_effects(); void run_environment_effects(); // Terrain changed under 'pos', perform necessary effects. -void dungeon_terrain_changed(const coord_def &pos); +void dungeon_terrain_changed(const coord_def &pos, + dungeon_feature_type feat = DNGN_UNSEEN, + bool affect_player = true); ////////////////////////////////////////////////////////////////////// // Places and names diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 1dc3d462fd..15194b25b2 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -33,6 +33,7 @@ #include "beam.h" #include "debug.h" #include "delay.h" +#include "dgnevent.h" #include "itemname.h" #include "itemprop.h" #include "items.h" @@ -4225,6 +4226,17 @@ void monsters::check_redraw(const coord_def &old) const } } +void monsters::apply_location_effects() +{ + dungeon_events.fire_position_event(DET_MONSTER_MOVED, pos()); + + // monsters stepping on traps: + mons_trap(this); + + if (alive()) + mons_check_pool(this); +} + ///////////////////////////////////////////////////////////////////////// // mon_enchant diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index f7b3f495fc..13ef4ef37f 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -1189,6 +1189,7 @@ bool monster_blink(monsters *monster) mgrd[monster->x][monster->y] = NON_MONSTER; + const coord_def oldplace = monster->pos(); monster->x = nx; monster->y = ny; @@ -1197,6 +1198,9 @@ bool monster_blink(monsters *monster) if (player_monster_visible(monster) && mons_near(monster)) seen_monster(monster); + monster->check_redraw(oldplace); + monster->apply_location_effects(); + return (true); } // end monster_blink() @@ -4131,9 +4135,8 @@ static bool monster_swaps_places( monsters *mon, int mx, int my ) immobile_monster[m2i] = true; mon->check_redraw(coord_def(cx, cy)); - - mons_trap(mon); - mons_trap(m2); + mon->apply_location_effects(); + m2->apply_location_effects(); return (false); } @@ -4186,12 +4189,7 @@ static void do_move_monster(monsters *monster, int xi, int yi) mgrd[monster->x][monster->y] = monster_index(monster); monster->check_redraw(monster->pos() - coord_def(xi, yi)); - - // monsters stepping on traps: - mons_trap(monster); - - if (monster->alive()) - mons_check_pool(monster); + monster->apply_location_effects(); } void mons_check_pool(monsters *mons, killer_type killer, int killnum) diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 5612953152..2a48a3fc35 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -835,8 +835,9 @@ void monster_teleport(struct monsters *monster, bool instan, bool silent) if (!silent) simple_monster_message(monster, " disappears!"); + const coord_def oldplace = monster->pos(); // pick the monster up - mgrd[monster->x][monster->y] = NON_MONSTER; + mgrd(oldplace) = NON_MONSTER; int newx, newy; while(true) @@ -880,6 +881,9 @@ void monster_teleport(struct monsters *monster, bool instan, bool silent) if (player_monster_visible(monster) && now_visible) seen_monster(monster); + monster->check_redraw(oldplace); + monster->apply_location_effects(); + // Teleporting mimics change form - if they reappear out of LOS, they are // no longer known. if (mons_is_mimic(monster->type)) diff --git a/crawl-ref/source/overmap.cc b/crawl-ref/source/overmap.cc index 1fa8d5ddee..078e3f3415 100644 --- a/crawl-ref/source/overmap.cc +++ b/crawl-ref/source/overmap.cc @@ -26,6 +26,7 @@ #include "externs.h" #include "branch.h" +#include "dgnevent.h" #include "direct.h" #include "files.h" #include "menu.h" @@ -46,11 +47,16 @@ altar_map_type altars_present; portal_map_type portals_present; static void seen_altar( god_type god, const coord_def& pos ); -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); void seen_notable_thing( dungeon_feature_type which_thing, int x, int y ) { + // Tell the world first. + dungeon_events.fire_position_event(DET_PLAYER_IN_LOS, coord_def(x, y)); + // Don't record in temporary terrain if (you.level_type != LEVEL_DUNGEON) return; @@ -348,16 +354,55 @@ std::string overview_description_string() return disp; } -void unnotice_labyrinth_portal() +template +inline static bool find_erase(Z &map, const Key &k) { - 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(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)); } void display_overmap() @@ -369,7 +414,8 @@ void display_overmap() redraw_screen(); } -void seen_staircase( unsigned char which_staircase, const coord_def& pos ) +void seen_staircase( dungeon_feature_type which_staircase, + const coord_def& pos ) { // which_staircase holds the grid value of the stair, must be converted // Only handles stairs, not gates or arches @@ -427,7 +473,7 @@ portal_type feature_to_portal( unsigned char feat ) } // if player has seen any other thing; record it -void seen_other_thing( unsigned char which_thing, const coord_def& pos ) +void seen_other_thing( dungeon_feature_type which_thing, const coord_def& pos ) { if ( you.level_type != LEVEL_DUNGEON ) // can't record in abyss or pan. return; diff --git a/crawl-ref/source/overmap.h b/crawl-ref/source/overmap.h index 214073cecf..f9f16a71ad 100644 --- a/crawl-ref/source/overmap.h +++ b/crawl-ref/source/overmap.h @@ -18,8 +18,7 @@ void seen_notable_thing( dungeon_feature_type which_thing, int x, int y ); bool overmap_knows_portal(dungeon_feature_type portal); void display_overmap(); -void unnotice_labyrinth_portal(); -void unnotice_altar(); +bool unnotice_feature(const level_pos &pos); std::string overview_description_string(); #endif diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index c3458c2eb7..1c6d08afe3 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -34,6 +34,7 @@ #include "clua.h" #include "delay.h" +#include "dgnevent.h" #include "effects.h" #include "fight.h" #include "food.h" @@ -5523,14 +5524,16 @@ bool player::is_icy() const void player::moveto(int x, int y) { - x_pos = x; - y_pos = y; - crawl_view.set_player_at(coord_def(x, y)); + moveto(coord_def(x, y)); } void player::moveto(const coord_def &c) { + const bool real_move = c != pos(); x_pos = c.x; y_pos = c.y; crawl_view.set_player_at(c); + + if (real_move) + dungeon_events.fire_position_event(DET_PLAYER_MOVED, c); } diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index 5748975294..cc0e1648d9 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -374,7 +374,7 @@ void marshallString(struct tagHeader &th, const std::string &data, int maxSize) } // string -- unmarshall length & string data -void unmarshallCString(struct tagHeader &th, char *data, int maxSize) +int unmarshallCString(struct tagHeader &th, char *data, int maxSize) { // get length short len = unmarshallShort(th); @@ -388,6 +388,8 @@ void unmarshallCString(struct tagHeader &th, char *data, int maxSize) data[copylen] = 0; th.offset += len; + + return (copylen); } std::string unmarshallString(tagHeader &th, int maxSize) @@ -398,10 +400,9 @@ std::string unmarshallString(tagHeader &th, int maxSize) if (!buffer) return (""); *buffer = 0; - unmarshallCString(th, buffer, maxSize); - const std::string res = buffer; + const int slen = unmarshallCString(th, buffer, maxSize); + const std::string res(buffer, slen); delete [] buffer; - return (res); } diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h index ebc61c6d4d..f07ada57ab 100644 --- a/crawl-ref/source/tags.h +++ b/crawl-ref/source/tags.h @@ -52,7 +52,7 @@ short unmarshallShort(struct tagHeader &th); long unmarshallLong(struct tagHeader &th); float unmarshallFloat(struct tagHeader &th); bool unmarshallBoolean(struct tagHeader &th); -void unmarshallCString(struct tagHeader &th, char *data, int maxSize); +int unmarshallCString(struct tagHeader &th, char *data, int maxSize); std::string unmarshallString(tagHeader &th, int maxSize = 1000); void unmarshallCoord(tagHeader &th, coord_def &c); diff --git a/crawl-ref/source/util/levcomp.lpp b/crawl-ref/source/util/levcomp.lpp index 6ec10124ad..e234f90b2d 100644 --- a/crawl-ref/source/util/levcomp.lpp +++ b/crawl-ref/source/util/levcomp.lpp @@ -39,14 +39,12 @@ static void clean() alloced = false; } -static void settext(bool trim_right = false, int strip_trailing = 0) +static char *copy_text(bool trim_right, int strip_trailing) { - clean(); char *newstring = NULL; if ((yylval.text = newstring = strdup(yytext))) { alloced = true; - char *s = NULL; if (trim_right) { @@ -62,6 +60,14 @@ static void settext(bool trim_right = false, int strip_trailing = 0) *s-- = 0; } } + return (newstring); +} + +static void settext(bool trim_right = false, int strip_trailing = 0) +{ + clean(); + char *newstring = copy_text(trim_right, strip_trailing); + yylval.text = newstring; } %} @@ -73,6 +79,7 @@ static void settext(bool trim_right = false, int strip_trailing = 0) %s MNAME %s KEYWORDS %x ITEM_LIST +%x TOEOL %option yylineno %option never-interactive @@ -152,6 +159,17 @@ NSPACE [^\ \t\r\n] \r?\n { BEGIN(INITIAL); } +.*\\[ \t]*$ { + settext(true, 1); + return STRING; + } + +.+$ { + BEGIN(INITIAL); + settext(); + return STRING; + } + ^[ \t]*#.* ; ^\s*MAP[ \t]*$ { BEGIN(MAPDEF); } @@ -178,7 +196,7 @@ SUBST: { BEGIN(ITEM_LIST); return SUBST; } NSUBST: { BEGIN(ITEM_LIST); return NSUBST; } MONS: { BEGIN(MNAME); return MONS; } ITEM: { BEGIN(ITEM_LIST); return ITEM; } -MARKER: { BEGIN(ITEM_LIST); return MARKER; } +MARKER: { BEGIN(TOEOL); return MARKER; } SHUFFLE: { BEGIN(ITEM_LIST); return SHUFFLE; } KFEAT: { BEGIN(ARGUMENT); return KFEAT; } diff --git a/crawl-ref/source/util/levcomp.ypp b/crawl-ref/source/util/levcomp.ypp index 0da88b1ce1..10b74dcc04 100644 --- a/crawl-ref/source/util/levcomp.ypp +++ b/crawl-ref/source/util/levcomp.ypp @@ -15,6 +15,8 @@ int yylex(); extern int yylineno; +static bool start_marker_segment = false; + void yyerror(const char *e) { if (strstr(e, lc_desfile.c_str()) == e) @@ -280,19 +282,32 @@ tagstring : STRING } ; -marker : MARKER marker_specs { } +marker : MARKER + { + lc_map.main.add(yylineno, "marker("); + start_marker_segment = true; + } + marker_spec + { + lc_map.main.add(yylineno, ")"); + } ; -marker_specs : marker_spec { } - | marker_specs COMMA marker_spec { } +marker_spec : mspec_segments + +mspec_segments : /* nothing */ + | mspec_segments mspec_segment ; -marker_spec : ITEM_INFO +mspec_segment : STRING { lc_map.main.add( - yylineno, - make_stringf("marker(\"%s\")", + yylineno, + make_stringf( + "%s\"%s\"", + start_marker_segment? "" : " .. ", quote_lua_string($1).c_str())); + start_marker_segment = false; } ; diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index 85e780d42d..67e9274d22 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -232,6 +232,11 @@ void clear_envmap_grid( int x, int y ) env.map[x][y].clear(); } +bool is_notable_terrain(dungeon_feature_type ftype) +{ + return Feature[ftype].notable; +} + #if defined(WIN32CONSOLE) || defined(DOS) static unsigned colflag2brand(int colflag) { diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h index 19f0cedbd5..a5357ce218 100644 --- a/crawl-ref/source/view.h +++ b/crawl-ref/source/view.h @@ -124,6 +124,7 @@ bool is_terrain_known( int x, int y ); bool is_terrain_seen( int x, int y ); bool is_terrain_changed( int x, int y ); bool is_terrain_known(const coord_def &p); +bool is_notable_terrain(dungeon_feature_type ftype); inline bool is_terrain_seen(const coord_def &c) { -- cgit v1.2.3-54-g00ecf