diff --git a/OpenRA.sln b/OpenRA.sln index 695125147e..e8b24077ef 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -48,6 +48,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tiberian Dawn Lua scripts", mods\cnc\maps\nod07a\nod07a.lua = mods\cnc\maps\nod07a\nod07a.lua mods\cnc\maps\nod07b\nod07b-AI.lua = mods\cnc\maps\nod07b\nod07b-AI.lua mods\cnc\maps\nod07b\nod07b.lua = mods\cnc\maps\nod07b\nod07b.lua + mods\cnc\maps\nod08a\nod08a-AI.lua = mods\cnc\maps\nod08a\nod08a-AI.lua + mods\cnc\maps\nod08a\nod08a.lua = mods\cnc\maps\nod08a\nod08a.lua mods\cnc\maps\funpark01\scj01ea.lua = mods\cnc\maps\funpark01\scj01ea.lua mods\cnc\maps\shellmap\shellmap.lua = mods\cnc\maps\shellmap\shellmap.lua EndProjectSection diff --git a/mods/cnc/maps/nod08a/map.bin b/mods/cnc/maps/nod08a/map.bin new file mode 100644 index 0000000000..9e8a7cfc2b Binary files /dev/null and b/mods/cnc/maps/nod08a/map.bin differ diff --git a/mods/cnc/maps/nod08a/map.png b/mods/cnc/maps/nod08a/map.png new file mode 100644 index 0000000000..79a18411c1 Binary files /dev/null and b/mods/cnc/maps/nod08a/map.png differ diff --git a/mods/cnc/maps/nod08a/map.yaml b/mods/cnc/maps/nod08a/map.yaml new file mode 100644 index 0000000000..0a7cd37e0b --- /dev/null +++ b/mods/cnc/maps/nod08a/map.yaml @@ -0,0 +1,611 @@ +MapFormat: 11 + +RequiresMod: cnc + +Title: Battle for Zaire (a) + +Author: Westwood Studios + +Tileset: DESERT + +MapSize: 64,64 + +Bounds: 8,12,55,49 + +Visibility: MissionSelector + +Categories: Campaign + +LockPreview: True + +Players: + PlayerReference@GDI: + Name: GDI + Faction: gdi + Color: F5D378 + Allies: Outpost + Enemies: Nod + PlayerReference@Neutral: + Name: Neutral + OwnsWorld: True + NonCombatant: True + Faction: gdi + PlayerReference@Civilians: + Name: Civilians + NonCombatant: True + Faction: gdi + Enemies: Nod + PlayerReference@Outpost: + Name: Outpost + Faction: gdi + Color: F5D378 + Allies: GDI + Enemies: Nod + PlayerReference@Nod: + Name: Nod + AllowBots: False + Playable: True + Required: True + LockFaction: True + Faction: nod + LockColor: True + Color: FE1100 + LockSpawn: True + LockTeam: True + Enemies: GDI, Civilians + PlayerReference@NodBase: + Name: NodBase + NonCombatant: True + Faction: nod + Color: FE1100 + Enemies: Nod + +Actors: + Actor0: brik + Location: 62,22 + Owner: GDI + Actor1: brik + Location: 61,22 + Owner: GDI + Actor2: brik + Location: 60,22 + Owner: GDI + Actor3: brik + Location: 59,22 + Owner: GDI + Actor4: brik + Location: 58,22 + Owner: GDI + Actor5: brik + Location: 57,22 + Owner: GDI + Actor6: brik + Location: 56,22 + Owner: GDI + Actor7: brik + Location: 55,22 + Owner: GDI + Actor8: brik + Location: 54,22 + Owner: GDI + Actor9: brik + Location: 53,22 + Owner: GDI + Actor10: brik + Location: 52,22 + Owner: GDI + Actor11: brik + Location: 47,22 + Owner: GDI + Actor12: brik + Location: 46,22 + Owner: GDI + Actor13: brik + Location: 62,21 + Owner: GDI + Actor14: brik + Location: 53,21 + Owner: GDI + Actor15: brik + Location: 52,21 + Owner: GDI + Actor16: brik + Location: 47,21 + Owner: GDI + Actor17: brik + Location: 46,21 + Owner: GDI + Actor18: brik + Location: 62,20 + Owner: GDI + Actor19: brik + Location: 46,20 + Owner: GDI + Actor20: brik + Location: 62,19 + Owner: GDI + Actor21: brik + Location: 46,19 + Owner: GDI + Actor22: brik + Location: 62,18 + Owner: GDI + Actor23: brik + Location: 47,18 + Owner: GDI + Actor24: brik + Location: 46,18 + Owner: GDI + Actor25: brik + Location: 62,17 + Owner: GDI + Actor26: brik + Location: 47,17 + Owner: GDI + Actor27: brik + Location: 46,17 + Owner: GDI + Actor28: brik + Location: 62,16 + Owner: GDI + Actor29: brik + Location: 62,15 + Owner: GDI + Actor30: brik + Location: 62,14 + Owner: GDI + Actor31: brik + Location: 62,13 + Owner: GDI + Actor32: brik + Location: 47,13 + Owner: GDI + Actor33: brik + Location: 46,13 + Owner: GDI + Actor34: cycl + Location: 40,13 + Owner: GDI + Actor35: cycl + Location: 39,13 + Owner: GDI + Actor36: brik + Location: 62,12 + Owner: GDI + Actor37: brik + Location: 61,12 + Owner: GDI + Actor38: brik + Location: 60,12 + Owner: GDI + Actor39: brik + Location: 59,12 + Owner: GDI + Actor40: brik + Location: 58,12 + Owner: GDI + Actor41: brik + Location: 57,12 + Owner: GDI + Actor42: brik + Location: 56,12 + Owner: GDI + Actor43: brik + Location: 55,12 + Owner: GDI + Actor44: brik + Location: 54,12 + Owner: GDI + Actor45: brik + Location: 53,12 + Owner: GDI + Actor46: brik + Location: 52,12 + Owner: GDI + Actor47: brik + Location: 51,12 + Owner: GDI + Actor48: brik + Location: 50,12 + Owner: GDI + Actor49: brik + Location: 49,12 + Owner: GDI + Actor50: brik + Location: 48,12 + Owner: GDI + Actor51: brik + Location: 47,12 + Owner: GDI + Actor52: brik + Location: 46,12 + Owner: GDI + Actor53: cycl + Location: 40,12 + Owner: GDI + Actor54: rock1 + Location: 19,17 + Owner: Neutral + Actor55: t08 + Location: 8,29 + Owner: Neutral + Actor56: t08 + Location: 35,50 + Owner: Neutral + Actor57: t08 + Location: 10,26 + Owner: Neutral + Actor58: t08 + Location: 17,48 + Owner: Neutral + Actor59: t08 + Location: 34,25 + Owner: Neutral + Actor60: t18 + Location: 51,54 + Owner: Neutral + Actor61: t18 + Location: 45,56 + Owner: Neutral + Actor62: t08 + Location: 23,36 + Owner: Neutral + Actor63: t08 + Location: 21,24 + Owner: Neutral + Actor64: t08 + Location: 62,25 + Owner: Neutral + Actor88: mtnk + Location: 45,20 + Owner: GDI + Facing: 160 + Actor89: mtnk + Location: 50,58 + Owner: GDI + Facing: 192 + Actor90: mtnk + Location: 57,28 + Owner: GDI + Facing: 160 + Actor91: e2 + Location: 27,60 + Owner: GDI + SubCell: 2 + Actor92: e3 + Location: 57,21 + Owner: GDI + SubCell: 3 + Actor93: e3 + Location: 56,21 + Owner: GDI + SubCell: 2 + Actor94: e3 + Location: 57,21 + Owner: GDI + SubCell: 1 + Actor95: e3 + Location: 56,21 + Owner: GDI + SubCell: 4 + Actor96: e2 + Location: 55,19 + Owner: GDI + SubCell: 3 + Actor97: e2 + Location: 55,20 + Owner: GDI + SubCell: 1 + Actor98: e2 + Location: 54,19 + Owner: GDI + SubCell: 3 + Actor99: e2 + Location: 54,19 + Owner: GDI + SubCell: 4 + Actor100: e2 + Location: 54,20 + Owner: GDI + SubCell: 2 + Actor101: e2 + Location: 54,20 + Owner: GDI + SubCell: 1 + Actor102: e1 + Location: 55,21 + Owner: GDI + SubCell: 1 + Actor103: e1 + Location: 54,21 + Owner: GDI + SubCell: 1 + Actor104: e1 + Location: 55,21 + Owner: GDI + SubCell: 3 + Actor105: e1 + Location: 54,21 + Owner: GDI + SubCell: 2 + Actor106: e1 + Location: 54,21 + Owner: GDI + SubCell: 4 + Actor107: e1 + Location: 54,21 + Owner: GDI + SubCell: 3 + Actor108: e3 + Location: 13,56 + Owner: GDI + SubCell: 0 + Actor109: e3 + Location: 13,54 + Owner: GDI + SubCell: 3 + Actor110: e2 + Location: 23,49 + Owner: GDI + SubCell: 0 + Actor111: e2 + Location: 21,49 + Owner: GDI + SubCell: 3 + Actor112: e1 + Location: 29,56 + Owner: GDI + SubCell: 0 + Actor113: e1 + Location: 28,56 + Owner: GDI + SubCell: 0 + Actor114: e3 + Location: 56,21 + Owner: GDI + SubCell: 3 + Actor115: e3 + Location: 56,21 + Owner: GDI + SubCell: 1 + Actor116: e3 + Location: 55,23 + Owner: GDI + Facing: 160 + SubCell: 1 + Actor117: e1 + Location: 56,23 + Owner: GDI + Facing: 160 + SubCell: 1 + Actor118: e1 + Location: 54,28 + Owner: GDI + Facing: 160 + SubCell: 1 + Actor119: e2 + Location: 53,28 + Owner: GDI + Facing: 96 + SubCell: 0 + Actor120: e2 + Location: 56,24 + Owner: GDI + Facing: 160 + SubCell: 2 + Actor121: e1 + Location: 35,25 + Owner: GDI + Facing: 160 + SubCell: 0 + Actor122: e2 + Location: 36,50 + Owner: GDI + Facing: 160 + SubCell: 4 + Actor123: e1 + Location: 44,58 + Owner: GDI + Facing: 224 + SubCell: 4 + Actor124: e1 + Location: 47,55 + Owner: GDI + Facing: 64 + SubCell: 0 + Actor135: e1 + Location: 46,58 + Owner: GDI + Facing: 32 + SubCell: 2 + Actor136: e2 + Location: 47,59 + Owner: GDI + Facing: 224 + SubCell: 0 + Actor137: e2 + Location: 43,57 + Owner: GDI + Facing: 160 + SubCell: 1 + waypoint27: waypoint + Location: 33,53 + Owner: Neutral + waypoint26: waypoint + Location: 33,53 + Owner: Neutral + waypoint25: waypoint + Location: 23,55 + Owner: Neutral + waypoint15: waypoint + Location: 55,54 + Owner: Neutral + waypoint14: waypoint + Location: 13,27 + Owner: Neutral + waypoint13: waypoint + Location: 15,13 + Owner: Neutral + waypoint12: waypoint + Location: 31,15 + Owner: Neutral + waypoint11: waypoint + Location: 13,56 + Owner: Neutral + waypoint10: waypoint + Location: 23,44 + Owner: Neutral + waypoint9: waypoint + Location: 9,44 + Owner: Neutral + waypoint8: waypoint + Location: 9,33 + Owner: Neutral + Actor138: waypoint + Location: 22,26 + Owner: Neutral + Actor139: waypoint + Location: 35,30 + Owner: Neutral + Actor140: waypoint + Location: 32,52 + Owner: Neutral + Actor141: waypoint + Location: 46,49 + Owner: Neutral + Actor142: waypoint + Location: 47,39 + Owner: Neutral + Actor143: waypoint + Location: 51,33 + Owner: Neutral + Actor144: waypoint + Location: 49,26 + Owner: Neutral + Actor145: waypoint + Location: 49,22 + Owner: Neutral + Actor156: split3 + Owner: Neutral + Location: 40,22 + Actor157: split3 + Owner: Neutral + Location: 57,42 + AirstrikeTarget: waypoint + Owner: Neutral + Location: 60,57 + AttackPath1: waypoint + Owner: Neutral + Location: 27,36 + AttackPath2: waypoint + Owner: Neutral + Location: 12,31 + AttackPath3: waypoint + Owner: Neutral + Location: 49,46 + GDIBuilding1: hpad + Location: 41,14 + Owner: GDI + GDIBuilding2: hpad + Location: 39,14 + Owner: GDI + GDIBuilding3: silo + Location: 57,14 + Owner: GDI + GDIBuilding4: silo + Location: 55,13 + Owner: GDI + GDIBuilding5: gtwr + Location: 52,23 + Owner: GDI + GDIBuilding6: gtwr + Location: 47,23 + Owner: GDI + GDIBuilding7: silo + Location: 55,17 + Owner: GDI + GDIBuilding8: silo + Location: 55,15 + Owner: GDI + GDIBuilding9: atwr + Owner: GDI + Location: 47,19 + TurretFacing: 92 + GDICYard: fact + Location: 59,13 + Owner: GDI + GDIHarvester: harv + Owner: GDI + Location: 50,18 + Facing: 92 + GDIHQ: hq + Location: 51,13 + Owner: GDI + GDINuke1: nuke + Location: 60,17 + Owner: GDI + GDINuke2: nuke + Location: 59,19 + Owner: GDI + GDINuke3: nuke + Location: 57,18 + Owner: GDI + GDINuke4: nuke + Location: 58,16 + Owner: GDI + GDIOrca1: orca + Owner: GDI + Location: 41,12 + Facing: 92 + GDIOrca2: orca + Owner: GDI + Location: 41,13 + Facing: 92 + GDIProc: proc + Owner: GDI + Location: 50,16 + FreeActor: False + GDIPyle: pyle + Location: 53,14 + Owner: GDI + GDIWeap: weap + Location: 48,12 + Owner: GDI + MoneyCrate: MoneyCrate + Owner: Neutral + Location: 39,12 + NodCYard: fact.in + Location: 60,53 + Owner: NodBase + Health: 37 + NodHand: hand + Location: 59,55 + Owner: NodBase + Health: 45 + NodNuke: nuke + Location: 61,56 + Owner: NodBase + Health: 47 + OutpostCYard: factout.in + Location: 17,54 + Owner: Outpost + OutpostHarvester: harv + Location: 10,58 + Owner: Outpost + OutpostNuke: nukeout.in + Location: 20,52 + Owner: Outpost + OutpostProc: procout.in + Location: 20,55 + Owner: Outpost + FreeActor: False + ReinforcementsHelicopterRally: waypoint + Owner: Neutral + Location: 37,58 + ReinforcementsHelicopterSpawn: waypoint + Owner: Neutral + Location: 25,60 + +Rules: cnc|rules/campaign-maprules.yaml, cnc|rules/campaign-tooltips.yaml, cnc|rules/campaign-palettes.yaml, rules.yaml + +Weapons: weapons.yaml diff --git a/mods/cnc/maps/nod08a/nod08a-AI.lua b/mods/cnc/maps/nod08a/nod08a-AI.lua new file mode 100644 index 0000000000..05be4c7e3e --- /dev/null +++ b/mods/cnc/maps/nod08a/nod08a-AI.lua @@ -0,0 +1,220 @@ +AttackPaths = { { AttackPath1 }, { AttackPath2 }, { AttackPath3 } } +GDIBase = { GDICYard, GDIPyle, GDIWeap, GDIHQ, GDIProc, GDINuke1, GDINuke2, GDINuke3, GDIBuilding1, GDIBuilding2, GDIBuilding3, GDIBuilding4, GDIBuilding5, GDIBuilding6, GDIBuilding7, GDIBuilding8, GDIBuilding9 } +GDIOrcas = { GDIOrca1, GDIOrca2 } +InfantryAttackGroup = { } +InfantryGroupSize = 4 +InfantryProductionCooldown = DateTime.Minutes(3) +InfantryProductionTypes = { "e1", "e1", "e2" } +HarvesterProductionType = { "harv" } +VehicleAttackGroup = { } +VehicleGroupSize = 4 +VehicleProductionCooldown = DateTime.Minutes(4) +VehicleProductionTypes = { "jeep", "jeep", "mtnk", "mtnk", "mtnk" } +StartingCash = 4000 + +BaseProc = { type = "proc", pos = CPos.New(50, 16), cost = 1500, exists = true } +BaseNuke1 = { type = "nuke", pos = CPos.New(60, 17), cost = 500, exists = true } +BaseNuke2 = { type = "nuke", pos = CPos.New(59, 19), cost = 500, exists = true } +BaseNuke3 = { type = "nuke", pos = CPos.New(57, 18), cost = 500, exists = true } +BaseNuke4 = { type = "nuke", pos = CPos.New(58, 16), cost = 500, exists = true } +InfantryProduction = { type = "pyle", pos = CPos.New(53, 14), cost = 500, exists = true } +VehicleProduction = { type = "weap", pos = CPos.New(48, 12), cost = 2000, exists = true } + +BaseBuildings = { BaseProc, BaseNuke1, BaseNuke2, BaseNuke3, BaseNuke4, InfantryProduction, VehicleProduction } + +AutoGuard = function(guards) + Utils.Do(guards, function(guard) + Trigger.OnDamaged(guard, function(guard) + IdleHunt(guard) + end) + end) +end + +BuildBase = function(cyard) + Utils.Do(BaseBuildings, function(building) + if not building.exists and not cyardIsBuilding then + BuildBuilding(building, cyard) + return + end + end) + Trigger.AfterDelay(DateTime.Seconds(10), function() BuildBase(cyard) end) +end + +BuildBuilding = function(building, cyard) + cyardIsBuilding = true + + Trigger.AfterDelay(Actor.BuildTime(building.type), function() + cyardIsBuilding = false + + if cyard.IsDead or cyard.Owner ~= enemy then + return + end + + local actor = Actor.Create(building.type, true, { Owner = enemy, Location = building.pos }) + enemy.Cash = enemy.Cash - building.cost + + building.exists = true + + if actor.Type == 'pyle' or actor.Type == 'hand' then + Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(actor) end) + elseif actor.Type == 'weap' or actor.Type == 'afld' then + Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceVehicle(actor) end) + end + + Trigger.OnKilled(actor, function() building.exists = false end) + + Trigger.OnDamaged(actor, function(building) + if building.Owner == enemy and building.Health < building.MaxHealth * 3/4 then + building.StartBuildingRepairs() + end + end) + + Trigger.AfterDelay(DateTime.Seconds(10), function() BuildBase(cyard) end) + end) +end + +CheckForHarvester = function() + local harv = enemy.GetActorsByType("harv") + return #harv > 0 +end + +GuardBase = function() + Utils.Do(GDIBase, function(building) + Trigger.OnDamaged(building, function(guard) + Utils.Do(GDIOrcas, function(guard) + if not guard.IsDead and not building.IsDead then + guard.Guard(building) + end + end) + end) + end) +end + +IdleHunt = function(unit) + if not unit.IsDead then + Trigger.OnIdle(unit, unit.Hunt) + end +end + +IdlingUnits = function(enemy) + local lazyUnits = enemy.GetGroundAttackers() + + Utils.Do(lazyUnits, function(unit) + IdleHunt(unit) + end) +end + +ProduceHarvester = function(building) + if not buildingHarvester then + buildingHarvester = true + building.Build(HarvesterProductionType, function() + buildingHarvester = false + end) + end +end + +ProduceInfantry = function(building) + if building.IsDead then + return + elseif not CheckForHarvester() then + Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end) + end + + local delay = Utils.RandomInteger(DateTime.Seconds(3), DateTime.Seconds(9)) + local toBuild = { Utils.Random(InfantryProductionTypes) } + local Path = Utils.Random(AttackPaths) + building.Build(toBuild, function(unit) + InfantryAttackGroup[#InfantryAttackGroup + 1] = unit[1] + + if #InfantryAttackGroup >= InfantryGroupSize then + SendUnits(InfantryAttackGroup, Path) + InfantryAttackGroup = { } + Trigger.AfterDelay(InfantryProductionCooldown, function() ProduceInfantry(building) end) + else + Trigger.AfterDelay(delay, function() ProduceInfantry(building) end) + end + end) + +end + +ProduceVehicle = function(building) + if building.IsDead then + return + elseif not CheckForHarvester() then + ProduceHarvester(building) + Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceVehicle(building) end) + end + + local delay = Utils.RandomInteger(DateTime.Seconds(12), DateTime.Seconds(17)) + local toBuild = { Utils.Random(VehicleProductionTypes) } + local Path = Utils.Random(AttackPaths) + building.Build(toBuild, function(unit) + VehicleAttackGroup[#VehicleAttackGroup + 1] = unit[1] + + if #VehicleAttackGroup >= VehicleGroupSize then + SendUnits(VehicleAttackGroup, Path) + VehicleAttackGroup = { } + Trigger.AfterDelay(VehicleProductionCooldown, function() ProduceVehicle(building) end) + else + Trigger.AfterDelay(delay, function() ProduceVehicle(building) end) + 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) +end + +StartAI = function(cyard) + Utils.Do(Map.NamedActors, function(actor) + if actor.Owner == enemy and actor.HasProperty("StartBuildingRepairs") then + Trigger.OnDamaged(actor, function(building) + if building.Owner == enemy and building.Health < 3/4 * building.MaxHealth then + building.StartBuildingRepairs() + end + end) + end + end) + enemy.Cash = StartingCash + BuildBase(cyard) + GuardBase() +end + +Trigger.OnAllKilledOrCaptured(GDIBase, function() + IdlingUnits(enemy) +end) + +Trigger.OnKilled(GDIProc, function(building) + BaseProc.exists = false +end) + +Trigger.OnKilled(GDINuke1, function(building) + BaseNuke1.exists = false +end) + +Trigger.OnKilled(GDINuke2, function(building) + BaseNuke2.exists = false +end) + +Trigger.OnKilled(GDINuke3, function(building) + BaseNuke3.exists = false +end) + +Trigger.OnKilled(GDINuke4, function(building) + BaseNuke4.exists = false +end) + +Trigger.OnKilled(GDIPyle, function(building) + InfantryProduction.exists = false +end) + +Trigger.OnKilled(GDIWeap, function(building) + VehicleProduction.exists = false +end) diff --git a/mods/cnc/maps/nod08a/nod08a.lua b/mods/cnc/maps/nod08a/nod08a.lua new file mode 100644 index 0000000000..569e80a0b9 --- /dev/null +++ b/mods/cnc/maps/nod08a/nod08a.lua @@ -0,0 +1,225 @@ +WaypointGroup1 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint6, waypoint10 } +WaypointGroup2 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint6, waypoint7, waypoint8, waypoint9, waypoint10 } +WaypointGroup3 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint4, waypoint5 } +WaypointGroup4 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint4 } +WaypointGroup5 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint6, waypoint7, waypoint8, waypoint9, waypoint11 } + +GDI1 = { units = { ['e1'] = 2, ['e2'] = 2 }, waypoints = WaypointGroup3, delay = 80 } +GDI2 = { units = { ['e2'] = 3, ['e3'] = 2 }, waypoints = WaypointGroup1, delay = 10 } +GDI3 = { units = { ['e1'] = 2, ['e3'] = 3 }, waypoints = WaypointGroup1, delay = 30 } +GDI4 = { units = { ['jeep'] = 2 }, waypoints = WaypointGroup3, delay = 45 } +GDI5 = { units = { ['mtnk'] = 1 }, waypoints = WaypointGroup3, delay = 10 } +Auto1 = { units = { ['e1'] = 2, ['e2'] = 2, ['e3'] = 2 }, waypoints = WaypointGroup4, delay = 25 } +Auto2 = { units = { ['e2'] = 2, ['jeep'] = 1 }, waypoints = WaypointGroup3, delay = 40 } +Auto3 = { units = { ['mtnk'] = 1 }, waypoints = WaypointGroup1, delay = 30 } +Auto4 = { units = { ['e1'] = 2, ['mtnk'] = 1 }, waypoints = WaypointGroup2, delay = 30 } +Auto5 = { units = { ['e3'] = 2, ['jeep'] = 1 }, waypoints = WaypointGroup1, delay = 30 } + +AutoAttackWaves = { GDI1, GDI2, GDI3, GDI4, GDI5, Auto1, Auto2, Auto3, Auto4, Auto5 } + +NodBase = { NodCYard, NodNuke, NodHand } +Outpost = { OutpostCYard, OutpostProc } + +IntroReinforcements = { "e1", "e1", "e1", "e1", "e1", "e1", "e3", "e3", "e3", "e3" } + +IntroGuards = { Actor89, Actor137, Actor123, Actor124, Actor135, Actor136 } +OutpostGuards = { Actor91, Actor108, Actor109, Actor110, Actor111, Actor112, Actor113, Actor122 } + +NodBaseTrigger = { CPos.New(52, 52), CPos.New(52, 53), CPos.New(52, 54), CPos.New(52, 55), CPos.New(52, 56), CPos.New(52, 57), CPos.New(52, 58), CPos.New(52, 59), CPos.New(52, 60), CPos.New(55, 54) } + +AirstrikeDelay = DateTime.Minutes(2) + DateTime.Seconds(20) + +NodBaseCapture = function() + nodBaseTrigger = true + player.MarkCompletedObjective(NodObjective1) + Utils.Do(NodBase, function(actor) + actor.Owner = player + end) + Trigger.AfterDelay(DateTime.Seconds(1), function() + Media.PlaySpeechNotification(player, "NewOptions") + end) +end + +searches = 0 +getAirstrikeTarget = function() + local list = player.GetGroundAttackers() + + if #list == 0 then + return + end + + local target = list[DateTime.GameTime % #list + 1].CenterPosition + + if searches < 6 then + searches = searches + 1 + return getAirstrikeTarget() + else + searches = 0 + return target + end +end + +--Provide the player with a helicopter until the outpost got captured +SendHelicopter = function() + Trigger.AfterDelay(DateTime.Seconds(5), function() + if not outpostCaptured then + Media.PlaySpeechNotification(player, "Reinforce") + TransportHelicopter = Reinforcements.ReinforceWithTransport(player, 'tran', nil, { ReinforcementsHelicopterSpawn.Location, waypoint15.Location })[1] + Trigger.OnKilled(TransportHelicopter, function() + SendHelicopter() + end) + end + end) +end + +SendAttackWave = function(team) + for type, amount in pairs(team.units) do + count = 0 + actors = enemy.GetActorsByType(type) + Utils.Do(actors, function(actor) + if actor.IsIdle and count < amount then + SetAttackWaypoints(actor, team.waypoints) + IdleHunt(actor) + count = count + 1 + end + end) + end +end + +SetAttackWaypoints = function(actor, waypoints) + if not actor.IsDead then + Utils.Do(waypoints, function(waypoint) + actor.AttackMove(waypoint.Location) + end) + end +end + +SendGDIAirstrike = function(hq, delay) + if not hq.IsDead and hq.Owner == enemy then + local target = getAirstrikeTarget() + + if target then + hq.SendAirstrike(target, false, Facing.NorthEast + 4) + Trigger.AfterDelay(delay, function() SendGDIAirstrike(hq, delay) end) + else + Trigger.AfterDelay(delay/4, function() SendGDIAirstrike(hq, delay) end) + end + end +end + +SendWaves = function(counter, Waves) + if counter <= #Waves then + local team = Waves[counter] + SendAttackWave(team) + Trigger.AfterDelay(DateTime.Seconds(team.delay), function() SendWaves(counter + 1, Waves) end) + end +end + +StartWaves = function() + SendWaves(1, AutoAttackWaves) +end + +Trigger.OnAllKilled(IntroGuards, function() + FlareCamera = Actor.Create("camera", true, { Owner = player, Location = waypoint25.Location }) + Flare = Actor.Create("flare", true, { Owner = player, Location = waypoint25.Location }) + SendHelicopter() + player.MarkCompletedObjective(NodObjective1) + NodBaseCapture() +end) + +Trigger.OnAllKilledOrCaptured(Outpost, function() + if not outpostCaptured then + outpostCaptured = true + + Trigger.AfterDelay(DateTime.Minutes(1), function() + + if not GDIHQ.IsDead and (not NodHand.IsDead or not NodNuke.IsDead) then + local airstrikeproxy = Actor.Create("airstrike.proxy", false, { Owner = enemy }) + airstrikeproxy.SendAirstrike(AirstrikeTarget.CenterPosition, false, Facing.NorthEast + 4) + airstrikeproxy.Destroy() + end + end) + + Trigger.AfterDelay(DateTime.Seconds(15), function() + Utils.Do(OutpostGuards, function(unit) + IdleHunt(unit) + end) + end) + + Trigger.AfterDelay(DateTime.Minutes(1), function() + FlareCamera.Kill() + Flare.Destroy() + end) + + player.MarkCompletedObjective(NodObjective2) + Trigger.AfterDelay(DateTime.Minutes(1), function() StartWaves() end) + Trigger.AfterDelay(AirstrikeDelay, function() SendGDIAirstrike(GDIHQ, AirstrikeDelay) end) + Trigger.AfterDelay(DateTime.Minutes(2), function() ProduceInfantry(GDIPyle) end) + Trigger.AfterDelay(DateTime.Minutes(3), function() ProduceVehicle(GDIWeap) end) + end +end) + +Trigger.OnCapture(OutpostCYard, function() + Trigger.AfterDelay(DateTime.Seconds(4), function() + Media.PlaySpeechNotification(player, "NewOptions") + end) +end) + +Trigger.OnAnyKilled(Outpost, function() + if not outpostCaptured then + player.MarkFailedObjective(NodObjective2) + end +end) + +Trigger.OnEnteredFootprint(NodBaseTrigger, function(a, id) + if not nodBaseTrigger and a.Owner == player then + NodBaseCapture() + end +end) + +WorldLoaded = function() + player = Player.GetPlayer("Nod") + enemy = Player.GetPlayer("GDI") + Camera.Position = waypoint26.CenterPosition + Media.PlaySpeechNotification(player, "Reinforce") + Reinforcements.ReinforceWithTransport(player, 'tran.in', IntroReinforcements, { ReinforcementsHelicopterSpawn.Location, ReinforcementsHelicopterRally.Location }, { ReinforcementsHelicopterSpawn.Location }, nil, nil) + + StartAI(GDICYard) + AutoGuard(IntroGuards) + AutoGuard(OutpostGuards) + + Trigger.OnObjectiveAdded(player, function(p, id) + Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective") + end) + + 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.OnPlayerWon(player, function() + Media.PlaySpeechNotification(player, "Win") + end) + + Trigger.OnPlayerLost(player, function() + Media.PlaySpeechNotification(player, "Lose") + end) + + NodObjective1 = player.AddPrimaryObjective("Locate the Nod base.") + NodObjective2 = player.AddPrimaryObjective("Capture the GDI outpost.") + NodObjective3 = player.AddPrimaryObjective("Eliminate all GDI forces in the area.") + GDIObjective = enemy.AddPrimaryObjective("Eliminate all Nod forces in the area.") +end + +Tick = function() + if DateTime.GameTime > 2 and player.HasNoRequiredUnits() then + enemy.MarkCompletedObjective(GDIObjective) + end + + if DateTime.GameTime > 2 and enemy.HasNoRequiredUnits() then + player.MarkCompletedObjective(NodObjective3) + end +end diff --git a/mods/cnc/maps/nod08a/rules.yaml b/mods/cnc/maps/nod08a/rules.yaml new file mode 100644 index 0000000000..ae9b90bc54 --- /dev/null +++ b/mods/cnc/maps/nod08a/rules.yaml @@ -0,0 +1,214 @@ +World: + LuaScript: + Scripts: nod08a.lua, nod08a-AI.lua + MusicPlaylist: + StartingMusic: linefire + VictoryMusic: nod_win1 + MissionData: + Briefing: Since we are low on troops, you will have to make use of all available resources.\n\n Locate the abandoned GDI base in the area and restore it to operational status. Once that is done, use GDI's own weapons against them.\n\nBe sure that no GDI forces remain alive. + BackgroundVideo: tiberfx.vqa + BriefingVideo: nod8.vqa + LossVideo: nodlose.vqa + SmudgeLayer@SCORCH: + InitialSmudges: + sc1 40,59 0 + sc3 39,59 0 + sc5 55,53 0 + sc2 23,53 0 + sc2 23,52 0 + sc3 59,51 0 + sc5 58,51 0 + sc2 23,51 0 + sc5 23,50 0 + sc3 23,49 0 + SmudgeLayer@CRATER: + InitialSmudges: + cr1 47,58 0 + cr1 56,54 0 + cr1 55,54 0 + +Player: + PlayerResources: + DefaultCash: 0 + +CYCL: + Buildable: + Prerequisites: ~disabled + +NUK2: + Buildable: + Prerequisites: ~disabled + +HPAD: + Buildable: + Prerequisites: ~disabled + +BRIK: + Buildable: + Prerequisites: ~disabled + +EYE: + Buildable: + Prerequisites: ~disabled + +GUN: + Buildable: + Prerequisites: ~disabled + +OBLI: + Buildable: + Prerequisites: ~disabled + +TMPL: + Buildable: + Prerequisites: ~disabled + +E2: + Buildable: + Prerequisites: ~pyle + +E4: + Buildable: + Prerequisites: ~hand + +E5: + Buildable: + Prerequisites: ~disabled + +E6: + -RepairsBridges: + +HARV: + Harvester: + SearchFromOrderRadius: 30 + +MTNK: + Buildable: + Prerequisites: ~weap + +HTNK: + Buildable: + Prerequisites: ~disabled + +HQ: + AirstrikePower: + Prerequisites: gdi + SquadSize: 1 + +RMBO: + Buildable: + Prerequisites: ~disabled + +MCV: + Buildable: + Prerequisites: ~disabled + +FTNK: + Buildable: + Prerequisites: ~disabled + +MLRS: + Buildable: + Prerequisites: ~disabled + +MSAM: + Buildable: + Prerequisites: ~disabled + +ATWR: + Buildable: + Prerequisites: ~disabled + +HELI: + Buildable: + Prerequisites: ~disabled + +SBAG: + Buildable: + Queue: Defence.GDI, Defence.Nod + +GTWR: + Buildable: + Queue: Defence.GDI + +A10.IN: + Inherits: A10 + RenderSprites: + Image: A10 + Armament@BOMBS: + Weapon: Napalm.in + +airstrike.proxy: + AlwaysVisible: + AirstrikePower: + Icon: airstrike + StartFullyCharged: True + ChargeTime: 120 + SquadSize: 3 + QuantizedFacings: 8 + Description: Air Strike + LongDesc: Deploy an aerial napalm strike.\nBurns buildings and infantry along a line. + EndChargeSound: airredy1.aud + SelectTargetSound: select1.aud + InsufficientPowerSound: nopower1.aud + IncomingSound: enemya.aud + UnitType: a10.in + DisplayBeacon: True + BeaconPoster: airstrike + DisplayRadarPing: True + CameraActor: camera + +FACT.IN: + Inherits: FACT + RenderSprites: + Image: FACT + ProvidesPrerequisite: + Prerequisite: fact + CustomSellValue: + Value: 13515 + +FACTOUT.IN: + Inherits: FACT + RenderSprites: + Image: fact + ProvidesPrerequisite: + Prerequisite: fact + Capturable: + CaptureThreshold: 100 + +MoneyCrate: + Inherits: ^Crate + GiveCashCrateAction: + Amount: 2000 + UseCashTick: yes + +NUKEOUT.IN: + Inherits: NUKE + RenderSprites: + Image: nuke + Buildable: + Prerequisites: ~disabled + ProvidesPrerequisite: + Prerequisite: anypower + Capturable: + CaptureThreshold: 100 + +PROCOUT.IN: + Inherits: PROC + RenderSprites: + Image: proc + Buildable: + Prerequisites: ~disabled + ProvidesPrerequisite: + Prerequisite: proc + Capturable: + CaptureThreshold: 100 + +TRAN.IN: + Inherits: TRAN + RejectsOrders: + -Selectable: + RenderSprites: + Image: TRAN + Buildable: + Prerequisites: ~disabled diff --git a/mods/cnc/maps/nod08a/weapons.yaml b/mods/cnc/maps/nod08a/weapons.yaml new file mode 100644 index 0000000000..5850590565 --- /dev/null +++ b/mods/cnc/maps/nod08a/weapons.yaml @@ -0,0 +1,7 @@ +Napalm.IN: + Inherits: Napalm + Projectile: GravityBomb + Image: BOMBLET + Warhead@1Dam: SpreadDamage + Spread: 1000 + Damage: 1500 diff --git a/mods/cnc/missions.yaml b/mods/cnc/missions.yaml index a285280e22..b987e860e1 100644 --- a/mods/cnc/missions.yaml +++ b/mods/cnc/missions.yaml @@ -23,6 +23,7 @@ Nod Campaign: ./mods/cnc/maps/nod06c ./mods/cnc/maps/nod07a ./mods/cnc/maps/nod07b + ./mods/cnc/maps/nod08a Funpark Campaign: ./mods/cnc/maps/funpark01