summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarshan Shaligram <dshaligram@users.sourceforge.net>2009-10-30 15:46:55 +0530
committerDarshan Shaligram <dshaligram@users.sourceforge.net>2009-10-31 22:16:46 +0530
commitad11744e5d49d226fc9c77ed48c3dd8b97921350 (patch)
treec4334fd1e6b4b052ac93593f9021f12c9f10baac
parent23f8631266eac1a7b4b4727b26a5fc088a6f0184 (diff)
downloadcrawl-ref-ad11744e5d49d226fc9c77ed48c3dd8b97921350.tar.gz
crawl-ref-ad11744e5d49d226fc9c77ed48c3dd8b97921350.zip
lmark.synchronized_markers(): apply one marker's effects to multiple points simultaneously.
synchronized_markers() takes a marker, and a list of method names to override, and returns a set of markers that fire simultaneously, to allow, say, fog machines that fire at random intervals, but always fire in unison. lm_mslav.lua has details. volcano.des has an example volcano entry vault that uses this. Allow fetching a list of Lua markers/marker positions by property value. Renamed dgn.find_marker_prop -> dgn.find_marker_position_by_prop. s/helper/listener/ for fog machine listeners.
-rw-r--r--crawl-ref/source/clua.cc50
-rw-r--r--crawl-ref/source/clua.h2
-rw-r--r--crawl-ref/source/cluautil.cc25
-rw-r--r--crawl-ref/source/cluautil.h18
-rw-r--r--crawl-ref/source/dat/clua/lm_fog.lua172
-rw-r--r--crawl-ref/source/dat/clua/lm_func.lua33
-rw-r--r--crawl-ref/source/dat/clua/lm_mslav.lua157
-rw-r--r--crawl-ref/source/dat/clua/luamark.lua3
-rw-r--r--crawl-ref/source/dat/clua/point.lua4
-rw-r--r--crawl-ref/source/dat/clua/util.lua8
-rw-r--r--crawl-ref/source/dat/clua/ziggurat.lua2
-rw-r--r--crawl-ref/source/dat/volcano.des51
-rw-r--r--crawl-ref/source/l_crawl.cc2
-rw-r--r--crawl-ref/source/l_dgn.cc40
-rw-r--r--crawl-ref/source/mapmark.cc53
-rw-r--r--crawl-ref/source/mapmark.h13
16 files changed, 506 insertions, 127 deletions
diff --git a/crawl-ref/source/clua.cc b/crawl-ref/source/clua.cc
index d9273b74c9..64fd63b768 100644
--- a/crawl-ref/source/clua.cc
+++ b/crawl-ref/source/clua.cc
@@ -12,6 +12,7 @@
#include "l_libs.h"
#include "files.h"
+#include "libutil.h"
#include "state.h"
#include "stuff.h"
@@ -275,7 +276,7 @@ bool CLua::runhook(const char *hook, const char *params, ...)
// Remember top of stack, for debugging porpoises
int stack_top = lua_gettop(ls);
- lua_getglobal(ls, hook);
+ pushglobal(hook);
if (!lua_istable(ls, -1))
{
lua_pop(ls, 1);
@@ -475,7 +476,7 @@ bool CLua::callbooleanfn(bool def, const char *fn, const char *params, ...)
int stacktop = lua_gettop(ls);
- lua_getglobal(ls, fn);
+ pushglobal(fn);
if (!lua_isfunction(ls, -1))
{
lua_pop(ls, 1);
@@ -497,6 +498,46 @@ bool CLua::proc_returns(const char *par) const
return (strchr(par, '>') != NULL);
}
+// Identical to lua_getglobal for simple names, but will look up
+// "a.b.c" names in tables, so you can pushglobal("dgn.point") and get
+// _G['dgn']['point'], as expected.
+//
+// Guarantees to push exactly one value onto the stack.
+//
+void CLua::pushglobal(const std::string &name)
+{
+ std::vector<std::string> pieces = split_string(".", name);
+ lua_State *ls(state());
+
+ if (pieces.empty())
+ lua_pushnil(ls);
+
+ for (unsigned i = 0, size = pieces.size(); i < size; ++i)
+ {
+ if (!i)
+ lua_getglobal(ls, pieces[i].c_str());
+ else
+ {
+ if (lua_istable(ls, -1))
+ {
+ lua_pushstring(ls, pieces[i].c_str());
+ lua_gettable(ls, -2);
+ // Swap the value we just found with the table itself.
+ lua_insert(ls, -2);
+ // And remove the table.
+ lua_pop(ls, 1);
+ }
+ else
+ {
+ // We expected a table here, but got something else. Fail.
+ lua_pop(ls, 1);
+ lua_pushnil(ls);
+ break;
+ }
+ }
+ }
+}
+
bool CLua::callfn(const char *fn, const char *params, ...)
{
error.clear();
@@ -504,7 +545,7 @@ bool CLua::callfn(const char *fn, const char *params, ...)
if (!ls)
return (false);
- lua_getglobal(ls, fn);
+ pushglobal(fn);
if (!lua_isfunction(ls, -1))
{
lua_pop(ls, 1);
@@ -536,7 +577,7 @@ bool CLua::callfn(const char *fn, int nargs, int nret)
// If a function is not provided on the stack, get the named function.
if (fn)
{
- lua_getglobal(ls, fn);
+ pushglobal(fn);
if (!lua_isfunction(ls, -1))
{
lua_settop(ls, -nargs - 2);
@@ -1139,4 +1180,3 @@ bool lua_datum::is_udata() const
{
LUA_CHECK_TYPE(lua_isuserdata);
}
-
diff --git a/crawl-ref/source/clua.h b/crawl-ref/source/clua.h
index a72d56159b..1bbf70c720 100644
--- a/crawl-ref/source/clua.h
+++ b/crawl-ref/source/clua.h
@@ -126,6 +126,8 @@ public:
int execfile(const char *filename, bool trusted = false,
bool die_on_fail = false);
+ void pushglobal(const std::string &name);
+
bool callbooleanfn(bool defval, const char *fn, const char *params, ...);
bool callfn(const char *fn, int nargs, int nret = 1);
bool callfn(const char *fn, const char *params, ...);
diff --git a/crawl-ref/source/cluautil.cc b/crawl-ref/source/cluautil.cc
index e55b836f3e..a9cb1cec7b 100644
--- a/crawl-ref/source/cluautil.cc
+++ b/crawl-ref/source/cluautil.cc
@@ -99,25 +99,24 @@ void clua_register_metatable(lua_State *ls, const char *tn,
}
}
-
-template <typename list, typename lpush>
-static int dlua_gentable(lua_State *ls, const list &strings, lpush push)
+int clua_pushcxxstring(lua_State *ls, const std::string &s)
{
- lua_newtable(ls);
- for (int i = 0, size = strings.size(); i < size; ++i)
- {
- push(ls, strings[i]);
- lua_rawseti(ls, -2, i + 1);
- }
+ lua_pushstring(ls, s.c_str());
return (1);
}
-inline static void dlua_pushcxxstring(lua_State *ls, const std::string &s)
+int clua_stringtable(lua_State *ls, const std::vector<std::string> &s)
{
- lua_pushstring(ls, s.c_str());
+ return clua_gentable(ls, s, clua_pushcxxstring);
}
-int dlua_stringtable(lua_State *ls, const std::vector<std::string> &s)
+int clua_pushpoint(lua_State *ls, const coord_def &pos)
{
- return dlua_gentable(ls, s, dlua_pushcxxstring);
+ lua_pushnumber(ls, pos.x);
+ lua_pushnumber(ls, pos.y);
+ CLua &vm(CLua::get_vm(ls));
+ if (!vm.callfn("dgn.point", 2, 1))
+ luaL_error(ls, "dgn.point(%d,%d) failed: %s",
+ pos.x, pos.y, vm.error.c_str());
+ return (1);
}
diff --git a/crawl-ref/source/cluautil.h b/crawl-ref/source/cluautil.h
index 8ea98028ee..0f02555750 100644
--- a/crawl-ref/source/cluautil.h
+++ b/crawl-ref/source/cluautil.h
@@ -57,7 +57,7 @@ void clua_register_metatable(lua_State *ls, const char *tn,
const luaL_reg *lr,
int (*gcfn)(lua_State *ls) = NULL);
-int dlua_stringtable(lua_State *ls, const std::vector<std::string> &s);
+int clua_stringtable(lua_State *ls, const std::vector<std::string> &s);
/*
* User-data templates.
@@ -175,4 +175,20 @@ 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)
+
+template <typename list, typename lpush>
+static int clua_gentable(lua_State *ls, const list &strings, lpush push)
+{
+ lua_newtable(ls);
+ for (int i = 0, size = strings.size(); i < size; ++i)
+ {
+ push(ls, strings[i]);
+ lua_rawseti(ls, -2, i + 1);
+ }
+ return (1);
+}
+
+int clua_pushcxxstring(lua_State *ls, const std::string &s);
+int clua_pushpoint(lua_State *ls, const coord_def &pos);
+
#endif
diff --git a/crawl-ref/source/dat/clua/lm_fog.lua b/crawl-ref/source/dat/clua/lm_fog.lua
index d893a150d2..97bc5750c4 100644
--- a/crawl-ref/source/dat/clua/lm_fog.lua
+++ b/crawl-ref/source/dat/clua/lm_fog.lua
@@ -48,11 +48,13 @@
-- start_clouds: The number of clouds to lay when the level containing
-- the cloud machine is entered. This is necessary since clouds
-- are cleared when the player leaves a level.
--- helper: A FunctionMachine helper marker. Will be called whenever the countdown
--- is activated, and whenever the fog machine is reset. It will be called
--- with the FogMachine's marker, a string containing the event ("decrement",
--- "trigger"), the actual event object, and a copy of the FogMachine itself.
--- See the section "Messages for fog machines" at the end of the file.
+-- listener: A FunctionMachine listener marker. Will be called
+-- whenever the countdown is activated, and whenever the fog
+-- machine is reset. It will be called with the FogMachine's
+-- marker, a string containing the event ("decrement", "trigger"),
+-- the actual event object, and a copy of the FogMachine itself.
+-- See the section "Messages for fog machines" at the end of the
+-- file.
--
------------------------------------------------------------------------------
@@ -96,7 +98,7 @@ function FogMachine:new(pars)
m.size_max = pars.size_max or pars.size
m.spread_rate = pars.spread_rate or -1
m.start_clouds = pars.start_clouds or 1
- m.helper = pars.helper or nil
+ m.listener = pars.listener or nil
m.size_buildup_amnt = pars.size_buildup_amnt or 0
m.size_buildup_time = pars.size_buildup_time or 1
@@ -109,10 +111,16 @@ function FogMachine:new(pars)
return m
end
-function FogMachine:do_fog(marker)
- local x, y = marker:pos()
+function FogMachine:apply_cloud(point, pow_min, pow_max, pow_rolls,
+ size, cloud_type, kill_cat, spread)
+ dgn.apply_area_cloud(point.x, point.y, pow_min, pow_max, pow_rolls, size,
+ cloud_type, kill_cat, spread)
+end
+
+function FogMachine:do_fog(point)
+ local p = point
if self.walk_dist > 0 then
- x, y = dgn.random_walk(x, y, self.walk_dist)
+ p = dgn.point(dgn.random_walk(p.x, p.y, self.walk_dist))
end
local buildup_turns = self.buildup_turns
@@ -142,9 +150,9 @@ function FogMachine:do_fog(marker)
local spread = self.spread_rate + (self.spread_buildup_amnt * buildup_turns /
self.spread_buildup_time)
- dgn.apply_area_cloud(x, y, self.pow_min, self.pow_max, self.pow_rolls,
- crawl.random_range(size_min, size_max, 1),
- self.cloud_type, self.kill_cat, spread)
+ self:apply_cloud(p, self.pow_min, self.pow_max, self.pow_rolls,
+ crawl.random_range(size_min, size_max, 1),
+ self.cloud_type, self.kill_cat, spread)
end
function FogMachine:activate(marker, verbose)
@@ -153,6 +161,12 @@ function FogMachine:activate(marker, verbose)
dgn.register_listener(dgn.dgn_event_type('entered_level'), marker)
end
+function FogMachine:notify_listener(point, event, evobj)
+ if self.listener then
+ return self.listener:do_function(point, event, ev, self)
+ end
+end
+
function FogMachine:event(marker, ev)
local _x, _y = marker:pos()
if ev:type() == dgn.dgn_event_type('turn') then
@@ -164,23 +178,21 @@ function FogMachine:event(marker, ev)
self.buildup_turns = self.size_buildup_time
end
- if self.helper ~= nil and self.countdown > 0 then
- self.helper:do_function(marker, "decrement", ev, self)
- elseif self.helper ~= nil and self.countdown <= 0 then
- self.helper:do_function(marker, "trigger", ev, self)
+ if self.countdown > 0 then
+ self:notify_listener(dgn.point(marker:pos()), "decrement", ev)
+ elseif self.countdown <= 0 then
+ self:notify_listener(dgn.point(marker:pos()), "trigger", ev)
end
while self.countdown <= 0 do
- self:do_fog(marker)
- self.countdown = self.countdown +
+ self:do_fog(dgn.point(marker:pos()))
+ self.countdown = self.countdown +
crawl.random_range(self.delay_min, self.delay_max, 1)
end
elseif ev:type() == dgn.dgn_event_type('entered_level') then
for i = 1, self.start_clouds do
- self:do_fog(marker)
- if self.helper ~= nil then
- self.helper:do_function(marker, "trigger", ev, self)
- end
+ self:do_fog(dgn.point(marker:pos()))
+ self:notify_listener(dgn.point(marker:pos()), "trigger", ev)
self.countdown = crawl.random_range(self.delay_min, self.delay_max, 1)
self.buildup_turns = 0
end
@@ -206,9 +218,9 @@ function FogMachine:write(marker, th)
file.marshall(th, self.spread_buildup_time)
file.marshall(th, self.buildup_turns)
file.marshall(th, self.countdown)
- if self.helper ~= nil then
+ if self.listener then
file.marshall_meta(th, true)
- self.helper:write(marker, th)
+ self.listener:write(marker, th)
else
file.marshall_meta(th, false)
end
@@ -233,12 +245,15 @@ function FogMachine:read(marker, th)
self.spread_buildup_time = file.unmarshall_number(th)
self.buildup_turns = file.unmarshall_number(th)
self.countdown = file.unmarshall_number(th)
- got_helper = file.unmarshall_meta(th)
- if got_helper == true then
- self.helper = function_machine ({marker_type = "helper", func = (function() end)})
- self.helper:read(marker, th)
+ got_listener = file.unmarshall_meta(th)
+ if got_listener == true then
+ self.listener = function_machine {
+ marker_type = "listener",
+ func = (function() end)
+ }
+ self.listener:read(marker, th)
else
- self.helper = nil
+ self.listener = nil
end
setmetatable(self, FogMachine)
@@ -280,41 +295,46 @@ end
-------------------------------------------------------------------------------
-- Messages for fog machines.
--
--- * warning_machine: Takes three parameters: turns, cantsee_message, and,
--- optionally, see_message. Turns is the value of player turns before to
--- trigger the message before the fog machine is fired. If only see_message
--- is provided, the message will only be printed if the player can see the
--- fog machine. If only cantsee_mesage is provided, the message will be
--- displayed regardless. In combination, the message will be different
--- depending on whether or not the player can see the marker. By default, the
--- message will be displaying using the "warning" channel.
+-- * warning_machine: Takes three parameters: turns, cantsee_message,
+-- and, optionally, see_message. Turns is the value of player
+-- turns before to trigger the message before the fog machine is
+-- fired. If only see_message is provided, the message will only
+-- be printed if the player can see the fog machine. If only
+-- cantsee_mesage is provided, the message will be displayed
+-- regardless. In combination, the message will be different
+-- depending on whether or not the player can see the marker. By
+-- default, the message will be displaying using the "warning"
+-- channel.
--
--- * trigger_machine: Takes three parameters: cantsee_message, see_message, and,
--- optionally, channel. The functionality is identical to a warning_machine,
--- only the message is instead displayed (or not displayed) when the fog machine
--- is triggered. The message channel can be provided.
+-- * trigger_machine: Takes three parameters: cantsee_message,
+-- see_message, and, optionally, channel. The functionality is
+-- identical to a warning_machine, only the message is instead
+-- displayed (or not displayed) when the fog machine is
+-- triggered. The message channel can be provided.
--
--- * tw_machine: Combines the above two message machines, providing warning messages
--- as well as messages when triggered. Takes the parameters: warn_turns,
--- warning_cantsee_message, trigger_cantsee_message, trigger_channel,
--- trigger_see_message, warning_see_message. Parameters work as described above.
+-- * tw_machine: Combines the above two message machines, providing
+-- warning messages as well as messages when triggered. Takes the
+-- parameters: warn_turns, warning_cantsee_message,
+-- trigger_cantsee_message, trigger_channel, trigger_see_message,
+-- warning_see_message. Parameters work as described above.
--
--- In all instances, the "cantsee" form of the message parameter cannot be null,
--- and for warning and dual trigger/warning machines, the turns parameter cannot
--- be null. All other parameters are considered optional.
+-- In all instances, the "cantsee" form of the message parameter
+-- cannot be null, and for warning and dual trigger/warning machines,
+-- the turns parameter cannot be null. All other parameters are
+-- considered optional.
function warning_machine (trns, cantsee_mesg, see_mesg)
if trns == nil or (see_mesg == nil and cantsee_mesg == nil) then
error("WarningMachine requires turns and message!")
end
- local function warning_func (marker, mtable, m2, event_name, event, fm)
+ local function warning_func (point, mtable, event_name, event, fm)
local countdown = fm.countdown
if event_name == "decrement" and countdown <= mtable.turns then
- if mtable.warning_done ~= true then
- if mtable.see_message ~= nil and you.see_cell(marker:pos()) then
- crawl.mpr(mtable.see_message, "warning")
- elseif mtable.cantsee_message ~= nil then
- crawl.mpr(mtable.cantsee_message, "warning")
+ if not mtable.warning_done then
+ if mtable.see_message and you.see_cell(point.x, point.y) then
+ crawl.mpr(mtable.see_message, "warning")
+ elseif mtable.cantsee_message then
+ crawl.mpr(mtable.cantsee_message, "warning")
end
mtable.warning_done = true
end
@@ -322,7 +342,7 @@ function warning_machine (trns, cantsee_mesg, see_mesg)
mtable.warning_done = false
end
end
- pars = {marker_type = "helper"}
+ pars = {marker_type = "listener"}
pars.marker_params = {see_message = see_mesg, cantsee_message = cantsee_mesg,
turns = trns * 10, warning_done = false}
pars.func = warning_func
@@ -333,34 +353,40 @@ function trigger_machine (cantsee_mesg, see_mesg, chan)
if see_mesg == nil and cantsee_mesg == nil then
error("Triggermachine requires a message!")
end
- local function trigger_func (marker, mtable, m2, event_name, event, fm)
+ local function trigger_func (point, mtable, event_name, event, fm)
local countdown = fm.countdown
if event_name == "trigger" then
channel = mtable.channel or ""
- if mtable.see_message ~= nil and you.see_cell(marker:pos()) then
+ if mtable.see_message ~= nil and you.see_cell(point.x, point.y) then
crawl.mpr(mtable.see_message, channel)
elseif mtable.cantsee_message ~= nil then
crawl.mpr(mtable.cantsee_message, channel)
end
end
end
- pars = {marker_type = "helper"}
- pars.marker_params = {channel = chan or nil, see_message = see_mesg, cantsee_message = cantsee_mesg}
+ pars = {marker_type = "listener"}
+ pars.marker_params = {
+ channel = chan or nil,
+ see_message = see_mesg,
+ cantsee_message = cantsee_mesg
+ }
pars.func = trigger_func
return FunctionMachine:new(pars)
end
-function tw_machine (warn_turns, warn_cantsee_message, trig_cantsee_message, trig_channel,
+function tw_machine (warn_turns, warn_cantsee_message,
+ trig_cantsee_message, trig_channel,
trig_see_message, warn_see_message)
- if warn_turns == nil or (warn_see_message == nil and warn_cantsee_message == nil)
- or (trig_see_message == nil and trig_cantsee_message == nil) then
- error("TWMachine needs warning turns, warning message and triggeing message.")
+ if (not warn_turns or (not warn_see_message and not warn_cantsee_message)
+ or (not trig_see_message and not trig_cantsee_message)) then
+ error("TWMachine needs warning turns, warning message and "
+ .. "triggering message.")
end
- local function tw_func (marker, mtable, m2, event_name, event, fm)
+ local function tw_func (point, mtable, event_name, event, fm)
local countdown = fm.countdown
if event_name == "decrement" and countdown <= mtable.warning_turns then
if mtable.warning_done ~= true then
- if mtable.warning_see_message ~= nil and you.see_cell(marker:pos()) then
+ if mtable.warning_see_message and you.see_cell(point.x, point.y) then
crawl.mpr(mtable.warning_see_message, "warning")
elseif mtable.warning_cantsee_message ~= nil then
crawl.mpr(mtable.warning_cantsee_message, "warning")
@@ -370,17 +396,23 @@ function tw_machine (warn_turns, warn_cantsee_message, trig_cantsee_message, tri
elseif event_name == "trigger" then
mtable.warning_done = false
channel = mtable.trigger_channel or ""
- if mtable.trigger_see_message ~= nil and you.see_cell(marker:pos()) then
+ if mtable.trigger_see_message and you.see_cell(point.x, point.y) then
crawl.mpr(mtable.trigger_see_message, channel)
elseif mtable.trigger_cantsee_message ~= nil then
crawl.mpr(mtable.trigger_cantsee_message, channel)
end
end
end
- pars = {marker_type = "helper"}
- pars.marker_params = {warning_see_message = warn_see_message, warning_cantsee_message = warn_cantsee_message,
- warning_turns = warn_turns * 10, warning_done = false, trigger_see_message = trig_see_message,
- trigger_cantsee_message = trig_cantsee_message, trigger_channel = trig_channel or nil}
+ pars = {marker_type = "listener"}
+ pars.marker_params = {
+ warning_see_message = warn_see_message,
+ warning_cantsee_message = warn_cantsee_message,
+ warning_turns = warn_turns * 10,
+ warning_done = false,
+ trigger_see_message = trig_see_message,
+ trigger_cantsee_message = trig_cantsee_message,
+ trigger_channel = trig_channel or nil
+ }
pars.func = tw_func
return FunctionMachine:new(pars)
end
diff --git a/crawl-ref/source/dat/clua/lm_func.lua b/crawl-ref/source/dat/clua/lm_func.lua
index b76d6cdc9a..cd83cf61a3 100644
--- a/crawl-ref/source/dat/clua/lm_func.lua
+++ b/crawl-ref/source/dat/clua/lm_func.lua
@@ -21,12 +21,14 @@
-- * "player_at": Calls the function whenever the player is at the
-- same position as the marker. Takes the same "repeated"
-- parameter as "in_los".
--- * "helper": A function machine that can be linked into other lua markers
--- and machines. It is not triggered independantly, but called by the "parent"
--- marker, though always with the same marker_table parameter as other
--- machines. May take further parameters, see the parent's documentation.
+-- * "helper": A function machine that can be linked into other lua
+-- markers and machines. It is not triggered independantly, but
+-- called by the "parent" marker, though always with the same
+-- marker_table parameter as other machines. May take further
+-- parameters, see the parent's documentation.
--
--- marker_table: Table to be passed to the function when called. Defaults to {}.
+-- marker_table: Table to be passed to the function when called.
+-- Defaults to {}.
--
-- Specific markers take specific parameters, as listed under marker_type.
--
@@ -95,13 +97,12 @@ function FunctionMachine:new(pars)
return m
end
-function FunctionMachine:do_function(...)
- marker = arg[1]
- local _x, _y = marker:pos()
- if #arg == 1 then
- self.func(marker, self.marker_params)
+function FunctionMachine:do_function(position, ...)
+ local largs = { ... }
+ if #largs == 0 then
+ self.func(position, self.marker_params)
else
- self.func(marker, self.marker_params, unpack(arg))
+ self.func(position, self.marker_params, unpack(largs))
end
end
@@ -121,7 +122,7 @@ function FunctionMachine:event(marker, ev)
self.countdown = self.countdown - ev:ticks()
while self.countdown <= 0 do
- self:do_function(marker)
+ self:do_function(dgn.point(marker:pos()))
self.activated = true
self.countdown = self.countdown +
crawl.random_range(self.turns_min, self.turns_max, 1)
@@ -129,7 +130,7 @@ function FunctionMachine:event(marker, ev)
elseif self.marker_type == "in_los" then
if you.see_cell(x, y) then
if not self.activated or self.repeated then
- self:do_function(marker)
+ self:do_function(dgn.point(marker:pos()))
self.activated = true
end
end
@@ -137,7 +138,7 @@ function FunctionMachine:event(marker, ev)
you_x, you_y = you.pos()
if you_x == x and you_y == y then
if not self.activated or self.repeated then
- self:do_function(marker)
+ self:do_function(dgn.point(marker:pos()))
self.activated = true
end
end
@@ -178,7 +179,9 @@ end
function message_machine (pars)
local channel = pars.channel or false
local mtable = {message = pars.message, channel = pars.channel}
- pars.func = (function(marker, mtable) crawl.mpr(mtable.message, mtable.channel) end)
+ pars.func = function (position, mtable)
+ crawl.mpr(mtable.message, mtable.channel)
+ end
pars.marker_params = mtable
return FunctionMachine:new(pars)
end
diff --git a/crawl-ref/source/dat/clua/lm_mslav.lua b/crawl-ref/source/dat/clua/lm_mslav.lua
new file mode 100644
index 0000000000..141dea7521
--- /dev/null
+++ b/crawl-ref/source/dat/clua/lm_mslav.lua
@@ -0,0 +1,157 @@
+----------------------------------------------------------------------------
+-- lm_mslav.lua
+--
+-- Wraps a marker to act as a master firing synchronized events to its
+-- own position, and to any number of (or zero) slave markers'
+-- positions.
+--
+-- API: lmark.synchronized_markers(<marker>, <trigger-function-names>)
+--
+-- Usage:
+-- ------
+--
+-- You can use synchronized_markers() if you have a marker that
+-- performs an activity at random intervals, and you want to apply
+-- this marker's effects to multiple locations at the same time.
+--
+-- As an example, take a fog machine:
+-- 1) Create the fog machine as you would normally:
+-- local fog = fog_machine {
+-- cloud_type = 'flame',
+-- size = 3, pow_min=2,
+-- pow_max = 5, delay_min = 22, delay_max = 120,
+-- }
+--
+-- 2) Apply it as a Lua marker to one or more locations, wrapping it
+-- with synchronized_markers():
+-- lua_marker('m', lmark.synchronized_markers(fog, 'do_fog'))
+-- Where 'do_fog' is the name of the trigger method on the
+-- underlying marker (here the fog machine) that performs the
+-- activity of interest (generating fog at some point). The first
+-- parameter of this overridden method must be a dgn.point that
+-- specifies where the effect occurs. The method may also take any
+-- number of additional parameters.
+--
+-- You may override multiple methods on the base marker:
+-- lmark.synchronized_markers(fog, 'do_fog', 'notify_listener')
+-- The only requirement for an overridden method is that it take a
+-- dgn.point as its first parameter.
+--
+-- Internals:
+-- ---------
+-- synchronized_markers() takes one marker instance, and creates one
+-- master marker (which is based on the given marker instance) and
+-- multiple slave markers (which are simple PortalDescriptor markers).
+-- The only purpose of the slave markers is to be discoverable by
+-- dgn.find_marker_positions_by_prop, given a unique, autogenerated
+-- slave id.
+--
+-- The master marker operates normally, but calls to any of the trigger
+-- methods (say 'do_fog') are intercepted. Every trigger call is performed
+-- on the master's position, and then on all the slaves' positions.
+----------------------------------------------------------------------------
+
+util.namespace('lmark')
+
+lmark.slave_cookie = 0
+
+function lmark.next_slave_id()
+ local slave_id = "marker_slave" .. lmark.slave_cookie
+ lmark.slave_cookie = lmark.slave_cookie + 1
+ return slave_id
+end
+
+function lmark.saveable_slave_table(slave)
+ local saveable = {
+ slave_id = slave.slave_id,
+ triggers = slave.triggers,
+ old_read = slave.old_read
+ }
+ return saveable
+end
+
+function lmark:master_trigger_fn(trigger_name, point, ...)
+ local old_trigger = self.slave_table.old_triggers[trigger_name]
+ -- Pull the trigger on the master first.
+ old_trigger(self, point, ...)
+
+ local slave_points =
+ dgn.find_marker_positions_by_prop("slave_id", self.slave_table.slave_id)
+ for _, slave_pos in ipairs(slave_points) do
+ old_trigger(self, slave_pos, ...)
+ end
+end
+
+function lmark:master_write(marker, th)
+ -- Save the slave table first.
+ lmark.marshall_table(th, lmark.saveable_slave_table(self.slave_table))
+ self.slave_table.old_write(self, marker, th)
+end
+
+
+function lmark:master_read(marker, th)
+ -- Load the slave table.
+ local slave_table = lmark.unmarshall_table(th)
+
+ local cookie_number = string.match(slave_table.slave_id, "marker_slave(%d+)")
+ -- [ds] Try to avoid reusing the same cookie as one we've reloaded.
+ -- This is only necessary to avoid collisions with cookies generated
+ -- for future vaults placed on this level (such as by the Trowel
+ -- card).
+ if cookie_number then
+ cookie_number = tonumber(cookie_number)
+ if lmark.slave_cookie <= cookie_number then
+ lmark.slave_cookie = cookie_number + 1
+ end
+ end
+
+ -- Call the old read function.
+ local newself = slave_table.old_read(self, marker, th)
+ -- And redecorate the marker as a master marker.
+ return lmark.make_master(newself, slave_table.slave_id,
+ slave_table.triggers)
+end
+
+function lmark.make_master(lmarker, slave_id, triggers)
+ local old_trigger_map = { }
+ for _, trigger_name in ipairs(triggers) do
+ old_trigger_map[trigger_name] = lmarker[trigger_name]
+ lmarker[trigger_name] =
+ function (self, ...)
+ return lmark.master_trigger_fn(self, trigger_name, ...)
+ end
+ end
+
+ lmarker.slave_table = {
+ slave_id = slave_id,
+ triggers = triggers,
+ old_write = lmarker.write,
+ old_triggers = old_trigger_map,
+ old_read = lmarker.read
+ }
+
+ lmarker.write = lmark.master_write
+ lmarker.read = lmark.master_read
+
+ return lmarker
+end
+
+function lmark.make_slave(slave_id)
+ return portal_desc { slave_id = slave_id }
+end
+
+function lmark.synchronized_markers(master, ...)
+ local first = true
+ local slave_id = lmark.next_slave_id()
+ local triggers = { ... }
+ assert(#triggers > 0,
+ "Please provide one or more trigger functions on the master marker")
+ return function ()
+ if first then
+ first = false
+ return lmark.make_master(master, slave_id, triggers)
+ else
+ return lmark.make_slave(slave_id)
+ end
+ end
+end \ No newline at end of file
diff --git a/crawl-ref/source/dat/clua/luamark.lua b/crawl-ref/source/dat/clua/luamark.lua
index 86b125a53c..10eb05945c 100644
--- a/crawl-ref/source/dat/clua/luamark.lua
+++ b/crawl-ref/source/dat/clua/luamark.lua
@@ -13,6 +13,7 @@ require('clua/lm_fog.lua')
require('clua/lm_props.lua')
require('clua/lm_monst.lua')
require('clua/lm_func.lua')
+require('clua/lm_mslav.lua')
function dlua_marker_function(table, name)
return table[name]
@@ -28,7 +29,7 @@ function dlua_marker_read(fn, marker, th)
return fn({ }, marker, th)
end
-lmark = { }
+util.namespace('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
diff --git a/crawl-ref/source/dat/clua/point.lua b/crawl-ref/source/dat/clua/point.lua
index b750372197..f186dc62f5 100644
--- a/crawl-ref/source/dat/clua/point.lua
+++ b/crawl-ref/source/dat/clua/point.lua
@@ -22,6 +22,10 @@ local function sgn(x)
end
end
+function point_metatable:xy()
+ return self.x, self.y
+end
+
point_metatable.sgn = function (p)
return dgn.point(sgn(p.x), sgn(p.y))
end
diff --git a/crawl-ref/source/dat/clua/util.lua b/crawl-ref/source/dat/clua/util.lua
index 1ed3670172..5ef063d9fb 100644
--- a/crawl-ref/source/dat/clua/util.lua
+++ b/crawl-ref/source/dat/clua/util.lua
@@ -326,3 +326,11 @@ function util.copy_table(object)
return _copy(object)
end
+-- Initialises a namespace that has functions spread across multiple files.
+-- If the namespace table does not exist, it is created. If it already exists,
+-- it is not modified.
+function util.namespace(table_name)
+ if _G[table_name] == nil then
+ _G[table_name] = { }
+ end
+end \ No newline at end of file
diff --git a/crawl-ref/source/dat/clua/ziggurat.lua b/crawl-ref/source/dat/clua/ziggurat.lua
index ba35595d70..8ac89bc460 100644
--- a/crawl-ref/source/dat/clua/ziggurat.lua
+++ b/crawl-ref/source/dat/clua/ziggurat.lua
@@ -600,7 +600,7 @@ local function ziggurat_create_loot_vault(entry, exit)
return exit
else
-- Find the square to drop the loot.
- local lootx, looty = dgn.find_marker_prop("ziggurat_loot")
+ local lootx, looty = dgn.find_marker_position_by_prop("ziggurat_loot")
if lootx and looty then
return dgn.point(lootx, looty)
diff --git a/crawl-ref/source/dat/volcano.des b/crawl-ref/source/dat/volcano.des
index fdfd592fe9..1b248e11ed 100644
--- a/crawl-ref/source/dat/volcano.des
+++ b/crawl-ref/source/dat/volcano.des
@@ -156,28 +156,28 @@ function place_large_volcano(e)
e.kfeat("V = l")
e.lua_marker('V', fog_machine { cloud_type = "flame", walk_dist=15, pow_max=6,
delay = 300, size = 10000, spread_rate = 30,
- helper = large_warning})
+ listener = large_warning})
end
function place_medium_volcano(e)
e.kfeat("V = l")
e.lua_marker('V', fog_machine { cloud_type = "flame", walk_dist=15, pow_max=6,
delay = 300, size = 3000, spread_rate = 30,
- helper = small_warning})
+ listener = small_warning})
end
function place_small_volcano(e)
e.kfeat("V = l")
e.lua_marker('V', fog_machine { cloud_type = "flame", walk_dist=15, pow_max=6,
delay = 300, size = 800, spread_rate = 30,
- helper = small_warning})
+ listener = small_warning})
end
function place_tiny_volcano(e)
e.kfeat("V = l")
e.lua_marker('V', fog_machine { cloud_type = "flame", walk_dist=10, pow_max=6,
delay = 800, size = 80, spread_rate = 10,
- helper = large_warning})
+ listener = large_warning})
end
function place_medium_flame_cloud (e, glyph, nolava)
@@ -389,6 +389,33 @@ default-depth: Lair:1-8, Orc:1-4, Hive:1
###############################################################################
# Entries:
+
+# [ds] A dummy entry that's a proof-of-concept for synchronized fog machines
+# that trigger at multiple places at the same instant, but still have a random
+# delay.
+NAME: enter_volcano_snarktest101
+TAGS: uniq_volcano
+ORIENT: float
+WEIGHT: 0
+{{
+ local fog = fog_machine { cloud_type = 'flame',
+ size = 3, pow_min=2,
+ pow_max = 5, delay_min = 22, delay_max = 120,
+ }
+ lua_marker('m', lmark.synchronized_markers(fog, 'do_fog'))
+}}
+SUBST: m = .
+: volcano_portal(_G)
+MAP
+.......
+...m...
+.......
+.m.O.m.
+.......
+...m...
+.......
+ENDMAP
+
NAME: enter_volcano_1
TAGS: uniq_volcano
ORIENT: float
@@ -841,8 +868,8 @@ LFLAGS: no_tele_control
{{
local mytable = { total_doorways=8, total_collapsed=0, ac = {} }
-local function collapse_doorways (marker, mytable)
- local x, y = marker:pos()
+local function collapse_doorways (point, mytable)
+ local x, y = point:xy()
local you_x, you_y = you.pos()
if mytable.total_collapsed == mytable.total_doorways then
return
@@ -856,7 +883,7 @@ local function collapse_doorways (marker, mytable)
local ack = x .. "/" .. y
if mytable.ac[ack] ~= true then
mytable.ac[ack] = true
- if you.see_cell(marker:pos()) then
+ if you.see_cell(x, y) then
crawl.mpr("The volcano erupts! Nearby, a roof collapses.", "warning")
else
crawl.mpr("There is a rumble as the volcano erupts. The roof shakes.",
@@ -868,8 +895,14 @@ local function collapse_doorways (marker, mytable)
end
end
-local collapse_marker = function_machine ( {marker_type = "random", turns_min=30,
- turns_max=40, func=collapse_doorways, marker_params=mytable } )
+local collapse_marker = function_machine {
+ marker_type = "random",
+ turns_min=30,
+ turns_max=40,
+ func=collapse_doorways,
+ marker_params=mytable
+}
+
}}
KPROP: RXZ12< = no_rtele_into
SUBST: X = .
diff --git a/crawl-ref/source/l_crawl.cc b/crawl-ref/source/l_crawl.cc
index d7571a63e7..a1682c59b3 100644
--- a/crawl-ref/source/l_crawl.cc
+++ b/crawl-ref/source/l_crawl.cc
@@ -603,7 +603,7 @@ void cluaopen_crawl(lua_State *ls)
LUAFN(_crawl_args)
{
- return dlua_stringtable(ls, SysEnv.cmd_args);
+ return clua_stringtable(ls, SysEnv.cmd_args);
}
LUAFN(_crawl_milestone)
diff --git a/crawl-ref/source/l_dgn.cc b/crawl-ref/source/l_dgn.cc
index 86d142192e..180ffc87ab 100644
--- a/crawl-ref/source/l_dgn.cc
+++ b/crawl-ref/source/l_dgn.cc
@@ -356,7 +356,7 @@ static int dgn_map(lua_State *ls)
{
MAP(ls, 1, map);
if (lua_gettop(ls) == 1)
- return dlua_stringtable(ls, map->map.get_lines());
+ return clua_stringtable(ls, map->map.get_lines());
if (lua_isnil(ls, 2))
{
@@ -1407,12 +1407,12 @@ LUAFN(_dgn_in_vault)
return (1);
}
-LUAFN(_dgn_find_marker_prop)
+LUAFN(_dgn_find_marker_position_by_prop)
{
const char *prop = luaL_checkstring(ls, 1);
const std::string value(
lua_gettop(ls) >= 2 ? luaL_checkstring(ls, 2) : "");
- const coord_def place = find_marker_prop(prop, value);
+ const coord_def place = find_marker_position_by_prop(prop, value);
if (map_bounds(place))
clua_push_coord(ls, place);
else
@@ -1423,6 +1423,36 @@ LUAFN(_dgn_find_marker_prop)
return (2);
}
+LUAFN(_dgn_find_marker_positions_by_prop)
+{
+ const char *prop = luaL_checkstring(ls, 1);
+ const std::string value(
+ lua_gettop(ls) >= 2 ? luaL_checkstring(ls, 2) : "");
+ const unsigned limit(lua_gettop(ls) >= 3 ? luaL_checkint(ls, 3) : 0);
+ const std::vector<coord_def> places =
+ find_marker_positions_by_prop(prop, value, limit);
+ clua_gentable(ls, places, clua_pushpoint);
+ return (1);
+}
+
+static int _push_mapmarker(lua_State *ls, map_marker *marker)
+{
+ dlua_push_userdata(ls, marker, MAPMARK_METATABLE);
+ return (1);
+}
+
+LUAFN(_dgn_find_markers_by_prop)
+{
+ const char *prop = luaL_checkstring(ls, 1);
+ const std::string value(
+ lua_gettop(ls) >= 2 ? luaL_checkstring(ls, 2) : "");
+ const unsigned limit(lua_gettop(ls) >= 3 ? luaL_checkint(ls, 3) : 0);
+ const std::vector<map_marker*> places =
+ find_markers_by_prop(prop, value, limit);
+ clua_gentable(ls, places, _push_mapmarker);
+ return (1);
+}
+
extern spec_room lua_special_room_spec;
extern int lua_special_room_level;
@@ -1588,7 +1618,9 @@ const struct luaL_reg dgn_dlib[] =
{ "resolve_map", _dgn_resolve_map },
{ "in_vault", _dgn_in_vault },
-{ "find_marker_prop", _dgn_find_marker_prop },
+{ "find_marker_position_by_prop", _dgn_find_marker_position_by_prop },
+{ "find_marker_positions_by_prop", _dgn_find_marker_positions_by_prop },
+{ "find_markers_by_prop", _dgn_find_markers_by_prop },
{ "get_special_room_info", dgn_get_special_room_info },
diff --git a/crawl-ref/source/mapmark.cc b/crawl-ref/source/mapmark.cc
index 253855eb23..e7d3abf65a 100644
--- a/crawl-ref/source/mapmark.cc
+++ b/crawl-ref/source/mapmark.cc
@@ -824,16 +824,59 @@ bool feature_marker_at(const coord_def &pos, dungeon_feature_type feat)
return (false);
}
-const coord_def find_marker_prop(const std::string &prop,
- const std::string &expected)
+coord_def find_marker_position_by_prop(const std::string &prop,
+ const std::string &expected)
{
+ const std::vector<coord_def> markers =
+ find_marker_positions_by_prop(prop, expected, 1);
+ if (markers.empty())
+ {
+ const coord_def nowhere(-1, -1);
+ return (nowhere);
+ }
+ return markers[0];
+}
+
+std::vector<coord_def> find_marker_positions_by_prop(
+ const std::string &prop,
+ const std::string &expected,
+ unsigned maxresults)
+{
+ std::vector<coord_def> marker_positions;
for (rectangle_iterator i(0, 0); i; ++i)
{
const std::string value =
env.markers.property_at(*i, MAT_ANY, prop);
if (!value.empty() && (expected.empty() || value == expected))
- return (*i);
+ {
+ marker_positions.push_back(*i);
+ if (maxresults && marker_positions.size() >= maxresults)
+ return (marker_positions);
+ }
+ }
+ return (marker_positions);
+}
+
+std::vector<map_marker*> find_markers_by_prop(
+ const std::string &prop,
+ const std::string &expected,
+ unsigned maxresults)
+{
+ std::vector<map_marker*> markers;
+ for (rectangle_iterator pos(0, 0); pos; ++pos)
+ {
+ const std::vector<map_marker*> markers_here =
+ env.markers.get_markers_at(*pos);
+ for (unsigned i = 0, size = markers_here.size(); i < size; ++i)
+ {
+ const std::string value(markers_here[i]->property(prop));
+ if (!value.empty() && (expected.empty() || value == expected))
+ {
+ markers.push_back(markers_here[i]);
+ if (maxresults && markers.size() >= maxresults)
+ return (markers);
+ }
+ }
}
- const coord_def nowhere(-1, -1);
- return (nowhere);
+ return (markers);
}
diff --git a/crawl-ref/source/mapmark.h b/crawl-ref/source/mapmark.h
index 71a142ce13..67e64a2804 100644
--- a/crawl-ref/source/mapmark.h
+++ b/crawl-ref/source/mapmark.h
@@ -13,6 +13,7 @@
#include "dlua.h"
#include <map>
#include <string>
+#include <vector>
#include <memory>
//////////////////////////////////////////////////////////////////////////
@@ -23,8 +24,16 @@ class writer;
bool marker_vetoes_operation(const char *op);
bool feature_marker_at(const coord_def &pos, dungeon_feature_type feat);
-const coord_def find_marker_prop(const std::string &prop,
- const std::string &expected = "");
+coord_def find_marker_position_by_prop(const std::string &prop,
+ const std::string &expected = "");
+std::vector<coord_def> find_marker_positions_by_prop(
+ const std::string &prop,
+ const std::string &expected = "",
+ unsigned maxresults = 0);
+std::vector<map_marker*> find_markers_by_prop(
+ const std::string &prop,
+ const std::string &expected = "",
+ unsigned maxresults = 0);
class map_marker
{