-
Notifications
You must be signed in to change notification settings - Fork 23
/
playerclasses.inc
1503 lines (1299 loc) · 58.9 KB
/
playerclasses.inc
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
/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: playerclasses.inc
* Type: Core
* Description: Provides functions for managing classes.
*
* Copyright (C) 2009-2013 Greyscale, Richard Helgeby
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ============================================================================
*/
/*
Ideas for immunity modes for humans:
- Zombies have to stab x times to infect.
- Zombies have to hurt humans so they loose hp. When the hp reach zero (or
below) they turn into zombies.
- Fully imune to all damage. Can't take or give damage. Sould only be used
on admin mode classes.
TODO: Make class attributes for for changing model render mode and colors.
TODO: Make class attributes for fancy effects, like a glow (if possible).
TODO: Make immunity settings suitable for both teams, or certain zombie-
only/human-only settings.
*/
/**
* Total number of classes that can be stored in each cache.
*/
#define ZR_CLASS_MAX 64
/**
* @section Class cache types. Specifies what data array to use.
*/
#define ZR_CLASS_CACHE_ORIGINAL 0 /** Points ClassData array. A cache that is never changed after loading. */
#define ZR_CLASS_CACHE_MODIFIED 1 /** Points to ClassDataCache array. Modified by admins or overrides. */
#define ZR_CLASS_CACHE_PLAYER 2 /** Points to ClassPlayerCache array. Current player attributes. */
/**
* @endsection
*/
/**
* Number of available class teams.
*/
#define ZR_CLASS_TEAMCOUNT 3
/**
* @section Available class teams. The admin team is optional and not required
* in class configs.
*/
#define ZR_CLASS_TEAM_ZOMBIES 0
#define ZR_CLASS_TEAM_HUMANS 1
#define ZR_CLASS_TEAM_ADMINS 2 /** Note: Will set you in a special mode where you don't really participates in the game, but just walk around. */
/**
* @endsection
*/
/**
* @section Damage immunity modes. The mode effects will vary depending on the
* class team.
*/
#define ZR_CLASS_IMMUNITY_DISABLED 0 /** No immunity. */
#define ZR_CLASS_IMMUNITY_CONSTANT 1 /** Always imune. Should only be used in special cases like on admin classes. */
#define ZR_CLASS_IMMUNITY_TIMED 2 /** Imune to damage for n seconds. The time is specified in a class as immunity amount. */
/**
* @endsection
*/
/**
* @section Flags for special classes.
*/
#define ZR_CLASS_FLAG_ADMIN_ONLY (1<<0) /** Class is usable by admins only. */
#define ZR_CLASS_FLAG_MOTHER_ZOMBIE (1<<1) /** Class is usable by mother zombies only. */
/** A combination of special class flags. Used to exclude special classes. */
#define ZR_CLASS_SPECIALFLAGS ZR_CLASS_FLAG_ADMIN_ONLY + ZR_CLASS_FLAG_MOTHER_ZOMBIE
/**
* @endsection
*/
/**
* @section Overall default class settings. Since this is a zombie plugin the
* default values represent a zombie.
*/
#define ZR_CLASS_DEFAULT_ENABLED "yes"
#define ZR_CLASS_DEFAULT_TEAM ZR_CLASS_TEAM_ZOMBIES
#define ZR_CLASS_DEFAULT_TEAM_DEFAULT "yes"
#define ZR_CLASS_DEFAULT_FLAGS 0
#define ZR_CLASS_DEFAULT_GROUP ""
#define ZR_CLASS_DEFAULT_NAME "classic"
#define ZR_CLASS_DEFAULT_DESCRIPTION "Need brains!!! Arrrrggghh!"
#define ZR_CLASS_DEFAULT_MODEL_PATH "models/player/zh/zh_zombie003.mdl"
#define ZR_CLASS_DEFAULT_MODEL_SKIN_INDEX 0
#define ZR_CLASS_DEFAULT_ALPHA_INITIAL 255
#define ZR_CLASS_DEFAULT_ALPHA_DAMAGED 255
#define ZR_CLASS_DEFAULT_ALPHA_DAMAGE 0
#define ZR_CLASS_DEFAULT_OVERLAY_PATH "overlays/zr/zvision"
#define ZR_CLASS_DEFAULT_NVGS "no"
#define ZR_CLASS_DEFAULT_FOV 90
#define ZR_CLASS_DEFAULT_HAS_NAPALM "no"
#define ZR_CLASS_DEFAULT_NAPALM_TIME 10.0
#define ZR_CLASS_DEFAULT_IMMUNITY_MODE "none"
#define ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT 1
#define ZR_CLASS_DEFAULT_IMMUNITY_COOLDOWN 60
#define ZR_CLASS_DEFAULT_NO_FALL_DAMAGE "yes"
#define ZR_CLASS_DEFAULT_HEALTH 6000
#define ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL 0.0
#define ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT 2
#define ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN 800
#define ZR_CLASS_DEFAULT_KILL_BONUS 2
#define ZR_CLASS_DEFAULT_SPEED 0.0
#define ZR_CLASS_DEFAULT_KNOCKBACK 2.0
#define ZR_CLASS_DEFAULT_JUMP_HEIGHT 10.0
#define ZR_CLASS_DEFAULT_JUMP_DISTANCE 0.2
/**
* @endsection
*/
/**
* @section Attribute limit values. Used when validating.
*/
#define ZR_CLASS_TEAM_MIN 0
#define ZR_CLASS_TEAM_MAX 2
#define ZR_CLASS_FLAGS_MIN 0
#define ZR_CLASS_FLAGS_MAX 3
#define ZR_CLASS_NAME_MIN 1
#define ZR_CLASS_DESCRIPTION_MIN 1
/** Model path is checked for existance. */
#define ZR_CLASS_MODEL_SKIN_INDEX_MIN 0
#define ZR_CLASS_ALPHA_INITIAL_MIN 0
#define ZR_CLASS_ALPHA_INITIAL_MAX 255
#define ZR_CLASS_ALPHA_DAMAGED_MIN 0
#define ZR_CLASS_ALPHA_DAMAGED_MAX 255
#define ZR_CLASS_ALPHA_DAMAGE_MIN 0
#define ZR_CLASS_ALPHA_DAMAGE_MAX 20000
/** Overlay path is optional, and file is checked for existance if specified. */
#define ZR_CLASS_FOV_MIN 15
#define ZR_CLASS_FOV_MAX 165
#define ZR_CLASS_NAPALM_TIME_MIN 0.0
#define ZR_CLASS_NAPALM_TIME_MAX 600.0
#define ZR_CLASS_IMMUNITY_COOLDOWN_MIN 0
#define ZR_CLASS_IMMUNITY_COOLDOWN_MAX 600
#define ZR_CLASS_HEALTH_MIN 1
#define ZR_CLASS_HEALTH_MAX 100000
#define ZR_CLASS_REGEN_INTERVAL_MIN 0.0
#define ZR_CLASS_REGEN_INTERVAL_MAX 900.0
#define ZR_CLASS_REGEN_AMOUNT_MIN 0
#define ZR_CLASS_REGEN_AMOUNT_MAX 10000
#define ZR_CLASS_HEALTH_INFECT_GAIN_MIN 0
#define ZR_CLASS_HEALTH_INFECT_GAIN_MAX 20000
#define ZR_CLASS_KILL_BONUS_MIN 0
#define ZR_CLASS_KILL_BONUS_MAX 16
#define ZR_CLASS_SPEED_LMV_MIN 10.0
#define ZR_CLASS_SPEED_LMV_MAX 2000.0
#define ZR_CLASS_SPEED_PROP_MIN -200.0
#define ZR_CLASS_SPEED_PROP_MAX 1750.0
#define ZR_CLASS_KNOCKBACK_MIN -30.0
#define ZR_CLASS_KNOCKBACK_MAX 30.0
#define ZR_CLASS_KNOCKBACK_IGNORE -31.0 /** Used by class editor volumetric feature. */
#define ZR_CLASS_JUMP_HEIGHT_MIN 0.0
#define ZR_CLASS_JUMP_HEIGHT_MAX 5.0
#define ZR_CLASS_JUMP_DISTANCE_MIN 0.0
#define ZR_CLASS_JUMP_DISTANCE_MAX 5.0
/**
* @endsection
*/
/**
* @section Class attribute flags.
*/
#define ZR_CLASS_ENABLED (1<<0)
#define ZR_CLASS_TEAM (1<<1)
#define ZR_CLASS_TEAM_DEFAULT (1<<2)
#define ZR_CLASS_FLAGS (1<<3)
#define ZR_CLASS_GROUP (1<<4)
#define ZR_CLASS_NAME (1<<5)
#define ZR_CLASS_DESCRIPTION (1<<6)
#define ZR_CLASS_MODEL_PATH (1<<7)
#define ZR_CLASS_MODEL_SKIN_INDEX (1<<8)
#define ZR_CLASS_ALPHA_INITIAL (1<<9)
#define ZR_CLASS_ALPHA_DAMAGED (1<<10)
#define ZR_CLASS_ALPHA_DAMAGE (1<<11)
#define ZR_CLASS_OVERLAY_PATH (1<<12)
#define ZR_CLASS_NVGS (1<<13)
#define ZR_CLASS_FOV (1<<14)
#define ZR_CLASS_HAS_NAPALM (1<<15)
#define ZR_CLASS_NAPALM_TIME (1<<16)
#define ZR_CLASS_IMMUNITY_MODE (1<<17)
#define ZR_CLASS_IMMUNITY_AMOUNT (1<<18)
#define ZR_CLASS_IMMUNITY_COOLDOWN (1<<19)
#define ZR_CLASS_NO_FALL_DAMAGE (1<<20)
#define ZR_CLASS_HEALTH (1<<21)
#define ZR_CLASS_HEALTH_REGEN_INTERVAL (1<<22)
#define ZR_CLASS_HEALTH_REGEN_AMOUNT (1<<23)
#define ZR_CLASS_HEALTH_INFECT_GAIN (1<<24)
#define ZR_CLASS_KILL_BONUS (1<<25)
#define ZR_CLASS_SPEED (1<<26)
#define ZR_CLASS_KNOCKBACK (1<<27)
#define ZR_CLASS_JUMP_HEIGHT (1<<28)
#define ZR_CLASS_JUMP_DISTANCE (1<<29)
/**
* @endsection
*/
/**
* Generic player attributes.
*
* Stuff that must be updated when new attributes are added:
* ZR_CLASS_DEFAULT_... define
* ZR_CLASS_..._MAX/MIN defines
* ZR_CLASS_... define (place in same order as listed in ClassAttributes, bump bit numbers + update numbers in docs)
* ClassLoad
* ClassReloadDataCache
* ClassReloadPlayerCache
* ClassDumpData
* attributes.inc - Add new Get-function
* ClassAttributeNameToFlag
* ClassGetAttributeType
* ClassValidateAttributes
* ClassModify* in classcommands.inc
* VolEmptyAttributes
* Update docs with detailed attribute description
*/
enum ClassAttributes
{
/* General */
bool:Class_Enabled,
Class_Team,
bool:Class_TeamDefault,
Class_Flags,
String:Class_Group[64],
String:Class_Name[64],
String:Class_Description[256],
/* Model */
String:Class_ModelPath[PLATFORM_MAX_PATH],
Class_ModelSkinIndex,
Class_AlphaInitial,
Class_AlphaDamaged,
Class_AlphaDamage,
/* Hud */
String:Class_OverlayPath[PLATFORM_MAX_PATH],
bool:Class_Nvgs,
Class_Fov,
/* Effects */
bool:Class_HasNapalm,
Float:Class_NapalmTime,
/* Player behaviour */
ImmunityMode:Class_ImmunityMode,
Class_ImmunityAmount,
Class_ImmunityCooldown,
bool:Class_NoFallDamage,
Class_Health,
Float:Class_HealthRegenInterval,
Class_HealthRegenAmount,
Class_HealthInfectGain,
Class_KillBonus,
Float:Class_Speed,
Float:Class_KnockBack,
Float:Class_JumpHeight,
Float:Class_JumpDistance
}
/**
* Structure of class attributes that are allowed to be modified directly,
* while the player is alive.
*
* Note: This structure is also used as a mask to tell if a individual
* attribute should be ignored or not. Negative valueas usually indicate
* ignored attributes. Booleans are now ints so they can be negative.
* Strings have reserved keywords like "nochange" that indicate a ignored
* attribute.
*/
enum ClassEditableAttributes
{
/* Model */
ClassEdit_ModelSkinIndex = 0,
ClassEdit_AlphaInitial,
ClassEdit_AlphaDamaged,
ClassEdit_AlphaDamage,
/* Hud */
String:ClassEdit_OverlayPath[PLATFORM_MAX_PATH],
ClassEdit_Nvgs,
ClassEdit_Fov,
/* Effects */
ClassEdit_HasNapalm,
Float:ClassEdit_NapalmTime,
/* Player behavior */
ImmunityMode:ClassEdit_ImmunityMode,
ClassEdit_ImmunityAmount,
ClassEdit_ImmunityCooldown,
ClassEdit_NoFallDamage,
Float:ClassEdit_RegenInterval,
ClassEdit_RegenAmount,
ClassEdit_InfectGain,
ClassEdit_KillBonus,
Float:ClassEdit_Speed,
Float:ClassEdit_KnockBack,
Float:ClassEdit_JumpHeight,
Float:ClassEdit_JumpDistance
}
/**
* Class attributes that support multipliers.
*/
enum ClassMultipliers
{
ClassM_Invalid = 0,
Float:ClassM_NapalmTime,
Float:ClassM_Health,
Float:ClassM_HealthRegenInterval,
Float:ClassM_HealthRegenAmount,
Float:ClassM_HealthInfectGain,
Float:ClassM_Speed,
Float:ClassM_Knockback,
Float:ClassM_JumpHeight,
Float:ClassM_JumpDistance
}
/**
* Available class teams, used to specify targets.
*/
enum ClassTeams
{
ClassTeam_Zombies = 0,
ClassTeam_Humans,
ClassTeam_Admins,
ClassTeam_All
}
/**
* Data types used in class attributes.
*/
enum ClassDataTypes
{
ClassDataType_InvalidType, /** Invalid type */
ClassDataType_Boolean, /** Boolean value */
ClassDataType_Integer, /** Integer value */
ClassDataType_Float, /** Floating point value */
ClassDataType_String /** String value */
}
/**
* Structure for class filter settings passed to various functions.
*/
enum ClassFilter
{
bool:ClassFilter_IgnoreEnabled, /** Ignore whether the class is disabled or not. */
ClassFilter_RequireFlags, /** Flags the classes must have set. */
ClassFilter_DenyFlags, /** Flags the classes cannot have set. */
ClassFilter_Client /** The client to check for class group permissions. Use 0 to ignore group filter and negative to exclude classes with groups set. */
}
/**
* Speed methods for applying player speed.
*/
enum ClassSpeedMethods
{
ClassSpeed_Invalid = -1,
ClassSpeed_LMV, /** Modifies lagged movement value. */
ClassSpeed_Prop, /** Modifies players' max speed property(m_flMaxspeed). */
}
/**
* Results when selecting a class for a player.
*/
enum ClassSelectResult
{
ClassSelected_NoChange, /** No class change was necessary (class already selected). */
ClassSelected_Instant, /** Class was instantly changed. */
ClassSelected_NextSpawn /** Class will be used next spawn. */
}
/**
* Empty filter structure.
*/
new ClassNoFilter[ClassFilter];
/**
* Filter structure for excluding special classes.
*/
new ClassNoSpecialClasses[ClassFilter] = {false, 0, ZR_CLASS_SPECIALFLAGS, -1};
/**
* The original class data. This array is only changed when class data is
* loaded. ZR_CLASS_CACHE_ORIGINAL is the cache type to this array.
*/
new ClassData[ZR_CLASS_MAX][ClassAttributes];
/**
* The class data cache that can be modified. ZR_CLASS_CACHE_MODIFIED is the
* cache type to this array.
*/
new ClassDataCache[ZR_CLASS_MAX][ClassAttributes];
/**
* Cache for player attributes. Makes it possible for one or more players to
* have custom attributes. ZR_CLASS_CACHE_PLAYER is the cache type to this
* array.
*/
new ClassPlayerCache[MAXPLAYERS + 1][ClassAttributes];
/**
* Cache for storing global multipliers, per team and per attribute when
* possible. Only attributes that support multipliers will be used, others are
* ignored.
*/
new Float:ClassMultiplierCache[ZR_CLASS_TEAMCOUNT][ClassMultipliers];
/**
* Number of classes loaded.
*/
new ClassCount;
/**
* Specifies whether the class team requirements and attributes are valid or not.
* Used to block events that happend before the module is done loading.
*/
new bool:ClassValidated;
/**
* Stores what class the player has selected, for each team.
*/
new ClassSelected[MAXPLAYERS + 1][ZR_CLASS_TEAMCOUNT];
/**
* Stores what class to be restored on next spawn, if available.
*/
new ClassSelectedNext[MAXPLAYERS + 1][ZR_CLASS_TEAMCOUNT];
/**
* Cache for the currently selected team (admin menus).
*/
new ClassAdminTeamSelected[MAXPLAYERS + 1];
/**
* Cookies for storing class indexes.
*/
new Handle:g_hClassCookieClassSelected[ZR_CLASS_TEAMCOUNT];
/**
* Cache for the currently selected attribute multiplier (admin menus).
*/
new ClassMultipliers:ClassAdminAttributeSelected[MAXPLAYERS + 1];
/**
* Specifies whether a player is currently in admin mode.
*/
new bool:ClassPlayerInAdminMode[MAXPLAYERS + 1];
/**
* Specifies whether a player is allowed to change class with instant effect.
* This is only used on human classes, and in combination with the
* zr_classes_change_timelimit time limit, but could be used other places too.
* The class menu will automatically check this setting and apply attributes if
* set to true.
*/
new bool:ClassAllowInstantChange[MAXPLAYERS + 1];
/**
* Cache for storing original model path before applying custom models. Used
* when restoring to old model.
*/
new String:ClassOriginalPlayerModel[MAXPLAYERS + 1][PLATFORM_MAX_PATH];
/**
* Specifies whether a player has spawned.
*/
new bool:ClassPlayerSpawned[MAXPLAYERS + 1];
/**
* What method used to apply speed on players.
*/
new ClassSpeedMethods:ClassSpeedMethod = ClassSpeed_Prop;
#include "zr/playerclasses/filtertools"
#include "zr/playerclasses/attributes"
#include "zr/playerclasses/apply"
#include "zr/playerclasses/clientoverlays"
#include "zr/playerclasses/clientalpha"
#include "zr/playerclasses/healthregen"
#include "zr/playerclasses/classevents"
#include "zr/playerclasses/classmenus"
#include "zr/playerclasses/classcommands"
/**
* Loads class attributes from the class file into ClassData array. If any
* error occur the plugin load will fail, and errors will be logged.
*
* @param keepMultipliers Optional. Don't reset multipliers. Default is
* false.
*/
ClassLoad()
{
// Register config file.
ConfigRegisterConfig(File_Classes, Structure_Keyvalue, CONFIG_FILE_ALIAS_CLASSES);
new Handle:kvClassData;
// Make sure kvClassData is ready to use.
kvClassData = CreateKeyValues(CONFIG_FILE_ALIAS_CLASSES);
// Get weapons config path.
decl String:pathclasses[PLATFORM_MAX_PATH];
new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_CLASSES, pathclasses);
// If file doesn't exist, then log and stop.
if (!exists)
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Missing playerclasses config file \"%s\"", pathclasses);
// Remove key/value cache.
CloseHandle(kvClassData);
kvClassData = INVALID_HANDLE;
return;
}
// Log what class file that is loaded.
LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Loading classes from file \"%s\".", pathclasses);
// Put file data into memory.
FileToKeyValues(kvClassData, pathclasses);
// Try to find the first class.
KvRewind(kvClassData);
if (!KvGotoFirstSubKey(kvClassData))
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Can't find any classes in \"%s\"", pathclasses);
}
new String:name[64];
new String:group[64];
new String:description[256];
new String:model_path[PLATFORM_MAX_PATH];
new String:immunity_mode[32];
new String:overlay_path[PLATFORM_MAX_PATH];
ClassCount = 0;
new failedcount;
new ClassErrorFlags;
// Loop through all classes and store attributes in the ClassData array.
do
{
if (ClassCount >= ZR_CLASS_MAX)
{
// Maximum classes reached. Write a warning and exit the loop.
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Maximum classes reached (%d). Skipping other classes.", ZR_CLASS_MAX + 1);
break;
}
/* General */
ClassData[ClassCount][Class_Enabled] = ConfigKvGetStringBool(kvClassData, "enabled", ZR_CLASS_DEFAULT_ENABLED);
ClassData[ClassCount][Class_Team] = KvGetNum(kvClassData, "team", ZR_CLASS_DEFAULT_TEAM);
ClassData[ClassCount][Class_TeamDefault] = ConfigKvGetStringBool(kvClassData, "team_default", ZR_CLASS_DEFAULT_TEAM_DEFAULT);
ClassData[ClassCount][Class_Flags] = KvGetNum(kvClassData, "flags", ZR_CLASS_DEFAULT_FLAGS);
KvGetString(kvClassData, "group", group, sizeof(group), ZR_CLASS_DEFAULT_GROUP);
strcopy(ClassData[ClassCount][Class_Group], 64, group);
KvGetString(kvClassData, "name", name, sizeof(name), ZR_CLASS_DEFAULT_NAME);
strcopy(ClassData[ClassCount][Class_Name], 64, name);
KvGetString(kvClassData, "description", description, sizeof(description), ZR_CLASS_DEFAULT_DESCRIPTION);
strcopy(ClassData[ClassCount][Class_Description], 256, description);
/* Model */
KvGetString(kvClassData, "model_path", model_path, sizeof(model_path), ZR_CLASS_DEFAULT_MODEL_PATH);
strcopy(ClassData[ClassCount][Class_ModelPath], PLATFORM_MAX_PATH, model_path);
ClassData[ClassCount][Class_ModelSkinIndex] = KvGetNum(kvClassData, "model_skin_index", ZR_CLASS_DEFAULT_MODEL_SKIN_INDEX);
ClassData[ClassCount][Class_AlphaInitial] = KvGetNum(kvClassData, "alpha_initial", ZR_CLASS_DEFAULT_ALPHA_INITIAL);
ClassData[ClassCount][Class_AlphaDamaged] = KvGetNum(kvClassData, "alpha_damaged", ZR_CLASS_DEFAULT_ALPHA_DAMAGED);
ClassData[ClassCount][Class_AlphaDamage] = KvGetNum(kvClassData, "alpha_damage", ZR_CLASS_DEFAULT_ALPHA_DAMAGE);
/* Hud */
KvGetString(kvClassData, "overlay_path", overlay_path, sizeof(overlay_path), ZR_CLASS_DEFAULT_OVERLAY_PATH);
strcopy(ClassData[ClassCount][Class_OverlayPath], PLATFORM_MAX_PATH, overlay_path);
ClassData[ClassCount][Class_Nvgs] = ConfigKvGetStringBool(kvClassData, "nvgs", ZR_CLASS_DEFAULT_NVGS);
ClassData[ClassCount][Class_Fov] = KvGetNum(kvClassData, "fov", ZR_CLASS_DEFAULT_FOV);
/* Effects */
ClassData[ClassCount][Class_HasNapalm] = ConfigKvGetStringBool(kvClassData, "has_napalm", ZR_CLASS_DEFAULT_HAS_NAPALM);
ClassData[ClassCount][Class_NapalmTime] = KvGetFloat(kvClassData, "napalm_time", ZR_CLASS_DEFAULT_NAPALM_TIME);
/* Player behaviour */
KvGetString(kvClassData, "immunity_mode", immunity_mode, sizeof(immunity_mode), ZR_CLASS_DEFAULT_IMMUNITY_MODE);
ClassData[ClassCount][Class_ImmunityMode] = ImmunityStringToMode(immunity_mode);
ClassData[ClassCount][Class_ImmunityAmount] = KvGetNum(kvClassData, "immunity_amount", ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT);
ClassData[ClassCount][Class_ImmunityCooldown] = KvGetNum(kvClassData, "immunity_cooldown", ZR_CLASS_DEFAULT_IMMUNITY_COOLDOWN);
ClassData[ClassCount][Class_NoFallDamage] = ConfigKvGetStringBool(kvClassData, "no_fall_damage", ZR_CLASS_DEFAULT_NO_FALL_DAMAGE);
ClassData[ClassCount][Class_Health] = KvGetNum(kvClassData, "health", ZR_CLASS_DEFAULT_HEALTH);
ClassData[ClassCount][Class_HealthRegenInterval] = KvGetFloat(kvClassData, "health_regen_interval", ZR_CLASS_DEFAULT_HEALTH_REGEN_INTERVAL);
ClassData[ClassCount][Class_HealthRegenAmount] = KvGetNum(kvClassData, "health_regen_amount", ZR_CLASS_DEFAULT_HEALTH_REGEN_AMOUNT);
ClassData[ClassCount][Class_HealthInfectGain] = KvGetNum(kvClassData, "health_infect_gain", ZR_CLASS_DEFAULT_HEALTH_INFECT_GAIN);
ClassData[ClassCount][Class_KillBonus] = KvGetNum(kvClassData, "kill_bonus", ZR_CLASS_DEFAULT_KILL_BONUS);
ClassData[ClassCount][Class_Speed] = KvGetFloat(kvClassData, "speed", ZR_CLASS_DEFAULT_SPEED);
ClassData[ClassCount][Class_KnockBack] = KvGetFloat(kvClassData, "knockback", ZR_CLASS_DEFAULT_KNOCKBACK);
ClassData[ClassCount][Class_JumpHeight] = KvGetFloat(kvClassData, "jump_height", ZR_CLASS_DEFAULT_JUMP_HEIGHT);
ClassData[ClassCount][Class_JumpDistance] = KvGetFloat(kvClassData, "jump_distance", ZR_CLASS_DEFAULT_JUMP_DISTANCE);
// Validate class attributes if class is enabled.
if (ClassData[ClassCount][Class_Enabled])
{
ClassErrorFlags = ClassValidateAttributes(ClassCount, true);
if (ClassErrorFlags > 0)
{
// There's one or more invalid class attributes. Disable the class
// and log an error message.
ClassData[ClassCount][Class_Enabled] = false;
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Warning: Invalid class at index %d, disabled class. Class error flags: %d.", ClassCount, ClassErrorFlags);
failedcount++;
}
}
// Update the counter.
ClassCount++;
} while (KvGotoNextKey(kvClassData));
// Validate team requirements.
if (!ClassValidateTeamRequirements())
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "The class configuration doesn't match the team requirements.");
}
// Validate team default requirements.
if (!ClassValidateTeamDefaults())
{
LogEvent(false, LogType_Fatal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Couldn't find a default class for one or more teams. At least one class per team must be marked as default.");
}
// Cache class data.
ClassReloadDataCache();
// Reset selected class indexes for next spawn.
ClassResetNextIndexes();
// Mark classes as valid.
ClassValidated = true;
// Log summary.
LogEvent(false, LogType_Normal, LOG_CORE_EVENTS, LogModule_Playerclasses, "Config Validation", "Total: %d | Successful: %d | Unsuccessful: %d", ClassCount, ClassCount - failedcount, failedcount);
// Set config data.
ConfigSetConfigLoaded(File_Classes, true);
ConfigSetConfigReloadFunc(File_Classes, GetFunctionByName(GetMyHandle(), "ClassOnConfigReload"));
ConfigSetConfigPath(File_Classes, pathclasses);
// Remove key/value cache.
CloseHandle(kvClassData);
kvClassData = INVALID_HANDLE;
}
/**
* Called when configs are being reloaded.
*
* @param config The config being reloaded. (only if 'all' is false)
*/
public ClassOnConfigReload(ConfigFile:config)
{
// Reload class config.
ClassLoad();
}
/**
* Gets the speed method.
*
* @return Speed method, or ClassSpeed_Invalid on error.
*/
ClassSpeedMethods:ClassGetSpeedMethod()
{
decl String:speedMethod[16];
speedMethod[0] = 0;
GetConVarString(g_hCvarsList[CVAR_CLASSES_SPEED_METHOD], speedMethod, sizeof(speedMethod));
if (StrEqual(speedMethod, "lmv", false))
{
return ClassSpeed_LMV;
}
else if (StrEqual(speedMethod, "prop", false))
{
return ClassSpeed_Prop;
}
return ClassSpeed_Invalid;
}
/**
* Updates the class data cache. Original values are retrieved from ClassData.
*
* @return True on success, false otherwise.
*/
bool:ClassReloadDataCache()
{
// Check if there are no classes.
if (ClassCount == 0)
{
return false;
}
// Loop through all classes.
for (new classindex = 0; classindex < ClassCount; classindex++)
{
/* General */
ClassDataCache[classindex][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassDataCache[classindex][Class_Team] = ClassData[classindex][Class_Team];
ClassDataCache[classindex][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault];
ClassDataCache[classindex][Class_Flags] = ClassData[classindex][Class_Flags];
strcopy(ClassDataCache[classindex][Class_Group], 64, ClassData[classindex][Class_Group]);
strcopy(ClassDataCache[classindex][Class_Name], 64, ClassData[classindex][Class_Name]);
strcopy(ClassDataCache[classindex][Class_Description], 256, ClassData[classindex][Class_Description]);
/* Model */
strcopy(ClassDataCache[classindex][Class_ModelPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_ModelPath]);
ClassDataCache[classindex][Class_ModelSkinIndex] = ClassData[classindex][Class_ModelSkinIndex];
ClassDataCache[classindex][Class_AlphaInitial] = ClassData[classindex][Class_AlphaInitial];
ClassDataCache[classindex][Class_AlphaDamaged] = ClassData[classindex][Class_AlphaDamaged];
ClassDataCache[classindex][Class_AlphaDamage] = ClassData[classindex][Class_AlphaDamage];
/* Hud */
strcopy(ClassDataCache[classindex][Class_OverlayPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_OverlayPath]);
ClassDataCache[classindex][Class_Nvgs] = ClassData[classindex][Class_Nvgs];
ClassDataCache[classindex][Class_Fov] = ClassData[classindex][Class_Fov];
/* Effects */
ClassDataCache[classindex][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm];
ClassDataCache[classindex][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime];
/* Player behavior */
ClassDataCache[classindex][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode];
ClassDataCache[classindex][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount];
ClassDataCache[classindex][Class_ImmunityCooldown] = ClassData[classindex][Class_ImmunityCooldown];
ClassDataCache[classindex][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage];
ClassDataCache[classindex][Class_Health] = ClassData[classindex][Class_Health];
ClassDataCache[classindex][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval];
ClassDataCache[classindex][Class_HealthRegenAmount] = ClassData[classindex][Class_HealthRegenAmount];
ClassDataCache[classindex][Class_HealthInfectGain] = ClassData[classindex][Class_HealthInfectGain];
ClassDataCache[classindex][Class_KillBonus] = ClassData[classindex][Class_KillBonus];
ClassDataCache[classindex][Class_Speed] = ClassData[classindex][Class_Speed];
ClassDataCache[classindex][Class_KnockBack] = ClassData[classindex][Class_KnockBack];
ClassDataCache[classindex][Class_JumpHeight] = ClassData[classindex][Class_JumpHeight];
ClassDataCache[classindex][Class_JumpDistance] = ClassData[classindex][Class_JumpDistance];
}
return true;
}
/**
* Refresh the specified player's cache from the specified class data cache.
*
* @param client The client index.
* @param classindex The index of the class to read from.
* @param cachetype Optional. Specifies what class cache to read from.
* Options: ZR_CLASS_CACHE_ORIGINAL (unchanged class
* data), ZR_CLASS_CACHE_MODIFIED (default, modified class
* data).
* @return True if successful, false otherwise.
*/
bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIFIED)
{
// Validate indexes.
if (!ClassValidateIndex(classindex) || !ZRIsClientValid(client))
{
return false;
}
switch (cachetype)
{
case ZR_CLASS_CACHE_ORIGINAL:
{
/* General */
ClassPlayerCache[client][Class_Enabled] = ClassData[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassData[classindex][Class_Team];
ClassPlayerCache[client][Class_TeamDefault] = ClassData[classindex][Class_TeamDefault];
ClassPlayerCache[client][Class_Flags] = ClassData[classindex][Class_Flags];
strcopy(ClassPlayerCache[client][Class_Group], 64, ClassData[classindex][Class_Group]);
strcopy(ClassPlayerCache[client][Class_Name], 64, ClassData[classindex][Class_Name]);
strcopy(ClassPlayerCache[client][Class_Description], 256, ClassData[classindex][Class_Description]);
/* Model */
strcopy(ClassPlayerCache[client][Class_ModelPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_ModelPath]);
ClassPlayerCache[client][Class_ModelSkinIndex] = ClassData[classindex][Class_ModelSkinIndex];
ClassPlayerCache[client][Class_AlphaInitial] = ClassData[classindex][Class_AlphaInitial];
ClassPlayerCache[client][Class_AlphaDamaged] = ClassData[classindex][Class_AlphaDamaged];
ClassPlayerCache[client][Class_AlphaDamage] = ClassData[classindex][Class_AlphaDamage];
/* Hud */
strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, ClassData[classindex][Class_OverlayPath]);
ClassPlayerCache[client][Class_Nvgs] = ClassData[classindex][Class_Nvgs];
ClassPlayerCache[client][Class_Fov] = ClassData[classindex][Class_Fov];
/* Effects */
ClassPlayerCache[client][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm];
ClassPlayerCache[client][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime];
/* Player behavior */
ClassPlayerCache[client][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode];
ClassPlayerCache[client][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount];
ClassPlayerCache[client][Class_ImmunityCooldown] = ClassData[classindex][Class_ImmunityCooldown];
ClassPlayerCache[client][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage];
ClassPlayerCache[client][Class_Health] = ClassData[classindex][Class_Health];
ClassPlayerCache[client][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval];
ClassPlayerCache[client][Class_HealthRegenAmount] = ClassData[classindex][Class_HealthRegenAmount];
ClassPlayerCache[client][Class_HealthInfectGain] = ClassData[classindex][Class_HealthInfectGain];
ClassPlayerCache[client][Class_KillBonus] = ClassData[classindex][Class_KillBonus];
ClassPlayerCache[client][Class_Speed] = ClassData[classindex][Class_Speed];
ClassPlayerCache[client][Class_KnockBack] = ClassData[classindex][Class_KnockBack];
ClassPlayerCache[client][Class_JumpHeight] = ClassData[classindex][Class_JumpHeight];
ClassPlayerCache[client][Class_JumpDistance] = ClassData[classindex][Class_JumpDistance];
}
case ZR_CLASS_CACHE_MODIFIED:
{
/* General */
ClassPlayerCache[client][Class_Enabled] = ClassDataCache[classindex][Class_Enabled];
ClassPlayerCache[client][Class_Team] = ClassDataCache[classindex][Class_Team];
ClassPlayerCache[client][Class_TeamDefault] = ClassDataCache[classindex][Class_TeamDefault];
ClassPlayerCache[client][Class_Flags] = ClassDataCache[classindex][Class_Flags];
strcopy(ClassPlayerCache[client][Class_Group], 64, ClassDataCache[classindex][Class_Group]);
strcopy(ClassPlayerCache[client][Class_Name], 64, ClassDataCache[classindex][Class_Name]);
strcopy(ClassPlayerCache[client][Class_Description], 256, ClassDataCache[classindex][Class_Description]);
/* Model */
strcopy(ClassPlayerCache[client][Class_ModelPath], PLATFORM_MAX_PATH, ClassDataCache[classindex][Class_ModelPath]);
ClassPlayerCache[client][Class_ModelSkinIndex] = ClassDataCache[classindex][Class_ModelSkinIndex];
ClassPlayerCache[client][Class_AlphaInitial] = ClassDataCache[classindex][Class_AlphaInitial];
ClassPlayerCache[client][Class_AlphaDamaged] = ClassDataCache[classindex][Class_AlphaDamaged];
ClassPlayerCache[client][Class_AlphaDamage] = ClassDataCache[classindex][Class_AlphaDamage];
/* Hud */
strcopy(ClassPlayerCache[client][Class_OverlayPath], PLATFORM_MAX_PATH, ClassDataCache[classindex][Class_OverlayPath]);
ClassPlayerCache[client][Class_Nvgs] = ClassDataCache[classindex][Class_Nvgs];
ClassPlayerCache[client][Class_Fov] = ClassDataCache[classindex][Class_Fov];
/* Effects */
ClassPlayerCache[client][Class_HasNapalm] = ClassDataCache[classindex][Class_HasNapalm];
ClassPlayerCache[client][Class_NapalmTime] = ClassDataCache[classindex][Class_NapalmTime];
/* Player behavior */
ClassPlayerCache[client][Class_ImmunityMode] = ClassDataCache[classindex][Class_ImmunityMode];
ClassPlayerCache[client][Class_ImmunityAmount] = ClassDataCache[classindex][Class_ImmunityAmount];
ClassPlayerCache[client][Class_ImmunityCooldown] = ClassDataCache[classindex][Class_ImmunityCooldown];
ClassPlayerCache[client][Class_NoFallDamage] = ClassDataCache[classindex][Class_NoFallDamage];
ClassPlayerCache[client][Class_Health] = ClassDataCache[classindex][Class_Health];
ClassPlayerCache[client][Class_HealthRegenInterval] = ClassDataCache[classindex][Class_HealthRegenInterval];
ClassPlayerCache[client][Class_HealthRegenAmount] = ClassDataCache[classindex][Class_HealthRegenAmount];
ClassPlayerCache[client][Class_HealthInfectGain] = ClassDataCache[classindex][Class_HealthInfectGain];
ClassPlayerCache[client][Class_KillBonus] = ClassDataCache[classindex][Class_KillBonus];
ClassPlayerCache[client][Class_Speed] = ClassDataCache[classindex][Class_Speed];
ClassPlayerCache[client][Class_KnockBack] = ClassDataCache[classindex][Class_KnockBack];
ClassPlayerCache[client][Class_JumpHeight] = ClassDataCache[classindex][Class_JumpHeight];
ClassPlayerCache[client][Class_JumpDistance] = ClassDataCache[classindex][Class_JumpDistance];
}
default:
{
// Invalid cache specified.
return false;
}
}
return true;
}
/**
* Refresh the specified player's cache and re-apply attributes.
*
* @param client The client index.
* @return True if successful, false otherwise.
*/
bool:ClassReloadPlayer(client)
{
new activeclass;
// Get active class index.
activeclass = ClassGetActiveIndex(client);
// Validate index.
if (activeclass < 0)
{
return false;
}
// Refresh cache and re-apply attributes.
ClassOnClientDeath(client); // Dummy event to clean up and turn off stuff.
ClassReloadPlayerCache(client, activeclass);
ClassApplyAttributes(client);
return true;
}
/**
* Reset all class attribute multipliers to 1.0.
*/
ClassResetMultiplierCache()
{
// Loop through all teams.
for (new teamid = 0; teamid < ZR_CLASS_TEAMCOUNT; teamid++)
{
ClassMultiplierCache[teamid][ClassM_NapalmTime] = 1.0;
ClassMultiplierCache[teamid][ClassM_Health] = 1.0;
ClassMultiplierCache[teamid][ClassM_HealthRegenInterval] = 1.0;
ClassMultiplierCache[teamid][ClassM_HealthRegenAmount] = 1.0;
ClassMultiplierCache[teamid][ClassM_HealthInfectGain] = 1.0;
ClassMultiplierCache[teamid][ClassM_Speed] = 1.0;
ClassMultiplierCache[teamid][ClassM_Knockback] = 1.0;
ClassMultiplierCache[teamid][ClassM_JumpHeight] = 1.0;
ClassMultiplierCache[teamid][ClassM_JumpDistance] = 1.0;
}
}
/**
* Resets the selected class indexes for next spawn on one or all clients.
*
* @param client Optional. Specify client to reset. Default is all.
*/
ClassResetNextIndexes(client = -1)
{
new teamid;
if (client > 0)
{
for (teamid = 0; teamid < ZR_CLASS_TEAMCOUNT; teamid++)
{
ClassSelectedNext[client][teamid] = -1;
}
}
else
{
for (client = 1; client <= MAXPLAYERS; client++)
{
for (teamid = 0; teamid < ZR_CLASS_TEAMCOUNT; teamid++)
{
ClassSelectedNext[client][teamid] = -1;
}
}
}
}
/**
* Restores next class indexes on a player, if available.
* Note: Does not apply attributes. The classes are only marked as selected.
*
* @param client The client index.
* @param excludeTeam Optional. Do not restore the specified team.
*/
ClassRestoreNextIndexes(client, excludeTeam = -1)
{
// Get next class indexes.
new zombie = ClassSelectedNext[client][ZR_CLASS_TEAM_ZOMBIES];
new human = ClassSelectedNext[client][ZR_CLASS_TEAM_HUMANS];
new admin = ClassSelectedNext[client][ZR_CLASS_TEAM_ADMINS];
// Check if the zombie team should be excluded.
if (excludeTeam != ZR_CLASS_TEAM_ZOMBIES)
{
// Validate zombie class index.
if (ClassValidateIndex(zombie))
{
// Mark next zombie class as selected.
ClassSelected[client][ZR_CLASS_TEAM_ZOMBIES] = zombie;
}