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,346 @@
--[[
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.
]]
UnitsEvacuatedThreshold =
{
hard = 200,
normal = 100,
easy = 50
}
AttackAtFrame =
{
hard = 500,
normal = 500,
easy = 600
}
MinAttackAtFrame =
{
hard = 100,
normal = 100,
easy = 150
}
MaxSovietYaks =
{
hard = 4,
normal = 2,
easy = 0
}
SovietParadrops =
{
hard = 40,
normal = 20,
easy = 0
}
SovietParadropTicks =
{
hard = DateTime.Minutes(17),
normal = DateTime.Minutes(20),
easy = DateTime.Minutes(20)
}
SovietUnits2Ticks =
{
hard = DateTime.Minutes(12),
normal = DateTime.Minutes(15),
easy = DateTime.Minutes(15)
}
SovietEntryPoints =
{
SovietEntryPoint1, SovietEntryPoint2, SovietEntryPoint3, SovietEntryPoint4, SovietEntryPoint5, SovietEntryPoint6
}
SovietRallyPoints =
{
SovietRallyPoint1, SovietRallyPoint2, SovietRallyPoint3, SovietRallyPoint4, SovietRallyPoint5, SovietRallyPoint6
}
SovietAirfields =
{
SovietAirfield1, SovietAirfield2, SovietAirfield3, SovietAirfield4,
SovietAirfield5, SovietAirfield6, SovietAirfield7, SovietAirfield8
}
MountainEntry = { CPos.New(25, 45), CPos.New(25, 46), CPos.New(25, 47), CPos.New(25, 48), CPos.New(25, 49) }
BridgeEntry = { CPos.New(25, 29), CPos.New(26, 29), CPos.New(27, 29), CPos.New(28, 29) }
MobileConstructionVehicle = { "mcv" }
Yak = { "yak" }
ReinforcementsTicks1 = DateTime.Minutes(5)
Reinforcements1 =
{
"mgg", "2tnk", "2tnk", "2tnk", "2tnk", "1tnk", "1tnk",
"jeep", "jeep", "e1", "e1", "e1", "e1", "e3", "e3"
}
ReinforcementsTicks2 = DateTime.Minutes(10)
Reinforcements2 =
{
"mgg", "2tnk", "2tnk", "2tnk", "2tnk", "truk", "truk", "truk",
"truk", "truk", "truk", "1tnk", "1tnk", "jeep", "jeep"
}
SovietUnits1 =
{
"3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "3tnk", "v2rl", "v2rl", "ftrk",
"apc", "e1", "e1", "e2", "e3", "e3", "e4"
}
SovietUnits2 =
{
"4tnk", "4tnk", "4tnk", "4tnk", "3tnk", "3tnk", "3tnk", "3tnk", "v2rl",
"v2rl", "ftrk", "apc", "e1", "e1", "e2", "e3", "e3", "e4"
}
CurrentReinforcement1 = 0
CurrentReinforcement2 = 0
SpawnAlliedUnit = function(units)
Reinforcements.Reinforce(Allies1, units, { Allies1EntryPoint.Location, Allies1MovePoint.Location })
if Allies2 then
Reinforcements.Reinforce(Allies2, units, { Allies2EntryPoint.Location, Allies2MovePoint.Location })
end
Utils.Do(Humans, function(player)
Trigger.AfterDelay(DateTime.Seconds(2), function()
Media.PlaySpeechNotification(player, "AlliedReinforcementsNorth")
end)
end)
if CurrentReinforcement1 < #Reinforcements1 then
CurrentReinforcement1 = CurrentReinforcement1 + 1
Trigger.AfterDelay(ReinforcementsTicks1, function()
reinforcements1 = { Reinforcements1[CurrentReinforcement1] }
SpawnAlliedUnit(reinforcements1)
end)
end
if CurrentReinforcement2 < #Reinforcements2 then
CurrentReinforcement2 = CurrentReinforcement2 + 1
Trigger.AfterDelay(ReinforcementsTicks2, function()
reinforcements2 = { Reinforcements2[CurrentReinforcement2] }
SpawnAlliedUnit(reinforcements2)
end)
end
end
SovietGroupSize = 5
SpawnSovietUnits = function()
local units = SovietUnits1
if DateTime.GameTime >= SovietUnits2Ticks[Difficulty] then
units = SovietUnits2
end
local unitType = Utils.Random(units)
local spawnPoint = Utils.Random(SovietEntryPoints)
local rallyPoint = Utils.Random(SovietRallyPoints)
local actor = Actor.Create(unitType, true, { Owner = Soviets, Location = spawnPoint.Location })
actor.AttackMove(rallyPoint.Location)
IdleHunt(actor)
local delay = math.max(AttackAtFrame[Difficulty] - 5, MinAttackAtFrame[Difficulty])
Trigger.AfterDelay(delay, SpawnSovietUnits)
end
SovietParadrop = 0
SendSovietParadrop = function()
local sovietParadrops = SovietParadrops[Difficulty]
if (SovietParadrop > sovietParadrops) then
return
end
SovietParadrop = SovietParadrop + 1
Utils.Do(Humans, function(player)
Media.PlaySpeechNotification(player, "SovietForcesApproaching")
end)
local x = Utils.RandomInteger(ParadropBoxTopLeft.Location.X, ParadropBoxBottomRight.Location.X)
local y = Utils.RandomInteger(ParadropBoxBottomRight.Location.Y, ParadropBoxTopLeft.Location.Y)
local randomParadropCell = CPos.New(x, y)
local lz = Map.CenterOfCell(randomParadropCell)
local powerproxy = Actor.Create("powerproxy.paratroopers", false, { Owner = Soviets })
powerproxy.TargetParatroopers(lz)
powerproxy.Destroy()
Trigger.AfterDelay(SovietParadropTicks[Difficulty], SendSovietParadrop)
end
AircraftTargets = function(yak)
local targets = Utils.Where(Map.ActorsInWorld, function(a)
return (a.Owner == Allies1 or a.Owner == Allies2) and a.HasProperty("Health") and yak.CanTarget(a)
end)
-- Prefer mobile units
table.sort(targets, function(a, b) return a.HasProperty("Move") and not b.HasProperty("Move") end)
return targets
end
YakAttack = function(yak, target)
if not target or target.IsDead or (not target.IsInWorld) or (not yak.CanTarget(target)) then
local targets = AircraftTargets(yak)
if #targets > 0 then
target = Utils.Random(targets)
end
end
if target and yak.AmmoCount() > 0 and yak.CanTarget(target) then
yak.Attack(target)
else
-- Includes yak.Resupply()
yak.ReturnToBase()
end
yak.CallFunc(function()
YakAttack(yak, target)
end)
end
ManageSovietAircraft = function()
if Allies1.IsObjectiveCompleted(DestroyAirbases) then
return
end
local maxSovietYaks = MaxSovietYaks[Difficulty]
local sovietYaks = Soviets.GetActorsByType('yak')
if #sovietYaks < maxSovietYaks then
Soviets.Build(Yak, function(units)
local yak = units[1]
YakAttack(yak)
end)
end
end
SetEvacuateMissionText = function()
local unitsEvacuated = UserInterface.GetFluentMessage("units-evacuated",
{ ["evacuated"] = UnitsEvacuated, ["threshold"] = UnitsEvacuatedThreshold[Difficulty] })
UserInterface.SetMissionText(unitsEvacuated, TextColor)
end
UnitsEvacuated = 0
EvacuateAlliedUnit = function(unit)
if (unit.Owner == Allies1 or unit.Owner == Allies2) and unit.HasProperty("Move") then
unit.Stop()
unit.Owner = Allies
if unit.Type == 'mgg' then
Utils.Do(Humans, function(player)
if player then
player.MarkCompletedObjective(EvacuateMgg)
end
end)
end
UnitsEvacuated = UnitsEvacuated + 1
if unit.HasProperty("HasPassengers") then
UnitsEvacuated = UnitsEvacuated + unit.PassengerCount
end
local exitCell = Map.ClosestEdgeCell(unit.Location)
Trigger.OnIdle(unit, function()
unit.ScriptedMove(exitCell)
end)
local exit = Map.CenterOfCell(exitCell)
Trigger.OnEnteredProximityTrigger(exit, WDist.FromCells(1), function(a)
a.Destroy()
end)
SetEvacuateMissionText()
if UnitsEvacuated >= UnitsEvacuatedThreshold[Difficulty] then
Utils.Do(Humans, function(player)
if player then
player.MarkCompletedObjective(EvacuateUnits)
end
end)
end
end
end
Tick = function()
if DateTime.GameTime % 100 == 0 then
ManageSovietAircraft()
Utils.Do(Humans, function(player)
if player and player.HasNoRequiredUnits() then
Soviets.MarkCompletedObjective(SovietObjective)
end
end)
end
end
WorldLoaded = function()
-- NPC
Neutral = Player.GetPlayer("Neutral")
Allies = Player.GetPlayer("Allies")
Soviets = Player.GetPlayer("Soviets")
-- Player controlled
Allies1 = Player.GetPlayer("Allies1")
Allies2 = Player.GetPlayer("Allies2")
Humans = { Allies1, Allies2 }
Utils.Do(Humans, function(player)
if player and player.IsLocalPlayer then
InitObjectives(player)
TextColor = player.Color
end
end)
SetEvacuateMissionText()
Utils.Do(Humans, function(player)
if player then
EvacuateUnits = AddPrimaryObjective(player, UserInterface.GetFluentMessage("evacuate-units", { ["threshold"] = UnitsEvacuatedThreshold[Difficulty] }))
DestroyAirbases = AddSecondaryObjective(player, "destroy-nearby-soviet-airbases")
EvacuateMgg = AddSecondaryObjective(player, "evacuate-at-least-one-gap-generator")
end
end)
Trigger.OnAllKilledOrCaptured(SovietAirfields, function()
Utils.Do(Humans, function(player)
if player then
player.MarkCompletedObjective(DestroyAirbases)
end
end)
end)
SovietObjective = AddPrimaryObjective(Soviets, "")
if not Allies2 or Allies1.IsLocalPlayer then
Camera.Position = Allies1EntryPoint.CenterPosition
else
Camera.Position = Allies2EntryPoint.CenterPosition
end
if not Allies2 then
Allies1.Cash = 10000
Media.DisplayMessage(UserInterface.GetFluentMessage("transferring-funds"), UserInterface.GetFluentMessage("co-commander-missing"))
end
SpawnAlliedUnit(MobileConstructionVehicle)
Trigger.AfterDelay(AttackAtFrame[Difficulty], SpawnSovietUnits)
Trigger.AfterDelay(SovietParadropTicks[Difficulty], SendSovietParadrop)
Trigger.OnEnteredFootprint(MountainEntry, EvacuateAlliedUnit)
Trigger.OnEnteredFootprint(BridgeEntry, EvacuateAlliedUnit)
end

