diff --git a/OpenRA.sln b/OpenRA.sln index b2acf4d482..d36c0523d3 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -130,6 +130,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dune 2000 Lua scripts", "Du mods\d2k\maps\ordos-02a\ordos02a-AI.lua = mods\d2k\maps\ordos-02a\ordos02a-AI.lua mods\d2k\maps\ordos-02b\ordos02b.lua = mods\d2k\maps\ordos-02b\ordos02b.lua mods\d2k\maps\ordos-02b\ordos02b-AI.lua = mods\d2k\maps\ordos-02b\ordos02b-AI.lua + mods\d2k\maps\ordos-03a\ordos03a.lua = mods\d2k\maps\ordos-03a\ordos03a.lua + mods\d2k\maps\ordos-03a\ordos03a-AI.lua = mods\d2k\maps\ordos-03a\ordos03a-AI.lua mods\d2k\maps\ordos-04\ordos04.lua = mods\d2k\maps\ordos-04\ordos04.lua mods\d2k\maps\ordos-04\ordos04-AI.lua = mods\d2k\maps\ordos-04\ordos04-AI.lua EndProjectSection diff --git a/mods/d2k/maps/ordos-03a/map.bin b/mods/d2k/maps/ordos-03a/map.bin new file mode 100644 index 0000000000..cb5c56e176 Binary files /dev/null and b/mods/d2k/maps/ordos-03a/map.bin differ diff --git a/mods/d2k/maps/ordos-03a/map.png b/mods/d2k/maps/ordos-03a/map.png new file mode 100644 index 0000000000..39f0d79c6e Binary files /dev/null and b/mods/d2k/maps/ordos-03a/map.png differ diff --git a/mods/d2k/maps/ordos-03a/map.yaml b/mods/d2k/maps/ordos-03a/map.yaml new file mode 100644 index 0000000000..7d65d94ba0 --- /dev/null +++ b/mods/d2k/maps/ordos-03a/map.yaml @@ -0,0 +1,146 @@ +MapFormat: 11 + +RequiresMod: d2k + +Title: Ordos 03a + +Author: Westwood Studios + +Tileset: ARRAKIS + +MapSize: 68,68 + +Bounds: 2,2,64,64 + +Visibility: MissionSelector + +Categories: Campaign + +LockPreview: True + +Players: + PlayerReference@Neutral: + Name: Neutral + OwnsWorld: True + NonCombatant: True + Faction: Random + PlayerReference@Creeps: + Name: Creeps + NonCombatant: True + Faction: Random + Enemies: Ordos, Harkonnen + PlayerReference@Ordos: + Name: Ordos + Playable: True + LockFaction: True + Faction: ordos + LockColor: True + Color: B3EAA5 + Enemies: Harkonnen, Creeps + PlayerReference@Harkonnen: + Name: Harkonnen + LockFaction: True + Faction: harkonnen + LockColor: True + Color: FE0000 + Enemies: Ordos + +Actors: + Actor0: light_inf + Location: 45,38 + Owner: Ordos + Actor1: trooper + Location: 50,38 + Owner: Ordos + Actor2: raider + Location: 44,41 + Owner: Ordos + OConyard: construction_yard + Location: 48,41 + Owner: Ordos + Actor4: light_inf + Location: 51,42 + Owner: Ordos + Actor5: quad + Location: 46,44 + Owner: Ordos + Actor6: wormspawner + Location: 26,46 + Owner: Creeps + Actor7: trike + Location: 11,48 + Owner: Harkonnen + Actor8: quad + Location: 3,49 + Owner: Harkonnen + HRefinery: refinery + Location: 6,50 + Owner: Harkonnen + HWindTrap1: wind_trap + Location: 10,53 + Owner: Harkonnen + HLightFactory: light_factory + Location: 3,54 + Owner: Harkonnen + HBarracks: barracks + Location: 7,56 + Owner: Harkonnen + HSilo2: silo + Location: 3,59 + Owner: Harkonnen + HOutpost: outpost + Location: 8,59 + Owner: Harkonnen + OrdosRally: waypoint + Owner: Neutral + Location: 51,39 + OrdosEntry: waypoint + Owner: Neutral + Location: 65,39 + HarkonnenRally1: waypoint + Owner: Neutral + Location: 41,47 + HarkonnenEntry1: waypoint + Owner: Neutral + Location: 41,65 + HarkonnenRally2: waypoint + Owner: Neutral + Location: 40,53 + HarkonnenEntry2: waypoint + Owner: Neutral + Location: 65,53 + HarkonnenRally3: waypoint + Owner: Neutral + Location: 11,20 + HarkonnenEntry3: waypoint + Owner: Neutral + Location: 2,20 + HarkonnenRally4: waypoint + Owner: Neutral + Location: 7,54 + HarkonnenEntry4: waypoint + Owner: Neutral + Location: 2,54 + HarkonnenRally5: waypoint + Owner: Neutral + Location: 12,61 + HarkonnenEntry5: waypoint + Owner: Neutral + Location: 12,65 + HSilo1: silo + Owner: Harkonnen + Location: 4,59 + HWindTrap2: wind_trap + Owner: Harkonnen + Location: 6,59 + HConyard: construction_yard + Owner: Harkonnen + Location: 3,62 + HSilo3: silo + Owner: Harkonnen + Location: 3,60 + HSilo4: silo + Owner: Harkonnen + Location: 4,60 + +Rules: d2k|rules/campaign-rules.yaml, rules.yaml diff --git a/mods/d2k/maps/ordos-03a/ordos03a-AI.lua b/mods/d2k/maps/ordos-03a/ordos03a-AI.lua new file mode 100644 index 0000000000..29fc817363 --- /dev/null +++ b/mods/d2k/maps/ordos-03a/ordos03a-AI.lua @@ -0,0 +1,159 @@ +IdlingUnits = { } + +AttackGroupSize = +{ + easy = 6, + normal = 8, + hard = 10 +} + +AttackDelays = +{ + easy = { DateTime.Seconds(4), DateTime.Seconds(9) }, + normal = { DateTime.Seconds(2), DateTime.Seconds(7) }, + hard = { DateTime.Seconds(1), DateTime.Seconds(5) } +} + +HarkonnenInfantryTypes = { "light_inf", "light_inf", "light_inf", "trooper", "trooper" } +HarkonnenVehicleTypes = { "trike", "trike", "quad" } + +HarvesterKilled = true + +IdleHunt = function(unit) if not unit.IsDead then Trigger.OnIdle(unit, unit.Hunt) end end + +SetupAttackGroup = function() + local units = { } + + for i = 0, AttackGroupSize[Map.LobbyOption("difficulty")], 1 do + if #IdlingUnits == 0 then + return units + end + + local number = Utils.RandomInteger(1, #IdlingUnits + 1) + + if IdlingUnits[number] and not IdlingUnits[number].IsDead then + units[i] = IdlingUnits[number] + table.remove(IdlingUnits, number) + end + end + + return units +end + +SendAttack = function() + if IsAttacking then + return + end + IsAttacking = true + HoldProduction = true + + local units = SetupAttackGroup() + Utils.Do(units, function(unit) + IdleHunt(unit) + end) + + Trigger.OnAllRemovedFromWorld(units, function() + IsAttacking = false + HoldProduction = false + end) +end + +ProtectHarvester = function(unit) + DefendActor(unit) + Trigger.OnKilled(unit, function() HarvesterKilled = true end) +end + +DefendActor = function(unit) + Trigger.OnDamaged(unit, function(self, attacker) + if AttackOnGoing then + return + end + AttackOnGoing = true + + local Guards = SetupAttackGroup() + + if #Guards <= 0 then + AttackOnGoing = false + return + end + + Utils.Do(Guards, function(unit) + if not self.IsDead then + unit.AttackMove(self.Location) + end + IdleHunt(unit) + end) + + Trigger.OnAllRemovedFromWorld(Guards, function() AttackOnGoing = false end) + end) +end + +InitAIUnits = function() + IdlingUnits = Reinforcements.Reinforce(harkonnen, HarkonnenInitialReinforcements, HarkonnenInitialPath) + + Utils.Do(HarkonnenBase, function(actor) + DefendActor(actor) + Trigger.OnDamaged(actor, function(building) + if building.Health < building.MaxHealth * 3/4 then + building.StartBuildingRepairs() + end + end) + end) +end + +ProduceInfantry = function() + if HBarracks.IsDead then + return + end + + if HoldProduction then + Trigger.AfterDelay(DateTime.Minutes(1), ProduceInfantry) + return + end + + local delay = Utils.RandomInteger(AttackDelays[Map.LobbyOption("difficulty")][1], AttackDelays[Map.LobbyOption("difficulty")][2] + 1) + local toBuild = { Utils.Random(HarkonnenInfantryTypes) } + harkonnen.Build(toBuild, function(unit) + IdlingUnits[#IdlingUnits + 1] = unit[1] + Trigger.AfterDelay(delay, ProduceInfantry) + + if #IdlingUnits >= (AttackGroupSize[Map.LobbyOption("difficulty")] * 2.5) then + SendAttack() + end + end) +end + +ProduceVehicles = function() + if HLightFactory.IsDead then + return + end + + if HoldProduction then + Trigger.AfterDelay(DateTime.Minutes(1), ProduceVehicles) + return + end + + local delay = Utils.RandomInteger(AttackDelays[Map.LobbyOption("difficulty")][1], AttackDelays[Map.LobbyOption("difficulty")][2] + 1) + local toBuild = { Utils.Random(HarkonnenVehicleTypes) } + harkonnen.Build(toBuild, function(unit) + IdlingUnits[#IdlingUnits + 1] = unit[1] + Trigger.AfterDelay(delay, ProduceVehicles) + + if #IdlingUnits >= (AttackGroupSize[Map.LobbyOption("difficulty")] * 2.5) then + SendAttack() + end + end) +end + +ActivateAI = function() + Trigger.AfterDelay(0, InitAIUnits) + + HConyard.Produce(OrdosUpgrades[1]) + HConyard.Produce(OrdosUpgrades[2]) + + -- Finish the upgrades first before trying to build something + Trigger.AfterDelay(DateTime.Seconds(14), function() + ProduceInfantry() + ProduceVehicles() + end) +end diff --git a/mods/d2k/maps/ordos-03a/ordos03a.lua b/mods/d2k/maps/ordos-03a/ordos03a.lua new file mode 100644 index 0000000000..9efa91f242 --- /dev/null +++ b/mods/d2k/maps/ordos-03a/ordos03a.lua @@ -0,0 +1,175 @@ +HarkonnenBase = { HBarracks, HWindTrap1, HWindTrap2, HLightFactory, HOutpost, HConyard, HRefinery, HSilo1, HSilo2, HSilo3, HSilo4 } +HarkonnenBaseAreaTrigger = { CPos.New(2, 58), CPos.New(3, 58), CPos.New(4, 58), CPos.New(5, 58), CPos.New(6, 58), CPos.New(7, 58), CPos.New(8, 58), CPos.New(9, 58), CPos.New(10, 58), CPos.New(11, 58), CPos.New(12, 58), CPos.New(13, 58), CPos.New(14, 58), CPos.New(15, 58), CPos.New(16, 58), CPos.New(16, 59), CPos.New(16, 60) } + +HarkonnenReinforcements = +{ + easy = + { + { "light_inf", "trike", "trooper" }, + { "light_inf", "trike", "quad" }, + { "light_inf", "light_inf", "trooper", "trike", "trike", "quad" } + }, + + normal = + { + { "light_inf", "trike", "trooper" }, + { "light_inf", "trike", "trike" }, + { "light_inf", "light_inf", "trooper", "trike", "trike", "quad" }, + { "light_inf", "light_inf", "trooper", "trooper" }, + { "light_inf", "light_inf", "light_inf", "light_inf" }, + { "light_inf", "trike", "quad", "quad" } + }, + + hard = + { + { "trike", "trike", "quad" }, + { "light_inf", "trike", "trike" }, + { "trooper", "trooper", "light_inf", "trike" }, + { "light_inf", "light_inf", "light_inf", "trike", "trike" }, + { "light_inf", "light_inf", "trooper", "trooper" }, + { "trike", "trike", "quad", "quad", "quad", "trike" }, + { "light_inf", "light_inf", "light_inf", "trike", "trike" }, + { "light_inf", "trike", "light_inf", "trooper", "trooper", "quad" }, + { "trike", "trike", "quad", "quad", "quad", "trike" } + } +} + +HarkonnenAttackDelay = +{ + easy = DateTime.Minutes(5), + normal = DateTime.Minutes(2) + DateTime.Seconds(40), + hard = DateTime.Minutes(1) + DateTime.Seconds(20) +} + +HarkonnenAttackWaves = +{ + easy = 3, + normal = 6, + hard = 9 +} + +HarkonnenPaths = +{ + { HarkonnenEntry1.Location, HarkonnenRally1.Location }, + { HarkonnenEntry2.Location, HarkonnenRally2.Location }, + { HarkonnenEntry3.Location, HarkonnenRally3.Location } +} + +HarkonnenHunters = { "light_inf", "light_inf", "trike", "quad" } +HarkonnenInitialReinforcements = { "light_inf", "light_inf", "quad", "quad", "trike", "trike", "trooper", "trooper" } + +HarkonnenHunterPath = { HarkonnenEntry5.Location, HarkonnenRally5.Location } +HarkonnenInitialPath = { HarkonnenEntry4.Location, HarkonnenRally4.Location } + +OrdosReinforcements = { "quad", "raider" } +OrdosPath = { OrdosEntry.Location, OrdosRally.Location } + +OrdosBaseBuildings = { "barracks", "light_factory" } +OrdosUpgrades = { "upgrade.barracks", "upgrade.light" } + +wave = 0 +SendHarkonnen = function() + Trigger.AfterDelay(HarkonnenAttackDelay[Map.LobbyOption("difficulty")], function() + if player.IsObjectiveCompleted(KillHarkonnen) then + return + end + + wave = wave + 1 + if wave > HarkonnenAttackWaves[Map.LobbyOption("difficulty")] then + return + end + + local path = Utils.Random(HarkonnenPaths) + local units = Reinforcements.ReinforceWithTransport(harkonnen, "carryall.reinforce", HarkonnenReinforcements[Map.LobbyOption("difficulty")][wave], path, { path[1] })[2] + Utils.Do(units, IdleHunt) + + SendHarkonnen() + end) +end + +MessageCheck = function(index) + return #player.GetActorsByType(OrdosBaseBuildings[index]) > 0 and not player.HasPrerequisites({ OrdosUpgrades[index] }) +end + +SendHunters = function(areaTrigger, unit, path, check) + Trigger.OnEnteredFootprint(areaTrigger, function(a, id) + if not check and a.Owner == player then + local units = Reinforcements.ReinforceWithTransport(harkonnen, "carryall.reinforce", unit, path, { path[1] })[2] + Utils.Do(units, IdleHunt) + check = true + end + end) +end + +Tick = function() + if player.HasNoRequiredUnits() then + harkonnen.MarkCompletedObjective(KillOrdos) + end + + if harkonnen.HasNoRequiredUnits() and not player.IsObjectiveCompleted(KillHarkonnen) then + Media.DisplayMessage("The Harkonnen have been annihilated!", "Mentat") + player.MarkCompletedObjective(KillHarkonnen) + end + + if DateTime.GameTime % DateTime.Seconds(30) and HarvesterKilled then + local units = harkonnen.GetActorsByType("harvester") + + if #units > 0 then + HarvesterKilled = false + ProtectHarvester(units[1]) + end + end + + if DateTime.GameTime % DateTime.Seconds(32) == 0 and (MessageCheck(1) or MessageCheck(2)) then + Media.DisplayMessage("Upgrade barracks and light factory to produce more advanced units.", "Mentat") + end +end + +WorldLoaded = function() + harkonnen = Player.GetPlayer("Harkonnen") + player = Player.GetPlayer("Ordos") + + InitObjectives() + + Camera.Position = OConyard.CenterPosition + + Trigger.OnAllKilled(HarkonnenBase, function() + Utils.Do(harkonnen.GetGroundAttackers(), IdleHunt) + end) + + SendHarkonnen() + ActivateAI() + + Trigger.AfterDelay(DateTime.Minutes(2) + DateTime.Seconds(30), function() + Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", OrdosReinforcements, OrdosPath, { OrdosPath[1] }) + end) + + SendHunters(HarkonnenBaseAreaTrigger, HarkonnenHunters, HarkonnenHunterPath, HuntersSent) +end + +InitObjectives = function() + Trigger.OnObjectiveAdded(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective") + end) + + KillOrdos = harkonnen.AddPrimaryObjective("Kill all Ordos units.") + KillHarkonnen = player.AddPrimaryObjective("Eliminate all Harkonnen units and reinforcements\nin the area.") + + Trigger.OnObjectiveCompleted(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed") + end) + Trigger.OnObjectiveFailed(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed") + end) + + Trigger.OnPlayerLost(player, function() + Trigger.AfterDelay(DateTime.Seconds(1), function() + Media.PlaySpeechNotification(player, "Lose") + end) + end) + Trigger.OnPlayerWon(player, function() + Trigger.AfterDelay(DateTime.Seconds(1), function() + Media.PlaySpeechNotification(player, "Win") + end) + end) +end diff --git a/mods/d2k/maps/ordos-03a/rules.yaml b/mods/d2k/maps/ordos-03a/rules.yaml new file mode 100644 index 0000000000..6afd90147e --- /dev/null +++ b/mods/d2k/maps/ordos-03a/rules.yaml @@ -0,0 +1,56 @@ +Player: + PlayerResources: + DefaultCash: 5000 + +World: + LuaScript: + Scripts: ordos03a.lua, ordos03a-AI.lua + MissionData: + Briefing: The Harkonnen hinder the production of Spice. A Harkonnen attack will disrupt efficient production. Inefficiency cannot be tolerated. The Harkonnen must be eliminated.\n\nNew weapons are available - the Quads. Newer weapons are more powerful. Powerful weapons ensure victory. + BriefingVideo: O_BR03_E.VQA + MapOptions: + TechLevel: low + ScriptLobbyDropdown@difficulty: + ID: difficulty + Label: Difficulty + Values: + easy: Easy + normal: Normal + hard: Hard + Default: easy + +carryall.reinforce: + Cargo: + MaxWeight: 10 + +concreteb: + Buildable: + Prerequisites: ~disabled + +heavy_factory: + Buildable: + Prerequisites: ~disabled + +medium_gun_turret: + Buildable: + Prerequisites: ~disabled + +outpost: + Buildable: + Prerequisites: barracks + +quad: + Buildable: + Prerequisites: upgrade.light + +trooper: + Buildable: + Prerequisites: upgrade.barracks + +upgrade.conyard: + Buildable: + Prerequisites: ~disabled + +upgrade.heavy: + Buildable: + Prerequisites: ~disabled diff --git a/mods/d2k/missions.yaml b/mods/d2k/missions.yaml index a16a9b0007..732d629e9a 100644 --- a/mods/d2k/missions.yaml +++ b/mods/d2k/missions.yaml @@ -13,6 +13,7 @@ Ordos Campaign: ./mods/d2k/maps/ordos-01b ./mods/d2k/maps/ordos-02a ./mods/d2k/maps/ordos-02b + ./mods/d2k/maps/ordos-03a ./mods/d2k/maps/ordos-04 Harkonnen Campaign: