diff --git a/src/evac.lua b/src/evac.lua index 82cb3fb..5d2dabb 100644 --- a/src/evac.lua +++ b/src/evac.lua @@ -1208,7 +1208,9 @@ Evac._internal.menu = { Gremlin.menu.updateF10(Evac.Id, Evac._internal.menu.commands, Evac._internal.utils.extractionUnitsToMenuUnits()) end, commands = {{ - text = 'Scan For Evacuation Beacons', + text = function(_unit) + return string.format('Scan For Evacuation Beacons (%i aboard)', Evac._internal.aircraft.countEvacuees(_unit)) + end, func = Evac.units.findEvacuees, args = {'{unit}:name'}, when = true @@ -1217,7 +1219,8 @@ Evac._internal.menu = { local _unitObj = Unit.getByName(_unit) if _unitObj ~= nil then local _zone = Evac._internal.utils.getUnitZone(_unit) - return string.format('Load %i Evacuees (%i In Zone)', math.min((Evac.carryLimits[_unitObj:getTypeName()] or 0), Gremlin.utils.countTableEntries(Evac._state.extractableNow[_zone])), Gremlin.utils.countTableEntries(Evac._state.extractableNow[_zone])) + local _seats = ((Evac.carryLimits[_unitObj:getTypeName()] or 0) - Evac._internal.aircraft.countEvacuees(_unit)) + return string.format('Load %i Evacuees (%i in area)', math.min(_seats, Gremlin.utils.countTableEntries(Evac._state.extractableNow[_zone])), Gremlin.utils.countTableEntries(Evac._state.extractableNow[_zone])) end end, func = Evac.units.loadEvacuees, @@ -1229,8 +1232,9 @@ Evac._internal.menu = { if _unitObj ~= nil then local _zone = Evac._internal.utils.getUnitZone(_unit) if _zone ~= nil then - local _seats = math.min((Evac.carryLimits[_unitObj:getTypeName()] or 0), Gremlin.utils.countTableEntries(Evac._state.extractableNow[_zone])) - return _seats > Evac._internal.aircraft.countEvacuees(_unit) + local _seats = ((Evac.carryLimits[_unitObj:getTypeName()] or 0) - Evac._internal.aircraft.countEvacuees(_unit)) + Gremlin.log.trace(Evac.Id, string.format('Recording Maximum Loadable : %i', _seats)) + return (math.min(_seats, Gremlin.utils.countTableEntries(Evac._state.extractableNow[_zone])) > 0) end end end @@ -1895,10 +1899,10 @@ function Evac:setup(config) Evac._internal.beacons.generateFMFrequencies() timer.scheduleFunction(function() - timer.scheduleFunction(Evac._internal.doSpawns, nil, timer.getTime() + 5) - timer.scheduleFunction(Evac._internal.beacons.killDead, nil, timer.getTime() + 5) - timer.scheduleFunction(Evac._internal.smoke.refresh, nil, timer.getTime() + 5) - timer.scheduleFunction(Evac._internal.menu.updateF10, nil, timer.getTime() + 5) + timer.scheduleFunction(Evac._internal.doSpawns, nil, timer.getTime() + 1) + timer.scheduleFunction(Evac._internal.beacons.killDead, nil, timer.getTime() + 1) + timer.scheduleFunction(Evac._internal.smoke.refresh, nil, timer.getTime() + 1) + timer.scheduleFunction(Evac._internal.menu.updateF10, nil, timer.getTime() + 1) end, nil, timer.getTime() + 1) for _name, _def in pairs(Evac._internal.handlers) do diff --git a/src/urgency.lua b/src/urgency.lua index 42ab884..cdafb66 100644 --- a/src/urgency.lua +++ b/src/urgency.lua @@ -101,15 +101,20 @@ Urgency._internal.doCountdowns = function() local _now = timer.getTime() + local _needsStart = {} for _name, _countdown in pairs(Urgency._state.countdowns.pending) do if (_countdown.startTrigger.type == 'time' and _countdown.startTrigger.value <= _now) or (_countdown.startTrigger.type == 'flag' and trigger.misc.getUserFlag(_countdown.startTrigger.value) ~= 0) then - Urgency._internal.startCountdown(_name) + table.insert(_needsStart, _name) Gremlin.log.trace(Urgency.Id, string.format('%s-Based Countdown Started : %s', _countdown.startTrigger.type, _name)) end end + for _, _name in pairs(_needsStart) do + Urgency._internal.startCountdown(_name) + end + local _needsEnd = {} for _name, _countdown in pairs(Urgency._state.countdowns.active) do local _endTime if _countdown.endTrigger.type == 'time' then @@ -133,10 +138,13 @@ Urgency._internal.doCountdowns = function() end if _endTime ~= nil and _now >= _endTime then - Urgency._internal.endCountdown(_name) + table.insert(_needsEnd, _name) Gremlin.log.trace(Urgency.Id, string.format('Time-Based Countdown Complete : %s', _name)) end end + for _, _name in pairs(_needsEnd) do + Urgency._internal.endCountdown(_name) + end Gremlin.log.trace(Urgency.Id, string.format('Time Checked Against Countdowns')) end diff --git a/src/waves.lua b/src/waves.lua index ba4be42..c5281ab 100644 --- a/src/waves.lua +++ b/src/waves.lua @@ -4,7 +4,12 @@ Waves = { config = { adminPilotNames = {}, - waves = {} + waves = { + time = {}, + flag = {}, + menu = {}, + event = {}, + } }, _state = { @@ -35,14 +40,22 @@ Waves._internal.spawnWave = function(_name, _wave) y = _alt, z = _pos2.y } - ---@diagnostic disable-next-line: deprecated - local _angle = math.atan2(_pos3.z, _pos3.x) + local _angle + if _groupData.route ~= nil and _groupData.route[2] ~= nil then + -- calculated + ---@diagnostic disable-next-line: deprecated + _angle = math.atan2(_pos3.z - (_groupData.route[2].y or 0), (_groupData.route[2].x or 0) - _pos3.x) + else + -- "random" + ---@diagnostic disable-next-line: deprecated + _angle = math.atan2(_pos3.z, _pos3.x) + end local _units = {} for _unitType, _unitCount in pairs(_groupData.units) do for i = 1, _unitCount do - local _xOffset = math.cos(_angle) * math.random(_groupData.scatter.min, _groupData.scatter.max) - local _yOffset = math.sin(_angle) * math.random(_groupData.scatter.min, _groupData.scatter.max) + local _xOffset = math.cos(_angle) * math.random(_groupData.scatter.min, _groupData.scatter.max) * (i - 1) + local _yOffset = math.sin(_angle) * math.random(_groupData.scatter.min, _groupData.scatter.max) * (i - 1) table.insert(_units, { type = _unitType, @@ -78,6 +91,7 @@ Waves._internal.spawnWave = function(_name, _wave) hidden = false, uncontrolled = false, uncontrollable = false, + manualHeading = true, units = _units, name = string.format('%s: %s', _name, _groupName), task = _groupTask, @@ -91,7 +105,7 @@ Waves._internal.spawnWave = function(_name, _wave) -- Apparently, ships in particular don't like having their AI messed with. -- We'll leave them be just following their routes. - if _group ~= nil and _groupData.category ~= Group.Category.SHIP and type(_groupData.orders) == 'table' and #_groupData.orders > 0 then + if _group ~= nil and _groupData.category ~= Group.Category.SHIP and type(_groupData.orders) == 'table' and Gremlin.utils.countTableEntries(_groupData.orders) > 0 then local _groupObj = Group.getByName(_group.name) if _groupObj ~= nil then @@ -120,7 +134,7 @@ Waves._internal.spawnWave = function(_name, _wave) end Waves._internal.getAdminUnits = function() - Gremlin.log.trace(Waves.Id, string.format('Scanning For Connected Admins')) + Gremlin.log.trace(Waves.Id, string.format('Scanning For Admin Units')) local _units = {} @@ -130,6 +144,8 @@ Waves._internal.getAdminUnits = function() if _unit ~= nil and _unit.isExist ~= nil and _unit:isExist() and _unit.getPlayerName ~= nil then local _pilot = _unit:getPlayerName() if _pilot ~= nil and _pilot ~= '' then + Gremlin.log.trace(Waves.Id, string.format('Found A Pilot : %s (in %s)', _pilot, _name)) + for _, _adminName in pairs(Waves.config.adminPilotNames) do if _adminName == _pilot then _units[_name] = _unit @@ -145,10 +161,33 @@ Waves._internal.getAdminUnits = function() return _units end +Waves._internal.initEvents = function() + Gremlin.log.trace(Waves.Id, string.format('Building Events')) + + for _name, _wave in pairs(Waves.config.waves.event) do + if _wave.trigger.type == 'event' then + Waves._internal.handlers[_name] = { + event = _wave.trigger.value.id, + fn = function(_event) + if _wave.trigger.value.filter(_event) then + Waves.config.waves.event[_name].trigger.fired = true + Waves._internal.spawnWave(_name, _wave) + end + + end + } + else + Gremlin.log.warn(Waves.Id, string.format('Non-event item in event list : %s', _name)) + end + end + + Gremlin.log.trace(Waves.Id, string.format('Events Ready')) +end + Waves._internal.initMenu = function() Gremlin.log.trace(Waves.Id, string.format('Building Menu')) - for _name, _wave in pairs(Waves.config.waves) do + for _name, _wave in pairs(Waves.config.waves.menu) do if _wave.trigger.type == 'menu' then table.insert(Waves._internal.menu, { text = _wave.trigger.value or ('Send In Reinforcements : ' .. _name), @@ -156,13 +195,15 @@ Waves._internal.initMenu = function() args = { _name }, when = { func = function(_name) - return not Waves._state.paused and not Waves.config.waves[_name].trigger.fired + return not Waves._state.paused and not Waves.config.waves.menu[_name].trigger.fired end, args = { _name }, comp = 'equal', value = true, } }) + else + Gremlin.log.warn(Waves.Id, string.format('Non-menu item in menu list : %s', _name)) end end @@ -178,11 +219,11 @@ Waves._internal.updateF10 = function() end Waves._internal.menuWave = function(_name) - if not Waves._state.paused and not Waves.config.waves[_name].trigger.fired then + if not Waves._state.paused and not Waves.config.waves.menu[_name].trigger.fired then Gremlin.log.trace(Waves.Id, string.format('Caling In Reinforcements : %s', _name)) - Waves.config.waves[_name].trigger.fired = true - Waves._internal.spawnWave(_name, Waves.config.waves[_name]) + Waves.config.waves.menu[_name].trigger.fired = true + Waves._internal.spawnWave(_name, Waves.config.waves.menu[_name]) Gremlin.log.trace(Waves.Id, string.format('Reinforcements En Route : %s', _name)) end @@ -194,14 +235,21 @@ Waves._internal.timeWave = function() if not Waves._state.paused then Gremlin.log.trace(Waves.Id, string.format('Checking On Next Wave')) - for _name, _wave in pairs(Waves.config.waves) do - if (_wave.trigger.type == 'time' and not _wave.trigger.fired and _wave.trigger.value <= timer.getTime()) - or (_wave.trigger.type == 'flag' and not _wave.trigger.fired and trigger.misc.getUserFlag(_wave.trigger.value) ~= 0) - then - Waves.config.waves[_name].trigger.fired = true + for _name, _wave in pairs(Waves.config.waves.time) do + if (_wave.trigger.type == 'time' and not _wave.trigger.fired and _wave.trigger.value <= timer.getTime()) then + Waves.config.waves.time[_name].trigger.fired = true + Waves._internal.spawnWave(_name, _wave) + end + end + + for _name, _wave in pairs(Waves.config.waves.flag) do + if (_wave.trigger.type == 'flag' and not _wave.trigger.fired and trigger.misc.getUserFlag(_wave.trigger.value) ~= 0) then + Waves.config.waves.flag[_name].trigger.fired = true Waves._internal.spawnWave(_name, _wave) end end + + Gremlin.log.trace(Waves.Id, string.format('All Ready Waves Spawned')) end end @@ -246,29 +294,7 @@ Waves._internal.menu = { }, } -Waves._internal.handlers = { - eventTriggers = { - event = -1, - fn = function(_event) - if not Waves._state.paused then - Gremlin.log.trace(Waves.Id, string.format('Checking Event Against Waves : %s', Gremlin.events.idToName[_event.id] or _event.id)) - - for _name, _wave in pairs(Waves.config.waves) do - if _wave.trigger.type == 'event' - and ( - _wave.trigger.value.id == _event.id - or _wave.trigger.value.id == -1 - ) - and _wave.trigger.value.filter(_event) - then - Waves.config.waves[_name].trigger.fired = true - Waves._internal.spawnWave(_name, _wave) - end - end - end - end - }, -} +Waves._internal.handlers = {} function Waves:setup(config) if config == nil then @@ -292,12 +318,20 @@ function Waves:setup(config) -- start configuration if not Waves._state.alreadyInitialized or config.forceReload then Waves.config.adminPilotNames = config.adminPilotNames or {} - Waves.config.waves = config.waves or {} + + local _waves = { time = {}, flag = {}, menu = {}, event = {} } + for _name, _wave in pairs(config.waves) do + if _wave.trigger.type ~= nil and _waves[_wave.trigger.type] ~= nil then + _waves[_wave.trigger.type][_name] = _wave + end + end + Waves.config.waves = _waves Gremlin.log.debug(Waves.Id, string.format('Configuration Loaded : %s', mist.utils.tableShowSorted(Waves.config))) end -- end configuration + Waves._internal.initEvents() Waves._internal.initMenu() timer.scheduleFunction(function() diff --git a/test/waves.lua b/test/waves.lua index 033e47a..4e90ecc 100644 --- a/test/waves.lua +++ b/test/waves.lua @@ -227,9 +227,16 @@ local setUp = function() } Waves.config.waves = { - [_testWaveTimedName] = mist.utils.deepCopy(_testWaveTimed), - [_testWaveMenuName] = mist.utils.deepCopy(_testWaveMenu), - [_testWaveEventName] = mist.utils.deepCopy(_testWaveEvent), + time = { + [_testWaveTimedName] = mist.utils.deepCopy(_testWaveTimed), + }, + flag = {}, + menu = { + [_testWaveMenuName] = mist.utils.deepCopy(_testWaveMenu), + }, + event = { + [_testWaveEventName] = mist.utils.deepCopy(_testWaveEvent), + }, } end @@ -239,7 +246,7 @@ local tearDown = function() Waves._state.alreadyInitialized = false Waves._state.paused = false Waves.config.adminPilotNames = {} - Waves.config.waves = {} + Waves.config.waves = { time = {}, flag = {}, menu = {}, event = {} } mist.nextUnitId = 1 mist.nextGroupId = 1 @@ -295,20 +302,20 @@ local function matcherForNameId(_tbl) } end -TestWavesInternalHandlers = { - setUp = setUp, - testEventTriggers = function() - -- INIT - -- N/A? +-- TestWavesInternalHandlers = { +-- setUp = setUp, +-- testEventTriggers = function() +-- -- INIT +-- -- N/A? - -- TEST - lu.assertEquals(Waves._internal.handlers.eventTriggers.fn({ id = world.event.S_EVENT_INVALID }), nil) +-- -- TEST +-- lu.assertEquals(Waves._internal.handlers.eventTriggers.fn({ id = world.event.S_EVENT_INVALID }), nil) - -- SIDE EFFECTS - -- N/A? - end, - tearDown = tearDown, -} +-- -- SIDE EFFECTS +-- -- N/A? +-- end, +-- tearDown = tearDown, +-- } TestWavesInternalMethods = { setUp = setUp, @@ -385,13 +392,13 @@ TestWavesInternalMethods = { end, testTimeWave = function() -- INIT - lu.assertEquals(Waves.config.waves[_testWaveTimedName].trigger.fired, nil) + lu.assertEquals(Waves.config.waves.time[_testWaveTimedName].trigger.fired, nil) -- TEST lu.assertEquals(Waves._internal.timeWave(), nil) -- SIDE EFFECTS - lu.assertEquals(Waves.config.waves[_testWaveTimedName].trigger.fired, true) + lu.assertEquals(Waves.config.waves.time[_testWaveTimedName].trigger.fired, true) end, testPause = function() -- INIT