From 8b522680e3d878b0b7d77cf4d228a83473c8da20 Mon Sep 17 00:00:00 2001 From: yamismo Date: Thu, 23 Feb 2023 16:25:36 +0100 Subject: [PATCH] Added Nod Covert Operations - Eviction Notice - scb30ea --- mods/cnc/languages/lua/en.ftl | 12 +- .../eviction-notice/eviction-notice-AI.lua | 407 +++++++++++ .../maps/eviction-notice/eviction-notice.lua | 349 ++++++++++ mods/cnc/maps/eviction-notice/map.bin | Bin 0 -> 20497 bytes mods/cnc/maps/eviction-notice/map.png | Bin 0 -> 9184 bytes mods/cnc/maps/eviction-notice/map.yaml | 649 ++++++++++++++++++ mods/cnc/maps/eviction-notice/rules.yaml | 139 ++++ mods/cnc/maps/eviction-notice/weapons.yaml | 3 + mods/cnc/missions.yaml | 3 + 9 files changed, 1561 insertions(+), 1 deletion(-) create mode 100644 mods/cnc/maps/eviction-notice/eviction-notice-AI.lua create mode 100644 mods/cnc/maps/eviction-notice/eviction-notice.lua create mode 100644 mods/cnc/maps/eviction-notice/map.bin create mode 100644 mods/cnc/maps/eviction-notice/map.png create mode 100644 mods/cnc/maps/eviction-notice/map.yaml create mode 100644 mods/cnc/maps/eviction-notice/rules.yaml create mode 100644 mods/cnc/maps/eviction-notice/weapons.yaml diff --git a/mods/cnc/languages/lua/en.ftl b/mods/cnc/languages/lua/en.ftl index 96940fb6ed..928fdfad7a 100644 --- a/mods/cnc/languages/lua/en.ftl +++ b/mods/cnc/languages/lua/en.ftl @@ -111,7 +111,7 @@ infiltrate-barracks-factory-conyard = Infiltrate the barracks, weapon factory an ## nod07a find-nod-base = Find the Nod base. -## nod07ab, nod08ab, nod09 +## nod07ab, nod08ab, nod09, eviction-notice eliminate-gdi-forces = Eliminate all GDI forces in the area. ## nod07c @@ -140,3 +140,13 @@ destroy-tech-center = Destroy the GDI R&D center. destroy-capture-warfactory = Destroy or capture the Weapons Factory. destroy-mammoth-tanks = Destroy the Mammoth tanks in the R&D base. keep-commando-alive = Keep your Commando alive. + +## eviction-notice +take-civilians-money-crates = Find all the civilians' money. + They won't need it anymore. +quickly-destroy-ion-cannon = Disable GDI Ion Cannon before + it fires two times. +nod-soldier = Nod Soldier +civilians-runs = Hey, those civilians... where are they going? +destroy-ion-cannon-advise = The GDI are preparing their ion cannon. Don't let them get used to it. +village-destruction-warning = Be careful, commander. The GDI won't stand still while we burn the entire village. diff --git a/mods/cnc/maps/eviction-notice/eviction-notice-AI.lua b/mods/cnc/maps/eviction-notice/eviction-notice-AI.lua new file mode 100644 index 0000000000..4c7263440f --- /dev/null +++ b/mods/cnc/maps/eviction-notice/eviction-notice-AI.lua @@ -0,0 +1,407 @@ +--[[ + Copyright (c) The OpenRA Developers and Contributors + This file is part of OpenRA, which is free software. It is made + available to you under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. For more + information, see COPYING. +]] + +GDInuke1 = { type = "nuk2", pos = CPos.New(6,3), blocked = false } +GDIproc1 = { type = "proc", pos = CPos.New(8,12), blocked = false } +GDIpyle = { type = "pyle", pos = CPos.New(17,4), blocked = false } +GDIweap = { type = "weap", pos = CPos.New(10,9), blocked = false } +GDIproc2 = { type = "proc", pos = CPos.New(12,3), blocked = false } +GDInuke2 = { type = "nuk2", pos = CPos.New(4,11), blocked = false } +GDInuke3 = { type = "nuk2", pos = CPos.New(3,14), blocked = false } +GDIdef1 = { type = "atwr", pos = CPos.New(8,5), blocked = false } +GDIdef2 = { type = "atwr", pos = CPos.New(6,10), blocked = false } +GDIdef3 = { type = "atwr", pos = CPos.New(6,16), blocked = false } +GDIdef4 = { type = "gtwr", pos = CPos.New(9,8), blocked = false } +GDInuke4 = { type = "nuk2", pos = CPos.New(13,12), blocked = false } +GDIdef5 = { type = "atwr", pos = CPos.New(15,10), blocked = false } +GDIdef6 = { type = "atwr", pos = CPos.New(13,16), blocked = false } +GDInuke5 = { type = "nuk2", pos = CPos.New(11,14), blocked = false } +GDIsilo1 = { type = "silo", pos = CPos.New(3,6), blocked = false } +GDIsilo2 = { type = "silo", pos = CPos.New(5,17), blocked = false } +GDIdef7 = { type = "atwr", pos = CPos.New(19,5), blocked = false } +GDIdef8 = { type = "atwr", pos = CPos.New(10,21), blocked = false } +GDIsilo3 = { type = "silo", pos = CPos.New(17,2), blocked = false } +GDIsilo4 = { type = "silo", pos = CPos.New(9,17), blocked = false } +GDIdef9 = { type = "atwr", pos = CPos.New(18,20), blocked = false } +GDIhpad1 = { type = "hpad", pos = CPos.New(3,18), blocked = false } + +GDIBase = { GDINuke1, GDIPyle, GDIWeap, GDINuke2, GDINuke3, GDIDef1, GDIDef2, GDIDef3, GDIDef4, + GDINuke4, GDIDef5, GDIDef6, GDINuke5, GDISilo1, GDISilo2, GDIDef7, GDIDef8, GDISilo3, GDISilo4, GDIDef9 } +GDIRebuildList = { GDInuke1, GDIproc1, GDIpyle, GDIweap, GDIproc2, GDInuke2, GDInuke3, GDIdef1, GDIdef2, GDIdef3, GDIdef4, + GDInuke4, GDIdef5, GDIdef6, GDInuke5, GDIsilo1, GDIsilo2, GDIdef7, GDIdef8, GDIsilo3, GDIsilo4, GDIdef9 } +BSizes = { nuk2 = CVec.New(2,3), proc = CVec.New(3,4), pyle = CVec.New(2,3), weap = CVec.New(3,3), atwr = CVec.New(1,1), gtwr = CVec.New(1,1), silo = CVec.New(2,1), hpad = CVec.New(2,2) } + +ProductionBuildings = { infantry = GDIPyle, vehicle = GDIWeap, aircraft = GDICYard } + +ProductionQueue = { infantry = { }, vehicle = { }, aircraft = { } } +NewTeam = { } +TeamJob = "attack" +AT1 = { "e2", "e2", "e2", "e3", "e3", "vehicle", "jeep", "jeep", "apc" } +AT2 = { "e2", "e2", "e2", "e3", "e3", "e1", "e1", "e1", "e1" } +AT3 = { "vehicle", "mtnk", "mtnk", "jeep", "jeep" } +AT4 = { "e3", "e3", "e1", "e1", "e1", "vehicle", "mtnk", "mtnk", "msam" } +AT5 = { "vehicle", "jeep", "jeep", "jeep", "apc", "apc" } +AT6 = { "e3", "e3", "e1", "e1", "e1", "vehicle", "htnk" } +AT7 = { "vehicle", "htnk", "htnk", "mtnk" } +AT8 = { "vehicle", "htnk", "htnk", "msam" } +AT9 = { "vehicle", "mtnk", "msam", "apc", "apc" } +AT10 = { "e2", "e2", "e2", "e2", "e2", "e2", "e2", "e2", "e2", "e2" } +OT1 = { "aircraft", "orca", "orca" } +OT2 = { "aircraft", "orca", "orca", "orca", "orca" } +AttackTeams = { AT1, AT2, AT3, AT4, AT5, AT6, AT7, AT8, AT9, AT10 } + +PT1 = { "vehicle", "htnk" } +PT2 = { "vehicle", "mtnk", "mtnk" } +PT3 = { "e2", "e2", "e2", "e3", "e3" } +PatrolTeams = { PT1, PT2, PT3 } + +DT1 = { "vehicle", "mtnk", "mtnk", "msam", "msam" } +DT2 = { "e3", "e3", "e1", "e1", "e1", "vehicle", "mtnk", "mtnk", "msam" } +DT3 = { "vehicle", "htnk", "htnk", "msam" } +DefenseTeams = { DT1, DT2, DT3 } + +AddOrcasTo = { AT1, AT2, AT5, AT6, AT9, DT1, DT2, DT3 } + +AP1 = { waypoint5.Location, waypoint9.Location, waypoint8.Location, waypoint13.Location } +AP2 = { waypoint2.Location, waypoint5.Location, waypoint4.Location, waypoint6.Location } +AP3 = { waypoint2.Location, waypoint3.Location, waypoint4.Location, waypoint6.Location } +AP4 = { waypoint5.Location, waypoint9.Location, waypoint14.Location, PlayerStart.Location, waypoint13.Location } +AP5 = { waypoint5.Location, waypoint9.Location, waypoint6.Location } +AttackPaths = { AP1, AP2, AP3, AP4, AP5 } + +InitAi = function() + + if Difficulty == "hard" then + PBDelay = 115 + PUDelay = 30 + ICDelay = 800 + ProdCDSecs = 30 + GDI.Resources = 10000 + MinHarvs = 3 + ProcUpgrade = "AIHProcUpgrade" + CivsBuildingsToDestroy = 8 + end + + if Difficulty == "normal" then + PBDelay = 140 + PUDelay = 50 + ICDelay = 1000 + ProdCDSecs = 40 + GDI.Resources = 7000 + MinHarvs = 2 + ProcUpgrade = "AINProcUpgrade" + CivsBuildingsToDestroy = 11 + end + + if Difficulty == "easy" then + PBDelay = 180 + PUDelay = 70 + ICDelay = 1200 + ProdCDSecs = 50 + GDI.Resources = 5000 + MinHarvs = 2 + Nod.Resources = 3000 + CivsBuildingsToDestroy = 13 + end + + RepairNamedActors(GDI, 0.75) + SetAutoRebuild() + + Trigger.OnKilled(GDIPyle, function() + ProductionQueue["infantry"] = { } + end) + Trigger.OnKilled(GDIWeap, function() + ProductionQueue["vehicle"] = { } + end) + + if ProcUpgrade then + ProcUpg = Actor.Create(ProcUpgrade, true, { Owner = GDI }) + end + + Trigger.AfterDelay(DateTime.Seconds(PBDelay), function() + + CheckBase() + + Trigger.AfterDelay(DateTime.Seconds(PUDelay), function() + ProduceUnits() + ReduceProdCD() + end) + + Trigger.AfterDelay(DateTime.Seconds(ICDelay), function() + FireIonCannon(270) + end) + + if not GDIAdvComCenter.IsDead then + IonCannonOnline = true + Media.DisplayMessage(UserInterface.Translate("destroy-ion-cannon-advise")) + DestroyIonCannon = AddSecondaryObjective(Nod, "quickly-destroy-ion-cannon") + end + end) + +end + +ReduceProdCD = function() + Trigger.AfterDelay(DateTime.Minutes(2), function() + ProdCDSecs = ProdCDSecs - 1 + if ProdCDSecs > 10 then + ReduceProdCD() + end + end) +end + +PrepareOrcas = function() + table.insert(GDIRebuildList, 11, GDIhpad1) + Utils.Do(AddOrcasTo, function(t) + Utils.Do(OT1, function(u) + table.insert(t, u) + end) + end) + table.insert(AttackTeams, OT2) + table.insert(PatrolTeams, OT1) +end + +--Building logic +SetAutoRebuild = function() + Utils.Do(GDIBase, function(b) + Trigger.OnKilled(b, CheckBase) + end) +end + +CheckBase = function() + if GDICYard.IsDead then return end + for i = 1, #GDIRebuildList do + if GDIRebuildList[i].blocked then + CheckBuildablePlace(GDIRebuildList[i]) + else + local building = GDI.GetActorsByType(GDIRebuildList[i].type) + if #building < 1 then + BuildBuilding(GDIRebuildList[i], GDICYard) + return + end + for ii = 1, #building do + if not building[ii].IsDead and building[ii].Location == GDIRebuildList[i].pos then + break + end + if ii == #building then + BuildBuilding(GDIRebuildList[i], GDICYard) + return + end + end + end + end + if not CheckProgrammed then + CheckProgrammed = true + Trigger.AfterDelay(250, function() + CheckProgrammed = false + CheckBase() + end) + end +end + +CheckBuildablePlace = function(b) + local actors = Map.ActorsInBox(WPos.New(b.pos.X * 1024, b.pos.Y * 1024, 0), WPos.New((b.pos.X + BSizes[b.type].X) * 1024, (b.pos.Y + BSizes[b.type].Y) * 1024, 0), function(a) return a.Owner == Nod end) + if #actors > 0 then + b.blocked = true + return false + else + b.blocked = false + return true + end +end + +BuildBuilding = function(building, cyard) + if CyardIsBuilding or GDI.Resources < Actor.Cost(building.type) then + if Dontspam == true then + return + end + Dontspam = true + Trigger.AfterDelay(DateTime.Seconds(10), function() + Dontspam = false + CheckBase() + end) + return + end + + CyardIsBuilding = true + + GDI.Resources = GDI.Resources - Actor.Cost(building.type) + Trigger.AfterDelay(Actor.BuildTime(building.type), function() + CyardIsBuilding = false + + if cyard.IsDead or cyard.Owner ~= GDI then + GDI.Resources = GDI.Resources + Actor.Cost(building.type) + return + end + + if CheckBuildablePlace(building) == false then + CheckBase() + return + end + + local newbuilding = Actor.Create(building.type, true, { Owner = GDI, Location = building.pos }) + Trigger.OnKilled(newbuilding, function() CheckBase() end) + RepairBuilding(GDI, newbuilding, 0.75) + GuardBuilding(newbuilding) + + if newbuilding.Type == "pyle" then + ProductionBuildings["infantry"] = newbuilding + Trigger.OnKilled(newbuilding, function() ProductionQueue["infantry"] = { } end) + RestartUnitProduction() + elseif newbuilding.Type == "weap" then + ProductionBuildings["vehicle"] = newbuilding + Trigger.OnKilled(newbuilding, function() ProductionQueue["vehicle"] = { } end) + RestartUnitProduction() + NeedHarv = false + elseif newbuilding.Type == "hpad" then + ProductionBuildings["aircraft"] = newbuilding + Trigger.OnKilled(newbuilding, function() ProductionQueue["aircraft"] = { } end) + RestartUnitProduction() + end + + Trigger.AfterDelay(50, CheckBase) + end) + +end + +--Units production logic +UniqueTeamsQueue = { } +ProdCooldown = false +CheckProduction = function() + if #GDI.GetActorsByType("proc") < 1 and GDI.Resources < 6000 then + Trigger.AfterDelay(250, CheckProduction) + return + elseif ProductionBuildings["infantry"].IsDead and ProductionBuildings["vehicle"].IsDead and ProductionBuildings["aircraft"].IsDead then + return + elseif not ProductionBuildings["vehicle"].IsDead and CheckForHarvester() then + NeedHarv = true + ProductionBuildings["vehicle"].Build( { "harv" }, function() + CheckProduction() + Trigger.AfterDelay(DateTime.Seconds(5), function() + if not ProductionBuildings["vehicle"].IsDead then + ProductionBuildings["vehicle"].Build( { "harv" } ) + end + end) + end) + return + end + NeedHarv = false + + if #ProductionQueue["infantry"] + #ProductionQueue["vehicle"] + #ProductionQueue["aircraft"] < 1 and Producing then + Producing = false + end + if ProdCooldown or GDI.Resources < 4000 then + RestartUnitProduction() + else + ProduceUnits() + end + +end + +ProduceUnits = function() + if NeedHarv then + RestartUnitProduction() + return + end + if not Producing then + NewTeam = { } + CreateUnitsGroup() + if #ProductionQueue["infantry"] + #ProductionQueue["vehicle"] + #ProductionQueue["aircraft"] > 0 then + Producing = true + else + RestartUnitProduction() + return + end + end + + if #ProductionQueue["infantry"] > 0 and not ProductionBuildings["infantry"].IsDead and not ProductionBuildings["infantry"].IsProducing("e2") then + ProductionBuildings["infantry"].Build( { ProductionQueue["infantry"][1] }, function(u) + table.insert(NewTeam, u[1]) + table.remove(ProductionQueue["infantry"], 1) + CheckTeamCompleted() + end) + end + if #ProductionQueue["vehicle"] > 0 and not ProductionBuildings["vehicle"].IsDead and not ProductionBuildings["vehicle"].IsProducing("mtnk") then + ProductionBuildings["vehicle"].Build( { ProductionQueue["vehicle"][1] }, function(u) + table.insert(NewTeam, u[1]) + table.remove(ProductionQueue["vehicle"], 1) + CheckTeamCompleted() + end) + end + if #ProductionQueue["aircraft"] > 0 and not ProductionBuildings["aircraft"].IsDead and not ProductionBuildings["aircraft"].IsProducing("orca") then + ProductionBuildings["aircraft"].Build( { ProductionQueue["aircraft"][1] }, function(u) + table.insert(NewTeam, u[1]) + table.remove(ProductionQueue["aircraft"], 1) + CheckTeamCompleted() + end) + end + + RestartUnitProduction() + +end + +CheckTeamCompleted = function() + if #ProductionQueue["infantry"] + #ProductionQueue["vehicle"] + #ProductionQueue["aircraft"] < 1 and #NewTeam > 0 then + NewTeam = Utils.Where(NewTeam, function(u) return not u.IsDead end) + if TeamJob == "attack" then + SendAttackGroup(NewTeam) + elseif TeamJob == "patrol" then + TeamJob = "attack" + PatrolTeam = NewTeam + StartPatrol() + elseif TeamJob == "defend" then + TeamJob = "attack" + BaseDefenseTeam = NewTeam + StartGuard() + end + Producing = false + ProdCooldown = true + NewTeam = { } + Trigger.AfterDelay(DateTime.Seconds(ProdCDSecs), function() ProdCooldown = false end) + else + ProduceUnits() + end +end + +SendAttackGroup = function(team) + MoveAsGroup(team, Utils.Random(AttackPaths), 1, false) +end + +CreateUnitsGroup = function() + local team = AttackTeams + if #UniqueTeamsQueue > 0 then + team = UniqueTeamsQueue[1].team + TeamJob = UniqueTeamsQueue[1].job + table.remove(UniqueTeamsQueue, 1) + end + local pb = "infantry" + Utils.Do(Utils.Random(team), function(u) + if u == "vehicle" or u == "aircraft" then + pb = u + elseif ProductionBuildings[pb] and not ProductionBuildings[pb].IsDead and ProductionBuildings[pb].Owner == GDI then + table.insert(ProductionQueue[pb], u) + end + end) +end + +RestartUnitProduction = function() + if not Restarting then + Restarting = true + else + return + end + Trigger.AfterDelay(DateTime.Seconds(5), function() + Restarting = false + CheckProduction() + end) +end + +CheckForHarvester = function() + local harv = GDI.GetActorsByType("harv") + return #harv < MinHarvs +end diff --git a/mods/cnc/maps/eviction-notice/eviction-notice.lua b/mods/cnc/maps/eviction-notice/eviction-notice.lua new file mode 100644 index 0000000000..01d0c8edb3 --- /dev/null +++ b/mods/cnc/maps/eviction-notice/eviction-notice.lua @@ -0,0 +1,349 @@ +--[[ + Copyright (c) The OpenRA Developers and Contributors + This file is part of OpenRA, which is free software. It is made + available to you under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. For more + information, see COPYING. +]] + +WelcomeTeam = { Wel1, Wel2, Wel3, Wel4, Wel5 } + +Civilians = { Actor111, Actor112, Actor113, Actor114, Actor115, Actor116, Actor117, Actor118, Actor119, Actor120, Actor121 } +RunForHelpCivs = { Actor109, Actor110 } +CiviliansBuildings = { Actor35, Actor36, Actor37, Actor38, Actor39, Actor40, Actor41, Actor42, Actor43, Actor44, Actor45, Actor54, Actor55, Actor56, Actor57, Actor58 } +OilDerricks = { Actor49, Actor50, Actor51, Actor52 } +CivsMoneyBuildings = { Actor37, Actor41, Actor42, Actor44, Actor58 } + +ReinforcementsMammoths = { "htnk", "htnk" } +ReinforcementsEngineers = { "e6", "e6", "e6", "e6", "e6" } +CiviliansHelpTeam = { "mtnk", "mtnk", "msam" } +BaseDefenseTeam = { Def1, Def2, Def3, Def4 } +PatrolTeam = { Actor103, Actor122, Actor123 } +PatrolPath = { waypoint2.Location, waypoint3.Location, waypoint4.Location, waypoint5.Location } + +CapturableStructures = { "afld", "hand", "hq", "nuke", "silo", "proc", "sam" } +IonCannonTargets = { { "obli", "gun", "gtwr" }, { "harv" } } + +WelcomeTeamCellTrigger = { CPos.New(17,59), CPos.New(18,59), CPos.New(19,59), CPos.New(17,60), CPos.New(18,60), CPos.New(19,60), CPos.New(25,53), CPos.New(25,54), CPos.New(25,55), CPos.New(26,53), CPos.New(26,54), CPos.New(26,55) } +CivsGDIHelpCellTrigger = { CPos.New(13,7) } +GDIBaseEntranceCells = { CPos.New(16,5), CPos.New(16,6), CPos.New(16,7), CPos.New(16,8), CPos.New(17,4), CPos.New(17,5), CPos.New(17,6), CPos.New(17,7), CPos.New(17,8), CPos.New(18,4), CPos.New(18,5), CPos.New(19,4), CPos.New(8,15), CPos.New(9,15), CPos.New(10,15), CPos.New(11,15), CPos.New(12,15), CPos.New(13,15), CPos.New(14,15), CPos.New(9,16), CPos.New(10,16), CPos.New(11,16), CPos.New(12,16), CPos.New(13,16), CPos.New(14,16) } +InnerGDIBaseEntranceCells = { CPos.New(7,5), CPos.New(7,6), CPos.New(7,7), CPos.New(8,6), CPos.New(8,7), CPos.New(9,6), CPos.New(9,7) } + +CaptureStructures = function(actor) + local structures = Nod.GetActorsByTypes(CapturableStructures) + local distance = 500 + local captst = nil + Utils.Do(structures, function(st) + if not actor.IsDead and not st.IsDead and distance > (math.abs((actor.Location - st.Location).X) + math.abs((actor.Location - st.Location).Y)) then + distance = math.abs((actor.Location - st.Location).X) + math.abs((actor.Location - st.Location).Y) + captst = st + end + end) + if captst then + actor.Capture(captst) + end +end + +SendGDIAirstrike = function() + if not GDIhq.IsDead and GDIhq.Owner == GDI then + local target = GetAirstrikeTarget(Nod) + if target then + GDIhq.TargetAirstrike(target, Angle.SouthEast) + else + Trigger.AfterDelay(DateTime.Seconds(5), SendGDIAirstrike) + end + end +end + +OilDerricksAstkSent = false +OilDerricksAirstrike = function() + if not OilDerricksAstkSent then + SendGDIAirstrike() + OilDerricksAstkSent = true + end +end + +CivsRunning = false +RunForHelp = function() + if not CivsRunning then + Utils.Do(RunForHelpCivs, function(actor) + actor.Move(CPos.New(53,45)) + actor.Move(waypoint6.Location) + actor.Move(waypoint4.Location) + actor.Move(waypoint9.Location) + actor.Move(waypoint2.Location) + actor.Move(waypoint12.Location) + actor.Move(waypoint6.Location) + end) + Trigger.OnEnteredFootprint(CivsGDIHelpCellTrigger, function(a, id) + if a == RunForHelpCivs[1] or a == RunForHelpCivs[2] then + Reinforcements.Reinforce(GDI, CiviliansHelpTeam, { CPos.New(2,9), CPos.New(3,9) }, 30, function(a) + a.AttackMove(waypoint2.Location) + a.AttackMove(waypoint9.Location) + a.AttackMove(waypoint8.Location) + IdleHunt(a) + end) + Trigger.RemoveFootprintTrigger(id) + end + end) + CivsRunning = true + local cam = Actor.Create("camera", true, { Owner = Nod, Location = CPos.New(53,44) }) + Trigger.AfterDelay(125, cam.Destroy) + Media.DisplayMessage(UserInterface.Translate("civilians-runs"), UserInterface.Translate("nod-soldier")) + end +end + +CivsBuildingsToDestroy = 0 +CheckVillageDestruction = function() + CivsBuildingsToDestroy = CivsBuildingsToDestroy - 1 + if CivsBuildingsToDestroy == 2 then + Media.DisplayMessage(UserInterface.Translate("village-destruction-warning")) + elseif CivsBuildingsToDestroy == 0 then + Reinforcements.Reinforce(GDI, ReinforcementsMammoths, { CPos.New(2,9), CPos.New(3,9) }, 40, function(a) + a.AttackMove(waypoint11.Location) + a.AttackMove(waypoint5.Location) + a.AttackMove(waypoint4.Location) + a.AttackMove(waypoint6.Location) + IdleHunt(a) + end) + end +end + +GuardBase = function() + Utils.Do(GDIBase, function(building) + GuardBuilding(building) + end) +end + +GuardBuilding = function(building) + Trigger.OnDamaged(building, function(slf, atk, dmg) + if atk.Type ~= "player" and not atk.IsDead and atk.Owner == Nod then + Utils.Do(BaseDefenseTeam, function(guard) + if not guard.IsDead and not building.IsDead then + if guard.Stance == "Defend" then + guard.Stop() + guard.Stance = "AttackAnything" + guard.AttackMove(atk.Location, 3) + Trigger.OnIdle(guard, function() + guard.AttackMove(waypoint12.Location, 3) + guard.Stance = "Defend" + Trigger.ClearAll(guard) + end) + end + end + end) + end + end) +end + +StartGuard = function() + Trigger.OnAllKilled(BaseDefenseTeam, function() + table.insert(UniqueTeamsQueue, { team = DefenseTeams, job = "defend" }) + end) + Utils.Do(BaseDefenseTeam, function(guard) + guard.Stance = "Defend" + end) +end + +StartPatrol = function() + Trigger.OnAllKilled(PatrolTeam, function() + table.insert(UniqueTeamsQueue, { team = PatrolTeams, job = "patrol" }) + end) + Utils.Do(PatrolTeam, function(a) + Trigger.OnKilled(a, function() + Utils.Do(PatrolTeam, function(a) + if not a.IsDead then + a.Stop() + IdleHunt(a) + end + end) + end) + end) + MoveAsGroup(PatrolTeam, PatrolPath, 1, true) +end + +MoveAsGroup = function(team, path, i, loop) + if i == 1 and not loop then + Utils.Do(team, function(u) + Trigger.OnDamaged(u, function() + Utils.Do(team, function(u) + if not u.IsDead then + Trigger.Clear(u, "OnDamaged") + IdleHunt(u) + end + end) + end) + end) + end + Utils.Do(team, function(a) + if not a.IsDead then + a.Stance = "AttackAnything" + a.AttackMove(path[i], 2) + Trigger.OnIdle(a, function() + Trigger.Clear(a, "OnIdle") + a.Stance = "Defend" + local teamNumber = 0 + local regrouped = false + Utils.Do(team, function(a) + if a.IsDead or a.Stance == "Defend" then + teamNumber = teamNumber + 1 + if teamNumber == #team then + regrouped = true + end + end + end) + if regrouped then + if i == #path then + if loop == true then + i = 1 + else + Trigger.AfterDelay(5, function() + Utils.Do(team, function(a) + if not a.IsDead then + a.Stance = "AttackAnything" + IdleHunt(a) + end + end) + end) + return + end + else + i = i + 1 + end + Trigger.AfterDelay(20, function() + MoveAsGroup(team, path, i, loop) + end) + end + end) + end + end) +end + +IonCannonOnline = false +ICShotsCount = 0 +FireIonCannon = function(timer) + if IonCannonOnline then + local ii = Utils.RandomInteger(1, 4) + local targets = { } + if ii < 3 then + targets = Nod.GetActorsByTypes(IonCannonTargets[ii]) + else + targets = Nod.GetGroundAttackers() + end + if #targets > 0 then + local rand = Utils.RandomInteger(1, #targets + 1) + if not targets[rand].IsDead then + GDIAdvComCenter.ActivateIonCannon(targets[rand].Location) + if ICShotsCount < 2 then + ICShotsCount = ICShotsCount + 1 + if ICShotsCount > 1 then + Nod.MarkFailedObjective(DestroyIonCannon) + Trigger.ClearAll(GDIAdvComCenter) + end + end + Trigger.AfterDelay(DateTime.Seconds(timer), function() FireIonCannon(timer) end) + return + end + end + Trigger.AfterDelay(DateTime.Seconds(1), function() FireIonCannon(timer) end) + end +end + +CheckObjectives = function() + if GDI.HasNoRequiredUnits() then Nod.MarkCompletedObjective(EliminateAllGDI) end + if Nod.HasNoRequiredUnits() then Nod.MarkFailedObjective(EliminateAllGDI) end + Trigger.AfterDelay(25, CheckObjectives) +end + +WorldLoaded = function() + Nod = Player.GetPlayer("Nod") + GDI = Player.GetPlayer("GDI") + Camera.Position = PlayerStart.CenterPosition + Flare = Actor.Create("flare", true, { Owner = Nod, Location = DefaultFlareLocation.Location }) + Trigger.AfterDelay(DateTime.Minutes(1), Flare.Destroy) + InitObjectives(Nod) + EliminateAllGDI = AddPrimaryObjective(Nod, "eliminate-gdi-forces") + FindAllCivMoney = AddSecondaryObjective(Nod, "take-civilians-money-crates") + CheckObjectives() + InitAi() + + Trigger.OnEnteredFootprint(WelcomeTeamCellTrigger, function(a, id) + if a.Owner == Nod then + Utils.Do(WelcomeTeam, function(a) + if not a.IsDead then + a.AttackMove(PlayerStart.Location) + IdleHunt(a) + end + end) + Trigger.RemoveFootprintTrigger(id) + end + end) + + Trigger.OnAllKilled(Civilians, function() + local cargo = Reinforcements.ReinforceWithTransport(GDI, "tran", ReinforcementsEngineers, { CPos.New(0,32), waypoint10.Location }, { CPos.New(0,32) })[2] + Utils.Do(cargo, function(engs) + if engs.Type == "e6" then + Trigger.OnIdle(engs, CaptureStructures) + end + end) + end) + + Utils.Do(CiviliansBuildings, function(b) + Trigger.OnKilled(b, CheckVillageDestruction) + end) + + Utils.Do(OilDerricks, function(actor) + Trigger.OnKilledOrCaptured(actor, OilDerricksAirstrike) + end) + + Utils.Do(RunForHelpCivs, function(actor) + Trigger.OnDiscovered(actor, RunForHelp) + end) + + Trigger.OnEnteredFootprint(GDIBaseEntranceCells, function(a, id) + if a.Owner == Nod and not BombTriggered then + BombTriggered = true + Trigger.AfterDelay(25, SendGDIAirstrike) + Trigger.AfterDelay(150, SendGDIAirstrike) + Trigger.AfterDelay(275, SendGDIAirstrike) + Trigger.RemoveFootprintTrigger(id) + end + end) + + Trigger.OnCapture(GDIhq, function() + GDIhq.GrantCondition("captured") + end) + + Trigger.OnEnteredFootprint(InnerGDIBaseEntranceCells, function(a, id) + if a.Owner == Nod and not InnerBaseEntered then + InnerBaseEntered = true + Reinforcements.Reinforce(GDI, ReinforcementsMammoths, { CPos.New(2,9), CPos.New(3,9) }, 40, IdleHunt) + Trigger.RemoveFootprintTrigger(id) + end + end) + + Trigger.AfterDelay(5, function() + StartPatrol() + GuardBase() + StartGuard() + end) + + Trigger.OnAllKilled(CivsMoneyBuildings, function() + Trigger.AfterDelay(1, function() + Trigger.OnAllRemovedFromWorld(Utils.Where(Map.ActorsInWorld, function(a) return a.Type == "moneycrate" or a.Type == "smallmcrate" end), function() + Nod.MarkCompletedObjective(FindAllCivMoney) + end) + end) + end) + + Trigger.OnKilledOrCaptured(GDIAdvComCenter, function() + if IonCannonOnline then + IonCannonOnline = false + else + DestroyIonCannon = AddSecondaryObjective(Nod, "quickly-destroy-ion-cannon") + end + Nod.MarkCompletedObjective(DestroyIonCannon) + PrepareOrcas() + end) +end diff --git a/mods/cnc/maps/eviction-notice/map.bin b/mods/cnc/maps/eviction-notice/map.bin new file mode 100644 index 0000000000000000000000000000000000000000..c9a6ba761928087a14a5facbfdcc10b50217e8e0 GIT binary patch literal 20497 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 literal 0 HcmV?d00001 diff --git a/mods/cnc/maps/eviction-notice/map.png b/mods/cnc/maps/eviction-notice/map.png new file mode 100644 index 0000000000000000000000000000000000000000..d8fb00c42959cf09b1115ba4eaf7276fd237e809 GIT binary patch literal 9184 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