Initial commit: OpenRA game engine
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled

Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
let5sne.win10
2026-01-10 21:46:54 +08:00
commit 9cf6ebb986
4065 changed files with 635973 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,7 @@
## rules.yaml
briefing =
There is a special cargo that needs to be transported to a nearby Soviet base in the northeast.
Make sure the trucks reach their destination intact. Along the way, there is a bridge which the Allies have likely destroyed.
Use the Naval options at your disposal. Our attack subs will make short work of any Allied boats you discover.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,512 @@
MapFormat: 12
RequiresMod: ra
Title: 06b: Bridge over the River VizchGoi
Author: Westwood Studios
Tileset: TEMPERAT
MapSize: 128,128
Bounds: 18,10,81,58
Visibility: MissionSelector
Categories: Campaign
LockPreview: True
Players:
PlayerReference@Neutral:
Name: Neutral
OwnsWorld: True
NonCombatant: True
Faction: allies
PlayerReference@Greece:
Name: Greece
Faction: allies
Color: E2E6F5
Enemies: USSR
Bot: campaign
PlayerReference@USSR:
Name: USSR
AllowBots: False
Playable: True
Required: True
LockFaction: True
Faction: soviet
LockColor: True
Color: FE1100
LockSpawn: True
LockTeam: True
Enemies: Greece
Actors:
Actor5: sbag
Location: 62,37
Owner: Greece
Actor6: sbag
Location: 63,37
Owner: Greece
Actor7: sbag
Location: 64,37
Owner: Greece
Actor8: sbag
Location: 65,37
Owner: Greece
Actor9: sbag
Location: 65,38
Owner: Greece
Actor10: sbag
Location: 66,38
Owner: Greece
Actor11: sbag
Location: 71,38
Owner: Greece
Actor12: sbag
Location: 72,38
Owner: Greece
Actor13: sbag
Location: 73,38
Owner: Greece
Actor14: sbag
Location: 74,38
Owner: Greece
Actor15: sbag
Location: 82,38
Owner: Greece
Actor16: sbag
Location: 83,38
Owner: Greece
Actor17: sbag
Location: 70,39
Owner: Greece
Actor18: sbag
Location: 71,39
Owner: Greece
Actor19: sbag
Location: 83,39
Owner: Greece
Actor20: sbag
Location: 83,40
Owner: Greece
Actor21: sbag
Location: 57,43
Owner: Greece
Actor22: sbag
Location: 58,43
Owner: Greece
Actor23: sbag
Location: 57,44
Owner: Greece
Actor24: sbag
Location: 80,46
Owner: Greece
Actor25: sbag
Location: 80,47
Owner: Greece
Actor26: sbag
Location: 63,48
Owner: Greece
Actor27: sbag
Location: 64,48
Owner: Greece
Actor28: sbag
Location: 79,48
Owner: Greece
Actor29: sbag
Location: 80,48
Owner: Greece
Actor30: sbag
Location: 64,49
Owner: Greece
Actor31: sbag
Location: 64,50
Owner: Greece
Actor32: sbag
Location: 65,50
Owner: Greece
Actor33: t17
Location: 66,28
Owner: Neutral
Actor34: t08
Location: 83,35
Owner: Neutral
Actor35: t16
Location: 82,34
Owner: Neutral
Actor36: tc04
Location: 63,26
Owner: Neutral
Actor37: tc05
Location: 67,30
Owner: Neutral
Actor38: tc01
Location: 79,32
Owner: Neutral
Actor39: tc04
Location: 34,20
Owner: Neutral
Actor40: tc05
Location: 82,15
Owner: Neutral
Actor41: tc04
Location: 90,17
Owner: Neutral
Actor42: tc01
Location: 92,18
Owner: Neutral
Actor43: tc03
Location: 82,14
Owner: Neutral
Actor44: tc01
Location: 95,15
Owner: Neutral
Actor45: tc02
Location: 83,12
Owner: Neutral
Actor46: t17
Location: 85,12
Owner: Neutral
Actor47: t16
Location: 86,13
Owner: Neutral
Actor48: t16
Location: 92,24
Owner: Neutral
Actor49: t14
Location: 93,24
Owner: Neutral
Actor50: t11
Location: 93,21
Owner: Neutral
Actor51: tc05
Location: 54,23
Owner: Neutral
Actor52: tc05
Location: 74,46
Owner: Neutral
Actor53: tc04
Location: 80,43
Owner: Neutral
Actor54: tc02
Location: 81,42
Owner: Neutral
Actor55: tc01
Location: 73,40
Owner: Neutral
Actor56: t17
Location: 71,40
Owner: Neutral
Actor57: t15
Location: 80,36
Owner: Neutral
Actor58: t12
Location: 31,57
Owner: Neutral
Actor59: tc04
Location: 54,26
Owner: Neutral
Actor60: t14
Location: 50,31
Owner: Neutral
Actor62: t05
Location: 48,39
Owner: Neutral
Actor63: t06
Location: 47,39
Owner: Neutral
Actor66: t08
Location: 49,38
Owner: Neutral
Actor68: t08
Location: 50,39
Owner: Neutral
Actor69: tc05
Location: 35,58
Owner: Neutral
Actor70: tc04
Location: 44,55
Owner: Neutral
Actor71: tc02
Location: 34,66
Owner: Neutral
Actor72: tc04
Location: 56,65
Owner: Neutral
Actor73: t17
Location: 49,66
Owner: Neutral
Actor74: t16
Location: 50,65
Owner: Neutral
Actor75: tc02
Location: 54,66
Owner: Neutral
Actor76: tc01
Location: 52,66
Owner: Neutral
Actor77: tc02
Location: 58,58
Owner: Neutral
Actor78: t15
Location: 59,60
Owner: Neutral
Actor79: t14
Location: 48,38
Owner: Neutral
Actor81: tc03
Location: 49,20
Owner: Neutral
Actor82: tc02
Location: 36,19
Owner: Neutral
Actor83: t15
Location: 39,25
Owner: Neutral
Actor84: t08
Location: 38,27
Owner: Neutral
Actor85: t08
Location: 40,25
Owner: Neutral
Actor86: t13
Location: 49,40
Owner: Neutral
Actor88: t02
Location: 50,37
Owner: Neutral
Actor90: hbox
Location: 58,46
Owner: Greece
Actor91: hbox
Location: 62,47
Owner: Greece
Actor92: gun
Location: 57,46
Owner: Greece
Facing: 640
TurretFacing: 768
Actor93: gun
Location: 61,48
Owner: Greece
Facing: 640
TurretFacing: 768
Actor103: 2tnk
Location: 76,30
Owner: Greece
Facing: 384
Actor104: 2tnk
Location: 76,29
Owner: Greece
Facing: 384
Actor106: arty
Location: 64,46
Owner: Greece
Facing: 384
Stance: Defend
Actor107: arty
Location: 58,44
Owner: Greece
Facing: 384
Stance: Defend
Actor110: 3tnk
Location: 43,63
Owner: USSR
Facing: 896
Actor111: 3tnk
Location: 44,62
Owner: USSR
Facing: 896
Actor112: v2rl
Location: 41,64
Owner: USSR
Facing: 896
Actor113: v2rl
Location: 43,64
Owner: USSR
Facing: 896
Actor115: 2tnk
Location: 75,30
Owner: Greece
Facing: 384
Actor116: 2tnk
Location: 75,31
Owner: Greece
Facing: 384
Actor117: 2tnk
Location: 83,19
Owner: Greece
Facing: 384
Actor118: 2tnk
Location: 88,21
Owner: Greece
Facing: 384
Actor119: 2tnk
Location: 62,44
Owner: Greece
Facing: 384
Actor120: 1tnk
Location: 59,44
Owner: Greece
Facing: 384
Actor121: 1tnk
Location: 63,46
Owner: Greece
Facing: 384
BridgeBreaker: ca
Location: 87,29
Owner: Greece
Facing: 128
BridgeBreaker2: ca
Location: 87,31
Owner: Greece
Facing: 384
ResponseCruiser: ca
Location: 75,25
Owner: Greece
Facing: 128
Actor126: pt
Location: 71,16
Owner: Greece
Facing: 128
Actor127: pt
Location: 65,20
Owner: Greece
Facing: 128
Actor128: dd
Location: 70,20
Owner: Greece
Facing: 128
Actor129: waypoint
Location: 46,60
Owner: Neutral
Actor144: silo
Owner: Greece
Location: 73,46
Actor145: silo
Owner: Greece
Location: 73,47
Actor146: silo
Owner: Greece
Location: 73,48
Actor147: silo
Owner: Greece
Location: 73,49
Actor148: sbag
Owner: Greece
Location: 65,51
waypoint69: waypoint
Location: 21,28
Owner: Neutral
waypoint78: waypoint
Location: 83,23
Owner: Neutral
waypoint79: waypoint
Location: 87,19
Owner: Neutral
waypoint80: waypoint
Location: 84,22
Owner: Neutral
waypoint81: waypoint
Location: 76,22
Owner: Neutral
waypoint82: waypoint
Location: 87,29
Owner: Neutral
waypoint94: waypoint
Location: 49,16
Owner: Neutral
AGun: agun
Owner: Greece
Location: 69,49
TurretFacing: 368
APCWaypoint1: waypoint
Location: 39,62
Owner: Neutral
APCWaypoint2: waypoint
Location: 46,66
Owner: Neutral
Apwr: apwr
Location: 76,41
Owner: Greece
Apwr2: apwr
Location: 77,45
Owner: Greece
AttackWaypoint1: waypoint
Owner: Neutral
Location: 26,55
AttackWaypoint2: waypoint
Owner: Neutral
Location: 41,55
Tent: tent
Location: 67,45
Owner: Greece
CameraStart: camera
Location: 46,61
Owner: Neutral
CYard: fact
Location: 70,46
Owner: Greece
CYardLocation: waypoint
Owner: Neutral
Location: 71,49
Dome: dome
Location: 67,49
Owner: Greece
EnemyEntry1: waypoint
Owner: Neutral
Location: 53,10
EnemyEntry2: waypoint
Owner: Neutral
Location: 18,62
EnemyRally1: waypoint
Owner: Neutral
Location: 53,31
EnemyRally2: waypoint
Owner: Neutral
Location: 25,62
Harvester: harv
Location: 77,39
Owner: Greece
IntroEnemy1: 1tnk
Location: 55,49
Owner: Greece
Facing: 384
IntroEnemy2: 2tnk
Location: 56,50
Owner: Greece
Facing: 384
IntroEnemy3: 1tnk
Location: 58,50
Owner: Greece
Facing: 384
Mcv: mcv
Location: 51,55
Owner: USSR
Health: 82
Facing: 384
McvWaypoint: waypoint
Location: 39,67
Owner: Neutral
Proc: proc
Location: 71,41
Owner: Greece
FreeActor: False
Truck1: truk
Location: 42,66
Owner: USSR
Facing: 896
Truck2: truk
Location: 39,65
Owner: USSR
Facing: 896
Weap: weap
Location: 67,41
Owner: Greece
Rules: ra|rules/campaign-rules.yaml, ra|rules/campaign-tooltips.yaml, ra|rules/campaign-palettes.yaml, rules.yaml
FluentMessages: ra|fluent/lua.ftl, ra|fluent/campaign.ftl, map.ftl

