Adds tools/gen_sideb.py — one data-driven builder replacing the copy-pasted gen_missionN.py pattern — and tools/smoketest.sh, which reports PASS/FAIL/DISPLAY_UNAVAILABLE instead of the process-is-alive check that has produced false passes twice now. NOT verified in-game: the display went to sleep mid-session. Static checks all pass (luac, check-yaml 0 errors, map refresh, check_assets, make) and the already-verified m03 hangs identically, confirming it is environmental. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
2.4 KiB
Lua
84 lines
2.4 KiB
Lua
-- 05: ENGAGEMENT METRICS — hold the Transcoder for six minutes against waves.
|
|
|
|
HOLD_TIME = DateTime.Minutes(6)
|
|
Deadline = nil
|
|
Ticks = 0
|
|
WaveNumber = 0
|
|
|
|
-- escalating waves; each entry is the composition of one wave
|
|
WAVES = {
|
|
{ "djgrunt", "djgrunt", "spinner" },
|
|
{ "djgrunt", "djgrunt", "djgrunt", "spinner", "boombike" },
|
|
{ "spinner", "spinner", "stacktank", "djgrunt", "djgrunt" },
|
|
{ "stacktank", "stacktank", "spinner", "boombike", "djgrunt", "djgrunt" },
|
|
{ "stacktank", "stacktank", "basscannon", "spinner", "spinner", "djgrunt", "djgrunt" },
|
|
}
|
|
ENTRIES = { { 2, 2 }, { 46, 2 }, { 2, 46 } }
|
|
|
|
SendWave = function()
|
|
WaveNumber = WaveNumber + 1
|
|
local wave = WAVES[WaveNumber]
|
|
if wave == nil then
|
|
wave = WAVES[#WAVES]
|
|
end
|
|
local e = ENTRIES[(WaveNumber % #ENTRIES) + 1]
|
|
Media.DisplayMessage(UserInterface.GetFluentMessage("incoming-engagement"))
|
|
Utils.Do(wave, function(unit)
|
|
local a = Actor.Create(unit, true, {
|
|
Owner = Party,
|
|
Location = CPos.New(e[1] + Utils.RandomInteger(0, 3), e[2] + Utils.RandomInteger(0, 3)),
|
|
})
|
|
if a.HasProperty("Hunt") then
|
|
a.Hunt()
|
|
end
|
|
end)
|
|
end
|
|
|
|
WorldLoaded = function()
|
|
Stream = Player.GetPlayer("Player")
|
|
Party = Player.GetPlayer("Enemy")
|
|
InitObjectives(Stream)
|
|
|
|
HoldObjective = Stream.AddPrimaryObjective("hold-the-transcoder")
|
|
CampObjective = Stream.AddSecondaryObjective("clear-the-staging-camp")
|
|
Deadline = DateTime.GameTime + HOLD_TIME
|
|
|
|
Trigger.OnKilled(TheTranscoder, function()
|
|
Stream.MarkFailedObjective(HoldObjective)
|
|
end)
|
|
|
|
Trigger.OnAllKilled({ StagingCamp, scamp2 }, function()
|
|
Stream.MarkCompletedObjective(CampObjective)
|
|
end)
|
|
|
|
-- waves every 55 seconds, first one after 40
|
|
for i = 0, #WAVES - 1 do
|
|
Trigger.AfterDelay(DateTime.Seconds(40 + 55 * i), function()
|
|
if not Stream.IsObjectiveCompleted(HoldObjective)
|
|
and not Stream.IsObjectiveFailed(HoldObjective) then
|
|
SendWave()
|
|
end
|
|
end)
|
|
end
|
|
|
|
Trigger.AfterDelay(HOLD_TIME, function()
|
|
if not Stream.IsObjectiveFailed(HoldObjective) then
|
|
Stream.MarkCompletedObjective(HoldObjective)
|
|
UserInterface.SetMissionText("")
|
|
end
|
|
end)
|
|
end
|
|
|
|
Tick = function()
|
|
Ticks = Ticks + 1
|
|
if Ticks % 25 ~= 0 or HoldObjective == nil then
|
|
return
|
|
end
|
|
if Stream.IsObjectiveCompleted(HoldObjective) or Stream.IsObjectiveFailed(HoldObjective) then
|
|
return
|
|
end
|
|
local left = Deadline - DateTime.GameTime
|
|
if left < 0 then left = 0 end
|
|
UserInterface.SetMissionText("Processing complete in " .. Utils.FormatTime(left))
|
|
end
|