diff --git a/OpenRA.sln b/OpenRA.sln index d36c0523d3..a006c71a39 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -132,6 +132,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dune 2000 Lua scripts", "Du 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-03b\ordos03b.lua = mods\d2k\maps\ordos-03b\ordos03b.lua + mods\d2k\maps\ordos-03b\ordos03b-AI.lua = mods\d2k\maps\ordos-03b\ordos03b-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-03b/map.bin b/mods/d2k/maps/ordos-03b/map.bin new file mode 100644 index 0000000000..18a28ffcbb Binary files /dev/null and b/mods/d2k/maps/ordos-03b/map.bin differ diff --git a/mods/d2k/maps/ordos-03b/map.png b/mods/d2k/maps/ordos-03b/map.png new file mode 100644 index 0000000000..f6cc983ce6 Binary files /dev/null and b/mods/d2k/maps/ordos-03b/map.png differ diff --git a/mods/d2k/maps/ordos-03b/map.yaml b/mods/d2k/maps/ordos-03b/map.yaml new file mode 100644 index 0000000000..9338de82e3 --- /dev/null +++ b/mods/d2k/maps/ordos-03b/map.yaml @@ -0,0 +1,257 @@ +MapFormat: 11 + +RequiresMod: d2k + +Title: Ordos 03b + +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: spicebloom.spawnpoint + Location: 63,5 + Owner: Neutral + OConyard: construction_yard + Location: 33,7 + Owner: Ordos + Actor2: light_inf + Location: 30,8 + Owner: Ordos + Actor3: trooper + Location: 30,10 + Owner: Ordos + Actor4: quad + Location: 31,10 + Owner: Ordos + Actor5: quad + Location: 38,11 + Owner: Ordos + Actor6: spicebloom.spawnpoint + Location: 4,12 + Owner: Neutral + Actor7: trooper + Location: 34,12 + Owner: Ordos + Actor8: light_inf + Location: 37,12 + Owner: Ordos + Actor9: light_inf + Location: 31,13 + Owner: Ordos + Actor10: raider + Location: 35,13 + Owner: Ordos + Actor11: spicebloom.spawnpoint + Location: 52,22 + Owner: Neutral + HConyard: construction_yard + Location: 45,29 + Owner: Harkonnen + HRefinery: refinery + Location: 50,29 + Owner: Harkonnen + Actor14: light_inf + Location: 55,30 + Owner: Harkonnen + HSilo1: silo + Location: 49,33 + Owner: Harkonnen + HSilo2: silo + Location: 50,33 + Owner: Harkonnen + Actor17: wall + Location: 37,34 + Owner: Harkonnen + HBarracks: barracks + Location: 45,34 + Owner: Harkonnen + HSilo3: silo + Location: 49,34 + Owner: Harkonnen + HSilo4: silo + Location: 50,34 + Owner: Harkonnen + Actor21: wall + Location: 37,35 + Owner: Harkonnen + HLightFactory: light_factory + Location: 40,35 + Owner: Harkonnen + Actor23: wall + Location: 37,36 + Owner: Harkonnen + Actor24: wall + Location: 38,36 + Owner: Harkonnen + HOutpost: outpost + Location: 52,36 + Owner: Harkonnen + Actor26: wall + Location: 38,37 + Owner: Harkonnen + Actor27: wall + Location: 38,38 + Owner: Harkonnen + Actor28: wall + Location: 38,39 + Owner: Harkonnen + Actor29: wall + Location: 39,39 + Owner: Harkonnen + Actor30: wall + Location: 40,39 + Owner: Harkonnen + HWindTrap1: wind_trap + Location: 49,39 + Owner: Harkonnen + HWindTrap2: wind_trap + Location: 52,41 + Owner: Harkonnen + HWindTrap3: wind_trap + Location: 46,42 + Owner: Harkonnen + Actor34: wall + Location: 40,43 + Owner: Harkonnen + Actor35: trooper + Location: 41,43 + Owner: Harkonnen + Actor36: wall + Location: 40,44 + Owner: Harkonnen + Actor37: wall + Location: 41,44 + Owner: Harkonnen + Actor38: wall + Location: 42,44 + Owner: Harkonnen + Actor39: wall + Location: 43,44 + Owner: Harkonnen + Actor40: wall + Location: 44,44 + Owner: Harkonnen + HWindTrap4: wind_trap + Location: 55,44 + Owner: Harkonnen + Actor42: wall + Location: 44,45 + Owner: Harkonnen + Actor43: wall + Location: 44,46 + Owner: Harkonnen + Actor44: wall + Location: 44,47 + Owner: Harkonnen + Actor45: wall + Location: 44,48 + Owner: Harkonnen + Actor46: wormspawner + Location: 19,55 + Owner: Creeps + OrdosRally1: waypoint + Owner: Neutral + Location: 27,12 + OrdosEntry1: waypoint + Owner: Neutral + Location: 27,2 + OrdosRally2: waypoint + Owner: Neutral + Location: 38,13 + OrdosEntry2: waypoint + Owner: Neutral + Location: 38,2 + OrdosRally3: waypoint + Owner: Neutral + Location: 35,20 + OrdosEntry3: waypoint + Owner: Neutral + Location: 35,2 + HarkonnenRally1: waypoint + Owner: Neutral + Location: 12,61 + HarkonnenEntry1: waypoint + Owner: Neutral + Location: 12,65 + HarkonnenRally2: waypoint + Owner: Neutral + Location: 6,57 + HarkonnenEntry2: waypoint + Owner: Neutral + Location: 2,57 + HarkonnenRally3: waypoint + Owner: Neutral + Location: 6,44 + HarkonnenEntry3: waypoint + Owner: Neutral + Location: 2,44 + HarkonnenRally4: waypoint + Owner: Neutral + Location: 48,37 + HarkonnenEntry4: waypoint + Owner: Neutral + Location: 65,37 + HarkonnenRally5: waypoint + Owner: Neutral + Location: 51,44 + HarkonnenEntry5: waypoint + Owner: Neutral + Location: 65,44 + HarkonnenRally6: waypoint + Owner: Neutral + Location: 45,43 + HarkonnenEntry6: waypoint + Owner: Neutral + Location: 45,65 + HarkonnenRally7: waypoint + Owner: Neutral + Location: 40,53 + HarkonnenEntry7: waypoint + Owner: Neutral + Location: 40,65 + HarkonnenRally8: waypoint + Owner: Neutral + Location: 11,20 + HarkonnenEntry8: waypoint + Owner: Neutral + Location: 2,20 + +Rules: d2k|rules/campaign-rules.yaml, rules.yaml diff --git a/mods/d2k/maps/ordos-03b/ordos03b-AI.lua b/mods/d2k/maps/ordos-03b/ordos03b-AI.lua new file mode 100644 index 0000000000..29fc817363 --- /dev/null +++ b/mods/d2k/maps/ordos-03b/ordos03b-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-03b/ordos03b.lua b/mods/d2k/maps/ordos-03b/ordos03b.lua new file mode 100644 index 0000000000..0ade233892 --- /dev/null +++ b/mods/d2k/maps/ordos-03b/ordos03b.lua @@ -0,0 +1,217 @@ +HarkonnenBase = { HBarracks, HWindTrap1, HWindTrap2, HWindTrap3, HWindTrap4, 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 = +{ + { HarkonnenEntry5.Location, HarkonnenRally5.Location }, + { HarkonnenEntry6.Location, HarkonnenRally6.Location }, + { HarkonnenEntry7.Location, HarkonnenRally7.Location }, + { HarkonnenEntry8.Location, HarkonnenRally8.Location } +} + +HarkonnenHunters = { + { "quad", "quad", "quad" }, + { "light_inf", "light_inf", "light_inf", "light_inf", "light_inf" }, + { "trike", "trike" } +} + +HarkonnenHunterPaths = +{ + { HarkonnenEntry1.Location, HarkonnenRally1.Location }, + { HarkonnenEntry2.Location, HarkonnenRally2.Location }, + { HarkonnenEntry3.Location, HarkonnenRally3.Location } +} + +HarkonnenInitialReinforcements = { "light_inf", "light_inf", "quad", "quad", "trike", "trike", "trooper", "trooper" } +HarkonnenInitialPath = { HarkonnenEntry4.Location, HarkonnenRally4.Location } + +OrdosReinforcements = +{ + { "quad", "quad" }, + { "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "raider", "raider" }, + { "raider", "raider", "raider" } +} + +OrdosPaths = +{ + { OrdosEntry1.Location, OrdosRally1.Location }, + { OrdosEntry2.Location, OrdosRally2.Location }, + { OrdosEntry3.Location, OrdosRally3.Location } +} + +OrdosReinforcementDelays = +{ + DateTime.Minutes(2) + DateTime.Seconds(30), + DateTime.Minutes(1) + DateTime.Seconds(15), + DateTime.Minutes(3) + DateTime.Seconds(15) +} + +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 + +SendOrdosReinforcements = function(timer, unit, path) + Trigger.AfterDelay(timer, function() + Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", unit, path, { path[1] }) + + SendOrdosReinforcements(timer, unit, path) + 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() + + SendOrdosReinforcements(OrdosReinforcementDelays[1], OrdosReinforcements[1], OrdosPaths[1]) + SendOrdosReinforcements(OrdosReinforcementDelays[3], OrdosReinforcements[3], OrdosPaths[3]) + + Trigger.AfterDelay(OrdosReinforcementDelays[2], function() + Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", OrdosReinforcements[2], OrdosPaths[2], { OrdosPaths[2][1] }) + end) + + SendHunters(HarkonnenBaseAreaTrigger, HarkonnenHunters[1], HarkonnenHunterPaths[1], HuntersSent1) + SendHunters(HarkonnenBaseAreaTrigger, HarkonnenHunters[2], HarkonnenHunterPaths[2], HuntersSent2) + SendHunters(HarkonnenBaseAreaTrigger, HarkonnenHunters[3], HarkonnenHunterPaths[3], HuntersSent3) +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-03b/rules.yaml b/mods/d2k/maps/ordos-03b/rules.yaml new file mode 100644 index 0000000000..ab94d01146 --- /dev/null +++ b/mods/d2k/maps/ordos-03b/rules.yaml @@ -0,0 +1,56 @@ +Player: + PlayerResources: + DefaultCash: 5000 + +World: + LuaScript: + Scripts: ordos03b.lua, ordos03b-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 732d629e9a..6ed0825041 100644 --- a/mods/d2k/missions.yaml +++ b/mods/d2k/missions.yaml @@ -14,6 +14,7 @@ Ordos Campaign: ./mods/d2k/maps/ordos-02a ./mods/d2k/maps/ordos-02b ./mods/d2k/maps/ordos-03a + ./mods/d2k/maps/ordos-03b ./mods/d2k/maps/ordos-04 Harkonnen Campaign: