RECORDANDRONCO/mods/vinylgod/maps/m05/m05.lua
type-two 6847a47fea SIDE B campaign: three Stream missions (m04 Onboarding quota race, m05 Engagement Metrics wave defence, m06 The Shuffle finale)
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>
2026-07-25 23:35:15 +10:00

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