BIN
mods/ra/maps/exodus/map.bin Normal file

Binary file not shown.

View File

@@ -0,0 +1,2 @@
## rules.yaml
briefing = We are being flanked from both sides. Evacuate before the remaining Allied forces in the area are wiped out. Einstein has recently developed a technology which allows us to obscure units from the enemy. Pull out at least one prototype mobile gap generator intact.

BIN
mods/ra/maps/exodus/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

1387
mods/ra/maps/exodus/map.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
Player:
PlayerResources:
DefaultCash: 5000
World:
MissionData:
Briefing: briefing
LuaScript:
Scripts: campaign.lua, utils.lua, exodus.lua
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
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
powerproxy.paratroopers:
ParatroopersPower:
DropItems: E1,E1,E3,E3,E4
HBOX:
Buildable:
# Invalid targets for YakAttack
Prerequisites: ~disabled
E7:
Buildable:
Prerequisites: ~disabled
MIG:
Buildable:
Prerequisites: ~disabled
ARTY:
Buildable:
Prerequisites: ~disabled
AGUN:
Buildable:
Prerequisites: ~disabled
MSLO:
Buildable:
Prerequisites: ~disabled
SPEN:
Buildable:
Prerequisites: ~disabled
SYRD:
Buildable:
Prerequisites: ~disabled
IRON:
Buildable:
Prerequisites: ~disabled
PDOX:
Buildable:
Prerequisites: ~disabled
SAM:
Buildable:
Prerequisites: ~disabled
HPAD:
Buildable:
Prerequisites: ~disabled
AFLD:
Buildable:
Prerequisites: ~disabled
ATEK:
Buildable:
Prerequisites: ~disabled
STEK:
Buildable:
Prerequisites: ~disabled
4TNK:
Buildable:
Prerequisites: ~disabled
MCV:
Buildable:
Prerequisites: ~disabled
MNLY:
Buildable:
Prerequisites: ~disabled
TTNK:
Buildable:
Prerequisites: ~disabled
CTNK:
Buildable:
Prerequisites: ~disabled
MRJ:
Buildable:
Prerequisites: ~disabled
MGG:
Buildable:
Prerequisites: ~disabled
CreatesShroud:
Range: 6c0
GAP:
Buildable:
Prerequisites: ~disabled
QTNK:
Buildable:
Prerequisites: ~disabled
THF:
Buildable:
Prerequisites: ~disabled
CAMERA.Large:
Inherits: CAMERA
# Required for YakAttack to work
RevealsShroud:
Range: 49c0