Add grace period & polish to Situation Critical

This commit is contained in:
JovialFeline
2025-10-15 11:47:41 -04:00
committed by Gustas Kažukauskas
parent 0fb42b39d9
commit 3dcbdaa56e
5 changed files with 302 additions and 137 deletions

View File

@@ -41,7 +41,7 @@ actor-5tnk =
actor-5tnk-husk-name = Husk (Super Tank)
## mousetrap
## mousetrap, situation-critical
actor-gas-name = Gas
## sarin-gas-2-down-under

View File

@@ -341,6 +341,9 @@ destroy-bio-weapons-lab-missile-silos = Destroy the bio-weapons lab and the miss
destroy-all-sam-sites-strategic-bombers = Destroy all SAM sites on the island.
Our strategic bombers will finish the rest.
## situation-critical, soviet-10
run-for-it = RUN FOR IT!
## situation-critical, soviet-soldier-volkov-n-chitzkoi
volkov-survive = Volkov must survive.
@@ -393,7 +396,6 @@ destroy-stolen-convoy-truck = Destroy the stolen convoy truck
defend-forward-command-center = Defend our forward Command Center.
## soviet-10
run-for-it = RUN FOR IT!
convoy-commander = Convoy commander
we-are-surrounded = We are surrounded!
corridor-closes-in = Corridor closes in { $time }

View File

@@ -24,10 +24,10 @@ Players:
OwnsWorld: True
NonCombatant: True
Faction: england
PlayerReference@Creeps:
Name: Creeps
NonCombatant: True
Faction: england
PlayerReference@Ukraine:
Name: Ukraine
Faction: allies
Color: FF8800
PlayerReference@Turkey:
Name: Turkey
Faction: allies
@@ -363,8 +363,8 @@ Actors:
TeamOne1: e1
Location: 47,54
Owner: Turkey
Facing: 124
SubCell: 3
Facing: 128
SubCell: 4
TeamOne2: e2
Location: 46,54
Owner: Turkey
@@ -376,12 +376,12 @@ Actors:
TeamTwo1: e1
Location: 51,71
Owner: Turkey
Facing: 124
SubCell: 4
Facing: 128
SubCell: 5
TeamTwo2: e1
Location: 51,71
Owner: Turkey
Facing: 124
Facing: 128
SubCell: 1
TeamTwo3: e2
Location: 50,70
@@ -390,52 +390,52 @@ Actors:
TeamThree1: e1
Location: 68,79
Owner: Turkey
Facing: 252
Facing: 256
SubCell: 1
TeamThree2: e1
Location: 68,79
Owner: Turkey
Facing: 252
SubCell: 4
Facing: 256
SubCell: 5
TeamThree3: e2
Location: 68,79
Owner: Turkey
Facing: 252
Facing: 256
SubCell: 5
TeamFour1: e1
Location: 66,44
Owner: Turkey
Facing: 636
Facing: 640
SubCell: 1
TeamFour2: e1
Location: 66,43
Owner: Turkey
Facing: 636
SubCell: 3
Facing: 640
SubCell: 4
TeamFour3: e2
Location: 66,44
Owner: Turkey
Facing: 636
Facing: 640
SubCell: 2
TeamFive1: e1
Location: 75,58
Owner: Turkey
Facing: 508
SubCell: 4
Facing: 512
SubCell: 5
TeamFive2: e2
Location: 75,58
Owner: Turkey
Facing: 508
SubCell: 3
Facing: 512
SubCell: 4
TeamFive3: e1
Location: 75,58
Owner: Turkey
Facing: 508
Facing: 512
SubCell: 1
Shok1: shok
Location: 54,50
Owner: Turkey
Facing: 380
Facing: 384
SubCell: 1
Shok2: shok
Location: 51,62
@@ -444,72 +444,72 @@ Actors:
Shok3: shok
Location: 63,68
Owner: Turkey
Facing: 380
Facing: 384
SubCell: 1
Shok4: shok
Location: 69,52
Owner: Turkey
Facing: 636
Facing: 640
SubCell: 0
Actor123: ss
Location: 35,34
Owner: USSR
Facing: 764
Facing: 768
Actor124: ss
Location: 36,35
Owner: USSR
Facing: 764
Facing: 768
Actor125: ss
Location: 35,36
Owner: USSR
Facing: 764
Facing: 768
MSub1: msub
Location: 32,35
Owner: USSR
Facing: 764
Facing: 768
MSub2: msub
Location: 31,36
Owner: USSR
Facing: 764
Facing: 768
MSub3: msub
Location: 31,34
Owner: USSR
Facing: 764
Facing: 768
MSub4: msub
Location: 30,35
Owner: USSR
Facing: 764
Facing: 768
Actor129: ss
Location: 71,36
Owner: Turkey
Facing: 124
Facing: 128
Actor130: ss
Location: 81,60
Owner: Turkey
Facing: 892
Facing: 896
Actor131: ss
Location: 59,83
Owner: Turkey
Facing: 252
Facing: 256
Actor132: ss
Location: 34,57
Owner: Turkey
Facing: 124
Facing: 128
Actor133: ss
Location: 36,81
Owner: Turkey
Actor134: ss
Location: 74,87
Owner: Turkey
Facing: 892
Facing: 896
Actor135: ss
Location: 91,39
Owner: Turkey
Facing: 380
Facing: 384
Actor137: ss
Location: 34,35
Owner: USSR
Facing: 764
Facing: 768
DefaultCameraPosition: waypoint
Location: 32,35
Owner: Neutral
@@ -561,6 +561,15 @@ Actors:
TacticalNuke3: waypoint
Location: 57,61
Owner: Neutral
GasEntry: waypoint
Location: 60,57
Owner: Neutral
LaunchCameraMark: waypoint
Location: 59,57
Owner: Neutral
TacticalNukeCenter: waypoint
Location: 62,54
Owner: Neutral
Rules: ra|rules/campaign-rules.yaml, ra|rules/campaign-tooltips.yaml, ra|rules/campaign-palettes.yaml, rules.yaml

