diff --git a/mods/d2k/maps/harkonnen-02a/harkonnen02a-AI.lua b/mods/d2k/maps/harkonnen-02a/harkonnen02a-AI.lua index f2447351eb..e165ba9b92 100644 --- a/mods/d2k/maps/harkonnen-02a/harkonnen02a-AI.lua +++ b/mods/d2k/maps/harkonnen-02a/harkonnen02a-AI.lua @@ -117,10 +117,6 @@ ProduceInfantry = function() end ActivateAI = function() - Trigger.AfterDelay(0, InitAIUnits) - - -- Finish the upgrades first before trying to build something - Trigger.AfterDelay(DateTime.Seconds(14), function() - ProduceInfantry() - end) + InitAIUnits() + ProduceInfantry() end diff --git a/mods/d2k/maps/harkonnen-02a/harkonnen02a.lua b/mods/d2k/maps/harkonnen-02a/harkonnen02a.lua index 1eaf553409..c405a5031f 100644 --- a/mods/d2k/maps/harkonnen-02a/harkonnen02a.lua +++ b/mods/d2k/maps/harkonnen-02a/harkonnen02a.lua @@ -94,7 +94,7 @@ WorldLoaded = function() end) SendAtreides() - ActivateAI() + Trigger.AfterDelay(0, ActivateAI) end InitObjectives = function() diff --git a/mods/d2k/maps/harkonnen-02b/harkonnen02b-AI.lua b/mods/d2k/maps/harkonnen-02b/harkonnen02b-AI.lua new file mode 100644 index 0000000000..cbdc719693 --- /dev/null +++ b/mods/d2k/maps/harkonnen-02b/harkonnen02b-AI.lua @@ -0,0 +1,146 @@ +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) } +} + +AtreidesInfantryTypes = { "light_inf" } +AtreidesVehicleTypes = { "trike" } + +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(AtreidesBase, 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 ABarracks.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(AtreidesInfantryTypes) } + atreides.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 ALightFactory.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(AtreidesVehicleTypes) } + atreides.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() + InitAIUnits() + ProduceInfantry() + ProduceVehicles() +end diff --git a/mods/d2k/maps/harkonnen-02b/harkonnen02b.lua b/mods/d2k/maps/harkonnen-02b/harkonnen02b.lua new file mode 100644 index 0000000000..c4ef54da15 --- /dev/null +++ b/mods/d2k/maps/harkonnen-02b/harkonnen02b.lua @@ -0,0 +1,125 @@ + +AtreidesBase = { AConyard, APower1, APower2, ABarracks, ALightFactory } + +AtreidesReinforcements = { } +AtreidesReinforcements["easy"] = +{ + { "light_inf", "trike" }, + { "light_inf", "trike" }, + { "light_inf", "light_inf", "light_inf", "trike", "trike" } +} + +AtreidesReinforcements["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" }, +} + +AtreidesReinforcements["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" } +} + +AtreidesAttackPaths = +{ + { AtreidesEntry1.Location, AtreidesRally1.Location }, + { AtreidesEntry1.Location, AtreidesRally4.Location }, + { AtreidesEntry2.Location, AtreidesRally2.Location }, + { AtreidesEntry2.Location, AtreidesRally3.Location } +} + +AtreidesAttackDelay = { } +AtreidesAttackDelay["easy"] = DateTime.Minutes(5) +AtreidesAttackDelay["normal"] = DateTime.Minutes(2) + DateTime.Seconds(40) +AtreidesAttackDelay["hard"] = DateTime.Minutes(1) + DateTime.Seconds(20) + +AtreidesAttackWaves = { } +AtreidesAttackWaves["easy"] = 3 +AtreidesAttackWaves["normal"] = 6 +AtreidesAttackWaves["hard"] = 9 + +wave = 0 +SendAtreides = function() + Trigger.AfterDelay(AtreidesAttackDelay[Map.LobbyOption("difficulty")], function() + wave = wave + 1 + if wave > AtreidesAttackWaves[Map.LobbyOption("difficulty")] then + return + end + + local path = Utils.Random(AtreidesAttackPaths) + local units = Reinforcements.ReinforceWithTransport(atreides, "carryall.reinforce", AtreidesReinforcements[Map.LobbyOption("difficulty")][wave], path, { path[1] })[2] + Utils.Do(units, IdleHunt) + + SendAtreides() + end) +end + +IdleHunt = function(unit) + Trigger.OnIdle(unit, unit.Hunt) +end + +Tick = function() + if player.HasNoRequiredUnits() then + atreides.MarkCompletedObjective(KillHarkonnen) + end + + if atreides.HasNoRequiredUnits() and not player.IsObjectiveCompleted(KillAtreides) then + Media.DisplayMessage("The Atreides have been annihilated!", "Mentat") + player.MarkCompletedObjective(KillAtreides) + end +end + +WorldLoaded = function() + atreides = Player.GetPlayer("Atreides") + player = Player.GetPlayer("Harkonnen") + + InitObjectives() + + Camera.Position = HConyard.CenterPosition + + Trigger.OnAllKilled(AtreidesBase, function() + Utils.Do(atreides.GetGroundAttackers(), IdleHunt) + end) + + SendAtreides() + 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) + + KillHarkonnen = atreides.AddPrimaryObjective("Kill all Harkonnen units.") + KillAtreides = player.AddPrimaryObjective("Destroy all Atreides 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/harkonnen-02b/map.bin b/mods/d2k/maps/harkonnen-02b/map.bin new file mode 100644 index 0000000000..93972c7258 Binary files /dev/null and b/mods/d2k/maps/harkonnen-02b/map.bin differ diff --git a/mods/d2k/maps/harkonnen-02b/map.png b/mods/d2k/maps/harkonnen-02b/map.png new file mode 100644 index 0000000000..d8fbb64fe2 Binary files /dev/null and b/mods/d2k/maps/harkonnen-02b/map.png differ diff --git a/mods/d2k/maps/harkonnen-02b/map.yaml b/mods/d2k/maps/harkonnen-02b/map.yaml new file mode 100644 index 0000000000..86be21676f --- /dev/null +++ b/mods/d2k/maps/harkonnen-02b/map.yaml @@ -0,0 +1,147 @@ +MapFormat: 11 + +RequiresMod: d2k + +Title: Harkonnen 02b + +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 + PlayerReference@Creeps: + Name: Creeps + NonCombatant: True + Enemies: Harkonnen, Atreides + PlayerReference@Harkonnen: + Name: Harkonnen + Playable: True + LockFaction: True + Faction: harkonnen + LockColor: True + Color: FE0000 + Enemies: Atreides, Creeps + PlayerReference@Atreides: + Name: Atreides + LockFaction: True + Faction: atreides + LockColor: True + Color: 9191FF + Enemies: Harkonnen + +Actors: + HConyard: construction_yard + Location: 3,3 + Owner: Harkonnen + Actor1: light_inf + Location: 8,3 + Owner: Harkonnen + Actor2: trike + Location: 11,4 + Owner: Harkonnen + Actor3: trike + Location: 9,5 + Owner: Harkonnen + Actor4: light_inf + Location: 6,7 + Owner: Harkonnen + Actor5: light_inf + Location: 8,7 + Owner: Harkonnen + Actor6: trike + Location: 2,8 + Owner: Harkonnen + Actor7: trike + Location: 23,8 + Owner: Atreides + Actor8: light_inf + Location: 5,9 + Owner: Harkonnen + Actor9: trike + Location: 33,17 + Owner: Atreides + Actor10: wormspawner + Location: 24,24 + Owner: Creeps + Actor11: wormspawner + Location: 39,30 + Owner: Creeps + Actor12: light_inf + Location: 44,35 + Owner: Atreides + Actor13: trike + Location: 44,37 + Owner: Atreides + Actor14: trike + Location: 46,37 + Owner: Atreides + Actor15: light_inf + Location: 44,39 + Owner: Atreides + Actor16: light_inf + Location: 39,40 + Owner: Atreides + Actor17: trike + Location: 41,40 + Owner: Atreides + ALightFactory: light_factory + Location: 38,42 + Owner: Atreides + APower1: wind_trap + Location: 47,42 + Owner: Atreides + Actor20: trike + Location: 43,43 + Owner: Atreides + Actor21: light_inf + Location: 45,43 + Owner: Atreides + Actor22: light_inf + Location: 41,44 + Owner: Atreides + ABarracks: barracks + Location: 38,46 + Owner: Atreides + APower2: wind_trap + Location: 43,46 + Owner: Atreides + AConyard: construction_yard + Location: 46,46 + Owner: Atreides + Actor26: light_inf + Location: 40,48 + Owner: Atreides + AtreidesEntry1: waypoint + Owner: Neutral + Location: 19,49 + AtreidesEntry2: waypoint + Owner: Neutral + Location: 49,16 + AtreidesRally1: waypoint + Owner: Neutral + Location: 5,19 + AtreidesRally2: waypoint + Owner: Neutral + Location: 25,7 + AtreidesRally3: waypoint + Owner: Neutral + Location: 14,10 + AtreidesRally4: waypoint + Owner: Neutral + Location: 23,16 + +Rules: d2k|rules/campaign-rules.yaml, rules.yaml diff --git a/mods/d2k/maps/harkonnen-02b/rules.yaml b/mods/d2k/maps/harkonnen-02b/rules.yaml new file mode 100644 index 0000000000..483ad53132 --- /dev/null +++ b/mods/d2k/maps/harkonnen-02b/rules.yaml @@ -0,0 +1,48 @@ +Player: + PlayerResources: + DefaultCash: 5000 + +World: + LuaScript: + Scripts: harkonnen02b.lua, harkonnen02b-AI.lua + MissionData: + Briefing: Strengthen your forces at our mining camp in the Imperial Basin. We must punish the Atreides for their insolence. Teach them the consequences of opposing House Harkonnen.\n\nOur radar will help you find your targets.\n + BriefingVideo: H_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 f138cee7b3..61fe741b68 100644 --- a/mods/d2k/missions.yaml +++ b/mods/d2k/missions.yaml @@ -15,3 +15,4 @@ Harkonnen Campaign: ./mods/d2k/maps/harkonnen-01a ./mods/d2k/maps/harkonnen-01b ./mods/d2k/maps/harkonnen-02a + ./mods/d2k/maps/harkonnen-02b