Initial commit: OpenRA game engine
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:
346
mods/ra/maps/exodus/exodus.lua
Normal file
346
mods/ra/maps/exodus/exodus.lua
Normal 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
BIN
mods/ra/maps/exodus/map.bin
Normal file
Binary file not shown.
2
mods/ra/maps/exodus/map.ftl
Normal file
2
mods/ra/maps/exodus/map.ftl
Normal 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
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
1387
mods/ra/maps/exodus/map.yaml
Normal file
File diff suppressed because it is too large
Load Diff
145
mods/ra/maps/exodus/rules.yaml
Normal file
145
mods/ra/maps/exodus/rules.yaml
Normal 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
|
||||
Reference in New Issue
Block a user