diff --git a/OpenRA.sln b/OpenRA.sln index 4422943c5e..cf70970311 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -90,6 +90,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dune 2000 Lua scripts", "Du mods\d2k\maps\atreides-01b\atreides01b.lua = mods\d2k\maps\atreides-01b\atreides01b.lua mods\d2k\maps\atreides-02a\atreides02a.lua = mods\d2k\maps\atreides-02a\atreides02a.lua mods\d2k\maps\atreides-02b\atreides02b.lua = mods\d2k\maps\atreides-02b\atreides02b.lua + mods\d2k\maps\atreides-03a\atreides03a.lua = mods\d2k\maps\atreides-03a\atreides03a.lua + mods\d2k\maps\atreides-03a\atreides03a-AI.lua = mods\d2k\maps\atreides-03a\atreides03a-AI.lua EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "System Lua scripts", "System Lua scripts", "{A4D6AEA4-8009-4256-903B-8D227E50452B}" diff --git a/mods/d2k/maps/atreides-03a/atreides03a-AI.lua b/mods/d2k/maps/atreides-03a/atreides03a-AI.lua new file mode 100644 index 0000000000..5480ffc3a1 --- /dev/null +++ b/mods/d2k/maps/atreides-03a/atreides03a-AI.lua @@ -0,0 +1,163 @@ +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) } +} + +OrdosInfantryTypes = { "light_inf", "light_inf", "light_inf", "trooper", "trooper" } +OrdosVehicleTypes = { "raider", "raider", "quad" } + +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.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 + +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(ordos, InitialOrdosReinforcements, OrdosPaths[2]) + IdlingUnits[#IdlingUnits + 1] = OTrooper1 + IdlingUnits[#IdlingUnits + 1] = OTrooper2 + IdlingUnits[#IdlingUnits + 1] = ORaider + + Utils.Do(OrdosBase, 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 OBarracks.IsDead then + return + end + + if HoldProduction then + Trigger.AfterDelay(DateTime.Minutes(1), ProduceInfantry) + return + end + + local delay = Utils.RandomInteger(AttackDelays[Map.Difficulty][1], AttackDelays[Map.Difficulty][2] + 1) + local toBuild = { Utils.Random(OrdosInfantryTypes) } + ordos.Build(toBuild, function(unit) + IdlingUnits[#IdlingUnits + 1] = unit[1] + Trigger.AfterDelay(delay, ProduceInfantry) + + if #IdlingUnits >= (AttackGroupSize[Map.Difficulty] * 2.5) then + SendAttack() + end + end) +end + +ProduceVehicles = function() + if OLightFactory.IsDead then + return + end + + if HoldProduction then + Trigger.AfterDelay(DateTime.Minutes(1), ProduceVehicles) + return + end + + local delay = Utils.RandomInteger(AttackDelays[Map.Difficulty][1], AttackDelays[Map.Difficulty][2] + 1) + local toBuild = { Utils.Random(OrdosVehicleTypes) } + ordos.Build(toBuild, function(unit) + IdlingUnits[#IdlingUnits + 1] = unit[1] + Trigger.AfterDelay(delay, ProduceVehicles) + + if #IdlingUnits >= (AttackGroupSize[Map.Difficulty] * 2.5) then + SendAttack() + end + end) +end + +ActivateAI = function() + Trigger.AfterDelay(0, InitAIUnits) + + OConyard.Produce(AtreidesUpgrades[1]) + OConyard.Produce(AtreidesUpgrades[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/atreides-03a/atreides03a.lua b/mods/d2k/maps/atreides-03a/atreides03a.lua new file mode 100644 index 0000000000..f8a7c5d775 --- /dev/null +++ b/mods/d2k/maps/atreides-03a/atreides03a.lua @@ -0,0 +1,170 @@ +OrdosBase = { OBarracks, OWindTrap1, OWindTrap2, OWindTrap3, OWindTrap4, OLightFactory, OOutpost, OConyard, ORefinery, OSilo1, OSilo2, OSilo3, OSilo4 } + +OrdosReinforcements = +{ + Easy = + { + { "light_inf", "raider", "trooper" }, + { "light_inf", "raider", "quad" }, + { "light_inf", "light_inf", "trooper", "raider", "raider", "quad" } + }, + + Normal = + { + { "light_inf", "raider", "trooper" }, + { "light_inf", "raider", "raider" }, + { "light_inf", "light_inf", "trooper", "raider", "raider", "quad" }, + { "light_inf", "light_inf", "trooper", "trooper" }, + { "light_inf", "light_inf", "light_inf", "light_inf" }, + { "light_inf", "raider", "quad", "quad" } + }, + + Hard = + { + { "raider", "raider", "quad" }, + { "light_inf", "raider", "raider" }, + { "trooper", "trooper", "light_inf", "raider" }, + { "light_inf", "light_inf", "light_inf", "raider", "raider" }, + { "light_inf", "light_inf", "trooper", "trooper" }, + { "raider", "raider", "quad", "quad", "quad", "raider" }, + { "light_inf", "light_inf", "light_inf", "raider", "raider" }, + { "light_inf", "raider", "light_inf", "trooper", "trooper", "quad" }, + { "raider", "raider", "quad", "quad", "quad", "raider" } + } +} + +OrdosAttackDelay = +{ + Easy = DateTime.Minutes(5), + Normal = DateTime.Minutes(2) + DateTime.Seconds(40), + Hard = DateTime.Minutes(1) + DateTime.Seconds(20) +} + +OrdosAttackWaves = +{ + Easy = 3, + Normal = 6, + Hard = 9 +} + +ToHarvest = +{ + Easy = 5000, + Normal = 6000, + Hard = 7000 +} + +InitialOrdosReinforcements = { "light_inf", "light_inf", "quad", "quad", "raider", "raider", "trooper", "trooper" } + +OrdosPaths = +{ + { OrdosEntry1.Location, OrdosRally1.Location }, + { OrdosEntry2.Location, OrdosRally2.Location } +} + +AtreidesReinforcements = { "quad", "quad", "trike", "trike" } +AtreidesPath = { AtreidesEntry.Location, AtreidesRally.Location } + +AtreidesBaseBuildings = { "barracks", "light_factory" } +AtreidesUpgrades = { "upgrade.barracks", "upgrade.light" } + +wave = 0 +SendOrdos = function() + Trigger.AfterDelay(OrdosAttackDelay[Map.Difficulty], function() + if player.IsObjectiveCompleted(KillOrdos) then + return + end + + wave = wave + 1 + if wave > OrdosAttackWaves[Map.Difficulty] then + return + end + + local units = Reinforcements.ReinforceWithTransport(ordos, "carryall.reinforce", OrdosReinforcements[Map.Difficulty][wave], OrdosPaths[1], { OrdosPaths[1][1] })[2] + Utils.Do(units, IdleHunt) + + SendOrdos() + end) +end + +MessageCheck = function(index) + return #player.GetActorsByType(AtreidesBaseBuildings[index]) > 0 and not player.HasPrerequisites({ AtreidesUpgrades[index] }) +end + +Tick = function() + if player.HasNoRequiredUnits() then + ordos.MarkCompletedObjective(KillAtreides) + end + + if ordos.HasNoRequiredUnits() and not player.IsObjectiveCompleted(KillOrdos) then + Media.DisplayMessage("The Ordos have been annihilated!", "Mentat") + player.MarkCompletedObjective(KillOrdos) + end + + if DateTime.GameTime % DateTime.Seconds(30) and HarvesterKilled then + local units = ordos.GetActorsByType("harvester") + + if #units > 0 then + HarvesterKilled = false + ProtectHarvester(units[1]) + end + end + + if player.Resources > ToHarvest[Map.Difficulty] - 1 then + player.MarkCompletedObjective(GatherSpice) + 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 + + UserInterface.SetMissionText("Harvested resources: " .. player.Resources .. "/" .. ToHarvest[Map.Difficulty], player.Color) +end + +WorldLoaded = function() + ordos = Player.GetPlayer("Ordos") + player = Player.GetPlayer("Atreides") + + InitObjectives() + + Camera.Position = AConyard.CenterPosition + + Trigger.OnAllKilled(OrdosBase, function() + Utils.Do(ordos.GetGroundAttackers(), IdleHunt) + end) + + SendOrdos() + ActivateAI() + + Trigger.AfterDelay(DateTime.Minutes(2) + DateTime.Seconds(30), function() + Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", AtreidesReinforcements, AtreidesPath, { AtreidesPath[1] }) + end) +end + +InitObjectives = function() + Trigger.OnObjectiveAdded(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective") + end) + + KillAtreides = ordos.AddPrimaryObjective("Kill all Atreides units.") + GatherSpice = player.AddPrimaryObjective("Harvest " .. tostring(ToHarvest[Map.Difficulty]) .. " Solaris worth of Spice.") + KillOrdos = player.AddSecondaryObjective("Eliminate all Ordos 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/atreides-03a/map.bin b/mods/d2k/maps/atreides-03a/map.bin new file mode 100644 index 0000000000..866f870597 Binary files /dev/null and b/mods/d2k/maps/atreides-03a/map.bin differ diff --git a/mods/d2k/maps/atreides-03a/map.png b/mods/d2k/maps/atreides-03a/map.png new file mode 100644 index 0000000000..c88778ca4e Binary files /dev/null and b/mods/d2k/maps/atreides-03a/map.png differ diff --git a/mods/d2k/maps/atreides-03a/map.yaml b/mods/d2k/maps/atreides-03a/map.yaml new file mode 100644 index 0000000000..fa1ae7cb53 --- /dev/null +++ b/mods/d2k/maps/atreides-03a/map.yaml @@ -0,0 +1,211 @@ +MapFormat: 8 + +RequiresMod: d2k + +Title: Atreides 03a + +Description: Bring the Atreides forces up to combat strength - quickly. Guard against surprise attacks and ensure Spice production.\n\nThe Ordos forces are light but numerous. To combat the Ordos, you have been granted license to produce Quads, which have a greater anti-vehicle capability than Trikes. Upgrade your Light Factories to allow production of these units.\n\nMeet any agression with overwhelming force.\n\nGood luck.\n + +Author: Westwood Studios + +Tileset: ARRAKIS + +MapSize: 68,68 + +Bounds: 2,2,64,64 + +Visibility: MissionSelector + +Type: Campaign + +Videos: + +Options: + Crates: False + Creeps: True + Fog: True + Shroud: True + AllyBuildRadius: False + FragileAlliances: False + StartingCash: 3000 + TechLevel: Low + ConfigurableStartingUnits: False + Difficulties: Easy, Normal, Hard + ShortGame: False + +Players: + PlayerReference@Neutral: + Name: Neutral + OwnsWorld: True + NonCombatant: True + PlayerReference@Creeps: + Name: Creeps + NonCombatant: True + Enemies: Atreides, Ordos + PlayerReference@Atreides: + Name: Atreides + Playable: True + LockFaction: True + Faction: atreides + LockColor: True + Color: 9191FF + Enemies: Ordos, Creeps + PlayerReference@Ordos: + Name: Ordos + LockFaction: True + Faction: ordos + LockColor: True + Color: B3EAA5 + Enemies: Atreides, Creeps + +Actors: + Actor0: light_inf + Location: 14,10 + Owner: Atreides + Actor1: trike + Location: 10,12 + Owner: Atreides + Actor2: quad + Location: 18,12 + Owner: Atreides + Actor5: trike + Location: 18,16 + Owner: Atreides + Actor8: quad + Location: 11,17 + Owner: Atreides + Actor10: light_inf + Location: 15,18 + Owner: Atreides + Actor23: wormspawner + Location: 24,49 + Owner: Creeps + AConyard: construction_yard + Location: 13,13 + Owner: Atreides + OTrooper1: trooper + Location: 41,27 + Owner: Ordos + OTrooper2: trooper + Location: 36,28 + Owner: Ordos + ORaider: raider + Location: 39,28 + Owner: Ordos + OBarracks: barracks + Location: 56,15 + Owner: Ordos + OWindTrap1: wind_trap + Location: 62,16 + Owner: Ordos + OWindTrap2: wind_trap + Location: 64,16 + Owner: Ordos + OWindTrap3: wind_trap + Location: 64,19 + Owner: Ordos + OWindTrap4: wind_trap + Location: 60,17 + Owner: Ordos + OLightFactory: light_factory + Location: 51,18 + Owner: Ordos + OOutpost: outpost + Location: 54,20 + Owner: Ordos + OConyard: construction_yard + Location: 60,21 + Owner: Ordos + ORefinery: refinery + Location: 57,23 + Owner: Ordos + OSilo1: silo + Location: 62,25 + Owner: Ordos + OSilo2: silo + Location: 63,25 + Owner: Ordos + OSilo3: silo + Location: 62,26 + Owner: Ordos + OSilo4: silo + Location: 63,26 + Owner: Ordos + AtreidesEntry: waypoint + Owner: Neutral + Location: 12,2 + AtreidesRally: waypoint + Owner: Neutral + Location: 15,9 + OrdosEntry1: waypoint + Owner: Neutral + Location: 2,6 + OrdosEntry2: waypoint + Owner: Neutral + Location: 65,30 + OrdosRally1: waypoint + Owner: Neutral + Location: 6,6 + OrdosRally2: waypoint + Owner: Neutral + Location: 39,30 + +Smudges: + +Rules: + Player: + -ConquestVictoryConditions: + MissionObjectives: + EarlyGameOver: true + World: + -CrateSpawner: + -SpawnMPUnits: + -MPStartLocations: + LuaScript: + Scripts: atreides03a.lua, atreides03a-AI.lua + ObjectivesPanel: + PanelName: MISSION_OBJECTIVES + WormManager: + Minimum: 1 + Maximum: 1 + 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 + +Sequences: + +VoxelSequences: + +Weapons: + +Voices: + +Music: + +Notifications: + +Translations: diff --git a/mods/d2k/missions.yaml b/mods/d2k/missions.yaml index b6bcdcb33e..4784ea512a 100644 --- a/mods/d2k/missions.yaml +++ b/mods/d2k/missions.yaml @@ -3,3 +3,4 @@ Atreides Campaign: ./mods/d2k/maps/atreides-01b ./mods/d2k/maps/atreides-02a ./mods/d2k/maps/atreides-02b + ./mods/d2k/maps/atreides-03a