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,356 @@
--[[
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.
]]
DeathThreshold =
{
easy = 200,
normal = 100,
}
TanyaType = "e7"
if Difficulty ~= "easy" then
TanyaType = "e7.noautotarget"
end
RepairTriggerThreshold =
{
easy = 50,
normal = 75,
}
Sams = { Sam1, Sam2, Sam3, Sam4 }
TownUnits =
{
Einstein, Engineer,
TownUnit01, TownUnit02, TownUnit03, TownUnit04, TownUnit05, TownUnit06, TownUnit07,
TownUnit08, TownUnit09, TownUnit10, TownUnit11, TownUnit12, TownUnit13, TownUnit14,
}
ParabombDelay = DateTime.Seconds(30)
ParatroopersDelay = DateTime.Minutes(5)
Paratroopers =
{
{
proxy = "powerproxy.paras1",
entry = BadgerEntryPoint1.Location,
drop = BadgerDropPoint1.Location,
},
{
proxy = "powerproxy.paras2",
entry = BadgerEntryPoint1.Location + CVec.New(3, 0),
drop = BadgerDropPoint2.Location,
},
{
proxy = "powerproxy.paras2",
entry = BadgerEntryPoint1.Location + CVec.New(6, 0),
drop = BadgerDropPoint3.Location,
},
}
AttackGroup = { }
AttackGroupSize = 5
SovietInfantry = { "e1", "e2", "e3" }
SovietVehiclesUpgradeDelay = DateTime.Minutes(4)
SovietVehicleType = "Normal"
SovietVehicles =
{
Normal = { "3tnk" },
Upgraded = { "3tnk", "v2rl" },
}
ProductionInterval =
{
easy = DateTime.Seconds(10),
normal = DateTime.Seconds(2),
}
ReinforcementsDelay = DateTime.Minutes(16)
ReinforcementsUnits = { "2tnk", "2tnk", "2tnk", "2tnk", "2tnk", "2tnk", "1tnk", "1tnk", "jeep", "e1",
"e1", "e1", "e1", "e3", "e3", "mcv", "truk", "truk", "truk", "truk", "truk", "truk" }
SpawnAlliedReinforcements = function()
if Allies2.IsLocalPlayer then
UserInterface.SetMissionText("")
Media.PlaySpeechNotification(Allies2, "AlliedReinforcementsArrived")
end
Reinforcements.Reinforce(Allies2, ReinforcementsUnits, { ReinforcementsEntryPoint.Location, Allies2BasePoint.Location })
end
Yak = nil
YakAttack = function()
local targets = Map.ActorsInCircle(YakAttackPoint.CenterPosition, WDist.FromCells(10), function(a)
return a.Owner == Allies1 and not a.IsDead and a ~= Einstein and a ~= Tanya and a ~= Engineer and Yak.CanTarget(a)
end)
if (#targets > 0) then
Yak.Attack(Utils.Random(targets))
end
Yak.Move(Map.ClosestEdgeCell(Yak.Location))
Yak.Destroy()
Trigger.OnRemovedFromWorld(Yak, function()
Yak = nil
end)
end
SovietTownAttack = function()
local units = Utils.Shuffle(Utils.Where(Map.ActorsWithTag("TownAttacker"), function(a) return not a.IsDead end))
Utils.Do(Utils.Take(5, units), function(unit)
unit.AttackMove(TownPoint.Location)
Trigger.OnIdle(unit, unit.Hunt)
end)
end
SendParabombs = function()
local proxy = Actor.Create("powerproxy.parabombs", false, { Owner = Soviets })
proxy.TargetAirstrike(ParabombPoint1.CenterPosition, (ParabombPoint1.CenterPosition - BadgerEntryPoint2.CenterPosition).Facing)
proxy.Destroy()
end
SendParatroopers = function()
Utils.Do(Paratroopers, function(para)
local proxy = Actor.Create(para.proxy, false, { Owner = Soviets })
local target = Map.CenterOfCell(para.drop)
local dir = target - Map.CenterOfCell(para.entry)
local aircraft = proxy.TargetParatroopers(target, dir.Facing)
Utils.Do(aircraft, function(a)
Trigger.OnPassengerExited(a, function(t, p)
IdleHunt(p)
end)
end)
proxy.Destroy()
end)
end
SendAttackGroup = function()
if #AttackGroup < AttackGroupSize then
return
end
Utils.Do(AttackGroup, function(unit)
if not unit.IsDead then
Trigger.OnIdle(unit, unit.Hunt)
end
end)
AttackGroup = { }
end
ProduceInfantry = function()
if SovietBarracks.IsDead or SovietBarracks.Owner ~= Soviets then
return
end
Soviets.Build({ Utils.Random(SovietInfantry) }, function(units)
table.insert(AttackGroup, units[1])
SendAttackGroup()
Trigger.AfterDelay(ProductionInterval[Difficulty], ProduceInfantry)
end)
end
ProduceVehicles = function()
if SovietWarFactory.IsDead or SovietWarFactory.Owner ~= Soviets then
return
end
Soviets.Build({ Utils.Random(SovietVehicles[SovietVehicleType]) }, function(units)
table.insert(AttackGroup, units[1])
SendAttackGroup()
Trigger.AfterDelay(ProductionInterval[Difficulty], ProduceVehicles)
end)
end
NumBaseBuildings = function()
local buildings = Map.ActorsInBox(AlliedBaseTopLeft.CenterPosition, AlliedBaseBottomRight.CenterPosition, function(a)
return not a.IsDead and a.Owner == Allies2 and a.HasProperty("StartBuildingRepairs")
end)
return #buildings
end
Tick = function()
if DateTime.GameTime > 1 and DateTime.GameTime % 25 == 0 and NumBaseBuildings() == 0 then
Allies2.MarkFailedObjective(HoldPositionObjective)
end
if not Allies2.IsObjectiveCompleted(CutSovietPowerObjective) and Soviets.PowerState ~= "Normal" then
Allies2.MarkCompletedObjective(CutSovietPowerObjective)
end
if not Allies2.IsObjectiveCompleted(LimitLossesObjective) and Allies2.UnitsLost > DeathThreshold[Difficulty] then
Allies2.MarkFailedObjective(LimitLossesObjective)
end
if Allies2.IsLocalPlayer and DateTime.GameTime <= ReinforcementsDelay then
if DateTime.GameTime % DateTime.Seconds(1) == 0 then
local time = Utils.FormatTime(ReinforcementsDelay - DateTime.GameTime)
local timer = UserInterface.GetFluentMessage("allied-reinforcements-arrive-in", { ["time"] = time })
UserInterface.SetMissionText(timer)
end
else
UserInterface.SetMissionText("")
end
end
SetupSoviets = function()
Soviets.Cash = 1000
if Difficulty == "easy" then
Utils.Do(Sams, function(sam)
local camera = Actor.Create("Camera.SAM", true, { Owner = Allies1, Location = sam.Location })
Trigger.OnKilledOrCaptured(sam, function()
camera.Destroy()
end)
end)
end
local buildings = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == Soviets and self.HasProperty("StartBuildingRepairs") end)
Utils.Do(buildings, function(actor)
Trigger.OnDamaged(actor, function(building)
if building.Owner == Soviets and building.Health < (building.MaxHealth * RepairTriggerThreshold[Difficulty] / 100) then
building.StartBuildingRepairs()
end
end)
end)
SovietBarracks.IsPrimaryBuilding = true
SovietBarracks.RallyPoint = SovietRallyPoint.Location
SovietWarFactory.IsPrimaryBuilding = true
SovietWarFactory.RallyPoint = SovietRallyPoint.Location
Trigger.AfterDelay(SovietVehiclesUpgradeDelay, function() SovietVehicleType = "Upgraded" end)
Trigger.AfterDelay(0, function()
ProduceInfantry()
ProduceVehicles()
end)
end
SetupTriggers = function()
Trigger.OnKilled(Tanya, function()
Allies1.MarkFailedObjective(TanyaMustSurviveObjective)
end)
Trigger.OnAllKilledOrCaptured(Sams, function()
Allies1.MarkCompletedObjective(DestroySamSitesObjective)
ExtractEinsteinObjective = AddPrimaryObjective(Allies1, "wait-for-helicopter-extract-einstein")
Actor.Create("flare", true, { Owner = Allies1, Location = ExtractionLZ.Location + CVec.New(1, -1) })
Beacon.New(Allies1, ExtractionLZ.CenterPosition)
Media.PlaySpeechNotification(Allies1, "SignalFlareNorth")
ExtractionHeli = Reinforcements.ReinforceWithTransport(Allies1, "tran", nil, { ExtractionLZEntryPoint.Location, ExtractionLZ.Location })[1]
Trigger.OnKilled(ExtractionHeli, function()
Allies1.MarkFailedObjective(ExtractEinsteinObjective)
end)
Trigger.OnPassengerEntered(ExtractionHeli, function(heli, passenger)
if passenger == Einstein then
heli.Move(ExtractionLZEntryPoint.Location)
heli.Destroy()
Trigger.OnRemovedFromWorld(heli, function()
Allies2.MarkCompletedObjective(LimitLossesObjective)
Allies2.MarkCompletedObjective(HoldPositionObjective)
Allies1.MarkCompletedObjective(TanyaMustSurviveObjective)
Allies1.MarkCompletedObjective(EinsteinSurvivalObjective)
Allies1.MarkCompletedObjective(ExtractEinsteinObjective)
end)
end
end)
end)
Trigger.OnEnteredProximityTrigger(TownPoint.CenterPosition, WDist.FromCells(15), function(actor, trigger)
if actor.Owner == Allies1 then
ReassignActors(TownUnits, Neutral, Allies1)
Utils.Do(TownUnits, function(a) a.Stance = "Defend" end)
Allies1.MarkCompletedObjective(FindEinsteinObjective)
EinsteinSurvivalObjective = AddPrimaryObjective(Allies1, "keep-einstein-alive-at-all-costs")
Trigger.OnKilled(Einstein, function()
Allies1.MarkFailedObjective(EinsteinSurvivalObjective)
end)
Trigger.RemoveProximityTrigger(trigger)
SovietTownAttack()
end
end)
Trigger.OnEnteredProximityTrigger(YakAttackPoint.CenterPosition, WDist.FromCells(5), function(actor, trigger)
if not (Yak == nil or Yak.IsDead) or actor.Owner ~= Allies1 then
return
end
Yak = Actor.Create("yak", true, { Owner = Soviets, Location = YakEntryPoint.Location, CenterPosition = YakEntryPoint.CenterPosition + WVec.New(0, 0, Actor.CruiseAltitude("yak")) })
Yak.Move(YakAttackPoint.Location + CVec.New(0, -10))
Yak.CallFunc(YakAttack)
end)
Trigger.AfterDelay(ParabombDelay, SendParabombs)
Trigger.AfterDelay(ParatroopersDelay, SendParatroopers)
Trigger.AfterDelay(ReinforcementsDelay, SpawnAlliedReinforcements)
end
SpawnTanya = function()
Tanya = Actor.Create(TanyaType, true, { Owner = Allies1, Location = TanyaLocation.Location })
if Difficulty ~= "easy" and Allies1.IsLocalPlayer then
Trigger.AfterDelay(DateTime.Seconds(2), function()
Media.DisplayMessageToPlayer(Allies1, UserInterface.GetFluentMessage("tanya-rules-of-engagement"), UserInterface.GetFluentMessage("tanya"))
end)
end
end
ReassignActors = function(actors, from, to)
Utils.Do(actors, function(a)
if a.Owner == from then
a.Owner = to
a.Stance = "Defend"
end
end)
end
WorldLoaded = function()
Neutral = Player.GetPlayer("Neutral")
-- Allies is the pre-set owner of units that get assigned to either the second player, if any, or the first player otherwise.
Allies = Player.GetPlayer("Allies")
-- Allies1 is the player starting on the right, controlling Tanya
Allies1 = Player.GetPlayer("Allies1")
-- Allies2 is the player starting on the left, defending the base
Allies2 = Player.GetPlayer("Allies2")
Soviets = Player.GetPlayer("Soviets")
Utils.Do({ Allies1, Allies2 }, function(player)
if player and player.IsLocalPlayer then
InitObjectives(player)
end
end)
if not Allies2 or Allies2.IsLocalPlayer then
Camera.Position = Allies2BasePoint.CenterPosition
else
Camera.Position = ChinookHusk.CenterPosition
end
if not Allies2 then
Allies2 = Allies1
end
ReassignActors(Map.ActorsInWorld, Allies, Allies2)
SpawnTanya()
TanyaMustSurviveObjective = AddPrimaryObjective(Allies1, "tanya-survive")
FindEinsteinObjective = AddPrimaryObjective(Allies1, "find-einstein-crashed-helicopter")
DestroySamSitesObjective = AddPrimaryObjective(Allies1, "destroy-sam-sites")
HoldPositionObjective = AddPrimaryObjective(Allies2, "hold-position-protect-base")
local dontLoseMoreThan = UserInterface.GetFluentMessage("do-not-lose-more-than", { ["units"] = DeathThreshold[Difficulty] })
LimitLossesObjective = AddSecondaryObjective(Allies2, dontLoseMoreThan)
CutSovietPowerObjective = AddSecondaryObjective(Allies2, "take-out-the-soviet-power-grid")
SetupTriggers()
SetupSoviets()
end