View File

@@ -0,0 +1,146 @@
Player:
PlayerResources:
DefaultCash: 11500
World:
LuaScript:
Scripts: campaign.lua, utils.lua, soviet06b.lua, soviet06b-AI.lua, soviet06b-reinforcements_teams.lua
MissionData:
Briefing: briefing
BriefingVideo: soviet6.vqa
StartVideo: onthprwl.vqa
WinVideo: sitduck.vqa
LossVideo: dpthchrg.vqa
ScriptLobbyDropdown@difficulty:
ID: difficulty
Label: dropdown-difficulty.label
Description: dropdown-difficulty.description
Values:
easy: options-difficulty.easy
normal: options-difficulty.normal
hard: options-difficulty.hard
Default: normal
APWR:
Buildable:
Prerequisites: ~structures.allies
ARTY:
Buildable:
Prerequisites: ~vehicles.allies, ~techlevel.low
ATEK:
Buildable:
Prerequisites: ~disabled
BRIK:
Buildable:
Prerequisites: ~disabled
TSLA:
Buildable:
Prerequisites: ~disabled
SAM:
Buildable:
Prerequisites: ~disabled
IRON:
Buildable:
Prerequisites: ~disabled
MECH:
Buildable:
Prerequisites: ~disabled
MSLO:
Buildable:
Prerequisites: ~disabled
E3:
Buildable:
Prerequisites: ~tent
E7:
Buildable:
Prerequisites: ~disabled
SHOK:
Buildable:
Prerequisites: ~disabled
SPY:
Buildable:
Prerequisites: ~disabled
THF:
Buildable:
Prerequisites: ~disabled
MIG:
Buildable:
Prerequisites: ~disabled
FTRK:
Buildable:
Prerequisites: ~disabled
2TNK:
Buildable:
Prerequisites: ~vehicles.allies, ~techlevel.low
4TNK:
Buildable:
Prerequisites: ~disabled
APC:
Buildable:
Prerequisites: ~disabled
TRUK:
-SpawnActorOnDeath:
-DeliversCash:
QTNK:
Buildable:
Prerequisites: ~disabled
MCV:
Buildable:
Prerequisites: ~disabled
MSUB:
Buildable:
Prerequisites: ~disabled
STEK:
Buildable:
Prerequisites: ~disabled
PDOX:
Buildable:
Prerequisites: ~disabled
MRJ:
Buildable:
Prerequisites: ~disabled
CA:
Buildable:
Prerequisites: ~disabled
HELI:
Buildable:
Prerequisites: ~disabled
GAP:
Buildable:
Prerequisites: ~disabled
MNLY:
Buildable:
Prerequisites: ~disabled
AFLD:
ParatroopersPower@paratroopers:
DropItems: E1,E1,E1,E1,E1

