-
Notifications
You must be signed in to change notification settings - Fork 0
/
supreme_tower_defence_script.lua
1964 lines (1619 loc) · 59.2 KB
/
supreme_tower_defence_script.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- Packer's Survival Map Script
-- Created by Packer
-- Inspired by Survival
-- Import Functionality
--------------------------------------------------------------------------
local s_Directory = ScenarioInfo.directory; --Map Directory
local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua');
local ScenarioFramework = import('/lua/ScenarioFramework.lua');
local Utilities = import('/lua/utilities.lua');
local ArmyToInt = import(s_Directory .. 'lib/ArmyToInt.lua');
local ResourceCreation = import(s_Directory .. 'lib/ResourceCreation.lua');
local UnitMods = import(s_Directory .. 'lib/T4UnitMods.lua');
local Nuke = import(s_Directory .. 'lib/Nuke.lua');
local Victory = import('/lua/victory.lua');
local ACUModule = import(s_Directory .. 'lib/SpawnACUs.lua');
--------------------------------------------------------------------------
--Editable - Easily accessible values to change the Dynamic of the map
--------------------------------------------------------------------------
local s_BossPerPlayer = false; --true = bosses spawn per player, false = Only one of each boss will spawn.
local s_SpawnRadius = 5; --How far can enemies spawn from their marker, keep low to stop outside spawning (5 Default).
local s_BossSpawnRadius = 10; --How far can Bosses spawn from their marker, keep low to stop outside spawning (10 Default).
local s_WaypointRadius = 5; --Random position around Waypoints to stop clogging
local s_Assassination = false; --Does killing the player's ACU cause them to be defeated?
local s_WaveBalance = true; --If more than 1 category (Land,Air,Navy) is enabled, spawn the reduced ratio of units. If Disabled, spawn 100% of units on table
local s_EnemyBaseVictory = true; --If true, when the enemy bases are destroyed the players win, even before the waves have ended.
--------------------------------------------------------------------------
-- Mass
local s_AutoResources = true; --If true the resources table below will be used, if false a manual import is needed
-- 1 = Mass, 0 = Empty, {0} = Space
local s_MassResources =
{
{1,0,1,0,1,0,1,0,1},
{0,1,0,1,0,1,0,1,0},
{1,0,1,0,1,0,1,0,1},
}
-- 1 = Hydro, 0 = Empty
local s_HydroResources =
{
{0,1,0},
{1,0,1},
}
-- Debug - For Testing purposes only
local s_DebugMode = false; --Makes the Defense Objective on the enemy team and spawns one of every single unit on the unit table
--------------------------------------------------------------------------
--Messages
--------------------------------------------------------------------------
--A place to put version update infomation
local s_VersionMessages =
{
"| * This is an example of your version message!",
"| * Added: New Version messages at the top of the script for easy access!",
--Add as many lines as you need for your new version
}
local s_InstructionMessage =
{
"| Build your defenses and protect the defense structure.",
"| More Example text can go here to explain additional infomation"
}
local s_TitleCredits =
{
"Created By PUMBA7",
}
local s_EndMessage =
{
"Victory!", --Victory Message
"Defeat!", --Loss Message
"The enemy structures have been destroyed,", --Enemy Base Destruction Victory Message
"The defense structure has been destroyed.", --When the defense structure is destroyed
"The defense structure was destroyed, but the waves were defeated!", --When the defense structure is destroyed during endless waves
}
--------------------------------------------------------------------------
-- Gamemodes
--------------------------------------------------------------------------
local s_Gamemodes =
{
["Standard"] = 'Gamemode_Standard.lua',
["RNG"] = 'Gamemode_RNG.lua',
}
-- Mod Support
local s_Mods =
{
{true, "Base", 'Units_Base.lua'}, --1
{false, "Nomads", 'Units_Nomads.lua'}, --2
{false, "BrewLAN", 'Units_BrewLAN.lua'}, --3
{false, "Total Mayhem", 'Units_TotalMayhem.lua'}, --4
{false, "BlackOps FAF: Unleashed", 'Units_BlackOps.lua'}, --5
{false, "BlackOps FAF: ACUs", nil}, --6
{false, "Marlo's mod Compilation", 'Units_Marlo.lua'}, --7
{false, "SupremeUnitPackV6Official", 'Units_SupremeUnit.lua'}, --8
{false, "Extreme Wars", 'Units_ExtremeWars.lua'}, --9
{false, "Survival Mayhem&BO balance", 'Units_SurvivalMayhemBO.lua'}, --10
{false, "Total Mayhem Lite", 'Units_TotalMayhemLite.lua'}, --11
{false, "Wyvern Unit Battle Pack", 'Units_Wyvern.lua'}, --12
{false, "Orbital Wars", 'Units_OrbitalWars.lua'}, --13
{false, "GGA Cybran T4 Crusher", 'Units_GGA.lua'}, --14
{false, "Research & Daiquiris", 'Units_BrewLAN_RnD.lua'}, --15
{false, "New SupCom Annihilation", 'Units_BrewLAN_RnD.lua'}, --16
{false, "BlackOps FAF: EXUnits", 'Units_BlackOpsEXUnits.lua'}, --17
{false, "Antares Unit Pack (Preservation)", 'Units_AntaresUnitPack.lua'}, --18
{false, "(F.B.P.) Future Battlefield Pack: Legends", 'Units_FBPL.lua'}, --19
{false, "Experimentals Wars", 'Units_ExperimentalsWars.lua'}, --20
{false, "Annihilation New SupCom", 'Units_AnnihilationSupCom.lua'}, --21
{false, "SCTABalance", 'Units_SCTABalance.lua'}, --22
{false, "SCTAFix", 'Units_SCTAFix.lua'}, --23
{false, "Hyper Experimental Tier", 'Units_HyperET.lua'}, --24
{false, "Mixed Combat Pack", 'Units_MCP.lua'}, --25
}
--------------------------------------------------------------------------
--Editable - end
--------------------------------------------------------------------------
local s_GamemodeSettings = nil;
local DebugTools = nil; --Only imports when we DebugMode is enabled, this will spawn EVERY SINGLE UNIT on every mod Table
local s_UnitTables = --Table of all Spawnable Units, dont modify
{
["Land T1"] = { {}, {}, {}, {}, {}, {} }, --1 Scout / 2 Assault / 3 Support / 4 Ranged / 5 Custom / 6 Experimental
["Air T1"] = { {}, {}, {}, {}, {}, {}, {}, {}, }, --1 Spy / 2 Fighter / 3 Support / 4 Ranged / 5 Custom / 6 Experimental / 7 Bomber / 8 Gunship
["Navy T1"] = { {}, {}, {}, {}, {}, {}, }, --1 Submarine / 2 Assault / 3 Support / 4 Ranged / 5 Custom / 6 Experimental
["Land T2"] = { {}, {}, {}, {}, {}, {}, }, --vvv Same as T1
["Air T2"] = { {}, {}, {}, {}, {}, {}, {}, {}, },
["Navy T2"] = { {}, {}, {}, {}, {}, {}, },
["Land T3"] = { {}, {}, {}, {}, {}, {}, },
["Air T3"] = { {}, {}, {}, {}, {}, {}, {}, {}, },
["Navy T3"] = { {}, {}, {}, {}, {}, {}, },
["Land T4"] = { {}, {}, {}, {}, {}, {}, },
["Air T4"] = { {}, {}, {}, {}, {}, {}, {}, {}, },
["Navy T4"] = { {}, {}, {}, {}, {}, {}, },
};
local s_EndlessWave = --Used to track endless wave, dont modify
{
{
["Land T1"] = { 0, 0, 0, 0, 0, 0, }, --1 Scout / 2 Assault / 3 Support / 4 Ranged / 5 Custom / 6 Experimental
["Air T1"] = { 0, 0, 0, 0, 0, 0, 0, 0, }, --1 Spy / 2 Fighter / 3 Support / 4 Ranged / 5 Custom / 6 Experimental / 7 Bomber / 8 Gunship
["Navy T1"] = { 0, 0, 0, 0, 0, 0, }, --1 Submarine / 2 Assault / 3 Support / 4 Ranged / 5 Custom / 6 Experimental
["Land T2"] = { 0, 0, 0, 0, 0, 0, }, --vvv Same as T1
["Air T2"] = { 0, 0, 0, 0, 0, 0, 0, 0, },
["Navy T2"] = { 0, 0, 0, 0, 0, 0, },
["Land T3"] = { 0, 0, 0, 0, 0, 0, },
["Air T3"] = { 0, 0, 0, 0, 0, 0, 0, 0, },
["Navy T3"] = { 0, 0, 0, 0, 0, 0, },
["Land T4"] = { 0, 0, 0, 0, 0, 0, },
["Air T4"] = { 0, 0, 0, 0, 0, 0, 0, 0, },
["Navy T4"] = { 0, 0, 0, 0, 0, 0, },
}
};
-- Enemy Data - Automatic
local s_TableFile = nil;
local s_WavesTable = nil; --The data for selecting units to be spawned
local s_EndlessTable = nil;
local s_NukeLaunchers = {};
local s_NukeMarkers = {};
local s_EnemyBases = {};
local s_EnemyBaseMarkers = {};
local s_EnemyBasesSpawned = false;
--------------------------------------------------------------------------
-- Variables - Automatic
local s_Tick = 1; --Update Rate, Higher values reduce lag
local s_WaveTick = 1; --How often does the wave tick trigger, should always be 1 sec unless certain circumstances
local s_GameStage = 0; --0 Build Time, 1 Gameplay, 2 Endless, 3 Player Win, 4 Player Loss
local s_PlayerCount = 0; --How many slots filled
--------------------------------------------------------------------------
-- Game Settings - Automatic
local s_Wave = 0; --Current Wave
local s_WaveEndless = 1; --Current Endless Wave
local s_WaveTotalCount = 0; --Current Wave
local s_TotalWaves = 24; --Dynamic Wave Count
local s_NextSpawnTime = 0; --Time index when next wave spawns
local s_CurrentTime = 0; --Current Time
local s_GameTimeTotal = 60; --TotalWaves * WaveTime + 60 + BuildTime - Total Game Time
local s_WarnTime = 90; --When the warning message will show
local s_LandEnabled = false;
local s_AirEnabled = false;
local s_NavyEnabled = false;
--------------------------------------------------------------------------
-- Enemy Setup - Automatic
local s_AttackObjectiveMarkers = {}; --Enemy base spawn Positions
local s_DamageScale = 0.04; --Damage Increase Per Wave
local s_HealthScale = 0.04; --Health Increase Per Wave
local s_SpeedScale = 0.004; --Speed Increase Per Wave
--------------------------------------------------------------------------
local s_PathsIndex =
{
["LAND"] = 0, ["AIR"] = 0, ["NAVY"] = 0, ["LANDBOSS"] = 0, ["AIRBOSS"] = 0, ["NAVYBOSS"] = 0, ["ENEMY"] = 0,
}
local s_MoveMarkers = --TYPE / PATHS / Path POINTS
{
["LAND"] =
{ --PATHS
["SPAWN"] = {}, --Spawn positions for Paths
{}, --1 Path POINTS
{}, --2 Path POINTS etc
},
["AIR"] = { {}, {}, },
["NAVY"] = { {}, {}, },
["LANDBOSS"] = { {}, {}, },
["AIRBOSS"] = { {}, {}, },
["NAVYBOSS"] = { {}, {}, },
["ENEMY"] = { {}, {}, },
}
-- Wave Data - Automatic
--------------------------------------------------------------------------
local s_WaveActive = false;
-- Tracking
local s_DefenseObjectiveUnit = nil;
local s_DefenseHP = 1;
local s_DefenseHPLast = 1;
local s_PlayerACUs = {};
-- Score
local s_ScoreModifier = 1;
local s_ScoreTotal = 0;
--------------------------------------------------------------------------
--Pregame Setup
function OnPopulate()
LOG("----- Survival MODE: OnPopulate()");
--Settings Check
SetupSettings();
--Spawn Resources
CreateResources();
--Spawn ACU Units
s_PlayerACUs = ACUModule.SpawnACUs(s_Mods);
s_PlayerCount = s_PlayerACUs["PlayerCount"];
--ACU Assassination
--SetupACUs();
--Spawn Defense Objective
SpawnDefenseObjective();
--Setup Markers
SetupMarkers();
--Setup Game
SetupGame();
end
--Called at Start of Game
function OnStart(self)
ScenarioFramework.SetPlayableArea('AREA_1' , false);
LOG("----- Survival MODE: OnStart");
--Debug Tools
if(s_DebugMode) then
DebugGame();
end
--Game Loop
ForkThread(GameUpdate);
end
SetupGame = function()
LOG("----- Survival MODE: Setup Game");
--Sandbox for our own Gamemode
ScenarioInfo.Options.Victory = 'sandbox';
-- Setup Alliances and Restrictions
SetAlliance("ARMY_SURVIVAL_ALLY", "ARMY_SURVIVAL_ENEMY", 'Enemy');
SetIgnoreArmyUnitCap('ARMY_SURVIVAL_ENEMY', true);
SetArmyColor('ARMY_SURVIVAL_ENEMY', 72, 32, 48); --Old Color 48, 32, 48
--Restrictions();
for i, Army in ListArmies() do
-- Player Army Check
if (ArmyToInt.GetInt(Army) > 0) then
--Setup Alliances
SetAlliance(Army, "ARMY_SURVIVAL_ALLY", 'Ally'); --Survival ALLY
SetAlliance(Army, "ARMY_SURVIVAL_ENEMY", 'Enemy'); --Survival ENEMY
--Set all players as Alliance
for x, ArmyX in ListArmies() do
-- Player Army Check
if (ArmyToInt.GetInt(ArmyX) > 0) then
SetAlliance(Army, ArmyX, 'Ally');
end
end
end
end
end
Restrictions = function()
LOG("----- Survival MODE: Restrictions");
for i, Army in ListArmies() do
-- Player Army Check
if (ArmyToInt.GetInt(Army) > 0) then
if(ScenarioInfo.Options.opt_Survival_Restrict == 0) then
--Restrictions (LAND and AIR is always enabled)
if(s_NavyEnabled == false) then
ScenarioFramework.AddRestriction(Army, categories.NAVAL);
ScenarioFramework.RemoveRestriction(Army, categories.DEFENSE);
end
--Gamemode Restrictions override
s_GamemodeSettings.PlayerRestrictions(Army, s_Mods);
end
--Nukes enabled, let defenders build anti nukes
if(ScenarioInfo.Options.opt_Survival_Nuke == 1) then
ScenarioFramework.RemoveRestriction(Army, categories.ANTIMISSILE);
end
else
s_GamemodeSettings.BotRestrictions(Army, s_Mods);
end
end
end
SetGameStage = function(stage)
if(stage == 0)then --Build Time
s_GameStage = 0;
elseif(stage == 1)then --Gameplay
s_GameStage = 1;
elseif(stage == 2) then --Endless
s_GameStage = 2;
elseif(stage == 3) then --Player Win
s_GameStage = 3;
elseif(stage == 4) then --Player loss
s_GameStage = 4;
elseif(stage == 5) then --Game end
s_GameStage = 5;
end
end
function CountUnitsForArmy(armyName)
local unitCount = 0
local allUnits = GetUnitsInRect(Rect(-100000, -100000, 100000, 100000)) -- Adjust the rectangle to cover your map area
local army = GetArmyBrain(armyName):GetArmyIndex()
for _, unit in allUnits do
if unit:GetArmy() == army then
unitCount = unitCount + 1
end
end
return unitCount
end
GameUpdate = function(self)
IntroMessage();
ModMessage();
VersionMessage();
Restrictions(); --Do here incase mods force enable units
--0 Build Time, 1 Gameplay, 2 Endless, 3 Player Win, 4 Player Loss
while (s_GameStage < 5) do
s_CurrentTime = GetGameTimeSeconds();
if(s_GameStage == 0) then --Build Time--
--Post Build Time
if (s_CurrentTime >= s_NextSpawnTime) then
LOG("----- Survival MODE: Build Time Over");
BroadcastMSG("Genesis has begun!", 4);
Sync.ObjectiveTimer = 0;
SetGameStage(1);
--Spawn first Wave
SpawnWave();
s_NextSpawnTime = s_CurrentTime + ScenarioInfo.Options.opt_Survival_WaveFrequency; --Time to Next Wave
else --During Build Time
Sync.ObjectiveTimer = math.floor(s_NextSpawnTime - s_CurrentTime);
s_DefenseObjectiveUnit:SetCustomName(SecondsToTime(Sync.ObjectiveTimer)); -- Classic name as timer
if (s_WarnTime > 0) then
--Warning Message
if(s_NextSpawnTime - s_CurrentTime <= s_WarnTime) then
BroadcastMSG(s_WarnTime .. " SECOND WARNING", 3);
if(s_WarnTime == 90) then
s_WarnTime = 60;
elseif(s_WarnTime == 60) then
s_WarnTime = 30;
elseif(s_WarnTime == 30) then
s_WarnTime = 0;
end
end
end
end
elseif(s_GameStage == 1) then --Gameplay--
--Objective Timer
if(ScenarioInfo.Options.opt_Survival_Endless == 1) then
Sync.ObjectiveTimer = math.floor(s_CurrentTime);
else
Sync.ObjectiveTimer = math.floor(s_GameTimeTotal - s_CurrentTime);
end
s_DefenseObjectiveUnit:SetCustomName("Wave: " .. s_WaveTotalCount .. "/" .. table.getn(s_WavesTable)); -- Classic defense object with wave count
--Gamemode Hook
s_GamemodeSettings.GameplayUpdate(s_WaveTotalCount);
--Base Destruction Victory
EnemyBaseVictoryCheck();
--end of Game, into Win / Endless Mode
if(s_CurrentTime >= s_GameTimeTotal) then
Sync.ObjectiveTimer = 0;
--If endless is enabled
if(ScenarioInfo.Options.opt_Survival_Endless == 1) then
SetGameStage(2); --Endless
s_Wave = 1; --Reset
s_WavesTable = s_EndlessWave; --Swap to the Endless Table
--Completed Basic Waves, endless does double scaling for challenge
if (ScenarioInfo.Options.opt_Survival_Scale ~= 1) then
s_DamageScale = s_DamageScale * 2; --Damage Increase Per Wave
s_HealthScale = s_HealthScale * 2; --Health Increase Per Wave
--s_SpeedScale = s_SpeedScale * 2; --Speed Increase Per Wave
end
s_NextSpawnTime = s_CurrentTime; --Next Wave Time
else
Sync.ObjectiveTimer = 0;
SetGameStage(3); --Victory
end
else
--Wave Spawning
if (s_CurrentTime >= s_NextSpawnTime and s_Wave <= table.getn(s_WavesTable)) then
local totalEnemies = 0;
if ScenarioInfo.Options.opt_Survival_SoftUnitCap > 0 then
totalEnemies = CountUnitsForArmy("ARMY_SURVIVAL_ENEMY");
if totalEnemies < ScenarioInfo.Options.opt_Survival_SoftUnitCap then
PrintText("Spawning next wave("..(s_Wave+1).."), number of enemies / cap: "..totalEnemies.." / "..ScenarioInfo.Options.opt_Survival_SoftUnitCap, 14, 'ffffffff', 2, 'rightbottom')
else
PrintText("Pausing next wave("..(s_Wave+1).."), number of enemies / cap: "..totalEnemies.." / "..ScenarioInfo.Options.opt_Survival_SoftUnitCap, 14, 'ffffffff', 2, 'rightbottom')
end
end
if ScenarioInfo.Options.opt_Survival_SoftUnitCap < 0 or totalEnemies < ScenarioInfo.Options.opt_Survival_SoftUnitCap then
SpawnWave();
s_NextSpawnTime = s_CurrentTime + ScenarioInfo.Options.opt_Survival_WaveFrequency; --Next Wave Time
else
s_NextSpawnTime = s_CurrentTime + 5;
s_GameTimeTotal = s_GameTimeTotal + 5;
end
end
end
elseif(s_GameStage == 2) then --Endless--
---Update Timer
Sync.ObjectiveTimer = math.floor(s_CurrentTime);
s_DefenseObjectiveUnit:SetCustomName("Endless Wave: " .. s_WaveTotalCount); -- Classic objective with wave count on it
--Gamemode Hook
s_GamemodeSettings.GameplayUpdate(s_WaveTotalCount);
--Base Destruction Victory
EnemyBaseVictoryCheck();
--Wave Spawning
if (s_CurrentTime >= s_NextSpawnTime) then
--end of the Endless Table
if(s_WaveEndless > table.getn(s_TableFile.GetEndlessTable())) then
s_WaveEndless = 0;
else
--Spawn Wave
SpawnWave();
s_NextSpawnTime = s_CurrentTime + ScenarioInfo.Options.opt_Survival_WaveFrequency; --Next Wave Time
end
end
elseif(s_GameStage == 3) then --Player Victory--
--Show Stats!
GetArmyBrain("ARMY_SURVIVAL_ENEMY"):OnDefeat();
GetArmyBrain("ARMY_SURVIVAL_ALLY"):OnVictory();
for i, Army in ListArmies() do
-- Player Army Check
if (ArmyToInt.GetInt(Army) > 0) then
GetArmyBrain(Army):OnVictory();
end
end
SetGameStage(5);
PrintText(s_EndMessage[1], 35, 'ffCBFFFF', 600, 'center')
CalculateFinalScore();
Victory.CallEndGame()
elseif(s_GameStage == 4) then --Player Loss--
GetArmyBrain("ARMY_SURVIVAL_ENEMY"):OnVictory();
GetArmyBrain("ARMY_SURVIVAL_ALLY"):OnDefeat();
for i, Army in ListArmies() do
-- Player Army Check
if (ArmyToInt.GetInt(Army) > 0) then
GetArmyBrain(Army):OnDefeat();
end
end
SetGameStage(5);
PrintText(s_EndMessage[2], 35, 'ffCBFFFF', 600, 'center')
CalculateFinalScore();
Victory.CallEndGame()
end
WaitSeconds(s_Tick);
end
KillThread(self);
end
SpawnWave = function()
--Wave Increment
s_Wave = s_Wave + 1;
s_WaveTotalCount = s_WaveTotalCount + 1;
--Endless
if(s_GameStage == 2) then
s_Wave = 1; --Set to 1 for Endless table
s_WaveEndless = s_WaveEndless + 1;
ImportEndlessTable(s_WaveEndless);
end
--Text
LOG("----- Survival MODE: Spawning Wave: " .. s_WaveTotalCount);
print("Wave: " .. s_WaveTotalCount);
if(s_WavesTable[s_Wave]["Title"] ~= nil) then
--Offset from center
PrintText(" ", 30, 'fff4f4f4', 3, 'center')
PrintText(" ", 30, 'fff4f4f4', 3, 'center')
PrintText(" ", 30, 'fff4f4f4', 3, 'center')
PrintText(" ", 30, 'fff4f4f4', 3, 'center')
PrintText(s_WavesTable[s_Wave]["Title"], 30, 'fff4f4f4', 4, 'center')
end
--Nuke
if(ScenarioInfo.Options.opt_Survival_Nuke == 1 and s_WavesTable[s_Wave]["Nuke"] ~= nil) then
--Spawn Nuke Launchers
if(s_WavesTable[s_Wave]["Nuke"]["Spawn"] == true and table.getn(s_NukeLaunchers) == 0) then
PrintText("Strategic Missile Launchers Detected", 30, 'fff4f4f4', 4, 'center')
for i = 1, table.getn(s_NukeMarkers) do
table.insert(s_NukeLaunchers, i, Nuke.SpawnNukeLauncher(s_NukeMarkers[i], s_Mods));
end
end
--Fire as many nukes as we have
if(s_WavesTable[s_Wave]["Nuke"]["Fire"] ~= nil) then
ForkThread(NukeWave);
end
end
--Base
if(ScenarioInfo.Options.opt_Survival_Bases ~= 0 and s_WavesTable[s_Wave]["Base"] ~= nil) then
local EnemyObjBP = ScenarioInfo.Options.opt_Survival_Bases;
--Spawn Enemy Base
if(table.getn(s_EnemyBases) == 0) then
PrintText("Enemy Structures Detected", 30, 'fff4f4f4', 4, 'center')
local position = {};
for i = 1, table.getn(s_EnemyBaseMarkers) do
position = s_EnemyBaseMarkers[i].position;
base = CreateUnitHPR(EnemyObjBP, "ARMY_SURVIVAL_ENEMY", position[1], position[2], position[3], 0,0,0);
base:SetRegenRate(s_WavesTable[s_Wave]["Base"][2] * s_PlayerCount);
base:SetMaxHealth(s_WavesTable[s_Wave]["Base"][1] * s_PlayerCount);
base:AdjustHealth(nil, base:GetMaxHealth());
base:SetHealth(nil, base:GetMaxHealth());
table.insert(s_EnemyBases, i, base);
end
s_EnemyBasesSpawned = true;
end
end
--Check if Boss wave
if(ScenarioInfo.Options.opt_Survival_Boss == 1 and s_WavesTable[s_Wave]["Boss"] ~= nil) then
SpawnBossWave();
end
s_WaveActive = true;
--Start Wave Update
ForkThread(WaveUpdate);
--Start Custom Wave Update
if(s_WavesTable[s_Wave]["Custom"] ~= nil) then
ForkThread(CustomWave);
end
end
CustomWave = function(self)
local spawnEnabled = true;
local MarkerData = nil;
local PlatoonList = {};
local type = "LAND";
local unitSpawn = 0;
local waveCount = 0;
local categoryCount = 0;
local position = {};
while (spawnEnabled == true) do
--Loop through each category
for v = 1, 3 do
--Category
if(v == 1) then
type = "LAND";
elseif(v == 2) then
type = "AIR"
elseif(v == 3) then
type = "NAVY"
end
--Category not used
if(s_WavesTable[s_Wave]["Custom"][type] == nil
or v == 1 and s_LandEnabled == false
or v == 2 and s_AirEnabled == false
or v == 3 and s_NavyEnabled == false) then
categoryCount = categoryCount + 1;
continue;
end
--Loop Through all the custom Unit groups
for x = 1, table.getn(s_WavesTable[s_Wave]["Custom"][type]) do
--If all units have been spawned in this group
if(s_WavesTable[s_Wave]["Custom"][type][x][1] <= 0) then
waveCount = waveCount + 1;
continue;
end
--Spawn Rate
if(ScenarioInfo.Options.opt_Survival_SpawnRate == 1) then
--Spawn at Once
unitSpawn = s_WavesTable[s_Wave]["Custom"][type][x][1];
else
--Spawn over the Wave
unitSpawn = math.ceil(s_WavesTable[s_Wave]["Custom"][type][x][1] / ScenarioInfo.Options.opt_Survival_WaveFrequency);
end
--Remaining Unit count from Wave Table
for w = 1, unitSpawn do
--Loop Through Blueprints for this group
for y = 2, table.getn(s_WavesTable[s_Wave]["Custom"][type][x]) do
--Make sure its valid
if(s_WavesTable[s_Wave]["Custom"][type][x][y] ~= nil) then
--Per Player Army
for z, Army in ListArmies() do
if (ArmyToInt.GetInt(Army) > 0) then
MarkerData = GetSpawnMarker(type, ArmyToInt.GetInt(Army) , false);
position[1] = MarkerData[1].position[1];
position[2] = MarkerData[1].position[2];
position[3] = MarkerData[1].position[3];
--If random range is not zero
if(s_SpawnRadius ~= 0) then
position[1] = MarkerData[1].position[1] + math.random(-s_SpawnRadius, s_SpawnRadius);
position[3] = MarkerData[1].position[3] + math.random(-s_SpawnRadius, s_SpawnRadius);
end
table.insert(PlatoonList, SpawnUnit(s_WavesTable[s_Wave]["Custom"][type][x][y], type, position));
--Give Orders
if(PlatoonList ~= nil and table.getn(PlatoonList) ~= 0) then
--AIR or Ranged
if(type == "AIR") then
order = 2; --Attack Move
else
order = 1; --Move
end
PlatoonOrder("ARMY_SURVIVAL_ENEMY", PlatoonList, order, type, MarkerData[2]);
PlatoonList = {};
end
end
end
end
end
s_WavesTable[s_Wave]["Custom"][type][x][1] = s_WavesTable[s_Wave]["Custom"][type][x][1] - 1;
end
end
if(waveCount == table.getn(s_WavesTable[s_Wave]["Custom"][type])) then
categoryCount = categoryCount + 1;
end
end
--Finished spawning
if(categoryCount == 3) then
spawnEnabled = false;
else
--Reset
categoryCount = 0;
waveCount = 0;
WaitSeconds(s_WaveTick); --One Sec Wave Tick
end
end
--LOG("---Survival: Kill Custom Spawning Thread---");
KillThread(self);
end
--Used in each wave
WaveUpdate = function(self)
local s_WaveCount = 0;
--LOG("WAVE " .. s_Wave .. " Started"):
while (s_WaveActive) do
s_WaveCount = 0;
for i = 1, 4 do
-- LAND
if(s_LandEnabled) then
if(SpawnCategory("Land T" .. i, "LAND")) then
s_WaveCount = s_WaveCount + 1;
end
else
s_WaveCount = s_WaveCount + 1;
end
-- AIR
if(s_AirEnabled) then
if(SpawnCategory("Air T" .. i, "AIR")) then
s_WaveCount = s_WaveCount + 1;
end
else
s_WaveCount = s_WaveCount + 1;
end
-- NAVY
if(s_NavyEnabled) then
if(SpawnCategory("Navy T" .. i, "NAVY")) then
s_WaveCount = s_WaveCount + 1;
end
else
s_WaveCount = s_WaveCount + 1;
end
end
--Check if all units have been spawned
if(s_WaveCount >= 12) then
s_WaveActive = false;
else
WaitSeconds(s_WaveTick); --One Sec Wave Tick
end
end
--print("Wave Complete: " .. Wave);
KillThread(self);
end
NukeWave = function(self)
local fireNukes = true;
local VolleyTimer = ScenarioInfo.Options.opt_Survival_WaveFrequency / (s_WavesTable[s_Wave]["Nuke"]["Fire"] / table.getn(s_NukeLaunchers));
while(fireNukes) do
--Nuke Volley
for i = 1,table.getn(s_NukeLaunchers) do
if(s_WavesTable[s_Wave]["Nuke"]["Fire"] > 0) then
Nuke.LaunchNuke(s_NukeLaunchers[i], s_DefenseObjectiveUnit);
s_WavesTable[s_Wave]["Nuke"]["Fire"] = s_WavesTable[s_Wave]["Nuke"]["Fire"] - 1;
else
KillThread(self);
end
end
WaitSeconds(VolleyTimer); --Time till next Volley
end
KillThread(self);
end
--Spawn Boss Wave
SpawnBossWave = function()
--Temp Vars
local Spawn = false;
local MarkerData = {};
local position = {0,0,0};
local PlatoonList = {};
local BossWave = {};
--Spawn for Each player (list through all armies and find players)
for z, Army in ListArmies() do
--Make sure this is a player
if (ArmyToInt.GetInt(Army) > 0) then
for i = 1, table.getn(s_WavesTable[s_Wave]["Boss"]) do
--Shorthand the Boss infomation
BossWave = s_WavesTable[s_Wave]["Boss"][i];
--Reset
Spawn = false;
--Check if enabled
if(BossWave["Type"] == "LAND" and s_LandEnabled) then
Spawn = true;
elseif(BossWave["Type"] == "AIR" and s_AirEnabled) then
Spawn = true;
elseif(BossWave["Type"] == "NAVY" and s_NavyEnabled) then
Spawn = true;
end
--Check this Unit Spawn is enabled
if(Spawn) then
--Unit count should be at least
if(BossWave["Count"] <= 0) then
LOG("ERROR - Wave " .. s_Wave .. " Boss Unit count is not a postive number!");
BossWave["Count"] = 1; --Set to 1 incase
end
--Each Blueprint
for y = 1, table.getn(BossWave["Blueprint"]) do
--Unit Count
for x = 1, BossWave["Count"] do
MarkerData = GetSpawnMarker(BossWave["Type"] .. "BOSS", z, true);
position[1] = MarkerData[1].position[1];
position[2] = MarkerData[1].position[2];
position[3] = MarkerData[1].position[3];
--If random range is not zero
if(s_SpawnRadius ~= 0) then
position[1] = MarkerData[1].position[1] + math.random(-s_SpawnRadius, s_SpawnRadius);
position[3] = MarkerData[1].position[3] + math.random(-s_SpawnRadius, s_SpawnRadius);
end
table.insert(PlatoonList, SpawnUnit(BossWave["Blueprint"][y], BossWave["Type"] .. "BOSS", position, BossWave["Mods"], true, BossWave["HP"], BossWave["Speed"], BossWave["Vet"]));
end
end
end
if(PlatoonList ~= nil and table.getn(PlatoonList) ~= 0) then
PlatoonOrder("ARMY_SURVIVAL_ENEMY", PlatoonList, 1, BossWave["Type"] .. "BOSS", MarkerData[2]);
PlatoonList = {};
end
end
end
--If NOT per player, just return as we only spawn the boss units ONCE
if(s_BossPerPlayer == false) then
return;
end
end
end
SpawnCategory = function(prefix, type)
local count = 0;
local bp;
local ref;
local PlatoonList = {};
local order = 2;
local unitCount = 0;
local MarkerData = {};
local newUnit = nil;
local position = {0,0,0};
local UnitSpawn = 0;
--Make sure table isn't empty
if(s_WavesTable[s_Wave][prefix] ~= nil) then
--Loop Through Tier Table
for x = 1, table.getn(s_WavesTable[s_Wave][prefix]) do
--If we have no unit blueprints for this table, Disable it
if(table.getn(s_UnitTables[prefix][x]) == 0) then
s_WavesTable[s_Wave][prefix][x] = 0;
end
unitCount = s_WavesTable[s_Wave][prefix][x];
--Has units left on the table
if(s_WavesTable[s_Wave][prefix][x] > 0) then
if(ScenarioInfo.Options.opt_Survival_SpawnRate == 1) then
--Spawn at Once
UnitSpawn = s_WavesTable[s_Wave][prefix][x];
else
--Spawn over the Wave
UnitSpawn = math.ceil(s_WavesTable[s_Wave][prefix][x] / ScenarioInfo.Options.opt_Survival_WaveFrequency);
end
--Remaining Unit count from Wave Table
for y = 1, UnitSpawn do
--Get Random Blueprint from Unit Table
ref = s_UnitTables[prefix][x];
--Mod / Tier & Category / Type / Unit
if(ref ~= nil) then
--IF RNG mode, use RNG unit!
if(ScenarioInfo.Options.opt_Survival_Gamemode == "RNG") then
bp = GetRandomUnit(type);
else
bp = ref[math.random(1, table.getn(ref))];
end
if(bp ~= nil) then
--Spawn for Each player (list through all armies and find players)
for z, Army in ListArmies() do
--Make sure this is a player
if (ArmyToInt.GetInt(Army) > 0) then
MarkerData = GetSpawnMarker(type, ArmyToInt.GetInt(Army) , false);
position[1] = MarkerData[1].position[1];
position[2] = MarkerData[1].position[2];
position[3] = MarkerData[1].position[3];
--If random range is not zero
if(s_SpawnRadius ~= 0) then
position[1] = MarkerData[1].position[1] + math.random(-s_SpawnRadius, s_SpawnRadius);
position[3] = MarkerData[1].position[3] + math.random(-s_SpawnRadius, s_SpawnRadius);
end
table.insert(PlatoonList, SpawnUnit(bp, type, position));
--Give Orders
if(PlatoonList ~= nil and table.getn(PlatoonList) ~= 0) then
--AIR or Ranged
if(type == "AIR" or x == 4 or type == "AIRBOSS") then
order = 2; --Attack Move
elseif(bp == 'UAL0303') then --Hack fix for Reclaim unit
order = 1;
else
order = 1; --Move
end
PlatoonOrder("ARMY_SURVIVAL_ENEMY", PlatoonList, order, type, MarkerData[2]);
PlatoonList = {};
end
end
end
end
end
--Progress Wave
s_WavesTable[s_Wave][prefix][x] = s_WavesTable[s_Wave][prefix][x] - 1;
end
else
count = count + 1;
end
end
if(count >= table.getn(s_WavesTable[s_Wave][prefix])) then
return true;
end
else
return true;
end
return false;
end
SpawnUnit = function(blueprint, type, position, mods, boss, addHP, addSpeed, vet)
if(blueprint == nil or not __blueprints[blueprint:lower()]) then
LOG("-- Missing Blueprint: " .. blueprint);
return nil;
end