diff options
author | Matthew Cline <zelgadis@sourceforge.net> | 2009-11-07 01:33:42 -0800 |
---|---|---|
committer | Matthew Cline <zelgadis@sourceforge.net> | 2009-11-07 01:39:30 -0800 |
commit | 46317865c83248fe3064c612e1e2ad7911fc36ac (patch) | |
tree | 22ef2173411821fae18373a81f75795865cc8c1c /crawl-ref/source/dat | |
parent | 61f9fdc9f2a833685b80b6a574b3989cc32556ca (diff) | |
download | crawl-ref-46317865c83248fe3064c612e1e2ad7911fc36ac.tar.gz crawl-ref-46317865c83248fe3064c612e1e2ad7911fc36ac.zip |
lm_trig.lua: turn and enter event, listeners
Triggerable now handles listeners, and DgnTriggerer now handles turn and
entered_level events. For turn events, DgnTriggerer also handles
randomized, repeating countdowns.
Also added a TriggerableFunction class.
Diffstat (limited to 'crawl-ref/source/dat')
-rw-r--r-- | crawl-ref/source/dat/clua/lm_trig.lua | 203 |
1 files changed, 194 insertions, 9 deletions
diff --git a/crawl-ref/source/dat/clua/lm_trig.lua b/crawl-ref/source/dat/clua/lm_trig.lua index 17d2898d78..265c7b2245 100644 --- a/crawl-ref/source/dat/clua/lm_trig.lua +++ b/crawl-ref/source/dat/clua/lm_trig.lua @@ -10,7 +10,7 @@ -- 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 +-- Unlike with the observable/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. @@ -22,8 +22,8 @@ -- "FOO", then when triggered on_trigger() will be called at each marker -- on the level which has the property "slaved_to" equal to "FOO". A -- master marker can be slaved to itself to cause on_trigger() to be called --- at it's location. If the master marker has the property --- "single_random_slave" set to anythinb but the empty string ("") then +-- at its location. If the master marker has the property +-- "single_random_slave" set to anything but the empty string ("") then -- on each triggering only a single, randomly chosen slave will have -- on_trigger() called. -- @@ -31,10 +31,18 @@ -- and should have the same code regardless of whether or not it's a -- master or just a plain triggerable. If on_trigger() calls -- self:remove() while it's acting on a slave marker then the slave marker --- will be removed, and the master marker will only be removed when all --- slaves are gone. +-- will be removed, and if called on the master while the master is slaved +-- to itself it will stop acting as a slave, with the master marker being +-- automatically removed when all slaves are gone. ------------------------------------------------------------------------------ +-- XXX: Listeners and the whole master/slave business could be more +-- generally and more flexibly done by implementing a framework for +-- Lua defined/fired events, making Triggerable fire a Lua event every +-- time it's triggered, and then registering listeners for those Lua +-- events. However, we don't need that much flexibility yet, so it's +-- all handled inside of the Triggerable class. + Triggerable = { CLASS = "Triggerable" } Triggerable.__index = Triggerable @@ -48,6 +56,7 @@ function Triggerable:new(props) tr.props = props tr.triggerers = { } tr.dgn_trigs_by_type = { } + tr.listeners = { } return tr end @@ -64,6 +73,25 @@ function Triggerable:property(marker, pname) return self:unmangle(self.props[pname] or '') end +function Triggerable:set_property(pname, pval) + local old_val = self.props[pname] + self.props[pname] = pval + return old_val +end + +function Triggerable:add_listener(listener) + if type(listener) ~= "table" then + error("listener isn't a table") + end + if not listener.func then + error("listener has no func") + end + if type(listener.func) ~= "function" then + error("listener.func isn't a function") + end + table.insert(self.listeners, listener) +end + function Triggerable:add_triggerer(triggerer) if not triggerer.type then error("triggerer has no type") @@ -87,6 +115,8 @@ function Triggerable:add_triggerer(triggerer) class = "(no meta table)" elseif not meta.CLASS then class = "(no class name)" + else + class = meta.CLASS end error("Unknown triggerer method '" .. method .. "' for trigger class '" @@ -116,7 +146,8 @@ end function Triggerable:remove(marker) if self.removed then - error("Trigerrable already removed") + crawl.mpr("ERROR: Trigerrable already removed", "error") + return end if self.calling_slaves then @@ -181,6 +212,14 @@ function Triggerable:event(marker, ev) end function Triggerable:do_trigger(triggerer, marker, ev) + for _, listener in ipairs(self.listeners) do + listener:func(triggerable, triggerer, marker, ev) + end + + if triggerer.listener_only then + return + end + local master_name = self:property(marker, "master_name") if master_name == "" then @@ -198,9 +237,10 @@ function Triggerable:do_trigger(triggerer, marker, ev) return end + local master_pos = dgn.point(marker:pos()) local num_slaves = #slaves - if self:property("single_random_slave") ~= '' then + if self:property(marker, "single_random_slave") ~= '' then slaves = { slaves[ crawl.random2(#slaves) + 1 ] } end @@ -214,7 +254,7 @@ function Triggerable:do_trigger(triggerer, marker, ev) if self.want_remove then num_want_remove = num_want_remove + 1 - if slave_marker == marker then + if dgn.point(slave_marker:pos()) == master_pos then -- The master marker shouldn't be removed until the end, so -- simply stop being slaved. self.props.slaved_to = nil @@ -248,6 +288,7 @@ function Triggerable:write(marker, th) lmark.marshall_table(th, self.dgn_trigs_by_type) lmark.marshall_table(th, self.props) + lmark.marshall_table(th, self.listeners) end function Triggerable:read(marker, th) @@ -266,6 +307,7 @@ function Triggerable:read(marker, th) self.dgn_trigs_by_type = lmark.unmarshall_table(th) self.props = lmark.unmarshall_table(th) + self.listeners = lmark.unmarshall_table(th) setmetatable(self, Triggerable) return self @@ -273,6 +315,98 @@ end -------------------------- +-- Convenience functions, similar to the lmark ones in lm_mslav.lua + +Triggerable.slave_cookie = 0 + +function Triggerable.next_slave_id() + local slave_id = "marker_slave" .. Triggerable.slave_cookie + Triggerable.slave_cookie = Triggerable.slave_cookie + 1 + return slave_id +end + +function Triggerable.make_master(lmarker, slave_id) + -- NOTE: The master marker is slaved to itself. + lmarker:set_property("master_name", slave_id) + lmarker:set_property("slaved_to", slave_id) + + return lmarker +end + +function Triggerable.make_slave(slave_id) + return props_marker { slaved_to = slave_id } +end + +function Triggerable.synchronized_markers(master) + local first = true + local slave_id = lmark.next_slave_id() + return function () + if first then + first = false + return Triggerable.make_master(master, slave_id) + else + return Triggerable.make_slave(slave_id) + end + end +end + +-------------------------- + +-- A simple class to invoke an arbitrary Lua function. Should be split out +-- into own file if/when it becomes more complex. + +TriggerableFunction = util.subclass(Triggerable) +TriggerableFunction.CLASS = "TriggerableFunction" + +function TriggerableFunction:new(pars) + pars = pars or { } + + local tf = self.super.new(self) + + if not pars.func then + error("Must provide func to TriggerableMessage") + elseif type(pars.func) ~= "function" then + error("TriggerableMessage func must be function, not " .. type(pars.msg)) + end + + tf.func = pars.func + tf.repeated = pars.repeated + tf.data = pars.data + tf.props = pars.props or {} + + return tf +end + +function TriggerableFunction:on_trigger(triggerer, marker, ev) + self.func(data, self, triggerer, marker, ev) + + if not self.repeated then + self:remove(marker) + end +end + +function TriggerableFunction:write(marker, th) + TriggerableFunction.super.write(self, marker, th) + + file.marshall(th, self.func) + file.marshall_meta(th, self.repeated) + file.marshall_meta(th, self.data) +end + +function TriggerableFunction:read(marker, th) + TriggerableFunction.super.read(self, marker, th) + + self.func = file.unmarshall_fn(th) + self.repeated = file.unmarshall_meta(th) + self.data = file.unmarshall_meta(th) + + setmetatable(self, TriggerableFunction) + + return self +end + +-------------------------- + -- A simple class to give out messages. Should be split out into own -- file if/when it becomes more complex. @@ -375,12 +509,19 @@ end -- * player_los: Wait for the player to come into LOS of a cell, which -- must contain a notable feature.. The triggerable/marker must be -- placed on top of cell in question. +-- +-- * turn: Called once for each player turn that passes. +-- +-- * entered_level: Called when player enters the level, after all level +-- stetup code has completed. DgnTriggerer = { CLASS = "DgnTriggerer" } DgnTriggerer.__index = DgnTriggerer function DgnTriggerer:new(pars) - pars = pars or {} + if not pars then + error("No parameters provided") + end if not pars.type then error("DgnTriggerer must have a type") @@ -405,6 +546,14 @@ function DgnTriggerer:new(pars) tr:setup() + if tr.type == "turn" and (tr.delay or (tr.delay_min and tr.delay_max)) then + tr.delay_min = tr.delay_min or tr.delay or 1 + tr.delay_max = tr.delay_max or tr.delay + + tr.buildup_turns = 0 + tr.countdown = 0 + end + return tr end @@ -422,6 +571,10 @@ function DgnTriggerer:added(triggerable) mover.marker_mover = true triggerable:add_triggerer( DgnTriggerer:new(mover) ) + elseif self.type == "turn" then + if self.countdown then + self:reset_countdown() + end end end @@ -521,6 +674,38 @@ function DgnTriggerer:player_los(triggerable, marker, ev) triggerable:do_trigger(self, marker, ev) end +function DgnTriggerer:turn(triggerable, marker, ev) + if not self.countdown then + triggerable:do_trigger(self, marker, ev) + end + + self.countdown = self.countdown - ev:ticks() + + if self.countdown > 0 then + self.sub_type = "tick" + triggerable:do_trigger(self, marker, ev) + return + end + + self.sub_type = "countdown" + + while self.countdown <= 0 do + triggerable:do_trigger(self, marker, ev) + self.countdown = self.countdown + + crawl.random_range(self.delay_min, self.delay_max, 1) + end +end + +function DgnTriggerer:reset_countdown() + assert(self.type == "turn") + + self.countdown = crawl.random_range(self.delay_min, self.delay_max, 1) +end + +function DgnTriggerer:entered_level(triggerable, marker, ev) + triggerable:do_trigger(self, marker, ev) +end + ------------------- function DgnTriggerer:write(marker, th) |