From ff5b5149b35b6c98fb360d3e61151b28bb56cede Mon Sep 17 00:00:00 2001 From: JovialFeline Date: Thu, 11 Apr 2024 18:27:45 -0400 Subject: [PATCH] Add navy orders, fixes to Soviet 06a --- mods/ra/maps/soviet-06a/map.yaml | 131 ++++--- mods/ra/maps/soviet-06a/soviet06a-AI.lua | 262 +++++++++---- .../soviet06a-reinforcements_teams.lua | 9 +- mods/ra/maps/soviet-06a/soviet06a.lua | 345 ++++++++++++------ 4 files changed, 508 insertions(+), 239 deletions(-) diff --git a/mods/ra/maps/soviet-06a/map.yaml b/mods/ra/maps/soviet-06a/map.yaml index 9551f362da..bb329e1d62 100644 --- a/mods/ra/maps/soviet-06a/map.yaml +++ b/mods/ra/maps/soviet-06a/map.yaml @@ -26,10 +26,10 @@ Players: Faction: allies PlayerReference@Greece: Name: Greece + Bot: campaign Faction: allies Color: E2E6F5 Enemies: USSR - Bot: campaign PlayerReference@USSR: Name: USSR AllowBots: False @@ -512,34 +512,38 @@ Actors: Actor155: tc01 Location: 33,1 Owner: Neutral - Actor156: hbox + Hbox1: hbox Location: 19,23 Owner: Greece - Actor157: hbox + Hbox2: hbox Location: 29,23 Owner: Greece - Actor158: gun + Gun3: gun Location: 20,23 Owner: Greece Facing: 512 - Actor159: gun + TurretFacing: 0 + Gun4: gun Location: 28,23 Owner: Greece Facing: 512 - Actor163: gap + TurretFacing: 0 + Gap: gap Location: 24,22 Owner: Greece - Actor164: syrd + Syrd: syrd Location: 35,14 Owner: Greece - Actor167: gun + Gun1: gun Location: 33,5 Owner: Greece Facing: 768 - Actor168: gun + TurretFacing: 512 + Gun2: gun Location: 33,9 Owner: Greece Facing: 768 + TurretFacing: 512 Actor169: v07 Location: 66,4 Owner: Greece @@ -557,60 +561,62 @@ Actors: Actor173: v06 Location: 68,4 Owner: Greece - Actor174: powr + Powr1: powr Location: 20,2 Owner: Greece - Actor175: powr + Powr2: powr Location: 25,2 Owner: Greece Actor179: 2tnk Location: 73,35 Owner: Greece - Facing: 640 + Facing: 384 Actor180: 2tnk Location: 72,34 Owner: Greece - Facing: 640 + Facing: 384 Actor181: 2tnk Location: 71,33 Owner: Greece - Facing: 640 + Facing: 384 Actor182: arty Location: 74,33 Owner: Greece - Facing: 640 + Facing: 384 + Stance: Defend Actor183: arty Location: 73,32 Owner: Greece - Facing: 640 + Facing: 384 + Stance: Defend Actor186: 3tnk Location: 56,47 Owner: USSR - Facing: 128 + Facing: 896 Actor187: 3tnk Location: 60,49 Owner: USSR - Facing: 128 + Facing: 896 Actor191: v2rl Location: 57,49 Owner: USSR - Facing: 128 + Facing: 896 Actor193: 2tnk Location: 73,31 Owner: Greece - Facing: 640 + Facing: 384 Actor194: 2tnk Location: 75,33 Owner: Greece - Facing: 640 + Facing: 384 Actor195: 2tnk Location: 85,18 Owner: Greece - Facing: 640 + Facing: 384 Actor196: 2tnk Location: 88,20 Owner: Greece - Facing: 640 + Facing: 384 Actor197: 2tnk Location: 22,23 Owner: Greece @@ -622,11 +628,13 @@ Actors: Actor199: arty Location: 18,23 Owner: Greece - Facing: 384 + Facing: 640 + Stance: Defend Actor200: arty Location: 30,23 Owner: Greece - Facing: 640 + Facing: 384 + Stance: Defend Actor202: 1tnk Location: 23,21 Owner: Greece @@ -638,64 +646,65 @@ Actors: Actor204: 2tnk Location: 40,6 Owner: Greece - Facing: 768 + Facing: 256 Actor205: 2tnk Location: 40,8 Owner: Greece - Facing: 768 + Facing: 256 Actor206: arty Location: 40,7 Owner: Greece - Facing: 768 + Facing: 256 + Stance: Defend Actor207: 1tnk Location: 77,10 Owner: Greece - Facing: 768 + Facing: 256 Actor208: 1tnk Location: 77,11 Owner: Greece - Facing: 768 + Facing: 256 Actor209: 1tnk Location: 77,12 Owner: Greece - Facing: 768 - Actor210: ca + Facing: 256 + BridgeBreaker2: ca Location: 87,29 Owner: Greece - Facing: 896 - Actor211: ca + Facing: 128 + BridgeBreaker1: ca Location: 87,25 Owner: Greece - Facing: 640 - Actor212: ca + Facing: 384 + ResponseCruiser: ca Location: 76,22 Owner: Greece - Facing: 896 + Facing: 128 Actor213: pt Location: 38,4 Owner: Greece - Facing: 768 + Facing: 256 Actor214: pt Location: 38,10 Owner: Greece - Facing: 768 + Facing: 256 Actor215: dd Location: 55,15 Owner: Greece - Facing: 768 - Actor216: waypoint + Facing: 256 + waypoint0: waypoint Location: 39,13 Owner: Neutral - Actor235: silo + Silo4: silo Owner: Greece Location: 26,9 - Actor236: silo + Silo3: silo Owner: Greece Location: 24,9 - Actor237: silo + Silo1: silo Owner: Greece Location: 27,2 - Actor238: silo + Silo2: silo Owner: Greece Location: 27,3 waypoint78: waypoint @@ -723,7 +732,7 @@ Actors: APCWaypoint2: waypoint Location: 58,51 Owner: Neutral - Apwr: apwr + Apwr1: apwr Location: 18,12 Owner: Greece Apwr2: apwr @@ -735,17 +744,17 @@ Actors: AttackWaypoint2: waypoint Owner: Neutral Location: 35,30 - Barr: tent + Tent: tent Location: 29,16 Owner: Greece BaseAttacker1: 1tnk Location: 76,35 Owner: Greece - Facing: 640 + Facing: 384 BaseAttacker2: 1tnk Location: 71,30 Owner: Greece - Facing: 640 + Facing: 384 CameraBarrier: camera Location: 70,36 Owner: Neutral @@ -792,21 +801,21 @@ Actors: Location: 63,42 Owner: Greece Health: 28 - Facing: 640 + Facing: 384 IntroEnemy2: jeep Location: 64,42 Owner: Greece Health: 43 - Facing: 640 + Facing: 384 IntroEnemy3: 1tnk Location: 64,43 Owner: Greece Health: 50 - Facing: 640 + Facing: 384 Mcv: mcv Location: 61,45 Owner: USSR - Facing: 384 + Facing: 640 McvWaypoint: waypoint Location: 53,53 Owner: Neutral @@ -817,14 +826,26 @@ Actors: Truck1: truk Location: 54,52 Owner: USSR - Facing: 128 + Facing: 896 Truck2: truk Location: 55,51 Owner: USSR - Facing: 128 + Facing: 896 Weap: weap Location: 22,15 Owner: Greece + NavyPatrol1: waypoint + Owner: Neutral + Location: 35,18 + NavyPatrol2: waypoint + Owner: Neutral + Location: 36,10 + NavyPatrol3: waypoint + Owner: Neutral + Location: 57,12 + NavyPatrol4: waypoint + Owner: Neutral + Location: 76,17 Rules: ra|rules/campaign-rules.yaml, ra|rules/campaign-tooltips.yaml, ra|rules/campaign-palettes.yaml, rules.yaml diff --git a/mods/ra/maps/soviet-06a/soviet06a-AI.lua b/mods/ra/maps/soviet-06a/soviet06a-AI.lua index 7cd4086f22..7e476239c2 100644 --- a/mods/ra/maps/soviet-06a/soviet06a-AI.lua +++ b/mods/ra/maps/soviet-06a/soviet06a-AI.lua @@ -6,29 +6,124 @@ the License, or (at your option) any later version. For more information, see COPYING. ]] -IdlingUnits = function() - local lazyUnits = Greece.GetGroundAttackers() +InfAttack = { } +ArmorAttack = { } +AttackPaths = { { AttackWaypoint1.Location }, { AttackWaypoint2.Location } } - Utils.Do(lazyUnits, function(unit) - Trigger.OnDamaged(unit, function() - Trigger.ClearAll(unit) - Trigger.AfterDelay(0, function() IdleHunt(unit) end) - end) +AlliedInfantryTypes = { "e1", "e1", "e3" } +AlliedArmorTypes = { "jeep", "jeep", "1tnk", "1tnk", "2tnk", "2tnk", "arty" } + +ProduceInfantry = function(barracks) + if barracks.IsDead or barracks.Owner ~= Greece then + return + elseif GreeceMoney() <= 299 and IsHarvesterMissing() then + return + end + + local delay = Utils.RandomInteger(DateTime.Seconds(3), DateTime.Seconds(9)) + local toBuild = { Utils.Random(AlliedInfantryTypes) } + local path = Utils.Random(AttackPaths) + Greece.Build(toBuild, function(unit) + InfAttack[#InfAttack + 1] = unit[1] + + if #InfAttack >= 10 then + SendUnits(InfAttack, path) + InfAttack = { } + Trigger.AfterDelay(DateTime.Minutes(2), function() + ProduceInfantry(barracks) + end) + else + Trigger.AfterDelay(delay, function() + ProduceInfantry(barracks) + end) + end end) end -BaseApwr = { type = "apwr", pos = CVec.New(-13, 7), cost = 500, exists = true } -BaseTent = { type = "tent", pos = CVec.New(-2, 12), cost = 400, exists = true } -BaseProc = { type = "proc", pos = CVec.New(-7, 5), cost = 1400, exists = true } -BaseWeap = { type = "weap", pos = CVec.New(-9, 11), cost = 2000, exists = true } -BaseApwr2 = { type = "apwr", pos = CVec.New(-4, 1), cost = 500, exists = true } +ProduceArmor = function(factory) + if factory.IsDead or factory.Owner ~= Greece then + return + elseif IsHarvesterMissing() then + ProduceHarvester(factory) + return + end -BaseBuildings = { BaseApwr, BaseTent, BaseProc, BaseWeap, BaseApwr2 } + local delay = Utils.RandomInteger(DateTime.Seconds(12), DateTime.Seconds(17)) + local toBuild = { Utils.Random(AlliedArmorTypes) } + local path = Utils.Random(AttackPaths) + Greece.Build(toBuild, function(unit) + ArmorAttack[#ArmorAttack + 1] = unit[1] + if #ArmorAttack >= 6 then + SendUnits(ArmorAttack, path) + ArmorAttack = { } + Trigger.AfterDelay(DateTime.Minutes(3), function() + ProduceArmor(factory) + end) + else + Trigger.AfterDelay(delay, function() + ProduceArmor(factory) + end) + end + end) +end + +ProduceHarvester = function(factory) + if GreeceMoney() < Actor.Cost("harv") then + return + end + + local toBuild = { "harv" } + Greece.Build(toBuild, function(unit) + unit.FindResources() + ProduceArmor(factory) + end) +end + +SendUnits = function(units, path) + Utils.Do(units, function(unit) + if unit.IsDead then + return + end + + unit.Patrol(path, false) + IdleHunt(unit) + end) +end + +IsHarvesterMissing = function() + return #Greece.GetActorsByType("harv") == 0 +end + +GreeceMoney = function() + return Greece.Cash + Greece.Resources +end + +BaseBlueprints = +{ + { type = "apwr", actor = Apwr1, cost = 500, shape = { 3, 3 }, location = CPos.New(18, 12) }, + { type = "apwr", actor = Apwr2, cost = 500, shape = { 3, 3 }, location = CPos.New(27, 6) }, + { type = "tent", actor = Tent, cost = 400, shape = { 2, 3 }, location = CPos.New(29, 16), onBuilt = ProduceInfantry }, + { type = "proc", actor = Proc, cost = 1400, shape = { 3, 4 }, location = CPos.New(24, 9) }, + { type = "weap", actor = Weap, cost = 2000, shape = { 3, 3 }, location = CPos.New(22, 15), onBuilt = ProduceArmor }, + { type = "powr", actor = Powr1, cost = 300, shape = { 2, 3 }, location = CPos.New(20, 2) }, + { type = "powr", actor = Powr2, cost = 300, shape = { 2, 3 }, location = CPos.New(25, 2) }, + { type = "gun", actor = Gun4, cost = 800, shape = { 1, 1 }, location = CPos.New(28, 23) }, + { type = "hbox", actor = Hbox2, cost = 600, shape = { 1, 1 }, location = CPos.New(29, 23) }, + { type = "gun", actor = Gun3, cost = 800, shape = { 1, 1 }, location = CPos.New(20, 23) }, + { type = "hbox", actor = Hbox1, cost = 600, shape = { 1, 1 }, location = CPos.New(19, 23) }, + { type = "gap", actor = Gap, cost = 800, shape = { 1, 1 }, location = CPos.New(24, 22) } +} + +--[[ + Similar to the original CnC/RA [BASE] and [STRUCTURES] .INI sections. + Check a list every so often and (re)build structures missing from + that list, in order, if circumstances allow for it. +]] BuildBase = function() - for i,v in ipairs(BaseBuildings) do - if not v.exists then - BuildBuilding(v) + for _, blueprint in pairs(BaseBlueprints) do + if not blueprint.actor then + BuildBlueprint(blueprint) return end end @@ -36,82 +131,107 @@ BuildBase = function() Trigger.AfterDelay(DateTime.Seconds(10), BuildBase) end -BuildBuilding = function(building) - Trigger.AfterDelay(Actor.BuildTime(building.type), function() +BuildBlueprint = function(blueprint) + Trigger.AfterDelay(Actor.BuildTime(blueprint.type), function() if CYard.IsDead or CYard.Owner ~= Greece then return - elseif Harvester.IsDead and Greece.Resources <= 299 then + elseif GreeceMoney() <= 299 and IsHarvesterMissing() then return end - local actor = Actor.Create(building.type, true, { Owner = Greece, Location = CYardLocation.Location + building.pos }) - Greece.Cash = Greece.Cash - building.cost + if IsBuildAreaBlocked(Greece, blueprint) then + Trigger.AfterDelay(DateTime.Seconds(5), function() + BuildBlueprint(blueprint) + end) + return + end - building.exists = true - Trigger.OnKilled(actor, function() building.exists = false end) - Trigger.OnDamaged(actor, function(building) - if building.Owner == Greece and building.Health < building.MaxHealth * 3/4 then - building.StartBuildingRepairs() - end - end) + local actor = Actor.Create(blueprint.type, true, { Owner = Greece, Location = blueprint.location }) + OnBlueprintBuilt(actor, blueprint) Trigger.AfterDelay(DateTime.Seconds(10), BuildBase) end) end -ProduceInfantry = function() - if not BaseTent.exists then - return - elseif Harvester.IsDead and Greece.Resources <= 299 then - return +OnBlueprintBuilt = function(actor, blueprint) + Greece.Cash = Greece.Cash - blueprint.cost + blueprint.actor = actor + MaintainBuilding(actor, blueprint, 0.75) + + if blueprint.onBuilt then + -- Build() will not work properly on producers if immediately called. + Trigger.AfterDelay(1, function() + blueprint.onBuilt(actor) + end) + end +end + +IsBuildAreaBlocked = function(player, blueprint) + local nw, se = blueprint.northwestEdge, blueprint.southeastEdge + local blockers = Map.ActorsInBox(nw, se, function(actor) + return actor.CenterPosition.Z == 0 and actor.HasProperty("Health") and not IsOwnedSilo(player, actor) + end) + + if #blockers == 0 then + return false end - local delay = Utils.RandomInteger(DateTime.Seconds(3), DateTime.Seconds(9)) - local toBuild = { Utils.Random(AlliedInfantryTypes) } - local Path = Utils.Random(AttackPaths) - Greece.Build(toBuild, function(unit) - InfAttack[#InfAttack + 1] = unit[1] + ScatterBlockers(player, blockers) + return true +end - if #InfAttack >= 10 then - SendUnits(InfAttack, Path) - InfAttack = { } - Trigger.AfterDelay(DateTime.Minutes(2), ProduceInfantry) - else - Trigger.AfterDelay(delay, ProduceInfantry) +-- This is used to disregard silos inside the refinery rebuild area. +IsOwnedSilo = function(player, actor) + return actor.Type == "silo" and actor.Owner == player +end + +ScatterBlockers = function(player, actors) + Utils.Do(actors, function(actor) + if actor.IsIdle and actor.Owner == player and actor.HasProperty("Scatter") then + actor.Scatter() end end) end -ProduceArmor = function() - if not BaseWeap.exists then - return - elseif Harvester.IsDead and Greece.Resources <= 599 then - return +BeginBaseMaintenance = function() + Utils.Do(BaseBlueprints, function(blueprint) + MaintainBuilding(blueprint.actor, blueprint) + end) + + Utils.Do(Greece.GetActors(), function(actor) + if actor.HasProperty("StartBuildingRepairs") then + MaintainBuilding(actor, nil, 0.75) + end + end) +end + +MaintainBuilding = function(actor, blueprint, repairThreshold) + if blueprint then + Trigger.OnKilled(actor, function() blueprint.actor = nil end) + Trigger.OnSold(actor, function() blueprint.actor = nil end) + if not blueprint.northwestEdge then + PrepareBlueprintEdges(blueprint) + end end - local delay = Utils.RandomInteger(DateTime.Seconds(12), DateTime.Seconds(17)) - local toBuild = { Utils.Random(AlliedArmorTypes) } - local Path = Utils.Random(AttackPaths) - Greece.Build(toBuild, function(unit) - ArmorAttack[#ArmorAttack + 1] = unit[1] + if repairThreshold then + local original = actor.Owner - if #ArmorAttack >= 6 then - SendUnits(ArmorAttack, Path) - ArmorAttack = { } - Trigger.AfterDelay(DateTime.Minutes(3), ProduceArmor) - else - Trigger.AfterDelay(delay, ProduceArmor) - end - end) + Trigger.OnDamaged(actor, function() + if actor.Owner ~= original or actor.Health > actor.MaxHealth * repairThreshold then + return + end + + actor.StartBuildingRepairs() + end) + end end -SendUnits = function(units, waypoints) - Utils.Do(units, function(unit) - if not unit.IsDead then - Utils.Do(waypoints, function(waypoint) - unit.AttackMove(waypoint.Location) - end) - IdleHunt(unit) - end - end) +PrepareBlueprintEdges = function(blueprint) + local shapeX, shapeY = blueprint.shape[1], blueprint.shape[2] + local northwestEdge = Map.CenterOfCell(blueprint.location) + WVec.New(-512, -512, 0) + local southeastEdge = northwestEdge + WVec.New(shapeX * 1024, shapeY * 1024, 0) + + blueprint.northwestEdge = northwestEdge + blueprint.southeastEdge = southeastEdge end diff --git a/mods/ra/maps/soviet-06a/soviet06a-reinforcements_teams.lua b/mods/ra/maps/soviet-06a/soviet06a-reinforcements_teams.lua index 6452540b4b..b00f0c3cf2 100644 --- a/mods/ra/maps/soviet-06a/soviet06a-reinforcements_teams.lua +++ b/mods/ra/maps/soviet-06a/soviet06a-reinforcements_teams.lua @@ -44,8 +44,11 @@ EnemyPaths = } Wave = 0 -SendEnemies = function() +SendReinforcements = function() Trigger.AfterDelay(EnemyAttackDelay[Difficulty], function() + if Dome.IsDead or Dome.Owner ~= Greece then + return + end Wave = Wave + 1 if Wave > 3 then @@ -60,8 +63,6 @@ SendEnemies = function() Utils.Do(units, IdleHunt) end - if not Dome.IsDead then - SendEnemies() - end + SendReinforcements() end) end diff --git a/mods/ra/maps/soviet-06a/soviet06a.lua b/mods/ra/maps/soviet-06a/soviet06a.lua index 4a40aa2ac0..8c68964846 100644 --- a/mods/ra/maps/soviet-06a/soviet06a.lua +++ b/mods/ra/maps/soviet-06a/soviet06a.lua @@ -6,146 +6,273 @@ the License, or (at your option) any later version. For more information, see COPYING. ]] -ArmorAttack = { } -AttackPaths = { { AttackWaypoint1 }, { AttackWaypoint2 } } -BaseAttackers = { BaseAttacker1, BaseAttacker2 } -InfAttack = { } -IntroAttackers = { IntroEnemy1, IntroEnemy2, IntroEnemy3 } -Trucks = { Truck1, Truck2 } - -AlliedInfantryTypes = { "e1", "e1", "e3" } -AlliedArmorTypes = { "jeep", "jeep", "1tnk", "1tnk", "2tnk", "2tnk", "arty" } - -SovietReinforcements1 = { "e6", "e6", "e6", "e6", "e6" } -SovietReinforcements2 = { "e4", "e4", "e2", "e2", "e2" } -SovietReinforcements1Waypoints = { McvWaypoint.Location, APCWaypoint1.Location } -SovietReinforcements2Waypoints = { McvWaypoint.Location, APCWaypoint2.Location } - -TruckGoalTrigger = { CPos.New(83, 7), CPos.New(83, 8), CPos.New(83, 9), CPos.New(83, 10), CPos.New(84, 10), CPos.New(84, 11), CPos.New(84, 12), CPos.New(85, 12), CPos.New(86, 12), CPos.New(87, 12), CPos.New(87, 13), CPos.New(88, 13), CPos.New(89, 13), CPos.New(90, 13), CPos.New(90, 14), CPos.New(90, 15), CPos.New(91, 15), CPos.New(92, 15), CPos.New(93, 15), CPos.New(94, 15) } -CameraBarrierTrigger = { CPos.New(65, 39), CPos.New(65, 40), CPos.New(66, 40), CPos.New(66, 41), CPos.New(67, 41), CPos.New(67, 42), CPos.New(68, 42), CPos.New(68, 43), CPos.New(68, 44) } -CameraBaseTrigger = { CPos.New(53, 42), CPos.New(54, 42), CPos.New(54, 41), CPos.New(55, 41), CPos.New(56, 41), CPos.New(56, 40), CPos.New(57, 40), CPos.New(57, 39), CPos.New(58, 39), CPos.New(59, 39), CPos.New(59, 38), CPos.New(60, 38), CPos.New(61, 38) } - -Trigger.OnEnteredFootprint(TruckGoalTrigger, function(a, id) - if not TruckGoalTriggered and a.Owner == USSR and a.Type == "truk" then - TruckGoalTriggered = true - USSR.MarkCompletedObjective(SovietObjective) - USSR.MarkCompletedObjective(SaveAllTrucks) +Trigger.OnRemovedFromWorld(Mcv, function() + if McvDeployed or Mcv.IsDead then + return end + + McvDeployed = true + BuildBase() + SendReinforcements() + + Trigger.AfterDelay(DateTime.Minutes(1), function() + ProduceInfantry(Tent) + end) + + Trigger.AfterDelay(DateTime.Minutes(2), function() + ProduceArmor(Weap) + end) + + local baseAttackers = { BaseAttacker1, BaseAttacker2 } + Trigger.AfterDelay(DateTime.Minutes(2), function() + Utils.Do(baseAttackers, IdleHunt) + end) end) -Trigger.OnEnteredFootprint(CameraBarrierTrigger, function(a, id) - if not CameraBarrierTriggered and a.Owner == USSR then - CameraBarrierTriggered = true +PrepareReveals = function() + local cameraBarrierCells = { CPos.New(65, 39), CPos.New(65, 40), CPos.New(66, 40), CPos.New(66, 41), CPos.New(67, 41), CPos.New(67, 42), CPos.New(68, 42), CPos.New(68, 43), CPos.New(68, 44) } + local cameraBaseCells = { CPos.New(53, 42), CPos.New(54, 42), CPos.New(54, 41), CPos.New(55, 41), CPos.New(56, 41), CPos.New(56, 40), CPos.New(57, 40), CPos.New(57, 39), CPos.New(58, 39), CPos.New(59, 39), CPos.New(59, 38), CPos.New(60, 38), CPos.New(61, 38) } + local cameraBarrierTriggered = false + local cameraBaseTriggered = false + + Trigger.OnEnteredFootprint(cameraBarrierCells, function(a, id) + if cameraBarrierTriggered or a.Owner ~= USSR then + return + end + + cameraBarrierTriggered = true + Trigger.RemoveFootprintTrigger(id) local cameraBarrier = Actor.Create("camera", true, { Owner = USSR, Location = CameraBarrier.Location }) - Trigger.AfterDelay(DateTime.Seconds(15), function() - cameraBarrier.Destroy() - end) - end -end) + Trigger.AfterDelay(DateTime.Seconds(12), cameraBarrier.Destroy) + end) -Trigger.OnEnteredFootprint(CameraBaseTrigger, function(a, id) - if not CameraBaseTriggered and a.Owner == USSR then - CameraBaseTriggered = true + Trigger.OnEnteredFootprint(cameraBaseCells, function(a, id) + if cameraBaseTriggered or a.Owner ~= USSR then + return + end + + cameraBaseTriggered = true + Trigger.RemoveFootprintTrigger(id) local cameraBase1 = Actor.Create("camera", true, { Owner = USSR, Location = CameraBase1.Location }) local cameraBase2 = Actor.Create("camera", true, { Owner = USSR, Location = CameraBase2.Location }) local cameraBase3 = Actor.Create("camera", true, { Owner = USSR, Location = CameraBase3.Location }) local cameraBase4 = Actor.Create("camera", true, { Owner = USSR, Location = CameraBase4.Location }) + Trigger.AfterDelay(DateTime.Minutes(1), function() cameraBase1.Destroy() cameraBase2.Destroy() cameraBase3.Destroy() cameraBase4.Destroy() end) - end -end) - -Trigger.OnAllKilled(Trucks, function() - Greece.MarkCompletedObjective(AlliedObjective) -end) - -Trigger.OnAnyKilled(Trucks, function() - USSR.MarkFailedObjective(SaveAllTrucks) -end) - -Trigger.OnKilled(Apwr, function() - BaseApwr.exists = false -end) - -Trigger.OnKilled(Barr, function() - BaseTent.exists = false -end) - -Trigger.OnKilled(Proc, function() - BaseProc.exists = false -end) - -Trigger.OnKilled(Weap, function() - BaseWeap.exists = false -end) - -Trigger.OnKilled(Apwr2, function() - BaseApwr2.exists = false -end) - -Trigger.OnKilledOrCaptured(Dome, function() - Trigger.AfterDelay(DateTime.Seconds(2), function() - USSR.MarkCompletedObjective(SovietObjective2) - Media.PlaySpeechNotification(USSR, "ObjectiveMet") end) -end) +end --- Activate the AI once the player deployed the Mcv -Trigger.OnRemovedFromWorld(Mcv, function() - if not McvDeployed then - McvDeployed = true - BuildBase() - SendEnemies() - Trigger.AfterDelay(DateTime.Minutes(1), ProduceInfantry) - Trigger.AfterDelay(DateTime.Minutes(2), ProduceArmor) - Trigger.AfterDelay(DateTime.Minutes(2), function() - Utils.Do(BaseAttackers, function(actor) - IdleHunt(actor) - end) +PrepareResponseCruiser = function() + local responseBuildings = { Apwr1, Apwr2, Powr1, Powr2, Weap, Tent } + local responseOrdered = false + + Utils.Do(responseBuildings, function(building) + Trigger.OnDamaged(building, function() + if responseOrdered or USSR.IsObjectiveCompleted(DisruptDome) then + return + end + + responseOrdered = true + OrderResponseCruiser() end) + end) +end + +OrderResponseCruiser = function() + if ResponseCruiser.IsDead then + return end -end) + + Trigger.OnIdle(ResponseCruiser, function() + ResponseCruiser.AttackMove(waypoint0.Location, 2) + end) + + Trigger.OnDamaged(ResponseCruiser, function(_, attacker) + if attacker.IsDead or not ResponseCruiser.CanTarget(attacker) then + return + end + + ResponseCruiser.Attack(attacker) + ResponseCruiser.Scatter() + end) +end + +PrepareBridgeBreakers = function() + local target = Map.ActorsInCircle(waypoint78.CenterPosition, WDist.New(1536), function(actor) + return actor.Type == "br3" + end)[1] + + if not target then + Media.Debug("No bridge segment found.") + return + end + + local orderSent = false + Trigger.AfterDelay(DateTime.Seconds(30), function() + orderSent = true + OrderBridgeBreakers(target) + end) + + local bridgeEntryCells = { CPos.New(75, 30), CPos.New(76, 30), CPos.New(77, 30) } + Trigger.OnEnteredFootprint(bridgeEntryCells, function(a, id) + if a.Owner ~= USSR then + return + end + + Trigger.RemoveFootprintTrigger(id) + + if not orderSent then + OrderBridgeBreakers(target, "with bridge reveal") + end + end) +end + +OrderBridgeBreakers = function(target, reveal) + if target.IsDead then + return + end + + local breakers = { BridgeBreaker1, BridgeBreaker2 } + Utils.Do(breakers, function(breaker) + if breaker.IsDead then + return + end + + breaker.Stop() + breaker.Attack(target, true, true) + end) + + if not reveal then + return + end + + local camera = Actor.Create("camera", true, { Owner = USSR, Location = target.Location }) + Trigger.OnKilled(target, function() + Trigger.AfterDelay(DateTime.Seconds(2), camera.Destroy) + end) +end + +PrepareObjectives = function() + InitObjectives(USSR) + KillTrucks = AddPrimaryObjective(Greece, "") + EscortConvoy = AddPrimaryObjective(USSR, "escort-convoy") + DisruptDome = AddSecondaryObjective(USSR, "destroy-capture-radar-dome-reinforcements") + SaveAllTrucks = AddSecondaryObjective(USSR, "keep-trucks-alive") + + Trigger.OnKilledOrCaptured(Dome, function() + -- Let the capture notification play first. + Trigger.AfterDelay(DateTime.Seconds(2), function() + USSR.MarkCompletedObjective(DisruptDome) + Media.PlaySpeechNotification(USSR, "ObjectiveMet") + end) + end) +end + +PrepareTrucks = function() + local trucks = { Truck1, Truck2 } + local goalCells = { CPos.New(83, 7), CPos.New(83, 8), CPos.New(83, 9), CPos.New(83, 10), CPos.New(84, 10), CPos.New(84, 11), CPos.New(84, 12), CPos.New(85, 12), CPos.New(86, 12), CPos.New(87, 12), CPos.New(87, 13), CPos.New(88, 13), CPos.New(89, 13), CPos.New(90, 13), CPos.New(90, 14), CPos.New(90, 15), CPos.New(91, 15), CPos.New(92, 15), CPos.New(93, 15), CPos.New(94, 15) } + + local goalTriggered = false + Trigger.OnEnteredFootprint(goalCells, function(a) + if not goalTriggered and a.Owner == USSR and a.Type == "truk" then + goalTriggered = true + USSR.MarkCompletedObjective(EscortConvoy) + USSR.MarkCompletedObjective(SaveAllTrucks) + end + end) + + Trigger.OnAllKilled(trucks, function() + Greece.MarkCompletedObjective(KillTrucks) + end) + + Trigger.OnAnyKilled(trucks, function() + USSR.MarkFailedObjective(SaveAllTrucks) + end) +end + +BeginIntro = function() + local introAttackers = { IntroEnemy1, IntroEnemy2, IntroEnemy3 } + local sovietReinforcements1 = { "e6", "e6", "e6", "e6", "e6" } + local sovietReinforcements2 = { "e4", "e4", "e2", "e2", "e2" } + local sovietReinforcements1Path = { McvWaypoint.Location, APCWaypoint1.Location } + local sovietReinforcements2Path = { McvWaypoint.Location, APCWaypoint2.Location } + + Mcv.Move(McvWaypoint.Location) + Utils.Do(introAttackers, IdleHunt) + Reinforcements.ReinforceWithTransport(USSR, "apc", sovietReinforcements1, sovietReinforcements1Path) + Reinforcements.ReinforceWithTransport(USSR, "apc", sovietReinforcements2, sovietReinforcements2Path) +end + +PrepareIdleGuards = function() + local lazyUnits = Utils.Where(Greece.GetGroundAttackers(), function(unit) + return unit.Type ~= "ca" and unit.Type ~= "arty" + end) + + Utils.Do(lazyUnits, function(unit) + local triggered = false + + Trigger.OnDamaged(unit, function() + if triggered then + return + end + + triggered = true + IdleHunt(unit) + end) + end) +end WorldLoaded = function() USSR = Player.GetPlayer("USSR") Greece = Player.GetPlayer("Greece") + PrepareReveals() + PrepareObjectives() Camera.Position = CameraStart.CenterPosition - - Mcv.Move(McvWaypoint.Location) Harvester.FindResources() - Utils.Do(IntroAttackers, function(actor) - IdleHunt(actor) + BeginBaseMaintenance() + + if Difficulty ~= "easy" then + PrepareResponseCruiser() + Trigger.AfterDelay(1, PrepareBridgeBreakers) + end + + if Difficulty == "hard" then + BuildNavyPatrol() + end + + PrepareTrucks() + BeginIntro() + PrepareIdleGuards() +end + +BuildNavyPatrol = function() + local types = { "dd", "dd" } + local patrolPath = { NavyPatrol1.Location, NavyPatrol2.Location, NavyPatrol3.Location, NavyPatrol4.Location } + + Greece.Build(types, function(units) + Utils.Do(units, function(u) + u.Patrol(patrolPath, true, 100) + end) + + Trigger.OnAllKilled(units, function() + if not Greece.HasPrerequisites({ "syrd", "dome" }) then + return + end + + BuildNavyPatrol() + end) end) - - Utils.Do(Map.NamedActors, function(actor) - if actor.Owner == Greece and actor.HasProperty("StartBuildingRepairs") then - Trigger.OnDamaged(actor, function(building) - if building.Owner == Greece and building.Health < 3/4 * building.MaxHealth then - building.StartBuildingRepairs() - end - end) - end - end) - - Reinforcements.ReinforceWithTransport(USSR, "apc", SovietReinforcements1, SovietReinforcements1Waypoints) - Reinforcements.ReinforceWithTransport(USSR, "apc", SovietReinforcements2, SovietReinforcements2Waypoints) - - InitObjectives(USSR) - - AlliedObjective = AddPrimaryObjective(Greece, "") - SovietObjective = AddPrimaryObjective(USSR, "escort-convoy") - SovietObjective2 = AddSecondaryObjective(USSR, "destroy-capture-radar-dome-reinforcements") - SaveAllTrucks = AddSecondaryObjective(USSR, "keep-trucks-alive") end Tick = function() if USSR.HasNoRequiredUnits() then - Greece.MarkCompletedObjective(AlliedObjective) + Greece.MarkCompletedObjective(KillTrucks) end if Greece.Resources >= Greece.ResourceCapacity * 0.75 then