Using the glory of regex, we can scrape any Lua script files that a map includes and locate calls to the UserInterface.Translate method. We can then treat them in the same way as C# fields marked with a TranslationReferenceAttribute. This allows the lint check to validate the translation invoked in the .lua script has a matching entry in the translation .ftl files, with all the required arguments (if any). We can also locate any calls to AddPrimaryObjective or AddSecondaryObjective defined by the utils.lua script, which also accept translation keys. The are a couple of restrictions: - When linting the map, we don't check for keys in the ftl file that are unused. This is because the linter doesn't load all the keys when checking maps. - In order to validate translation arguments with the regex, we require the Lua script to pass the table of arguments inline at the callsite. If it does not, we raise a warning so the user can adjust the code.
431 lines
16 KiB
Lua
431 lines
16 KiB
Lua
--[[
|
|
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.
|
|
]]
|
|
|
|
HarkonnenBase = { HarkonnenConstructionYard, HarkonnenWindTrap1, HarkonnenWindTrap2, HarkonnenWindTrap3, HarkonnenWindTrap4, HarkonnenWindTrap5, HarkonnenWindTrap6, HarkonnenWindTrap7, HarkonnenWindTrap8, HarkonnenSilo1, HarkonnenSilo2, HarkonnenSilo3, HarkonnenSilo4, HarkonnenGunTurret1, HarkonnenGunTurret2, HarkonnenGunTurret3, HarkonnenGunTurret4, HarkonnenGunTurret5, HarkonnenGunTurret6, HarkonnenGunTurret7, HarkonnenHeavyFactory, HarkonnenRefinery, HarkonnenOutpost, HarkonnenLightFactory }
|
|
SmugglerBase = { SmugglerWindTrap1, SmugglerWindTrap2 }
|
|
|
|
HarkonnenReinforcements =
|
|
{
|
|
easy =
|
|
{
|
|
{ "combat_tank_h", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "trooper", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "combat_tank_h", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper" }
|
|
},
|
|
|
|
normal =
|
|
{
|
|
{ "combat_tank_h", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "trooper", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "combat_tank_h", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "trike", "combat_tank_h", "light_inf", "trooper", "trooper", "quad" },
|
|
{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" }
|
|
},
|
|
|
|
hard =
|
|
{
|
|
{ "combat_tank_h", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "trooper", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "combat_tank_h", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper" },
|
|
{ "combat_tank_h", "trike", "combat_tank_h", "light_inf", "trooper", "trooper", "quad" },
|
|
{ "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" },
|
|
{ "combat_tank_h", "combat_tank_h", "trike", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" },
|
|
{ "combat_tank_h", "combat_tank_h", "combat_tank_h", "combat_tank_h", "combat_tank_h", "combat_tank_h" }
|
|
}
|
|
}
|
|
|
|
HarkonnenInfantryReinforcements =
|
|
{
|
|
normal =
|
|
{
|
|
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper", "trooper" }
|
|
},
|
|
|
|
hard =
|
|
{
|
|
{ "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper", "trooper" }
|
|
}
|
|
}
|
|
InfantryPath = { HarkonnenEntry3.Location }
|
|
|
|
HarkonnenAttackDelay =
|
|
{
|
|
easy = DateTime.Minutes(3),
|
|
normal = DateTime.Minutes(2) + DateTime.Seconds(20),
|
|
hard = DateTime.Minutes(1)
|
|
}
|
|
|
|
HarkonnenAttackWaves =
|
|
{
|
|
easy = 5,
|
|
normal = 7,
|
|
hard = 9
|
|
}
|
|
|
|
MercenaryReinforcements =
|
|
{
|
|
easy =
|
|
{
|
|
{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf" },
|
|
{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike" },
|
|
{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" }
|
|
},
|
|
|
|
normal =
|
|
{
|
|
{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike" },
|
|
{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf" },
|
|
{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper" },
|
|
{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike", "trike", "trike", "trike" },
|
|
{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" }
|
|
},
|
|
|
|
hard =
|
|
{
|
|
{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf" },
|
|
{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike" },
|
|
{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike", "trike", "trike", "trike" },
|
|
{ "combat_tank_o", "combat_tank_o", "quad", "quad", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper" },
|
|
{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" },
|
|
{ "combat_tank_o", "combat_tank_o", "quad", "quad", "trike", "trike", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper" },
|
|
{ "trike", "trike", "quad", "quad", "quad", "trike", "trike", "trike", "trike", "trike", "trike", "quad", "quad" },
|
|
{ "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "combat_tank_o", "trike", "trike", "quad", "quad", "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "trooper" }
|
|
}
|
|
}
|
|
|
|
MercenaryAttackDelay =
|
|
{
|
|
easy = DateTime.Minutes(2) + DateTime.Seconds(40),
|
|
normal = DateTime.Minutes(1) + DateTime.Seconds(50),
|
|
hard = DateTime.Minutes(1) + DateTime.Seconds(10)
|
|
}
|
|
|
|
MercenaryAttackWaves =
|
|
{
|
|
easy = 3,
|
|
normal = 6,
|
|
hard = 9
|
|
}
|
|
|
|
MercenarySpawn = { HarkonnenRally4.Location + CVec.New(2, -2) }
|
|
|
|
-- Ordos tanks because those were intended for the Smugglers not the Atreides
|
|
ContrabandReinforcements = { "mcv", "quad", "quad", "combat_tank_o", "combat_tank_o", "combat_tank_o" }
|
|
SmugglerReinforcements = { "quad", "quad", "trike", "trike" }
|
|
InitialHarkonnenReinforcements = { "trooper", "trooper", "quad", "quad", "trike", "trike", "trike", "light_inf" }
|
|
|
|
HarkonnenPaths =
|
|
{
|
|
{ HarkonnenEntry1.Location, HarkonnenRally1.Location },
|
|
{ HarkonnenEntry1.Location, HarkonnenRally3.Location },
|
|
{ HarkonnenEntry1.Location, HarkonnenRally2.Location },
|
|
{ HarkonnenEntry1.Location, HarkonnenRally4.Location },
|
|
{ HarkonnenEntry2.Location }
|
|
}
|
|
|
|
AtreidesReinforcements = { "trike", "combat_tank_a" }
|
|
AtreidesPath = { AtreidesEntry.Location, AtreidesRally.Location }
|
|
|
|
ContrabandTimes =
|
|
{
|
|
easy = DateTime.Minutes(10),
|
|
normal = DateTime.Minutes(5),
|
|
hard = DateTime.Minutes(2) + DateTime.Seconds(30)
|
|
}
|
|
|
|
Wave = 0
|
|
SendHarkonnen = function()
|
|
Trigger.AfterDelay(HarkonnenAttackDelay[Difficulty], function()
|
|
if Atreides.IsObjectiveCompleted(KillHarkonnen) then
|
|
return
|
|
end
|
|
|
|
Wave = Wave + 1
|
|
|
|
if InfantryReinforcements and Wave % 4 == 0 then
|
|
local inf = Reinforcements.Reinforce(Harkonnen, HarkonnenInfantryReinforcements[Difficulty][Wave/4], InfantryPath)
|
|
Utils.Do(inf, function(unit)
|
|
unit.AttackMove(HarkonnenAttackLocation)
|
|
IdleHunt(unit)
|
|
end)
|
|
end
|
|
|
|
local entryPath = Utils.Random(HarkonnenPaths)
|
|
local units = Reinforcements.ReinforceWithTransport(Harkonnen, "carryall.reinforce", HarkonnenReinforcements[Difficulty][Wave], entryPath, { entryPath[1] })[2]
|
|
Utils.Do(units, function(unit)
|
|
unit.AttackMove(HarkonnenAttackLocation)
|
|
IdleHunt(unit)
|
|
end)
|
|
|
|
if Wave < HarkonnenAttackWaves[Difficulty] then
|
|
SendHarkonnen()
|
|
return
|
|
end
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(3), function() LastHarkonnenArrived = true end)
|
|
end)
|
|
end
|
|
|
|
MercWave = 0
|
|
SendMercenaries = function()
|
|
Trigger.AfterDelay(MercenaryAttackDelay[Difficulty], function()
|
|
MercWave = MercWave + 1
|
|
|
|
Media.DisplayMessage(UserInterface.Translate("incoming-mercenary-force"), Mentat)
|
|
|
|
local units = Reinforcements.Reinforce(Mercenary, MercenaryReinforcements[Difficulty][MercWave], MercenarySpawn)
|
|
Utils.Do(units, function(unit)
|
|
unit.AttackMove(MercenaryAttackLocation1)
|
|
unit.AttackMove(MercenaryAttackLocation2)
|
|
IdleHunt(unit)
|
|
end)
|
|
|
|
if MercWave < MercenaryAttackWaves[Difficulty] then
|
|
SendMercenaries()
|
|
return
|
|
end
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(3), function() LastMercenariesArrived = true end)
|
|
end)
|
|
end
|
|
|
|
SendContraband = function(owner)
|
|
ContrabandArrived = true
|
|
UserInterface.SetMissionText(UserInterface.Translate("contraband-has-arrived"), Atreides.Color)
|
|
|
|
local units = SmugglerReinforcements
|
|
if owner == Atreides then
|
|
units = ContrabandReinforcements
|
|
end
|
|
|
|
Reinforcements.ReinforceWithTransport(owner, "frigate", units, { ContrabandEntry.Location, Starport.Location + CVec.New(1, 1) }, { ContrabandExit.Location })
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(3), function()
|
|
if owner == Atreides then
|
|
Atreides.MarkCompletedObjective(CaptureStarport)
|
|
Media.DisplayMessage(UserInterface.Translate("contraband-confiscated"), Mentat)
|
|
else
|
|
Atreides.MarkFailedObjective(CaptureStarport)
|
|
Media.DisplayMessage(UserInterface.Translate("contraband-not-confiscated"), Mentat)
|
|
end
|
|
end)
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(5), function()
|
|
UserInterface.SetMissionText("")
|
|
end)
|
|
end
|
|
|
|
SmugglersAttack = function()
|
|
Utils.Do(SmugglerBase, function(building)
|
|
if not building.IsDead and building.Owner == Smuggler then
|
|
building.Sell()
|
|
end
|
|
end)
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(1), function()
|
|
Utils.Do(Smuggler.GetGroundAttackers(), function(unit)
|
|
IdleHunt(unit)
|
|
end)
|
|
end)
|
|
end
|
|
|
|
AttackNotifier = 0
|
|
Tick = function()
|
|
if Atreides.HasNoRequiredUnits() then
|
|
Harkonnen.MarkCompletedObjective(KillAtreides)
|
|
end
|
|
|
|
if LastHarkonnenArrived and not Atreides.IsObjectiveCompleted(KillHarkonnen) and Harkonnen.HasNoRequiredUnits() then
|
|
Media.DisplayMessage(UserInterface.Translate("harkonnen-annihilated"), Mentat)
|
|
Atreides.MarkCompletedObjective(KillHarkonnen)
|
|
end
|
|
|
|
if LastMercenariesArrived and not Atreides.IsObjectiveCompleted(KillSmuggler) and Smuggler.HasNoRequiredUnits() and Mercenary.HasNoRequiredUnits() then
|
|
Media.DisplayMessage(UserInterface.Translate("smugglers-annihilated"), Mentat)
|
|
Atreides.MarkCompletedObjective(KillSmuggler)
|
|
end
|
|
|
|
if LastHarvesterEaten[Harkonnen] and DateTime.GameTime % DateTime.Seconds(10) == 0 then
|
|
local units = Harkonnen.GetActorsByType("harvester")
|
|
|
|
if #units > 0 then
|
|
LastHarvesterEaten[Harkonnen] = false
|
|
ProtectHarvester(units[1], Harkonnen, AttackGroupSize[Difficulty])
|
|
end
|
|
end
|
|
|
|
AttackNotifier = AttackNotifier - 1
|
|
|
|
if TimerTicks and not ContrabandArrived then
|
|
TimerTicks = TimerTicks - 1
|
|
if (TimerTicks % DateTime.Seconds(1)) == 0 then
|
|
local contrabandArrivesIn = UserInterface.Translate("contraband-arrives-in", { ["time"] = Utils.FormatTime(TimerTicks)})
|
|
UserInterface.SetMissionText(contrabandArrivesIn, Atreides.Color)
|
|
end
|
|
|
|
if TimerTicks <= 0 then
|
|
SendContraband(Smuggler)
|
|
end
|
|
end
|
|
end
|
|
|
|
WorldLoaded = function()
|
|
Harkonnen = Player.GetPlayer("Harkonnen")
|
|
Smuggler = Player.GetPlayer("Smugglers")
|
|
Mercenary = Player.GetPlayer("Mercenaries")
|
|
Atreides = Player.GetPlayer("Atreides")
|
|
|
|
InfantryReinforcements = Difficulty ~= "easy"
|
|
|
|
InitObjectives(Atreides)
|
|
KillAtreides = AddPrimaryObjective(Harkonnen, "")
|
|
CaptureBarracks = AddPrimaryObjective(Atreides, "capture-barracks-sietch-tabr")
|
|
KillHarkonnen = AddSecondaryObjective(Atreides, "annihilate-harkonnen-units-reinforcements")
|
|
CaptureStarport = AddSecondaryObjective(Atreides, "capture-smuggler-starport-confiscate-contraband")
|
|
|
|
Camera.Position = ARefinery.CenterPosition
|
|
HarkonnenAttackLocation = AtreidesRally.Location
|
|
MercenaryAttackLocation1 = Starport.Location + CVec.New(-16, 0)
|
|
MercenaryAttackLocation2 = Starport.Location
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(2), function()
|
|
TimerTicks = ContrabandTimes[Difficulty]
|
|
local time = Utils.FormatTime(TimerTicks)
|
|
local contrabandApproaching = UserInterface.Translate("contraband-approaching-starport-north-in", { ["time"] = time })
|
|
Media.DisplayMessage(contrabandApproaching, Mentat)
|
|
end)
|
|
|
|
Trigger.OnAllKilledOrCaptured(HarkonnenBase, function()
|
|
Utils.Do(Harkonnen.GetGroundAttackers(), IdleHunt)
|
|
end)
|
|
|
|
Trigger.OnKilled(Starport, function()
|
|
if not Atreides.IsObjectiveCompleted(CaptureStarport) then
|
|
ContrabandArrived = true
|
|
UserInterface.SetMissionText(UserInterface.Translate("starport-destroyed-no-contraband"), Atreides.Color)
|
|
Atreides.MarkFailedObjective(CaptureStarport)
|
|
SmugglersAttack()
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(15), function()
|
|
UserInterface.SetMissionText("")
|
|
end)
|
|
end
|
|
|
|
if DefendStarport then
|
|
Atreides.MarkFailedObjective(DefendStarport)
|
|
end
|
|
end)
|
|
Trigger.OnDamaged(Starport, function(_, attacker)
|
|
if Starport.Owner ~= Smuggler or attacker.IsDead or attacker.Owner ~= Atreides then
|
|
return
|
|
end
|
|
|
|
if AttackNotifier <= 0 then
|
|
AttackNotifier = DateTime.Seconds(10)
|
|
Media.DisplayMessage(UserInterface.Translate("do-not-destroy-starport"), Mentat)
|
|
|
|
local defenders = Smuggler.GetGroundAttackers()
|
|
if #defenders > 0 then
|
|
Utils.Do(defenders, function(unit)
|
|
unit.Guard(Starport)
|
|
end)
|
|
end
|
|
end
|
|
end)
|
|
Trigger.OnCapture(Starport, function()
|
|
DefendStarport = AddSecondaryObjective(Atreides, "defend-captured-starport")
|
|
|
|
Trigger.ClearAll(Starport)
|
|
Trigger.AfterDelay(0, function()
|
|
Trigger.OnRemovedFromWorld(Starport, function()
|
|
Atreides.MarkFailedObjective(DefendStarport)
|
|
end)
|
|
end)
|
|
|
|
if not ContrabandArrived then
|
|
SendContraband(Atreides)
|
|
end
|
|
SmugglersAttack()
|
|
end)
|
|
|
|
Trigger.OnKilled(HarkonnenBarracks, function()
|
|
Atreides.MarkFailedObjective(CaptureBarracks)
|
|
end)
|
|
Trigger.OnDamaged(HarkonnenBarracks, function()
|
|
if AttackNotifier <= 0 and HarkonnenBarracks.Health < HarkonnenBarracks.MaxHealth * 3/4 then
|
|
AttackNotifier = DateTime.Seconds(10)
|
|
Media.DisplayMessage(UserInterface.Translate("do-not-destroy-barracks"), Mentat)
|
|
end
|
|
end)
|
|
Trigger.OnCapture(HarkonnenBarracks, function()
|
|
Media.DisplayMessage(UserInterface.Translate("hostages-released"), Mentat)
|
|
|
|
if DefendStarport then
|
|
Atreides.MarkCompletedObjective(DefendStarport)
|
|
end
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(3), function()
|
|
Atreides.MarkCompletedObjective(CaptureBarracks)
|
|
end)
|
|
end)
|
|
|
|
SendHarkonnen()
|
|
Actor.Create("upgrade.barracks", true, { Owner = Harkonnen })
|
|
Actor.Create("upgrade.light", true, { Owner = Harkonnen })
|
|
Actor.Create("upgrade.heavy", true, { Owner = Harkonnen })
|
|
Trigger.AfterDelay(0, ActivateAI)
|
|
|
|
Trigger.AfterDelay(DateTime.Minutes(1), function()
|
|
Media.PlaySpeechNotification(Atreides, "Reinforce")
|
|
Reinforcements.ReinforceWithTransport(Atreides, "carryall.reinforce", AtreidesReinforcements, AtreidesPath, { AtreidesPath[1] })
|
|
end)
|
|
|
|
local smugglerWaypoint = SmugglerWaypoint1.Location
|
|
Trigger.OnEnteredFootprint({ smugglerWaypoint + CVec.New(-2, 0), smugglerWaypoint + CVec.New(-1, 0), smugglerWaypoint, smugglerWaypoint + CVec.New(1, -1), smugglerWaypoint + CVec.New(2, -1), SmugglerWaypoint3.Location }, function(a, id)
|
|
if not Warned and a.Owner == Atreides and a.Type ~= "carryall" then
|
|
Warned = true
|
|
Trigger.RemoveFootprintTrigger(id)
|
|
Media.DisplayMessage(UserInterface.Translate("stay-away-from-starport"), UserInterface.Translate("smuggler-leader"))
|
|
end
|
|
end)
|
|
|
|
Trigger.OnEnteredFootprint({ SmugglerWaypoint2.Location }, function(a, id)
|
|
if not Paid and a.Owner == Atreides and a.Type ~= "carryall" then
|
|
Paid = true
|
|
Trigger.RemoveFootprintTrigger(id)
|
|
Media.DisplayMessage(UserInterface.Translate("were-warned-will-pay"), UserInterface.Translate("smuggler-leader"))
|
|
Utils.Do(Smuggler.GetGroundAttackers(), function(unit)
|
|
unit.AttackMove(SmugglerWaypoint2.Location)
|
|
end)
|
|
|
|
Trigger.AfterDelay(DateTime.Seconds(3), function()
|
|
KillSmuggler = AddSecondaryObjective(Atreides, "destroy-smugglers-mercenaries")
|
|
SendMercenaries()
|
|
end)
|
|
end
|
|
end)
|
|
|
|
Trigger.OnEnteredProximityTrigger(HarkonnenBarracks.CenterPosition, WDist.New(5 * 1024), function(a, id)
|
|
if a.Owner == Atreides and a.Type ~= "carryall" then
|
|
Trigger.RemoveProximityTrigger(id)
|
|
Media.DisplayMessage(UserInterface.Translate("capture-harkonnen-barracks-release-hostages"), Mentat)
|
|
StopInfantryProduction = true
|
|
end
|
|
end)
|
|
end
|