View File

@@ -5,6 +5,8 @@ World:
WinVideo: flare.vqa
LossVideo: nukestok.vqa
Briefing: A radical faction of our forces has stolen a biological weapon, threatening to use it on Allied emplacements. Since the weapon is highly unstable, it could destroy us all if released. \n\nThe facility must be destroyed, but the weapon must be neutralized first. Destroy the island's defenses, then use Volkov to assist our scientist in reaching the bio-research center. If either are killed before the weapon is neutralized, the mission is a failure.\n\nOnce the scientist completes his mission, destroy the base.
TimeLimitManager:
SkipTimerExpiredNotification: true
VOLK:
Inherits: GNRL
@@ -50,15 +52,29 @@ LST.Reinforcement:
RenderSprites:
Image: lst
Interactable:
Cargo:
BetweenUnloadDelay: 10
MSLO:
Power:
Amount: 0
-WithColoredOverlay@IDISABLE:
-NukePower:
NukePower:
DisplayTimerRelationships: Neutral
ExternalCondition@NODEMO:
Condition: demolish-disabled
Demolishable:
RequiresCondition: !demolish-disabled
DamageMultiplier@NODEMO:
Modifier: 0
RequiresCondition: demolish-disabled
SAM:
-WithColoredOverlay@IDISABLE:
RevealsShroud:
Range: 2c0
RevealsShroud@GAPGEN:
Range: 2c0
BADR.Bomber:
Aircraft:
@@ -68,16 +84,36 @@ BADR.Bomber:
RenderSprites:
Image: U2
FLARE:
Tooltip:
Name: actor-gas-name
powerproxy.parabombs:
AirstrikePower:
DisplayBeacon: False
CAMERA.Bomber:
Inherits: CAMERA
RevealsShroud:
Range: 12c0
DELPHI:
Tooltip:
Name: actor-scientist-name
Infiltrates:
Types: ScientistInfiltrate
ExternalCondition:
Condition: can-be-targeted
Targetable:
RequiresCondition: can-be-targeted
BIO:
Targetable:
TargetTypes: GroundActor, Structure, C4, DetonateAttack, ScientistInfiltrate
ExternalCondition@NODEMO:
Condition: demolish-disabled
Demolishable:
RequiresCondition: !demolish-disabled
DamageMultiplier@NODEMO:
Modifier: 0
RequiresCondition: demolish-disabled

View File

