diff --git a/OpenRA.sln b/OpenRA.sln index e8b24077ef..5da7941277 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -50,6 +50,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tiberian Dawn Lua scripts", 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\nod08b\nod08b-AI.lua = mods\cnc\maps\nod08b\nod08b-AI.lua + mods\cnc\maps\nod08b\nod08b.lua = mods\cnc\maps\nod08b\nod08b.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/nod08b/map.bin b/mods/cnc/maps/nod08b/map.bin new file mode 100644 index 0000000000..a86056a0fd Binary files /dev/null and b/mods/cnc/maps/nod08b/map.bin differ diff --git a/mods/cnc/maps/nod08b/map.png b/mods/cnc/maps/nod08b/map.png new file mode 100644 index 0000000000..7488a87ded Binary files /dev/null and b/mods/cnc/maps/nod08b/map.png differ diff --git a/mods/cnc/maps/nod08b/map.yaml b/mods/cnc/maps/nod08b/map.yaml new file mode 100644 index 0000000000..3aeab7d422 --- /dev/null +++ b/mods/cnc/maps/nod08b/map.yaml @@ -0,0 +1,782 @@ +MapFormat: 11 + +RequiresMod: cnc + +Title: Battle for Zaire (b) + +Author: Westwood Studios + +Tileset: DESERT + +MapSize: 64,64 + +Bounds: 3,2,59,60 + +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: 27,56 + Owner: GDI + Actor1: brik + Location: 26,56 + Owner: GDI + Actor2: brik + Location: 25,56 + Owner: GDI + Actor3: brik + Location: 24,56 + Owner: GDI + Actor4: brik + Location: 23,56 + Owner: GDI + Actor5: brik + Location: 22,56 + Owner: GDI + Actor6: brik + Location: 21,56 + Owner: GDI + Actor7: brik + Location: 20,56 + Owner: GDI + Actor8: brik + Location: 19,56 + Owner: GDI + Actor9: brik + Location: 18,56 + Owner: GDI + Actor10: brik + Location: 17,56 + Owner: GDI + Actor11: brik + Location: 16,56 + Owner: GDI + Actor12: brik + Location: 15,56 + Owner: GDI + Actor13: brik + Location: 14,56 + Owner: GDI + Actor14: brik + Location: 13,56 + Owner: GDI + Actor15: brik + Location: 12,56 + Owner: GDI + Actor16: brik + Location: 11,56 + Owner: GDI + Actor17: brik + Location: 10,56 + Owner: GDI + Actor18: brik + Location: 9,56 + Owner: GDI + Actor19: brik + Location: 8,56 + Owner: GDI + Actor20: brik + Location: 7,56 + Owner: GDI + Actor21: brik + Location: 6,56 + Owner: GDI + Actor22: brik + Location: 5,56 + Owner: GDI + Actor23: brik + Location: 4,56 + Owner: GDI + Actor24: brik + Location: 3,56 + Owner: GDI + Actor25: brik + Location: 27,55 + Owner: GDI + Actor26: cycl + Location: 25,55 + Owner: GDI + Actor27: cycl + Location: 24,55 + Owner: GDI + Actor28: cycl + Location: 23,55 + Owner: GDI + Actor29: brik + Location: 4,55 + Owner: GDI + Actor30: brik + Location: 3,55 + Owner: GDI + Actor31: brik + Location: 27,54 + Owner: GDI + Actor32: cycl + Location: 25,54 + Owner: GDI + Actor33: cycl + Location: 23,54 + Owner: GDI + Actor34: brik + Location: 3,54 + Owner: GDI + Actor35: brik + Location: 27,53 + Owner: GDI + Actor36: brik + Location: 26,53 + Owner: GDI + Actor37: cycl + Location: 25,53 + Owner: GDI + Actor38: cycl + Location: 24,53 + Owner: GDI + Actor39: cycl + Location: 23,53 + Owner: GDI + Actor40: brik + Location: 3,53 + Owner: GDI + Actor41: brik + Location: 27,52 + Owner: GDI + Actor42: brik + Location: 26,52 + Owner: GDI + Actor43: brik + Location: 3,52 + Owner: GDI + Actor44: brik + Location: 3,51 + Owner: GDI + Actor45: brik + Location: 27,50 + Owner: GDI + Actor46: brik + Location: 26,50 + Owner: GDI + Actor47: brik + Location: 3,50 + Owner: GDI + Actor48: brik + Location: 27,49 + Owner: GDI + Actor49: brik + Location: 26,49 + Owner: GDI + Actor50: brik + Location: 3,49 + Owner: GDI + Actor51: brik + Location: 27,48 + Owner: GDI + Actor52: brik + Location: 18,48 + Owner: GDI + Actor53: brik + Location: 17,48 + Owner: GDI + Actor54: brik + Location: 13,48 + Owner: GDI + Actor55: brik + Location: 12,48 + Owner: GDI + Actor56: brik + Location: 4,48 + Owner: GDI + Actor57: brik + Location: 3,48 + Owner: GDI + Actor58: brik + Location: 27,47 + Owner: GDI + Actor59: brik + Location: 26,47 + Owner: GDI + Actor60: brik + Location: 25,47 + Owner: GDI + Actor61: brik + Location: 24,47 + Owner: GDI + Actor62: brik + Location: 23,47 + Owner: GDI + Actor63: brik + Location: 22,47 + Owner: GDI + Actor64: brik + Location: 21,47 + Owner: GDI + Actor65: brik + Location: 20,47 + Owner: GDI + Actor66: brik + Location: 19,47 + Owner: GDI + Actor67: brik + Location: 18,47 + Owner: GDI + Actor68: brik + Location: 17,47 + Owner: GDI + Actor69: brik + Location: 13,47 + Owner: GDI + Actor70: brik + Location: 12,47 + Owner: GDI + Actor71: brik + Location: 11,47 + Owner: GDI + Actor72: brik + Location: 10,47 + Owner: GDI + Actor73: brik + Location: 9,47 + Owner: GDI + Actor74: brik + Location: 8,47 + Owner: GDI + Actor75: brik + Location: 7,47 + Owner: GDI + Actor76: brik + Location: 6,47 + Owner: GDI + Actor77: brik + Location: 5,47 + Owner: GDI + Actor78: brik + Location: 4,47 + Owner: GDI + Actor79: brik + Location: 3,47 + Owner: GDI + Actor80: sbag + Location: 61,29 + Owner: GDI + Actor81: sbag + Location: 61,28 + Owner: GDI + Actor82: sbag + Location: 61,27 + Owner: GDI + Actor83: sbag + Location: 61,26 + Owner: GDI + Actor84: sbag + Location: 61,25 + Owner: GDI + Actor85: sbag + Location: 61,24 + Owner: GDI + Actor86: sbag + Location: 59,23 + Owner: GDI + Actor87: sbag + Location: 58,23 + Owner: GDI + Actor88: t18 + Location: 15,16 + Owner: Neutral + Actor89: t18 + Location: 7,9 + Owner: Neutral + Actor90: t08 + Location: 22,31 + Owner: Neutral + Actor91: t08 + Location: 29,56 + Owner: Neutral + Actor92: t08 + Location: 45,52 + Owner: Neutral + Actor93: t08 + Location: 46,52 + Owner: Neutral + Actor94: t08 + Location: 57,39 + Owner: Neutral + Actor95: t08 + Location: 9,29 + Owner: Neutral + Actor96: t08 + Location: 40,16 + Owner: Neutral + Actor97: t08 + Location: 40,29 + Owner: Neutral + Actor98: t08 + Location: 10,29 + Owner: Neutral + Actor99: rock5 + Location: 22,18 + Owner: Neutral + Actor100: t08 + Location: 39,6 + Owner: Neutral + Actor101: t08 + Location: 40,6 + Owner: Neutral + Actor102: t08 + Location: 54,9 + Owner: Neutral + Actor103: t08 + Location: 59,15 + Owner: Neutral + Actor104: t08 + Location: 49,3 + Owner: Neutral + Actor105: t18 + Location: 13,23 + Owner: Neutral + Actor106: t18 + Location: 15,35 + Owner: Neutral + Actor107: t18 + Location: 26,45 + Owner: Neutral + Actor108: t18 + Location: 31,49 + Owner: Neutral + Actor109: t18 + Location: 54,35 + Owner: Neutral + Actor110: t18 + Location: 25,56 + Owner: Neutral + Actor111: t18 + Location: 11,43 + Owner: Neutral + Actor112: split3 + Location: 5,31 + Owner: Neutral + Actor113: split3 + Location: 58,47 + Owner: Neutral + Actor142: mtnk + Location: 8,9 + Owner: GDI + Facing: 64 + Actor143: mtnk + Location: 27,34 + Owner: GDI + Facing: 64 + Actor144: mtnk + Location: 14,25 + Owner: GDI + Facing: 96 + Actor145: mtnk + Location: 47,3 + Owner: GDI + Facing: 192 + Actor148: jeep + Location: 56,33 + Owner: GDI + Actor151: mtnk + Location: 16,32 + Owner: GDI + Facing: 224 + Actor152: mtnk + Location: 35,31 + Owner: GDI + Facing: 224 + Actor153: jeep + Location: 18,55 + Owner: GDI + Actor154: jeep + Location: 19,55 + Owner: GDI + Actor155: mtnk + Location: 21,55 + Owner: GDI + Actor156: mtnk + Location: 22,55 + Owner: GDI + Actor157: e2 + Location: 16,16 + Owner: GDI + SubCell: 3 + Actor158: e2 + Location: 15,16 + Owner: GDI + SubCell: 4 + Actor159: e2 + Location: 50,3 + Owner: GDI + Facing: 224 + SubCell: 1 + Actor160: e2 + Location: 49,5 + Owner: GDI + Facing: 192 + SubCell: 2 + Actor161: e1 + Location: 48,6 + Owner: GDI + Facing: 224 + SubCell: 4 + Actor162: e2 + Location: 41,16 + Owner: GDI + SubCell: 1 + Actor163: e2 + Location: 35,17 + Owner: GDI + Facing: 224 + SubCell: 2 + Actor171: e1 + Location: 45,5 + Owner: GDI + Facing: 160 + SubCell: 1 + Actor172: e1 + Location: 44,4 + Owner: GDI + Facing: 160 + SubCell: 1 + Actor173: e1 + Location: 45,4 + Owner: GDI + Facing: 224 + SubCell: 1 + Actor176: e1 + Location: 58,27 + Owner: GDI + Facing: 224 + SubCell: 4 + Actor177: e1 + Location: 52,22 + Owner: GDI + Facing: 160 + SubCell: 4 + Actor178: e1 + Location: 50,23 + Owner: GDI + Facing: 224 + SubCell: 3 + Actor179: e2 + Location: 59,30 + Owner: GDI + SubCell: 4 + Actor180: e2 + Location: 49,24 + Owner: GDI + Facing: 64 + SubCell: 3 + Actor181: e1 + Location: 31,33 + Owner: GDI + Facing: 32 + SubCell: 2 + Actor182: e3 + Location: 54,35 + Owner: GDI + SubCell: 4 + Actor183: e3 + Location: 55,35 + Owner: GDI + Facing: 32 + SubCell: 4 + Actor184: e3 + Location: 55,35 + Owner: GDI + Facing: 224 + SubCell: 0 + Actor185: e3 + Location: 47,32 + Owner: GDI + Facing: 64 + SubCell: 0 + Actor186: e3 + Location: 48,35 + Owner: GDI + Facing: 32 + SubCell: 1 + Actor187: e3 + Location: 43,26 + Owner: GDI + Facing: 96 + SubCell: 0 + Actor188: e3 + Location: 46,22 + Owner: GDI + Facing: 64 + SubCell: 3 + Actor190: e2 + Location: 31,33 + Owner: GDI + Facing: 160 + SubCell: 3 + Actor191: e2 + Location: 32,33 + Owner: GDI + Facing: 32 + SubCell: 0 + waypoint27: waypoint + Location: 20,54 + Owner: Neutral + waypoint26: waypoint + Location: 34,2 + Owner: Neutral + waypoint25: waypoint + Location: 57,27 + Owner: Neutral + waypoint18: waypoint + Location: 35,32 + Owner: Neutral + waypoint17: waypoint + Location: 35,38 + Owner: Neutral + waypoint16: waypoint + Location: 13,26 + Owner: Neutral + waypoint15: waypoint + Location: 8,14 + Owner: Neutral + waypoint14: waypoint + Location: 15,32 + Owner: Neutral + waypoint13: waypoint + Location: 27,35 + Owner: Neutral + waypoint12: waypoint + Location: 28,14 + Owner: Neutral + waypoint11: waypoint + Location: 37,27 + Owner: Neutral + waypoint10: waypoint + Location: 37,14 + Owner: Neutral + waypoint9: waypoint + Location: 20,14 + Owner: Neutral + waypoint8: waypoint + Location: 51,42 + Owner: Neutral + waypoint7: waypoint + Location: 50,55 + Owner: Neutral + waypoint6: waypoint + Location: 36,56 + Owner: Neutral + waypoint5: waypoint + Location: 36,51 + Owner: Neutral + waypoint4: waypoint + Location: 31,35 + Owner: Neutral + waypoint3: waypoint + Location: 13,29 + Owner: Neutral + waypoint2: waypoint + Location: 14,40 + Owner: Neutral + waypoint1: waypoint + Location: 15,46 + Owner: Neutral + waypoint0: waypoint + Location: 54,6 + Owner: Neutral + AirstrikeTarget: waypoint + Owner: Neutral + Location: 57,4 + AttackPath1: waypoint + Owner: Neutral + Location: 29,38 + AttackPath2: waypoint + Owner: Neutral + Location: 50,49 + AttackPath3: waypoint + Owner: Neutral + Location: 20,17 + FlareExtraCamera: camera + Owner: Neutral + Location: 48,27 + GDIBuilding1: atwr + Location: 11,47 + Owner: GDI + GDIBuilding2: gtwr + Location: 31,49 + Owner: GDI + GDIBuilding3: gtwr + Location: 13,46 + Owner: GDI + GDIBuilding4: silo + Location: 7,43 + Owner: GDI + GDIBuilding5: silo + Location: 5,45 + Owner: GDI + GDIBuilding6: silo + Location: 5,43 + Owner: GDI + GDIBuilding7: silo + Location: 3,43 + Owner: GDI + GDIBuilding8: silo + Location: 3,45 + Owner: GDI + GDIBuilding9: gtwr + Location: 17,46 + Owner: GDI + GDIBuilding10: gtwr + Location: 31,52 + Owner: GDI + GDIBuilding11: silo + Location: 7,45 + Owner: GDI + GDIBuilding12: hpad + Location: 22,48 + Owner: GDI + GDIBuilding13: hpad + Location: 24,48 + Owner: GDI + GDICYard: fact + Location: 4,52 + Owner: GDI + GDIHarv: harv + Location: 15,46 + Owner: GDI + GDIHQ: hq + Location: 4,49 + Owner: GDI + GDIProc: proc + Location: 19,48 + Owner: GDI + FreeActor: False + GDIPyle: pyle + Location: 15,52 + Owner: GDI + GDINuke1: nuke + Location: 9,53 + Owner: GDI + GDINuke2: nuke + Location: 7,52 + Owner: GDI + GDINuke3: nuke + Location: 11,53 + Owner: GDI + GDINuke4: nuke + Location: 13,52 + Owner: GDI + GDIOrca1: orca + Owner: GDI + Location: 17,52 + Facing: 92 + GDIOrca2: orca + Owner: GDI + Location: 12,52 + Facing: 92 + GDIWeap: weap + Location: 8,47 + Owner: GDI + Gunboat1: boat + Location: 31,59 + Owner: GDI + Facing: 192 + Gunboat2: boat + Location: 25,60 + Owner: GDI + Facing: 192 + GunboatLeft1: waypoint + Owner: Neutral + Location: 3,59 + GunboatLeft2: waypoint + Owner: Neutral + Location: 3,60 + GunboatRight1: waypoint + Owner: Neutral + Location: 61,59 + GunboatRight2: waypoint + Owner: Neutral + Location: 61,60 + MoneyCrate: MoneyCrate + Owner: Neutral + Location: 24,54 + NodCYard: fact.in + Location: 58,6 + Owner: NodBase + Health: 39 + NodHand: hand + Location: 58,2 + Owner: NodBase + Health: 31 + NodNuke: nuke + Location: 56,4 + Owner: NodBase + Health: 41 + OutpostCYard: factout.in + Location: 58,24 + Owner: Outpost + OutpostHarv: harv + Location: 55,27 + Owner: Outpost + Facing: 192 + OutpostNuke: nukeout.in + Location: 59,27 + Owner: Outpost + OutpostProc: procout.in + Location: 55,23 + Owner: Outpost + FreeActor: False + ReinforcementsHelicopterSpawn: waypoint + Owner: Neutral + Location: 31,2 + ReinforcementsHelicopterRally: waypoint + Owner: Neutral + Location: 38,4 + +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/nod08b/nod08b-AI.lua b/mods/cnc/maps/nod08b/nod08b-AI.lua new file mode 100644 index 0000000000..3f5fef9021 --- /dev/null +++ b/mods/cnc/maps/nod08b/nod08b-AI.lua @@ -0,0 +1,220 @@ +AttackPaths = { { AttackPath1 }, { AttackPath2 }, { AttackPath3 } } +GDIBase = { GDICYard, GDIPyle, GDIWeap, GDIHQ, GDINuke1, GDINuke2, GDINuke3, GDINuke4, GDIProc, GDIBuilding1, GDIBuilding2, GDIBuilding3, GDIBuilding4, GDIBuilding5, GDIBuilding6, GDIBuilding7, GDIBuilding8, GDIBuilding9, GDIBuilding10, GDIBuilding11, GDIBuilding12, GDIBuilding13 } +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(19, 48), cost = 1500, exists = true } +BaseNuke1 = { type = "nuke", pos = CPos.New(9, 53), cost = 500, exists = true } +BaseNuke2 = { type = "nuke", pos = CPos.New(7, 52), cost = 500, exists = true } +BaseNuke3 = { type = "nuke", pos = CPos.New(11, 53), cost = 500, exists = true } +BaseNuke4 = { type = "nuke", pos = CPos.New(13, 52), cost = 500, exists = true } +InfantryProduction = { type = "pyle", pos = CPos.New(15, 52), cost = 500, exists = true } +VehicleProduction = { type = "weap", pos = CPos.New(8, 47), 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/nod08b/nod08b.lua b/mods/cnc/maps/nod08b/nod08b.lua new file mode 100644 index 0000000000..74fb1b8745 --- /dev/null +++ b/mods/cnc/maps/nod08b/nod08b.lua @@ -0,0 +1,247 @@ +WaypointGroup1 = { waypoint1, waypoint2, waypoint3, waypoint9, waypoint10 } +WaypointGroup2 = { waypoint5, waypoint6, waypoint7, waypoint8 } +WaypointGroup3 = { waypoint1, waypoint2, waypoint4, waypoint11 } + +GDI1 = { units = { ['e2'] = 2, ['e3'] = 2 }, waypoints = WaypointGroup1, delay = 30 } +GDI2 = { units = { ['mtnk'] = 1 }, waypoints = WaypointGroup2, delay = 40 } +GDI3 = { units = { ['e1'] = 3, ['e2'] = 3 }, waypoints = WaypointGroup3, delay = 40 } +GDI4 = { units = { ['jeep'] = 2 }, waypoints = WaypointGroup2, delay = 20 } +Auto1 = { units = { ['e1'] = 3, ['e2'] = 1 }, waypoints = WaypointGroup2, delay = 30 } +Auto2 = { units = { ['e1'] = 2, ['e2'] = 1 }, waypoints = WaypointGroup3, delay = 40 } +Auto3 = { units = { ['e1'] = 2, ['e2'] = 2 }, waypoints = WaypointGroup1, delay = 30 } +Auto4 = { units = { ['mtnk'] = 1 }, waypoints = WaypointGroup1, delay = 30 } +Auto5 = { units = { ['mtnk'] = 1 }, waypoints = WaypointGroup3, delay = 40 } +Auto6 = { units = { ['jeep'] = 1 }, waypoints = WaypointGroup3, delay = 40 } +Auto7 = { units = { ['jeep'] = 1 }, waypoints = WaypointGroup1, delay = 50 } + +AutoAttackWaves = { GDI1, GDI2, GDI3, GDI4, Auto1, Auto2, Auto3, Auto4, Auto5, Auto6, Auto7 } + +NodBase = { NodCYard, NodNuke, NodHand } +Outpost = { OutpostCYard, OutpostProc } + +IntroGuards = { Actor171, Actor172, Actor173, Actor145, Actor159, Actor160, Actor161 } +OutpostGuards = { Actor177, Actor178, Actor180, Actor187, Actor188, Actor185, Actor186, Actor184, Actor148, Actor179, Actor176, Actor183, Actor182 } +IntroReinforcements = { "e1", "e1", "e1", "e1", "e1", "e1", "e3", "e3", "e3", "e3" } + +NodBaseTrigger = { CPos.New(52, 2), CPos.New(52, 3), CPos.New(52, 4), CPos.New(52, 5), CPos.New(52, 6), CPos.New(52, 7), CPos.New(52, 8) } + +Gunboat1PatrolPath = { GunboatLeft1.Location, GunboatRight1.Location } +Gunboat2PatrolPath = { GunboatLeft2.Location, GunboatRight2.Location } + +AirstrikeDelay = DateTime.Minutes(2) + DateTime.Seconds(30) + +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, waypoint0.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() + FlareCamera1 = Actor.Create("camera", true, { Owner = player, Location = waypoint25.Location }) + FlareCamera2 = Actor.Create("camera", true, { Owner = player, Location = FlareExtraCamera.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() + FlareCamera1.Kill() + FlareCamera2.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) + +Trigger.OnKilled(Gunboat1, function() + Gunboat1Camera.Destroy() +end) +Trigger.OnKilled(Gunboat2, function() + Gunboat2Camera.Destroy() +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(enemy.GetGroundAttackers()) + + Gunboat1Camera = Actor.Create("camera.boat", true, { Owner = player, Location = Gunboat1.Location }) + Gunboat2Camera = Actor.Create("camera.boat", true, { Owner = player, Location = Gunboat2.Location }) + Trigger.OnIdle(Gunboat1, function() Gunboat1.Patrol(Gunboat1PatrolPath) end) + Trigger.OnIdle(Gunboat2, function() Gunboat2.Patrol(Gunboat2PatrolPath) end) + + 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 not Gunboat1.IsDead then + Gunboat1Camera.Teleport(Gunboat1.Location) + end + + if not Gunboat2.IsDead then + Gunboat2Camera.Teleport(Gunboat2.Location) + end + + 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/nod08b/rules.yaml b/mods/cnc/maps/nod08b/rules.yaml new file mode 100644 index 0000000000..2246efa0b7 --- /dev/null +++ b/mods/cnc/maps/nod08b/rules.yaml @@ -0,0 +1,247 @@ +World: + LuaScript: + Scripts: nod08b.lua, nod08b-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: flag.vqa + SmudgeLayer@SCORCH: + InitialSmudges: + sc2 44,57 0 + sc1 40,56 0 + sc5 50,55 0 + sc3 45,55 0 + sc2 61,43 0 + sc6 58,42 0 + sc4 61,41 0 + sc1 55,41 0 + sc5 59,39 0 + sc5 53,39 0 + sc4 60,9 0 + sc6 58,9 0 + sc2 57,8 0 + sc3 54,6 0 + sc2 60,5 0 + sc4 55,5 0 + sc6 61,4 0 + sc6 55,4 0 + sc1 57,3 0 + SmudgeLayer@CRATER: + InitialSmudges: + cr1 42,56 0 + cr1 49,55 0 + cr1 51,52 0 + cr1 41,52 0 + cr1 60,43 0 + cr1 59,41 0 + cr1 55,40 0 + cr1 49,40 0 + cr1 53,36 0 + cr1 55,6 0 + cr1 54,5 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 + +BOAT: + Health: + HP: 1500 + AutoTarget: + InitialStance: AttackAnything + RejectsOrders: + Except: Attack + +Camera.Boat: + AlwaysVisible: + Mobile: + TerrainSpeeds: + RevealsShroud: + Range: 4c0 + ScriptTriggers: + +FACT.IN: + Inherits: FACT + RenderSprites: + Image: FACT + ProvidesPrerequisite: + Prerequisite: fact + CustomSellValue: + Value: 12825 + +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/nod08b/weapons.yaml b/mods/cnc/maps/nod08b/weapons.yaml new file mode 100644 index 0000000000..5850590565 --- /dev/null +++ b/mods/cnc/maps/nod08b/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 b987e860e1..1feabcbd80 100644 --- a/mods/cnc/missions.yaml +++ b/mods/cnc/missions.yaml @@ -24,6 +24,7 @@ Nod Campaign: ./mods/cnc/maps/nod07a ./mods/cnc/maps/nod07b ./mods/cnc/maps/nod08a + ./mods/cnc/maps/nod08b Funpark Campaign: ./mods/cnc/maps/funpark01