summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/dat/clua/lm_flags.lua267
-rw-r--r--crawl-ref/source/dat/clua/lm_monst.lua135
-rw-r--r--crawl-ref/source/dat/clua/lm_trig.lua352
-rw-r--r--crawl-ref/source/dat/clua/luamark.lua2
-rw-r--r--crawl-ref/source/dat/clua/util.lua20
-rw-r--r--crawl-ref/source/dat/hells.des2
-rw-r--r--crawl-ref/source/dgnevent.h10
-rw-r--r--crawl-ref/source/l_dgnevt.cc26
8 files changed, 535 insertions, 279 deletions
diff --git a/crawl-ref/source/dat/clua/lm_flags.lua b/crawl-ref/source/dat/clua/lm_flags.lua
index 4660fa5abb..3a2f3e1b5b 100644
--- a/crawl-ref/source/dat/clua/lm_flags.lua
+++ b/crawl-ref/source/dat/clua/lm_flags.lua
@@ -53,51 +53,43 @@
-- "item", which is the plain name of the item its watching
-- (i.e., "Orb of Zot" and "golden rune of Zot" rather than
-- "the Orb of Zot" or "a golden rune of Zot").
+--
+-- ChangeFlags is a Triggerable subclass, and the above three functions
+-- are just convenience functions which add a single triggerer. Other
+-- triggerers (or more than one) can be added.
--------------------------------------------------------------------------
-
-ChangeFlags = { CLASS = "ChangeFlags" }
-ChangeFlags.__index = ChangeFlags
-function ChangeFlags:_new()
- local cf = { }
- setmetatable(cf, self)
- self.__index = self
+require('clua/lm_trig.lua')
- return cf
-end
+ChangeFlags = util.subclass(Triggerable)
+ChangeFlags.CLASS = "ChangeFlags"
function ChangeFlags:new(pars)
pars = pars or { }
+ local cf = self.super.new(self)
+
pars.level_flags = pars.level_flags or ""
pars.branch_flags = pars.branch_flags or ""
pars.msg = pars.msg or ""
- if pars.level_flags == "" and pars.branch_flags == ""
- and not pars.trigger
- then
- error("Must provide at least one of level_flags, branch_flags, or trigger.")
+ if pars.level_flags == "" and pars.branch_flags == "" then
+ error("Must provide at least one of level_flags or branch_flags.")
end
- local cf = self:_new()
cf.level_flags = pars.level_flags
cf.branch_flags = pars.branch_flags
cf.msg = pars.msg
- cf.trigger = pars.trigger
cf.props = { flag_group = pars.group }
return cf
end
-function ChangeFlags:do_change(marker)
+function ChangeFlags:on_trigger(triggerer, marker, ev)
local did_change1 = false
local did_change2 = false
local silent = self.msg and self.msg ~= ""
- if self.trigger then
- self.trigger(marker)
- end
-
if self.props.flag_group and self.props.flag_group ~= "" then
local num_markers = dgn.num_matching_markers("flag_group",
self.props.group)
@@ -115,6 +107,8 @@ function ChangeFlags:do_change(marker)
did_change2 = dgn.change_branch_flags(self.branch_flags, silent)
end
+ self:remove(marker)
+
if did_change1 or did_change2 then
if self.msg and self.msg ~= "" then
crawl.mpr(self.smg)
@@ -131,19 +125,22 @@ function ChangeFlags:property(marker, pname)
end
function ChangeFlags:write(marker, th)
+ ChangeFlags.super.write(self, marker, th)
+
file.marshall(th, self.level_flags)
file.marshall(th, self.branch_flags)
file.marshall(th, self.msg)
- file.marshall_meta(th, self.trigger)
lmark.marshall_table(th, self.props)
end
function ChangeFlags:read(marker, th)
+ ChangeFlags.super.read(self, marker, th)
+
self.level_flags = file.unmarshall_string(th)
self.branch_flags = file.unmarshall_string(th)
self.msg = file.unmarshall_string(th)
- self.trigger = file.unmarshall_meta(th)
self.props = lmark.unmarshall_table(th)
+
setmetatable(self, ChangeFlags)
return self
@@ -152,214 +149,56 @@ end
--------------------------------------------------------------------------
--------------------------------------------------------------------------
-MonDiesChangeFlags = ChangeFlags:_new()
-MonDiesChangeFlags.__index = MonDiesChangeFlags
-
-function MonDiesChangeFlags:_new(pars)
- local mdcf
-
- if pars then
- mdcf = ChangeFlags:new(pars)
- else
- mdcf = ChangeFlags:_new()
- end
-
- setmetatable(mdcf, self)
- self.__index = self
-
- return mdcf
-end
-
-function MonDiesChangeFlags:new(pars)
- pars = pars or { }
-
- if not pars.mon_name then
- error("No monster name provided.")
- end
-
- local mdcf = self:_new(pars)
- mdcf.mon_name = pars.mon_name
-
- return mdcf
-end
-
-function MonDiesChangeFlags:activate(marker)
- dgn.register_listener(dgn.dgn_event_type('monster_dies'), marker)
-end
-
-function MonDiesChangeFlags:event(marker, ev)
- local midx = ev:arg1()
- local mons = dgn.mons_from_index(midx)
-
- if not mons then
- error("MonDiesChangeFlags:event() didn't get a valid monster index")
- end
-
- if mons.name == self.mon_name then
- ChangeFlags.do_change(self, marker)
- dgn.remove_listener(marker)
- dgn.remove_marker(marker)
- end
-end
-
-function MonDiesChangeFlags:write(marker, th)
- ChangeFlags.write(self, marker, th)
- file.marshall(th, self.mon_name)
-end
-
-function MonDiesChangeFlags:read(marker, th)
- ChangeFlags.read(self, marker, th)
- self.mon_name = file.unmarshall_string(th)
- setmetatable(self, MonDiesChangeFlags)
-
- return self
-end
-
function mons_dies_change_flags(pars)
- return MonDiesChangeFlags:new(pars)
-end
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-FeatChangeChangeFlags = ChangeFlags:_new()
-FeatChangeChangeFlags.__index = FeatChangeChangeFlags
-
-function FeatChangeChangeFlags:_new(pars)
- local fccf
-
- if pars then
- fccf = ChangeFlags:new(pars)
- else
- fccf = ChangeFlags:_new()
- end
-
- setmetatable(fccf, self)
- self.__index = self
-
- return fccf
-end
-
-function FeatChangeChangeFlags:new(pars)
- pars = pars or { }
-
- local fccf = self:_new(pars)
-
- fccf.final_feat = pars.final_feat
-
- return fccf
-end
-
-function FeatChangeChangeFlags:activate(marker)
- dgn.register_listener(dgn.dgn_event_type('feat_change'), marker,
- marker:pos())
-end
-
-function FeatChangeChangeFlags:event(marker, ev)
- if self.final_feat and self.final_feat ~= "" then
- local feat = dgn.feature_name(dgn.grid(ev:pos()))
- if not string.find(feat, self.final_feat) then
- return
- end
- end
+ local mon_name = pars.mon_name or pars.target
- ChangeFlags.do_change(self, marker)
- dgn.remove_listener(marker, marker:pos())
- dgn.remove_marker(marker)
-end
+ pars.mon_name = nil
+ pars.target = nil
-function FeatChangeChangeFlags:write(marker, th)
- ChangeFlags.write(self, marker, th)
- file.marshall(th, self.final_feat)
-end
+ local cf = ChangeFlags:new(pars)
-function FeatChangeChangeFlags:read(marker, th)
- ChangeFlags.read(self, marker, th)
- self.final_feat = file.unmarshall_string(th)
- setmetatable(self, FeatChangeChangeFlags)
+ cf:add_triggerer(
+ DgnTriggerer:new {
+ type = "monster_dies",
+ target = mon_name
+ }
+ )
- return self
+ return cf
end
function feat_change_change_flags(pars)
- return FeatChangeChangeFlags:new(pars)
-end
+ local final_feat = pars.final_feat or pars.target
---------------------------------------------------------------------------
---------------------------------------------------------------------------
+ pars.final_feat = nil
+ pars.target = nil
-ItemPickupChangeFlags = ChangeFlags:_new()
-ItemPickupChangeFlags.__index = ItemPickupChangeFlags
+ local cf = ChangeFlags:new(pars)
-function ItemPickupChangeFlags:_new(pars)
- local ipcf
+ cf:add_triggerer(
+ DgnTriggerer:new {
+ type = "feat_change",
+ target = final_feat
+ }
+ )
- if pars then
- ipcf = ChangeFlags:new(pars)
- else
- ipcf = ChangeFlags:_new()
- end
-
- setmetatable(ipcf, self)
- self.__index = self
-
- return ipcf
-end
-
-function ItemPickupChangeFlags:new(pars)
- pars = pars or { }
-
- if not pars.item then
- error("No item name provided.")
- end
-
- local ipcf = self:_new(pars)
- ipcf.item = pars.item
-
- return ipcf
-end
-
-function ItemPickupChangeFlags:activate(marker)
- dgn.register_listener(dgn.dgn_event_type('item_pickup'), marker,
- marker:pos())
- dgn.register_listener(dgn.dgn_event_type('item_moved'), marker,
- marker:pos())
+ return cf
end
-function ItemPickupChangeFlags:event(marker, ev)
- local obj_idx = ev:arg1()
- local it = dgn.item_from_index(obj_idx)
-
- if not it then
- error("ItemPickupChangeFlags:event() didn't get a valid item index")
- end
-
- if item.name(it) == self.item then
- local picked_up = ev:type() == dgn.dgn_event_type('item_pickup')
- if picked_up then
- ChangeFlags.do_change(self, marker)
- dgn.remove_listener(marker, marker:pos())
- dgn.remove_marker(marker)
- else
- dgn.remove_listener(marker, marker:pos())
- marker:move(ev:dest())
- self:activate(marker)
- end
- end
-end
+function item_pickup_change_flags(pars)
+ local item = pars.item or pars.target
-function ItemPickupChangeFlags:write(marker, th)
- ChangeFlags.write(self, marker, th)
- file.marshall(th, self.item)
-end
+ pars.item = nil
+ pars.target = nil
-function ItemPickupChangeFlags:read(marker, th)
- ChangeFlags.read(self, marker, th)
- self.item = file.unmarshall_string(th)
- setmetatable(self, ItemPickupChangeFlags)
+ local cf = ChangeFlags:new(pars)
- return self
-end
+ cf:add_triggerer(
+ DgnTriggerer:new {
+ type = "item_pickup",
+ target = item
+ }
+ )
-function item_pickup_change_flags(pars)
- return ItemPickupChangeFlags:new(pars)
+ return cf
end
diff --git a/crawl-ref/source/dat/clua/lm_monst.lua b/crawl-ref/source/dat/clua/lm_monst.lua
index f26e73e89b..539b7b140b 100644
--- a/crawl-ref/source/dat/clua/lm_monst.lua
+++ b/crawl-ref/source/dat/clua/lm_monst.lua
@@ -3,11 +3,12 @@
-- Create a monster on certain events
--------------------------------------------------------------------------
---------------------------------------------------------------------------------------
--- This marker creates a monster on certain events. It uses the following parameters:
+-------------------------------------------------------------------------------
+-- This marker creates a monster on certain events. It uses the following
+-- parameters:
--
--- * death_monster: The name of the monster who's death triggers the creation of
--- the new monster.
+-- * death_monster: The name of the monster who's death triggers the creation
+-- of the new monster.
--
-- * new_monster: The name of the monster to create.
--
@@ -20,30 +21,21 @@
--
-- NOTE: If the feature where the marker is isn't habitable by the new monster,
-- the feature will be changed to the monster's primary habitat.
---------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
-- TODO:
-- * Place more than one monster.
-- * Place monster displaced from marker.
+-- * Be triggered more than once.
-MonsterOnTrigger = { CLASS = "MonsterOnTrigger" }
-MonsterOnTrigger.__index = MonsterOnTrigger
+require('clua/lm_trig.lua')
-function MonsterOnTrigger:_new()
- local pm = { }
- setmetatable(pm, self)
- self.__index = self
-
- return pm
-end
+MonsterOnTrigger = util.subclass(Triggerable)
+MonsterOnTrigger.CLASS = "MonsterOnTrigger"
function MonsterOnTrigger:new(pars)
pars = pars or { }
- if not pars.death_monster or pars.death_monster == "" then
- error("Must provide death_monster")
- end
-
if not pars.new_monster or pars.new_monster == "" then
error("Must provide new_monster")
end
@@ -51,14 +43,15 @@ function MonsterOnTrigger:new(pars)
pars.message_seen = pars.message_seen or pars.message or ""
pars.message_unseen = pars.message_unseen or pars.message or ""
- local pm = self:_new()
- pm.message_seen = pars.message_seen
- pm.message_unseen = pars.message_unseen
- pm.death_monster = pars.death_monster
- pm.new_monster = pars.new_monster
- pm.props = pars
+ local mot = self.super.new(self)
- return pm
+ mot.message_seen = pars.message_seen
+ mot.message_unseen = pars.message_unseen
+ mot.death_monster = pars.death_monster
+ mot.new_monster = pars.new_monster
+ mot.props = pars
+
+ return mot
end
function MonsterOnTrigger:property(marker, pname)
@@ -66,53 +59,71 @@ function MonsterOnTrigger:property(marker, pname)
end
function MonsterOnTrigger:write(marker, th)
- lmark.marshall_table(th, self)
+ MonsterOnTrigger.super.write(self, marker, th)
+
+ file.marshall(th, self.message_seen)
+ file.marshall(th, self.message_unseen)
+ file.marshall(th, self.new_monster)
+ lmark.marshall_table(th, self.props)
end
function MonsterOnTrigger:read(marker, th)
- return MonsterOnTrigger:new(lmark.unmarshall_table(th))
-end
+ MonsterOnTrigger.super.read(self, marker, th)
+
+ self.message_seen = file.unmarshall_string(th)
+ self.message_unseen = file.unmarshall_string(th)
+ self.new_monster = file.unmarshall_string(th)
+ self.props = lmark.unmarshall_table(th)
-function MonsterOnTrigger:activate(marker)
- dgn.register_listener(dgn.dgn_event_type('monster_dies'), marker)
+ setmetatable(self, MonsterOnTrigger)
+
+ return self
end
-function MonsterOnTrigger:event(marker, ev)
- local midx = ev:arg1()
- local mons = dgn.mons_from_index(midx)
+function MonsterOnTrigger:on_trigger(triggerer, marker, ev)
+ local x, y = marker:pos()
+ local you_x, you_y = you.pos()
+
+ if x == you_x and y == you_y then
+ return
+ end
- if not mons then
- error("MonsterOnTrigger:event() didn't get a valid monster index")
+ if dgn.mons_at(x, y) then
+ return
end
- if mons.name == self.death_monster then
- local x, y = marker:pos()
- local you_x, you_y = you.pos()
-
- if x == you_x and y == you_y then
- return
- end
-
- -- you.losight() seems to be necessary if the player has been moved by
- -- a wizard command and then the marker triggered by another wizard
- -- command, since then no turns will have been taken and the LOS info
- -- won't have been updated.
- you.losight()
- local see_cell = you.see_cell(x, y)
-
- if (not dgn.create_monster(x, y, self.new_monster)) then
- return
- elseif self.message_seen ~= "" and see_cell then
- crawl.mpr(self.message_seen)
- elseif self.message_unseen ~= "" and not see_cell then
- crawl.mpr(self.message_unseen)
- end
-
- dgn.remove_listener(marker)
- dgn.remove_marker(marker)
+ -- you.losight() seems to be necessary if the player has been moved by
+ -- a wizard command and then the marker triggered by another wizard
+ -- command, since then no turns will have been taken and the LOS info
+ -- won't have been updated.
+ you.losight()
+ local see_cell = you.see_cell(x, y)
+
+ if (not dgn.create_monster(x, y, self.new_monster)) then
+ return
+ elseif self.message_seen ~= "" and see_cell then
+ crawl.mpr(self.message_seen)
+ elseif self.message_unseen ~= "" and not see_cell then
+ crawl.mpr(self.message_unseen)
end
+
+ self:remove(marker)
end
-function monster_on_trigger(pars)
- return MonsterOnTrigger:new(pars)
+function monster_on_death(pars)
+ local death_monster = pars.death_monster or pars.target
+
+ pars.death_monster = nil
+ pars.target = nil
+
+ local mod = MonsterOnTrigger:new(pars)
+
+ mod:add_triggerer(
+ DgnTriggerer:new {
+ type = "monster_dies",
+ target = death_monster
+ }
+ )
+
+ return mod
end
diff --git a/crawl-ref/source/dat/clua/lm_trig.lua b/crawl-ref/source/dat/clua/lm_trig.lua
new file mode 100644
index 0000000000..f435fd2424
--- /dev/null
+++ b/crawl-ref/source/dat/clua/lm_trig.lua
@@ -0,0 +1,352 @@
+------------------------------------------------------------------------------
+-- dungeon.lua:
+-- DgnTriggerers and triggerables:
+--
+-- This is similar to the overvable/observer design pattern: a triggerable
+-- class which does something and a triggerrer which sets it off. As an
+-- example, the ChangeFlags class (clua/lm_flags.lua), rather than having
+-- three subclasses (for monster death, feature change and item pickup)
+-- needs no subclasses, but is just one class which is given triggerers
+-- which listen for different events. Additionally, new types of triggerers
+-- can be developed and used without have to update the ChangeFlags code.
+--
+-- Unlike with the overvable/observer design pattern, each triggerer is
+-- associated with a signle triggerable, rather than there being one observable
+-- and multiple observers, since each triggerer might have a data payload which
+-- is meant to be different for each triggerable.
+--
+-- A triggerable class just needs to subclass Triggerable and define an
+-- "on_trigger" method.
+------------------------------------------------------------------------------
+
+Triggerable = { CLASS = "Triggerable" }
+Triggerable.__index = Triggerable
+
+function Triggerable:new()
+ local tr = { }
+ setmetatable(tr, self)
+ self.__index = self
+
+ tr.triggerers = { }
+ tr.dgn_trigs_by_type = { }
+
+ return tr
+end
+
+function Triggerable:add_triggerer(triggerer)
+ if not triggerer.type then
+ error("triggerer has no type")
+ end
+
+ table.insert(self.triggerers, triggerer)
+
+ if (triggerer.method == "dgn_event") then
+ local et = dgn.dgn_event_type(triggerer.type)
+ if not self.dgn_trigs_by_type[et] then
+ self.dgn_trigs_by_type[et] = {}
+ end
+
+ table.insert(self.dgn_trigs_by_type[et], #self.triggerers)
+ else
+ local method = triggerer.method or "(nil)"
+
+ local class
+ local meta = getmetatable(triggerer)
+ if not meta then
+ class = "(no meta table)"
+ elseif not meta.CLASS then
+ class = "(no class name)"
+ end
+
+ error("Unknown triggerer method '" .. method .. "' for trigger class '"
+ .. class .. "'")
+ end
+
+ triggerer:added(self)
+end
+
+function Triggerable:move(marker, dest, y)
+ local was_activated = self.activated
+
+ self:remove_all_triggerers(marker)
+
+ -- XXX: Are coordinated passed around as single objects?
+ if y then
+ marker:move(dest, y)
+ else
+ marker:move(dest)
+ end
+
+ if was_activated then
+ self.activated = false
+ self:activate(marker)
+ end
+end
+
+function Triggerable:remove(marker)
+ if self.removed then
+ error("Trigerrable already removed")
+ end
+
+ self:remove_all_triggerers(marker)
+ dgn.remove_marker(marker)
+
+ self.removed = true
+end
+
+function Triggerable:remove_all_triggerers(marker)
+ for _, trig in ipairs(self.triggerers) do
+ trig:remove(self, marker)
+ end
+end
+
+function Triggerable:activate(marker)
+ if self.removed then
+ error("Can't activate, trigerrable removed")
+ end
+
+ if self.activating then
+ error("Triggerable already activating")
+ end
+
+ if self.activated then
+ error("Triggerable already activated")
+ end
+
+ self.activating = true
+ for _, trig in ipairs(self.triggerers) do
+ trig:activate(self, marker)
+ end
+ self.activating = false
+ self.activated = true
+end
+
+function Triggerable:event(marker, ev)
+ local et = ev:type()
+
+ local trig_list = self.dgn_trigs_by_type[et]
+
+ if not trig_list then
+ local class = getmetatable(self).CLASS
+ local x, y = marker:pos()
+ local e_type = dgn.dgn_event_type(et)
+
+ error("Triggerable type " .. class .. " at (" ..x .. ", " .. y .. ") " ..
+ "has no triggerers for dgn_event " .. e_type )
+ end
+
+ for _, trig_idx in ipairs(trig_list) do
+ self.triggerers[trig_idx]:event(self, marker, ev)
+
+ if self.removed then
+ return
+ end
+ end
+end
+
+function Triggerable:write(marker, th)
+ file.marshall(th, #self.triggerers)
+ for _, trig in ipairs(self.triggerers) do
+ -- We'll be handling the de-serialization of the triggerer, so we need to
+ -- save the class name.
+ file.marshall(th, getmetatable(trig).CLASS)
+ trig:write(marker, th)
+ end
+
+ lmark.marshall_table(th, self.dgn_trigs_by_type)
+end
+
+function Triggerable:read(marker, th)
+ self.triggerers = {}
+
+ local num_trigs = file.unmarshall_number(th)
+ for i = 1, num_trigs do
+ -- Hack to let triggerer classes de-serialize themselves.
+ local trig_class = file.unmarshall_string(th)
+
+ -- _G is the global symbol table, and the class name of the triggerer is
+ -- the name of that class's class object
+ local trig_table = _G[trig_class].read(nil, marker, th)
+ table.insert(self.triggerers, trig_table)
+ end
+
+ self.dgn_trigs_by_type = lmark.unmarshall_table(th)
+
+ setmetatable(self, Triggerable)
+ return self
+end
+
+-------------------------------------------------------------------------------
+-- NOTE: The CLASS string of a triggerer class *MUST* be exactly the same as
+-- the triggerer class name, or it won't be able to deserialize properly.
+--
+-- NOTE: A triggerer shouldn't store a reference to the triggerable it
+-- belongs to, and if it does then it must not save/restore that reference.
+-------------------------------------------------------------------------------
+
+-- DgnTriggerer listens for dungeon events of these types:
+--
+-- * monster_dies: Waits for a monster to die. Needs the parameter
+-- "target", who's value is the name of the monster who's death
+-- we're wating for. Doesn't matter where the triggerable/marker
+-- is placed.
+--
+-- * feat_change: Waits for a cell's feature to change. Accepts the
+-- optional parameter "target", which if set delays the trigger
+-- until the feature the cell turns into contains the target as a
+-- substring. The triggerable/marker must be placed on top of the
+-- cell who's feature you wish to monitor.
+--
+-- * item_moved: Wait for an item to move from one cell to another.
+-- Needs the parameter "target", who's value is the name of the
+-- item that is being tracked. The triggerable/marker must be placed
+-- on top of the cell containing the item.
+--
+-- * item_pickup: Wait for an item to be picked up. Needs the parameter
+-- "target", who's value is the name of the item that is being tracked.
+-- The triggerable/marker must be placed on top of the cell containing
+-- the item. Automatically takes care of the item moving from one
+-- square to another without being picked up.
+
+DgnTriggerer = { CLASS = "DgnTriggerer" }
+DgnTriggerer.__index = DgnTriggerer
+
+function DgnTriggerer:new(pars)
+ pars = pars or {}
+
+ if not pars.type then
+ error("DgnTriggerer must have a type")
+ end
+
+ if pars.type == "monster_dies" or pars.type == "item_moved"
+ or pars.type == "item_pickup"
+ then
+ if not pars.target then
+ error(pars.type .. " DgnTriggerer must have parameter 'target'")
+ end
+ end
+
+ local tr = util.copy_table(pars)
+ setmetatable(tr, self)
+ self.__index = self
+
+ tr:setup()
+
+ return tr
+end
+
+function DgnTriggerer:setup()
+ self.method = "dgn_event"
+end
+
+function DgnTriggerer:added(triggerable)
+ if self.type == "item_pickup" then
+ -- Automatically move the triggerable if the item we're watching is moved
+ -- before it it's picked up.
+ local mover = util.copy_table(self)
+
+ mover.type = "item_moved"
+ mover.marker_mover = true
+
+ triggerable:add_triggerer( DgnTriggerer:new(mover) )
+ end
+end
+
+function DgnTriggerer:activate(triggerable, marker)
+ if not (triggerable.activated or triggerable.activating) then
+ error("DgnTriggerer:activate(): triggerable is not activated")
+ end
+
+ local et = dgn.dgn_event_type(self.type)
+
+ if (dgn.dgn_event_is_position(et)) then
+ dgn.register_listener(et, marker, marker:pos())
+ else
+ dgn.register_listener(et, marker)
+ end
+end
+
+function DgnTriggerer:remove(triggerable, marker)
+ if not triggerable.activated then
+ return
+ end
+
+ local et = dgn.dgn_event_type(self.type)
+
+ if (dgn.dgn_event_is_position(et)) then
+ dgn.remove_listener(marker, marker:pos())
+ else
+ dgn.remove_listener(marker)
+ end
+end
+
+function DgnTriggerer:event(triggerable, marker, ev)
+ if self.type == "monster_dies" then
+ local midx = ev:arg1()
+ local mons = dgn.mons_from_index(midx)
+
+ if not mons then
+ error("DgnTriggerer:event() didn't get a valid monster index")
+ end
+
+ if mons.name == self.target then
+ triggerable:on_trigger(self, marker, ev)
+ end
+ elseif self.type == "feat_change" then
+ if self.target and self.target ~= "" then
+ local feat = dgn.feature_name(dgn.grid(ev:pos()))
+ if not string.find(feat, self.target) then
+ return
+ end
+ end
+ triggerable:on_trigger(self, marker, ev)
+ elseif self.type == "item_moved" then
+ local obj_idx = ev:arg1()
+ local it = dgn.item_from_index(obj_idx)
+
+ if not it then
+ error("DgnTriggerer:event() didn't get a valid item index")
+ end
+
+ if item.name(it) == self.target then
+ if self.marker_mover then
+ -- We only exist to move the triggerable if the item moves
+ triggerable:move(marker, ev:dest())
+ else
+ triggerable:on_trigger(self, marker, ev)
+ end
+ end
+ elseif self.type == "item_pickup" then
+ local obj_idx = ev:arg1()
+ local it = dgn.item_from_index(obj_idx)
+
+ if not it then
+ error("DgnTriggerer:event() didn't get a valid item index")
+ end
+
+ if item.name(it) == self.target then
+ triggerable:on_trigger(self, marker, ev)
+ end
+ else
+ local e_type = dgn.dgn_event_type(et)
+
+ error("DgnTriggerer can't handle events of type " .. e_type)
+ end
+end
+
+function DgnTriggerer:write(marker, th)
+ -- Will always be "dgn_event", so we don't need to save it.
+ self.method = nil
+
+ lmark.marshall_table(th, self)
+end
+
+function DgnTriggerer:read(marker, th)
+ local tr = lmark.unmarshall_table(th)
+
+ setmetatable(tr, DgnTriggerer)
+
+ tr:setup()
+
+ return tr
+end
diff --git a/crawl-ref/source/dat/clua/luamark.lua b/crawl-ref/source/dat/clua/luamark.lua
index 43ec7f94ae..d987e88ef1 100644
--- a/crawl-ref/source/dat/clua/luamark.lua
+++ b/crawl-ref/source/dat/clua/luamark.lua
@@ -3,6 +3,8 @@
-- Lua map marker handling.
------------------------------------------------------------------------------
+require('clua/lm_trig.lua')
+
require('clua/lm_pdesc.lua')
require('clua/lm_1way.lua')
require('clua/lm_timed.lua')
diff --git a/crawl-ref/source/dat/clua/util.lua b/crawl-ref/source/dat/clua/util.lua
index 4894f1fc67..1ed3670172 100644
--- a/crawl-ref/source/dat/clua/util.lua
+++ b/crawl-ref/source/dat/clua/util.lua
@@ -306,3 +306,23 @@ function table_to_string(table, depth)
return str
end
+
+-- Copied from http://lua-users.org/wiki/CopyTable
+function util.copy_table(object)
+ local lookup_table = {}
+ local function _copy(object)
+ if type(object) ~= "table" then
+ return object
+ elseif lookup_table[object] then
+ return lookup_table[object]
+ end
+ local new_table = {}
+ lookup_table[object] = new_table
+ for index, value in pairs(object) do
+ new_table[_copy(index)] = _copy(value)
+ end
+ return setmetatable(new_table, getmetatable(object))
+ end
+ return _copy(object)
+end
+
diff --git a/crawl-ref/source/dat/hells.des b/crawl-ref/source/dat/hells.des
index 3eefbf5c19..6122bc4027 100644
--- a/crawl-ref/source/dat/hells.des
+++ b/crawl-ref/source/dat/hells.des
@@ -454,7 +454,7 @@ SUBST: 4 = 4:20 7
SUBST: 8 = 8o
COLOUR: o : cyan
: dis_setup(_G)
-MARKER: o = lua:monster_on_trigger { \
+MARKER: o = lua:monster_on_death { \
death_monster="Dispater", new_monster="generate_awake iron golem", \
message_seen="The iron statue comes to life!", \
message_unseen="You hear a grinding sound.", \
diff --git a/crawl-ref/source/dgnevent.h b/crawl-ref/source/dgnevent.h
index 6b37744583..8d77d03b96 100644
--- a/crawl-ref/source/dgnevent.h
+++ b/crawl-ref/source/dgnevent.h
@@ -28,7 +28,15 @@ enum dgn_event_type
DET_ITEM_PICKUP = 0x0200,
DET_ITEM_MOVED = 0x0400,
DET_FEAT_CHANGE = 0x0800,
- DET_WALL_HIT = 0x1000
+ DET_WALL_HIT = 0x1000,
+
+ DET_GLOBAL_MASK = DET_TURN_ELAPSED | DET_LEAVING_LEVEL
+ | DET_ENTERING_LEVEL | DET_ENTERED_LEVEL
+ | DET_MONSTER_DIED,
+
+ DET_POSITION_MASK = DET_MONSTER_MOVED | DET_PLAYER_MOVED
+ | DET_PLAYER_IN_LOS | DET_ITEM_PICKUP
+ | DET_ITEM_MOVED | DET_FEAT_CHANGE | DET_WALL_HIT
};
class dgn_event
diff --git a/crawl-ref/source/l_dgnevt.cc b/crawl-ref/source/l_dgnevt.cc
index c379bad317..f6d1b56aeb 100644
--- a/crawl-ref/source/l_dgnevt.cc
+++ b/crawl-ref/source/l_dgnevt.cc
@@ -119,9 +119,33 @@ static int dgn_dgn_event(lua_State *ls)
return (retvals);
}
+static dgn_event_type dgn_param_to_event_type(lua_State *ls, int n)
+{
+ if (lua_isnumber(ls, n))
+ return (static_cast<dgn_event_type>(luaL_checkint(ls, n)));
+ else if (lua_isstring(ls, n))
+ return dgn_event_type_by_name(lua_tostring(ls, n));
+ else
+ return (DET_NONE);
+}
+
+static int dgn_dgn_event_is_global(lua_State *ls)
+{
+ lua_pushboolean(ls, dgn_param_to_event_type(ls, 1) & DET_GLOBAL_MASK);
+ return (1);
+}
+
+static int dgn_dgn_event_is_position(lua_State *ls)
+{
+ lua_pushboolean(ls, dgn_param_to_event_type(ls, 1) & DET_POSITION_MASK);
+ return (1);
+}
+
const struct luaL_reg dgn_event_dlib[] =
{
-{ "dgn_event_type", dgn_dgn_event },
+{ "dgn_event_type", dgn_dgn_event },
+{ "dgn_event_is_global", dgn_dgn_event_is_global },
+{ "dgn_event_is_position", dgn_dgn_event_is_position},
{ NULL, NULL }
};