| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- -- One encounter's progress lives in transactional mission state, never module-local tables.
- local Encounter = {}
- Encounter.__index = Encounter
- function Encounter.new(tag, squads, cleared)
- assert(#squads > 0 and #squads <= 30, "encounter needs 1..30 squads")
- return setmetatable({tag = tag, squads = squads, cleared = cleared,
- full = (1 << #squads) - 1}, Encounter)
- end
- function Encounter:phase(state)
- return state:variable(self.tag .. ".phase") or 0
- end
- function Encounter:enter(context, state)
- if self:phase(state) ~= 0 then
- return false
- end
- -- Resolve everything before staging the first placement. Missing SDK rows are errors.
- local handles = {}
- for index, squad in ipairs(self.squads) do
- handles[index] = context:squad(squad.id)
- local count = 0
- for _, value in ipairs(handles[index].default_counts) do
- assert(value >= 0, "squad has unresolved authored counts: " .. squad.name)
- count = count + value
- end
- assert(count > 0, "encounter squad has no authored members: " .. squad.name)
- end
- context:set_variable(self.tag .. ".seen", 0)
- context:set_variable(self.tag .. ".alive", 0)
- context:set_variable(self.tag .. ".phase", 1)
- for _, handle in ipairs(handles) do
- handle:place{mode = context.sdk.squad_modes.replace}
- end
- return true
- end
- function Encounter:on_squad_state(context, state, event)
- if self:phase(state) ~= 1 then
- return
- end
- local bit
- for index, squad in ipairs(self.squads) do
- local slot = context:slot(squad.sensor)
- if event.registry_key == slot.registry_key and event.slot_type == slot.slot_type
- and event.slot_index == slot.slot_index then
- bit = 1 << (index - 1)
- break
- end
- end
- if bit == nil or event.alive_count == nil then
- return
- end
- local seen = state:variable(self.tag .. ".seen") or 0
- local alive = state:variable(self.tag .. ".alive") or 0
- if event.removal_flag then
- context:set_variable(self.tag .. ".seen", seen & ~bit)
- context:set_variable(self.tag .. ".alive", alive & ~bit)
- return
- end
- -- Initial empty Sense and an unloaded squad are not evidence of a combat clear.
- if event.alive_count > 0 or (event.previous_alive_count or 0) > 0 then
- seen = seen | bit
- end
- if event.alive_count > 0 then
- alive = alive | bit
- else
- alive = alive & ~bit
- end
- context:set_variable(self.tag .. ".seen", seen)
- context:set_variable(self.tag .. ".alive", alive)
- if seen == self.full and alive == 0 then
- context:set_variable(self.tag .. ".phase", 2)
- self.cleared(context, state)
- end
- end
- -- Called only by a confirmed checkpoint reset, not by region transit or script reload.
- function Encounter:reset(context)
- context:clear_variable(self.tag .. ".phase")
- context:clear_variable(self.tag .. ".seen")
- context:clear_variable(self.tag .. ".alive")
- end
- return Encounter
|