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

View File

@@ -0,0 +1,249 @@
--[[
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.
]]
AllToHuntTrigger =
{
Silo1, Proc1, Silo2, Silo3, Silo4, Afld1, Hand1, Nuke1, Nuke2, Nuke3, Fact1
}
AtkRoute1 = { waypoint4, waypoint5, waypoint6, waypoint7, waypoint8, GDIBaseCenter }
AtkRoute2 = { waypoint0, waypoint1, waypoint2, waypoint3, GDIBaseCenter }
AutoCreateTeams =
{
{ types = { e1 = 1, e3 = 3 }, route = AtkRoute2 },
{ types = { e1 = 3, e3 = 1 }, route = AtkRoute2 },
{ types = { e3 = 4 } , route = AtkRoute1 },
{ types = { e1 = 4 } , route = AtkRoute1 },
{ types = { bggy = 1 } , route = AtkRoute1 },
{ types = { bggy = 1 } , route = AtkRoute2 },
{ types = { ltnk = 1 } , route = AtkRoute1 },
{ types = { ltnk = 1 } , route = AtkRoute2 }
}
RepairThreshold = 0.6
Atk1Delay = DateTime.Seconds(40)
Atk2Delay = DateTime.Seconds(60)
Atk3Delay = DateTime.Seconds(70)
Atk4Delay = DateTime.Seconds(90)
AutoAtkStartDelay = DateTime.Seconds(115)
AutoAtkMinDelay = DateTime.Seconds(45)
AutoAtkMaxDelay = DateTime.Seconds(90)
Atk5CellTriggers =
{
CPos.New(17,55), CPos.New(16,55), CPos.New(15,55), CPos.New(50,54), CPos.New(49,54),
CPos.New(48,54), CPos.New(16,54), CPos.New(15,54), CPos.New(14,54), CPos.New(50,53),
CPos.New(49,53), CPos.New(48,53), CPos.New(50,52), CPos.New(49,52)
}
GDIBase = { GdiNuke1, GdiProc1, GdiWeap1, GdiNuke2, GdiPyle1, GdiSilo1, GdiSilo2 }
GDIUnits = { "e2", "e2", "e2", "e2", "e1", "e1", "e1", "e1", "mtnk", "mtnk", "jeep", "jeep", "apc" }
NodSams = { Sam1, Sam2, Sam3, Sam4 }
NodAttackers = { }
EarlyAttackTimer = 0
SendAttackers = function(actors, path)
Utils.Do(actors, function(actor)
local id = #NodAttackers + 1
NodAttackers[id] = actor
Trigger.OnKilled(actor, function()
NodAttackers[id] = nil
end)
end)
MoveAndHunt(actors, path)
end
AutoCreateTeam = function()
local team = Utils.Random(AutoCreateTeams)
for type, count in pairs(team.types) do
SendAttackers(Utils.Take(count, Nod.GetActorsByType(type)), team.route)
end
Trigger.AfterDelay(Utils.RandomInteger(AutoAtkMinDelay, AutoAtkMaxDelay), AutoCreateTeam)
end
DiscoverGDIBase = function(_, discoverer)
if BaseDiscovered or not discoverer == GDI then
return
end
Utils.Do(GDIBase, function(actor)
actor.Owner = GDI
end)
BaseDiscovered = true
EliminateNod = AddPrimaryObjective(GDI, "eliminate-nod")
GDI.MarkCompletedObjective(FindBase)
-- Delay spawn to avoid wasted tiberium and enemy attention.
if not GdiProc1.IsDead then
local origin = GdiProc1.Location + CVec.New(2, 3)
Reinforcements.Reinforce(GDI, { "harv" }, { origin, origin + CVec.New(-2, 0) })
end
end
LoseGDIBase = function(location)
if BaseDiscovered then
return
end
GDI.MarkFailedObjective(FindBase)
Actor.Create("camera", true, { Owner = GDI, Location = location })
Camera.Position = Map.CenterOfCell(location)
end
Atk1TriggerFunction = function()
SendAttackers(Utils.Take(2, Nod.GetActorsByType('e1')), AtkRoute1)
SendAttackers(Utils.Take(3, Nod.GetActorsByType('e3')), AtkRoute1)
end
Atk2TriggerFunction = function()
SendAttackers(Utils.Take(3, Nod.GetActorsByType('e1')), AtkRoute2)
SendAttackers(Utils.Take(3, Nod.GetActorsByType('e3')), AtkRoute2)
end
Atk3TriggerFunction = function()
SendAttackers(Utils.Take(1, Nod.GetActorsByType('bggy')), AtkRoute1)
end
Atk4TriggerFunction = function()
SendAttackers(Utils.Take(1, Nod.GetActorsByType('bggy')), AtkRoute2)
end
Atk5TriggerFunction = function()
SendAttackers(Utils.Take(1, Nod.GetActorsByType('ltnk')), AtkRoute2)
end
InsertGDIUnits = function()
Media.PlaySpeechNotification(GDI, "Reinforce")
Reinforcements.Reinforce(GDI, GDIUnits, { UnitsEntry.Location, UnitsRally.Location }, 15)
end
ScheduleNodAttacks = function()
Trigger.AfterDelay(Atk1Delay, Atk1TriggerFunction)
Trigger.AfterDelay(Atk2Delay, Atk2TriggerFunction)
Trigger.AfterDelay(Atk3Delay, Atk3TriggerFunction)
Trigger.AfterDelay(Atk4Delay, Atk4TriggerFunction)
Trigger.OnEnteredFootprint(Atk5CellTriggers, function(a, id)
if a.Owner == GDI then
Atk5TriggerFunction()
Trigger.RemoveFootprintTrigger(id)
end
end)
Trigger.AfterDelay(AutoAtkStartDelay, AutoCreateTeam)
Trigger.AfterDelay(DateTime.Seconds(40), function()
local delay = function() return DateTime.Seconds(30) end
local toBuild = function() return { "e1" } end
ProduceUnits(Nod, Hand1, delay, toBuild)
end)
Trigger.OnAllRemovedFromWorld(AllToHuntTrigger, function()
Utils.Do(Nod.GetGroundAttackers(), IdleHunt)
end)
local baseDefenses = Nod.GetActorsByTypes({ "sam", "gun" })
local pullBuildings = Utils.Concat(AllToHuntTrigger, baseDefenses)
Utils.Do(pullBuildings, function(building)
-- Skip the lone SAM that is not part of the Nod base.
if building == Sam4 then
return
end
Trigger.OnDamaged(building, OnNodBaseDamaged)
end)
end
OnNodBaseDamaged = function(building, attacker)
if BaseDiscovered then
return
end
if EarlyAttackTimer > 0 or attacker.Owner ~= GDI then
return
end
EarlyAttackTimer = DateTime.Seconds(10)
if attacker.IsDead then
PullAttackers(building.Location)
return
end
PullAttackers(attacker.Location)
end
PullAttackers = function(location)
Utils.Do(NodAttackers, function(attacker)
if attacker.IsDead or attacker.Stance == "Defend" then
return
end
-- Ignore structures.
attacker.Stance = "Defend"
attacker.Stop()
attacker.AttackMove(location, 2)
attacker.CallFunc(function()
-- No targets nearby. Reset stance for IdleHunt.
attacker.Stance = "AttackAnything"
end)
end)
end
WorldLoaded = function()
GDI = Player.GetPlayer("GDI")
AbandonedBase = Player.GetPlayer("AbandonedBase")
Nod = Player.GetPlayer("Nod")
InitObjectives(GDI)
RepairNamedActors(Nod, RepairThreshold)
FindBase = AddPrimaryObjective(GDI, "find-gdi-base")
DestroySAMs = AddSecondaryObjective(GDI, "destroy-sams")
NodObjective = AddPrimaryObjective(Nod, "")
Trigger.OnPlayerDiscovered(AbandonedBase, DiscoverGDIBase)
local revealCell = GdiNuke1.Location
Trigger.OnAllKilled(GDIBase, function()
LoseGDIBase(revealCell)
end)
Trigger.OnAllKilled(NodSams, function()
GDI.MarkCompletedObjective(DestroySAMs)
Actor.Create("airstrike.proxy", true, { Owner = GDI })
end)
Camera.Position = UnitsRally.CenterPosition
InsertGDIUnits()
ScheduleNodAttacks()
end
Tick = function()
if DateTime.GameTime > 2 and GDI.HasNoRequiredUnits() then
Nod.MarkCompletedObjective(NodObjective)
end
if not BaseDiscovered then
EarlyAttackTimer = EarlyAttackTimer - 1
return
end
if Nod.HasNoRequiredUnits() then
GDI.MarkCompletedObjective(EliminateNod)
end
end

Binary file not shown.

View File

@@ -0,0 +1,7 @@
## rules.yaml
briefing =
A GDI field base is under attack. They have fended off one attack but will not survive another.
Move to the base, repair the structures, then launch a strike force to destroy the Nod base in the area.
Destroy all Nod units and structures.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,616 @@
MapFormat: 12
RequiresMod: cnc
Title: 05b: Repair the GDI base
Author: Westwood Studios
Tileset: TEMPERAT
MapSize: 64,64
Bounds: 8,13,51,47
Visibility: MissionSelector
Categories: Campaign
LockPreview: True
Players:
PlayerReference@Nod:
Name: Nod
Faction: nod
Color: FE1100
Enemies: GDI, AbandonedBase
Bot: campaign
PlayerReference@GDI:
Name: GDI
Playable: True
AllowBots: False
Required: True
LockFaction: True
Faction: gdi
LockColor: True
Color: F5D378
LockSpawn: True
LockTeam: True
Enemies: Nod
PlayerReference@Neutral:
Name: Neutral
OwnsWorld: True
NonCombatant: True
Faction: gdi
Color: F5D378
PlayerReference@AbandonedBase:
Name: AbandonedBase
NonCombatant: True
Faction: gdi
Color: F5D378
Enemies: Nod
Bot: campaign
Actors:
Actor0: sbag
Location: 38,59
Owner: GDI
Actor1: sbag
Location: 37,59
Owner: GDI
Actor2: sbag
Location: 36,59
Owner: GDI
Actor3: sbag
Location: 35,59
Owner: GDI
Actor4: sbag
Location: 34,59
Owner: GDI
Actor5: sbag
Location: 33,59
Owner: GDI
Actor6: sbag
Location: 32,59
Owner: GDI
Actor7: sbag
Location: 31,59
Owner: GDI
Actor8: sbag
Location: 30,59
Owner: GDI
Actor9: sbag
Location: 29,59
Owner: GDI
Actor10: sbag
Location: 28,59
Owner: GDI
Actor11: sbag
Location: 27,59
Owner: GDI
Actor12: sbag
Location: 26,59
Owner: GDI
Actor13: sbag
Location: 25,59
Owner: GDI
Actor14: sbag
Location: 25,58
Owner: GDI
Actor15: sbag
Location: 25,57
Owner: GDI
Actor16: sbag
Location: 24,57
Owner: GDI
Actor17: sbag
Location: 38,54
Owner: GDI
Actor18: sbag
Location: 25,54
Owner: GDI
Actor19: sbag
Location: 24,54
Owner: GDI
Actor20: sbag
Location: 38,53
Owner: GDI
Actor21: sbag
Location: 25,53
Owner: GDI
Actor22: sbag
Location: 25,51
Owner: GDI
Actor23: sbag
Location: 38,50
Owner: GDI
Actor24: sbag
Location: 37,50
Owner: GDI
Actor25: sbag
Location: 36,50
Owner: GDI
Actor26: sbag
Location: 35,50
Owner: GDI
Actor27: sbag
Location: 34,50
Owner: GDI
Actor28: sbag
Location: 33,50
Owner: GDI
Actor29: sbag
Location: 32,50
Owner: GDI
Actor30: sbag
Location: 31,50
Owner: GDI
Actor31: sbag
Location: 30,50
Owner: GDI
Actor32: sbag
Location: 29,50
Owner: GDI
Actor33: sbag
Location: 27,50
Owner: GDI
Actor34: sbag
Location: 26,50
Owner: GDI
Actor35: sbag
Location: 25,50
Owner: GDI
Actor36: sbag
Location: 54,26
Owner: Nod
Actor37: sbag
Location: 53,26
Owner: Nod
Actor38: sbag
Location: 52,26
Owner: Nod
Actor39: sbag
Location: 51,26
Owner: Nod
Actor40: sbag
Location: 50,26
Owner: Nod
Actor41: sbag
Location: 45,26
Owner: Nod
Actor42: sbag
Location: 44,26
Owner: Nod
Actor43: sbag
Location: 43,26
Owner: Nod
Actor44: sbag
Location: 42,26
Owner: Nod
Actor45: sbag
Location: 41,26
Owner: Nod
Actor46: sbag
Location: 40,26
Owner: Nod
Actor47: sbag
Location: 39,26
Owner: Nod
Actor48: sbag
Location: 38,26
Owner: Nod
Actor49: sbag
Location: 37,26
Owner: Nod
Actor50: sbag
Location: 54,25
Owner: Nod
Actor51: sbag
Location: 51,25
Owner: Nod
Actor52: sbag
Location: 50,25
Owner: Nod
Actor53: sbag
Location: 45,25
Owner: Nod
Actor54: sbag
Location: 44,25
Owner: Nod
Actor55: sbag
Location: 37,25
Owner: Nod
Actor56: sbag
Location: 37,24
Owner: Nod
Actor57: sbag
Location: 37,23
Owner: Nod
Actor58: t01
Location: 22,51
Owner: Neutral
Actor59: t01
Location: 24,50
Owner: Neutral
Actor60: t01
Location: 28,49
Owner: Neutral
Actor61: t05
Location: 30,15
Owner: Neutral
Actor62: t03
Location: 13,34
Owner: Neutral
Actor63: t03
Location: 26,42
Owner: Neutral
Actor64: tc04
Location: 26,37
Owner: Neutral
Actor65: tc02
Location: 22,50
Owner: Neutral
Actor66: t01
Location: 24,30
Owner: Neutral
Actor67: tc01
Location: 8,23
Owner: Neutral
Actor68: tc02
Location: 21,22
Owner: Neutral
Actor69: tc04
Location: 39,26
Owner: Neutral
Actor70: tc02
Location: 10,25
Owner: Neutral
Actor71: t07
Location: 46,42
Owner: Neutral
Actor72: t12
Location: 46,39
Owner: Neutral
Actor73: tc04
Location: 48,41
Owner: Neutral
Actor74: tc01
Location: 12,32
Owner: Neutral
Actor75: tc04
Location: 20,23
Owner: Neutral
Actor76: t01
Location: 20,40
Owner: Neutral
Actor77: t05
Location: 8,48
Owner: Neutral
Actor78: t06
Location: 18,45
Owner: Neutral
Actor79: tc01
Location: 8,58
Owner: Neutral
Actor80: tc04
Location: 20,52
Owner: Neutral
Actor81: tc04
Location: 17,32
Owner: Neutral
Actor82: t01
Location: 31,41
Owner: Neutral
Actor83: t01
Location: 31,44
Owner: Neutral
Actor84: t01
Location: 22,40
Owner: Neutral
Actor85: t02
Location: 27,42
Owner: Neutral
Actor86: t02
Location: 17,41
Owner: Neutral
Actor87: t05
Location: 19,42
Owner: Neutral
Actor88: tc01
Location: 27,15
Owner: Neutral
Actor89: tc04
Location: 17,17
Owner: Neutral
Actor90: t07
Location: 44,26
Owner: Neutral
Actor91: t07
Location: 23,29
Owner: Neutral
Actor92: t07
Location: 50,30
Owner: Neutral
Actor93: t16
Location: 38,47
Owner: Neutral
Actor94: tc02
Location: 38,48
Owner: Neutral
Actor95: tc02
Location: 44,43
Owner: Neutral
Actor96: tc01
Location: 14,22
Owner: Neutral
Actor97: tc04
Location: 16,21
Owner: Neutral
Actor98: t01
Location: 22,35
Owner: Neutral
Actor99: t06
Location: 34,44
Owner: Neutral
Actor100: tc01
Location: 55,25
Owner: Neutral
Actor101: tc02
Location: 40,30
Owner: Neutral
Actor102: t03
Location: 36,24
Owner: Neutral
Actor121: gun
Location: 50,27
Owner: Nod
Facing: 512
Actor122: gun
Location: 45,27
Owner: Nod
Facing: 512
Actor127: gun
Location: 36,24
Owner: Nod
Facing: 768
Actor128: bggy
Location: 39,19
Owner: Nod
Facing: 512
Actor129: bggy
Location: 38,23
Owner: Nod
Facing: 768
Actor130: ltnk
Location: 38,24
Owner: Nod
Facing: 768
Actor131: ltnk
Location: 45,21
Owner: Nod
Facing: 512
Actor132: ltnk
Location: 44,21
Owner: Nod
Facing: 512
Actor133: bggy
Location: 23,32
Owner: Nod
Facing: 896
Actor134: ltnk
Location: 13,32
Owner: Nod
Facing: 896
Actor135: ltnk
Location: 48,41
Owner: Nod
Facing: 384
Actor142: bggy
Location: 53,27
Owner: Nod
Facing: 640
Actor143: bggy
Location: 42,27
Owner: Nod
Facing: 384
Actor144: e3
Location: 23,29
Owner: Nod
Facing: 384
SubCell: 4
Actor145: e3
Location: 40,30
Owner: Nod
SubCell: 0
Actor146: e3
Location: 35,31
Owner: Nod
SubCell: 2
Actor147: e3
Location: 43,27
Owner: Nod
Facing: 512
SubCell: 0
Actor148: e3
Location: 11,39
Owner: Nod
SubCell: 1
Actor149: e1
Location: 18,25
Owner: Nod
SubCell: 1
Actor150: e1
Location: 18,40
Owner: Nod
Facing: 384
SubCell: 4
Actor151: e3
Location: 12,38
Owner: Nod
Facing: 896
SubCell: 4
Actor152: e3
Location: 12,40
Owner: Nod
Facing: 896
SubCell: 1
Actor153: e3
Location: 44,43
Owner: Nod
Facing: 256
SubCell: 1
Actor154: e1
Location: 17,27
Owner: Nod
Facing: 640
SubCell: 1
Actor163: e1
Location: 17,25
Owner: Nod
Facing: 512
SubCell: 3
Actor164: e3
Location: 21,22
Owner: Nod
Facing: 384
SubCell: 2
Actor165: e3
Location: 18,41
Owner: Nod
SubCell: 3
Actor166: e3
Location: 17,41
Owner: Nod
Facing: 640
SubCell: 4
Actor167: moneycrate
Location: 26,42
Owner: Neutral
waypoint27: waypoint
Location: 32,41
Owner: Neutral
waypoint26: waypoint
Location: 8,13
Owner: Neutral
waypoint11: waypoint
Location: 34,20
Owner: Neutral
waypoint10: waypoint
Location: 34,29
Owner: Neutral
waypoint9: waypoint
Location: 56,29
Owner: Neutral
waypoint8: waypoint
Location: 20,57
Owner: Neutral
waypoint7: waypoint
Location: 11,51
Owner: Neutral
waypoint6: waypoint
Location: 12,30
Owner: Neutral
waypoint5: waypoint
Location: 12,21
Owner: Neutral
waypoint4: waypoint
Location: 35,22
Owner: Neutral
waypoint3: waypoint
Location: 42,54
Owner: Neutral
waypoint2: waypoint
Location: 53,53
Owner: Neutral
waypoint1: waypoint
Location: 53,42
Owner: Neutral
waypoint0: waypoint
Location: 47,27
Owner: Neutral
UnitsEntry: waypoint
Location: 11,13
Owner: Neutral
UnitsRally: waypoint
Location: 12,17
Owner: Neutral
Silo1: silo
Location: 51,20
Owner: Nod
Nuke1: nuke
Location: 45,18
Owner: Nod
Proc1: proc
Location: 46,21
Owner: Nod
Silo2: silo
Location: 49,22
Owner: Nod
Silo3: silo
Location: 49,20
Owner: Nod
Silo4: silo
Location: 51,22
Owner: Nod
Afld1: afld
Location: 40,18
Owner: Nod
Hand1: hand
Location: 41,22
Owner: Nod
Nuke2: nuke
Location: 47,18
Owner: Nod
Nuke3: nuke
Location: 49,17
Owner: Nod
Fact1: fact
Location: 51,17
Owner: Nod
GdiNuke2: nuke
Location: 33,51
Owner: AbandonedBase
Health: 46
GdiProc1: proc
Location: 27,52
Owner: AbandonedBase
Health: 48
FreeActor: False
GdiWeap1: weap
Location: 35,52
Owner: AbandonedBase
Health: 41
GdiNuke1: nuke
Location: 31,51
Owner: AbandonedBase
Health: 39
GdiPyle1: pyle
Location: 31,54
Owner: AbandonedBase
Health: 46
GdiSilo1: silo
Location: 26,51
Owner: AbandonedBase
Health: 41
GdiSilo2: silo
Location: 29,51
Owner: AbandonedBase
Sam1: sam
Location: 52,25
Owner: Nod
Sam2: sam
Location: 38,25
Owner: Nod
Sam3: sam
Location: 40,17
Owner: Nod
Sam4: sam
Location: 26,37
Owner: Nod
GDIBaseCenter: waypoint
Owner: Neutral
Location: 32,53
Rules: cnc|rules/campaign-maprules.yaml, cnc|rules/campaign-tooltips.yaml, cnc|rules/campaign-palettes.yaml, rules.yaml
FluentMessages: cnc|fluent/lua.ftl, cnc|fluent/campaign.ftl, map.ftl