@@ -6,17 +6,38 @@
the License, or (at your option) any later version. For more
information, see COPYING.
]]
MissleSubs = { MSub1, MSub2, MSub3, MSub4 }
VolkovEntryPath = { LSTEntry.Location, LZ.Location }
VolkovandFriend = { "volk", "delphi" }
InsertionTransport = "lst.reinforcement"
SamSites = { Sam1, Sam2, Sam3, Sam4, Sam5, Sam6, Sam7, Sam8, Sam9, Sam10, Sam11, Sam12 }
PrimaryTargets = { BioLab, Silo1, Silo2 }
TimerTicks = DateTime.Minutes(8)
Shocktroopers = { Shok1, Shok2, Shok3, Shok4 }
local USSR = Player.GetPlayer("USSR")
local Turkey = Player.GetPlayer("Turkey")
local Ukraine = Player.GetPlayer("Ukraine")
InnerPatrolPaths =
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 },
@@ -24,7 +45,8 @@ InnerPatrolPaths =
{ InnerPatrol1.Location, InnerPatrol4.Location, InnerPatrol3.Location, InnerPatrol2.Location }
}
OuterPatrols =
---@type actor[][]
local OuterPatrols =
{
{ TeamOne1, TeamOne2, TeamOne3 },
{ TeamTwo1, TeamTwo2, TeamTwo3 },
@@ -33,7 +55,8 @@ OuterPatrols =
{ TeamFive1, TeamFive2, TeamFive3 }
}
OuterPatrolPaths =
---@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 },
@@ -42,7 +65,42 @@ OuterPatrolPaths =
{ OuterPatrol3.Location, OuterPatrol2.Location, OuterPatrol1.Location, OuterPatrol7.Location, OuterPatrol6.Location, OuterPatrol5.Location, OuterPatrol4.Location }
}
GroupPatrol = function(units, waypoints, delay)
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
@@ -51,30 +109,36 @@ GroupPatrol = function(units, waypoints, delay)
if stop then
return
end
if unit.Location == waypoints[i] then
local bool = Utils.All(units, function(actor) return actor.IsIdle end)
if bool then
stop = true
i = i + 1
if i > #waypoints then
i = 1
end
Trigger.AfterDelay(delay, function() stop = false 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
else
unit.AttackMove(waypoints[i])
Trigger.AfterDelay(delay, function() stop = false end)
end
end)
end)
end
StartPatrols = function()
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()
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)
@@ -82,8 +146,104 @@ StartPatrols = function()
end
end
LabInfiltrated = false
SetupTriggers = function()
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()
@@ -105,94 +265,52 @@ SetupTriggers = function()
end)
Trigger.OnAllKilled(PrimaryTargets, function()
if LaunchStarted then
return
end
if not BombersCalled then
ClearCountdown()
end
USSR.MarkCompletedObjective(DestroyFacility)
USSR.MarkCompletedObjective(VolkovSurvive)
Trigger.AfterDelay(DateTime.Seconds(2), function()
USSR.MarkCompletedObjective(VolkovSurvive)
end)
end)
Trigger.OnAllKilled(MissleSubs, function()
Trigger.OnAllKilled(MissileSubs, function()
if not VolkovArrived then
USSR.MarkFailedObjective(KillPower)
end
end)
end
SendInBombers = function()
if LabInfiltrated and USSR.IsObjectiveCompleted(KillSams) then
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
SendInVolkov = function()
if not VolkovArrived then
USSR.MarkCompletedObjective(KillPower)
Media.PlaySpeechNotification(USSR, "ReinforcementsArrived")
local teamVolkov = Reinforcements.ReinforceWithTransport(USSR, InsertionTransport, VolkovandFriend, VolkovEntryPath, { VolkovEntryPath[1] })[2]
VolkovArrived = true
Trigger.OnKilled(teamVolkov[1], function()
USSR.MarkFailedObjective(VolkovSurvive)
end)
Trigger.OnAddedToWorld(teamVolkov[1], function(a)
Media.DisplayMessage(UserInterface.GetFluentMessage("software-update-failed-manual-targets"), UserInterface.GetFluentMessage("volkov"))
end)
Trigger.OnAddedToWorld(teamVolkov[2], function(b)
Trigger.OnKilled(b, function()
if not LabInfiltrated then
USSR.MarkFailedObjective(InfiltrateLab)
end
end)
end)
end
end
Ticked = TimerTicks
Tick = function()
if Turkey.PowerState ~= "Normal" then
LowPowerDuration = LowPowerDuration + 1
SendInVolkov()
else
LowPowerDuration = 0
end
if Ticked > 0 then
if (Ticked % DateTime.Seconds(1)) == 0 then
Timer = UserInterface.GetFluentMessage("missiles-launch-in", { ["time"] = Utils.FormatTime(Ticked) })
UserInterface.SetMissionText(Timer, TimerColor)
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
Ticked = Ticked - 1
elseif Ticked == 0 then
UserInterface.SetMissionText(UserInterface.GetFluentMessage("too-late"), USSR.Color)
Turkey.MarkCompletedObjective(LaunchMissles)
TimerTicks = TimerTicks - 1
elseif TimerTicks == 0 and not BombersCalled and not USSR.IsObjectiveCompleted(DestroyFacility) then
Turkey.MarkCompletedObjective(GuardLab)
LaunchMissiles()
end
end
WorldLoaded = function()
USSR = Player.GetPlayer("USSR")
Turkey = Player.GetPlayer("Turkey")
InitObjectives(USSR)
LaunchMissles = AddPrimaryObjective(Turkey, "")
KillPower = AddPrimaryObjective(USSR, "kill-power")
InfiltrateLab = AddPrimaryObjective(USSR, "infiltrate-bio-weapons-lab-scientist")
DestroyFacility = AddPrimaryObjective(USSR, "destroy-bio-weapons-lab-missile-silos")
KillSams = AddSecondaryObjective(USSR, "destroy-all-sam-sites-strategic-bombers")
VolkovSurvive = AddPrimaryObjective(USSR, "volkov-survive")
Trigger.AfterDelay(DateTime.Minutes(3), function()
Media.PlaySpeechNotification(USSR, "WarningFiveMinutesRemaining")
end)
Trigger.AfterDelay(DateTime.Minutes(5), function()
Media.PlaySpeechNotification(USSR, "WarningThreeMinutesRemaining")
end)
Trigger.AfterDelay(DateTime.Minutes(7), function()
Media.PlaySpeechNotification(USSR, "WarningOneMinuteRemaining")
end)
StartPatrols()
SetupTriggers()
SetupStartTriggers()
Camera.Position = DefaultCameraPosition.CenterPosition
TimerColor = Turkey.Color
DateTime.TimeLimit = TimerTicks
end