diff --git a/mods/d2k/maps/ordos-02a/map.bin b/mods/d2k/maps/ordos-02a/map.bin new file mode 100644 index 0000000000..c9b708ea7e Binary files /dev/null and b/mods/d2k/maps/ordos-02a/map.bin differ diff --git a/mods/d2k/maps/ordos-02a/map.png b/mods/d2k/maps/ordos-02a/map.png new file mode 100644 index 0000000000..852d270da3 Binary files /dev/null and b/mods/d2k/maps/ordos-02a/map.png differ diff --git a/mods/d2k/maps/ordos-02a/map.yaml b/mods/d2k/maps/ordos-02a/map.yaml new file mode 100644 index 0000000000..3cde25a582 --- /dev/null +++ b/mods/d2k/maps/ordos-02a/map.yaml @@ -0,0 +1,155 @@ +MapFormat: 11 + +RequiresMod: d2k + +Title: Ordos 02a + +Author: Westwood Studios + +Tileset: ARRAKIS + +MapSize: 52,52 + +Bounds: 2,2,48,48 + +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: 7,18 + Owner: Neutral + Actor1: light_inf + Location: 18,25 + Owner: Ordos + Actor2: raider + Location: 21,26 + Owner: Ordos + Actor3: light_inf + Location: 22,26 + Owner: Ordos + OConyard: construction_yard + Location: 18,27 + Owner: Ordos + Actor5: raider + Location: 16,28 + Owner: Ordos + Actor6: raider + Location: 22,28 + Owner: Ordos + Actor7: light_inf + Location: 17,30 + Owner: Ordos + Actor8: light_inf + Location: 21,30 + Owner: Ordos + Actor9: wormspawner + Location: 20,37 + Owner: Creeps + Actor10: light_inf + Location: 27,40 + Owner: Harkonnen + Actor11: trike + Location: 31,41 + Owner: Harkonnen + Actor12: light_inf + Location: 34,41 + Owner: Harkonnen + HOutpost: outpost + Location: 25,42 + Owner: Harkonnen + Actor14: trike + Location: 29,42 + Owner: Harkonnen + Actor15: light_inf + Location: 36,42 + Owner: Harkonnen + HPower2: wind_trap + Location: 30,44 + Owner: Harkonnen + HBarracks: barracks + Location: 34,44 + Owner: Harkonnen + HPower1: wind_trap + Location: 22,45 + Owner: Harkonnen + Actor19: trike + Location: 40,45 + Owner: Harkonnen + HConyard: construction_yard + Location: 26,46 + Owner: Harkonnen + Actor21: trike + Location: 37,46 + Owner: Harkonnen + HarkonnenRally1: waypoint + Owner: Neutral + Location: 24,44 + HarkonnenEntry1: waypoint + Owner: Neutral + Location: 24,49 + HarkonnenRally2: waypoint + Owner: Neutral + Location: 39,44 + HarkonnenEntry2: waypoint + Owner: Neutral + Location: 39,49 + OrdosRally: waypoint + Owner: Neutral + Location: 16,25 + OrdosEntry: waypoint + Owner: Neutral + Location: 2,25 + HarkonnenRally3: waypoint + Owner: Neutral + Location: 27,31 + HarkonnenEntry3: waypoint + Owner: Neutral + Location: 27,49 + HarkonnenEntry4: waypoint + Owner: Neutral + Location: 49,38 + HarkonnenEntry5: waypoint + Owner: Neutral + Location: 2,38 + HarkonnenRally4: waypoint + Owner: Neutral + Location: 15,30 + HarkonnenRally5: waypoint + Owner: Neutral + Location: 31,24 + HarkonnenRally6: waypoint + Owner: Neutral + Location: 25,17 + +Rules: d2k|rules/campaign-rules.yaml, rules.yaml diff --git a/mods/d2k/maps/ordos-02a/ordos02a-AI.lua b/mods/d2k/maps/ordos-02a/ordos02a-AI.lua new file mode 100644 index 0000000000..00e5e4dcc7 --- /dev/null +++ b/mods/d2k/maps/ordos-02a/ordos02a-AI.lua @@ -0,0 +1,122 @@ +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" } + +AttackOnGoing = false +HoldProduction = false +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 Attacking then + return + end + Attacking = true + HoldProduction = true + + local units = SetupAttackGroup() + Utils.Do(units, function(unit) + IdleHunt(unit) + end) + + Trigger.OnAllRemovedFromWorld(units, function() + Attacking = false + HoldProduction = false + 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() + 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 + +ActivateAI = function() + InitAIUnits() + ProduceInfantry() +end diff --git a/mods/d2k/maps/ordos-02a/ordos02a.lua b/mods/d2k/maps/ordos-02a/ordos02a.lua new file mode 100644 index 0000000000..885bbd0973 --- /dev/null +++ b/mods/d2k/maps/ordos-02a/ordos02a.lua @@ -0,0 +1,159 @@ + +HarkonnenBase = { HConyard, HPower1, HPower2, HBarracks, HOutpost } +HarkonnenBaseAreaTrigger = { CPos.New(31, 37), CPos.New(32, 37), CPos.New(33, 37), CPos.New(34, 37), CPos.New(35, 37), CPos.New(36, 37), CPos.New(37, 37), CPos.New(38, 37), CPos.New(39, 37), CPos.New(40, 37), CPos.New(41, 37), CPos.New(42, 37), CPos.New(42, 38), CPos.New(42, 39), CPos.New(42, 40), CPos.New(42, 41), CPos.New(42, 42), CPos.New(42, 43), CPos.New(42, 44), CPos.New(42, 45), CPos.New(42, 46), CPos.New(42, 47), CPos.New(42, 48), CPos.New(42, 49) } + +HarkonnenReinforcements = { } +HarkonnenReinforcements["easy"] = +{ + { "light_inf", "trike" }, + { "light_inf", "trike" }, + { "light_inf", "light_inf", "light_inf", "trike", "trike" } +} + +HarkonnenReinforcements["normal"] = +{ + { "light_inf", "trike" }, + { "light_inf", "trike" }, + { "light_inf", "light_inf", "light_inf", "trike", "trike" }, + { "light_inf", "light_inf" }, + { "light_inf", "light_inf", "light_inf" }, + { "light_inf", "trike" }, +} + +HarkonnenReinforcements["hard"] = +{ + { "trike", "trike" }, + { "light_inf", "trike" }, + { "light_inf", "trike" }, + { "light_inf", "light_inf", "light_inf", "trike", "trike" }, + { "light_inf", "light_inf" }, + { "trike", "trike" }, + { "light_inf", "light_inf", "light_inf" }, + { "light_inf", "trike" }, + { "trike", "trike" } +} + +HarkonnenAttackPaths = +{ + { HarkonnenEntry3.Location, HarkonnenRally3.Location }, + { HarkonnenEntry4.Location, HarkonnenRally5.Location }, + { HarkonnenEntry4.Location, HarkonnenRally6.Location }, + { HarkonnenEntry5.Location, HarkonnenRally4.Location } +} + +InitialHarkonnenReinforcementsPaths = +{ + { HarkonnenEntry1.Location, HarkonnenRally1.Location }, + { HarkonnenEntry2.Location, HarkonnenRally2.Location } +} + +InitialHarkonnenReinforcements = +{ + { "trike", "trike" }, + { "light_inf", "light_inf" } +} + +HarkonnenAttackDelay = { } +HarkonnenAttackDelay["easy"] = DateTime.Minutes(5) +HarkonnenAttackDelay["normal"] = DateTime.Minutes(2) + DateTime.Seconds(40) +HarkonnenAttackDelay["hard"] = DateTime.Minutes(1) + DateTime.Seconds(20) + +HarkonnenAttackWaves = { } +HarkonnenAttackWaves["easy"] = 3 +HarkonnenAttackWaves["normal"] = 6 +HarkonnenAttackWaves["hard"] = 9 + +OrdosReinforcements = { "light_inf", "light_inf", "raider" } +OrdosEntryPath = { OrdosEntry.Location, OrdosRally.Location } + +wave = 0 +SendHarkonnen = function() + Trigger.AfterDelay(HarkonnenAttackDelay[Map.LobbyOption("difficulty")], function() + wave = wave + 1 + if wave > HarkonnenAttackWaves[Map.LobbyOption("difficulty")] then + return + end + + local path = Utils.Random(HarkonnenAttackPaths) + local units = Reinforcements.ReinforceWithTransport(harkonnen, "carryall.reinforce", HarkonnenReinforcements[Map.LobbyOption("difficulty")][wave], path, { path[1] })[2] + Utils.Do(units, IdleHunt) + + SendHarkonnen() + end) +end + +IdleHunt = function(unit) + Trigger.OnIdle(unit, unit.Hunt) +end + +SendInitialUnits = 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 +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) + + Trigger.AfterDelay(DateTime.Minutes(1), function() + Media.PlaySpeechNotification(player, "Reinforce") + Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", OrdosReinforcements, OrdosEntryPath, { OrdosEntryPath[1] }) + end) + + SendInitialUnits(HarkonnenBaseAreaTrigger, InitialHarkonnenReinforcements[1], InitialHarkonnenReinforcementsPaths[1], InitialReinforcementsSent1) + SendInitialUnits(HarkonnenBaseAreaTrigger, InitialHarkonnenReinforcements[2], InitialHarkonnenReinforcementsPaths[2], InitialReinforcementsSent2) + + SendHarkonnen() + Trigger.AfterDelay(0, ActivateAI) +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("Destroy all Harkonnen forces.") + + 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-02a/rules.yaml b/mods/d2k/maps/ordos-02a/rules.yaml new file mode 100644 index 0000000000..c56754e16b --- /dev/null +++ b/mods/d2k/maps/ordos-02a/rules.yaml @@ -0,0 +1,48 @@ +Player: + PlayerResources: + DefaultCash: 5000 + +World: + LuaScript: + Scripts: ordos02a.lua, ordos02a-AI.lua + MissionData: + Briefing: Harkonnen forces are weakened in the Imperial Basin. Use the sensors in our Outpost to find them. Strike hard and destroy everything.\n + BriefingVideo: O_BR02_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 + +construction_yard: + Production: + Produces: Building + +concreteb: + Buildable: + Prerequisites: ~disabled + +heavy_factory: + Buildable: + Prerequisites: ~disabled + +medium_gun_turret: + Buildable: + Prerequisites: ~disabled + +wall: + Buildable: + Prerequisites: ~disabled + +outpost: + Buildable: + Prerequisites: barracks diff --git a/mods/d2k/missions.yaml b/mods/d2k/missions.yaml index 61fe741b68..30d11841de 100644 --- a/mods/d2k/missions.yaml +++ b/mods/d2k/missions.yaml @@ -10,6 +10,7 @@ Atreides Campaign: Ordos Campaign: ./mods/d2k/maps/ordos-01a ./mods/d2k/maps/ordos-01b + ./mods/d2k/maps/ordos-02a Harkonnen Campaign: ./mods/d2k/maps/harkonnen-01a