hoggit/lib/spawner.lua
2018-12-25 00:07:58 -06:00

265 lines
11 KiB
Lua

--- spawner.lua
-- Spawning utilities for H.O.G.G.I.T. Framework
--Groups that should be auto-respawned along with their delay, and respawn percentage
HOGGIT.zombies = {}
-- Scheduled functions to check for respawns. This allows us to queue up functions to check for
-- dead zombies, and by keeping a reference to the scheduled functions ID we can cancel it if
-- another unit in the same group dies a short while after, and queue up another one, basically
-- batching the dead checks if you kill many units in the same group in quick succession.
HOGGIT.zombie_checks = {}
--[[
Auto generated spawner objects created from the .miz
Example usage: if you have a red coalition group in the .miz named "Bad Guys":
HOGGIT.spawners.red['Bad Guys']:Spawn()
]]
HOGGIT.spawners = {['neutral'] = {}, ['red'] = {}, ['blue'] = {}}
--[[
Spawner object that takes a group name from the ME.
Has flexible options for spawning a group once defined. One of these are created for each group that exists
in the .miz later on (stored in HOGGIT.spawners), but you can also define your own if you'd like to have groups
that have the same template in the .miz, but have different behaviors
(like spawning in a different location, or adding respawn options)
Example usage: With a group in your .miz with the name "Good Guys 1":
local good_guys_spawn = HOGGIT.Spawner("Good Guys 1")
local spawned_good_guys_group = good_guys_spawn:Spawn()
@param grpName Name of a group defined in the mission editor
@return HOGGIT.Spawner
]]
HOGGIT.Spawner = function(grpName)
local GroupName = grpName
--- holds the function and args that are called when this spawner spawns a group
local CallBack = {}
--- Table/Dictionary keyed by a group object with true value if group is alive.
-- Dead groups will be set to nil and garbage collected
local SpawnedGroups = {}
--- Respawn options
local zombie = false
local respawn_delay = 60
local partial_death_threshold_percent = nil
local partial_death_respawn_delay = respawn_delay
return {
--- Spawns a copy of the "template" group in the mission editor
-- Spawns the group at the same location and with the same parameters as the 'template' in the mission editor.
-- @param self HOGGIT.Spawner object
-- @return Group
Spawn = function(self)
local added_grp = Group.getByName(mist.cloneGroup(grpName, true).name)
if CallBack.func then
if not CallBack.args then CallBack.args = {} end
mist.scheduleFunction(CallBack.func, {added_grp, unpack(CallBack.args)}, timer.getTime() + 1)
end
SpawnedGroups[added_grp] = true
if zombie then
HOGGIT.setZombie(added_grp, self, true)
trigger.action.outText("Spawned new zombie " .. added_grp:getName(), 3)
end
return added_grp
end,
--- Same as 'Spawn', but takes an SSE Vec2 (table with an X and Y keys)
-- @param self HOGGIT.Spawner object
-- @param point Vec2
-- @return Group
SpawnAtPoint = function(self, point)
local vars = {
groupName = grpName,
point = point,
action = "clone"
}
local new_group = mist.teleportToPoint(vars)
if new_group then
local name = new_group.name
if CallBack.func then
if not CallBack.args then CallBack.args = {} end
mist.scheduleFunction(CallBack.func, {Group.getByName(name), unpack(CallBack.args)}, timer.getTime() + 1)
end
local added_grp = Group.getByName(name)
SpawnedGroups[added_grp] = true
if zombie then HOGGIT.setZombie(added_grp, self, true) end
return added_grp
else
trigger.action.outText("Error spawning " .. grpName, 15)
end
end,
--- Same as 'Spawn' but takes a zone name defined in the mission editor
-- @param self HOGGIT.Spawner object
-- @param zoneName Name of a zone that's defined in the mission editor
-- @return Group
SpawnInZone = function(self, zoneName)
local added_grp = Group.getByName(mist.cloneInZone(grpName, zoneName).name)
if CallBack.func then
if not CallBack.args then CallBack.args = {} end
mist.scheduleFunction(CallBack.func, {added_grp, unpack(CallBack.args)}, timer.getTime() + 1)
end
SpawnedGroups[added_grp] = true
if zombie then HOGGIT.setZombie(added_grp, self, true) end
return added_grp
end,
--- Add a function with arguments to be called when this Spawner actually spawns a unit
-- @param self HOGGIT.Spawner
-- @param f the function to be called, always called with spawned group as first param
-- @param args table of additional arguments that the callback function will be called with
OnSpawnGroup = function(self, f, args)
CallBack.func = f
CallBack.args = args
end,
--[[
Setup Zombies that respawn upon death!
Adds groups to the HOGGIT.zombies table for evaulation for respawning.
@param spawner HOGGIT.Spawner object
@param respawn_delay Amount of seconds group will respawn after emitting S_EVENT_DEAD event
@param partial_death_threshold_percent Percentage of dead units in group to consider whole group dead
@param partial_death_respawn_delay Amount of time that a partially dead unit that meets the threshold percent
to be considered completely dead takes to respawn in seconds, default: respawn_delay
]]
SetGroupRespawnOptions = function(
self,
respawn_delay,
partial_death_threshold_percent,
partial_death_respawn_delay) do
if partial_death_respawn_delay == nil and partial_death_threshold_percent ~= nil then
partial_death_respawn_delay = respawn_delay
end
self:SetZombieOptions({
['zombie'] = true,
['respawn_delay'] = respawn_delay,
['partial_death_threshold_percent'] = partial_death_threshold_percent,
['partial_death_respawn_delay'] = partial_death_respawn_delay
})
end
end,
SetZombieOptions = function(self, options)
zombie = options['zombie']
if zombie then
respawn_delay = options['respawn_delay']
partial_death_threshold_percent = options['partial_death_threshold_percent']
if options['partial_death_respawn_delay'] then
partial_death_respawn_delay = options['partial_death_respawn_delay']
else
partial_death_respawn_delay = respawn_delay
end
trigger.action.outText(GroupName .. " is now a zombie! Arggghhh!", 10)
end
end,
GetRespawnDelay = function(self, partial)
if partial then
return partial_death_respawn_delay
else
return respawn_delay
end
end,
GetPartialDeathThresholdPercent = function(self)
return partial_death_threshold_percent
end
}
end
--- Setup the default table of spawners, one for every group in the .miz
HOGGIT.SetupDefaultSpawners = function()
for cidx, coalitionName in ipairs({'neutral', 'red', 'blue'}) do
for gidx, group in pairs(coalition.getGroups(cidx - 1)) do
HOGGIT.spawners[coalitionName][Group.getName(group)] = HOGGIT.Spawner(Group.getName(group))
end
end
end
HOGGIT.setZombie = function(group, spawner, state)
if state then
HOGGIT.zombies[group:getName()] = spawner
else
HOGGIT.zombies[group:getName()] = nil
end
end
HOGGIT._deathHandler = function(event)
if event.id ~= world.event.S_EVENT_DEAD then return end
trigger.action.outText("SOMETHING DEAD YO", 10)
if not event.initiator then return end
if not event.initiator.getGroup then return end
local grp = event.initiator:getGroup():getName()
if grp then
trigger.action.outText("FOUND GROUP", 10)
if HOGGIT.zombies[grp] then
trigger.action.outText("CONFIRMED ZOMBIE", 10)
local spawner = HOGGIT.zombies[grp]
trigger.action.outText("FOUND SPAWNER", 10)
if HOGGIT.zombie_checks[spawner] then
trigger.action.outText("FOUND SPAWNER CHECK", 10)
local s_func_id = HOGGIT.zombie_checks[spawner]
mist.removeFunction(s_func_id)
trigger.action.outText("Removing dead check id ".. s_func_id .." for group: " .. grp, 10)
end
local new_func_id = mist.scheduleFunction(function()
trigger.action.outText("STARTING DEAD CHECK", 10)
local needs_respawn = false
local partial_respawn = false
if not HOGGIT.GroupIsAlive(grp) then
needs_respawn = true
trigger.action.outText("THEY DEAD, RESPAWNIN", 10)
elseif spawner:GetPartialDeathThresholdPercent() then
trigger.action.outText("CHECKING PERCENT", 10)
local group_obj = Group.getByName(grp)
local group_size = group_obj:getSize()
local initial_size = group_obj:getInitialSize()
local percent_alive = group_size / initial_size * 100
trigger.action.outText(percent_alive .. " percent alive. Threshold is " .. spawner:GetPartialDeathThresholdPercent(), 10)
if (100 - percent_alive) >= spawner:GetPartialDeathThresholdPercent() then
trigger.action.outText("TRIGGERING PARTIAL RESPAWNING", 10)
needs_respawn = true
partial_respawn = true
end
end
if needs_respawn then
trigger.action.outText("GROUP NEEDS RESPAWNING", 10)
local delay = spawner:GetRespawnDelay(partial_respawn)
trigger.action.outText("DELAY OF " .. delay, 10)
HOGGIT.zombies[grp] = nil
mist.scheduleFunction(function()
spawner.Spawn()
end, {}, timer.getTime() + delay)
end
HOGGIT.zombie_checks[spawner] = nil
end, {}, timer.getTime() + 10)
trigger.action.outText("Scheduled NEW dead check id ".. new_func_id .." for group: " .. grp, 10)
HOGGIT.zombie_checks[spawner] = new_func_id
else
end
end
end
mist.scheduleFunction(function()
trigger.action.outText("Showing current zombies", 10)
for i,data in pairs(HOGGIT.zombies) do
if HOGGIT.zombies[i] then trigger.action.outText("TOOOO", 10) end
if HOGGIT.zombies['IRAN gnd 1'] then trigger.action.outText("DOOOOOO", 10) end
end
end, {}, timer.getTime() + 40)
mist.addEventHandler(HOGGIT._deathHandler)