View File

@@ -0,0 +1,228 @@
--[[
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.
]]
InfAttack = { }
ArmorAttack = { }
AttackPaths = { { AttackWaypoint1.Location }, { AttackWaypoint2.Location } }
AlliedInfantryTypes = { "e1", "e1", "e3" }
AlliedArmorTypes = { "jeep", "jeep", "1tnk", "1tnk", "2tnk", "2tnk", "arty" }
ProduceInfantry = function(barracks)
if barracks.IsDead or barracks.Owner ~= Greece then
return
elseif GreeceMoney() <= 299 and IsHarvesterMissing() then
return
end
local delay = Utils.RandomInteger(DateTime.Seconds(3), DateTime.Seconds(9))
local toBuild = { Utils.Random(AlliedInfantryTypes) }
local path = Utils.Random(AttackPaths)
Greece.Build(toBuild, function(units)
InfAttack[#InfAttack + 1] = units[1]
if #InfAttack >= 10 then
SendUnits(InfAttack, path)
InfAttack = { }
Trigger.AfterDelay(DateTime.Minutes(2), function()
ProduceInfantry(barracks)
end)
else
Trigger.AfterDelay(delay, function()
ProduceInfantry(barracks)
end)
end
end)
end
ProduceArmor = function(factory)
local delay = Utils.RandomInteger(DateTime.Seconds(12), DateTime.Seconds(17))
if factory.IsDead or factory.Owner ~= Greece then
return
elseif IsHarvesterMissing() then
ProduceHarvester(factory, delay)
return
end
local toBuild = { Utils.Random(AlliedArmorTypes) }
local path = Utils.Random(AttackPaths)
Greece.Build(toBuild, function(units)
ArmorAttack[#ArmorAttack + 1] = units[1]
if #ArmorAttack >= 6 then
SendUnits(ArmorAttack, path)
ArmorAttack = { }
Trigger.AfterDelay(DateTime.Minutes(3), function()
ProduceArmor(factory)
end)
else
Trigger.AfterDelay(delay, function()
ProduceArmor(factory)
end)
end
end)
end
ProduceHarvester = function(factory, delay)
if GreeceMoney() < Actor.Cost("harv") then
return
end
local toBuild = { "harv" }
Greece.Build(toBuild, function()
Trigger.AfterDelay(delay, function()
ProduceArmor(factory)
end)
end)
end
SendUnits = function(units, path)
Utils.Do(units, function(unit)
if unit.IsDead then
return
end
unit.Patrol(path, false)
IdleHunt(unit)
end)
end
IsHarvesterMissing = function()
return #Greece.GetActorsByType("harv") == 0
end
GreeceMoney = function()
return Greece.Cash + Greece.Resources
end
BaseBlueprints =
{
{ type = "apwr", actor = Apwr, cost = 500, shape = { 3, 3 }, location = CPos.New(76, 41) },
{ type = "tent", actor = Tent, cost = 400, shape = { 2, 3 }, location = CPos.New(67, 45), onBuilt = ProduceInfantry },
{ type = "proc", actor = Proc, cost = 1400, shape = { 3, 4 }, location = CPos.New(71, 41) },
{ type = "weap", actor = Weap, cost = 2000, shape = { 3, 3 }, location = CPos.New(67, 41), onBuilt = ProduceArmor },
{ type = "apwr", actor = Apwr2, cost = 500, shape = { 3, 3 }, location = CPos.New(77, 45) }
}
--[[
Similar to the original CnC/RA [BASE] and [STRUCTURES] .INI sections.
Check a list every so often and (re)build structures missing from
that list, in order, if circumstances allow for it.
]]
BuildBase = function()
for _, blueprint in pairs(BaseBlueprints) do
if not blueprint.actor then
BuildBlueprint(blueprint)
return
end
end
Trigger.AfterDelay(DateTime.Seconds(10), BuildBase)
end
BuildBlueprint = function(blueprint)
Trigger.AfterDelay(Actor.BuildTime(blueprint.type), function()
if CYard.IsDead or CYard.Owner ~= Greece then
return
elseif GreeceMoney() <= 299 and IsHarvesterMissing() then
return
end
if IsBuildAreaBlocked(Greece, blueprint) then
Trigger.AfterDelay(DateTime.Seconds(5), function()
BuildBlueprint(blueprint)
end)
return
end
local actor = Actor.Create(blueprint.type, true, { Owner = Greece, Location = blueprint.location })
OnBlueprintBuilt(actor, blueprint)
Trigger.AfterDelay(DateTime.Seconds(10), BuildBase)
end)
end
OnBlueprintBuilt = function(actor, blueprint)
Greece.Cash = Greece.Cash - blueprint.cost
blueprint.actor = actor
MaintainBuilding(actor, blueprint, 0.75)
if blueprint.onBuilt then
-- Build() will not work properly on producers if immediately called.
Trigger.AfterDelay(1, function()
blueprint.onBuilt(actor)
end)
end
end
IsBuildAreaBlocked = function(player, blueprint)
local nw, se = blueprint.northwestEdge, blueprint.southeastEdge
local blockers = Map.ActorsInBox(nw, se, function(actor)
-- Neutral check is for ignoring trees near the refinery.
return actor.Owner ~= Neutral and actor.CenterPosition.Z == 0 and actor.HasProperty("Health")
end)
if #blockers == 0 then
return false
end
ScatterBlockers(player, blockers)
return true
end
ScatterBlockers = function(player, actors)
Utils.Do(actors, function(actor)
if actor.IsIdle and actor.Owner == player and actor.HasProperty("Scatter") then
actor.Scatter()
end
end)
end
BeginBaseMaintenance = function()
Utils.Do(BaseBlueprints, function(blueprint)
MaintainBuilding(blueprint.actor, blueprint)
end)
Utils.Do(Greece.GetActors(), function(actor)
if actor.HasProperty("StartBuildingRepairs") then
MaintainBuilding(actor, nil, 0.75)
end
end)
end
MaintainBuilding = function(actor, blueprint, repairThreshold)
if blueprint then
Trigger.OnKilled(actor, function() blueprint.actor = nil end)
Trigger.OnSold(actor, function() blueprint.actor = nil end)
if not blueprint.northwestEdge then
PrepareBlueprintEdges(blueprint)
end
end
if repairThreshold then
local original = actor.Owner
Trigger.OnDamaged(actor, function()
if actor.Owner ~= original or actor.Health > actor.MaxHealth * repairThreshold then
return
end
actor.StartBuildingRepairs()
end)
end
end
PrepareBlueprintEdges = function(blueprint)
local shapeX, shapeY = blueprint.shape[1], blueprint.shape[2]
local northwestEdge = Map.CenterOfCell(blueprint.location) + WVec.New(-512, -512, 0)
local southeastEdge = northwestEdge + WVec.New(shapeX * 1024, shapeY * 1024, 0)
blueprint.northwestEdge = northwestEdge
blueprint.southeastEdge = southeastEdge
end

View File

@@ -0,0 +1,68 @@
--[[
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.
]]
EnemyReinforcements =
{
easy =
{
{ "e1", "e1", "e3" },
{ "e1", "e3", "jeep" },
{ "e1", "jeep", "1tnk" }
},
normal =
{
{ "e1", "e1", "e3", "e3" },
{ "e1", "e3", "jeep", "jeep" },
{ "e1", "jeep", "1tnk", "2tnk" }
},
hard =
{
{ "e1", "e1", "e3", "e3", "e1" },
{ "e1", "e3", "jeep", "jeep", "1tnk" },
{ "e1", "jeep", "1tnk", "2tnk", "arty" }
}
}
EnemyAttackDelay =
{
easy = DateTime.Minutes(5),
normal = DateTime.Minutes(2) + DateTime.Seconds(40),
hard = DateTime.Minutes(1) + DateTime.Seconds(30)
}
EnemyPaths =
{
{ EnemyEntry1.Location, EnemyRally1.Location },
{ EnemyEntry2.Location, EnemyRally2.Location }
}
Wave = 0
SendReinforcements = function()
Trigger.AfterDelay(EnemyAttackDelay[Difficulty], function()
if Dome.IsDead or Dome.Owner ~= Greece then
return
end
Wave = Wave + 1
if Wave > 3 then
Wave = 1
end
if Wave == 1 then
local units = Reinforcements.ReinforceWithTransport(Greece, "tran", EnemyReinforcements[Difficulty][Wave], EnemyPaths[1], { EnemyPaths[1][1] })[2]
Utils.Do(units, IdleHunt)
else
local units = Reinforcements.ReinforceWithTransport(Greece, "lst", EnemyReinforcements[Difficulty][Wave], EnemyPaths[2], { EnemyPaths[2][1] })[2]
Utils.Do(units, IdleHunt)
end
SendReinforcements()
end)
end

View File

@@ -0,0 +1,221 @@
--[[
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.
]]
Trigger.OnRemovedFromWorld(Mcv, function()
if McvDeployed or Mcv.IsDead then
return
end
McvDeployed = true
BuildBase()
SendReinforcements()
Trigger.AfterDelay(DateTime.Minutes(1), function()
ProduceInfantry(Tent)
end)
Trigger.AfterDelay(DateTime.Minutes(2), function()
ProduceArmor(Weap)
end)
end)
PrepareResponseCruiser = function()
local responseBuildings = { Apwr, Tent, Weap }
local responseOrdered = false
Utils.Do(responseBuildings, function(building)
Trigger.OnDamaged(building, function()
if responseOrdered or USSR.IsObjectiveCompleted(DisruptDome) then
return
end
responseOrdered = true
OrderResponseCruiser()
end)
end)
end
OrderResponseCruiser = function()
if ResponseCruiser.IsDead then
return
end
ResponseCruiser.AttackMove(waypoint94.Location, 2)
ResponseCruiser.Wait(DateTime.Seconds(90))
Trigger.OnIdle(ResponseCruiser, function()
ResponseCruiser.AttackMove(waypoint69.Location, 2)
end)
Trigger.OnDamaged(ResponseCruiser, function(_, attacker)
if attacker.IsDead or not ResponseCruiser.CanTarget(attacker) then
return
end
ResponseCruiser.Attack(attacker)
ResponseCruiser.Scatter()
end)
end
PrepareBridgeBreakers = function()
local target = Map.ActorsInCircle(waypoint78.CenterPosition, WDist.New(1536), function(actor)
return actor.Type == "br3"
end)[1]
if not target then
Media.Debug("No bridge segment found.")
return
end
local orderSent = false
Trigger.AfterDelay(DateTime.Seconds(30), function()
orderSent = true
OrderBridgeBreakers(target)
end)
local bridgeEntryCells = { CPos.New(75, 30), CPos.New(76, 30), CPos.New(77, 30) }
Trigger.OnEnteredFootprint(bridgeEntryCells, function(a, id)
if a.Owner ~= USSR then
return
end
Trigger.RemoveFootprintTrigger(id)
if not orderSent then
OrderBridgeBreakers(target, "with bridge reveal")
end
end)
end
OrderBridgeBreakers = function(target, reveal)
if target.IsDead then
return
end
local breakers = { BridgeBreaker, BridgeBreaker2 }
Utils.Do(breakers, function(breaker)
if breaker.IsDead then
return
end
breaker.Stop()
breaker.Attack(target, true, true)
end)
if not reveal then
return
end
local camera = Actor.Create("camera", true, { Owner = USSR, Location = target.Location })
Trigger.OnKilled(target, function()
Trigger.AfterDelay(DateTime.Seconds(2), camera.Destroy)
end)
end
PrepareObjectives = function()
InitObjectives(USSR)
KillTrucks = AddPrimaryObjective(Greece, "")
EscortConvoy = AddPrimaryObjective(USSR, "escort-convoy")
DisruptDome = AddSecondaryObjective(USSR, "destroy-capture-radar-dome-reinforcements")
SaveAllTrucks = AddSecondaryObjective(USSR, "keep-trucks-alive")
Trigger.OnKilledOrCaptured(Dome, function()
-- Let the capture notification play first.
Trigger.AfterDelay(DateTime.Seconds(2), function()
USSR.MarkCompletedObjective(DisruptDome)
Media.PlaySpeechNotification(USSR, "ObjectiveMet")
end)
end)
end
PrepareTrucks = function()
local trucks = { Truck1, Truck2 }
local goalCells = { CPos.New(85, 10), CPos.New(85, 11), CPos.New(85, 12), CPos.New(86, 13), CPos.New(87, 13), CPos.New(88, 13), CPos.New(88, 14), CPos.New(89, 14), CPos.New(90, 14), CPos.New(90, 15), CPos.New(91, 15), CPos.New(91, 16), CPos.New(91, 17), CPos.New(92, 17), CPos.New(93, 17), CPos.New(94, 17), CPos.New(94, 18), CPos.New(95, 18), CPos.New(96, 18), CPos.New(96, 19), CPos.New(97, 19), CPos.New(98, 19)}
local goalTriggered = false
Trigger.OnEnteredFootprint(goalCells, function(a)
if not goalTriggered and a.Owner == USSR and a.Type == "truk" then
goalTriggered = true
USSR.MarkCompletedObjective(EscortConvoy)
USSR.MarkCompletedObjective(SaveAllTrucks)
end
end)
Trigger.OnAllKilled(trucks, function()
Greece.MarkCompletedObjective(KillTrucks)
end)
Trigger.OnAnyKilled(trucks, function()
USSR.MarkFailedObjective(SaveAllTrucks)
end)
end
BeginIntro = function()
local introAttackers = { IntroEnemy1, IntroEnemy2, IntroEnemy3 }
local sovietReinforcements1 = { "e6", "e6", "e6", "e6", "e6" }
local sovietReinforcements2 = { "e4", "e4", "e2", "e2", "e2" }
local sovietReinforcements1Path = { McvWaypoint.Location, APCWaypoint1.Location }
local sovietReinforcements2Path = { McvWaypoint.Location, APCWaypoint2.Location }
Mcv.Move(McvWaypoint.Location)
Utils.Do(introAttackers, IdleHunt)
Reinforcements.ReinforceWithTransport(USSR, "apc", sovietReinforcements1, sovietReinforcements1Path)
Reinforcements.ReinforceWithTransport(USSR, "apc", sovietReinforcements2, sovietReinforcements2Path)
end
PrepareIdleGuards = function()
local lazyUnits = Utils.Where(Greece.GetGroundAttackers(), function(unit)
return unit.Type ~= "ca" and unit.Type ~= "arty"
end)
Utils.Do(lazyUnits, function(unit)
local triggered = false
Trigger.OnDamaged(unit, function()
if triggered then
return
end
triggered = true
IdleHunt(unit)
end)
end)
end
WorldLoaded = function()
USSR = Player.GetPlayer("USSR")
Greece = Player.GetPlayer("Greece")
Neutral = Player.GetPlayer("Neutral")
PrepareObjectives()
Camera.Position = CameraStart.CenterPosition
Harvester.FindResources()
BeginBaseMaintenance()
if Difficulty ~= "easy" then
PrepareResponseCruiser()
Trigger.AfterDelay(1, PrepareBridgeBreakers)
end
PrepareTrucks()
BeginIntro()
PrepareIdleGuards()
end
Tick = function()
if USSR.HasNoRequiredUnits() then
Greece.MarkCompletedObjective(KillTrucks)
end
if Greece.Resources >= Greece.ResourceCapacity * 0.75 then
Greece.Cash = Greece.Cash + Greece.Resources - Greece.ResourceCapacity * 0.25
Greece.Resources = Greece.ResourceCapacity * 0.25
end
end