View File

@@ -0,0 +1,144 @@
World:
LuaScript:
Scripts: campaign.lua, utils.lua, gdi05b.lua
MusicPlaylist:
StartingMusic: rain
MissionData:
Briefing: briefing
BackgroundVideo: podium.vqa
BriefingVideo: gdi5.vqa
StartVideo: seige.vqa
WinVideo: nodlose.vqa
LossVideo: gdilose.vqa
SmudgeLayer@SCORCH:
InitialSmudges:
15,56: sc3,0
40,53: sc2,0
24,53: sc2,0
39,52: sc5,0
24,52: sc1,0
39,51: sc4,0
Player:
EnemyWatcher:
PlayerResources:
DefaultCash: 4000
^Palettes:
IndexedPlayerPalette:
PlayerIndex:
AbandonedBase: 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
IndexedPlayerPalette@units:
PlayerIndex:
AbandonedBase: 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191
^Building:
AnnounceOnSeen:
SBAG:
Buildable:
Prerequisites: ~disabled
GTWR:
Buildable:
Prerequisites: ~disabled
ATWR:
Buildable:
Prerequisites: ~disabled
WEAP:
Buildable:
Prerequisites: ~disabled
CYCL:
Buildable:
Prerequisites: ~disabled
NUK2:
Buildable:
Prerequisites: ~disabled
FIX:
Buildable:
Prerequisites: ~disabled
HPAD:
Buildable:
Prerequisites: ~disabled
BRIK:
Buildable:
Prerequisites: ~disabled
EYE:
Buildable:
Prerequisites: ~disabled
GUN:
Buildable:
Prerequisites: ~disabled
OBLI:
Buildable:
Prerequisites: ~disabled
TMPL:
Buildable:
Prerequisites: ~disabled
E2:
Buildable:
Prerequisites: ~pyle
E3:
Buildable:
Prerequisites: ~disabled
E5:
Buildable:
Prerequisites: ~disabled
HARV:
Buildable:
Prerequisites: ~disabled
MTNK:
Buildable:
Prerequisites: ~disabled
HTNK:
Buildable:
Prerequisites: ~disabled
RMBO:
Buildable:
Prerequisites: ~disabled
MSAM:
Buildable:
Prerequisites: ~disabled
ARTY:
Buildable:
Prerequisites: ~disabled
MLRS:
Buildable:
Prerequisites: ~disabled
STNK:
Buildable:
Prerequisites: ~disabled
MCV:
Buildable:
Prerequisites: ~disabled
FTNK:
Buildable:
Prerequisites: ~disabled
airstrike.proxy:
AirstrikePower:
SquadSize: 1