Binary file not shown.

View File

@@ -0,0 +1,5 @@
## rules.yaml
briefing =
One of our outposts is under heavy attack from a nearby Soviet base. Einstein, Tanya and many of our forces were evacuated by helicopter, but were shot down on the other side of the river. Fortunately they survived the crash, but are now stranded in enemy territory and must fight their way out. The air defenses must be eliminated before another Search & Rescue helicopter can arrive.
Meanwhile, those left at the base need to fend off Soviet attacks.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,222 @@
Player:
MissionObjectives:
Cooperative: True
PlayerResources:
DefaultCash: 5000
World:
MissionData:
Briefing: briefing
LuaScript:
Scripts: campaign.lua, utils.lua, evacuation.lua
ScriptLobbyDropdown@difficulty:
ID: difficulty
Label: dropdown-difficulty.label
Description: dropdown-difficulty.description
Values:
easy: options-difficulty.easy
normal: options-difficulty.normal
Default: normal
TimeLimitManager:
TimeLimitLocked: True
^Palettes:
IndexedPlayerPalette:
PlayerIndex:
Allies: 224, 224, 225, 225, 226, 184, 185, 186, 187, 188, 188, 189, 190, 190, 191, 191
Allies1: 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
Allies2: 208, 208, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 154, 155, 143
IndexedPlayerPalette@NOSHADOW:
PlayerIndex:
Allies: 224, 224, 225, 225, 226, 184, 185, 186, 187, 188, 188, 189, 190, 190, 191, 191
Allies1: 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175
Allies2: 208, 208, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 154, 155, 143
E1:
ScriptTags:
E2:
ScriptTags:
DOG:
ScriptTags:
3TNK:
ScriptTags:
TRAN.Husk1:
WithIdleOverlay@Burns:
Image: fire
Sequence: 1
IsDecoration: True
TRAN.Husk2:
WithIdleOverlay@Burns:
Image: fire
Sequence: 1
IsDecoration: True
E7:
Passenger:
Weight: 0
Buildable:
Prerequisites: ~disabled
EINSTEIN:
Passenger:
Weight: 0
V01:
SpawnActorOnDeath:
Actor: healcrate
TRAN:
-Selectable:
Buildable:
Prerequisites: ~disabled
RevealsShroud:
Range: 0c0
Interactable:
2TNK:
Buildable:
Prerequisites: ~vehicles.allies
MECH:
Buildable:
Prerequisites: ~disabled
THF:
Buildable:
Prerequisites: ~disabled
SPEN:
Buildable:
Prerequisites: ~disabled
SYRD:
Buildable:
Prerequisites: ~disabled
TSLA:
Buildable:
Prerequisites: ~disabled
AGUN:
Buildable:
Prerequisites: ~disabled
SAM:
Buildable:
Prerequisites: ~disabled
ATEK:
Buildable:
Prerequisites: ~disabled
HPAD:
Buildable:
Prerequisites: ~disabled
AFLD:
Buildable:
Prerequisites: ~disabled
STEK:
Buildable:
Prerequisites: ~disabled
GAP:
Buildable:
Prerequisites: ~disabled
PDOX:
Buildable:
Prerequisites: ~disabled
IRON:
Buildable:
Prerequisites: ~disabled
MSLO:
Buildable:
Prerequisites: ~disabled
MIG:
Buildable:
Prerequisites: ~disabled
HELI:
Buildable:
Prerequisites: ~disabled
4TNK:
Buildable:
Prerequisites: ~disabled
MCV:
Buildable:
Prerequisites: ~disabled
ARTY:
Buildable:
Prerequisites: ~disabled
APC:
Buildable:
Prerequisites: ~disabled
MNLY:
Buildable:
Prerequisites: ~disabled
FTRK:
Buildable:
Prerequisites: ~disabled
MRJ:
Buildable:
Prerequisites: ~disabled
MGG:
Buildable:
Prerequisites: ~disabled
TTNK:
Buildable:
Prerequisites: ~disabled
QTNK:
Buildable:
Prerequisites: ~disabled
DTRK:
Buildable:
Prerequisites: ~disabled
CTNK:
Buildable:
Prerequisites: ~disabled
STNK:
Buildable:
Prerequisites: ~disabled
MSUB:
Buildable:
Prerequisites: ~disabled
Camera.SAM:
Inherits: CAMERA
RevealsShroud:
Range: 2c0
powerproxy.paras1:
Inherits: powerproxy.paratroopers
ParatroopersPower:
DropItems: E1,E1,E1,E2,3TNK
powerproxy.paras2:
Inherits: powerproxy.paratroopers
ParatroopersPower:
DropItems: E1,E1,E1,E2,E2