Files
OpenRA/mods/ra/maps/situation-critical/situation-critical.lua
let5sne.win10 9cf6ebb986
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled
Initial commit: OpenRA game engine
Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 21:46:54 +08:00

317 lines
9.4 KiB
Lua

--[[
Copyright (c) The OpenRA Developers and Contributors
This file is part of OpenRA, which is free software. It is made
available to you under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version. For more
information, see COPYING.
]]
local USSR = Player.GetPlayer("USSR")
local Turkey = Player.GetPlayer("Turkey")
local Ukraine = Player.GetPlayer("Ukraine")
local MissileSubs = { MSub1, MSub2, MSub3, MSub4 }
local VolkovEntryPath = { LSTEntry.Location, LZ.Location }
local VolkovAndFriend = { "delphi", "volk" }
local InsertionTransport = "lst.reinforcement"
local SamSites = { Sam1, Sam2, Sam3, Sam4, Sam5, Sam6, Sam7, Sam8, Sam9, Sam10, Sam11, Sam12 }
local PrimaryTargets = { BioLab, Silo1, Silo2 }
local Shocktroopers = { Shok1, Shok2, Shok3, Shok4 }
local GuardLab = AddPrimaryObjective(Turkey, "")
local KillPower = AddPrimaryObjective(USSR, "kill-power")
local InfiltrateLab = AddPrimaryObjective(USSR, "infiltrate-bio-weapons-lab-scientist")
local DestroyFacility = AddPrimaryObjective(USSR, "destroy-bio-weapons-lab-missile-silos")
local KillSams = AddSecondaryObjective(USSR, "destroy-all-sam-sites-strategic-bombers")
local VolkovSurvive = AddPrimaryObjective(USSR, "volkov-survive")
local LabInfiltrated = false
local VolkovArrived = false
local LaunchStarted = false
local BombersCalled = false
local LowPowerDuration = 0
local LowPowerThreshold = DateTime.Seconds(2)
local TimerTicks = DateTime.Minutes(8)
local TimerColor = Turkey.Color
---@type cpos[][]
local InnerPatrolPaths =
{
{ InnerPatrol2.Location, InnerPatrol3.Location, InnerPatrol4.Location, InnerPatrol1.Location },
{ InnerPatrol3.Location, InnerPatrol2.Location, InnerPatrol1.Location, InnerPatrol4.Location },
{ InnerPatrol4.Location, InnerPatrol1.Location, InnerPatrol2.Location, InnerPatrol3.Location },
{ InnerPatrol1.Location, InnerPatrol4.Location, InnerPatrol3.Location, InnerPatrol2.Location }
}
---@type actor[][]
local OuterPatrols =
{
{ TeamOne1, TeamOne2, TeamOne3 },
{ TeamTwo1, TeamTwo2, TeamTwo3 },
{ TeamThree1, TeamThree2, TeamThree3 },
{ TeamFour1, TeamFour2, TeamFour3 },
{ TeamFive1, TeamFive2, TeamFive3 }
}
---@type cpos[][]
local OuterPatrolPaths =
{
{ OuterPatrol1.Location, OuterPatrol2.Location, OuterPatrol3.Location, OuterPatrol4.Location, OuterPatrol5.Location, OuterPatrol6.Location, OuterPatrol7.Location },
{ OuterPatrol5.Location, OuterPatrol4.Location, OuterPatrol3.Location, OuterPatrol2.Location, OuterPatrol1.Location, OuterPatrol7.Location, OuterPatrol6.Location },
{ OuterPatrol6.Location, OuterPatrol7.Location, OuterPatrol1.Location, OuterPatrol2.Location, OuterPatrol3.Location, OuterPatrol4.Location, OuterPatrol5.Location },
{ OuterPatrol3.Location, OuterPatrol4.Location, OuterPatrol5.Location, OuterPatrol6.Location, OuterPatrol7.Location, OuterPatrol1.Location, OuterPatrol2.Location },
{ OuterPatrol3.Location, OuterPatrol2.Location, OuterPatrol1.Location, OuterPatrol7.Location, OuterPatrol6.Location, OuterPatrol5.Location, OuterPatrol4.Location }
}
local function IsVolkovNearBlast()
-- Within this radius, most normal infantry are left at 20% HP or less.
local cyborgsInDanger = Map.ActorsInCircle(TacticalNukeCenter.CenterPosition, WDist.FromCells(12), function(a)
return a.Type == "volk"
end)
return #cyborgsInDanger == 1
end
local function ClearCountdown()
UserInterface.SetMissionText("")
DateTime.TimeLimit = 0
TimerTicks = 0
end
---@param duration integer
local function SetRunText(duration)
local text = UserInterface.GetFluentMessage("run-for-it")
local seconds = math.floor(duration / DateTime.Seconds(1))
Media.PlaySoundNotification(USSR, "AlertBleep")
for i = 0, seconds - 1, 1 do
local color = Ukraine.Color
if i % 2 ~= 0 then
color = HSLColor.White
end
Trigger.AfterDelay(DateTime.Seconds(i), function() UserInterface.SetMissionText(text, color) end)
end
Trigger.AfterDelay(duration, function() UserInterface.SetMissionText("") end)
end
---@param units actor[]
---@param path cpos[]
---@param delay integer
local function GroupPatrol(units, path, delay)
local i = 1
local stop = false
Utils.Do(units, function(unit)
Trigger.OnIdle(unit, function()
if stop then
return
end
if unit.Location ~= path[i] then
unit.AttackMove(path[i])
return
end
local bool = Utils.All(units, function(actor) return actor.IsIdle or actor.IsDead end)
if bool then
stop = true
i = i + 1
if i > #path then
i = 1
end
Trigger.AfterDelay(delay, function() stop = false end)
end
end)
end)
end
local function StartPatrols()
for i = 1, 5 do
GroupPatrol(OuterPatrols[i], OuterPatrolPaths[i], DateTime.Seconds(3))
end
for i = 1, 4 do
Trigger.AfterDelay(DateTime.Seconds(3 * (i - 1)), function()
if Shocktroopers[i].IsDead then
return
end
Trigger.OnIdle(Shocktroopers[i], function()
Shocktroopers[i].Patrol(InnerPatrolPaths[i])
end)
end)
end
end
local function LaunchMissiles()
if LaunchStarted then
return
end
LaunchStarted = true
UserInterface.SetMissionText(UserInterface.GetFluentMessage("too-late"), USSR.Color)
Actor.Create("camera", true, { Owner = USSR, Location = LaunchCameraMark.Location })
-- Edge case that seems impossible in RA1.
if Silo1.IsDead and Silo2.IsDead and not BioLab.IsDead then
Actor.Create("flare", true, { Owner = Ukraine, Location = GasEntry.Location })
BioLab.GrantCondition("demolish-disabled")
return
end
Camera.Position = LaunchCameraMark.CenterPosition
local delay = 15
Utils.Do({ Silo1, Silo2 }, function(silo)
Trigger.AfterDelay(delay, function()
if silo.IsDead then
return
end
silo.ActivateNukePower(DefaultCameraPosition.Location)
end)
if not silo.IsDead then
silo.GrantCondition("demolish-disabled")
delay = delay + 15
end
end)
end
local function SendInBombers()
if LaunchStarted or not LabInfiltrated or not USSR.IsObjectiveCompleted(KillSams) then
return
end
BombersCalled = true
ClearCountdown()
local bombDelay = DateTime.Seconds(2)
Trigger.AfterDelay(DateTime.Seconds(1), function()
Actor.Create("camera.bomber", true, { Owner = USSR, Location = TacticalNukeCenter.Location })
end)
if IsVolkovNearBlast() then
bombDelay = DateTime.Seconds(8)
SetRunText(bombDelay + DateTime.Seconds(1))
end
Trigger.AfterDelay(bombDelay, function()
local proxy = Actor.Create("powerproxy.parabombs", false, { Owner = USSR })
proxy.TargetAirstrike(TacticalNuke1.CenterPosition, Angle.SouthWest)
proxy.TargetAirstrike(TacticalNuke2.CenterPosition, Angle.SouthWest)
proxy.TargetAirstrike(TacticalNuke3.CenterPosition, Angle.SouthWest)
proxy.Destroy()
end)
end
local function SendInVolkov()
if VolkovArrived or LowPowerDuration < LowPowerThreshold then
return
end
VolkovArrived = true
USSR.MarkCompletedObjective(KillPower)
Media.PlaySpeechNotification(USSR, "ReinforcementsArrived")
Beacon.New(USSR, LZ.CenterPosition, DateTime.Seconds(6))
local teamVolkov = Reinforcements.ReinforceWithTransport(USSR, InsertionTransport, VolkovAndFriend, VolkovEntryPath, { VolkovEntryPath[1] })[2]
local scientist, volkov = teamVolkov[1], teamVolkov[2]
Trigger.OnKilled(volkov, function()
USSR.MarkFailedObjective(VolkovSurvive)
end)
Trigger.OnAddedToWorld(volkov, function()
Media.DisplayMessage(UserInterface.GetFluentMessage("software-update-failed-manual-targets"), UserInterface.GetFluentMessage("volkov"))
end)
Trigger.OnAddedToWorld(scientist, function(b)
Trigger.OnKilled(b, function()
if not LabInfiltrated then
USSR.MarkFailedObjective(InfiltrateLab)
end
end)
Trigger.AfterDelay(DateTime.Seconds(5), function()
if not scientist.IsDead then
scientist.GrantCondition("can-be-targeted")
end
end)
end)
end
local function SetupStartTriggers()
Trigger.OnAllKilled(SamSites, function()
USSR.MarkCompletedObjective(KillSams)
SendInBombers()
end)
Trigger.OnInfiltrated(BioLab, function()
Media.DisplayMessage(UserInterface.GetFluentMessage("plans-stolen-erase-data"), UserInterface.GetFluentMessage("scientist"))
Trigger.AfterDelay(DateTime.Seconds(5), function()
USSR.MarkCompletedObjective(InfiltrateLab)
LabInfiltrated = true
SendInBombers()
end)
end)
Trigger.OnKilled(BioLab, function()
if not LabInfiltrated then
USSR.MarkFailedObjective(InfiltrateLab)
end
end)
Trigger.OnAllKilled(PrimaryTargets, function()
if LaunchStarted then
return
end
if not BombersCalled then
ClearCountdown()
end
USSR.MarkCompletedObjective(DestroyFacility)
Trigger.AfterDelay(DateTime.Seconds(2), function()
USSR.MarkCompletedObjective(VolkovSurvive)
end)
end)
Trigger.OnAllKilled(MissileSubs, function()
if not VolkovArrived then
USSR.MarkFailedObjective(KillPower)
end
end)
end
Tick = function()
if Turkey.PowerState ~= "Normal" then
LowPowerDuration = LowPowerDuration + 1
SendInVolkov()
else
LowPowerDuration = 0
end
if TimerTicks > 0 then
if (TimerTicks % DateTime.Seconds(1)) == 0 then
local text = UserInterface.GetFluentMessage("missiles-launch-in", { ["time"] = Utils.FormatTime(TimerTicks) })
UserInterface.SetMissionText(text, TimerColor)
end
TimerTicks = TimerTicks - 1
elseif TimerTicks == 0 and not BombersCalled and not USSR.IsObjectiveCompleted(DestroyFacility) then
Turkey.MarkCompletedObjective(GuardLab)
LaunchMissiles()
end
end
WorldLoaded = function()
InitObjectives(USSR)
StartPatrols()
SetupStartTriggers()
Camera.Position = DefaultCameraPosition.CenterPosition
DateTime.TimeLimit = TimerTicks
end