diff --git a/OpenRA.sln b/OpenRA.sln index ea62892b62..e1198b8dae 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -120,6 +120,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dune 2000 Lua scripts", "Du mods\d2k\maps\harkonnen-02a\harkonnen02a.lua = mods\d2k\maps\harkonnen-02a\harkonnen02a.lua mods\d2k\maps\harkonnen-02b\harkonnen02b-AI.lua = mods\d2k\maps\harkonnen-02b\harkonnen02b-AI.lua mods\d2k\maps\harkonnen-02b\harkonnen02b.lua = mods\d2k\maps\harkonnen-02b\harkonnen02b.lua + mods\d2k\maps\harkonnen-03a\harkonnen03a-AI.lua = mods\d2k\maps\harkonnen-03a\harkonnen03a-AI.lua + mods\d2k\maps\harkonnen-03a\harkonnen03a.lua = mods\d2k\maps\harkonnen-03a\harkonnen03a.lua mods\d2k\maps\ordos-01a\ordos01a.lua = mods\d2k\maps\ordos-01a\ordos01a.lua mods\d2k\maps\ordos-01b\ordos01b.lua = mods\d2k\maps\ordos-01b\ordos01b.lua EndProjectSection diff --git a/mods/d2k/maps/harkonnen-03a/harkonnen03a-AI.lua b/mods/d2k/maps/harkonnen-03a/harkonnen03a-AI.lua new file mode 100644 index 0000000000..a92a678f3e --- /dev/null +++ b/mods/d2k/maps/harkonnen-03a/harkonnen03a-AI.lua @@ -0,0 +1,157 @@ +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", "light_inf", "light_inf", "trooper", "trooper" } +AtreidesVehicleTypes = { "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() + 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() + Trigger.AfterDelay(0, InitAIUnits) + + AConyard.Produce(HarkonnenUpgrades[1]) + AConyard.Produce(HarkonnenUpgrades[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/harkonnen-03a/harkonnen03a.lua b/mods/d2k/maps/harkonnen-03a/harkonnen03a.lua new file mode 100644 index 0000000000..9c3e8b233e --- /dev/null +++ b/mods/d2k/maps/harkonnen-03a/harkonnen03a.lua @@ -0,0 +1,196 @@ +AtreidesBase = { ABarracks, AWindTrap1, AWindTrap2, ALightFactory, AOutpost, AConyard, ARefinery, ASilo } +AtreidesBaseAreaTriggers = +{ + { CPos.New(34, 50), CPos.New(35, 50), CPos.New(36, 50), CPos.New(37, 50), CPos.New(38, 50), CPos.New(39, 50), CPos.New(40, 50), CPos.New(41, 50), CPos.New(14, 57), CPos.New(14, 58), CPos.New(14, 59), CPos.New(14, 60), CPos.New(14, 61), CPos.New(14, 62), CPos.New(14, 63), CPos.New(14, 64), CPos.New(14, 65)}, + { CPos.New(29, 51), CPos.New(29, 52), CPos.New(29, 53), CPos.New(29, 54), CPos.New(44, 50), CPos.New(44, 51), CPos.New(44, 52), CPos.New(44, 53), CPos.New(44, 54), CPos.New(43, 54), CPos.New(42, 54), CPos.New(41, 54), CPos.New(40, 54), CPos.New(39, 54), CPos.New(38, 54), CPos.New(37, 54), CPos.New(36, 54), CPos.New(35, 54), CPos.New(34, 54), CPos.New(33, 54), CPos.New(32, 54), CPos.New(31, 54), CPos.New(30, 54) }, + { CPos.New(46, 18), CPos.New(46, 19), CPos.New(46, 20), CPos.New(46, 21), CPos.New(46, 22), CPos.New(46, 23) } +} + +AtreidesReinforcements = +{ + 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" } + } +} + +AtreidesAttackDelay = +{ + easy = DateTime.Minutes(5), + normal = DateTime.Minutes(2) + DateTime.Seconds(40), + hard = DateTime.Minutes(1) + DateTime.Seconds(20) +} + +AtreidesAttackWaves = +{ + easy = 3, + normal = 6, + hard = 9 +} + +AtreidesHunters = +{ + { "trooper", "trooper", "trooper", "trooper" }, + { "trike", "trike", "trike" }, + { "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "trike", "trike" }, + { "trooper", "trooper", "trooper", "trooper", "trooper", "quad", "quad" }, + { "quad", "trooper", "trooper", "trooper" } +} + +AtreidesPaths = +{ + { AtreidesEntry1.Location, AtreidesRally1.Location }, + { AtreidesEntry2.Location, AtreidesRally2.Location }, + { AtreidesEntry3.Location, AtreidesRally3.Location } +} + +AtreidesHunterPaths = +{ + { AtreidesEntry4.Location, AtreidesRally4.Location }, + { AtreidesEntry5.Location, AtreidesRally5.Location }, + { AtreidesEntry6.Location, AtreidesRally6.Location }, + { AtreidesEntry7.Location, AtreidesRally7.Location }, + { AtreidesEntry8.Location, AtreidesRally8.Location } +} + +HarkonnenReinforcements = { "trike", "trike", "quad" } +HarkonnenPath = { HarkonnenEntry.Location, HarkonnenRally.Location } + +HarkonnenBaseBuildings = { "barracks", "light_factory" } +HarkonnenUpgrades = { "upgrade.barracks", "upgrade.light" } + +wave = 0 +SendAtreides = function() + Trigger.AfterDelay(AtreidesAttackDelay[Map.LobbyOption("difficulty")], function() + if player.IsObjectiveCompleted(KillAtreides) then + return + end + + wave = wave + 1 + if wave > AtreidesAttackWaves[Map.LobbyOption("difficulty")] then + return + end + + local path = Utils.Random(AtreidesPaths) + local units = Reinforcements.ReinforceWithTransport(atreides, "carryall.reinforce", AtreidesReinforcements[Map.LobbyOption("difficulty")][wave], path, { path[1] })[2] + Utils.Do(units, IdleHunt) + + SendAtreides() + end) +end + +MessageCheck = function(index) + return #player.GetActorsByType(HarkonnenBaseBuildings[index]) > 0 and not player.HasPrerequisites({ HarkonnenUpgrades[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(atreides, "carryall.reinforce", unit, path, { path[1] })[2] + Utils.Do(units, IdleHunt) + check = true + end + end) +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 + + if DateTime.GameTime % DateTime.Seconds(30) and HarvesterKilled then + local units = atreides.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() + atreides = Player.GetPlayer("Atreides") + player = Player.GetPlayer("Harkonnen") + + InitObjectives() + + Camera.Position = HConyard.CenterPosition + + Trigger.OnAllKilled(AtreidesBase, function() + Utils.Do(atreides.GetGroundAttackers(), IdleHunt) + end) + + SendAtreides() + ActivateAI() + + Trigger.AfterDelay(DateTime.Minutes(2) + DateTime.Seconds(30), function() + Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", HarkonnenReinforcements, HarkonnenPath, { HarkonnenPath[1] }) + end) + + SendHunters(AtreidesBaseAreaTriggers[1], AtreidesHunters[1], AtreidesHunterPaths[1], HuntersSent1) + SendHunters(AtreidesBaseAreaTriggers[1], AtreidesHunters[2], AtreidesHunterPaths[2], HuntersSent2) + SendHunters(AtreidesBaseAreaTriggers[2], AtreidesHunters[3], AtreidesHunterPaths[3], HuntersSent3) + SendHunters(AtreidesBaseAreaTriggers[2], AtreidesHunters[4], AtreidesHunterPaths[4], HuntersSent4) + SendHunters(AtreidesBaseAreaTriggers[3], AtreidesHunters[5], AtreidesHunterPaths[5], HuntersSent5) +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("Eliminate all Atreides 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/harkonnen-03a/map.bin b/mods/d2k/maps/harkonnen-03a/map.bin new file mode 100644 index 0000000000..6b3ce0087a Binary files /dev/null and b/mods/d2k/maps/harkonnen-03a/map.bin differ diff --git a/mods/d2k/maps/harkonnen-03a/map.png b/mods/d2k/maps/harkonnen-03a/map.png new file mode 100644 index 0000000000..69859201bf Binary files /dev/null and b/mods/d2k/maps/harkonnen-03a/map.png differ diff --git a/mods/d2k/maps/harkonnen-03a/map.yaml b/mods/d2k/maps/harkonnen-03a/map.yaml new file mode 100644 index 0000000000..b1ac67df63 --- /dev/null +++ b/mods/d2k/maps/harkonnen-03a/map.yaml @@ -0,0 +1,180 @@ +MapFormat: 11 + +RequiresMod: d2k + +Title: Harkonnen 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 + 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: + Actor0: light_inf + Location: 30,9 + Owner: Harkonnen + Actor1: trooper + Location: 34,9 + Owner: Harkonnen + HConyard: construction_yard + Location: 31,11 + Owner: Harkonnen + Actor3: quad + Location: 37,12 + Owner: Harkonnen + Actor4: wormspawner + Location: 6,14 + Owner: Creeps + Actor5: trike + Location: 27,16 + Owner: Harkonnen + Actor6: light_inf + Location: 31,16 + Owner: Harkonnen + Actor7: trooper + Location: 33,16 + Owner: Harkonnen + Actor8: wall + Location: 33,51 + Owner: Atreides + Actor9: wall + Location: 42,51 + Owner: Atreides + Actor10: wall + Location: 33,52 + Owner: Atreides + Actor11: wall + Location: 34,52 + Owner: Atreides + Actor12: wall + Location: 35,52 + Owner: Atreides + Actor13: wall + Location: 40,52 + Owner: Atreides + Actor14: wall + Location: 41,52 + Owner: Atreides + Actor15: wall + Location: 42,52 + Owner: Atreides + Actor16: quad + Location: 34,53 + Owner: Atreides + Actor17: quad + Location: 40,53 + Owner: Atreides + ARefinery: refinery + Location: 23,57 + Owner: Atreides + AWindTrap1: wind_trap + Location: 20,59 + Owner: Atreides + ASilo: silo + Location: 27,59 + Owner: Atreides + AConyard: construction_yard + Location: 22,61 + Owner: Atreides + AWindTrap2: wind_trap + Location: 27,62 + Owner: Atreides + AOutpost: outpost + Location: 29,62 + Owner: Atreides + ALightFactory: light_factory + Location: 37,62 + Owner: Atreides + ABarracks: barracks + Location: 34,63 + Owner: Atreides + AtreidesRally4: waypoint + Owner: Neutral + Location: 21,11 + AtreidesEntry4: waypoint + Owner: Neutral + Location: 21,2 + AtreidesRally2: waypoint + Owner: Neutral + Location: 13,19 + AtreidesEntry3: waypoint + Owner: Neutral + Location: 29,60 + AtreidesRally3: waypoint + Owner: Neutral + Location: 29,65 + AtreidesRally6: waypoint + Owner: Neutral + Location: 19,57 + AtreidesEntry6: waypoint + Owner: Neutral + Location: 19,65 + AtreidesRally7: waypoint + Owner: Neutral + Location: 45,61 + AtreidesEntry7: waypoint + Owner: Neutral + Location: 45,65 + AtreidesRally5: waypoint + Owner: Neutral + Location: 33,45 + AtreidesEntry5: waypoint + Owner: Neutral + Location: 33,65 + AtreidesEntry2: waypoint + Owner: Neutral + Location: 2,19 + AtreidesRally1: waypoint + Owner: Neutral + Location: 42,18 + AtreidesEntry1: waypoint + Owner: Neutral + Location: 65,18 + AtreidesRally8: waypoint + Owner: Neutral + Location: 47,27 + AtreidesEntry8: waypoint + Owner: Neutral + Location: 65,27 + HarkonnenRally: waypoint + Owner: Neutral + Location: 29,12 + HarkonnenEntry: waypoint + Owner: Neutral + Location: 29,2 + +Rules: d2k|rules/campaign-rules.yaml, rules.yaml diff --git a/mods/d2k/maps/harkonnen-03a/rules.yaml b/mods/d2k/maps/harkonnen-03a/rules.yaml new file mode 100644 index 0000000000..22abfebc3a --- /dev/null +++ b/mods/d2k/maps/harkonnen-03a/rules.yaml @@ -0,0 +1,56 @@ +Player: + PlayerResources: + DefaultCash: 5000 + +World: + LuaScript: + Scripts: harkonnen03a.lua, harkonnen03a-AI.lua + MissionData: + Briefing: Attack and destroy the Atreides base at Sietch Tabr. Strike hard and eliminate all resistance.\n\nHeavier Quad vehicles will be made available for your attack. Upgrade your Light Factory to gain access to these vehicles.\n + BriefingVideo: H_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 f0b71a6f17..9afd985b36 100644 --- a/mods/d2k/missions.yaml +++ b/mods/d2k/missions.yaml @@ -20,3 +20,4 @@ Harkonnen Campaign: ./mods/d2k/maps/harkonnen-01b ./mods/d2k/maps/harkonnen-02a ./mods/d2k/maps/harkonnen-02b + ./mods/d2k/maps/harkonnen-03a