· 6 years ago · Jan 31, 2020, 03:30 PM
1/*================================================================================
2
3 *****************************************************
4 ************** [Zombie Plague Mod 4.3] **************
5 *****************************************************
6
7 ----------------------
8 -*- Licensing Info -*-
9 ----------------------
10
11 Zombie Plague Mod
12 Copyright (C) 2008-2009 by MeRcyLeZZ
13
14 This program is free software: you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
26
27 In addition, as a special exception, the author gives permission to
28 link the code of this program with the Half-Life Game Engine ("HL
29 Engine") and Modified Game Libraries ("MODs") developed by Valve,
30 L.L.C ("Valve"). You must obey the GNU General Public License in all
31 respects for all of the code used other than the HL Engine and MODs
32 from Valve. If you modify this file, you may extend this exception
33 to your version of the file, but you are not obligated to do so. If
34 you do not wish to do so, delete this exception statement from your
35 version.
36
37 -------------------
38 -*- Description -*-
39 -------------------
40
41 Zombie Plague is a Counter-Strike server side modification, developed as
42 an AMX Mod X plugin, which completely revamps the gameplay, turning the
43 game into an intense "Humans vs Zombies" survival experience.
44
45 Even though it's strongly based on the classic zombie infection mods, it
46 takes the concept to a new level by introducing:
47
48 * New Gameplay Modes: Nemesis, Survivor, Multi Infection, Swarm, Plague
49 * Zombie Classes System: allows addding unlimited custom zombie classes
50 * Ammo Packs: awarded to skilled players, can be exchanged for goods
51 * Extra Items System: allows adding unlimited custom items to buy
52 * Custom Grenades: Napalms, Frost Nades, Flares, and Infection Bombs
53 * Deathmatch Mode: where zombies or humans can continually respawn
54 * Admin Menus: to easily perform the included console commands
55 * Special Effects: from the HL Engine, such as dynamic lighting and fog
56
57 There is plenty of customization as well, which enables you to create
58 several different styles of gameplay. You can:
59
60 * Set zombies and humans' health, speed, models, rewards, and more
61 * Toggle unlimited ammo and adjustable knockback for weapons
62 * Separately enable and customize the new gameplay modes to your liking
63 * Change overall map lighting (lightnings available for the dark settings)
64 * Set different colors and sizes for flashlight and nightvision
65 * Toggle leap (long jumping) and pain shock free (no damage slowdowns)
66 * Toggle various infection effects, such as sparks and screen shakes
67 * Enable random spawning (CSDM-spawn friendly)
68 * Replace sounds or add some background themes
69 * And many more...
70
71 -------------
72 -*- Media -*-
73 -------------
74
75 * Gameplay Video 1: http://www.youtube.com/watch?v=HFUyF7-_uzw
76 * Gameplay Video 2: http://www.youtube.com/watch?v=XByif6Mti-w
77
78 --------------------
79 -*- Requirements -*-
80 --------------------
81
82 * Mods: Counter-Strike 1.6 or Condition-Zero
83 * AMXX: Version 1.8.0 or later
84
85 --------------------
86 -*- Installation -*-
87 --------------------
88
89 Extract the contents from the .zip file to your server's mod directory
90 ("cstrike" or "czero"). Make sure to keep folder structure.
91
92 -----------------------
93 -*- Official Forums -*-
94 -----------------------
95
96 For the official Zombie Plague forums visit:
97 http://forums.alliedmods.net/forumdisplay.php?f=126
98
99 There you can:
100
101 * Get the latest releases and early betas
102 * Discuss new features and suggestions
103 * Share sub-plugins (expansions) for the mod
104 * Find the support and help you need
105 * Report any bugs you might find
106 * And all that sort of stuff...
107
108 -------------------------------
109 -*- CVARS and Customization -*-
110 -------------------------------
111
112 For a complete and in-depth cvar list, look at the zombieplague.cfg file
113 located in the amxmodx\configs directory.
114
115 Additionally, you can change player models, sounds, weather effects,
116 and some other stuff from the configuration file zombieplague.ini.
117
118 As for editing attributes of zombie classes or custom extra items, you'll
119 find a zp_zombieclasses.ini and zp_extraitems.ini. These files will be
120 automatically updated as you install new custom classes or items with
121 new entries for you to edit conveniently.
122
123 ---------------
124 -*- History -*-
125 ---------------
126
127 This project started back on late 2007, when the free infection mods
128 around were quite buggy and I wanted to make one on my own. With little
129 to no experience at AMXX scripting, I had to start from the very scratch.
130
131 Not after spending over a week looking at many plugins (mainly Zombie
132 Swarm) and scarce tutorials, I somehow managed to have all the basic
133 stuff working quite well (even though the code was a real mess). The
134 following months were spent polishing things up and trying to fulfill
135 new original ideas, most of them quite worth the hard work.
136
137 In the meantime, I got the chance to try the plugin out on a 32 man
138 server. This meant a huge progress on development, and after lots of
139 testing and bug fixing, the mod turned out to be more than the simple
140 infection plugin I had originally planned it to be.
141
142 The project has come a long way since, and I'm glad to say I'm finally
143 making it freely available. All I'm asking in return is to keep my
144 name in the plugin.
145
146 -Enjoy!
147
148 ----------------------
149 -*- Infection Mode -*-
150 ----------------------
151
152 On every round players start out as humans, equip themselves with a few
153 weapons and grenades, and head to the closest cover they find, knowing
154 that one of them is infected with the T-Virus, and will suddenly turn
155 into a vicious brain eating creature.
156
157 Only little time after, the battle for survival begins. The first zombie
158 has to infect as many humans as possible to cluster a numerous zombie
159 horde and take over the world.
160
161 Maps are set in the dark by default. Humans must use flashlights to light
162 their way and spot any enemies. Zombies, on the other hand, have night
163 vision but can only attack melee.
164
165 --------------------------
166 -*- New Gameplay Modes -*-
167 --------------------------
168
169 * Nemesis:
170 The first zombie may turn into a Nemesis, a powerful fast-moving
171 beast. His goal is to kill every human while sustaining the gunfire.
172
173 * Survivor:
174 Everyone became a zombie except him. The survivor gets a machinegun
175 with unlimited ammo and has to stop the never-ending army of undead.
176
177 * Multiple Infection:
178 The round starts with many humans infected, so the remaining players
179 will have to act quickly in order to control the situation.
180
181 * Swarm Mode:
182 Half of the players turn into zombies, the rest become immune and
183 cannot be infected. It's a battle to death.
184
185 * Plague Mode: [bonus]
186 A full armed Survivor and his soldiers are to face Nemesis and
187 his zombie minions. The future of the world is in their hands.
188
189 --------------------
190 -*- In-Game Menu -*-
191 --------------------
192
193 Players can access the mod menu by typing "zpmenu" on chat, or by
194 pressing the M ("chooseteam") key. The menu allows players to choose
195 their zombie class, buy extra items, get unstuck, or see the ingame
196 help. Admins will find an additional option to easily perform all
197 console commands.
198
199 ----------------------
200 -*- Admin Commands -*-
201 ----------------------
202
203 The following console commands are available:
204
205 * zp_zombie <target> - Turn someone into a Zombie
206 * zp_human <target> - Turn someone back to Human
207 * zp_nemesis <target> - Turn someone into a Nemesis
208 * zp_survivor <target> - Turn someone into a Survivor
209 * zp_respawn <target> - Respawn someone
210 * zp_swarm - Start Swarm Mode (*)
211 * zp_multi - Start Multi Infection (*)
212 * zp_plague - Start Plague Mode (*)
213
214 (*) - These commands can only be used at round start, that is, when the
215 T-Virus notice is shown on screen.
216
217 ------------------
218 -*- Plugin API -*-
219 ------------------
220
221 From version 3.6, some natives and forwards have been added to ease the
222 development of sub-plugins, though you may also find them useful to work
223 out compatibility issues with existing plugins.
224
225 Look for the zombieplague.inc file in your amxmodx\scripting\include
226 folder for the full documented list.
227
228 ----------------------
229 -*- Zombie Classes -*-
230 ----------------------
231
232 From version 4.0 it is possible to create and add an unlimited number of
233 zombie classes to the main mod. They can be made as separate plugins,
234 by using the provided zombie class API, and easily distributed.
235
236 By default, five zombie classes are included:
237
238 * Classic Zombie: well balanced zombie for beginners.
239 * Raptor Zombie: fast moving zombie, but also the weakest.
240 * Poison Zombie: light weighed zombie, jumps higher.
241 * Big Zombie: slow but strong zombie, with lots of hit points.
242 * Leech Zombie: regains additional health when infecting.
243
244 -------------------
245 -*- Extra Items -*-
246 -------------------
247
248 From version 4.0 it is possible to add an unlimited number of items
249 which can be purchased through the Extra Items menu. All you need
250 to do is use the provided item registration natives on your custom
251 plugins. You can set the name, the cost in ammo packs, and the team
252 the extra item should be available for.
253
254 By default there is a number of items already included, listed here:
255
256 * Night Vision: makes you able to see in the dark for a single round [Human]
257 * T-Virus Antidote: makes you turn back to your human form [Zombie]
258 * Zombie Madness: you develop a powerful shield for a short time [Zombie]
259 * Infection Bomb: infects anyone within its explosion radius [Zombie]
260
261 You are also able to choose some weapons to act as extra items, and change
262 ammo packs costs in the customization file (zombieplague.ini).
263
264 ---------------
265 -*- Credits -*-
266 ---------------
267
268 * AMXX Dev Team: for all the hard work which made this possible
269 * Imperio LNJ Community: for providing the first server where I
270 could really test the plugin and for everyone's support
271 * Mini_Midget: for his Zombie Swarm plugin which I used for reference
272 on earliest stages of development
273 * Avalanche: for the random spawning code I got from GunGame and the
274 original Frostnades concept that I ported in here
275 * cheap_suit: for some modelchange and knockback codes that I got from
276 Biohazard
277 * Simon Logic/ConnorMcLeod: for the Pain Shock Free feature
278 * KRoT@L: for some code from Follow the Wounded, used to make the zombie
279 bleeding feature
280 * VEN: for Fakemeta Utilities and some useful stocks
281 * RaaPuar and Goltark: for the custom grenade models
282 * Orangutanz: for finding the precached modelindex offset
283 * ML Translations: DKs/nunoabc/DarkMarcos (bp), JahMan/KWo (pl), DA (de),
284 Zombie Lurker (ls), DoPe^ (da), k1nny (fr), NeWbiE' (cz), skymoon (tc),
285 SUPER MATRIX/Shidla/zDemon/4eRT (ru), zsy314 (cn), lOlIl/Seehank (sk),
286 Bridgestone (sv), crazyeffect.net/Mave/Wesley (nl), hleV/aaarnas (lt),
287 darkbad945 (bg), decongamco (vn), beckham9224 (mn), TehGeorge (gr),
288 shadoww_ro/tuty/georgik57/EastSider (ro)
289 * Beta testers: for all the feedback, bug reports, and suggestions which
290 constantly help improve this mod further
291 * And to all zombie-mod supporters out there!
292
293 -----------------
294 -*- Changelog -*-
295 -----------------
296
297 * v1.0: (Dec 2007)
298 - First Release: most of the basic stuff done.
299 - Added: random spawning, HP display on hud, lighting setting,
300 simple buy menu, custom nightvision, admin commands, Nemesis
301 and Survivor modes, glow and leap settings for them.
302
303 * v2.2: (Jan 2008)
304 - Added: zombie classes, ammo packs system, buying ammo for weapons,
305 custom flashlight, admin skins setting, zombieplague.cfg file
306 - Upgraded: weapons menu improved, flashlight and nightvision colors
307 now customizable, HamSandwich module used to handle damage.
308 - Fixed various bugs.
309
310 * v3.0: (Mar 2008)
311 - Added: door removal setting, unstuck feature, human cvars, armor
312 cvar for zombies, weapon knockback, zombie bleeding, flares,
313 extra items (weapons, antidote, infection bomb), pain shock
314 free setting, Multiple Infection and Swarm modes.
315 - Upgraded: dumped Engine, Fun and Cstrike modules, code optimized,
316 new model change method, new gfx effects for zombie infections.
317 - Fixed a bunch of gameplay bugs.
318
319 * v3.5: (May 2008)
320 - Added: deathmatch setting with spawn protection, unlimited ammo
321 setting, fire and frost grenades, additional customization cvars,
322 new extra items, help menu.
323 - Upgraded: better objectives removal method, dropped weapons now
324 keep their bpammo, code optimized a lot.
325 - Fixed: no more game commencing bug when last zombie/human leaves,
326 no more hegrenade infection bug, reduced svc_bad errors, and
327 many more.
328
329 * v3.6: (Jun 2008)
330 - Added: a few natives and forwards for sub-plugins support,
331 zombie classes can now have their own models, additional
332 knockback customization, bot support, various CVARs.
333 - Upgraded: extra items now supporting grenades and pistols, changed
334 bomb removal method, players can join on survivor/swarm rounds,
335 extended lightnings support to other dark settings.
336 - Fixed: a bunch of minor bugs, and a server crash with CZ bots.
337
338 * v4.0: (Aug 2008)
339 - Added: new gameplay mode (Plague Mode), option to remember weapon
340 selection, command to enable/disable the plugin, more CVARs.
341 - Upgraded: redid all the menus, extra items and zombie classes now
342 support external additions, survivor can now have its own model,
343 upgraded model changing method.
344 - Fixed: some bugs with bots, win sounds not being precached.
345
346 * v4.1: (Oct 2008)
347 - Added: more CVARs, more customization, more natives, custom
348 leap system, admin zombie models support, and more.
349 - Upgraded: custom grenades compatible with Nade Modes, ambience
350 sounds specific game mode support, optimized bandwidth usage
351 for temp ents, admin commands logged with IP and SteamID.
352 - Fixed: lots of bugs (some minor, some not)
353
354 * v4.2: (Feb 2009)
355 - Added various CVARs for customization, improved prevention of
356 svc_bad in some cases, optimized ammo handling code.
357 - Fixed server crash with 'msg 35 has not been sent yet' error,
358 fixed client overflow issues with ambience sounds, resolved
359 many gameplay bugs.
360
361 * v4.3: (Apr 2009)
362 - Customization settings can now be edited through external files,
363 added support for global and multiple random zombie models,
364 added even more CVARs for tweaking stuff, extended admin commands'
365 functionality, greatly extended API capabilities, implemented a
366 more efficient Pain Shock Free code, reworked some menus.
367 - Fixed pretty much all reported bugs to the date.
368
369=================================================================================*/
370
371/*================================================================================
372 [Plugin Customization]
373=================================================================================*/
374
375// All customization settings have been moved
376// to external files to allow easier editing
377new const ZP_CUSTOMIZATION_FILE[] = "zombieplague.ini"
378new const ZP_EXTRAITEMS_FILE[] = "zp_extraitems.ini"
379new const ZP_ZOMBIECLASSES_FILE[] = "zp_zombieclasses.ini"
380
381// Limiters for stuff not worth making dynamic arrays out of (increase if needed)
382const MAX_CSDM_SPAWNS = 128
383const MAX_STATS_SAVED = 64
384
385/*================================================================================
386 Customization ends here! Yes, that's it. Editing anything beyond
387 here is not officially supported. Proceed at your own risk...
388=================================================================================*/
389
390#include <amxmodx>
391#include <amxmisc>
392#include <cstrike>
393#include <fakemeta>
394#include <hamsandwich>
395#include <xs>
396
397/*================================================================================
398 [Constants, Offsets, Macros]
399=================================================================================*/
400
401// Plugin Version
402new const PLUGIN_VERSION[] = "4.3"
403
404// Customization file sections
405enum
406{
407 SECTION_NONE = 0,
408 SECTION_ACCESS_FLAGS,
409 SECTION_PLAYER_MODELS,
410 SECTION_WEAPON_MODELS,
411 SECTION_GRENADE_SPRITES,
412 SECTION_SOUNDS,
413 SECTION_AMBIENCE_SOUNDS,
414 SECTION_BUY_MENU_WEAPONS,
415 SECTION_EXTRA_ITEMS_WEAPONS,
416 SECTION_HARD_CODED_ITEMS_COSTS,
417 SECTION_WEATHER_EFFECTS,
418 SECTION_SKY,
419 SECTION_LIGHTNING,
420 SECTION_ZOMBIE_DECALS,
421 SECTION_KNOCKBACK,
422 SECTION_OBJECTIVE_ENTS,
423 SECTION_SVC_BAD
424}
425
426// Access flags
427enum
428{
429 ACCESS_ENABLE_MOD = 0,
430 ACCESS_ADMIN_MENU,
431 ACCESS_MODE_INFECTION,
432 ACCESS_MODE_NEMESIS,
433 ACCESS_MODE_SURVIVOR,
434 ACCESS_MODE_SWARM,
435 ACCESS_MODE_MULTI,
436 ACCESS_MODE_PLAGUE,
437 ACCESS_MAKE_ZOMBIE,
438 ACCESS_MAKE_HUMAN,
439 ACCESS_MAKE_NEMESIS,
440 ACCESS_MAKE_SURVIVOR,
441 ACCESS_RESPAWN_PLAYERS,
442 ACCESS_ADMIN_MODELS,
443 MAX_ACCESS_FLAGS
444}
445
446// Task offsets
447enum (+= 100)
448{
449 TASK_MODEL = 2000,
450 TASK_TEAM,
451 TASK_SPAWN,
452 TASK_BLOOD,
453 TASK_AURA,
454 TASK_BURN,
455 TASK_NVISION,
456 TASK_FLASH,
457 TASK_CHARGE,
458 TASK_SHOWHUD,
459 TASK_MAKEZOMBIE,
460 TASK_WELCOMEMSG,
461 TASK_THUNDER_PRE,
462 TASK_THUNDER,
463 TASK_AMBIENCESOUNDS
464}
465
466// IDs inside tasks
467#define ID_MODEL (taskid - TASK_MODEL)
468#define ID_TEAM (taskid - TASK_TEAM)
469#define ID_SPAWN (taskid - TASK_SPAWN)
470#define ID_BLOOD (taskid - TASK_BLOOD)
471#define ID_AURA (taskid - TASK_AURA)
472#define ID_BURN (taskid - TASK_BURN)
473#define ID_NVISION (taskid - TASK_NVISION)
474#define ID_FLASH (taskid - TASK_FLASH)
475#define ID_CHARGE (taskid - TASK_CHARGE)
476#define ID_SHOWHUD (taskid - TASK_SHOWHUD)
477
478// BP Ammo Refill task
479#define REFILL_WEAPONID args[0]
480
481// For weapon buy menu handlers
482#define WPN_STARTID g_menu_data[id][1]
483#define WPN_MAXIDS ArraySize(g_primary_items)
484#define WPN_SELECTION (g_menu_data[id][1]+key)
485#define WPN_AUTO_ON g_menu_data[id][2]
486#define WPN_AUTO_PRI g_menu_data[id][3]
487#define WPN_AUTO_SEC g_menu_data[id][4]
488
489// For player list menu handlers
490#define PL_ACTION g_menu_data[id][0]
491
492// For extra items menu handlers
493#define EXTRAS_CUSTOM_STARTID (EXTRA_WEAPONS_STARTID + ArraySize(g_extraweapon_names))
494
495// Menu selections
496const MENU_KEY_AUTOSELECT = 7
497const MENU_KEY_BACK = 7
498const MENU_KEY_NEXT = 8
499const MENU_KEY_EXIT = 9
500
501// Hard coded extra items
502enum
503{
504 EXTRA_NVISION = 0,
505 EXTRA_ANTIDOTE,
506 EXTRA_MADNESS,
507 EXTRA_INFBOMB,
508 EXTRA_WEAPONS_STARTID
509}
510
511// Game modes
512enum
513{
514 MODE_NONE = 0,
515 MODE_INFECTION,
516 MODE_NEMESIS,
517 MODE_SURVIVOR,
518 MODE_SWARM,
519 MODE_MULTI,
520 MODE_PLAGUE
521}
522
523// ZP Teams
524const ZP_TEAM_NO_ONE = 0
525const ZP_TEAM_ANY = 0
526const ZP_TEAM_ZOMBIE = (1<<0)
527const ZP_TEAM_HUMAN = (1<<1)
528const ZP_TEAM_NEMESIS = (1<<2)
529const ZP_TEAM_SURVIVOR = (1<<3)
530new const ZP_TEAM_NAMES[][] = { "ZOMBIE , HUMAN", "ZOMBIE", "HUMAN", "ZOMBIE , HUMAN", "NEMESIS",
531 "ZOMBIE , NEMESIS", "HUMAN , NEMESIS", "ZOMBIE , HUMAN , NEMESIS",
532 "SURVIVOR", "ZOMBIE , SURVIVOR", "HUMAN , SURVIVOR", "ZOMBIE , HUMAN , SURVIVOR",
533 "NEMESIS , SURVIVOR", "ZOMBIE , NEMESIS , SURVIVOR", "HUMAN, NEMESIS, SURVIVOR",
534 "ZOMBIE , HUMAN , NEMESIS , SURVIVOR" }
535
536// Zombie classes
537const ZCLASS_NONE = -1
538
539// HUD messages
540const Float:HUD_EVENT_X = -1.0
541const Float:HUD_EVENT_Y = 0.17
542const Float:HUD_INFECT_X = 0.05
543const Float:HUD_INFECT_Y = 0.45
544const Float:HUD_SPECT_X = 0.6
545const Float:HUD_SPECT_Y = 0.8
546const Float:HUD_STATS_X = 0.02
547const Float:HUD_STATS_Y = 0.9
548
549// CS Player PData Offsets (win32)
550const OFFSET_PAINSHOCK = 108 // ConnorMcLeod
551const OFFSET_CSTEAMS = 114
552const OFFSET_CSMONEY = 115
553const OFFSET_FLASHLIGHT_BATTERY = 244
554const OFFSET_CSDEATHS = 444
555const OFFSET_MODELINDEX = 491 // Orangutanz
556
557// CS Player CBase Offsets (win32)
558const OFFSET_ACTIVE_ITEM = 373
559
560// CS Weapon CBase Offsets (win32)
561const OFFSET_WEAPONOWNER = 41
562
563// Linux diff's
564const OFFSET_LINUX = 5 // offsets 5 higher in Linux builds
565const OFFSET_LINUX_WEAPONS = 4 // weapon offsets are only 4 steps higher on Linux
566
567// CS Teams
568enum
569{
570 FM_CS_TEAM_UNASSIGNED = 0,
571 FM_CS_TEAM_T,
572 FM_CS_TEAM_CT,
573 FM_CS_TEAM_SPECTATOR
574}
575new const CS_TEAM_NAMES[][] = { "UNASSIGNED", "TERRORIST", "CT", "SPECTATOR" }
576
577// Some constants
578const HIDE_MONEY = (1<<5)
579const UNIT_SECOND = (1<<12)
580const DMG_HEGRENADE = (1<<24)
581const IMPULSE_FLASHLIGHT = 100
582const USE_USING = 2
583const USE_STOPPED = 0
584const STEPTIME_SILENT = 999
585const BREAK_GLASS = 0x01
586const FFADE_IN = 0x0000
587const FFADE_STAYOUT = 0x0004
588const PEV_SPEC_TARGET = pev_iuser2
589
590// Max BP ammo for weapons
591new const MAXBPAMMO[] = { -1, 52, -1, 90, 1, 32, 1, 100, 90, 1, 120, 100, 100, 90, 90, 90, 100, 120,
592 30, 120, 200, 32, 90, 120, 90, 2, 35, 90, 90, -1, 100 }
593
594// Max Clip for weapons
595new const MAXCLIP[] = { -1, 13, -1, 10, -1, 7, -1, 30, 30, -1, 30, 20, 25, 30, 35, 25, 12, 20,
596 10, 30, 100, 8, 30, 30, 20, -1, 7, 30, 30, -1, 50 }
597
598// Amount of ammo to give when buying additional clips for weapons
599new const BUYAMMO[] = { -1, 13, -1, 30, -1, 8, -1, 12, 30, -1, 30, 50, 12, 30, 30, 30, 12, 30,
600 10, 30, 30, 8, 30, 30, 30, -1, 7, 30, 30, -1, 50 }
601
602// Ammo IDs for weapons
603new const AMMOID[] = { -1, 9, -1, 2, 12, 5, 14, 6, 4, 13, 10, 7, 6, 4, 4, 4, 6, 10,
604 1, 10, 3, 5, 4, 10, 2, 11, 8, 4, 2, -1, 7 }
605
606// Ammo Type Names for weapons
607new const AMMOTYPE[][] = { "", "357sig", "", "762nato", "", "buckshot", "", "45acp", "556nato", "", "9mm", "57mm", "45acp",
608 "556nato", "556nato", "556nato", "45acp", "9mm", "338magnum", "9mm", "556natobox", "buckshot",
609 "556nato", "9mm", "762nato", "", "50ae", "556nato", "762nato", "", "57mm" }
610
611// Weapon IDs for ammo types
612new const AMMOWEAPON[] = { 0, CSW_AWP, CSW_SCOUT, CSW_M249, CSW_AUG, CSW_XM1014, CSW_MAC10, CSW_FIVESEVEN, CSW_DEAGLE,
613 CSW_P228, CSW_ELITE, CSW_FLASHBANG, CSW_HEGRENADE, CSW_SMOKEGRENADE, CSW_C4 }
614
615// Primary and Secondary Weapon Names
616new const WEAPONNAMES[][] = { "", "P228 Compact", "", "Schmidt Scout", "", "XM1014 M4", "", "Ingram MAC-10", "Steyr AUG A1",
617 "", "Dual Elite Berettas", "FiveseveN", "UMP 45", "SG-550 Auto-Sniper", "IMI Galil", "Famas",
618 "USP .45 ACP Tactical", "Glock 18C", "AWP Magnum Sniper", "MP5 Navy", "M249 Para Machinegun",
619 "M3 Super 90", "M4A1 Carbine", "Schmidt TMP", "G3SG1 Auto-Sniper", "", "Desert Eagle .50 AE",
620 "SG-552 Commando", "AK-47 Kalashnikov", "", "ES P90" }
621
622// Weapon entity names
623new const WEAPONENTNAMES[][] = { "", "weapon_p228", "", "weapon_scout", "weapon_hegrenade", "weapon_xm1014", "weapon_c4", "weapon_mac10",
624 "weapon_aug", "weapon_smokegrenade", "weapon_elite", "weapon_fiveseven", "weapon_ump45", "weapon_sg550",
625 "weapon_galil", "weapon_famas", "weapon_usp", "weapon_glock18", "weapon_awp", "weapon_mp5navy", "weapon_m249",
626 "weapon_m3", "weapon_m4a1", "weapon_tmp", "weapon_g3sg1", "weapon_flashbang", "weapon_deagle", "weapon_sg552",
627 "weapon_ak47", "weapon_knife", "weapon_p90" }
628
629// CS sounds
630new const sound_flashlight[] = "items/flashlight1.wav"
631new const sound_buyammo[] = "items/9mmclip1.wav"
632new const sound_armorhit[] = "player/bhit_helmet-1.wav"
633
634// Explosion radius for custom grenades
635const Float:NADE_EXPLOSION_RADIUS = 240.0
636
637// HACK: pev_ field used to store additional ammo on weapons
638const PEV_ADDITIONAL_AMMO = pev_iuser1
639
640// HACK: pev_ field used to store custom nade types and their values
641const PEV_NADE_TYPE = pev_flTimeStepSound
642const NADE_TYPE_INFECTION = 1111
643const NADE_TYPE_NAPALM = 2222
644const NADE_TYPE_FROST = 3333
645const NADE_TYPE_FLARE = 4444
646const PEV_FLARE_COLOR = pev_punchangle
647const PEV_FLARE_DURATION = pev_flSwimTime
648
649// Weapon bitsums
650const PRIMARY_WEAPONS_BIT_SUM = (1<<CSW_SCOUT)|(1<<CSW_XM1014)|(1<<CSW_MAC10)|(1<<CSW_AUG)|(1<<CSW_UMP45)|(1<<CSW_SG550)|(1<<CSW_GALIL)|(1<<CSW_FAMAS)|(1<<CSW_AWP)|(1<<CSW_MP5NAVY)|(1<<CSW_M249)|(1<<CSW_M3)|(1<<CSW_M4A1)|(1<<CSW_TMP)|(1<<CSW_G3SG1)|(1<<CSW_SG552)|(1<<CSW_AK47)|(1<<CSW_P90)
651const SECONDARY_WEAPONS_BIT_SUM = (1<<CSW_P228)|(1<<CSW_ELITE)|(1<<CSW_FIVESEVEN)|(1<<CSW_USP)|(1<<CSW_GLOCK18)|(1<<CSW_DEAGLE)
652
653// Allowed weapons for zombies (added grenades/bomb for sub-plugin support, since they shouldn't be getting them anyway)
654const ZOMBIE_ALLOWED_WEAPONS_BITSUM = (1<<CSW_KNIFE)|(1<<CSW_HEGRENADE)|(1<<CSW_FLASHBANG)|(1<<CSW_SMOKEGRENADE)|(1<<CSW_C4)
655
656// Classnames for separate model entities
657new const MODEL_ENT_CLASSNAME[] = "player_model"
658new const WEAPON_ENT_CLASSNAME[] = "weapon_model"
659
660// Menu keys
661const KEYSMENU = MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_4|MENU_KEY_5|MENU_KEY_6|MENU_KEY_7|MENU_KEY_8|MENU_KEY_9|MENU_KEY_0
662
663// Ambience Sounds
664enum
665{
666 AMBIENCE_SOUNDS_INFECTION = 0,
667 AMBIENCE_SOUNDS_NEMESIS,
668 AMBIENCE_SOUNDS_SURVIVOR,
669 AMBIENCE_SOUNDS_SWARM,
670 AMBIENCE_SOUNDS_PLAGUE,
671 MAX_AMBIENCE_SOUNDS
672}
673
674// Admin menu actions
675enum
676{
677 ACTION_ZOMBIEFY_HUMANIZE = 0,
678 ACTION_MAKE_NEMESIS,
679 ACTION_MAKE_SURVIVOR,
680 ACTION_RESPAWN_PLAYER,
681 ACTION_MODE_SWARM,
682 ACTION_MODE_MULTI,
683 ACTION_MODE_PLAGUE
684}
685
686// Custom forward return values
687const ZP_PLUGIN_HANDLED = 97
688
689/*================================================================================
690 [Global Variables]
691=================================================================================*/
692
693// Player vars
694new g_zombie[33] // is zombie
695new g_nemesis[33] // is nemesis
696new g_survivor[33] // is survivor
697new g_firstzombie[33] // is first zombie
698new g_lastzombie[33] // is last zombie
699new g_lasthuman[33] // is last human
700new g_frozen[33] // is frozen (can't move)
701new g_nodamage[33] // has spawn protection/zombie madness
702new g_respawn_as_zombie[33] // should respawn as zombie
703new g_nvision[33] // has night vision
704new g_nvisionenabled[33] // has night vision turned on
705new g_zombieclass[33] // zombie class
706new g_zombieclassnext[33] // zombie class for next infection
707new g_flashlight[33] // has custom flashlight turned on
708new g_flashbattery[33] = { 100, ... } // custom flashlight battery
709new g_canbuy[33] // is allowed to buy a new weapon through the menu
710new g_ammopacks[33] // ammo pack count
711new g_damagedealt[33] // damage dealt to zombies (used to calculate ammo packs reward)
712new Float:g_lastleaptime[33] // time leap was last used
713new Float:g_lastflashtime[33] // time flashlight was last toggled
714new g_playermodel[33][32] // current model's short name [player][model]
715new g_menu_data[33][5] // data for some menu handlers
716new g_ent_playermodel[33] // player model entity
717new g_ent_weaponmodel[33] // weapon model entity
718new g_burning_duration[33] // burning task duration
719
720// Game vars
721new g_pluginenabled // ZP enabled
722new g_newround // new round starting
723new g_endround // round ended
724new g_nemround // nemesis round
725new g_survround // survivor round
726new g_swarmround // swarm round
727new g_plagueround // plague round
728new g_modestarted // mode fully started
729new g_lastmode // last played mode
730new g_scorezombies, g_scorehumans // team scores
731new g_spawnCount, g_spawnCount2 // available spawn points counter
732new Float:g_spawns[MAX_CSDM_SPAWNS][3], Float:g_spawns2[MAX_CSDM_SPAWNS][3] // spawn points data
733new g_lights_i // lightning current lights counter
734new g_lights_cycle[32] // current lightning cycle
735new g_lights_cycle_len // lightning cycle length
736new Float:g_models_targettime // for adding delays between Model Change messages
737new Float:g_teams_targettime // for adding delays between Team Change messages
738new g_MsgSync, g_MsgSync2 // message sync objects
739new g_trailSpr, g_exploSpr, g_flameSpr, g_smokeSpr, g_glassSpr // grenade sprites
740new g_modname[32] // for formatting the mod name
741new g_freezetime // whether CS's freeze time is on
742new g_maxplayers // max players counter
743new g_czero // whether we are running on a CZ server
744new g_hamczbots // whether ham forwards are registered for CZ bots
745new g_fwSpawn, g_fwPrecacheSound // spawn and precache sound forward handles
746new g_infbombcounter, g_antidotecounter, g_madnesscounter // to limit buying some items
747new g_arrays_created // to prevent stuff from being registered before initializing arrays
748new g_lastplayerleaving // flag for whenever a player leaves and another takes his place
749new g_switchingteam // flag for whenever a player's team change emessage is sent
750
751// Message IDs vars
752new g_msgScoreInfo, g_msgNVGToggle, g_msgScoreAttrib, g_msgAmmoPickup, g_msgScreenFade,
753g_msgDeathMsg, g_msgSetFOV, g_msgFlashlight, g_msgFlashBat, g_msgTeamInfo, g_msgDamage,
754g_msgHideWeapon, g_msgCrosshair, g_msgSayText, g_msgScreenShake, g_msgCurWeapon
755
756// Some forward handlers
757new g_fwRoundStart, g_fwRoundEnd, g_fwUserInfected_pre, g_fwUserInfected_post,
758g_fwUserHumanized_pre, g_fwUserHumanized_post, g_fwUserInfect_attempt,
759g_fwUserHumanize_attempt, g_fwExtraItemSelected, g_fwUserUnfrozen,
760g_fwUserLastZombie, g_fwUserLastHuman, g_fwDummyResult
761
762// Temporary Database vars (used to restore players stats in case they get disconnected)
763new db_name[MAX_STATS_SAVED][32] // player name
764new db_ammopacks[MAX_STATS_SAVED] // ammo pack count
765new db_zombieclass[MAX_STATS_SAVED] // zombie class
766new db_slot_i // additional saved slots counter (should start on maxplayers+1)
767
768// Extra Items vars
769new Array:g_extraitem_name // caption
770new Array:g_extraitem_cost // cost
771new Array:g_extraitem_team // team
772new g_extraitem_i // loaded extra items counter
773
774// For extra items file parsing
775new Array:g_extraitem2_realname, Array:g_extraitem2_name, Array:g_extraitem2_cost,
776Array:g_extraitem2_team, Array:g_extraitem_new
777
778// Zombie Classes vars
779new Array:g_zclass_name // caption
780new Array:g_zclass_info // description
781new Array:g_zclass_modelsstart // start position in models array
782new Array:g_zclass_modelsend // end position in models array
783new Array:g_zclass_playermodel // player models array
784new Array:g_zclass_modelindex // model indices array
785new Array:g_zclass_clawmodel // claw model
786new Array:g_zclass_hp // health
787new Array:g_zclass_spd // speed
788new Array:g_zclass_grav // gravity
789new Array:g_zclass_kb // knockback
790new g_zclass_i // loaded zombie classes counter
791
792// For zombie classes file parsing
793new Array:g_zclass2_realname, Array:g_zclass2_name, Array:g_zclass2_info,
794Array:g_zclass2_modelsstart, Array:g_zclass2_modelsend, Array:g_zclass2_playermodel,
795Array:g_zclass2_modelindex, Array:g_zclass2_clawmodel, Array:g_zclass2_hp,
796Array:g_zclass2_spd, Array:g_zclass2_grav, Array:g_zclass2_kb, Array:g_zclass_new
797
798// Customization vars
799new g_access_flag[MAX_ACCESS_FLAGS], Array:model_nemesis, Array:model_survivor, Array:model_human,
800Array:model_admin_zombie, Array:model_admin_human, Array:g_modelindex_human,
801Array:g_modelindex_nemesis, Array:g_modelindex_survivor, g_same_models_for_all,
802Array:g_modelindex_admin_zombie, Array:g_modelindex_admin_human, model_vknife_human[64],
803model_vknife_nemesis[64], model_vm249_survivor[64], model_grenade_infect[64],
804model_grenade_fire[64], model_grenade_frost[64], model_grenade_flare[64],
805model_vknife_admin_human[64], model_vknife_admin_zombie[64],
806sprite_grenade_trail[64], sprite_grenade_ring[64], sprite_grenade_fire[64],
807sprite_grenade_smoke[64], sprite_grenade_glass[64], Array:sound_win_zombies,
808Array:sound_win_humans, Array:sound_win_no_one, Array:zombie_infect, Array:zombie_idle,
809Array:zombie_pain, Array:nemesis_pain, Array:zombie_die, Array:zombie_fall,
810Array:zombie_miss_wall, Array:zombie_hit_normal, Array:zombie_hit_stab, g_ambience_rain,
811Array:zombie_idle_last, Array:zombie_madness, Array:sound_nemesis, Array:sound_survivor,
812Array:sound_swarm, Array:sound_multi, Array:sound_plague, Array:grenade_infect,
813Array:grenade_infect_player, Array:grenade_fire, Array:grenade_fire_player,
814Array:grenade_frost, Array:grenade_frost_player, Array:grenade_frost_break,
815Array:grenade_flare, Array:sound_antidote, Array:sound_thunder, g_ambience_sounds[MAX_AMBIENCE_SOUNDS],
816Array:sound_ambience1, Array:sound_ambience2, Array:sound_ambience3, Array:sound_ambience4,
817Array:sound_ambience5, Array:sound_ambience1_duration, Array:sound_ambience2_duration,
818Array:sound_ambience3_duration, Array:sound_ambience4_duration,
819Array:sound_ambience5_duration, Array:sound_ambience1_ismp3, Array:sound_ambience2_ismp3,
820Array:sound_ambience3_ismp3, Array:sound_ambience4_ismp3, Array:sound_ambience5_ismp3,
821Array:g_primary_items, Array:g_secondary_items, Array:g_additional_items,
822Array:g_primary_weaponids, Array:g_secondary_weaponids, Array:g_extraweapon_names,
823Array:g_extraweapon_items, Array:g_extraweapon_costs, g_extra_costs2[EXTRA_WEAPONS_STARTID],
824g_ambience_snow, g_ambience_fog, g_fog_density[10], g_fog_color[12], g_sky_enable,
825Array:g_sky_names, Array:lights_thunder, Array:zombie_decals, Array:g_objective_ents,
826Float:g_modelchange_delay, g_set_modelindex_offset, g_handle_models_on_separate_ent,
827Float:kb_weapon_power[31] = { -1.0, ... }, Array:zombie_miss_slash, g_force_consistency
828
829// CVAR pointers
830new cvar_lighting, cvar_zombiefov, cvar_plague, cvar_plaguechance, cvar_zombiefirsthp,
831cvar_removemoney, cvar_thunder, cvar_zombiebonushp, cvar_nemhp, cvar_nem, cvar_surv,
832cvar_nemchance, cvar_deathmatch, cvar_nemglow, cvar_customnvg, cvar_hitzones, cvar_humanhp,
833cvar_nemgravity, cvar_flashsize, cvar_ammodamage, cvar_zombiearmor, cvar_survpainfree,
834cvar_nempainfree, cvar_nemspd, cvar_survchance, cvar_survhp, cvar_survspd, cvar_humanspd,
835cvar_swarmchance, cvar_flashdrain, cvar_zombiebleeding, cvar_removedoors, cvar_customflash,
836cvar_randspawn, cvar_multi, cvar_multichance, cvar_infammo, cvar_swarm, cvar_ammoinfect,
837cvar_toggle, cvar_knockbackpower, cvar_freezeduration, cvar_triggered, cvar_flashcharge,
838cvar_firegrenades, cvar_frostgrenades, cvar_survgravity, cvar_logcommands, cvar_survglow,
839cvar_humangravity, cvar_spawnprotection, cvar_nvgsize, cvar_flareduration, cvar_zclasses,
840cvar_extraitems, cvar_showactivity, cvar_humanlasthp, cvar_nemignorefrags, cvar_warmup,
841cvar_flashdist, cvar_flarecolor, cvar_survignorefrags, cvar_fireduration, cvar_firedamage,
842cvar_flaregrenades, cvar_knockbackducking, cvar_knockbackdamage, cvar_knockbackzvel,
843cvar_multiratio, cvar_flaresize, cvar_spawndelay, cvar_extraantidote, cvar_extramadness,
844cvar_extraweapons, cvar_extranvision, cvar_nvggive, cvar_preventconsecutive, cvar_botquota,
845cvar_buycustom, cvar_zombiepainfree, cvar_fireslowdown, cvar_survbasehp, cvar_survaura,
846cvar_nemignoreammo, cvar_survignoreammo, cvar_nemaura, cvar_extrainfbomb, cvar_knockback,
847cvar_fragsinfect, cvar_fragskill, cvar_humanarmor, cvar_zombiesilent, cvar_removedropped,
848cvar_plagueratio, cvar_blocksuicide, cvar_knockbackdist, cvar_nemdamage, cvar_leapzombies,
849cvar_leapzombiesforce, cvar_leapzombiesheight, cvar_leapzombiescooldown, cvar_leapnemesis,
850cvar_leapnemesisforce, cvar_leapnemesisheight, cvar_leapnemesiscooldown, cvar_leapsurvivor,
851cvar_leapsurvivorforce, cvar_leapsurvivorheight, cvar_nemminplayers, cvar_survminplayers,
852cvar_respawnonsuicide, cvar_respawnafterlast, cvar_leapsurvivorcooldown, cvar_statssave,
853cvar_swarmminplayers, cvar_multiminplayers, cvar_plagueminplayers, cvar_adminmodelshuman,
854cvar_adminmodelszombie, cvar_nembasehp, cvar_blockpushables, cvar_respawnworldspawnkill,
855cvar_madnessduration, cvar_plaguenemnum, cvar_plaguenemhpmulti, cvar_plaguesurvhpmulti,
856cvar_survweapon, cvar_plaguesurvnum, cvar_infectionscreenfade, cvar_infectionscreenshake,
857cvar_infectionsparkle, cvar_infectiontracers, cvar_infectionparticles, cvar_infbomblimit,
858cvar_allowrespawnsurv, cvar_flashshowall, cvar_allowrespawninfection, cvar_allowrespawnnem,
859cvar_allowrespawnswarm, cvar_allowrespawnplague, cvar_survinfammo, cvar_nemknockback,
860cvar_nvgcolor[3], cvar_nemnvgcolor[3], cvar_humnvgcolor[3], cvar_flashcolor[3],
861cvar_hudicons, cvar_respawnzomb, cvar_respawnhum, cvar_respawnnem, cvar_respawnsurv,
862cvar_startammopacks, cvar_randweapons, cvar_antidotelimit, cvar_madnesslimit,
863cvar_adminknifemodelshuman, cvar_adminknifemodelszombie, cvar_keephealthondisconnect
864
865// Cached stuff for players
866new g_isconnected[33] // whether player is connected
867new g_isalive[33] // whether player is alive
868new g_isbot[33] // whether player is a bot
869new g_currentweapon[33] // player's current weapon id
870new g_playername[33][32] // player's name
871new Float:g_zombie_spd[33] // zombie class speed
872new Float:g_zombie_knockback[33] // zombie class knockback
873new g_zombie_classname[33][32] // zombie class name
874#define is_user_valid_connected(%1) (1 <= %1 <= g_maxplayers && g_isconnected[%1])
875#define is_user_valid_alive(%1) (1 <= %1 <= g_maxplayers && g_isalive[%1])
876
877// Cached CVARs
878new g_cached_customflash, g_cached_zombiesilent, Float:g_cached_humanspd, Float:g_cached_nemspd,
879Float:g_cached_survspd, g_cached_leapzombies, Float:g_cached_leapzombiescooldown, g_cached_leapnemesis,
880Float:g_cached_leapnemesiscooldown, g_cached_leapsurvivor, Float:g_cached_leapsurvivorcooldown
881
882/*================================================================================
883 [Natives, Precache and Init]
884=================================================================================*/
885
886public plugin_natives()
887{
888 // Player specific natives
889 register_native("zp_get_user_zombie", "native_get_user_zombie", 1)
890 register_native("zp_get_user_nemesis", "native_get_user_nemesis", 1)
891 register_native("zp_get_user_survivor", "native_get_user_survivor", 1)
892 register_native("zp_get_user_first_zombie", "native_get_user_first_zombie", 1)
893 register_native("zp_get_user_last_zombie", "native_get_user_last_zombie", 1)
894 register_native("zp_get_user_last_human", "native_get_user_last_human", 1)
895 register_native("zp_get_user_zombie_class", "native_get_user_zombie_class", 1)
896 register_native("zp_get_user_next_class", "native_get_user_next_class", 1)
897 register_native("zp_set_user_zombie_class", "native_set_user_zombie_class", 1)
898 register_native("zp_get_user_ammo_packs", "native_get_user_ammo_packs", 1)
899 register_native("zp_set_user_ammo_packs", "native_set_user_ammo_packs", 1)
900 register_native("zp_get_zombie_maxhealth", "native_get_zombie_maxhealth", 1)
901 register_native("zp_get_user_batteries", "native_get_user_batteries", 1)
902 register_native("zp_set_user_batteries", "native_set_user_batteries", 1)
903 register_native("zp_get_user_nightvision", "native_get_user_nightvision", 1)
904 register_native("zp_set_user_nightvision", "native_set_user_nightvision", 1)
905 register_native("zp_infect_user", "native_infect_user", 1)
906 register_native("zp_disinfect_user", "native_disinfect_user", 1)
907 register_native("zp_make_user_nemesis", "native_make_user_nemesis", 1)
908 register_native("zp_make_user_survivor", "native_make_user_survivor", 1)
909 register_native("zp_respawn_user", "native_respawn_user", 1)
910 register_native("zp_force_buy_extra_item", "native_force_buy_extra_item", 1)
911
912 // Round natives
913 register_native("zp_has_round_started", "native_has_round_started", 1)
914 register_native("zp_is_nemesis_round", "native_is_nemesis_round", 1)
915 register_native("zp_is_survivor_round", "native_is_survivor_round", 1)
916 register_native("zp_is_swarm_round", "native_is_swarm_round", 1)
917 register_native("zp_is_plague_round", "native_is_plague_round", 1)
918 register_native("zp_get_zombie_count", "native_get_zombie_count", 1)
919 register_native("zp_get_human_count", "native_get_human_count", 1)
920 register_native("zp_get_nemesis_count", "native_get_nemesis_count", 1)
921 register_native("zp_get_survivor_count", "native_get_survivor_count", 1)
922
923 // External additions natives
924 register_native("zp_register_extra_item", "native_register_extra_item", 1)
925 register_native("zp_register_zombie_class", "native_register_zombie_class", 1)
926 register_native("zp_get_extra_item_id", "native_get_extra_item_id", 1)
927 register_native("zp_get_zombie_class_id", "native_get_zombie_class_id", 1)
928}
929
930public plugin_precache()
931{
932 // Register earlier to show up in plugins list properly after plugin disable/error at loading
933 register_plugin("Zombie Plague", PLUGIN_VERSION, "MeRcyLeZZ")
934
935 // To switch plugin on/off
936 register_concmd("zp_toggle", "cmd_toggle", _, "<1/0> - Enable/Disable Zombie Plague (will restart the current map)", 0)
937 cvar_toggle = register_cvar("zp_on", "1")
938
939 // Plugin disabled?
940 if (!get_pcvar_num(cvar_toggle)) return;
941 g_pluginenabled = true
942
943 // Initialize a few dynamically sized arrays (alright, maybe more than just a few...)
944 model_human = ArrayCreate(32, 1)
945 model_nemesis = ArrayCreate(32, 1)
946 model_survivor = ArrayCreate(32, 1)
947 model_admin_human = ArrayCreate(32, 1)
948 model_admin_zombie = ArrayCreate(32, 1)
949 g_modelindex_human = ArrayCreate(1, 1)
950 g_modelindex_nemesis = ArrayCreate(1, 1)
951 g_modelindex_survivor = ArrayCreate(1, 1)
952 g_modelindex_admin_human = ArrayCreate(1, 1)
953 g_modelindex_admin_zombie = ArrayCreate(1, 1)
954 sound_win_zombies = ArrayCreate(64, 1)
955 sound_win_humans = ArrayCreate(64, 1)
956 sound_win_no_one = ArrayCreate(64, 1)
957 zombie_infect = ArrayCreate(64, 1)
958 zombie_pain = ArrayCreate(64, 1)
959 nemesis_pain = ArrayCreate(64, 1)
960 zombie_die = ArrayCreate(64, 1)
961 zombie_fall = ArrayCreate(64, 1)
962 zombie_miss_slash = ArrayCreate(64, 1)
963 zombie_miss_wall = ArrayCreate(64, 1)
964 zombie_hit_normal = ArrayCreate(64, 1)
965 zombie_hit_stab = ArrayCreate(64, 1)
966 zombie_idle = ArrayCreate(64, 1)
967 zombie_idle_last = ArrayCreate(64, 1)
968 zombie_madness = ArrayCreate(64, 1)
969 sound_nemesis = ArrayCreate(64, 1)
970 sound_survivor = ArrayCreate(64, 1)
971 sound_swarm = ArrayCreate(64, 1)
972 sound_multi = ArrayCreate(64, 1)
973 sound_plague = ArrayCreate(64, 1)
974 grenade_infect = ArrayCreate(64, 1)
975 grenade_infect_player = ArrayCreate(64, 1)
976 grenade_fire = ArrayCreate(64, 1)
977 grenade_fire_player = ArrayCreate(64, 1)
978 grenade_frost = ArrayCreate(64, 1)
979 grenade_frost_player = ArrayCreate(64, 1)
980 grenade_frost_break = ArrayCreate(64, 1)
981 grenade_flare = ArrayCreate(64, 1)
982 sound_antidote = ArrayCreate(64, 1)
983 sound_thunder = ArrayCreate(64, 1)
984 sound_ambience1 = ArrayCreate(64, 1)
985 sound_ambience2 = ArrayCreate(64, 1)
986 sound_ambience3 = ArrayCreate(64, 1)
987 sound_ambience4 = ArrayCreate(64, 1)
988 sound_ambience5 = ArrayCreate(64, 1)
989 sound_ambience1_duration = ArrayCreate(1, 1)
990 sound_ambience2_duration = ArrayCreate(1, 1)
991 sound_ambience3_duration = ArrayCreate(1, 1)
992 sound_ambience4_duration = ArrayCreate(1, 1)
993 sound_ambience5_duration = ArrayCreate(1, 1)
994 sound_ambience1_ismp3 = ArrayCreate(1, 1)
995 sound_ambience2_ismp3 = ArrayCreate(1, 1)
996 sound_ambience3_ismp3 = ArrayCreate(1, 1)
997 sound_ambience4_ismp3 = ArrayCreate(1, 1)
998 sound_ambience5_ismp3 = ArrayCreate(1, 1)
999 g_primary_items = ArrayCreate(32, 1)
1000 g_secondary_items = ArrayCreate(32, 1)
1001 g_additional_items = ArrayCreate(32, 1)
1002 g_primary_weaponids = ArrayCreate(1, 1)
1003 g_secondary_weaponids = ArrayCreate(1, 1)
1004 g_extraweapon_names = ArrayCreate(32, 1)
1005 g_extraweapon_items = ArrayCreate(32, 1)
1006 g_extraweapon_costs = ArrayCreate(1, 1)
1007 g_sky_names = ArrayCreate(32, 1)
1008 lights_thunder = ArrayCreate(32, 1)
1009 zombie_decals = ArrayCreate(1, 1)
1010 g_objective_ents = ArrayCreate(32, 1)
1011 g_extraitem_name = ArrayCreate(32, 1)
1012 g_extraitem_cost = ArrayCreate(1, 1)
1013 g_extraitem_team = ArrayCreate(1, 1)
1014 g_extraitem2_realname = ArrayCreate(32, 1)
1015 g_extraitem2_name = ArrayCreate(32, 1)
1016 g_extraitem2_cost = ArrayCreate(1, 1)
1017 g_extraitem2_team = ArrayCreate(1, 1)
1018 g_extraitem_new = ArrayCreate(1, 1)
1019 g_zclass_name = ArrayCreate(32, 1)
1020 g_zclass_info = ArrayCreate(32, 1)
1021 g_zclass_modelsstart = ArrayCreate(1, 1)
1022 g_zclass_modelsend = ArrayCreate(1, 1)
1023 g_zclass_playermodel = ArrayCreate(32, 1)
1024 g_zclass_modelindex = ArrayCreate(1, 1)
1025 g_zclass_clawmodel = ArrayCreate(32, 1)
1026 g_zclass_hp = ArrayCreate(1, 1)
1027 g_zclass_spd = ArrayCreate(1, 1)
1028 g_zclass_grav = ArrayCreate(1, 1)
1029 g_zclass_kb = ArrayCreate(1, 1)
1030 g_zclass2_realname = ArrayCreate(32, 1)
1031 g_zclass2_name = ArrayCreate(32, 1)
1032 g_zclass2_info = ArrayCreate(32, 1)
1033 g_zclass2_modelsstart = ArrayCreate(1, 1)
1034 g_zclass2_modelsend = ArrayCreate(1, 1)
1035 g_zclass2_playermodel = ArrayCreate(32, 1)
1036 g_zclass2_modelindex = ArrayCreate(1, 1)
1037 g_zclass2_clawmodel = ArrayCreate(32, 1)
1038 g_zclass2_hp = ArrayCreate(1, 1)
1039 g_zclass2_spd = ArrayCreate(1, 1)
1040 g_zclass2_grav = ArrayCreate(1, 1)
1041 g_zclass2_kb = ArrayCreate(1, 1)
1042 g_zclass_new = ArrayCreate(1, 1)
1043
1044 // Allow registering stuff now
1045 g_arrays_created = true
1046
1047 // Load customization data
1048 load_customization_from_files()
1049
1050 new i, buffer[100]
1051
1052 // Load up the hard coded extra items
1053 native_register_extra_item2("NightVision", g_extra_costs2[EXTRA_NVISION], ZP_TEAM_HUMAN)
1054 native_register_extra_item2("T-Virus Antidote", g_extra_costs2[EXTRA_ANTIDOTE], ZP_TEAM_ZOMBIE)
1055 native_register_extra_item2("Zombie Madness", g_extra_costs2[EXTRA_MADNESS], ZP_TEAM_ZOMBIE)
1056 native_register_extra_item2("Infection Bomb", g_extra_costs2[EXTRA_INFBOMB], ZP_TEAM_ZOMBIE)
1057
1058 // Extra weapons
1059 for (i = 0; i < ArraySize(g_extraweapon_names); i++)
1060 {
1061 ArrayGetString(g_extraweapon_names, i, buffer, charsmax(buffer))
1062 native_register_extra_item2(buffer, ArrayGetCell(g_extraweapon_costs, i), ZP_TEAM_HUMAN)
1063 }
1064
1065 // Custom player models
1066 for (i = 0; i < ArraySize(model_human); i++)
1067 {
1068 ArrayGetString(model_human, i, buffer, charsmax(buffer))
1069 format(buffer, charsmax(buffer), "models/player/%s/%s.mdl", buffer, buffer)
1070 ArrayPushCell(g_modelindex_human, engfunc(EngFunc_PrecacheModel, buffer))
1071 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, buffer)
1072 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, buffer)
1073 }
1074 for (i = 0; i < ArraySize(model_nemesis); i++)
1075 {
1076 ArrayGetString(model_nemesis, i, buffer, charsmax(buffer))
1077 format(buffer, charsmax(buffer), "models/player/%s/%s.mdl", buffer, buffer)
1078 ArrayPushCell(g_modelindex_nemesis, engfunc(EngFunc_PrecacheModel, buffer))
1079 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, buffer)
1080 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, buffer)
1081 }
1082 for (i = 0; i < ArraySize(model_survivor); i++)
1083 {
1084 ArrayGetString(model_survivor, i, buffer, charsmax(buffer))
1085 format(buffer, charsmax(buffer), "models/player/%s/%s.mdl", buffer, buffer)
1086 ArrayPushCell(g_modelindex_survivor, engfunc(EngFunc_PrecacheModel, buffer))
1087 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, buffer)
1088 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, buffer)
1089 }
1090 for (i = 0; i < ArraySize(model_admin_zombie); i++)
1091 {
1092 ArrayGetString(model_admin_zombie, i, buffer, charsmax(buffer))
1093 format(buffer, charsmax(buffer), "models/player/%s/%s.mdl", buffer, buffer)
1094 ArrayPushCell(g_modelindex_admin_zombie, engfunc(EngFunc_PrecacheModel, buffer))
1095 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, buffer)
1096 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, buffer)
1097 }
1098 for (i = 0; i < ArraySize(model_admin_human); i++)
1099 {
1100 ArrayGetString(model_admin_human, i, buffer, charsmax(buffer))
1101 format(buffer, charsmax(buffer), "models/player/%s/%s.mdl", buffer, buffer)
1102 ArrayPushCell(g_modelindex_admin_human, engfunc(EngFunc_PrecacheModel, buffer))
1103 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, buffer)
1104 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, buffer)
1105 }
1106
1107 // Custom weapon models
1108 engfunc(EngFunc_PrecacheModel, model_vknife_human)
1109 engfunc(EngFunc_PrecacheModel, model_vknife_nemesis)
1110 engfunc(EngFunc_PrecacheModel, model_vm249_survivor)
1111 engfunc(EngFunc_PrecacheModel, model_grenade_infect)
1112 engfunc(EngFunc_PrecacheModel, model_grenade_fire)
1113 engfunc(EngFunc_PrecacheModel, model_grenade_frost)
1114 engfunc(EngFunc_PrecacheModel, model_grenade_flare)
1115 engfunc(EngFunc_PrecacheModel, model_vknife_admin_human)
1116 engfunc(EngFunc_PrecacheModel, model_vknife_admin_zombie)
1117
1118 // Custom sprites for grenades
1119 g_trailSpr = engfunc(EngFunc_PrecacheModel, sprite_grenade_trail)
1120 g_exploSpr = engfunc(EngFunc_PrecacheModel, sprite_grenade_ring)
1121 g_flameSpr = engfunc(EngFunc_PrecacheModel, sprite_grenade_fire)
1122 g_smokeSpr = engfunc(EngFunc_PrecacheModel, sprite_grenade_smoke)
1123 g_glassSpr = engfunc(EngFunc_PrecacheModel, sprite_grenade_glass)
1124
1125 // Custom sounds
1126 for (i = 0; i < ArraySize(sound_win_zombies); i++)
1127 {
1128 ArrayGetString(sound_win_zombies, i, buffer, charsmax(buffer))
1129 engfunc(EngFunc_PrecacheSound, buffer)
1130 }
1131 for (i = 0; i < ArraySize(sound_win_humans); i++)
1132 {
1133 ArrayGetString(sound_win_humans, i, buffer, charsmax(buffer))
1134 engfunc(EngFunc_PrecacheSound, buffer)
1135 }
1136 for (i = 0; i < ArraySize(sound_win_no_one); i++)
1137 {
1138 ArrayGetString(sound_win_no_one, i, buffer, charsmax(buffer))
1139 engfunc(EngFunc_PrecacheSound, buffer)
1140 }
1141 for (i = 0; i < ArraySize(zombie_infect); i++)
1142 {
1143 ArrayGetString(zombie_infect, i, buffer, charsmax(buffer))
1144 engfunc(EngFunc_PrecacheSound, buffer)
1145 }
1146 for (i = 0; i < ArraySize(zombie_pain); i++)
1147 {
1148 ArrayGetString(zombie_pain, i, buffer, charsmax(buffer))
1149 engfunc(EngFunc_PrecacheSound, buffer)
1150 }
1151 for (i = 0; i < ArraySize(nemesis_pain); i++)
1152 {
1153 ArrayGetString(nemesis_pain, i, buffer, charsmax(buffer))
1154 engfunc(EngFunc_PrecacheSound, buffer)
1155 }
1156 for (i = 0; i < ArraySize(zombie_die); i++)
1157 {
1158 ArrayGetString(zombie_die, i, buffer, charsmax(buffer))
1159 engfunc(EngFunc_PrecacheSound, buffer)
1160 }
1161 for (i = 0; i < ArraySize(zombie_fall); i++)
1162 {
1163 ArrayGetString(zombie_fall, i, buffer, charsmax(buffer))
1164 engfunc(EngFunc_PrecacheSound, buffer)
1165 }
1166 for (i = 0; i < ArraySize(zombie_miss_slash); i++)
1167 {
1168 ArrayGetString(zombie_miss_slash, i, buffer, charsmax(buffer))
1169 engfunc(EngFunc_PrecacheSound, buffer)
1170 }
1171 for (i = 0; i < ArraySize(zombie_miss_wall); i++)
1172 {
1173 ArrayGetString(zombie_miss_wall, i, buffer, charsmax(buffer))
1174 engfunc(EngFunc_PrecacheSound, buffer)
1175 }
1176 for (i = 0; i < ArraySize(zombie_hit_normal); i++)
1177 {
1178 ArrayGetString(zombie_hit_normal, i, buffer, charsmax(buffer))
1179 engfunc(EngFunc_PrecacheSound, buffer)
1180 }
1181 for (i = 0; i < ArraySize(zombie_hit_stab); i++)
1182 {
1183 ArrayGetString(zombie_hit_stab, i, buffer, charsmax(buffer))
1184 engfunc(EngFunc_PrecacheSound, buffer)
1185 }
1186 for (i = 0; i < ArraySize(zombie_idle); i++)
1187 {
1188 ArrayGetString(zombie_idle, i, buffer, charsmax(buffer))
1189 engfunc(EngFunc_PrecacheSound, buffer)
1190 }
1191 for (i = 0; i < ArraySize(zombie_idle_last); i++)
1192 {
1193 ArrayGetString(zombie_idle_last, i, buffer, charsmax(buffer))
1194 engfunc(EngFunc_PrecacheSound, buffer)
1195 }
1196 for (i = 0; i < ArraySize(zombie_madness); i++)
1197 {
1198 ArrayGetString(zombie_madness, i, buffer, charsmax(buffer))
1199 engfunc(EngFunc_PrecacheSound, buffer)
1200 }
1201 for (i = 0; i < ArraySize(sound_nemesis); i++)
1202 {
1203 ArrayGetString(sound_nemesis, i, buffer, charsmax(buffer))
1204 engfunc(EngFunc_PrecacheSound, buffer)
1205 }
1206 for (i = 0; i < ArraySize(sound_survivor); i++)
1207 {
1208 ArrayGetString(sound_survivor, i, buffer, charsmax(buffer))
1209 engfunc(EngFunc_PrecacheSound, buffer)
1210 }
1211 for (i = 0; i < ArraySize(sound_swarm); i++)
1212 {
1213 ArrayGetString(sound_swarm, i, buffer, charsmax(buffer))
1214 engfunc(EngFunc_PrecacheSound, buffer)
1215 }
1216 for (i = 0; i < ArraySize(sound_multi); i++)
1217 {
1218 ArrayGetString(sound_multi, i, buffer, charsmax(buffer))
1219 engfunc(EngFunc_PrecacheSound, buffer)
1220 }
1221 for (i = 0; i < ArraySize(sound_plague); i++)
1222 {
1223 ArrayGetString(sound_plague, i, buffer, charsmax(buffer))
1224 engfunc(EngFunc_PrecacheSound, buffer)
1225 }
1226 for (i = 0; i < ArraySize(grenade_infect); i++)
1227 {
1228 ArrayGetString(grenade_infect, i, buffer, charsmax(buffer))
1229 engfunc(EngFunc_PrecacheSound, buffer)
1230 }
1231 for (i = 0; i < ArraySize(grenade_infect_player); i++)
1232 {
1233 ArrayGetString(grenade_infect_player, i, buffer, charsmax(buffer))
1234 engfunc(EngFunc_PrecacheSound, buffer)
1235 }
1236 for (i = 0; i < ArraySize(grenade_fire); i++)
1237 {
1238 ArrayGetString(grenade_fire, i, buffer, charsmax(buffer))
1239 engfunc(EngFunc_PrecacheSound, buffer)
1240 }
1241 for (i = 0; i < ArraySize(grenade_fire_player); i++)
1242 {
1243 ArrayGetString(grenade_fire_player, i, buffer, charsmax(buffer))
1244 engfunc(EngFunc_PrecacheSound, buffer)
1245 }
1246 for (i = 0; i < ArraySize(grenade_frost); i++)
1247 {
1248 ArrayGetString(grenade_frost, i, buffer, charsmax(buffer))
1249 engfunc(EngFunc_PrecacheSound, buffer)
1250 }
1251 for (i = 0; i < ArraySize(grenade_frost_player); i++)
1252 {
1253 ArrayGetString(grenade_frost_player, i, buffer, charsmax(buffer))
1254 engfunc(EngFunc_PrecacheSound, buffer)
1255 }
1256 for (i = 0; i < ArraySize(grenade_frost_break); i++)
1257 {
1258 ArrayGetString(grenade_frost_break, i, buffer, charsmax(buffer))
1259 engfunc(EngFunc_PrecacheSound, buffer)
1260 }
1261 for (i = 0; i < ArraySize(grenade_flare); i++)
1262 {
1263 ArrayGetString(grenade_flare, i, buffer, charsmax(buffer))
1264 engfunc(EngFunc_PrecacheSound, buffer)
1265 }
1266 for (i = 0; i < ArraySize(sound_antidote); i++)
1267 {
1268 ArrayGetString(sound_antidote, i, buffer, charsmax(buffer))
1269 engfunc(EngFunc_PrecacheSound, buffer)
1270 }
1271 for (i = 0; i < ArraySize(sound_thunder); i++)
1272 {
1273 ArrayGetString(sound_thunder, i, buffer, charsmax(buffer))
1274 engfunc(EngFunc_PrecacheSound, buffer)
1275 }
1276
1277 // Ambience Sounds
1278 if (g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION])
1279 {
1280 for (i = 0; i < ArraySize(sound_ambience1); i++)
1281 {
1282 ArrayGetString(sound_ambience1, i, buffer, charsmax(buffer))
1283
1284 if (ArrayGetCell(sound_ambience1_ismp3, i))
1285 {
1286 format(buffer, charsmax(buffer), "sound/%s", buffer)
1287 engfunc(EngFunc_PrecacheGeneric, buffer)
1288 }
1289 else
1290 {
1291 engfunc(EngFunc_PrecacheSound, buffer)
1292 }
1293 }
1294 }
1295 if (g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS])
1296 {
1297 for (i = 0; i < ArraySize(sound_ambience2); i++)
1298 {
1299 ArrayGetString(sound_ambience2, i, buffer, charsmax(buffer))
1300
1301 if (ArrayGetCell(sound_ambience2_ismp3, i))
1302 {
1303 format(buffer, charsmax(buffer), "sound/%s", buffer)
1304 engfunc(EngFunc_PrecacheGeneric, buffer)
1305 }
1306 else
1307 {
1308 engfunc(EngFunc_PrecacheSound, buffer)
1309 }
1310 }
1311 }
1312 if (g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR])
1313 {
1314 for (i = 0; i < ArraySize(sound_ambience3); i++)
1315 {
1316 ArrayGetString(sound_ambience3, i, buffer, charsmax(buffer))
1317
1318 if (ArrayGetCell(sound_ambience3_ismp3, i))
1319 {
1320 format(buffer, charsmax(buffer), "sound/%s", buffer)
1321 engfunc(EngFunc_PrecacheGeneric, buffer)
1322 }
1323 else
1324 {
1325 engfunc(EngFunc_PrecacheSound, buffer)
1326 }
1327 }
1328 }
1329 if (g_ambience_sounds[AMBIENCE_SOUNDS_SWARM])
1330 {
1331 for (i = 0; i < ArraySize(sound_ambience4); i++)
1332 {
1333 ArrayGetString(sound_ambience4, i, buffer, charsmax(buffer))
1334
1335 if (ArrayGetCell(sound_ambience4_ismp3, i))
1336 {
1337 format(buffer, charsmax(buffer), "sound/%s", buffer)
1338 engfunc(EngFunc_PrecacheGeneric, buffer)
1339 }
1340 else
1341 {
1342 engfunc(EngFunc_PrecacheSound, buffer)
1343 }
1344 }
1345 }
1346 if (g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE])
1347 {
1348 for (i = 0; i < ArraySize(sound_ambience5); i++)
1349 {
1350 ArrayGetString(sound_ambience5, i, buffer, charsmax(buffer))
1351
1352 if (ArrayGetCell(sound_ambience5_ismp3, i))
1353 {
1354 format(buffer, charsmax(buffer), "sound/%s", buffer)
1355 engfunc(EngFunc_PrecacheGeneric, buffer)
1356 }
1357 else
1358 {
1359 engfunc(EngFunc_PrecacheSound, buffer)
1360 }
1361 }
1362 }
1363
1364 // CS sounds (just in case)
1365 engfunc(EngFunc_PrecacheSound, sound_flashlight)
1366 engfunc(EngFunc_PrecacheSound, sound_buyammo)
1367 engfunc(EngFunc_PrecacheSound, sound_armorhit)
1368
1369 new ent
1370
1371 // Fake Hostage (to force round ending)
1372 ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "hostage_entity"))
1373 if (pev_valid(ent))
1374 {
1375 engfunc(EngFunc_SetOrigin, ent, Float:{8192.0,8192.0,8192.0})
1376 dllfunc(DLLFunc_Spawn, ent)
1377 }
1378
1379 // Weather/ambience effects
1380 if (g_ambience_fog)
1381 {
1382 ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "env_fog"))
1383 if (pev_valid(ent))
1384 {
1385 fm_set_kvd(ent, "density", g_fog_density, "env_fog")
1386 fm_set_kvd(ent, "rendercolor", g_fog_color, "env_fog")
1387 }
1388 }
1389 if (g_ambience_rain) engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "env_rain"))
1390 if (g_ambience_snow) engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "env_snow"))
1391
1392 // Prevent some entities from spawning
1393 g_fwSpawn = register_forward(FM_Spawn, "fw_Spawn")
1394
1395 // Prevent hostage sounds from being precached
1396 g_fwPrecacheSound = register_forward(FM_PrecacheSound, "fw_PrecacheSound")
1397}
1398
1399public plugin_init()
1400{
1401 // Plugin disabled?
1402 if (!g_pluginenabled) return;
1403
1404 // No zombie classes?
1405 if (!g_zclass_i) set_fail_state("No zombie classes loaded!")
1406
1407 // Language files
1408 register_dictionary("zombie_plague.txt")
1409
1410 // Events
1411 register_event("HLTV", "event_round_start", "a", "1=0", "2=0")
1412 register_logevent("logevent_round_start",2, "1=Round_Start")
1413 register_logevent("logevent_round_end", 2, "1=Round_End")
1414 register_event("AmmoX", "event_ammo_x", "be")
1415 if (g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION] || g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS] || g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR] || g_ambience_sounds[AMBIENCE_SOUNDS_SWARM] || g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE])
1416 register_event("30", "event_intermission", "a")
1417
1418 // HAM Forwards
1419 RegisterHam(Ham_Spawn, "player", "fw_PlayerSpawn_Post", 1)
1420 RegisterHam(Ham_Killed, "player", "fw_PlayerKilled")
1421 RegisterHam(Ham_Killed, "player", "fw_PlayerKilled_Post", 1)
1422 RegisterHam(Ham_TakeDamage, "player", "fw_TakeDamage")
1423 RegisterHam(Ham_TakeDamage, "player", "fw_TakeDamage_Post", 1)
1424 RegisterHam(Ham_TraceAttack, "player", "fw_TraceAttack")
1425 RegisterHam(Ham_Use, "func_tank", "fw_UseStationary")
1426 RegisterHam(Ham_Use, "func_tankmortar", "fw_UseStationary")
1427 RegisterHam(Ham_Use, "func_tankrocket", "fw_UseStationary")
1428 RegisterHam(Ham_Use, "func_tanklaser", "fw_UseStationary")
1429 RegisterHam(Ham_Use, "func_tank", "fw_UseStationary_Post", 1)
1430 RegisterHam(Ham_Use, "func_tankmortar", "fw_UseStationary_Post", 1)
1431 RegisterHam(Ham_Use, "func_tankrocket", "fw_UseStationary_Post", 1)
1432 RegisterHam(Ham_Use, "func_tanklaser", "fw_UseStationary_Post", 1)
1433 RegisterHam(Ham_Use, "func_pushable", "fw_UsePushable")
1434 RegisterHam(Ham_Touch, "weaponbox", "fw_TouchWeapon")
1435 RegisterHam(Ham_Touch, "armoury_entity", "fw_TouchWeapon")
1436 RegisterHam(Ham_Touch, "weapon_shield", "fw_TouchWeapon")
1437 RegisterHam(Ham_AddPlayerItem, "player", "fw_AddPlayerItem")
1438 for (new i = 1; i < sizeof WEAPONENTNAMES; i++)
1439 if (WEAPONENTNAMES[i][0]) RegisterHam(Ham_Item_Deploy, WEAPONENTNAMES[i], "fw_Item_Deploy_Post", 1)
1440
1441 // FM Forwards
1442 register_forward(FM_ClientDisconnect, "fw_ClientDisconnect")
1443 register_forward(FM_ClientDisconnect, "fw_ClientDisconnect_Post", 1)
1444 register_forward(FM_ClientKill, "fw_ClientKill")
1445 register_forward(FM_EmitSound, "fw_EmitSound")
1446 if (!g_handle_models_on_separate_ent) register_forward(FM_SetClientKeyValue, "fw_SetClientKeyValue")
1447 register_forward(FM_ClientUserInfoChanged, "fw_ClientUserInfoChanged")
1448 register_forward(FM_GetGameDescription, "fw_GetGameDescription")
1449 register_forward(FM_SetModel, "fw_SetModel")
1450 RegisterHam(Ham_Think, "grenade", "fw_ThinkGrenade")
1451 register_forward(FM_CmdStart, "fw_CmdStart")
1452 register_forward(FM_PlayerPreThink, "fw_PlayerPreThink")
1453 unregister_forward(FM_Spawn, g_fwSpawn)
1454 unregister_forward(FM_PrecacheSound, g_fwPrecacheSound)
1455
1456 // Client commands
1457 register_clcmd("say zpmenu", "clcmd_saymenu")
1458 register_clcmd("say /zpmenu", "clcmd_saymenu")
1459 register_clcmd("say unstuck", "clcmd_sayunstuck")
1460 register_clcmd("say /unstuck", "clcmd_sayunstuck")
1461 register_clcmd("nightvision", "clcmd_nightvision")
1462 register_clcmd("drop", "clcmd_drop")
1463 register_clcmd("buyammo1", "clcmd_buyammo")
1464 register_clcmd("buyammo2", "clcmd_buyammo")
1465 register_clcmd("chooseteam", "clcmd_changeteam")
1466 register_clcmd("jointeam", "clcmd_changeteam")
1467
1468 // Menus
1469 register_menu("Game Menu", KEYSMENU, "menu_game")
1470 register_menu("Buy Menu 1", KEYSMENU, "menu_buy1")
1471 register_menu("Buy Menu 2", KEYSMENU, "menu_buy2")
1472 register_menu("Mod Info", KEYSMENU, "menu_info")
1473 register_menu("Admin Menu", KEYSMENU, "menu_admin")
1474
1475 // Admin commands
1476 register_concmd("zp_zombie", "cmd_zombie", _, "<target> - Turn someone into a Zombie", 0)
1477 register_concmd("zp_human", "cmd_human", _, "<target> - Turn someone back to Human", 0)
1478 register_concmd("zp_nemesis", "cmd_nemesis", _, "<target> - Turn someone into a Nemesis", 0)
1479 register_concmd("zp_survivor", "cmd_survivor", _, "<target> - Turn someone into a Survivor", 0)
1480 register_concmd("zp_respawn", "cmd_respawn", _, "<target> - Respawn someone", 0)
1481 register_concmd("zp_swarm", "cmd_swarm", _, " - Start Swarm Mode", 0)
1482 register_concmd("zp_multi", "cmd_multi", _, " - Start Multi Infection", 0)
1483 register_concmd("zp_plague", "cmd_plague", _, " - Start Plague Mode", 0)
1484
1485 // Message IDs
1486 g_msgScoreInfo = get_user_msgid("ScoreInfo")
1487 g_msgTeamInfo = get_user_msgid("TeamInfo")
1488 g_msgDeathMsg = get_user_msgid("DeathMsg")
1489 g_msgScoreAttrib = get_user_msgid("ScoreAttrib")
1490 g_msgSetFOV = get_user_msgid("SetFOV")
1491 g_msgScreenFade = get_user_msgid("ScreenFade")
1492 g_msgScreenShake = get_user_msgid("ScreenShake")
1493 g_msgNVGToggle = get_user_msgid("NVGToggle")
1494 g_msgFlashlight = get_user_msgid("Flashlight")
1495 g_msgFlashBat = get_user_msgid("FlashBat")
1496 g_msgAmmoPickup = get_user_msgid("AmmoPickup")
1497 g_msgDamage = get_user_msgid("Damage")
1498 g_msgHideWeapon = get_user_msgid("HideWeapon")
1499 g_msgCrosshair = get_user_msgid("Crosshair")
1500 g_msgSayText = get_user_msgid("SayText")
1501 g_msgCurWeapon = get_user_msgid("CurWeapon")
1502
1503 // Message hooks
1504 register_message(g_msgCurWeapon, "message_cur_weapon")
1505 register_message(get_user_msgid("Money"), "message_money")
1506 register_message(get_user_msgid("Health"), "message_health")
1507 register_message(g_msgFlashBat, "message_flashbat")
1508 register_message(g_msgScreenFade, "message_screenfade")
1509 register_message(g_msgNVGToggle, "message_nvgtoggle")
1510 if (g_handle_models_on_separate_ent) register_message(get_user_msgid("ClCorpse"), "message_clcorpse")
1511 register_message(get_user_msgid("WeapPickup"), "message_weappickup")
1512 register_message(g_msgAmmoPickup, "message_ammopickup")
1513 register_message(get_user_msgid("Scenario"), "message_scenario")
1514 register_message(get_user_msgid("HostagePos"), "message_hostagepos")
1515 register_message(get_user_msgid("TextMsg"), "message_textmsg")
1516 register_message(get_user_msgid("SendAudio"), "message_sendaudio")
1517 register_message(get_user_msgid("TeamScore"), "message_teamscore")
1518 register_message(g_msgTeamInfo, "message_teaminfo")
1519
1520 // CVARS - General Purpose
1521 cvar_warmup = register_cvar("zp_delay", "10")
1522 cvar_lighting = register_cvar("zp_lighting", "a")
1523 cvar_thunder = register_cvar("zp_thunderclap", "90")
1524 cvar_triggered = register_cvar("zp_triggered_lights", "1")
1525 cvar_removedoors = register_cvar("zp_remove_doors", "0")
1526 cvar_blockpushables = register_cvar("zp_blockuse_pushables", "1")
1527 cvar_blocksuicide = register_cvar("zp_block_suicide", "1")
1528 cvar_randspawn = register_cvar("zp_random_spawn", "1")
1529 cvar_respawnworldspawnkill = register_cvar("zp_respawn_on_worldspawn_kill", "1")
1530 cvar_removedropped = register_cvar("zp_remove_dropped", "0")
1531 cvar_removemoney = register_cvar("zp_remove_money", "1")
1532 cvar_buycustom = register_cvar("zp_buy_custom", "1")
1533 cvar_randweapons = register_cvar("zp_random_weapons", "0")
1534 cvar_adminmodelshuman = register_cvar("zp_admin_models_human", "1")
1535 cvar_adminknifemodelshuman = register_cvar("zp_admin_knife_models_human", "0")
1536 cvar_adminmodelszombie = register_cvar("zp_admin_models_zombie", "1")
1537 cvar_adminknifemodelszombie = register_cvar("zp_admin_knife_models_zombie", "0")
1538 cvar_zclasses = register_cvar("zp_zombie_classes", "1")
1539 cvar_statssave = register_cvar("zp_stats_save", "1")
1540 cvar_startammopacks = register_cvar("zp_starting_ammo_packs", "5")
1541 cvar_preventconsecutive = register_cvar("zp_prevent_consecutive_modes", "1")
1542 cvar_keephealthondisconnect = register_cvar("zp_keep_health_on_disconnect", "1")
1543
1544 // CVARS - Deathmatch
1545 cvar_deathmatch = register_cvar("zp_deathmatch", "0")
1546 cvar_spawndelay = register_cvar("zp_spawn_delay", "5")
1547 cvar_spawnprotection = register_cvar("zp_spawn_protection", "5")
1548 cvar_respawnonsuicide = register_cvar("zp_respawn_on_suicide", "0")
1549 cvar_respawnafterlast = register_cvar("zp_respawn_after_last_human", "1")
1550 cvar_allowrespawninfection = register_cvar("zp_infection_allow_respawn", "1")
1551 cvar_allowrespawnnem = register_cvar("zp_nem_allow_respawn", "0")
1552 cvar_allowrespawnsurv = register_cvar("zp_surv_allow_respawn", "0")
1553 cvar_allowrespawnswarm = register_cvar("zp_swarm_allow_respawn", "0")
1554 cvar_allowrespawnplague = register_cvar("zp_plague_allow_respawn", "0")
1555 cvar_respawnzomb = register_cvar("zp_respawn_zombies", "1")
1556 cvar_respawnhum = register_cvar("zp_respawn_humans", "1")
1557 cvar_respawnnem = register_cvar("zp_respawn_nemesis", "1")
1558 cvar_respawnsurv = register_cvar("zp_respawn_survivors", "1")
1559
1560 // CVARS - Extra Items
1561 cvar_extraitems = register_cvar("zp_extra_items", "1")
1562 cvar_extraweapons = register_cvar("zp_extra_weapons", "1")
1563 cvar_extranvision = register_cvar("zp_extra_nvision", "1")
1564 cvar_extraantidote = register_cvar("zp_extra_antidote", "1")
1565 cvar_antidotelimit = register_cvar("zp_extra_antidote_limit", "999")
1566 cvar_extramadness = register_cvar("zp_extra_madness", "1")
1567 cvar_madnesslimit = register_cvar("zp_extra_madness_limit", "999")
1568 cvar_madnessduration = register_cvar("zp_extra_madness_duration", "5.0")
1569 cvar_extrainfbomb = register_cvar("zp_extra_infbomb", "1")
1570 cvar_infbomblimit = register_cvar("zp_extra_infbomb_limit", "999")
1571
1572 // CVARS - Flashlight and Nightvision
1573 cvar_nvggive = register_cvar("zp_nvg_give", "1")
1574 cvar_customnvg = register_cvar("zp_nvg_custom", "1")
1575 cvar_nvgsize = register_cvar("zp_nvg_size", "80")
1576 cvar_nvgcolor[0] = register_cvar("zp_nvg_color_R", "0")
1577 cvar_nvgcolor[1] = register_cvar("zp_nvg_color_G", "150")
1578 cvar_nvgcolor[2] = register_cvar("zp_nvg_color_B", "0")
1579 cvar_humnvgcolor[0] = register_cvar("zp_nvg_hum_color_R", "0")
1580 cvar_humnvgcolor[1] = register_cvar("zp_nvg_hum_color_G", "150")
1581 cvar_humnvgcolor[2] = register_cvar("zp_nvg_hum_color_B", "0")
1582 cvar_nemnvgcolor[0] = register_cvar("zp_nvg_nem_color_R", "150")
1583 cvar_nemnvgcolor[1] = register_cvar("zp_nvg_nem_color_G", "0")
1584 cvar_nemnvgcolor[2] = register_cvar("zp_nvg_nem_color_B", "0")
1585 cvar_customflash = register_cvar("zp_flash_custom", "0")
1586 cvar_flashsize = register_cvar("zp_flash_size", "10")
1587 cvar_flashdrain = register_cvar("zp_flash_drain", "1")
1588 cvar_flashcharge = register_cvar("zp_flash_charge", "5")
1589 cvar_flashdist = register_cvar("zp_flash_distance", "1000")
1590 cvar_flashcolor[0] = register_cvar("zp_flash_color_R", "100")
1591 cvar_flashcolor[1] = register_cvar("zp_flash_color_G", "100")
1592 cvar_flashcolor[2] = register_cvar("zp_flash_color_B", "100")
1593 cvar_flashshowall = register_cvar("zp_flash_show_all", "1")
1594
1595 // CVARS - Knockback
1596 cvar_knockback = register_cvar("zp_knockback", "0")
1597 cvar_knockbackdamage = register_cvar("zp_knockback_damage", "1")
1598 cvar_knockbackpower = register_cvar("zp_knockback_power", "1")
1599 cvar_knockbackzvel = register_cvar("zp_knockback_zvel", "0")
1600 cvar_knockbackducking = register_cvar("zp_knockback_ducking", "0.25")
1601 cvar_knockbackdist = register_cvar("zp_knockback_distance", "500")
1602 cvar_nemknockback = register_cvar("zp_knockback_nemesis", "0.25")
1603
1604 // CVARS - Leap
1605 cvar_leapzombies = register_cvar("zp_leap_zombies", "0")
1606 cvar_leapzombiesforce = register_cvar("zp_leap_zombies_force", "500")
1607 cvar_leapzombiesheight = register_cvar("zp_leap_zombies_height", "300")
1608 cvar_leapzombiescooldown = register_cvar("zp_leap_zombies_cooldown", "5.0")
1609 cvar_leapnemesis = register_cvar("zp_leap_nemesis", "1")
1610 cvar_leapnemesisforce = register_cvar("zp_leap_nemesis_force", "500")
1611 cvar_leapnemesisheight = register_cvar("zp_leap_nemesis_height", "300")
1612 cvar_leapnemesiscooldown = register_cvar("zp_leap_nemesis_cooldown", "5.0")
1613 cvar_leapsurvivor = register_cvar("zp_leap_survivor", "0")
1614 cvar_leapsurvivorforce = register_cvar("zp_leap_survivor_force", "500")
1615 cvar_leapsurvivorheight = register_cvar("zp_leap_survivor_height", "300")
1616 cvar_leapsurvivorcooldown = register_cvar("zp_leap_survivor_cooldown", "5.0")
1617
1618 // CVARS - Humans
1619 cvar_humanhp = register_cvar("zp_human_health", "100")
1620 cvar_humanlasthp = register_cvar("zp_human_last_extrahp", "0")
1621 cvar_humanspd = register_cvar("zp_human_speed", "240")
1622 cvar_humangravity = register_cvar("zp_human_gravity", "1.0")
1623 cvar_humanarmor = register_cvar("zp_human_armor_protect", "1")
1624 cvar_infammo = register_cvar("zp_human_unlimited_ammo", "0")
1625 cvar_ammodamage = register_cvar("zp_human_damage_reward", "500")
1626 cvar_fragskill = register_cvar("zp_human_frags_for_kill", "1")
1627
1628 // CVARS - Custom Grenades
1629 cvar_firegrenades = register_cvar("zp_fire_grenades", "1")
1630 cvar_fireduration = register_cvar("zp_fire_duration", "10")
1631 cvar_firedamage = register_cvar("zp_fire_damage", "5")
1632 cvar_fireslowdown = register_cvar("zp_fire_slowdown", "0.5")
1633 cvar_frostgrenades = register_cvar("zp_frost_grenades", "1")
1634 cvar_freezeduration = register_cvar("zp_frost_duration", "3")
1635 cvar_flaregrenades = register_cvar("zp_flare_grenades","1")
1636 cvar_flareduration = register_cvar("zp_flare_duration", "60")
1637 cvar_flaresize = register_cvar("zp_flare_size", "25")
1638 cvar_flarecolor = register_cvar("zp_flare_color", "0")
1639
1640 // CVARS - Zombies
1641 cvar_zombiefirsthp = register_cvar("zp_zombie_first_hp", "2.0")
1642 cvar_zombiearmor = register_cvar("zp_zombie_armor", "0.75")
1643 cvar_hitzones = register_cvar("zp_zombie_hitzones", "0")
1644 cvar_zombiebonushp = register_cvar("zp_zombie_infect_health", "100")
1645 cvar_zombiefov = register_cvar("zp_zombie_fov", "110")
1646 cvar_zombiesilent = register_cvar("zp_zombie_silent", "1")
1647 cvar_zombiepainfree = register_cvar("zp_zombie_painfree", "2")
1648 cvar_zombiebleeding = register_cvar("zp_zombie_bleeding", "1")
1649 cvar_ammoinfect = register_cvar("zp_zombie_infect_reward", "1")
1650 cvar_fragsinfect = register_cvar("zp_zombie_frags_for_infect", "1")
1651
1652 // CVARS - Special Effects
1653 cvar_infectionscreenfade = register_cvar("zp_infection_screenfade", "1")
1654 cvar_infectionscreenshake = register_cvar("zp_infection_screenshake", "1")
1655 cvar_infectionsparkle = register_cvar("zp_infection_sparkle", "1")
1656 cvar_infectiontracers = register_cvar("zp_infection_tracers", "1")
1657 cvar_infectionparticles = register_cvar("zp_infection_particles", "1")
1658 cvar_hudicons = register_cvar("zp_hud_icons", "1")
1659
1660 // CVARS - Nemesis
1661 cvar_nem = register_cvar("zp_nem_enabled", "1")
1662 cvar_nemchance = register_cvar("zp_nem_chance", "20")
1663 cvar_nemminplayers = register_cvar("zp_nem_min_players", "0")
1664 cvar_nemhp = register_cvar("zp_nem_health", "0")
1665 cvar_nembasehp = register_cvar("zp_nem_base_health", "0")
1666 cvar_nemspd = register_cvar("zp_nem_speed", "250")
1667 cvar_nemgravity = register_cvar("zp_nem_gravity", "0.5")
1668 cvar_nemdamage = register_cvar("zp_nem_damage", "250")
1669 cvar_nemglow = register_cvar("zp_nem_glow", "1")
1670 cvar_nemaura = register_cvar("zp_nem_aura", "1")
1671 cvar_nempainfree = register_cvar("zp_nem_painfree", "0")
1672 cvar_nemignorefrags = register_cvar("zp_nem_ignore_frags", "1")
1673 cvar_nemignoreammo = register_cvar("zp_nem_ignore_rewards", "1")
1674
1675 // CVARS - Survivor
1676 cvar_surv = register_cvar("zp_surv_enabled", "1")
1677 cvar_survchance = register_cvar("zp_surv_chance", "20")
1678 cvar_survminplayers = register_cvar("zp_surv_min_players", "0")
1679 cvar_survhp = register_cvar("zp_surv_health", "0")
1680 cvar_survbasehp = register_cvar("zp_surv_base_health", "0")
1681 cvar_survspd = register_cvar("zp_surv_speed", "230")
1682 cvar_survgravity = register_cvar("zp_surv_gravity", "1.25")
1683 cvar_survglow = register_cvar("zp_surv_glow", "1")
1684 cvar_survaura = register_cvar("zp_surv_aura", "1")
1685 cvar_survpainfree = register_cvar("zp_surv_painfree", "1")
1686 cvar_survignorefrags = register_cvar("zp_surv_ignore_frags", "1")
1687 cvar_survignoreammo = register_cvar("zp_surv_ignore_rewards", "1")
1688 cvar_survweapon = register_cvar("zp_surv_weapon", "weapon_m249")
1689 cvar_survinfammo = register_cvar("zp_surv_unlimited_ammo", "2")
1690
1691 // CVARS - Swarm Mode
1692 cvar_swarm = register_cvar("zp_swarm_enabled", "1")
1693 cvar_swarmchance = register_cvar("zp_swarm_chance", "20")
1694 cvar_swarmminplayers = register_cvar("zp_swarm_min_players", "0")
1695
1696 // CVARS - Multi Infection
1697 cvar_multi = register_cvar("zp_multi_enabled", "1")
1698 cvar_multichance = register_cvar("zp_multi_chance", "20")
1699 cvar_multiminplayers = register_cvar("zp_multi_min_players", "0")
1700 cvar_multiratio = register_cvar("zp_multi_ratio", "0.15")
1701
1702 // CVARS - Plague Mode
1703 cvar_plague = register_cvar("zp_plague_enabled", "1")
1704 cvar_plaguechance = register_cvar("zp_plague_chance", "30")
1705 cvar_plagueminplayers = register_cvar("zp_plague_min_players", "0")
1706 cvar_plagueratio = register_cvar("zp_plague_ratio", "0.5")
1707 cvar_plaguenemnum = register_cvar("zp_plague_nem_number", "1")
1708 cvar_plaguenemhpmulti = register_cvar("zp_plague_nem_hp_multi", "0.5")
1709 cvar_plaguesurvnum = register_cvar("zp_plague_surv_number", "1")
1710 cvar_plaguesurvhpmulti = register_cvar("zp_plague_surv_hp_multi", "0.5")
1711
1712 // CVARS - Others
1713 cvar_logcommands = register_cvar("zp_logcommands", "1")
1714 cvar_showactivity = get_cvar_pointer("amx_show_activity")
1715 cvar_botquota = get_cvar_pointer("bot_quota")
1716 register_cvar("zp_version", PLUGIN_VERSION, FCVAR_SERVER|FCVAR_SPONLY)
1717 set_cvar_string("zp_version", PLUGIN_VERSION)
1718
1719 // Custom Forwards
1720 g_fwRoundStart = CreateMultiForward("zp_round_started", ET_IGNORE, FP_CELL, FP_CELL)
1721 g_fwRoundEnd = CreateMultiForward("zp_round_ended", ET_IGNORE, FP_CELL)
1722 g_fwUserInfected_pre = CreateMultiForward("zp_user_infected_pre", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL)
1723 g_fwUserInfected_post = CreateMultiForward("zp_user_infected_post", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL)
1724 g_fwUserHumanized_pre = CreateMultiForward("zp_user_humanized_pre", ET_IGNORE, FP_CELL, FP_CELL)
1725 g_fwUserHumanized_post = CreateMultiForward("zp_user_humanized_post", ET_IGNORE, FP_CELL, FP_CELL)
1726 g_fwUserInfect_attempt = CreateMultiForward("zp_user_infect_attempt", ET_CONTINUE, FP_CELL, FP_CELL, FP_CELL)
1727 g_fwUserHumanize_attempt = CreateMultiForward("zp_user_humanize_attempt", ET_CONTINUE, FP_CELL, FP_CELL)
1728 g_fwExtraItemSelected = CreateMultiForward("zp_extra_item_selected", ET_CONTINUE, FP_CELL, FP_CELL)
1729 g_fwUserUnfrozen = CreateMultiForward("zp_user_unfrozen", ET_IGNORE, FP_CELL)
1730 g_fwUserLastZombie = CreateMultiForward("zp_user_last_zombie", ET_IGNORE, FP_CELL)
1731 g_fwUserLastHuman = CreateMultiForward("zp_user_last_human", ET_IGNORE, FP_CELL)
1732
1733 // Collect random spawn points
1734 load_spawns()
1735
1736 // Set a random skybox?
1737 if (g_sky_enable)
1738 {
1739 new sky[32]
1740 ArrayGetString(g_sky_names, random_num(0, ArraySize(g_sky_names) - 1), sky, charsmax(sky))
1741 set_cvar_string("sv_skyname", sky)
1742 }
1743
1744 // Disable sky lighting so it doesn't mess with our custom lighting
1745 set_cvar_num("sv_skycolor_r", 0)
1746 set_cvar_num("sv_skycolor_g", 0)
1747 set_cvar_num("sv_skycolor_b", 0)
1748
1749 // Create the HUD Sync Objects
1750 g_MsgSync = CreateHudSyncObj()
1751 g_MsgSync2 = CreateHudSyncObj()
1752
1753 // Format mod name
1754 formatex(g_modname, charsmax(g_modname), "Zombie Plague %s", PLUGIN_VERSION)
1755
1756 // Get Max Players
1757 g_maxplayers = get_maxplayers()
1758
1759 // Reserved saving slots starts on maxplayers+1
1760 db_slot_i = g_maxplayers+1
1761
1762 // Check if it's a CZ server
1763 new mymod[6]
1764 get_modname(mymod, charsmax(mymod))
1765 if (equal(mymod, "czero")) g_czero = 1
1766}
1767
1768public plugin_cfg()
1769{
1770 // Plugin disabled?
1771 if (!g_pluginenabled) return;
1772
1773 // Get configs dir
1774 new cfgdir[32]
1775 get_configsdir(cfgdir, charsmax(cfgdir))
1776
1777 // Execute config file (zombieplague.cfg)
1778 server_cmd("exec %s/zombieplague.cfg", cfgdir)
1779
1780 // Prevent any more stuff from registering
1781 g_arrays_created = false
1782
1783 // Save customization data
1784 save_customization()
1785
1786 // Lighting task
1787 set_task(5.0, "lighting_effects", _, _, _, "b")
1788
1789 // Cache CVARs after configs are loaded / call roundstart manually
1790 set_task(0.5, "cache_cvars")
1791 set_task(0.5, "event_round_start")
1792 set_task(0.5, "logevent_round_start")
1793}
1794
1795/*================================================================================
1796 [Main Events]
1797=================================================================================*/
1798
1799// Event Round Start
1800public event_round_start()
1801{
1802 // Remove doors/lights?
1803 set_task(0.1, "remove_stuff")
1804
1805 // New round starting
1806 g_newround = true
1807 g_endround = false
1808 g_survround = false
1809 g_nemround = false
1810 g_swarmround = false
1811 g_plagueround = false
1812 g_modestarted = false
1813
1814 // Reset bought infection bombs counter
1815 g_infbombcounter = 0
1816 g_antidotecounter = 0
1817 g_madnesscounter = 0
1818
1819 // Freezetime begins
1820 g_freezetime = true
1821
1822 // Show welcome message and T-Virus notice
1823 remove_task(TASK_WELCOMEMSG)
1824 set_task(2.0, "welcome_msg", TASK_WELCOMEMSG)
1825
1826 // Set a new "Make Zombie Task"
1827 remove_task(TASK_MAKEZOMBIE)
1828 set_task(2.0 + get_pcvar_float(cvar_warmup), "make_zombie_task", TASK_MAKEZOMBIE)
1829}
1830
1831// Log Event Round Start
1832public logevent_round_start()
1833{
1834 // Freezetime ends
1835 g_freezetime = false
1836}
1837
1838// Log Event Round End
1839public logevent_round_end()
1840{
1841 // Prevent this from getting called twice when restarting (bugfix)
1842 static Float:lastendtime, Float:current_time
1843 current_time = get_gametime()
1844 if (current_time - lastendtime < 0.5) return;
1845 lastendtime = current_time
1846
1847 // Temporarily save player stats?
1848 if (get_pcvar_num(cvar_statssave))
1849 {
1850 static id, team
1851 for (id = 1; id <= g_maxplayers; id++)
1852 {
1853 // Not connected
1854 if (!g_isconnected[id])
1855 continue;
1856
1857 team = fm_cs_get_user_team(id)
1858
1859 // Not playing
1860 if (team == FM_CS_TEAM_SPECTATOR || team == FM_CS_TEAM_UNASSIGNED)
1861 continue;
1862
1863 save_stats(id)
1864 }
1865 }
1866
1867 // Round ended
1868 g_endround = true
1869
1870 // Stop old tasks (if any)
1871 remove_task(TASK_WELCOMEMSG)
1872 remove_task(TASK_MAKEZOMBIE)
1873
1874 // Stop ambience sounds
1875 if ((g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS] && g_nemround) || (g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR] && g_survround) || (g_ambience_sounds[AMBIENCE_SOUNDS_SWARM] && g_swarmround) || (g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE] && g_plagueround) || (g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION] && !g_nemround && !g_survround && !g_swarmround && !g_plagueround))
1876 {
1877 remove_task(TASK_AMBIENCESOUNDS)
1878 ambience_sound_stop()
1879 }
1880
1881 // Show HUD notice, play win sound, update team scores...
1882 static sound[64]
1883 if (!fnGetZombies())
1884 {
1885 // Human team wins
1886 set_hudmessage(0, 0, 200, HUD_EVENT_X, HUD_EVENT_Y, 0, 0.0, 3.0, 2.0, 1.0, -1)
1887 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "WIN_HUMAN")
1888
1889 // Play win sound and increase score
1890 ArrayGetString(sound_win_humans, random_num(0, ArraySize(sound_win_humans) - 1), sound, charsmax(sound))
1891 PlaySound(sound)
1892 g_scorehumans++
1893
1894 // Round end forward
1895 ExecuteForward(g_fwRoundEnd, g_fwDummyResult, ZP_TEAM_HUMAN);
1896 }
1897 else if (!fnGetHumans())
1898 {
1899 // Zombie team wins
1900 set_hudmessage(200, 0, 0, HUD_EVENT_X, HUD_EVENT_Y, 0, 0.0, 3.0, 2.0, 1.0, -1)
1901 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "WIN_ZOMBIE")
1902
1903 // Play win sound and increase score
1904 ArrayGetString(sound_win_zombies, random_num(0, ArraySize(sound_win_zombies) - 1), sound, charsmax(sound))
1905 PlaySound(sound)
1906 g_scorezombies++
1907
1908 // Round end forward
1909 ExecuteForward(g_fwRoundEnd, g_fwDummyResult, ZP_TEAM_ZOMBIE);
1910 }
1911 else
1912 {
1913 // No one wins
1914 set_hudmessage(0, 200, 0, HUD_EVENT_X, HUD_EVENT_Y, 0, 0.0, 3.0, 2.0, 1.0, -1)
1915 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "WIN_NO_ONE")
1916
1917 // Play win sound
1918 ArrayGetString(sound_win_no_one, random_num(0, ArraySize(sound_win_no_one) - 1), sound, charsmax(sound))
1919 PlaySound(sound)
1920
1921 // Round end forward
1922 ExecuteForward(g_fwRoundEnd, g_fwDummyResult, ZP_TEAM_NO_ONE);
1923 }
1924
1925 // Balance the teams
1926 balance_teams()
1927}
1928
1929// Event Map Ended
1930public event_intermission()
1931{
1932 // Remove ambience sounds task
1933 remove_task(TASK_AMBIENCESOUNDS)
1934}
1935
1936// BP Ammo update
1937public event_ammo_x(id)
1938{
1939 // Humans only
1940 if (g_zombie[id])
1941 return;
1942
1943 // Get ammo type
1944 static type
1945 type = read_data(1)
1946
1947 // Unknown ammo type
1948 if (type >= sizeof AMMOWEAPON)
1949 return;
1950
1951 // Get weapon's id
1952 static weapon
1953 weapon = AMMOWEAPON[type]
1954
1955 // Primary and secondary only
1956 if (MAXBPAMMO[weapon] <= 2)
1957 return;
1958
1959 // Get ammo amount
1960 static amount
1961 amount = read_data(2)
1962
1963 // Unlimited BP Ammo?
1964 if (g_survivor[id] ? get_pcvar_num(cvar_survinfammo) : get_pcvar_num(cvar_infammo))
1965 {
1966 if (amount < MAXBPAMMO[weapon])
1967 {
1968 // The BP Ammo refill code causes the engine to send a message, but we
1969 // can't have that in this forward or we risk getting some recursion bugs.
1970 // For more info see: https://bugs.alliedmods.net/show_bug.cgi?id=3664
1971 static args[1]
1972 args[0] = weapon
1973 set_task(0.1, "refill_bpammo", id, args, sizeof args)
1974 }
1975 }
1976 // Bots automatically buy ammo when needed
1977 else if (g_isbot[id] && amount <= BUYAMMO[weapon])
1978 {
1979 // Task needed for the same reason as above
1980 set_task(0.1, "clcmd_buyammo", id)
1981 }
1982}
1983
1984/*================================================================================
1985 [Main Forwards]
1986=================================================================================*/
1987
1988// Entity Spawn Forward
1989public fw_Spawn(entity)
1990{
1991 // Invalid entity
1992 if (!pev_valid(entity)) return FMRES_IGNORED;
1993
1994 // Get classname
1995 new classname[32], objective[32], size = ArraySize(g_objective_ents)
1996 pev(entity, pev_classname, classname, charsmax(classname))
1997
1998 // Check whether it needs to be removed
1999 for (new i = 0; i < size; i++)
2000 {
2001 ArrayGetString(g_objective_ents, i, objective, charsmax(objective))
2002
2003 if (equal(classname, objective))
2004 {
2005 engfunc(EngFunc_RemoveEntity, entity)
2006 return FMRES_SUPERCEDE;
2007 }
2008 }
2009
2010 return FMRES_IGNORED;
2011}
2012
2013// Sound Precache Forward
2014public fw_PrecacheSound(const sound[])
2015{
2016 // Block all those unneeeded hostage sounds
2017 if (equal(sound, "hostage", 7))
2018 return FMRES_SUPERCEDE;
2019
2020 return FMRES_IGNORED;
2021}
2022
2023// Ham Player Spawn Post Forward
2024public fw_PlayerSpawn_Post(id)
2025{
2026 // Not alive or didn't join a team yet
2027 if (!is_user_alive(id) || !fm_cs_get_user_team(id))
2028 return;
2029
2030 // Player spawned
2031 g_isalive[id] = true
2032
2033 // Remove previous tasks
2034 remove_task(id+TASK_SPAWN)
2035 remove_task(id+TASK_MODEL)
2036 remove_task(id+TASK_BLOOD)
2037 remove_task(id+TASK_AURA)
2038 remove_task(id+TASK_BURN)
2039 remove_task(id+TASK_CHARGE)
2040 remove_task(id+TASK_FLASH)
2041 remove_task(id+TASK_NVISION)
2042
2043 // Spawn at a random location?
2044 if (get_pcvar_num(cvar_randspawn)) do_random_spawn(id)
2045
2046 // Hide money?
2047 if (get_pcvar_num(cvar_removemoney))
2048 set_task(0.4, "task_hide_money", id+TASK_SPAWN)
2049
2050 // Respawn player if he dies because of a worldspawn kill?
2051 if (get_pcvar_num(cvar_respawnworldspawnkill))
2052 set_task(2.0, "respawn_player_task", id+TASK_SPAWN)
2053
2054 // Spawn as zombie?
2055 if (g_respawn_as_zombie[id] && !g_newround)
2056 {
2057 reset_vars(id, 0) // reset player vars
2058 zombieme(id, 0, 0, 0, 0) // make him zombie right away
2059 return;
2060 }
2061
2062 // Reset player vars
2063 reset_vars(id, 0)
2064
2065 // Show custom buy menu?
2066 if (get_pcvar_num(cvar_buycustom))
2067 set_task(0.2, "show_menu_buy1", id+TASK_SPAWN)
2068
2069 // Set health and gravity
2070 fm_set_user_health(id, get_pcvar_num(cvar_humanhp))
2071 set_pev(id, pev_gravity, get_pcvar_float(cvar_humangravity))
2072
2073 // Switch to CT if spawning mid-round
2074 if (!g_newround && fm_cs_get_user_team(id) != FM_CS_TEAM_CT) // need to change team?
2075 {
2076 remove_task(id+TASK_TEAM)
2077 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
2078 fm_user_team_update(id)
2079 }
2080
2081 // Custom models stuff
2082 static currentmodel[32], tempmodel[32], already_has_model, i, iRand, size
2083 already_has_model = false
2084
2085 if (g_handle_models_on_separate_ent)
2086 {
2087 // Set the right model
2088 if (get_pcvar_num(cvar_adminmodelshuman) && (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS]))
2089 {
2090 iRand = random_num(0, ArraySize(model_admin_human) - 1)
2091 ArrayGetString(model_admin_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
2092 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_admin_human, iRand))
2093 }
2094 else
2095 {
2096 iRand = random_num(0, ArraySize(model_human) - 1)
2097 ArrayGetString(model_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
2098 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_human, iRand))
2099 }
2100
2101 // Set model on player model entity
2102 fm_set_playermodel_ent(id)
2103
2104 // Remove glow on player model entity
2105 fm_set_rendering(g_ent_playermodel[id])
2106 }
2107 else
2108 {
2109 // Get current model for comparing it with the current one
2110 fm_cs_get_user_model(id, currentmodel, charsmax(currentmodel))
2111
2112 // Set the right model, after checking that we don't already have it
2113 if (get_pcvar_num(cvar_adminmodelshuman) && (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS]))
2114 {
2115 size = ArraySize(model_admin_human)
2116 for (i = 0; i < size; i++)
2117 {
2118 ArrayGetString(model_admin_human, i, tempmodel, charsmax(tempmodel))
2119 if (equal(currentmodel, tempmodel)) already_has_model = true
2120 }
2121
2122 if (!already_has_model)
2123 {
2124 iRand = random_num(0, size - 1)
2125 ArrayGetString(model_admin_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
2126 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_admin_human, iRand))
2127 }
2128 }
2129 else
2130 {
2131 size = ArraySize(model_human)
2132 for (i = 0; i < size; i++)
2133 {
2134 ArrayGetString(model_human, i, tempmodel, charsmax(tempmodel))
2135 if (equal(currentmodel, tempmodel)) already_has_model = true
2136 }
2137
2138 if (!already_has_model)
2139 {
2140 iRand = random_num(0, size - 1)
2141 ArrayGetString(model_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
2142 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_human, iRand))
2143 }
2144 }
2145
2146 // Need to change the model?
2147 if (!already_has_model)
2148 {
2149 // An additional delay is offset at round start
2150 // since SVC_BAD is more likely to be triggered there
2151 if (g_newround)
2152 set_task(5.0 * g_modelchange_delay, "fm_user_model_update", id+TASK_MODEL)
2153 else
2154 fm_user_model_update(id+TASK_MODEL)
2155 }
2156
2157 // Remove glow
2158 fm_set_rendering(id)
2159 }
2160
2161 // Bots stuff
2162 if (g_isbot[id])
2163 {
2164 // Turn off NVG for bots
2165 cs_set_user_nvg(id, 0)
2166
2167 // Automatically buy extra items/weapons after first zombie is chosen
2168 if (get_pcvar_num(cvar_extraitems))
2169 {
2170 if (g_newround) set_task(10.0 + get_pcvar_float(cvar_warmup), "bot_buy_extras", id+TASK_SPAWN)
2171 else set_task(10.0, "bot_buy_extras", id+TASK_SPAWN)
2172 }
2173 }
2174
2175 // Enable spawn protection for humans spawning mid-round
2176 if (!g_newround && get_pcvar_float(cvar_spawnprotection) > 0.0)
2177 {
2178 // Do not take damage
2179 g_nodamage[id] = true
2180
2181 // Make temporarily invisible
2182 set_pev(id, pev_effects, pev(id, pev_effects) | EF_NODRAW)
2183
2184 // Set task to remove it
2185 set_task(get_pcvar_float(cvar_spawnprotection), "remove_spawn_protection", id+TASK_SPAWN)
2186 }
2187
2188 // Set the flashlight charge task to update battery status
2189 if (g_cached_customflash)
2190 set_task(1.0, "flashlight_charge", id+TASK_CHARGE, _, _, "b")
2191
2192 // Replace weapon models (bugfix)
2193 static weapon_ent
2194 weapon_ent = fm_cs_get_current_weapon_ent(id)
2195 if (pev_valid(weapon_ent)) replace_weapon_models(id, cs_get_weapon_id(weapon_ent))
2196
2197 // Last Zombie Check
2198 fnCheckLastZombie()
2199}
2200
2201// Ham Player Killed Forward
2202public fw_PlayerKilled(victim, attacker, shouldgib)
2203{
2204 // Player killed
2205 g_isalive[victim] = false
2206
2207 // Enable dead players nightvision
2208 set_task(0.1, "spec_nvision", victim)
2209
2210 // Disable nightvision when killed (bugfix)
2211 if (get_pcvar_num(cvar_nvggive) == 0 && g_nvision[victim])
2212 {
2213 if (get_pcvar_num(cvar_customnvg)) remove_task(victim+TASK_NVISION)
2214 else if (g_nvisionenabled[victim]) set_user_gnvision(victim, 0)
2215 g_nvision[victim] = false
2216 g_nvisionenabled[victim] = false
2217 }
2218
2219 // Turn off nightvision when killed (bugfix)
2220 if (get_pcvar_num(cvar_nvggive) == 2 && g_nvision[victim] && g_nvisionenabled[victim])
2221 {
2222 if (get_pcvar_num(cvar_customnvg)) remove_task(victim+TASK_NVISION)
2223 else set_user_gnvision(victim, 0)
2224 g_nvisionenabled[victim] = false
2225 }
2226
2227 // Turn off custom flashlight when killed
2228 if (g_cached_customflash)
2229 {
2230 // Turn it off
2231 g_flashlight[victim] = false
2232 g_flashbattery[victim] = 100
2233
2234 // Remove previous tasks
2235 remove_task(victim+TASK_CHARGE)
2236 remove_task(victim+TASK_FLASH)
2237 }
2238
2239 // Stop bleeding/burning/aura when killed
2240 if (g_zombie[victim])
2241 {
2242 remove_task(victim+TASK_BLOOD)
2243 remove_task(victim+TASK_AURA)
2244 remove_task(victim+TASK_BURN)
2245 }
2246
2247 // Nemesis explodes!
2248 if (g_nemesis[victim])
2249 SetHamParamInteger(3, 2)
2250
2251 // Get deathmatch mode status and whether the player killed himself
2252 static selfkill
2253 selfkill = (victim == attacker || !is_user_valid_connected(attacker)) ? true : false
2254
2255 // Respawn if deathmatch is enabled
2256 if (get_pcvar_num(cvar_deathmatch))
2257 {
2258 // Respawn on suicide?
2259 if (selfkill && !get_pcvar_num(cvar_respawnonsuicide))
2260 return;
2261
2262 // Respawn if only the last human is left?
2263 if (!get_pcvar_num(cvar_respawnafterlast) && fnGetHumans() <= 1)
2264 return;
2265
2266 // Respawn if human/zombie/nemesis/survivor?
2267 if ((g_zombie[victim] && !g_nemesis[victim] && !get_pcvar_num(cvar_respawnzomb)) || (!g_zombie[victim] && !g_survivor[victim] && !get_pcvar_num(cvar_respawnhum)) || (g_nemesis[victim] && !get_pcvar_num(cvar_respawnnem)) || (g_survivor[victim] && !get_pcvar_num(cvar_respawnsurv)))
2268 return;
2269
2270 // Respawn as zombie?
2271 if (get_pcvar_num(cvar_deathmatch) == 2 || (get_pcvar_num(cvar_deathmatch) == 3 && random_num(0, 1)) || (get_pcvar_num(cvar_deathmatch) == 4 && fnGetZombies() < fnGetAlive()/2))
2272 g_respawn_as_zombie[victim] = true
2273
2274 // Set the respawn task
2275 set_task(get_pcvar_float(cvar_spawndelay), "respawn_player_task", victim+TASK_SPAWN)
2276 }
2277
2278 // Killed by a non-player entity or self killed
2279 if (selfkill) return;
2280
2281 // Ignore Nemesis/Survivor Frags?
2282 if ((g_nemesis[attacker] && get_pcvar_num(cvar_nemignorefrags)) || (g_survivor[attacker] && get_pcvar_num(cvar_survignorefrags)))
2283 RemoveFrags(attacker, victim)
2284
2285 // Zombie/nemesis killed human, reward ammo packs
2286 if (g_zombie[attacker] && (!g_nemesis[attacker] || !get_pcvar_num(cvar_nemignoreammo)))
2287 g_ammopacks[attacker] += get_pcvar_num(cvar_ammoinfect)
2288
2289 // Human killed zombie, add up the extra frags for kill
2290 if (!g_zombie[attacker] && get_pcvar_num(cvar_fragskill) > 1)
2291 UpdateFrags(attacker, victim, get_pcvar_num(cvar_fragskill) - 1, 0, 0)
2292
2293 // Zombie killed human, add up the extra frags for kill
2294 if (g_zombie[attacker] && get_pcvar_num(cvar_fragsinfect) > 1)
2295 UpdateFrags(attacker, victim, get_pcvar_num(cvar_fragsinfect) - 1, 0, 0)
2296}
2297
2298// Ham Player Killed Post Forward
2299public fw_PlayerKilled_Post()
2300{
2301 // Last Zombie Check
2302 fnCheckLastZombie()
2303}
2304
2305// Ham Take Damage Forward
2306public fw_TakeDamage(victim, inflictor, attacker, Float:damage, damage_type)
2307{
2308 // Non-player damage or self damage
2309 if (victim == attacker || !is_user_valid_connected(attacker))
2310 return HAM_IGNORED;
2311
2312 // New round starting or round ended
2313 if (g_newround || g_endround)
2314 return HAM_SUPERCEDE;
2315
2316 // Victim shouldn't take damage or victim is frozen
2317 if (g_nodamage[victim] || g_frozen[victim])
2318 return HAM_SUPERCEDE;
2319
2320 // Prevent friendly fire
2321 if (g_zombie[attacker] == g_zombie[victim])
2322 return HAM_SUPERCEDE;
2323
2324 // Attacker is human...
2325 if (!g_zombie[attacker])
2326 {
2327 // Armor multiplier for the final damage on normal zombies
2328 if (!g_nemesis[victim])
2329 {
2330 damage *= get_pcvar_float(cvar_zombiearmor)
2331 SetHamParamFloat(4, damage)
2332 }
2333
2334 // Reward ammo packs
2335 if (!g_survivor[attacker] || !get_pcvar_num(cvar_survignoreammo))
2336 {
2337 // Store damage dealt
2338 g_damagedealt[attacker] += floatround(damage)
2339
2340 // Reward ammo packs for every [ammo damage] dealt
2341 while (g_damagedealt[attacker] > get_pcvar_num(cvar_ammodamage))
2342 {
2343 g_ammopacks[attacker]++
2344 g_damagedealt[attacker] -= get_pcvar_num(cvar_ammodamage)
2345 }
2346 }
2347
2348 return HAM_IGNORED;
2349 }
2350
2351 // Attacker is zombie...
2352
2353 // Prevent infection/damage by HE grenade (bugfix)
2354 if (damage_type & DMG_HEGRENADE)
2355 return HAM_SUPERCEDE;
2356
2357 // Nemesis?
2358 if (g_nemesis[attacker])
2359 {
2360 // Ignore nemesis damage override if damage comes from a 3rd party entity
2361 // (to prevent this from affecting a sub-plugin's rockets e.g.)
2362 if (inflictor == attacker)
2363 {
2364 // Set nemesis damage
2365 SetHamParamFloat(4, get_pcvar_float(cvar_nemdamage))
2366 }
2367
2368 return HAM_IGNORED;
2369 }
2370
2371 // Last human or not an infection round
2372 if (g_survround || g_nemround || g_swarmround || g_plagueround || fnGetHumans() == 1)
2373 return HAM_IGNORED; // human is killed
2374
2375 // Does human armor need to be reduced before infecting?
2376 if (get_pcvar_num(cvar_humanarmor))
2377 {
2378 // Get victim armor
2379 static Float:armor
2380 pev(victim, pev_armorvalue, armor)
2381
2382 // Block the attack if he has some
2383 if (armor > 0.0)
2384 {
2385 emit_sound(victim, CHAN_BODY, sound_armorhit, 1.0, ATTN_NORM, 0, PITCH_NORM)
2386 set_pev(victim, pev_armorvalue, floatmax(0.0, armor - damage))
2387 return HAM_SUPERCEDE;
2388 }
2389 }
2390
2391 // Infection allowed
2392 zombieme(victim, attacker, 0, 0, 1) // turn into zombie
2393 return HAM_SUPERCEDE;
2394}
2395
2396// Ham Take Damage Post Forward
2397public fw_TakeDamage_Post(victim)
2398{
2399 // --- Check if victim should be Pain Shock Free ---
2400
2401 // Check if proper CVARs are enabled
2402 if (g_zombie[victim])
2403 {
2404 if (g_nemesis[victim])
2405 {
2406 if (!get_pcvar_num(cvar_nempainfree)) return;
2407 }
2408 else
2409 {
2410 switch (get_pcvar_num(cvar_zombiepainfree))
2411 {
2412 case 0: return;
2413 case 2: if (!g_lastzombie[victim]) return;
2414 }
2415 }
2416 }
2417 else
2418 {
2419 if (g_survivor[victim])
2420 {
2421 if (!get_pcvar_num(cvar_survpainfree)) return;
2422 }
2423 else return;
2424 }
2425
2426 // Set pain shock free offset
2427 set_pdata_float(victim, OFFSET_PAINSHOCK, 1.0, OFFSET_LINUX)
2428}
2429
2430// Ham Trace Attack Forward
2431public fw_TraceAttack(victim, attacker, Float:damage, Float:direction[3], tracehandle, damage_type)
2432{
2433 // Non-player damage or self damage
2434 if (victim == attacker || !is_user_valid_connected(attacker))
2435 return HAM_IGNORED;
2436
2437 // New round starting or round ended
2438 if (g_newround || g_endround)
2439 return HAM_SUPERCEDE;
2440
2441 // Victim shouldn't take damage or victim is frozen
2442 if (g_nodamage[victim] || g_frozen[victim])
2443 return HAM_SUPERCEDE;
2444
2445 // Prevent friendly fire
2446 if (g_zombie[attacker] == g_zombie[victim])
2447 return HAM_SUPERCEDE;
2448
2449 // Victim isn't a zombie or not bullet damage, nothing else to do here
2450 if (!g_zombie[victim] || !(damage_type & DMG_BULLET))
2451 return HAM_IGNORED;
2452
2453 // If zombie hitzones are enabled, check whether we hit an allowed one
2454 if (get_pcvar_num(cvar_hitzones) && !g_nemesis[victim] && !(get_pcvar_num(cvar_hitzones) & (1<<get_tr2(tracehandle, TR_iHitgroup))))
2455 return HAM_SUPERCEDE;
2456
2457 // Knockback disabled, nothing else to do here
2458 if (!get_pcvar_num(cvar_knockback))
2459 return HAM_IGNORED;
2460
2461 // Nemesis knockback disabled, nothing else to do here
2462 if (g_nemesis[victim] && get_pcvar_float(cvar_nemknockback) == 0.0)
2463 return HAM_IGNORED;
2464
2465 // Get whether the victim is in a crouch state
2466 static ducking
2467 ducking = pev(victim, pev_flags) & (FL_DUCKING | FL_ONGROUND) == (FL_DUCKING | FL_ONGROUND)
2468
2469 // Zombie knockback when ducking disabled
2470 if (ducking && get_pcvar_float(cvar_knockbackducking) == 0.0)
2471 return HAM_IGNORED;
2472
2473 // Get distance between players
2474 static origin1[3], origin2[3]
2475 get_user_origin(victim, origin1)
2476 get_user_origin(attacker, origin2)
2477
2478 // Max distance exceeded
2479 if (get_distance(origin1, origin2) > get_pcvar_num(cvar_knockbackdist))
2480 return HAM_IGNORED;
2481
2482 // Get victim's velocity
2483 static Float:velocity[3]
2484 pev(victim, pev_velocity, velocity)
2485
2486 // Use damage on knockback calculation
2487 if (get_pcvar_num(cvar_knockbackdamage))
2488 xs_vec_mul_scalar(direction, damage, direction)
2489
2490 // Use weapon power on knockback calculation
2491 if (get_pcvar_num(cvar_knockbackpower) && kb_weapon_power[g_currentweapon[attacker]] > 0.0)
2492 xs_vec_mul_scalar(direction, kb_weapon_power[g_currentweapon[attacker]], direction)
2493
2494 // Apply ducking knockback multiplier
2495 if (ducking)
2496 xs_vec_mul_scalar(direction, get_pcvar_float(cvar_knockbackducking), direction)
2497
2498 // Apply zombie class/nemesis knockback multiplier
2499 if (g_nemesis[victim])
2500 xs_vec_mul_scalar(direction, get_pcvar_float(cvar_nemknockback), direction)
2501 else
2502 xs_vec_mul_scalar(direction, g_zombie_knockback[victim], direction)
2503
2504 // Add up the new vector
2505 xs_vec_add(velocity, direction, direction)
2506
2507 // Should knockback also affect vertical velocity?
2508 if (!get_pcvar_num(cvar_knockbackzvel))
2509 direction[2] = velocity[2]
2510
2511 // Set the knockback'd victim's velocity
2512 set_pev(victim, pev_velocity, direction)
2513
2514 return HAM_IGNORED;
2515}
2516
2517// Ham Use Stationary Gun Forward
2518public fw_UseStationary(entity, caller, activator, use_type)
2519{
2520 // Prevent zombies from using stationary guns
2521 if (use_type == USE_USING && is_user_valid_connected(caller) && g_zombie[caller])
2522 return HAM_SUPERCEDE;
2523
2524 return HAM_IGNORED;
2525}
2526
2527// Ham Use Stationary Gun Post Forward
2528public fw_UseStationary_Post(entity, caller, activator, use_type)
2529{
2530 // Someone stopped using a stationary gun
2531 if (use_type == USE_STOPPED && is_user_valid_connected(caller))
2532 replace_weapon_models(caller, g_currentweapon[caller]) // replace weapon models (bugfix)
2533}
2534
2535// Ham Use Pushable Forward
2536public fw_UsePushable()
2537{
2538 // Prevent speed bug with pushables?
2539 if (get_pcvar_num(cvar_blockpushables))
2540 return HAM_SUPERCEDE;
2541
2542 return HAM_IGNORED;
2543}
2544
2545// Ham Weapon Touch Forward
2546public fw_TouchWeapon(weapon, id)
2547{
2548 // Not a player
2549 if (!is_user_valid_connected(id))
2550 return HAM_IGNORED;
2551
2552 // Dont pickup weapons if zombie or survivor (+PODBot MM fix)
2553 if (g_zombie[id] || (g_survivor[id] && !g_isbot[id]))
2554 return HAM_SUPERCEDE;
2555
2556 return HAM_IGNORED;
2557}
2558
2559// Ham Weapon Pickup Forward
2560public fw_AddPlayerItem(id, weapon_ent)
2561{
2562 // HACK: Retrieve our custom extra ammo from the weapon
2563 static extra_ammo
2564 extra_ammo = pev(weapon_ent, PEV_ADDITIONAL_AMMO)
2565
2566 // If present
2567 if (extra_ammo)
2568 {
2569 // Get weapon's id
2570 static weaponid
2571 weaponid = cs_get_weapon_id(weapon_ent)
2572
2573 // Add to player's bpammo
2574 ExecuteHamB(Ham_GiveAmmo, id, extra_ammo, AMMOTYPE[weaponid], MAXBPAMMO[weaponid])
2575 set_pev(weapon_ent, PEV_ADDITIONAL_AMMO, 0)
2576 }
2577}
2578
2579// Ham Weapon Deploy Forward
2580public fw_Item_Deploy_Post(weapon_ent)
2581{
2582 // Get weapon's owner
2583 static owner
2584 owner = fm_cs_get_weapon_ent_owner(weapon_ent)
2585
2586 // Get weapon's id
2587 static weaponid
2588 weaponid = cs_get_weapon_id(weapon_ent)
2589
2590 // Store current weapon's id for reference
2591 g_currentweapon[owner] = weaponid
2592
2593 // Replace weapon models with custom ones
2594 replace_weapon_models(owner, weaponid)
2595
2596 // Zombie not holding an allowed weapon for some reason
2597 if (g_zombie[owner] && !((1<<weaponid) & ZOMBIE_ALLOWED_WEAPONS_BITSUM))
2598 {
2599 // Switch to knife
2600 g_currentweapon[owner] = CSW_KNIFE
2601 engclient_cmd(owner, "weapon_knife")
2602 }
2603}
2604
2605// WeaponMod bugfix
2606//forward wpn_gi_reset_weapon(id);
2607public wpn_gi_reset_weapon(id)
2608{
2609 // Replace knife model
2610 replace_weapon_models(id, CSW_KNIFE)
2611}
2612
2613// Client joins the game
2614public client_putinserver(id)
2615{
2616 // Plugin disabled?
2617 if (!g_pluginenabled) return;
2618
2619 // Player joined
2620 g_isconnected[id] = true
2621
2622 // Cache player's name
2623 get_user_name(id, g_playername[id], charsmax(g_playername[]))
2624
2625 // Initialize player vars
2626 reset_vars(id, 1)
2627
2628 // Load player stats?
2629 if (get_pcvar_num(cvar_statssave)) load_stats(id)
2630
2631 // Set some tasks for humans only
2632 if (!is_user_bot(id))
2633 {
2634 // Set the custom HUD display task
2635 set_task(1.0, "ShowHUD", id+TASK_SHOWHUD, _, _, "b")
2636
2637 // Disable minmodels for clients to see zombies properly
2638 set_task(5.0, "disable_minmodels", id)
2639 }
2640 else
2641 {
2642 // Set bot flag
2643 g_isbot[id] = true
2644
2645 // CZ bots seem to use a different "classtype" for player entities
2646 // (or something like that) which needs to be hooked separately
2647 if (!g_hamczbots && cvar_botquota)
2648 {
2649 // Set a task to let the private data initialize
2650 set_task(0.1, "register_ham_czbots", id)
2651 }
2652 }
2653}
2654
2655// Client leaving
2656public fw_ClientDisconnect(id)
2657{
2658 // Check that we still have both humans and zombies to keep the round going
2659 if (g_isalive[id]) check_round(id)
2660
2661 // Temporarily save player stats?
2662 if (get_pcvar_num(cvar_statssave)) save_stats(id)
2663
2664 // Remove previous tasks
2665 remove_task(id+TASK_TEAM)
2666 remove_task(id+TASK_MODEL)
2667 remove_task(id+TASK_FLASH)
2668 remove_task(id+TASK_CHARGE)
2669 remove_task(id+TASK_SPAWN)
2670 remove_task(id+TASK_BLOOD)
2671 remove_task(id+TASK_AURA)
2672 remove_task(id+TASK_BURN)
2673 remove_task(id+TASK_NVISION)
2674 remove_task(id+TASK_SHOWHUD)
2675
2676 if (g_handle_models_on_separate_ent)
2677 {
2678 // Remove custom model entities
2679 fm_remove_model_ents(id)
2680 }
2681
2682 // Player left, clear cached flags
2683 g_isconnected[id] = false
2684 g_isbot[id] = false
2685 g_isalive[id] = false
2686}
2687
2688// Client left
2689public fw_ClientDisconnect_Post()
2690{
2691 // Last Zombie Check
2692 fnCheckLastZombie()
2693}
2694
2695// Client Kill Forward
2696public fw_ClientKill()
2697{
2698 // Prevent players from killing themselves?
2699 if (get_pcvar_num(cvar_blocksuicide))
2700 return FMRES_SUPERCEDE;
2701
2702 return FMRES_IGNORED;
2703}
2704
2705// Emit Sound Forward
2706public fw_EmitSound(id, channel, const sample[], Float:volume, Float:attn, flags, pitch)
2707{
2708 // Block all those unneeeded hostage sounds
2709 if (sample[0] == 'h' && sample[1] == 'o' && sample[2] == 's' && sample[3] == 't' && sample[4] == 'a' && sample[5] == 'g' && sample[6] == 'e')
2710 return FMRES_SUPERCEDE;
2711
2712 // Replace these next sounds for zombies only
2713 if (!is_user_valid_connected(id) || !g_zombie[id])
2714 return FMRES_IGNORED;
2715
2716 static sound[64]
2717
2718 // Zombie being hit
2719 if (sample[7] == 'b' && sample[8] == 'h' && sample[9] == 'i' && sample[10] == 't')
2720 {
2721 if (g_nemesis[id])
2722 {
2723 ArrayGetString(nemesis_pain, random_num(0, ArraySize(nemesis_pain) - 1), sound, charsmax(sound))
2724 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2725 }
2726 else
2727 {
2728 ArrayGetString(zombie_pain, random_num(0, ArraySize(zombie_pain) - 1), sound, charsmax(sound))
2729 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2730 }
2731 return FMRES_SUPERCEDE;
2732 }
2733
2734 // Zombie attacks with knife
2735 if (sample[8] == 'k' && sample[9] == 'n' && sample[10] == 'i')
2736 {
2737 if (sample[14] == 's' && sample[15] == 'l' && sample[16] == 'a') // slash
2738 {
2739 ArrayGetString(zombie_miss_slash, random_num(0, ArraySize(zombie_miss_slash) - 1), sound, charsmax(sound))
2740 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2741 return FMRES_SUPERCEDE;
2742 }
2743 if (sample[14] == 'h' && sample[15] == 'i' && sample[16] == 't') // hit
2744 {
2745 if (sample[17] == 'w') // wall
2746 {
2747 ArrayGetString(zombie_miss_wall, random_num(0, ArraySize(zombie_miss_wall) - 1), sound, charsmax(sound))
2748 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2749 return FMRES_SUPERCEDE;
2750 }
2751 else
2752 {
2753 ArrayGetString(zombie_hit_normal, random_num(0, ArraySize(zombie_hit_normal) - 1), sound, charsmax(sound))
2754 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2755 return FMRES_SUPERCEDE;
2756 }
2757 }
2758 if (sample[14] == 's' && sample[15] == 't' && sample[16] == 'a') // stab
2759 {
2760 ArrayGetString(zombie_hit_stab, random_num(0, ArraySize(zombie_hit_stab) - 1), sound, charsmax(sound))
2761 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2762 return FMRES_SUPERCEDE;
2763 }
2764 }
2765
2766 // Zombie dies
2767 if (sample[7] == 'd' && ((sample[8] == 'i' && sample[9] == 'e') || (sample[8] == 'e' && sample[9] == 'a')))
2768 {
2769 ArrayGetString(zombie_die, random_num(0, ArraySize(zombie_die) - 1), sound, charsmax(sound))
2770 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2771 return FMRES_SUPERCEDE;
2772 }
2773
2774 // Zombie falls off
2775 if (sample[10] == 'f' && sample[11] == 'a' && sample[12] == 'l' && sample[13] == 'l')
2776 {
2777 ArrayGetString(zombie_fall, random_num(0, ArraySize(zombie_fall) - 1), sound, charsmax(sound))
2778 emit_sound(id, channel, sound, volume, attn, flags, pitch)
2779 return FMRES_SUPERCEDE;
2780 }
2781
2782 return FMRES_IGNORED;
2783}
2784
2785// Forward Set ClientKey Value -prevent CS from changing player models-
2786public fw_SetClientKeyValue(id, const infobuffer[], const key[])
2787{
2788 // Block CS model changes
2789 if (key[0] == 'm' && key[1] == 'o' && key[2] == 'd' && key[3] == 'e' && key[4] == 'l')
2790 return FMRES_SUPERCEDE;
2791
2792 return FMRES_IGNORED;
2793}
2794
2795// Forward Client User Info Changed -prevent players from changing models-
2796public fw_ClientUserInfoChanged(id)
2797{
2798 // Cache player's name
2799 get_user_name(id, g_playername[id], charsmax(g_playername[]))
2800
2801 if (!g_handle_models_on_separate_ent)
2802 {
2803 // Get current model
2804 static currentmodel[32]
2805 fm_cs_get_user_model(id, currentmodel, charsmax(currentmodel))
2806
2807 // If they're different, set model again
2808 if (!equal(currentmodel, g_playermodel[id]) && !task_exists(id+TASK_MODEL))
2809 fm_cs_set_user_model(id+TASK_MODEL)
2810 }
2811}
2812
2813// Forward Get Game Description
2814public fw_GetGameDescription()
2815{
2816 // Return the mod name so it can be easily identified
2817 forward_return(FMV_STRING, g_modname)
2818
2819 return FMRES_SUPERCEDE;
2820}
2821
2822// Forward Set Model
2823public fw_SetModel(entity, const model[])
2824{
2825 // We don't care
2826 if (strlen(model) < 8)
2827 return;
2828
2829 // Remove weapons?
2830 if (get_pcvar_float(cvar_removedropped) > 0.0)
2831 {
2832 // Get entity's classname
2833 static classname[10]
2834 pev(entity, pev_classname, classname, charsmax(classname))
2835
2836 // Check if it's a weapon box
2837 if (equal(classname, "weaponbox"))
2838 {
2839 // They get automatically removed when thinking
2840 set_pev(entity, pev_nextthink, get_gametime() + get_pcvar_float(cvar_removedropped))
2841 return;
2842 }
2843 }
2844
2845 // Narrow down our matches a bit
2846 if (model[7] != 'w' || model[8] != '_')
2847 return;
2848
2849 // Get damage time of grenade
2850 static Float:dmgtime
2851 pev(entity, pev_dmgtime, dmgtime)
2852
2853 // Grenade not yet thrown
2854 if (dmgtime == 0.0)
2855 return;
2856
2857 // Get whether grenade's owner is a zombie
2858 if (g_zombie[pev(entity, pev_owner)])
2859 {
2860 if (model[9] == 'h' && model[10] == 'e' && get_pcvar_num(cvar_extrainfbomb)) // Infection Bomb
2861 {
2862 // Give it a glow
2863 fm_set_rendering(entity, kRenderFxGlowShell, 0, 200, 0, kRenderNormal, 16);
2864
2865 // And a colored trail
2866 message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
2867 write_byte(TE_BEAMFOLLOW) // TE id
2868 write_short(entity) // entity
2869 write_short(g_trailSpr) // sprite
2870 write_byte(10) // life
2871 write_byte(10) // width
2872 write_byte(0) // r
2873 write_byte(200) // g
2874 write_byte(0) // b
2875 write_byte(200) // brightness
2876 message_end()
2877
2878 // Set grenade type on the thrown grenade entity
2879 set_pev(entity, PEV_NADE_TYPE, NADE_TYPE_INFECTION)
2880 }
2881 }
2882 else if (model[9] == 'h' && model[10] == 'e' && get_pcvar_num(cvar_firegrenades)) // Napalm Grenade
2883 {
2884 // Give it a glow
2885 fm_set_rendering(entity, kRenderFxGlowShell, 200, 0, 0, kRenderNormal, 16);
2886
2887 // And a colored trail
2888 message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
2889 write_byte(TE_BEAMFOLLOW) // TE id
2890 write_short(entity) // entity
2891 write_short(g_trailSpr) // sprite
2892 write_byte(10) // life
2893 write_byte(10) // width
2894 write_byte(200) // r
2895 write_byte(0) // g
2896 write_byte(0) // b
2897 write_byte(200) // brightness
2898 message_end()
2899
2900 // Set grenade type on the thrown grenade entity
2901 set_pev(entity, PEV_NADE_TYPE, NADE_TYPE_NAPALM)
2902 }
2903 else if (model[9] == 'f' && model[10] == 'l' && get_pcvar_num(cvar_frostgrenades)) // Frost Grenade
2904 {
2905 // Give it a glow
2906 fm_set_rendering(entity, kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 16);
2907
2908 // And a colored trail
2909 message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
2910 write_byte(TE_BEAMFOLLOW) // TE id
2911 write_short(entity) // entity
2912 write_short(g_trailSpr) // sprite
2913 write_byte(10) // life
2914 write_byte(10) // width
2915 write_byte(0) // r
2916 write_byte(100) // g
2917 write_byte(200) // b
2918 write_byte(200) // brightness
2919 message_end()
2920
2921 // Set grenade type on the thrown grenade entity
2922 set_pev(entity, PEV_NADE_TYPE, NADE_TYPE_FROST)
2923 }
2924 else if (model[9] == 's' && model[10] == 'm' && get_pcvar_num(cvar_flaregrenades)) // Flare
2925 {
2926 // Build flare's color
2927 static rgb[3]
2928 switch (get_pcvar_num(cvar_flarecolor))
2929 {
2930 case 0: // white
2931 {
2932 rgb[0] = 255 // r
2933 rgb[1] = 255 // g
2934 rgb[2] = 255 // b
2935 }
2936 case 1: // red
2937 {
2938 rgb[0] = random_num(50,255) // r
2939 rgb[1] = 0 // g
2940 rgb[2] = 0 // b
2941 }
2942 case 2: // green
2943 {
2944 rgb[0] = 0 // r
2945 rgb[1] = random_num(50,255) // g
2946 rgb[2] = 0 // b
2947 }
2948 case 3: // blue
2949 {
2950 rgb[0] = 0 // r
2951 rgb[1] = 0 // g
2952 rgb[2] = random_num(50,255) // b
2953 }
2954 case 4: // random (all colors)
2955 {
2956 rgb[0] = random_num(50,200) // r
2957 rgb[1] = random_num(50,200) // g
2958 rgb[2] = random_num(50,200) // b
2959 }
2960 case 5: // random (r,g,b)
2961 {
2962 switch (random_num(1, 3))
2963 {
2964 case 1: // red
2965 {
2966 rgb[0] = random_num(50,255) // r
2967 rgb[1] = 0 // g
2968 rgb[2] = 0 // b
2969 }
2970 case 2: // green
2971 {
2972 rgb[0] = 0 // r
2973 rgb[1] = random_num(50,255) // g
2974 rgb[2] = 0 // b
2975 }
2976 case 3: // blue
2977 {
2978 rgb[0] = 0 // r
2979 rgb[1] = 0 // g
2980 rgb[2] = random_num(50,255) // b
2981 }
2982 }
2983 }
2984 }
2985
2986 // Give it a glow
2987 fm_set_rendering(entity, kRenderFxGlowShell, rgb[0], rgb[1], rgb[2], kRenderNormal, 16);
2988
2989 // And a colored trail
2990 message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
2991 write_byte(TE_BEAMFOLLOW) // TE id
2992 write_short(entity) // entity
2993 write_short(g_trailSpr) // sprite
2994 write_byte(10) // life
2995 write_byte(10) // width
2996 write_byte(rgb[0]) // r
2997 write_byte(rgb[1]) // g
2998 write_byte(rgb[2]) // b
2999 write_byte(200) // brightness
3000 message_end()
3001
3002 // Set grenade type on the thrown grenade entity
3003 set_pev(entity, PEV_NADE_TYPE, NADE_TYPE_FLARE)
3004
3005 // Set flare color on the thrown grenade entity
3006 set_pev(entity, PEV_FLARE_COLOR, rgb)
3007 }
3008}
3009
3010// Ham Grenade Think Forward
3011public fw_ThinkGrenade(entity)
3012{
3013 // Invalid entity
3014 if (!pev_valid(entity)) return HAM_IGNORED;
3015
3016 // Get damage time of grenade
3017 static Float:dmgtime, Float:current_time
3018 pev(entity, pev_dmgtime, dmgtime)
3019 current_time = get_gametime()
3020
3021 // Check if it's time to go off
3022 if (dmgtime > current_time)
3023 return HAM_IGNORED;
3024
3025 // Check if it's one of our custom nades
3026 switch (pev(entity, PEV_NADE_TYPE))
3027 {
3028 case NADE_TYPE_INFECTION: // Infection Bomb
3029 {
3030 infection_explode(entity)
3031 return HAM_SUPERCEDE;
3032 }
3033 case NADE_TYPE_NAPALM: // Napalm Grenade
3034 {
3035 fire_explode(entity)
3036 return HAM_SUPERCEDE;
3037 }
3038 case NADE_TYPE_FROST: // Frost Grenade
3039 {
3040 frost_explode(entity)
3041 return HAM_SUPERCEDE;
3042 }
3043 case NADE_TYPE_FLARE: // Flare
3044 {
3045 // Get its duration
3046 static duration
3047 duration = pev(entity, PEV_FLARE_DURATION)
3048
3049 // Already went off, do lighting loop for the duration of PEV_FLARE_DURATION
3050 if (duration > 0)
3051 {
3052 // Check whether this is the last loop
3053 if (duration == 1)
3054 {
3055 // Get rid of the flare entity
3056 engfunc(EngFunc_RemoveEntity, entity)
3057 return HAM_SUPERCEDE;
3058 }
3059
3060 // Light it up!
3061 flare_lighting(entity, duration)
3062
3063 // Set time for next loop
3064 set_pev(entity, PEV_FLARE_DURATION, --duration)
3065 set_pev(entity, pev_dmgtime, current_time + 5.0)
3066 }
3067 // Light up when it's stopped on ground
3068 else if ((pev(entity, pev_flags) & FL_ONGROUND) && fm_get_speed(entity) < 10)
3069 {
3070 // Flare sound
3071 static sound[64]
3072 ArrayGetString(grenade_flare, random_num(0, ArraySize(grenade_flare) - 1), sound, charsmax(sound))
3073 emit_sound(entity, CHAN_WEAPON, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
3074
3075 // Set duration and start lightning loop on next think
3076 set_pev(entity, PEV_FLARE_DURATION, 1 + get_pcvar_num(cvar_flareduration)/5)
3077 set_pev(entity, pev_dmgtime, current_time + 0.1)
3078 }
3079 else
3080 {
3081 // Delay explosion until we hit ground
3082 set_pev(entity, pev_dmgtime, current_time + 0.5)
3083 }
3084 }
3085 }
3086
3087 return HAM_IGNORED;
3088}
3089
3090// Forward CmdStart
3091public fw_CmdStart(id, handle)
3092{
3093 // Not alive
3094 if (!g_isalive[id])
3095 return;
3096
3097 // This logic looks kinda weird, but it should work in theory...
3098 // p = g_zombie[id], q = g_survivor[id], r = g_cached_customflash
3099 // ¬(p v q v (¬p ^ r)) <==> ¬p ^ ¬q ^ (p v ¬r)
3100 if (!g_zombie[id] && !g_survivor[id] && (g_zombie[id] || !g_cached_customflash))
3101 return;
3102
3103 // Check if it's a flashlight impulse
3104 if (get_uc(handle, UC_Impulse) != IMPULSE_FLASHLIGHT)
3105 return;
3106
3107 // Block it I say!
3108 set_uc(handle, UC_Impulse, 0)
3109
3110 // Should human's custom flashlight be turned on?
3111 if (!g_zombie[id] && !g_survivor[id] && g_flashbattery[id] > 2 && get_gametime() - g_lastflashtime[id] > 1.2)
3112 {
3113 // Prevent calling flashlight too quickly (bugfix)
3114 g_lastflashtime[id] = get_gametime()
3115
3116 // Toggle custom flashlight
3117 g_flashlight[id] = !(g_flashlight[id])
3118
3119 // Play flashlight toggle sound
3120 emit_sound(id, CHAN_ITEM, sound_flashlight, 1.0, ATTN_NORM, 0, PITCH_NORM)
3121
3122 // Update flashlight status on the HUD
3123 message_begin(MSG_ONE, g_msgFlashlight, _, id)
3124 write_byte(g_flashlight[id]) // toggle
3125 write_byte(g_flashbattery[id]) // battery
3126 message_end()
3127
3128 // Remove previous tasks
3129 remove_task(id+TASK_CHARGE)
3130 remove_task(id+TASK_FLASH)
3131
3132 // Set the flashlight charge task
3133 set_task(1.0, "flashlight_charge", id+TASK_CHARGE, _, _, "b")
3134
3135 // Call our custom flashlight task if enabled
3136 if (g_flashlight[id]) set_task(0.1, "set_user_flashlight", id+TASK_FLASH, _, _, "b")
3137 }
3138}
3139
3140// Forward Player PreThink
3141public fw_PlayerPreThink(id)
3142{
3143 // Not alive
3144 if (!g_isalive[id])
3145 return;
3146
3147 // Silent footsteps for zombies?
3148 if (g_cached_zombiesilent && g_zombie[id] && !g_nemesis[id])
3149 set_pev(id, pev_flTimeStepSound, STEPTIME_SILENT)
3150
3151 // Set Player MaxSpeed
3152 if (g_frozen[id])
3153 {
3154 set_pev(id, pev_velocity, Float:{0.0,0.0,0.0}) // stop motion
3155 set_pev(id, pev_maxspeed, 1.0) // prevent from moving
3156 return; // shouldn't leap while frozen
3157 }
3158 else if (g_freezetime)
3159 {
3160 return; // shouldn't leap while in freezetime
3161 }
3162 else
3163 {
3164 if (g_zombie[id])
3165 {
3166 if (g_nemesis[id])
3167 set_pev(id, pev_maxspeed, g_cached_nemspd)
3168 else
3169 set_pev(id, pev_maxspeed, g_zombie_spd[id])
3170 }
3171 else
3172 {
3173 if (g_survivor[id])
3174 set_pev(id, pev_maxspeed, g_cached_survspd)
3175 else
3176 set_pev(id, pev_maxspeed, g_cached_humanspd)
3177 }
3178 }
3179
3180 // --- Check if player should leap ---
3181
3182 // Check if proper CVARs are enabled and retrieve leap settings
3183 static Float:cooldown, Float:current_time
3184 if (g_zombie[id])
3185 {
3186 if (g_nemesis[id])
3187 {
3188 if (!g_cached_leapnemesis) return;
3189 cooldown = g_cached_leapnemesiscooldown
3190 }
3191 else
3192 {
3193 switch (g_cached_leapzombies)
3194 {
3195 case 0: return;
3196 case 2: if (!g_firstzombie[id]) return;
3197 case 3: if (!g_lastzombie[id]) return;
3198 }
3199 cooldown = g_cached_leapzombiescooldown
3200 }
3201 }
3202 else
3203 {
3204 if (g_survivor[id])
3205 {
3206 if (!g_cached_leapsurvivor) return;
3207 cooldown = g_cached_leapsurvivorcooldown
3208 }
3209 else return;
3210 }
3211
3212 current_time = get_gametime()
3213
3214 // Cooldown not over yet
3215 if (current_time - g_lastleaptime[id] < cooldown)
3216 return;
3217
3218 // Not doing a longjump (don't perform check for bots, they leap automatically)
3219 if (!g_isbot[id] && !(pev(id, pev_button) & (IN_JUMP | IN_DUCK) == (IN_JUMP | IN_DUCK)))
3220 return;
3221
3222 // Not on ground or not enough speed
3223 if (!(pev(id, pev_flags) & FL_ONGROUND) || fm_get_speed(id) < 80)
3224 return;
3225
3226 static Float:velocity[3]
3227
3228 // Make velocity vector
3229 velocity_by_aim(id, g_survivor[id] ? get_pcvar_num(cvar_leapsurvivorforce) : g_nemesis[id] ? get_pcvar_num(cvar_leapnemesisforce) : get_pcvar_num(cvar_leapzombiesforce), velocity)
3230
3231 // Set custom height
3232 velocity[2] = g_survivor[id] ? get_pcvar_float(cvar_leapsurvivorheight) : g_nemesis[id] ? get_pcvar_float(cvar_leapnemesisheight) : get_pcvar_float(cvar_leapzombiesheight)
3233
3234 // Apply the new velocity
3235 set_pev(id, pev_velocity, velocity)
3236
3237 // Update last leap time
3238 g_lastleaptime[id] = current_time
3239}
3240
3241/*================================================================================
3242 [Client Commands]
3243=================================================================================*/
3244
3245// Say "/zpmenu"
3246public clcmd_saymenu(id)
3247{
3248 show_menu_game(id) // show game menu
3249}
3250
3251// Say "/unstuck"
3252public clcmd_sayunstuck(id)
3253{
3254 menu_game(id, 3) // try to get unstuck
3255}
3256
3257// Nightvision toggle
3258public clcmd_nightvision(id)
3259{
3260 if (g_nvision[id])
3261 {
3262 // Enable-disable
3263 g_nvisionenabled[id] = !(g_nvisionenabled[id])
3264
3265 // Custom nvg?
3266 if (get_pcvar_num(cvar_customnvg))
3267 {
3268 remove_task(id+TASK_NVISION)
3269 if (g_nvisionenabled[id]) set_task(0.1, "set_user_nvision", id+TASK_NVISION, _, _, "b")
3270 }
3271 else
3272 set_user_gnvision(id, g_nvisionenabled[id])
3273 }
3274
3275 return PLUGIN_HANDLED;
3276}
3277
3278// Weapon Drop
3279public clcmd_drop(id)
3280{
3281 // Survivor should stick with its weapon
3282 if (g_survivor[id])
3283 return PLUGIN_HANDLED;
3284
3285 return PLUGIN_CONTINUE;
3286}
3287
3288// Buy BP Ammo
3289public clcmd_buyammo(id)
3290{
3291 // Not alive or infinite ammo setting enabled
3292 if (!g_isalive[id] || get_pcvar_num(cvar_infammo))
3293 return PLUGIN_HANDLED;
3294
3295 // Not human
3296 if (g_zombie[id])
3297 {
3298 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_HUMAN_ONLY")
3299 return PLUGIN_HANDLED;
3300 }
3301
3302 // Not enough ammo packs
3303 if (g_ammopacks[id] < 1)
3304 {
3305 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "NOT_ENOUGH_AMMO")
3306 return PLUGIN_HANDLED;
3307 }
3308
3309 // Get user weapons
3310 static weapons[32], num, i, currentammo, weaponid, refilled
3311 num = 0 // reset passed weapons count (bugfix)
3312 refilled = false
3313 get_user_weapons(id, weapons, num)
3314
3315 // Loop through them and give the right ammo type
3316 for (i = 0; i < num; i++)
3317 {
3318 // Prevents re-indexing the array
3319 weaponid = weapons[i]
3320
3321 // Primary and secondary only
3322 if (MAXBPAMMO[weaponid] > 2)
3323 {
3324 // Get current ammo of the weapon
3325 currentammo = cs_get_user_bpammo(id, weaponid)
3326
3327 // Give additional ammo
3328 ExecuteHamB(Ham_GiveAmmo, id, BUYAMMO[weaponid], AMMOTYPE[weaponid], MAXBPAMMO[weaponid])
3329
3330 // Check whether we actually refilled the weapon's ammo
3331 if (cs_get_user_bpammo(id, weaponid) - currentammo > 0) refilled = true
3332 }
3333 }
3334
3335 // Weapons already have full ammo
3336 if (!refilled) return PLUGIN_HANDLED;
3337
3338 // Deduce ammo packs, play clip purchase sound, and notify player
3339 g_ammopacks[id]--
3340 emit_sound(id, CHAN_ITEM, sound_buyammo, 1.0, ATTN_NORM, 0, PITCH_NORM)
3341 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "AMMO_BOUGHT")
3342
3343 return PLUGIN_HANDLED;
3344}
3345
3346// Block Team Change
3347public clcmd_changeteam(id)
3348{
3349 static team
3350 team = fm_cs_get_user_team(id)
3351
3352 // Unless it's a spectator joining the game
3353 if (team == FM_CS_TEAM_SPECTATOR || team == FM_CS_TEAM_UNASSIGNED)
3354 return PLUGIN_CONTINUE;
3355
3356 // Pressing 'M' (chooseteam) ingame should show the main menu instead
3357 show_menu_game(id)
3358 return PLUGIN_HANDLED;
3359}
3360
3361/*================================================================================
3362 [Menus]
3363=================================================================================*/
3364
3365// Game Menu
3366show_menu_game(id)
3367{
3368 static menu[250], len, userflags
3369 len = 0
3370 userflags = get_user_flags(id)
3371
3372 // Title
3373 len += formatex(menu[len], charsmax(menu) - len, "\y%s^n^n", g_modname)
3374
3375 // 1. Buy weapons
3376 if (get_pcvar_num(cvar_buycustom))
3377 len += formatex(menu[len], charsmax(menu) - len, "\r1.\w %L^n", id, "MENU_BUY")
3378 else
3379 len += formatex(menu[len], charsmax(menu) - len, "\d1. %L^n", id, "MENU_BUY")
3380
3381 // 2. Extra items
3382 if (get_pcvar_num(cvar_extraitems) && g_isalive[id])
3383 len += formatex(menu[len], charsmax(menu) - len, "\r2.\w %L^n", id, "MENU_EXTRABUY")
3384 else
3385 len += formatex(menu[len], charsmax(menu) - len, "\d2. %L^n", id, "MENU_EXTRABUY")
3386
3387 // 3. Zombie class
3388 if (get_pcvar_num(cvar_zclasses))
3389 len += formatex(menu[len], charsmax(menu) - len, "\r3.\w %L^n", id,"MENU_ZCLASS")
3390 else
3391 len += formatex(menu[len], charsmax(menu) - len, "\d3. %L^n", id,"MENU_ZCLASS")
3392
3393 // 4. Unstuck
3394 if (g_isalive[id])
3395 len += formatex(menu[len], charsmax(menu) - len, "\r4.\w %L^n", id, "MENU_UNSTUCK")
3396 else
3397 len += formatex(menu[len], charsmax(menu) - len, "\d4. %L^n", id, "MENU_UNSTUCK")
3398
3399 // VIP Menu
3400 len += formatex(menu[len], charsmax(menu) - len, "\r6.\w %L^n", id, "\rV.I.P\w Menu")
3401 // Party Menu
3402 len += formatex(menu[len], charsmax(menu) - len, "\r7.\w %L^n", id, "\rParty\w Menu")
3403
3404 // 6. Join spec
3405 if (!g_isalive[id] || !get_pcvar_num(cvar_blocksuicide) || (userflags & g_access_flag[ACCESS_ADMIN_MENU]))
3406 len += formatex(menu[len], charsmax(menu) - len, "\r6.\w %L^n^n", id, "MENU_SPECTATOR")
3407 else
3408 len += formatex(menu[len], charsmax(menu) - len, "\d6. %L^n^n", id, "MENU_SPECTATOR")
3409
3410 // 9. Admin menu
3411 if (userflags & g_access_flag[ACCESS_ADMIN_MENU])
3412 len += formatex(menu[len], charsmax(menu) - len, "\r9.\w %L", id, "MENU_ADMIN")
3413 else
3414 len += formatex(menu[len], charsmax(menu) - len, "\d9. %L", id, "MENU_ADMIN")
3415
3416 // 0. Exit
3417 len += formatex(menu[len], charsmax(menu) - len, "^n^n\r0.\w %L", id, "MENU_EXIT")
3418
3419 show_menu(id, KEYSMENU, menu, -1, "Game Menu")
3420}
3421
3422// Buy Menu 1
3423public show_menu_buy1(taskid)
3424{
3425 // Get player's id
3426 static id
3427 (taskid > g_maxplayers) ? (id = ID_SPAWN) : (id = taskid);
3428
3429 // Zombies or survivors get no guns
3430 if (!g_isalive[id] || g_zombie[id] || g_survivor[id])
3431 return;
3432
3433 // Bots pick their weapons randomly / Random weapons setting enabled
3434 if (get_pcvar_num(cvar_randweapons) || g_isbot[id])
3435 {
3436 buy_primary_weapon(id, random_num(0, ArraySize(g_primary_items) - 1))
3437 menu_buy2(id, random_num(0, ArraySize(g_secondary_items) - 1))
3438 return;
3439 }
3440
3441 // Automatic selection enabled for player and menu called on spawn event
3442 if (WPN_AUTO_ON && taskid > g_maxplayers)
3443 {
3444 buy_primary_weapon(id, WPN_AUTO_PRI)
3445 menu_buy2(id, WPN_AUTO_SEC)
3446 return;
3447 }
3448
3449 static menu[300], len, weap, maxloops
3450 len = 0
3451 maxloops = min(WPN_STARTID+7, WPN_MAXIDS)
3452
3453 // Title
3454 len += formatex(menu[len], charsmax(menu) - len, "\y%L \r[%d-%d]^n^n", id, "MENU_BUY1_TITLE", WPN_STARTID+1, min(WPN_STARTID+7, WPN_MAXIDS))
3455
3456 // 1-7. Weapon List
3457 for (weap = WPN_STARTID; weap < maxloops; weap++)
3458 len += formatex(menu[len], charsmax(menu) - len, "\r%d.\w %s^n", weap-WPN_STARTID+1, WEAPONNAMES[ArrayGetCell(g_primary_weaponids, weap)])
3459
3460 // 8. Auto Select
3461 len += formatex(menu[len], charsmax(menu) - len, "^n\r8.\w %L \y[%L]", id, "MENU_AUTOSELECT", id, (WPN_AUTO_ON) ? "MOTD_ENABLED" : "MOTD_DISABLED")
3462
3463 // 9. Next/Back - 0. Exit
3464 len += formatex(menu[len], charsmax(menu) - len, "^n^n\r9.\w %L/%L^n^n\r0.\w %L", id, "MENU_NEXT", id, "MENU_BACK", id, "MENU_EXIT")
3465
3466 show_menu(id, KEYSMENU, menu, -1, "Buy Menu 1")
3467}
3468
3469// Buy Menu 2
3470show_menu_buy2(id)
3471{
3472 static menu[250], len, weap, maxloops
3473 len = 0
3474 maxloops = ArraySize(g_secondary_items)
3475
3476 // Title
3477 len += formatex(menu[len], charsmax(menu) - len, "\y%L^n", id, "MENU_BUY2_TITLE")
3478
3479 // 1-6. Weapon List
3480 for (weap = 0; weap < maxloops; weap++)
3481 len += formatex(menu[len], charsmax(menu) - len, "^n\r%d.\w %s", weap+1, WEAPONNAMES[ArrayGetCell(g_secondary_weaponids, weap)])
3482
3483 // 8. Auto Select
3484 len += formatex(menu[len], charsmax(menu) - len, "^n^n\r8.\w %L \y[%L]", id, "MENU_AUTOSELECT", id, (WPN_AUTO_ON) ? "MOTD_ENABLED" : "MOTD_DISABLED")
3485
3486 // 0. Exit
3487 len += formatex(menu[len], charsmax(menu) - len, "^n^n\r0.\w %L", id, "MENU_EXIT")
3488
3489 show_menu(id, KEYSMENU, menu, -1, "Buy Menu 2")
3490}
3491
3492// Extra Items Menu
3493show_menu_extras(id)
3494{
3495 static menuid, menu[128], item, team, buffer[32]
3496
3497 // Title
3498 formatex(menu, charsmax(menu), "%L [%L]\r", id, "MENU_EXTRA_TITLE", id, g_zombie[id] ? g_nemesis[id] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE" : g_survivor[id] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3499 menuid = menu_create(menu, "menu_extras")
3500
3501 // Item List
3502 for (item = 0; item < g_extraitem_i; item++)
3503 {
3504 // Retrieve item's team
3505 team = ArrayGetCell(g_extraitem_team, item)
3506
3507 // Item not available to player's team/class
3508 if ((g_zombie[id] && !g_nemesis[id] && !(team & ZP_TEAM_ZOMBIE)) || (!g_zombie[id] && !g_survivor[id] && !(team & ZP_TEAM_HUMAN)) || (g_nemesis[id] && !(team & ZP_TEAM_NEMESIS)) || (g_survivor[id] && !(team & ZP_TEAM_SURVIVOR)))
3509 continue;
3510
3511 // Check if it's one of the hardcoded items, check availability, set translated caption
3512 switch (item)
3513 {
3514 case EXTRA_NVISION:
3515 {
3516 if (!get_pcvar_num(cvar_extranvision)) continue;
3517 formatex(buffer, charsmax(buffer), "%L", id, "MENU_EXTRA1")
3518 }
3519 case EXTRA_ANTIDOTE:
3520 {
3521 if (!get_pcvar_num(cvar_extraantidote) || g_antidotecounter >= get_pcvar_num(cvar_antidotelimit)) continue;
3522 formatex(buffer, charsmax(buffer), "%L", id, "MENU_EXTRA2")
3523 }
3524 case EXTRA_MADNESS:
3525 {
3526 if (!get_pcvar_num(cvar_extramadness) || g_madnesscounter >= get_pcvar_num(cvar_madnesslimit)) continue;
3527 formatex(buffer, charsmax(buffer), "%L", id, "MENU_EXTRA3")
3528 }
3529 case EXTRA_INFBOMB:
3530 {
3531 if (!get_pcvar_num(cvar_extrainfbomb) || g_infbombcounter >= get_pcvar_num(cvar_infbomblimit)) continue;
3532 formatex(buffer, charsmax(buffer), "%L", id, "MENU_EXTRA4")
3533 }
3534 default:
3535 {
3536 if (item >= EXTRA_WEAPONS_STARTID && item <= EXTRAS_CUSTOM_STARTID-1 && !get_pcvar_num(cvar_extraweapons)) continue;
3537 ArrayGetString(g_extraitem_name, item, buffer, charsmax(buffer))
3538 }
3539 }
3540
3541 // Add Item Name and Cost
3542 formatex(menu, charsmax(menu), "%s \y%d %L", buffer, ArrayGetCell(g_extraitem_cost, item), id, "AMMO_PACKS2")
3543 buffer[0] = item
3544 buffer[1] = 0
3545 menu_additem(menuid, menu, buffer)
3546 }
3547
3548 // No items to display?
3549 if (menu_items(menuid) <= 0)
3550 {
3551 zp_colored_print(id, "^x04[ZP]^x01 %L", id ,"CMD_NOT_EXTRAS")
3552 menu_destroy(menuid)
3553 return;
3554 }
3555
3556 // Back - Next - Exit
3557 formatex(menu, charsmax(menu), "%L", id, "MENU_BACK")
3558 menu_setprop(menuid, MPROP_BACKNAME, menu)
3559 formatex(menu, charsmax(menu), "%L", id, "MENU_NEXT")
3560 menu_setprop(menuid, MPROP_NEXTNAME, menu)
3561 formatex(menu, charsmax(menu), "%L", id, "MENU_EXIT")
3562 menu_setprop(menuid, MPROP_EXITNAME, menu)
3563
3564 menu_display(id, menuid)
3565}
3566
3567// Zombie Class Menu
3568public show_menu_zclass(id)
3569{
3570 // Player disconnected
3571 if (!g_isconnected[id])
3572 return;
3573
3574 // Bots pick their zombie class randomly
3575 if (g_isbot[id])
3576 {
3577 g_zombieclassnext[id] = random_num(0, g_zclass_i - 1)
3578 return;
3579 }
3580
3581 static menuid, menu[128], class, buffer[32], buffer2[32]
3582
3583 // Title
3584 formatex(menu, charsmax(menu), "%L\r", id, "MENU_ZCLASS_TITLE")
3585 menuid = menu_create(menu, "menu_zclass")
3586
3587 // Class List
3588 for (class = 0; class < g_zclass_i; class++)
3589 {
3590 // Retrieve name and info
3591 ArrayGetString(g_zclass_name, class, buffer, charsmax(buffer))
3592 ArrayGetString(g_zclass_info, class, buffer2, charsmax(buffer2))
3593
3594 // Add to menu
3595 if (class == g_zombieclassnext[id])
3596 formatex(menu, charsmax(menu), "\d%s %s", buffer, buffer2)
3597 else
3598 formatex(menu, charsmax(menu), "%s \y%s", buffer, buffer2)
3599
3600 buffer[0] = class
3601 buffer[1] = 0
3602 menu_additem(menuid, menu, buffer)
3603 }
3604
3605 // Back - Next - Exit
3606 formatex(menu, charsmax(menu), "%L", id, "MENU_BACK")
3607 menu_setprop(menuid, MPROP_BACKNAME, menu)
3608 formatex(menu, charsmax(menu), "%L", id, "MENU_NEXT")
3609 menu_setprop(menuid, MPROP_NEXTNAME, menu)
3610 formatex(menu, charsmax(menu), "%L", id, "MENU_EXIT")
3611 menu_setprop(menuid, MPROP_EXITNAME, menu)
3612
3613 menu_display(id, menuid)
3614}
3615
3616// Help Menu
3617show_menu_info(id)
3618{
3619 static menu[150]
3620
3621 formatex(menu, charsmax(menu), "\y%L^n^n\r1.\w %L^n\r2.\w %L^n\r3.\w %L^n\r4.\w %L^n^n\r0.\w %L", id, "MENU_INFO_TITLE", id, "MENU_INFO1", id,"MENU_INFO2", id,"MENU_INFO3", id,"MENU_INFO4", id, "MENU_EXIT")
3622 show_menu(id, KEYSMENU, menu, -1, "Mod Info")
3623}
3624
3625// Admin Menu
3626show_menu_admin(id)
3627{
3628 static menu[250], len, userflags
3629 len = 0
3630 userflags = get_user_flags(id)
3631
3632 // Title
3633 len += formatex(menu[len], charsmax(menu) - len, "\y%L^n^n", id, "MENU_ADMIN_TITLE")
3634
3635 // 1. Zombiefy/Humanize command
3636 if (userflags & (g_access_flag[ACCESS_MODE_INFECTION] | g_access_flag[ACCESS_MAKE_ZOMBIE] | g_access_flag[ACCESS_MAKE_HUMAN]))
3637 len += formatex(menu[len], charsmax(menu) - len, "\r1.\w %L^n", id, "MENU_ADMIN1")
3638 else
3639 len += formatex(menu[len], charsmax(menu) - len, "\d1. %L^n", id, "MENU_ADMIN1")
3640
3641 // 2. Nemesis command
3642 if (userflags & (g_access_flag[ACCESS_MODE_NEMESIS] | g_access_flag[ACCESS_MAKE_NEMESIS]))
3643 len += formatex(menu[len], charsmax(menu) - len, "\r2.\w %L^n", id, "MENU_ADMIN2")
3644 else
3645 len += formatex(menu[len], charsmax(menu) - len, "\d2. %L^n", id, "MENU_ADMIN2")
3646
3647 // 3. Survivor command
3648 if (userflags & (g_access_flag[ACCESS_MODE_SURVIVOR] | g_access_flag[ACCESS_MAKE_SURVIVOR]))
3649 len += formatex(menu[len], charsmax(menu) - len, "\r3.\w %L^n", id, "MENU_ADMIN3")
3650 else
3651 len += formatex(menu[len], charsmax(menu) - len, "\d3. %L^n", id, "MENU_ADMIN3")
3652
3653 // 4. Respawn command
3654 if (userflags & g_access_flag[ACCESS_RESPAWN_PLAYERS])
3655 len += formatex(menu[len], charsmax(menu) - len, "\r4.\w %L^n", id, "MENU_ADMIN4")
3656 else
3657 len += formatex(menu[len], charsmax(menu) - len, "\d4. %L^n", id, "MENU_ADMIN4")
3658
3659 // 5. Swarm mode command
3660 if ((userflags & g_access_flag[ACCESS_MODE_SWARM]) && allowed_swarm())
3661 len += formatex(menu[len], charsmax(menu) - len, "\r5.\w %L^n", id, "MENU_ADMIN5")
3662 else
3663 len += formatex(menu[len], charsmax(menu) - len, "\d5. %L^n", id, "MENU_ADMIN5")
3664
3665 // 6. Multi infection command
3666 if ((userflags & g_access_flag[ACCESS_MODE_MULTI]) && allowed_multi())
3667 len += formatex(menu[len], charsmax(menu) - len, "\r6.\w %L^n", id, "MENU_ADMIN6")
3668 else
3669 len += formatex(menu[len], charsmax(menu) - len, "\d6. %L^n", id, "MENU_ADMIN6")
3670
3671 // 7. Plague mode command
3672 if ((userflags & g_access_flag[ACCESS_MODE_PLAGUE]) && allowed_plague())
3673 len += formatex(menu[len], charsmax(menu) - len, "\r7.\w %L^n", id, "MENU_ADMIN7")
3674 else
3675 len += formatex(menu[len], charsmax(menu) - len, "\d7. %L^n", id, "MENU_ADMIN7")
3676
3677 // 0. Exit
3678 len += formatex(menu[len], charsmax(menu) - len, "^n\r0.\w %L", id, "MENU_EXIT")
3679
3680 show_menu(id, KEYSMENU, menu, -1, "Admin Menu")
3681}
3682
3683// Player List Menu
3684show_menu_player_list(id)
3685{
3686 static menuid, menu[128], player, userflags, buffer[2]
3687 userflags = get_user_flags(id)
3688
3689 // Title
3690 switch (PL_ACTION)
3691 {
3692 case ACTION_ZOMBIEFY_HUMANIZE: formatex(menu, charsmax(menu), "%L\r", id, "MENU_ADMIN1")
3693 case ACTION_MAKE_NEMESIS: formatex(menu, charsmax(menu), "%L\r", id, "MENU_ADMIN2")
3694 case ACTION_MAKE_SURVIVOR: formatex(menu, charsmax(menu), "%L\r", id, "MENU_ADMIN3")
3695 case ACTION_RESPAWN_PLAYER: formatex(menu, charsmax(menu), "%L\r", id, "MENU_ADMIN4")
3696 }
3697 menuid = menu_create(menu, "menu_player_list")
3698
3699 // Player List
3700 for (player = 0; player <= g_maxplayers; player++)
3701 {
3702 // Skip if not connected
3703 if (!g_isconnected[player])
3704 continue;
3705
3706 // Format text depending on the action to take
3707 switch (PL_ACTION)
3708 {
3709 case ACTION_ZOMBIEFY_HUMANIZE: // Zombiefy/Humanize command
3710 {
3711 if (g_zombie[player])
3712 {
3713 if (allowed_human(player) && (userflags & g_access_flag[ACCESS_MAKE_HUMAN]))
3714 formatex(menu, charsmax(menu), "%s \r[%L]", g_playername[player], id, g_nemesis[player] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE")
3715 else
3716 formatex(menu, charsmax(menu), "\d%s [%L]", g_playername[player], id, g_nemesis[player] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE")
3717 }
3718 else
3719 {
3720 if (allowed_zombie(player) && (g_newround ? (userflags & g_access_flag[ACCESS_MODE_INFECTION]) : (userflags & g_access_flag[ACCESS_MAKE_ZOMBIE])))
3721 formatex(menu, charsmax(menu), "%s \y[%L]", g_playername[player], id, g_survivor[player] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3722 else
3723 formatex(menu, charsmax(menu), "\d%s [%L]", g_playername[player], id, g_survivor[player] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3724 }
3725 }
3726 case ACTION_MAKE_NEMESIS: // Nemesis command
3727 {
3728 if (allowed_nemesis(player) && (g_newround ? (userflags & g_access_flag[ACCESS_MODE_NEMESIS]) : (userflags & g_access_flag[ACCESS_MAKE_NEMESIS])))
3729 {
3730 if (g_zombie[player])
3731 formatex(menu, charsmax(menu), "%s \r[%L]", g_playername[player], id, g_nemesis[player] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE")
3732 else
3733 formatex(menu, charsmax(menu), "%s \y[%L]", g_playername[player], id, g_survivor[player] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3734 }
3735 else
3736 formatex(menu, charsmax(menu), "\d%s [%L]", g_playername[player], id, g_zombie[player] ? g_nemesis[player] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE" : g_survivor[player] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3737 }
3738 case ACTION_MAKE_SURVIVOR: // Survivor command
3739 {
3740 if (allowed_survivor(player) && (g_newround ? (userflags & g_access_flag[ACCESS_MODE_SURVIVOR]) : (userflags & g_access_flag[ACCESS_MAKE_SURVIVOR])))
3741 {
3742 if (g_zombie[player])
3743 formatex(menu, charsmax(menu), "%s \r[%L]", g_playername[player], id, g_nemesis[player] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE")
3744 else
3745 formatex(menu, charsmax(menu), "%s \y[%L]", g_playername[player], id, g_survivor[player] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3746 }
3747 else
3748 formatex(menu, charsmax(menu), "\d%s [%L]", g_playername[player], id, g_zombie[player] ? g_nemesis[player] ? "CLASS_NEMESIS" : "CLASS_ZOMBIE" : g_survivor[player] ? "CLASS_SURVIVOR" : "CLASS_HUMAN")
3749 }
3750 case ACTION_RESPAWN_PLAYER: // Respawn command
3751 {
3752 if (allowed_respawn(player) && (userflags & g_access_flag[ACCESS_RESPAWN_PLAYERS]))
3753 formatex(menu, charsmax(menu), "%s", g_playername[player])
3754 else
3755 formatex(menu, charsmax(menu), "\d%s", g_playername[player])
3756 }
3757 }
3758
3759 // Add player
3760 buffer[0] = player
3761 buffer[1] = 0
3762 menu_additem(menuid, menu, buffer)
3763 }
3764
3765 // Back - Next - Exit
3766 formatex(menu, charsmax(menu), "%L", id, "MENU_BACK")
3767 menu_setprop(menuid, MPROP_BACKNAME, menu)
3768 formatex(menu, charsmax(menu), "%L", id, "MENU_NEXT")
3769 menu_setprop(menuid, MPROP_NEXTNAME, menu)
3770 formatex(menu, charsmax(menu), "%L", id, "MENU_EXIT")
3771 menu_setprop(menuid, MPROP_EXITNAME, menu)
3772
3773 menu_display(id, menuid)
3774}
3775
3776/*================================================================================
3777 [Menu Handlers]
3778=================================================================================*/
3779
3780// Game Menu
3781public menu_game(id, key)
3782{
3783 switch (key)
3784 {
3785 case 0: // Buy Weapons
3786 {
3787 // Custom buy menus enabled?
3788 if (get_pcvar_num(cvar_buycustom))
3789 {
3790 // Disable the remember selection setting
3791 WPN_AUTO_ON = 0
3792 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "BUY_ENABLED")
3793
3794 // Show menu if player hasn't yet bought anything
3795 if (g_canbuy[id]) show_menu_buy1(id)
3796 }
3797 else
3798 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
3799 }
3800 case 1: // Extra Items
3801 {
3802 // Extra items enabled?
3803 if (get_pcvar_num(cvar_extraitems))
3804 {
3805 // Check whether the player is able to buy anything
3806 if (g_isalive[id])
3807 show_menu_extras(id)
3808 else
3809 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
3810 }
3811 else
3812 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_EXTRAS")
3813 }
3814 case 2: // Zombie Classes
3815 {
3816 // Zombie classes enabled?
3817 if (get_pcvar_num(cvar_zclasses))
3818 show_menu_zclass(id)
3819 else
3820 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ZCLASSES")
3821 }
3822 case 3: // Unstuck
3823 {
3824 // Check if player is stuck
3825 if (g_isalive[id])
3826 {
3827 if (is_player_stuck(id))
3828 {
3829 // Move to an initial spawn
3830 if (get_pcvar_num(cvar_randspawn))
3831 do_random_spawn(id) // random spawn (including CSDM)
3832 else
3833 do_random_spawn(id, 1) // regular spawn
3834 }
3835 else
3836 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_STUCK")
3837 }
3838 else
3839 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
3840 }
3841 case 4: // Join Spectator
3842 {
3843 // Player alive?
3844 if (g_isalive[id])
3845 {
3846 // Prevent abuse by non-admins if block suicide setting is enabled
3847 if (get_pcvar_num(cvar_blocksuicide) && !(get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MENU]))
3848 {
3849 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
3850 return PLUGIN_HANDLED;
3851 }
3852
3853 // Check that we still have both humans and zombies to keep the round going
3854 check_round(id)
3855
3856 // Kill him before he switches team
3857 dllfunc(DLLFunc_ClientKill, id)
3858 }
3859
3860 // Temporarily save player stats?
3861 if (get_pcvar_num(cvar_statssave)) save_stats(id)
3862
3863 // Remove previous tasks
3864 remove_task(id+TASK_TEAM)
3865 remove_task(id+TASK_MODEL)
3866 remove_task(id+TASK_FLASH)
3867 remove_task(id+TASK_CHARGE)
3868 remove_task(id+TASK_SPAWN)
3869 remove_task(id+TASK_BLOOD)
3870 remove_task(id+TASK_AURA)
3871 remove_task(id+TASK_BURN)
3872
3873 // Then move him to the spectator team
3874 fm_cs_set_user_team(id, FM_CS_TEAM_SPECTATOR)
3875 fm_user_team_update(id)
3876 }
3877 // VIP Menu
3878 case 5:
3879 {
3880 client_cmd(id,"say /vm");
3881 }
3882 // Party Menu
3883 case 6:
3884 {
3885 client_cmd(id,"say /party");
3886 }
3887
3888 case 8: // Admin Menu
3889 {
3890 // Check if player has the required access
3891 if (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MENU])
3892 show_menu_admin(id)
3893 else
3894 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
3895 }
3896 }
3897
3898 return PLUGIN_HANDLED;
3899}
3900
3901// Buy Menu 1
3902public menu_buy1(id, key)
3903{
3904 // Zombies or survivors get no guns
3905 if (!g_isalive[id] || g_zombie[id] || g_survivor[id])
3906 return PLUGIN_HANDLED;
3907
3908 // Special keys / weapon list exceeded
3909 if (key >= MENU_KEY_AUTOSELECT || WPN_SELECTION >= WPN_MAXIDS)
3910 {
3911 switch (key)
3912 {
3913 case MENU_KEY_AUTOSELECT: // toggle auto select
3914 {
3915 WPN_AUTO_ON = 1 - WPN_AUTO_ON
3916 }
3917 case MENU_KEY_NEXT: // next/back
3918 {
3919 if (WPN_STARTID+7 < WPN_MAXIDS)
3920 WPN_STARTID += 7
3921 else
3922 WPN_STARTID = 0
3923 }
3924 case MENU_KEY_EXIT: // exit
3925 {
3926 return PLUGIN_HANDLED;
3927 }
3928 }
3929
3930 // Show buy menu again
3931 show_menu_buy1(id)
3932 return PLUGIN_HANDLED;
3933 }
3934
3935 // Store selected weapon id
3936 WPN_AUTO_PRI = WPN_SELECTION
3937
3938 // Buy primary weapon
3939 buy_primary_weapon(id, WPN_AUTO_PRI)
3940
3941 // Show pistols menu
3942 show_menu_buy2(id)
3943
3944 return PLUGIN_HANDLED;
3945}
3946
3947// Buy Primary Weapon
3948buy_primary_weapon(id, selection)
3949{
3950 // Drop previous weapons
3951 drop_weapons(id, 1)
3952 drop_weapons(id, 2)
3953
3954 // Strip off from weapons
3955 fm_strip_user_weapons(id)
3956 fm_give_item(id, "weapon_knife")
3957
3958 // Get weapon's id and name
3959 static weaponid, wname[32]
3960 weaponid = ArrayGetCell(g_primary_weaponids, selection)
3961 ArrayGetString(g_primary_items, selection, wname, charsmax(wname))
3962
3963 // Give the new weapon and full ammo
3964 fm_give_item(id, wname)
3965 ExecuteHamB(Ham_GiveAmmo, id, MAXBPAMMO[weaponid], AMMOTYPE[weaponid], MAXBPAMMO[weaponid])
3966
3967 // Weapons bought
3968 g_canbuy[id] = false
3969
3970 // Give additional items
3971 static i
3972 for (i = 0; i < ArraySize(g_additional_items); i++)
3973 {
3974 ArrayGetString(g_additional_items, i, wname, charsmax(wname))
3975 fm_give_item(id, wname)
3976 }
3977}
3978
3979// Buy Menu 2
3980public menu_buy2(id, key)
3981{
3982 // Zombies or survivors get no guns
3983 if (!g_isalive[id] || g_zombie[id] || g_survivor[id])
3984 return PLUGIN_HANDLED;
3985
3986 // Special keys / weapon list exceeded
3987 if (key >= ArraySize(g_secondary_items))
3988 {
3989 // Toggle autoselect
3990 if (key == MENU_KEY_AUTOSELECT)
3991 WPN_AUTO_ON = 1 - WPN_AUTO_ON
3992
3993 // Reshow menu unless user exited
3994 if (key != MENU_KEY_EXIT)
3995 show_menu_buy2(id)
3996
3997 return PLUGIN_HANDLED;
3998 }
3999
4000 // Store selected weapon
4001 WPN_AUTO_SEC = key
4002
4003 // Drop secondary gun again, in case we picked another (bugfix)
4004 drop_weapons(id, 2)
4005
4006 // Get weapon's id
4007 static weaponid, wname[32]
4008 weaponid = ArrayGetCell(g_secondary_weaponids, key)
4009 ArrayGetString(g_secondary_items, key, wname, charsmax(wname))
4010
4011 // Give the new weapon and full ammo
4012 fm_give_item(id, wname)
4013 ExecuteHamB(Ham_GiveAmmo, id, MAXBPAMMO[weaponid], AMMOTYPE[weaponid], MAXBPAMMO[weaponid])
4014
4015 return PLUGIN_HANDLED;
4016}
4017
4018// Extra Items Menu
4019public menu_extras(id, menuid, item)
4020{
4021 // Menu was closed
4022 if (item == MENU_EXIT)
4023 {
4024 menu_destroy(menuid)
4025 return PLUGIN_HANDLED;
4026 }
4027
4028 // Dead players are not allowed to buy items
4029 if (!g_isalive[id])
4030 {
4031 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4032 menu_destroy(menuid)
4033 return PLUGIN_HANDLED;
4034 }
4035
4036 // Retrieve extra item id
4037 static buffer[2], dummy, itemid
4038 menu_item_getinfo(menuid, item, dummy, buffer, charsmax(buffer), _, _, dummy)
4039 itemid = buffer[0]
4040
4041 // Attempt to buy the item
4042 buy_extra_item(id, itemid)
4043 menu_destroy(menuid)
4044 return PLUGIN_HANDLED;
4045}
4046
4047// Buy Extra Item
4048buy_extra_item(id, itemid, ignorecost = 0)
4049{
4050 // Retrieve item's team
4051 static team
4052 team = ArrayGetCell(g_extraitem_team, itemid)
4053
4054 // Check for team/class specific items
4055 if ((g_zombie[id] && !g_nemesis[id] && !(team & ZP_TEAM_ZOMBIE)) || (!g_zombie[id] && !g_survivor[id] && !(team & ZP_TEAM_HUMAN)) || (g_nemesis[id] && !(team & ZP_TEAM_NEMESIS)) || (g_survivor[id] && !(team & ZP_TEAM_SURVIVOR)))
4056 {
4057 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4058 return;
4059 }
4060
4061 // Check for unavailable items
4062 if ((itemid == EXTRA_NVISION && !get_pcvar_num(cvar_extranvision))
4063 || (itemid == EXTRA_ANTIDOTE && (!get_pcvar_num(cvar_extraantidote) || g_antidotecounter >= get_pcvar_num(cvar_antidotelimit)))
4064 || (itemid == EXTRA_MADNESS && (!get_pcvar_num(cvar_extramadness) || g_madnesscounter >= get_pcvar_num(cvar_madnesslimit)))
4065 || (itemid == EXTRA_INFBOMB && (!get_pcvar_num(cvar_extrainfbomb) || g_infbombcounter >= get_pcvar_num(cvar_infbomblimit)))
4066 || (itemid >= EXTRA_WEAPONS_STARTID && itemid <= EXTRAS_CUSTOM_STARTID-1 && !get_pcvar_num(cvar_extraweapons)))
4067 {
4068 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4069 return;
4070 }
4071
4072 // Check for hard coded items with special conditions
4073 if ((itemid == EXTRA_ANTIDOTE && (g_endround || g_swarmround || g_nemround || g_survround || g_plagueround || fnGetZombies() <= 1 || (get_pcvar_num(cvar_deathmatch) && !get_pcvar_num(cvar_respawnafterlast) && fnGetHumans() == 1)))
4074 || (itemid == EXTRA_MADNESS && g_nodamage[id]) || (itemid == EXTRA_INFBOMB && (g_endround || g_swarmround || g_nemround || g_survround || g_plagueround)))
4075 {
4076 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_CANTUSE")
4077 return;
4078 }
4079
4080 // Ignore item's cost?
4081 if (!ignorecost)
4082 {
4083 // Check that we have enough ammo packs
4084 if (g_ammopacks[id] < ArrayGetCell(g_extraitem_cost, itemid))
4085 {
4086 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "NOT_ENOUGH_AMMO")
4087 return;
4088 }
4089
4090 // Deduce item cost
4091 g_ammopacks[id] -= ArrayGetCell(g_extraitem_cost, itemid)
4092 }
4093
4094 // Check which kind of item we're buying
4095 switch (itemid)
4096 {
4097 case EXTRA_NVISION: // Night Vision
4098 {
4099 g_nvision[id] = true
4100
4101 if (!g_isbot[id])
4102 {
4103 g_nvisionenabled[id] = true
4104
4105 // Custom nvg?
4106 if (get_pcvar_num(cvar_customnvg))
4107 {
4108 remove_task(id+TASK_NVISION)
4109 set_task(0.1, "set_user_nvision", id+TASK_NVISION, _, _, "b")
4110 }
4111 else
4112 set_user_gnvision(id, 1)
4113 }
4114 else
4115 cs_set_user_nvg(id, 1)
4116 }
4117 case EXTRA_ANTIDOTE: // Antidote
4118 {
4119 // Increase antidote purchase count for this round
4120 g_antidotecounter++
4121
4122 humanme(id, 0, 0)
4123 }
4124 case EXTRA_MADNESS: // Zombie Madness
4125 {
4126 // Increase madness purchase count for this round
4127 g_madnesscounter++
4128
4129 g_nodamage[id] = true
4130 set_task(0.1, "zombie_aura", id+TASK_AURA, _, _, "b")
4131 set_task(get_pcvar_float(cvar_madnessduration), "madness_over", id+TASK_BLOOD)
4132
4133 static sound[64]
4134 ArrayGetString(zombie_madness, random_num(0, ArraySize(zombie_madness) - 1), sound, charsmax(sound))
4135 emit_sound(id, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
4136 }
4137 case EXTRA_INFBOMB: // Infection Bomb
4138 {
4139 // Increase infection bomb purchase count for this round
4140 g_infbombcounter++
4141
4142 // Already own one
4143 if (user_has_weapon(id, CSW_HEGRENADE))
4144 {
4145 // Increase BP ammo on it instead
4146 cs_set_user_bpammo(id, CSW_HEGRENADE, cs_get_user_bpammo(id, CSW_HEGRENADE) + 1)
4147
4148 // Flash ammo in hud
4149 message_begin(MSG_ONE_UNRELIABLE, g_msgAmmoPickup, _, id)
4150 write_byte(AMMOID[CSW_HEGRENADE]) // ammo id
4151 write_byte(1) // ammo amount
4152 message_end()
4153
4154 // Play clip purchase sound
4155 emit_sound(id, CHAN_ITEM, sound_buyammo, 1.0, ATTN_NORM, 0, PITCH_NORM)
4156
4157 return; // stop here
4158 }
4159
4160 // Give weapon to the player
4161 fm_give_item(id, "weapon_hegrenade")
4162 }
4163 default:
4164 {
4165 if (itemid >= EXTRA_WEAPONS_STARTID && itemid <= EXTRAS_CUSTOM_STARTID-1) // Weapons
4166 {
4167 // Get weapon's id and name
4168 static weaponid, wname[32]
4169 ArrayGetString(g_extraweapon_items, itemid - EXTRA_WEAPONS_STARTID, wname, charsmax(wname))
4170 weaponid = cs_weapon_name_to_id(wname)
4171
4172 // If we are giving a primary/secondary weapon
4173 if (MAXBPAMMO[weaponid] > 2)
4174 {
4175 // Make user drop the previous one
4176 if ((1<<weaponid) & PRIMARY_WEAPONS_BIT_SUM)
4177 drop_weapons(id, 1)
4178 else
4179 drop_weapons(id, 2)
4180
4181 // Give full BP ammo for the new one
4182 ExecuteHamB(Ham_GiveAmmo, id, MAXBPAMMO[weaponid], AMMOTYPE[weaponid], MAXBPAMMO[weaponid])
4183 }
4184 // If we are giving a grenade which the user already owns
4185 else if (user_has_weapon(id, weaponid))
4186 {
4187 // Increase BP ammo on it instead
4188 cs_set_user_bpammo(id, weaponid, cs_get_user_bpammo(id, weaponid) + 1)
4189
4190 // Flash ammo in hud
4191 message_begin(MSG_ONE_UNRELIABLE, g_msgAmmoPickup, _, id)
4192 write_byte(AMMOID[weaponid]) // ammo id
4193 write_byte(1) // ammo amount
4194 message_end()
4195
4196 // Play clip purchase sound
4197 emit_sound(id, CHAN_ITEM, sound_buyammo, 1.0, ATTN_NORM, 0, PITCH_NORM)
4198
4199 return; // stop here
4200 }
4201
4202 // Give weapon to the player
4203 fm_give_item(id, wname)
4204 }
4205 else // Custom additions
4206 {
4207 // Item selected forward
4208 ExecuteForward(g_fwExtraItemSelected, g_fwDummyResult, id, itemid);
4209
4210 // Item purchase blocked, restore buyer's ammo packs
4211 if (g_fwDummyResult >= ZP_PLUGIN_HANDLED && !ignorecost)
4212 g_ammopacks[id] += ArrayGetCell(g_extraitem_cost, itemid)
4213 }
4214 }
4215 }
4216}
4217
4218// Zombie Class Menu
4219public menu_zclass(id, menuid, item)
4220{
4221 // Menu was closed
4222 if (item == MENU_EXIT)
4223 {
4224 menu_destroy(menuid)
4225 return PLUGIN_HANDLED;
4226 }
4227
4228 // Retrieve zombie class id
4229 static buffer[2], dummy, classid
4230 menu_item_getinfo(menuid, item, dummy, buffer, charsmax(buffer), _, _, dummy)
4231 classid = buffer[0]
4232
4233 // Store selection for the next infection
4234 g_zombieclassnext[id] = classid
4235
4236 static name[32]
4237 ArrayGetString(g_zclass_name, g_zombieclassnext[id], name, charsmax(name))
4238
4239 // Show selected zombie class info and stats
4240 zp_colored_print(id, "^x04[ZP]^x01 %L: %s", id, "ZOMBIE_SELECT", name)
4241 zp_colored_print(id, "^x04[ZP]^x01 %L: %d %L: %d %L: %d %L: %d%%", id, "ZOMBIE_ATTRIB1", ArrayGetCell(g_zclass_hp, g_zombieclassnext[id]), id, "ZOMBIE_ATTRIB2", ArrayGetCell(g_zclass_spd, g_zombieclassnext[id]),
4242 id, "ZOMBIE_ATTRIB3", floatround(Float:ArrayGetCell(g_zclass_grav, g_zombieclassnext[id]) * 800.0), id, "ZOMBIE_ATTRIB4", floatround(Float:ArrayGetCell(g_zclass_kb, g_zombieclassnext[id]) * 100.0))
4243
4244 menu_destroy(menuid)
4245 return PLUGIN_HANDLED;
4246}
4247
4248// Info Menu
4249public menu_info(id, key)
4250{
4251 static motd[1500], len
4252 len = 0
4253
4254 switch (key)
4255 {
4256 case 0: // General
4257 {
4258 static weather, lighting[2]
4259 weather = 0
4260 get_pcvar_string(cvar_lighting, lighting, charsmax(lighting))
4261 strtolower(lighting)
4262
4263 len += formatex(motd[len], charsmax(motd) - len, "%L ", id, "MOTD_INFO11", "Zombie Plague", PLUGIN_VERSION, "MeRcyLeZZ")
4264 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO12")
4265 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_A")
4266
4267 if (g_ambience_fog)
4268 {
4269 len += formatex(motd[len], charsmax(motd) - len, (weather < 1) ? " %L" : ". %L", id, "MOTD_FOG")
4270 weather++
4271 }
4272 if (g_ambience_rain)
4273 {
4274 len += formatex(motd[len], charsmax(motd) - len, (weather < 1) ? " %L" : ". %L", id, "MOTD_RAIN")
4275 weather++
4276 }
4277 if (g_ambience_snow)
4278 {
4279 len += formatex(motd[len], charsmax(motd) - len, (weather < 1) ? " %L" : ". %L", id, "MOTD_SNOW")
4280 weather++
4281 }
4282 if (weather < 1) len += formatex(motd[len], charsmax(motd) - len, " %L", id, "MOTD_DISABLED")
4283
4284 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_B", lighting)
4285 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_C", id, get_pcvar_num(cvar_triggered) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4286 if (lighting[0] >= 'a' && lighting[0] <= 'd' && get_pcvar_float(cvar_thunder) > 0.0) len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_D", floatround(get_pcvar_float(cvar_thunder)))
4287 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_E", id, get_pcvar_num(cvar_removedoors) > 0 ? get_pcvar_num(cvar_removedoors) > 1 ? "MOTD_DOORS" : "MOTD_ROTATING" : "MOTD_ENABLED")
4288 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_F", id, get_pcvar_num(cvar_deathmatch) > 0 ? get_pcvar_num(cvar_deathmatch) > 1 ? get_pcvar_num(cvar_deathmatch) > 2 ? "MOTD_ENABLED" : "MOTD_DM_ZOMBIE" : "MOTD_DM_HUMAN" : "MOTD_DISABLED")
4289 if (get_pcvar_num(cvar_deathmatch)) len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_G", floatround(get_pcvar_float(cvar_spawnprotection)))
4290 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_H", id, get_pcvar_num(cvar_randspawn) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4291 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_I", id, get_pcvar_num(cvar_extraitems) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4292 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_J", id, get_pcvar_num(cvar_zclasses) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4293 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_K", id, get_pcvar_num(cvar_customnvg) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4294 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO1_L", id, g_cached_customflash ? "MOTD_ENABLED" : "MOTD_DISABLED")
4295
4296 show_motd(id, motd)
4297 }
4298 case 1: // Humans
4299 {
4300 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2")
4301 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_A", get_pcvar_num(cvar_humanhp))
4302 if (get_pcvar_num(cvar_humanlasthp) > 0) len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_B", get_pcvar_num(cvar_humanlasthp))
4303 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_C", floatround(g_cached_humanspd))
4304 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_D", floatround(get_pcvar_float(cvar_humangravity) * 800.0))
4305 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_E", id, get_pcvar_num(cvar_infammo) > 0 ? get_pcvar_num(cvar_infammo) > 1 ? "MOTD_AMMO_CLIP" : "MOTD_AMMO_BP" : "MOTD_LIMITED")
4306 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_F", get_pcvar_num(cvar_ammodamage))
4307 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_G", id, get_pcvar_num(cvar_firegrenades) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4308 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_H", id, get_pcvar_num(cvar_frostgrenades) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4309 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_I", id, get_pcvar_num(cvar_flaregrenades) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4310 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO2_J", id, get_pcvar_num(cvar_knockback) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4311
4312 show_motd(id, motd)
4313 }
4314 case 2: // Zombies
4315 {
4316 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3")
4317 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_A", ArrayGetCell(g_zclass_hp, 0))
4318 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_B", floatround(float(ArrayGetCell(g_zclass_hp, 0)) * get_pcvar_float(cvar_zombiefirsthp)))
4319 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_C", floatround(get_pcvar_float(cvar_zombiearmor) * 100.0))
4320 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_D", ArrayGetCell(g_zclass_spd, 0))
4321 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_E", floatround(Float:ArrayGetCell(g_zclass_grav, 0) * 800.0))
4322 if (get_pcvar_num(cvar_zombiebonushp)) len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_F", get_pcvar_num(cvar_zombiebonushp))
4323 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_G", id, get_pcvar_num(cvar_zombiepainfree) > 0 ? get_pcvar_num(cvar_zombiepainfree) > 1 ? "MOTD_LASTZOMBIE" : "MOTD_ENABLED" : "MOTD_DISABLED")
4324 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_H", id, get_pcvar_num(cvar_zombiebleeding) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4325 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO3_I", get_pcvar_num(cvar_ammoinfect))
4326
4327 show_motd(id, motd)
4328 }
4329 case 3: // Gameplay Modes
4330 {
4331 static nemhp[5], survhp[5]
4332
4333 // Get nemesis and survivor health
4334 num_to_str(get_pcvar_num(cvar_nemhp), nemhp, charsmax(nemhp))
4335 num_to_str(get_pcvar_num(cvar_survhp), survhp, charsmax(survhp))
4336
4337 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4")
4338 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_A", id, get_pcvar_num(cvar_nem) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4339 if (get_pcvar_num(cvar_nem))
4340 {
4341 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_B", get_pcvar_num(cvar_nemchance))
4342 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_C", get_pcvar_num(cvar_nemhp) > 0 ? nemhp : "[Auto]")
4343 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_D", floatround(g_cached_nemspd))
4344 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_E", floatround(get_pcvar_float(cvar_nemgravity) * 800.0))
4345 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_F", id, g_cached_leapnemesis ? "MOTD_ENABLED" : "MOTD_DISABLED")
4346 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_G", id, get_pcvar_num(cvar_nempainfree) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4347 }
4348 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_H", id, get_pcvar_num(cvar_surv) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4349 if (get_pcvar_num(cvar_surv))
4350 {
4351 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_I", get_pcvar_num(cvar_survchance))
4352 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_J", get_pcvar_num(cvar_survhp) > 0 ? survhp : "[Auto]")
4353 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_K", floatround(g_cached_survspd))
4354 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_L", floatround(get_pcvar_float(cvar_survgravity) * 800.0))
4355 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_M", id, g_cached_leapsurvivor ? "MOTD_ENABLED" : "MOTD_DISABLED")
4356 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_N", id, get_pcvar_num(cvar_survpainfree) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4357 }
4358 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_O", id, get_pcvar_num(cvar_swarm) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4359 if (get_pcvar_num(cvar_swarm)) len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_P", get_pcvar_num(cvar_swarmchance))
4360 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_Q", id, get_pcvar_num(cvar_multi) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4361 if (get_pcvar_num(cvar_multi))
4362 {
4363 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_R", get_pcvar_num(cvar_multichance))
4364 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_S", floatround(get_pcvar_float(cvar_multiratio) * 100.0))
4365 }
4366 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_T", id, get_pcvar_num(cvar_plague) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4367 if (get_pcvar_num(cvar_plague))
4368 {
4369 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_U", get_pcvar_num(cvar_plaguechance))
4370 len += formatex(motd[len], charsmax(motd) - len, "%L", id, "MOTD_INFO4_V", floatround(get_pcvar_float(cvar_plagueratio) * 100.0))
4371 }
4372
4373 show_motd(id, motd)
4374 }
4375 default: return PLUGIN_HANDLED;
4376 }
4377
4378 // Show help menu again if user wishes to read another topic
4379 show_menu_info(id)
4380
4381 return PLUGIN_HANDLED;
4382}
4383
4384// Admin Menu
4385public menu_admin(id, key)
4386{
4387 static userflags
4388 userflags = get_user_flags(id)
4389
4390 switch (key)
4391 {
4392 case ACTION_ZOMBIEFY_HUMANIZE: // Zombiefy/Humanize command
4393 {
4394 if (userflags & (g_access_flag[ACCESS_MODE_INFECTION] | g_access_flag[ACCESS_MAKE_ZOMBIE] | g_access_flag[ACCESS_MAKE_HUMAN]))
4395 {
4396 // Show player list for admin to pick a target
4397 PL_ACTION = ACTION_ZOMBIEFY_HUMANIZE
4398 show_menu_player_list(id)
4399 }
4400 else
4401 {
4402 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4403 show_menu_admin(id)
4404 }
4405 }
4406 case ACTION_MAKE_NEMESIS: // Nemesis command
4407 {
4408 if (userflags & (g_access_flag[ACCESS_MODE_NEMESIS] | g_access_flag[ACCESS_MAKE_NEMESIS]))
4409 {
4410 // Show player list for admin to pick a target
4411 PL_ACTION = ACTION_MAKE_NEMESIS
4412 show_menu_player_list(id)
4413 }
4414 else
4415 {
4416 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4417 show_menu_admin(id)
4418 }
4419 }
4420 case ACTION_MAKE_SURVIVOR: // Survivor command
4421 {
4422 if (userflags & (g_access_flag[ACCESS_MODE_SURVIVOR] | g_access_flag[ACCESS_MAKE_SURVIVOR]))
4423 {
4424 // Show player list for admin to pick a target
4425 PL_ACTION = ACTION_MAKE_SURVIVOR
4426 show_menu_player_list(id)
4427 }
4428 else
4429 {
4430 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4431 show_menu_admin(id)
4432 }
4433 }
4434 case ACTION_RESPAWN_PLAYER: // Respawn command
4435 {
4436 if (userflags & g_access_flag[ACCESS_RESPAWN_PLAYERS])
4437 {
4438 // Show player list for admin to pick a target
4439 PL_ACTION = ACTION_RESPAWN_PLAYER
4440 show_menu_player_list(id)
4441 }
4442 else
4443 {
4444 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4445 show_menu_admin(id)
4446 }
4447 }
4448 case ACTION_MODE_SWARM: // Swarm Mode command
4449 {
4450 if (userflags & g_access_flag[ACCESS_MODE_SWARM])
4451 {
4452 if (allowed_swarm())
4453 command_swarm(id)
4454 else
4455 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4456 }
4457 else
4458 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4459
4460 show_menu_admin(id)
4461 }
4462 case ACTION_MODE_MULTI: // Multiple Infection command
4463 {
4464 if (userflags & g_access_flag[ACCESS_MODE_MULTI])
4465 {
4466 if (allowed_multi())
4467 command_multi(id)
4468 else
4469 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4470 }
4471 else
4472 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4473
4474 show_menu_admin(id)
4475 }
4476 case ACTION_MODE_PLAGUE: // Plague Mode command
4477 {
4478 if (userflags & g_access_flag[ACCESS_MODE_PLAGUE])
4479 {
4480 if (allowed_plague())
4481 command_plague(id)
4482 else
4483 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4484 }
4485 else
4486 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4487
4488 show_menu_admin(id)
4489 }
4490 }
4491
4492 return PLUGIN_HANDLED;
4493}
4494
4495// Player List Menu
4496public menu_player_list(id, menuid, item)
4497{
4498 // Menu was closed
4499 if (item == MENU_EXIT)
4500 {
4501 menu_destroy(menuid)
4502 show_menu_admin(id)
4503 return PLUGIN_HANDLED;
4504 }
4505
4506 // Retrieve player id
4507 static buffer[2], dummy, playerid
4508 menu_item_getinfo(menuid, item, dummy, buffer, charsmax(buffer), _, _, dummy)
4509 playerid = buffer[0]
4510
4511 // Perform action on player
4512
4513 // Get admin flags
4514 static userflags
4515 userflags = get_user_flags(id)
4516
4517 // Make sure it's still connected
4518 if (g_isconnected[playerid])
4519 {
4520 // Perform the right action if allowed
4521 switch (PL_ACTION)
4522 {
4523 case ACTION_ZOMBIEFY_HUMANIZE: // Zombiefy/Humanize command
4524 {
4525 if (g_zombie[playerid])
4526 {
4527 if (userflags & g_access_flag[ACCESS_MAKE_HUMAN])
4528 {
4529 if (allowed_human(playerid))
4530 command_human(id, playerid)
4531 else
4532 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4533 }
4534 else
4535 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4536 }
4537 else
4538 {
4539 if (g_newround ? (userflags & g_access_flag[ACCESS_MODE_INFECTION]) : (userflags & g_access_flag[ACCESS_MAKE_ZOMBIE]))
4540 {
4541 if (allowed_zombie(playerid))
4542 command_zombie(id, playerid)
4543 else
4544 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4545 }
4546 else
4547 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4548 }
4549 }
4550 case ACTION_MAKE_NEMESIS: // Nemesis command
4551 {
4552 if (g_newround ? (userflags & g_access_flag[ACCESS_MODE_NEMESIS]) : (userflags & g_access_flag[ACCESS_MAKE_NEMESIS]))
4553 {
4554 if (allowed_nemesis(playerid))
4555 command_nemesis(id, playerid)
4556 else
4557 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4558 }
4559 else
4560 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4561 }
4562 case ACTION_MAKE_SURVIVOR: // Survivor command
4563 {
4564 if (g_newround ? (userflags & g_access_flag[ACCESS_MODE_SURVIVOR]) : (userflags & g_access_flag[ACCESS_MAKE_SURVIVOR]))
4565 {
4566 if (allowed_survivor(playerid))
4567 command_survivor(id, playerid)
4568 else
4569 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4570 }
4571 else
4572 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4573 }
4574 case ACTION_RESPAWN_PLAYER: // Respawn command
4575 {
4576 if (userflags & g_access_flag[ACCESS_RESPAWN_PLAYERS])
4577 {
4578 if (allowed_respawn(playerid))
4579 command_respawn(id, playerid)
4580 else
4581 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4582 }
4583 else
4584 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT_ACCESS")
4585 }
4586 }
4587 }
4588 else
4589 zp_colored_print(id, "^x04[ZP]^x01 %L", id, "CMD_NOT")
4590
4591 menu_destroy(menuid)
4592 show_menu_player_list(id)
4593 return PLUGIN_HANDLED;
4594}
4595
4596/*================================================================================
4597 [Admin Commands]
4598=================================================================================*/
4599
4600// zp_toggle [1/0]
4601public cmd_toggle(id, level, cid)
4602{
4603 // Check for access flag - Enable/Disable Mod
4604 if (!cmd_access(id, g_access_flag[ACCESS_ENABLE_MOD], cid, 2))
4605 return PLUGIN_HANDLED;
4606
4607 // Retrieve arguments
4608 new arg[2]
4609 read_argv(1, arg, charsmax(arg))
4610
4611 // Mod already enabled/disabled
4612 if (str_to_num(arg) == g_pluginenabled)
4613 return PLUGIN_HANDLED;
4614
4615 // Set toggle cvar
4616 set_pcvar_num(cvar_toggle, str_to_num(arg))
4617 client_print(id, print_console, "Zombie Plague %L.", id, str_to_num(arg) ? "MOTD_ENABLED" : "MOTD_DISABLED")
4618
4619 // Retrieve map name
4620 new mapname[32]
4621 get_mapname(mapname, charsmax(mapname))
4622
4623 // Restart current map
4624 server_cmd("changelevel %s", mapname)
4625
4626 return PLUGIN_HANDLED;
4627}
4628
4629// zp_zombie [target]
4630public cmd_zombie(id, level, cid)
4631{
4632 // Check for access flag depending on the resulting action
4633 if (g_newround)
4634 {
4635 // Start Mode Infection
4636 if (!cmd_access(id, g_access_flag[ACCESS_MODE_INFECTION], cid, 2))
4637 return PLUGIN_HANDLED;
4638 }
4639 else
4640 {
4641 // Make Zombie
4642 if (!cmd_access(id, g_access_flag[ACCESS_MAKE_ZOMBIE], cid, 2))
4643 return PLUGIN_HANDLED;
4644 }
4645
4646 // Retrieve arguments
4647 static arg[32], player
4648 read_argv(1, arg, charsmax(arg))
4649 player = cmd_target(id, arg, (CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF))
4650
4651 // Invalid target
4652 if (!player) return PLUGIN_HANDLED;
4653
4654 // Target not allowed to be zombie
4655 if (!allowed_zombie(player))
4656 {
4657 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4658 return PLUGIN_HANDLED
4659 }
4660
4661 command_zombie(id, player)
4662
4663 return PLUGIN_HANDLED;
4664}
4665
4666// zp_human [target]
4667public cmd_human(id, level, cid)
4668{
4669 // Check for access flag - Make Human
4670 if (!cmd_access(id, g_access_flag[ACCESS_MAKE_HUMAN], cid, 2))
4671 return PLUGIN_HANDLED;
4672
4673 // Retrieve arguments
4674 static arg[32], player
4675 read_argv(1, arg, charsmax(arg))
4676 player = cmd_target(id, arg, (CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF))
4677
4678 // Invalid target
4679 if (!player) return PLUGIN_HANDLED;
4680
4681 // Target not allowed to be human
4682 if (!allowed_human(player))
4683 {
4684 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4685 return PLUGIN_HANDLED;
4686 }
4687
4688 command_human(id, player)
4689
4690 return PLUGIN_HANDLED;
4691}
4692
4693// zp_survivor [target]
4694public cmd_survivor(id, level, cid)
4695{
4696 // Check for access flag depending on the resulting action
4697 if (g_newround)
4698 {
4699 // Start Mode Survivor
4700 if (!cmd_access(id, g_access_flag[ACCESS_MODE_SURVIVOR], cid, 2))
4701 return PLUGIN_HANDLED;
4702 }
4703 else
4704 {
4705 // Make Survivor
4706 if (!cmd_access(id, g_access_flag[ACCESS_MAKE_SURVIVOR], cid, 2))
4707 return PLUGIN_HANDLED;
4708 }
4709
4710 // Retrieve arguments
4711 static arg[32], player
4712 read_argv(1, arg, charsmax(arg))
4713 player = cmd_target(id, arg, (CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF))
4714
4715 // Invalid target
4716 if (!player) return PLUGIN_HANDLED;
4717
4718 // Target not allowed to be survivor
4719 if (!allowed_survivor(player))
4720 {
4721 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4722 return PLUGIN_HANDLED;
4723 }
4724
4725 command_survivor(id, player)
4726
4727 return PLUGIN_HANDLED;
4728}
4729
4730// zp_nemesis [target]
4731public cmd_nemesis(id, level, cid)
4732{
4733 // Check for access flag depending on the resulting action
4734 if (g_newround)
4735 {
4736 // Start Mode Nemesis
4737 if (!cmd_access(id, g_access_flag[ACCESS_MODE_NEMESIS], cid, 2))
4738 return PLUGIN_HANDLED;
4739 }
4740 else
4741 {
4742 // Make Nemesis
4743 if (!cmd_access(id, g_access_flag[ACCESS_MAKE_NEMESIS], cid, 2))
4744 return PLUGIN_HANDLED;
4745 }
4746
4747 // Retrieve arguments
4748 static arg[32], player
4749 read_argv(1, arg, charsmax(arg))
4750 player = cmd_target(id, arg, (CMDTARGET_ONLY_ALIVE | CMDTARGET_ALLOW_SELF))
4751
4752 // Invalid target
4753 if (!player) return PLUGIN_HANDLED;
4754
4755 // Target not allowed to be nemesis
4756 if (!allowed_nemesis(player))
4757 {
4758 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4759 return PLUGIN_HANDLED;
4760 }
4761
4762 command_nemesis(id, player)
4763
4764 return PLUGIN_HANDLED;
4765}
4766
4767// zp_respawn [target]
4768public cmd_respawn(id, level, cid)
4769{
4770 // Check for access flag - Respawn
4771 if (!cmd_access(id, g_access_flag[ACCESS_RESPAWN_PLAYERS], cid, 2))
4772 return PLUGIN_HANDLED;
4773
4774 // Retrieve arguments
4775 static arg[32], player
4776 read_argv(1, arg, charsmax(arg))
4777 player = cmd_target(id, arg, CMDTARGET_ALLOW_SELF)
4778
4779 // Invalid target
4780 if (!player) return PLUGIN_HANDLED;
4781
4782 // Target not allowed to be respawned
4783 if (!allowed_respawn(player))
4784 {
4785 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4786 return PLUGIN_HANDLED;
4787 }
4788
4789 command_respawn(id, player)
4790
4791 return PLUGIN_HANDLED;
4792}
4793
4794// zp_swarm
4795public cmd_swarm(id, level, cid)
4796{
4797 // Check for access flag - Mode Swarm
4798 if (!cmd_access(id, g_access_flag[ACCESS_MODE_SWARM], cid, 2))
4799 return PLUGIN_HANDLED;
4800
4801 // Swarm mode not allowed
4802 if (!allowed_swarm())
4803 {
4804 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4805 return PLUGIN_HANDLED;
4806 }
4807
4808 command_swarm(id)
4809
4810 return PLUGIN_HANDLED;
4811}
4812
4813// zp_multi
4814public cmd_multi(id, level, cid)
4815{
4816 // Check for access flag - Mode Multi
4817 if (!cmd_access(id, g_access_flag[ACCESS_MODE_MULTI], cid, 2))
4818 return PLUGIN_HANDLED;
4819
4820 // Multi infection mode not allowed
4821 if (!allowed_multi())
4822 {
4823 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4824 return PLUGIN_HANDLED;
4825 }
4826
4827 command_multi(id)
4828
4829 return PLUGIN_HANDLED;
4830}
4831
4832// zp_plague
4833public cmd_plague(id, level, cid)
4834{
4835 // Check for access flag - Mode Plague
4836 if (!cmd_access(id, g_access_flag[ACCESS_MODE_PLAGUE], cid, 2))
4837 return PLUGIN_HANDLED;
4838
4839 // Plague mode not allowed
4840 if (!allowed_plague())
4841 {
4842 client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
4843 return PLUGIN_HANDLED;
4844 }
4845
4846 command_plague(id)
4847
4848 return PLUGIN_HANDLED;
4849}
4850
4851/*================================================================================
4852 [Message Hooks]
4853=================================================================================*/
4854
4855// Current Weapon info
4856public message_cur_weapon(msg_id, msg_dest, msg_entity)
4857{
4858 // Not alive or zombie
4859 if (!g_isalive[msg_entity] || g_zombie[msg_entity])
4860 return;
4861
4862 // Not an active weapon
4863 if (get_msg_arg_int(1) != 1)
4864 return;
4865
4866 // Unlimited clip disabled for class
4867 if (g_survivor[msg_entity] ? get_pcvar_num(cvar_survinfammo) <= 1 : get_pcvar_num(cvar_infammo) <= 1)
4868 return;
4869
4870 // Get weapon's id
4871 static weapon
4872 weapon = get_msg_arg_int(2)
4873
4874 // Unlimited Clip Ammo for this weapon?
4875 if (MAXBPAMMO[weapon] > 2)
4876 {
4877 // Max out clip ammo
4878 cs_set_weapon_ammo(fm_cs_get_current_weapon_ent(msg_entity), MAXCLIP[weapon])
4879
4880 // HUD should show full clip all the time
4881 set_msg_arg_int(3, get_msg_argtype(3), MAXCLIP[weapon])
4882 }
4883}
4884
4885// Take off player's money
4886public message_money(msg_id, msg_dest, msg_entity)
4887{
4888 // Remove money setting enabled?
4889 if (!get_pcvar_num(cvar_removemoney))
4890 return PLUGIN_CONTINUE;
4891
4892 fm_cs_set_user_money(msg_entity, 0)
4893 return PLUGIN_HANDLED;
4894}
4895
4896// Fix for the HL engine bug when HP is multiples of 256
4897public message_health(msg_id, msg_dest, msg_entity)
4898{
4899 // Get player's health
4900 static health
4901 health = get_msg_arg_int(1)
4902
4903 // Don't bother
4904 if (health < 256) return;
4905
4906 // Check if we need to fix it
4907 if (health % 256 == 0)
4908 fm_set_user_health(msg_entity, pev(msg_entity, pev_health) + 1)
4909
4910 // HUD can only show as much as 255 hp
4911 set_msg_arg_int(1, get_msg_argtype(1), 255)
4912}
4913
4914// Block flashlight battery messages if custom flashlight is enabled instead
4915public message_flashbat()
4916{
4917 if (g_cached_customflash)
4918 return PLUGIN_HANDLED;
4919
4920 return PLUGIN_CONTINUE;
4921}
4922
4923// Flashbangs should only affect zombies
4924public message_screenfade(msg_id, msg_dest, msg_entity)
4925{
4926 if (get_msg_arg_int(4) != 255 || get_msg_arg_int(5) != 255 || get_msg_arg_int(6) != 255 || get_msg_arg_int(7) < 200)
4927 return PLUGIN_CONTINUE;
4928
4929 // Nemesis shouldn't be FBed
4930 if (g_zombie[msg_entity] && !g_nemesis[msg_entity])
4931 {
4932 // Set flash color to nighvision's
4933 set_msg_arg_int(4, get_msg_argtype(4), get_pcvar_num(cvar_nvgcolor[0]))
4934 set_msg_arg_int(5, get_msg_argtype(5), get_pcvar_num(cvar_nvgcolor[1]))
4935 set_msg_arg_int(6, get_msg_argtype(6), get_pcvar_num(cvar_nvgcolor[2]))
4936 return PLUGIN_CONTINUE;
4937 }
4938
4939 return PLUGIN_HANDLED;
4940}
4941
4942// Prevent spectators' nightvision from being turned off when switching targets, etc.
4943public message_nvgtoggle()
4944{
4945 return PLUGIN_HANDLED;
4946}
4947
4948// Set correct model on player corpses
4949public message_clcorpse()
4950{
4951 set_msg_arg_string(1, g_playermodel[get_msg_arg_int(12)])
4952}
4953
4954// Prevent zombies from seeing any weapon pickup icon
4955public message_weappickup(msg_id, msg_dest, msg_entity)
4956{
4957 if (g_zombie[msg_entity])
4958 return PLUGIN_HANDLED;
4959
4960 return PLUGIN_CONTINUE;
4961}
4962
4963// Prevent zombies from seeing any ammo pickup icon
4964public message_ammopickup(msg_id, msg_dest, msg_entity)
4965{
4966 if (g_zombie[msg_entity])
4967 return PLUGIN_HANDLED;
4968
4969 return PLUGIN_CONTINUE;
4970}
4971
4972// Block hostage HUD display
4973public message_scenario()
4974{
4975 if (get_msg_args() > 1)
4976 {
4977 static sprite[8]
4978 get_msg_arg_string(2, sprite, charsmax(sprite))
4979
4980 if (equal(sprite, "hostage"))
4981 return PLUGIN_HANDLED;
4982 }
4983
4984 return PLUGIN_CONTINUE;
4985}
4986
4987// Block hostages from appearing on radar
4988public message_hostagepos()
4989{
4990 return PLUGIN_HANDLED;
4991}
4992
4993// Block some text messages
4994public message_textmsg()
4995{
4996 static textmsg[22]
4997 get_msg_arg_string(2, textmsg, charsmax(textmsg))
4998
4999 // Game restarting, reset scores and call round end to balance the teams
5000 if (equal(textmsg, "#Game_will_restart_in"))
5001 {
5002 g_scorehumans = 0
5003 g_scorezombies = 0
5004 logevent_round_end()
5005 }
5006 // Block round end related messages
5007 else if (equal(textmsg, "#Hostages_Not_Rescued") || equal(textmsg, "#Round_Draw") || equal(textmsg, "#Terrorists_Win") || equal(textmsg, "#CTs_Win"))
5008 {
5009 return PLUGIN_HANDLED;
5010 }
5011
5012 return PLUGIN_CONTINUE;
5013}
5014
5015// Block CS round win audio messages, since we're playing our own instead
5016public message_sendaudio()
5017{
5018 static audio[17]
5019 get_msg_arg_string(2, audio, charsmax(audio))
5020
5021 if(equal(audio[7], "terwin") || equal(audio[7], "ctwin") || equal(audio[7], "rounddraw"))
5022 return PLUGIN_HANDLED;
5023
5024 return PLUGIN_CONTINUE;
5025}
5026
5027// Send actual team scores (T = zombies // CT = humans)
5028public message_teamscore()
5029{
5030 static team[2]
5031 get_msg_arg_string(1, team, charsmax(team))
5032
5033 switch (team[0])
5034 {
5035 // CT
5036 case 'C': set_msg_arg_int(2, get_msg_argtype(2), g_scorehumans)
5037 // Terrorist
5038 case 'T': set_msg_arg_int(2, get_msg_argtype(2), g_scorezombies)
5039 }
5040}
5041
5042// Team Switch (or player joining a team for first time)
5043public message_teaminfo(msg_id, msg_dest)
5044{
5045 // Only hook global messages
5046 if (msg_dest != MSG_ALL && msg_dest != MSG_BROADCAST) return;
5047
5048 // Don't pick up our own TeamInfo messages for this player (bugfix)
5049 if (g_switchingteam) return;
5050
5051 // Get player's id
5052 static id
5053 id = get_msg_arg_int(1)
5054
5055 // Enable spectators' nightvision if not spawning right away
5056 set_task(0.2, "spec_nvision", id)
5057
5058 // Round didn't start yet, nothing to worry about
5059 if (g_newround) return;
5060
5061 // Get his new team
5062 static team[2]
5063 get_msg_arg_string(2, team, charsmax(team))
5064
5065 // Perform some checks to see if they should join a different team instead
5066 switch (team[0])
5067 {
5068 case 'C': // CT
5069 {
5070 if (g_survround && fnGetHumans()) // survivor alive --> switch to T and spawn as zombie
5071 {
5072 g_respawn_as_zombie[id] = true;
5073 remove_task(id+TASK_TEAM)
5074 fm_cs_set_user_team(id, FM_CS_TEAM_T)
5075 set_msg_arg_string(2, "TERRORIST")
5076 }
5077 else if (!fnGetZombies()) // no zombies alive --> switch to T and spawn as zombie
5078 {
5079 g_respawn_as_zombie[id] = true;
5080 remove_task(id+TASK_TEAM)
5081 fm_cs_set_user_team(id, FM_CS_TEAM_T)
5082 set_msg_arg_string(2, "TERRORIST")
5083 }
5084 }
5085 case 'T': // Terrorist
5086 {
5087 if ((g_swarmround || g_survround) && fnGetHumans()) // survivor alive or swarm round w/ humans --> spawn as zombie
5088 {
5089 g_respawn_as_zombie[id] = true;
5090 }
5091 else if (fnGetZombies()) // zombies alive --> switch to CT
5092 {
5093 remove_task(id+TASK_TEAM)
5094 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
5095 set_msg_arg_string(2, "CT")
5096 }
5097 }
5098 }
5099}
5100
5101/*================================================================================
5102 [Main Functions]
5103=================================================================================*/
5104
5105// Make Zombie Task
5106public make_zombie_task()
5107{
5108 // Call make a zombie with no specific mode
5109 make_a_zombie(MODE_NONE, 0)
5110}
5111
5112// Make a Zombie Function
5113make_a_zombie(mode, id)
5114{
5115 // Get alive players count
5116 static iPlayersnum
5117 iPlayersnum = fnGetAlive()
5118
5119 // Not enough players, come back later!
5120 if (iPlayersnum < 1)
5121 {
5122 set_task(2.0, "make_zombie_task", TASK_MAKEZOMBIE)
5123 return;
5124 }
5125
5126 // Round started!
5127 g_newround = false
5128
5129 // Set up some common vars
5130 static forward_id, sound[64], iZombies, iMaxZombies
5131
5132 if ((mode == MODE_NONE && (!get_pcvar_num(cvar_preventconsecutive) || g_lastmode != MODE_SURVIVOR) && random_num(1, get_pcvar_num(cvar_survchance)) == get_pcvar_num(cvar_surv) && iPlayersnum >= get_pcvar_num(cvar_survminplayers)) || mode == MODE_SURVIVOR)
5133 {
5134 // Survivor Mode
5135 g_survround = true
5136 g_lastmode = MODE_SURVIVOR
5137
5138 // Choose player randomly?
5139 if (mode == MODE_NONE)
5140 id = fnGetRandomAlive(random_num(1, iPlayersnum))
5141
5142 // Remember id for calling our forward later
5143 forward_id = id
5144
5145 // Turn player into a survivor
5146 humanme(id, 1, 0)
5147
5148 // Turn the remaining players into zombies
5149 for (id = 1; id <= g_maxplayers; id++)
5150 {
5151 // Not alive
5152 if (!g_isalive[id])
5153 continue;
5154
5155 // Survivor or already a zombie
5156 if (g_survivor[id] || g_zombie[id])
5157 continue;
5158
5159 // Turn into a zombie
5160 zombieme(id, 0, 0, 1, 0)
5161 }
5162
5163 // Play survivor sound
5164 ArrayGetString(sound_survivor, random_num(0, ArraySize(sound_survivor) - 1), sound, charsmax(sound))
5165 PlaySound(sound);
5166
5167 // Show Survivor HUD notice
5168 set_hudmessage(20, 20, 255, HUD_EVENT_X, HUD_EVENT_Y, 1, 0.0, 5.0, 1.0, 1.0, -1)
5169 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_SURVIVOR", g_playername[forward_id])
5170
5171 // Mode fully started!
5172 g_modestarted = true
5173
5174 // Round start forward
5175 ExecuteForward(g_fwRoundStart, g_fwDummyResult, MODE_SURVIVOR, forward_id);
5176 }
5177 else if ((mode == MODE_NONE && (!get_pcvar_num(cvar_preventconsecutive) || g_lastmode != MODE_SWARM) && random_num(1, get_pcvar_num(cvar_swarmchance)) == get_pcvar_num(cvar_swarm) && iPlayersnum >= get_pcvar_num(cvar_swarmminplayers)) || mode == MODE_SWARM)
5178 {
5179 // Swarm Mode
5180 g_swarmround = true
5181 g_lastmode = MODE_SWARM
5182
5183 // Make sure there are alive players on both teams (BUGFIX)
5184 if (!fnGetAliveTs())
5185 {
5186 // Move random player to T team
5187 id = fnGetRandomAlive(random_num(1, iPlayersnum))
5188 remove_task(id+TASK_TEAM)
5189 fm_cs_set_user_team(id, FM_CS_TEAM_T)
5190 fm_user_team_update(id)
5191 }
5192 else if (!fnGetAliveCTs())
5193 {
5194 // Move random player to CT team
5195 id = fnGetRandomAlive(random_num(1, iPlayersnum))
5196 remove_task(id+TASK_TEAM)
5197 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
5198 fm_user_team_update(id)
5199 }
5200
5201 // Turn every T into a zombie
5202 for (id = 1; id <= g_maxplayers; id++)
5203 {
5204 // Not alive
5205 if (!g_isalive[id])
5206 continue;
5207
5208 // Not a Terrorist
5209 if (fm_cs_get_user_team(id) != FM_CS_TEAM_T)
5210 continue;
5211
5212 // Turn into a zombie
5213 zombieme(id, 0, 0, 1, 0)
5214 }
5215
5216 // Play swarm sound
5217 ArrayGetString(sound_swarm, random_num(0, ArraySize(sound_swarm) - 1), sound, charsmax(sound))
5218 PlaySound(sound);
5219
5220 // Show Swarm HUD notice
5221 set_hudmessage(20, 255, 20, HUD_EVENT_X, HUD_EVENT_Y, 1, 0.0, 5.0, 1.0, 1.0, -1)
5222 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_SWARM")
5223
5224 // Mode fully started!
5225 g_modestarted = true
5226
5227 // Round start forward
5228 ExecuteForward(g_fwRoundStart, g_fwDummyResult, MODE_SWARM, 0);
5229 }
5230 else if ((mode == MODE_NONE && (!get_pcvar_num(cvar_preventconsecutive) || g_lastmode != MODE_MULTI) && random_num(1, get_pcvar_num(cvar_multichance)) == get_pcvar_num(cvar_multi) && floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil) >= 2 && floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil) < iPlayersnum && iPlayersnum >= get_pcvar_num(cvar_multiminplayers)) || mode == MODE_MULTI)
5231 {
5232 // Multi Infection Mode
5233 g_lastmode = MODE_MULTI
5234
5235 // iMaxZombies is rounded up, in case there aren't enough players
5236 iMaxZombies = floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil)
5237 iZombies = 0
5238
5239 // Randomly turn iMaxZombies players into zombies
5240 while (iZombies < iMaxZombies)
5241 {
5242 // Keep looping through all players
5243 if (++id > g_maxplayers) id = 1
5244
5245 // Dead or already a zombie
5246 if (!g_isalive[id] || g_zombie[id])
5247 continue;
5248
5249 // Random chance
5250 if (random_num(0, 1))
5251 {
5252 // Turn into a zombie
5253 zombieme(id, 0, 0, 1, 0)
5254 iZombies++
5255 }
5256 }
5257
5258 // Turn the remaining players into humans
5259 for (id = 1; id <= g_maxplayers; id++)
5260 {
5261 // Only those of them who aren't zombies
5262 if (!g_isalive[id] || g_zombie[id])
5263 continue;
5264
5265 // Switch to CT
5266 if (fm_cs_get_user_team(id) != FM_CS_TEAM_CT) // need to change team?
5267 {
5268 remove_task(id+TASK_TEAM)
5269 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
5270 fm_user_team_update(id)
5271 }
5272 }
5273
5274 // Play multi infection sound
5275 ArrayGetString(sound_multi, random_num(0, ArraySize(sound_multi) - 1), sound, charsmax(sound))
5276 PlaySound(sound);
5277
5278 // Show Multi Infection HUD notice
5279 set_hudmessage(200, 50, 0, HUD_EVENT_X, HUD_EVENT_Y, 1, 0.0, 5.0, 1.0, 1.0, -1)
5280 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_MULTI")
5281
5282 // Mode fully started!
5283 g_modestarted = true
5284
5285 // Round start forward
5286 ExecuteForward(g_fwRoundStart, g_fwDummyResult, MODE_MULTI, 0);
5287 }
5288 else if ((mode == MODE_NONE && (!get_pcvar_num(cvar_preventconsecutive) || g_lastmode != MODE_PLAGUE) && random_num(1, get_pcvar_num(cvar_plaguechance)) == get_pcvar_num(cvar_plague) && floatround((iPlayersnum-(get_pcvar_num(cvar_plaguenemnum)+get_pcvar_num(cvar_plaguesurvnum)))*get_pcvar_float(cvar_plagueratio), floatround_ceil) >= 1
5289 && iPlayersnum-(get_pcvar_num(cvar_plaguesurvnum)+get_pcvar_num(cvar_plaguenemnum)+floatround((iPlayersnum-(get_pcvar_num(cvar_plaguenemnum)+get_pcvar_num(cvar_plaguesurvnum)))*get_pcvar_float(cvar_plagueratio), floatround_ceil)) >= 1 && iPlayersnum >= get_pcvar_num(cvar_plagueminplayers)) || mode == MODE_PLAGUE)
5290 {
5291 // Plague Mode
5292 g_plagueround = true
5293 g_lastmode = MODE_PLAGUE
5294
5295 // Turn specified amount of players into Survivors
5296 static iSurvivors, iMaxSurvivors
5297 iMaxSurvivors = get_pcvar_num(cvar_plaguesurvnum)
5298 iSurvivors = 0
5299
5300 while (iSurvivors < iMaxSurvivors)
5301 {
5302 // Choose random guy
5303 id = fnGetRandomAlive(random_num(1, iPlayersnum))
5304
5305 // Already a survivor?
5306 if (g_survivor[id])
5307 continue;
5308
5309 // If not, turn him into one
5310 humanme(id, 1, 0)
5311 iSurvivors++
5312
5313 // Apply survivor health multiplier
5314 fm_set_user_health(id, floatround(float(pev(id, pev_health)) * get_pcvar_float(cvar_plaguesurvhpmulti)))
5315 }
5316
5317 // Turn specified amount of players into Nemesis
5318 static iNemesis, iMaxNemesis
5319 iMaxNemesis = get_pcvar_num(cvar_plaguenemnum)
5320 iNemesis = 0
5321
5322 while (iNemesis < iMaxNemesis)
5323 {
5324 // Choose random guy
5325 id = fnGetRandomAlive(random_num(1, iPlayersnum))
5326
5327 // Already a survivor or nemesis?
5328 if (g_survivor[id] || g_nemesis[id])
5329 continue;
5330
5331 // If not, turn him into one
5332 zombieme(id, 0, 1, 0, 0)
5333 iNemesis++
5334
5335 // Apply nemesis health multiplier
5336 fm_set_user_health(id, floatround(float(pev(id, pev_health)) * get_pcvar_float(cvar_plaguenemhpmulti)))
5337 }
5338
5339 // iMaxZombies is rounded up, in case there aren't enough players
5340 iMaxZombies = floatround((iPlayersnum-(get_pcvar_num(cvar_plaguenemnum)+get_pcvar_num(cvar_plaguesurvnum)))*get_pcvar_float(cvar_plagueratio), floatround_ceil)
5341 iZombies = 0
5342
5343 // Randomly turn iMaxZombies players into zombies
5344 while (iZombies < iMaxZombies)
5345 {
5346 // Keep looping through all players
5347 if (++id > g_maxplayers) id = 1
5348
5349 // Dead or already a zombie or survivor
5350 if (!g_isalive[id] || g_zombie[id] || g_survivor[id])
5351 continue;
5352
5353 // Random chance
5354 if (random_num(0, 1))
5355 {
5356 // Turn into a zombie
5357 zombieme(id, 0, 0, 1, 0)
5358 iZombies++
5359 }
5360 }
5361
5362 // Turn the remaining players into humans
5363 for (id = 1; id <= g_maxplayers; id++)
5364 {
5365 // Only those of them who arent zombies or survivor
5366 if (!g_isalive[id] || g_zombie[id] || g_survivor[id])
5367 continue;
5368
5369 // Switch to CT
5370 if (fm_cs_get_user_team(id) != FM_CS_TEAM_CT) // need to change team?
5371 {
5372 remove_task(id+TASK_TEAM)
5373 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
5374 fm_user_team_update(id)
5375 }
5376 }
5377
5378 // Play plague sound
5379 ArrayGetString(sound_plague, random_num(0, ArraySize(sound_plague) - 1), sound, charsmax(sound))
5380 PlaySound(sound);
5381
5382 // Show Plague HUD notice
5383 set_hudmessage(0, 50, 200, HUD_EVENT_X, HUD_EVENT_Y, 1, 0.0, 5.0, 1.0, 1.0, -1)
5384 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_PLAGUE")
5385
5386 // Mode fully started!
5387 g_modestarted = true
5388
5389 // Round start forward
5390 ExecuteForward(g_fwRoundStart, g_fwDummyResult, MODE_PLAGUE, 0);
5391 }
5392 else
5393 {
5394 // Single Infection Mode or Nemesis Mode
5395
5396 // Choose player randomly?
5397 if (mode == MODE_NONE)
5398 id = fnGetRandomAlive(random_num(1, iPlayersnum))
5399
5400 // Remember id for calling our forward later
5401 forward_id = id
5402
5403 if ((mode == MODE_NONE && (!get_pcvar_num(cvar_preventconsecutive) || g_lastmode != MODE_NEMESIS) && random_num(1, get_pcvar_num(cvar_nemchance)) == get_pcvar_num(cvar_nem) && iPlayersnum >= get_pcvar_num(cvar_nemminplayers)) || mode == MODE_NEMESIS)
5404 {
5405 // Nemesis Mode
5406 g_nemround = true
5407 g_lastmode = MODE_NEMESIS
5408
5409 // Turn player into nemesis
5410 zombieme(id, 0, 1, 0, 0)
5411 }
5412 else
5413 {
5414 // Single Infection Mode
5415 g_lastmode = MODE_INFECTION
5416
5417 // Turn player into the first zombie
5418 zombieme(id, 0, 0, 0, 0)
5419 }
5420
5421 // Remaining players should be humans (CTs)
5422 for (id = 1; id <= g_maxplayers; id++)
5423 {
5424 // Not alive
5425 if (!g_isalive[id])
5426 continue;
5427
5428 // First zombie/nemesis
5429 if (g_zombie[id])
5430 continue;
5431
5432 // Switch to CT
5433 if (fm_cs_get_user_team(id) != FM_CS_TEAM_CT) // need to change team?
5434 {
5435 remove_task(id+TASK_TEAM)
5436 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
5437 fm_user_team_update(id)
5438 }
5439 }
5440
5441 if (g_nemround)
5442 {
5443 // Play Nemesis sound
5444 ArrayGetString(sound_nemesis, random_num(0, ArraySize(sound_nemesis) - 1), sound, charsmax(sound))
5445 PlaySound(sound);
5446
5447 // Show Nemesis HUD notice
5448 set_hudmessage(255, 20, 20, HUD_EVENT_X, HUD_EVENT_Y, 1, 0.0, 5.0, 1.0, 1.0, -1)
5449 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_NEMESIS", g_playername[forward_id])
5450
5451 // Mode fully started!
5452 g_modestarted = true
5453
5454 // Round start forward
5455 ExecuteForward(g_fwRoundStart, g_fwDummyResult, MODE_NEMESIS, forward_id);
5456 }
5457 else
5458 {
5459 // Show First Zombie HUD notice
5460 set_hudmessage(255, 0, 0, HUD_EVENT_X, HUD_EVENT_Y, 0, 0.0, 5.0, 1.0, 1.0, -1)
5461 ShowSyncHudMsg(0, g_MsgSync, "%L",LANG_PLAYER, "NOTICE_FIRST", g_playername[forward_id])
5462
5463 // Mode fully started!
5464 g_modestarted = true
5465
5466 // Round start forward
5467 ExecuteForward(g_fwRoundStart, g_fwDummyResult, MODE_INFECTION, forward_id);
5468 }
5469 }
5470
5471 // Start ambience sounds after a mode begins
5472 if ((g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS] && g_nemround) || (g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR] && g_survround) || (g_ambience_sounds[AMBIENCE_SOUNDS_SWARM] && g_swarmround) || (g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE] && g_plagueround) || (g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION] && !g_nemround && !g_survround && !g_swarmround && !g_plagueround))
5473 {
5474 remove_task(TASK_AMBIENCESOUNDS)
5475 set_task(2.0, "ambience_sound_effects", TASK_AMBIENCESOUNDS)
5476 }
5477}
5478
5479// Zombie Me Function (player id, infector, turn into a nemesis, silent mode, deathmsg and rewards)
5480zombieme(id, infector, nemesis, silentmode, rewards)
5481{
5482 // User infect attempt forward
5483 ExecuteForward(g_fwUserInfect_attempt, g_fwDummyResult, id, infector, nemesis)
5484
5485 // One or more plugins blocked the infection. Only allow this after making sure it's
5486 // not going to leave us with no zombies. Take into account a last player leaving case.
5487 // BUGFIX: only allow after a mode has started, to prevent blocking first zombie e.g.
5488 if (g_fwDummyResult >= ZP_PLUGIN_HANDLED && g_modestarted && fnGetZombies() > g_lastplayerleaving)
5489 return;
5490
5491 // Pre user infect forward
5492 ExecuteForward(g_fwUserInfected_pre, g_fwDummyResult, id, infector, nemesis)
5493
5494 // Show zombie class menu if they haven't chosen any (e.g. just connected)
5495 if (g_zombieclassnext[id] == ZCLASS_NONE && get_pcvar_num(cvar_zclasses))
5496 set_task(0.2, "show_menu_zclass", id)
5497
5498 // Set selected zombie class
5499 g_zombieclass[id] = g_zombieclassnext[id]
5500 // If no class selected yet, use the first (default) one
5501 if (g_zombieclass[id] == ZCLASS_NONE) g_zombieclass[id] = 0
5502
5503 // Way to go...
5504 g_zombie[id] = true
5505 g_nemesis[id] = false
5506 g_survivor[id] = false
5507 g_firstzombie[id] = false
5508
5509 // Remove survivor's aura (bugfix)
5510 set_pev(id, pev_effects, pev(id, pev_effects) &~ EF_BRIGHTLIGHT)
5511
5512 // Remove spawn protection (bugfix)
5513 g_nodamage[id] = false
5514 set_pev(id, pev_effects, pev(id, pev_effects) &~ EF_NODRAW)
5515
5516 // Reset burning duration counter (bugfix)
5517 g_burning_duration[id] = 0
5518
5519 // Show deathmsg and reward infector?
5520 if (rewards && infector)
5521 {
5522 // Send death notice and fix the "dead" attrib on scoreboard
5523 SendDeathMsg(infector, id)
5524 FixDeadAttrib(id)
5525
5526 // Reward frags, deaths, health, and ammo packs
5527 UpdateFrags(infector, id, get_pcvar_num(cvar_fragsinfect), 1, 1)
5528 g_ammopacks[infector] += get_pcvar_num(cvar_ammoinfect)
5529 fm_set_user_health(infector, pev(infector, pev_health) + get_pcvar_num(cvar_zombiebonushp))
5530 }
5531
5532 // Cache speed, knockback, and name for player's class
5533 g_zombie_spd[id] = float(ArrayGetCell(g_zclass_spd, g_zombieclass[id]))
5534 g_zombie_knockback[id] = Float:ArrayGetCell(g_zclass_kb, g_zombieclass[id])
5535 ArrayGetString(g_zclass_name, g_zombieclass[id], g_zombie_classname[id], charsmax(g_zombie_classname[]))
5536
5537 // Set zombie attributes based on the mode
5538 static sound[64]
5539 if (!silentmode)
5540 {
5541 if (nemesis)
5542 {
5543 // Nemesis
5544 g_nemesis[id] = true
5545
5546 // Set health [0 = auto]
5547 if (get_pcvar_num(cvar_nemhp) == 0)
5548 {
5549 if (get_pcvar_num(cvar_nembasehp) == 0)
5550 fm_set_user_health(id, ArrayGetCell(g_zclass_hp, 0) * fnGetAlive())
5551 else
5552 fm_set_user_health(id, get_pcvar_num(cvar_nembasehp) * fnGetAlive())
5553 }
5554 else
5555 fm_set_user_health(id, get_pcvar_num(cvar_nemhp))
5556
5557 // Set gravity, unless frozen
5558 if (!g_frozen[id]) set_pev(id, pev_gravity, get_pcvar_float(cvar_nemgravity))
5559 }
5560 else if (fnGetZombies() == 1)
5561 {
5562 // First zombie
5563 g_firstzombie[id] = true
5564
5565 // Set health and gravity, unless frozen
5566 fm_set_user_health(id, floatround(float(ArrayGetCell(g_zclass_hp, g_zombieclass[id])) * get_pcvar_float(cvar_zombiefirsthp)))
5567 if (!g_frozen[id]) set_pev(id, pev_gravity, Float:ArrayGetCell(g_zclass_grav, g_zombieclass[id]))
5568
5569 // Infection sound
5570 ArrayGetString(zombie_infect, random_num(0, ArraySize(zombie_infect) - 1), sound, charsmax(sound))
5571 emit_sound(id, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
5572 }
5573 else
5574 {
5575 // Infected by someone
5576
5577 // Set health and gravity, unless frozen
5578 fm_set_user_health(id, ArrayGetCell(g_zclass_hp, g_zombieclass[id]))
5579 if (!g_frozen[id]) set_pev(id, pev_gravity, Float:ArrayGetCell(g_zclass_grav, g_zombieclass[id]))
5580
5581 // Infection sound
5582 ArrayGetString(zombie_infect, random_num(0, ArraySize(zombie_infect) - 1), sound, charsmax(sound))
5583 emit_sound(id, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
5584
5585 // Show Infection HUD notice
5586 set_hudmessage(255, 0, 0, HUD_INFECT_X, HUD_INFECT_Y, 0, 0.0, 5.0, 1.0, 1.0, -1)
5587
5588 if (infector) // infected by someone?
5589 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_INFECT2", g_playername[id], g_playername[infector])
5590 else
5591 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_INFECT", g_playername[id])
5592 }
5593 }
5594 else
5595 {
5596 // Silent mode, no HUD messages, no infection sounds
5597
5598 // Set health and gravity, unless frozen
5599 fm_set_user_health(id, ArrayGetCell(g_zclass_hp, g_zombieclass[id]))
5600 if (!g_frozen[id]) set_pev(id, pev_gravity, Float:ArrayGetCell(g_zclass_grav, g_zombieclass[id]))
5601 }
5602
5603 // Remove previous tasks
5604 remove_task(id+TASK_MODEL)
5605 remove_task(id+TASK_BLOOD)
5606 remove_task(id+TASK_AURA)
5607 remove_task(id+TASK_BURN)
5608
5609 // Switch to T
5610 if (fm_cs_get_user_team(id) != FM_CS_TEAM_T) // need to change team?
5611 {
5612 remove_task(id+TASK_TEAM)
5613 fm_cs_set_user_team(id, FM_CS_TEAM_T)
5614 fm_user_team_update(id)
5615 }
5616
5617 // Custom models stuff
5618 static currentmodel[32], tempmodel[32], already_has_model, i, iRand, size
5619 already_has_model = false
5620
5621 if (g_handle_models_on_separate_ent)
5622 {
5623 // Set the right model
5624 if (g_nemesis[id])
5625 {
5626 iRand = random_num(0, ArraySize(model_nemesis) - 1)
5627 ArrayGetString(model_nemesis, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5628 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_nemesis, iRand))
5629 }
5630 else
5631 {
5632 if (get_pcvar_num(cvar_adminmodelszombie) && (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS]))
5633 {
5634 iRand = random_num(0, ArraySize(model_admin_zombie) - 1)
5635 ArrayGetString(model_admin_zombie, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5636 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_admin_zombie, iRand))
5637 }
5638 else
5639 {
5640 iRand = random_num(ArrayGetCell(g_zclass_modelsstart, g_zombieclass[id]), ArrayGetCell(g_zclass_modelsend, g_zombieclass[id]) - 1)
5641 ArrayGetString(g_zclass_playermodel, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5642 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_zclass_modelindex, iRand))
5643 }
5644 }
5645
5646 // Set model on player model entity
5647 fm_set_playermodel_ent(id)
5648
5649 // Nemesis glow / remove glow on player model entity, unless frozen
5650 if (!g_frozen[id])
5651 {
5652 if (g_nemesis[id] && get_pcvar_num(cvar_nemglow))
5653 fm_set_rendering(g_ent_playermodel[id], kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 25)
5654 else
5655 fm_set_rendering(g_ent_playermodel[id])
5656 }
5657 }
5658 else
5659 {
5660 // Get current model for comparing it with the current one
5661 fm_cs_get_user_model(id, currentmodel, charsmax(currentmodel))
5662
5663 // Set the right model, after checking that we don't already have it
5664 if (g_nemesis[id])
5665 {
5666 size = ArraySize(model_nemesis)
5667 for (i = 0; i < size; i++)
5668 {
5669 ArrayGetString(model_nemesis, i, tempmodel, charsmax(tempmodel))
5670 if (equal(currentmodel, tempmodel)) already_has_model = true
5671 }
5672
5673 if (!already_has_model)
5674 {
5675 iRand = random_num(0, size - 1)
5676 ArrayGetString(model_nemesis, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5677 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_nemesis, iRand))
5678 }
5679 }
5680 else
5681 {
5682 if (get_pcvar_num(cvar_adminmodelszombie) && (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS]))
5683 {
5684 size = ArraySize(model_admin_zombie)
5685 for (i = 0; i < size; i++)
5686 {
5687 ArrayGetString(model_admin_zombie, i, tempmodel, charsmax(tempmodel))
5688 if (equal(currentmodel, tempmodel)) already_has_model = true
5689 }
5690
5691 if (!already_has_model)
5692 {
5693 iRand = random_num(0, size - 1)
5694 ArrayGetString(model_admin_zombie, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5695 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_admin_zombie, iRand))
5696 }
5697 }
5698 else
5699 {
5700 for (i = ArrayGetCell(g_zclass_modelsstart, g_zombieclass[id]); i < ArrayGetCell(g_zclass_modelsend, g_zombieclass[id]); i++)
5701 {
5702 ArrayGetString(g_zclass_playermodel, i, tempmodel, charsmax(tempmodel))
5703 if (equal(currentmodel, tempmodel)) already_has_model = true
5704 }
5705
5706 if (!already_has_model)
5707 {
5708 iRand = random_num(ArrayGetCell(g_zclass_modelsstart, g_zombieclass[id]), ArrayGetCell(g_zclass_modelsend, g_zombieclass[id]) - 1)
5709 ArrayGetString(g_zclass_playermodel, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5710 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_zclass_modelindex, iRand))
5711 }
5712 }
5713 }
5714
5715 // Need to change the model?
5716 if (!already_has_model)
5717 {
5718 // An additional delay is offset at round start
5719 // since SVC_BAD is more likely to be triggered there
5720 if (g_newround)
5721 set_task(5.0 * g_modelchange_delay, "fm_user_model_update", id+TASK_MODEL)
5722 else
5723 fm_user_model_update(id+TASK_MODEL)
5724 }
5725
5726 // Nemesis glow / remove glow, unless frozen
5727 if (!g_frozen[id])
5728 {
5729 if (g_nemesis[id] && get_pcvar_num(cvar_nemglow))
5730 fm_set_rendering(id, kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 25)
5731 else
5732 fm_set_rendering(id)
5733 }
5734 }
5735
5736 // Remove any zoom (bugfix)
5737 cs_set_user_zoom(id, CS_RESET_ZOOM, 1)
5738
5739 // Remove armor
5740 set_pev(id, pev_armorvalue, 0.0)
5741
5742 // Drop weapons when infected
5743 drop_weapons(id, 1)
5744 drop_weapons(id, 2)
5745
5746 // Strip zombies from guns and give them a knife
5747 fm_strip_user_weapons(id)
5748 fm_give_item(id, "weapon_knife")
5749
5750 // Fancy effects
5751 infection_effects(id)
5752
5753 // Nemesis aura task
5754 if (g_nemesis[id] && get_pcvar_num(cvar_nemaura))
5755 set_task(0.1, "zombie_aura", id+TASK_AURA, _, _, "b")
5756
5757 // Give Zombies Night Vision?
5758 if (get_pcvar_num(cvar_nvggive))
5759 {
5760 g_nvision[id] = true
5761
5762 if (!g_isbot[id])
5763 {
5764 // Turn on Night Vision automatically?
5765 if (get_pcvar_num(cvar_nvggive) == 1)
5766 {
5767 g_nvisionenabled[id] = true
5768
5769 // Custom nvg?
5770 if (get_pcvar_num(cvar_customnvg))
5771 {
5772 remove_task(id+TASK_NVISION)
5773 set_task(0.1, "set_user_nvision", id+TASK_NVISION, _, _, "b")
5774 }
5775 else
5776 set_user_gnvision(id, 1)
5777 }
5778 // Turn off nightvision when infected (bugfix)
5779 else if (g_nvisionenabled[id])
5780 {
5781 if (get_pcvar_num(cvar_customnvg)) remove_task(id+TASK_NVISION)
5782 else set_user_gnvision(id, 0)
5783 g_nvisionenabled[id] = false
5784 }
5785 }
5786 else
5787 cs_set_user_nvg(id, 1); // turn on NVG for bots
5788 }
5789 // Disable nightvision when infected (bugfix)
5790 else if (g_nvision[id])
5791 {
5792 if (g_isbot[id]) cs_set_user_nvg(id, 0) // Turn off NVG for bots
5793 if (get_pcvar_num(cvar_customnvg)) remove_task(id+TASK_NVISION)
5794 else if (g_nvisionenabled[id]) set_user_gnvision(id, 0)
5795 g_nvision[id] = false
5796 g_nvisionenabled[id] = false
5797 }
5798
5799 // Set custom FOV?
5800 if (get_pcvar_num(cvar_zombiefov) != 90 && get_pcvar_num(cvar_zombiefov) != 0)
5801 {
5802 message_begin(MSG_ONE, g_msgSetFOV, _, id)
5803 write_byte(get_pcvar_num(cvar_zombiefov)) // fov angle
5804 message_end()
5805 }
5806
5807 // Call the bloody task
5808 if (!g_nemesis[id] && get_pcvar_num(cvar_zombiebleeding))
5809 set_task(0.7, "make_blood", id+TASK_BLOOD, _, _, "b")
5810
5811 // Idle sounds task
5812 if (!g_nemesis[id])
5813 set_task(random_float(50.0, 70.0), "zombie_play_idle", id+TASK_BLOOD, _, _, "b")
5814
5815 // Turn off zombie's flashlight
5816 turn_off_flashlight(id)
5817
5818 // Post user infect forward
5819 ExecuteForward(g_fwUserInfected_post, g_fwDummyResult, id, infector, nemesis)
5820
5821 // Last Zombie Check
5822 fnCheckLastZombie()
5823}
5824
5825// Function Human Me (player id, turn into a survivor, silent mode)
5826humanme(id, survivor, silentmode)
5827{
5828 // User humanize attempt forward
5829 ExecuteForward(g_fwUserHumanize_attempt, g_fwDummyResult, id, survivor)
5830
5831 // One or more plugins blocked the "humanization". Only allow this after making sure it's
5832 // not going to leave us with no humans. Take into account a last player leaving case.
5833 // BUGFIX: only allow after a mode has started, to prevent blocking first survivor e.g.
5834 if (g_fwDummyResult >= ZP_PLUGIN_HANDLED && g_modestarted && fnGetHumans() > g_lastplayerleaving)
5835 return;
5836
5837 // Pre user humanize forward
5838 ExecuteForward(g_fwUserHumanized_pre, g_fwDummyResult, id, survivor)
5839
5840 // Remove previous tasks
5841 remove_task(id+TASK_MODEL)
5842 remove_task(id+TASK_BLOOD)
5843 remove_task(id+TASK_AURA)
5844 remove_task(id+TASK_BURN)
5845 remove_task(id+TASK_NVISION)
5846
5847 // Reset some vars
5848 g_zombie[id] = false
5849 g_nemesis[id] = false
5850 g_survivor[id] = false
5851 g_firstzombie[id] = false
5852 g_canbuy[id] = true
5853 g_nvision[id] = false
5854 g_nvisionenabled[id] = false
5855
5856 // Remove survivor's aura (bugfix)
5857 set_pev(id, pev_effects, pev(id, pev_effects) &~ EF_BRIGHTLIGHT)
5858
5859 // Remove spawn protection (bugfix)
5860 g_nodamage[id] = false
5861 set_pev(id, pev_effects, pev(id, pev_effects) &~ EF_NODRAW)
5862
5863 // Reset burning duration counter (bugfix)
5864 g_burning_duration[id] = 0
5865
5866 // Drop previous weapons
5867 drop_weapons(id, 1)
5868 drop_weapons(id, 2)
5869
5870 // Strip off from weapons
5871 fm_strip_user_weapons(id)
5872 fm_give_item(id, "weapon_knife")
5873
5874 // Set human attributes based on the mode
5875 if (survivor)
5876 {
5877 // Survivor
5878 g_survivor[id] = true
5879
5880 // Set Health [0 = auto]
5881 if (get_pcvar_num(cvar_survhp) == 0)
5882 {
5883 if (get_pcvar_num(cvar_survbasehp) == 0)
5884 fm_set_user_health(id, get_pcvar_num(cvar_humanhp) * fnGetAlive())
5885 else
5886 fm_set_user_health(id, get_pcvar_num(cvar_survbasehp) * fnGetAlive())
5887 }
5888 else
5889 fm_set_user_health(id, get_pcvar_num(cvar_survhp))
5890
5891 // Set gravity, unless frozen
5892 if (!g_frozen[id]) set_pev(id, pev_gravity, get_pcvar_float(cvar_survgravity))
5893
5894 // Give survivor his own weapon
5895 static survweapon[32]
5896 get_pcvar_string(cvar_survweapon, survweapon, charsmax(survweapon))
5897 fm_give_item(id, survweapon)
5898 ExecuteHamB(Ham_GiveAmmo, id, MAXBPAMMO[cs_weapon_name_to_id(survweapon)], AMMOTYPE[cs_weapon_name_to_id(survweapon)], MAXBPAMMO[cs_weapon_name_to_id(survweapon)])
5899
5900 // Turn off his flashlight
5901 turn_off_flashlight(id)
5902
5903 // Give the survivor a bright light
5904 if (get_pcvar_num(cvar_survaura)) set_pev(id, pev_effects, pev(id, pev_effects) | EF_BRIGHTLIGHT)
5905
5906 // Survivor bots will also need nightvision to see in the dark
5907 if (g_isbot[id])
5908 {
5909 g_nvision[id] = true
5910 cs_set_user_nvg(id, 1)
5911 }
5912 }
5913 else
5914 {
5915 // Human taking an antidote
5916
5917 // Set health
5918 fm_set_user_health(id, get_pcvar_num(cvar_humanhp))
5919
5920 // Set gravity, unless frozen
5921 if (!g_frozen[id]) set_pev(id, pev_gravity, get_pcvar_float(cvar_humangravity))
5922
5923 // Show custom buy menu?
5924 if (get_pcvar_num(cvar_buycustom))
5925 set_task(0.2, "show_menu_buy1", id+TASK_SPAWN)
5926
5927 // Silent mode = no HUD messages, no antidote sound
5928 if (!silentmode)
5929 {
5930 // Antidote sound
5931 static sound[64]
5932 ArrayGetString(sound_antidote, random_num(0, ArraySize(sound_antidote) - 1), sound, charsmax(sound))
5933 emit_sound(id, CHAN_ITEM, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
5934
5935 // Show Antidote HUD notice
5936 set_hudmessage(0, 0, 255, HUD_INFECT_X, HUD_INFECT_Y, 0, 0.0, 5.0, 1.0, 1.0, -1)
5937 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_ANTIDOTE", g_playername[id])
5938 }
5939 }
5940
5941 // Switch to CT
5942 if (fm_cs_get_user_team(id) != FM_CS_TEAM_CT) // need to change team?
5943 {
5944 remove_task(id+TASK_TEAM)
5945 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
5946 fm_user_team_update(id)
5947 }
5948
5949 // Custom models stuff
5950 static currentmodel[32], tempmodel[32], already_has_model, i, iRand, size
5951 already_has_model = false
5952
5953 if (g_handle_models_on_separate_ent)
5954 {
5955 // Set the right model
5956 if (g_survivor[id])
5957 {
5958 iRand = random_num(0, ArraySize(model_survivor) - 1)
5959 ArrayGetString(model_survivor, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5960 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_survivor, iRand))
5961 }
5962 else
5963 {
5964 if (get_pcvar_num(cvar_adminmodelshuman) && (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS]))
5965 {
5966 iRand = random_num(0, ArraySize(model_admin_human) - 1)
5967 ArrayGetString(model_admin_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5968 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_admin_human, iRand))
5969 }
5970 else
5971 {
5972 iRand = random_num(0, ArraySize(model_human) - 1)
5973 ArrayGetString(model_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
5974 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_human, iRand))
5975 }
5976 }
5977
5978 // Set model on player model entity
5979 fm_set_playermodel_ent(id)
5980
5981 // Set survivor glow / remove glow on player model entity, unless frozen
5982 if (!g_frozen[id])
5983 {
5984 if (g_survivor[id] && get_pcvar_num(cvar_survglow))
5985 fm_set_rendering(g_ent_playermodel[id], kRenderFxGlowShell, 0, 0, 255, kRenderNormal, 25)
5986 else
5987 fm_set_rendering(g_ent_playermodel[id])
5988 }
5989 }
5990 else
5991 {
5992 // Get current model for comparing it with the current one
5993 fm_cs_get_user_model(id, currentmodel, charsmax(currentmodel))
5994
5995 // Set the right model, after checking that we don't already have it
5996 if (g_survivor[id])
5997 {
5998 size = ArraySize(model_survivor)
5999 for (i = 0; i < size; i++)
6000 {
6001 ArrayGetString(model_survivor, i, tempmodel, charsmax(tempmodel))
6002 if (equal(currentmodel, tempmodel)) already_has_model = true
6003 }
6004
6005 if (!already_has_model)
6006 {
6007 iRand = random_num(0, size - 1)
6008 ArrayGetString(model_survivor, iRand, g_playermodel[id], charsmax(g_playermodel[]))
6009 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_survivor, iRand))
6010 }
6011 }
6012 else
6013 {
6014 if (get_pcvar_num(cvar_adminmodelshuman) && (get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS]))
6015 {
6016 size = ArraySize(model_admin_human)
6017 for (i = 0; i < size; i++)
6018 {
6019 ArrayGetString(model_admin_human, i, tempmodel, charsmax(tempmodel))
6020 if (equal(currentmodel, tempmodel)) already_has_model = true
6021 }
6022
6023 if (!already_has_model)
6024 {
6025 iRand = random_num(0, size - 1)
6026 ArrayGetString(model_admin_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
6027 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_admin_human, iRand))
6028 }
6029 }
6030 else
6031 {
6032 size = ArraySize(model_human)
6033 for (i = 0; i < size; i++)
6034 {
6035 ArrayGetString(model_human, i, tempmodel, charsmax(tempmodel))
6036 if (equal(currentmodel, tempmodel)) already_has_model = true
6037 }
6038
6039 if (!already_has_model)
6040 {
6041 iRand = random_num(0, size - 1)
6042 ArrayGetString(model_human, iRand, g_playermodel[id], charsmax(g_playermodel[]))
6043 if (g_set_modelindex_offset) fm_cs_set_user_model_index(id, ArrayGetCell(g_modelindex_human, iRand))
6044 }
6045 }
6046 }
6047
6048 // Need to change the model?
6049 if (!already_has_model)
6050 {
6051 // An additional delay is offset at round start
6052 // since SVC_BAD is more likely to be triggered there
6053 if (g_newround)
6054 set_task(5.0 * g_modelchange_delay, "fm_user_model_update", id+TASK_MODEL)
6055 else
6056 fm_user_model_update(id+TASK_MODEL)
6057 }
6058
6059 // Set survivor glow / remove glow, unless frozen
6060 if (!g_frozen[id])
6061 {
6062 if (g_survivor[id] && get_pcvar_num(cvar_survglow))
6063 fm_set_rendering(id, kRenderFxGlowShell, 0, 0, 255, kRenderNormal, 25)
6064 else
6065 fm_set_rendering(id)
6066 }
6067 }
6068
6069 // Restore FOV?
6070 if (get_pcvar_num(cvar_zombiefov) != 90 && get_pcvar_num(cvar_zombiefov) != 0)
6071 {
6072 message_begin(MSG_ONE, g_msgSetFOV, _, id)
6073 write_byte(90) // angle
6074 message_end()
6075 }
6076
6077 // Disable nightvision
6078 if (g_isbot[id]) cs_set_user_nvg(id, 0)
6079 else if (!get_pcvar_num(cvar_customnvg) && g_nvisionenabled[id]) set_user_gnvision(id, 0)
6080
6081 // Post user humanize forward
6082 ExecuteForward(g_fwUserHumanized_post, g_fwDummyResult, id, survivor)
6083
6084 // Last Zombie Check
6085 fnCheckLastZombie()
6086}
6087
6088/*================================================================================
6089 [Other Functions and Tasks]
6090=================================================================================*/
6091
6092public cache_cvars()
6093{
6094 g_cached_zombiesilent = get_pcvar_num(cvar_zombiesilent)
6095 g_cached_customflash = get_pcvar_num(cvar_customflash)
6096 g_cached_humanspd = get_pcvar_float(cvar_humanspd)
6097 g_cached_nemspd = get_pcvar_float(cvar_nemspd)
6098 g_cached_survspd = get_pcvar_float(cvar_survspd)
6099 g_cached_leapzombies = get_pcvar_num(cvar_leapzombies)
6100 g_cached_leapzombiescooldown = get_pcvar_float(cvar_leapzombiescooldown)
6101 g_cached_leapnemesis = get_pcvar_num(cvar_leapnemesis)
6102 g_cached_leapnemesiscooldown = get_pcvar_float(cvar_leapnemesiscooldown)
6103 g_cached_leapsurvivor = get_pcvar_num(cvar_leapsurvivor)
6104 g_cached_leapsurvivorcooldown = get_pcvar_float(cvar_leapsurvivorcooldown)
6105}
6106
6107load_customization_from_files()
6108{
6109 // Build customization file path
6110 new path[64]
6111 get_configsdir(path, charsmax(path))
6112 format(path, charsmax(path), "%s/%s", path, ZP_CUSTOMIZATION_FILE)
6113
6114 // File not present
6115 if (!file_exists(path))
6116 {
6117 new error[100]
6118 formatex(error, charsmax(error), "Cannot load customization file %s!", path)
6119 set_fail_state(error)
6120 return;
6121 }
6122
6123 // Set up some vars to hold parsing info
6124 new linedata[1024], key[64], value[960], section, teams
6125
6126 // Open customization file for reading
6127 new file = fopen(path, "rt")
6128
6129 while (file && !feof(file))
6130 {
6131 // Read one line at a time
6132 fgets(file, linedata, charsmax(linedata))
6133
6134 // Replace newlines with a null character to prevent headaches
6135 replace(linedata, charsmax(linedata), "^n", "")
6136
6137 // Blank line or comment
6138 if (!linedata[0] || linedata[0] == ';') continue;
6139
6140 // New section starting
6141 if (linedata[0] == '[')
6142 {
6143 section++
6144 continue;
6145 }
6146
6147 // Get key and value(s)
6148 strtok(linedata, key, charsmax(key), value, charsmax(value), '=')
6149
6150 // Trim spaces
6151 trim(key)
6152 trim(value)
6153
6154 switch (section)
6155 {
6156 case SECTION_ACCESS_FLAGS:
6157 {
6158 if (equal(key, "ENABLE/DISABLE MOD"))
6159 g_access_flag[ACCESS_ENABLE_MOD] = read_flags(value)
6160 else if (equal(key, "ADMIN MENU"))
6161 g_access_flag[ACCESS_ADMIN_MENU] = read_flags(value)
6162 else if (equal(key, "START MODE INFECTION"))
6163 g_access_flag[ACCESS_MODE_INFECTION] = read_flags(value)
6164 else if (equal(key, "START MODE NEMESIS"))
6165 g_access_flag[ACCESS_MODE_NEMESIS] = read_flags(value)
6166 else if (equal(key, "START MODE SURVIVOR"))
6167 g_access_flag[ACCESS_MODE_SURVIVOR] = read_flags(value)
6168 else if (equal(key, "START MODE SWARM"))
6169 g_access_flag[ACCESS_MODE_SWARM] = read_flags(value)
6170 else if (equal(key, "START MODE MULTI"))
6171 g_access_flag[ACCESS_MODE_MULTI] = read_flags(value)
6172 else if (equal(key, "START MODE PLAGUE"))
6173 g_access_flag[ACCESS_MODE_PLAGUE] = read_flags(value)
6174 else if (equal(key, "MAKE ZOMBIE"))
6175 g_access_flag[ACCESS_MAKE_ZOMBIE] = read_flags(value)
6176 else if (equal(key, "MAKE HUMAN"))
6177 g_access_flag[ACCESS_MAKE_HUMAN] = read_flags(value)
6178 else if (equal(key, "MAKE NEMESIS"))
6179 g_access_flag[ACCESS_MAKE_NEMESIS] = read_flags(value)
6180 else if (equal(key, "MAKE SURVIVOR"))
6181 g_access_flag[ACCESS_MAKE_SURVIVOR] = read_flags(value)
6182 else if (equal(key, "RESPAWN PLAYERS"))
6183 g_access_flag[ACCESS_RESPAWN_PLAYERS] = read_flags(value)
6184 else if (equal(key, "ADMIN MODELS"))
6185 g_access_flag[ACCESS_ADMIN_MODELS] = read_flags(value)
6186 }
6187 case SECTION_PLAYER_MODELS:
6188 {
6189 if (equal(key, "HUMAN"))
6190 {
6191 // Parse models
6192 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6193 {
6194 // Trim spaces
6195 trim(key)
6196 trim(value)
6197
6198 // Add to models array
6199 ArrayPushString(model_human, key)
6200 }
6201 }
6202 else if (equal(key, "NEMESIS"))
6203 {
6204 // Parse models
6205 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6206 {
6207 // Trim spaces
6208 trim(key)
6209 trim(value)
6210
6211 // Add to models array
6212 ArrayPushString(model_nemesis, key)
6213 }
6214 }
6215 else if (equal(key, "SURVIVOR"))
6216 {
6217 // Parse models
6218 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6219 {
6220 // Trim spaces
6221 trim(key)
6222 trim(value)
6223
6224 // Add to models array
6225 ArrayPushString(model_survivor, key)
6226 }
6227 }
6228 else if (equal(key, "ADMIN ZOMBIE"))
6229 {
6230 // Parse models
6231 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6232 {
6233 // Trim spaces
6234 trim(key)
6235 trim(value)
6236
6237 // Add to models array
6238 ArrayPushString(model_admin_zombie, key)
6239 }
6240 }
6241 else if (equal(key, "ADMIN HUMAN"))
6242 {
6243 // Parse models
6244 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6245 {
6246 // Trim spaces
6247 trim(key)
6248 trim(value)
6249
6250 // Add to models array
6251 ArrayPushString(model_admin_human, key)
6252 }
6253 }
6254 else if (equal(key, "FORCE CONSISTENCY"))
6255 g_force_consistency = str_to_num(value)
6256 else if (equal(key, "SAME MODELS FOR ALL"))
6257 g_same_models_for_all = str_to_num(value)
6258 else if (g_same_models_for_all && equal(key, "ZOMBIE"))
6259 {
6260 // Parse models
6261 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6262 {
6263 // Trim spaces
6264 trim(key)
6265 trim(value)
6266
6267 // Add to models array
6268 ArrayPushString(g_zclass_playermodel, key)
6269
6270 // Precache model and retrieve its modelindex
6271 formatex(linedata, charsmax(linedata), "models/player/%s/%s.mdl", key, key)
6272 ArrayPushCell(g_zclass_modelindex, engfunc(EngFunc_PrecacheModel, linedata))
6273 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, linedata)
6274 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, linedata)
6275 }
6276 }
6277 }
6278 case SECTION_WEAPON_MODELS:
6279 {
6280 if (equal(key, "V_KNIFE HUMAN"))
6281 copy(model_vknife_human, charsmax(model_vknife_human), value)
6282 else if (equal(key, "V_KNIFE NEMESIS"))
6283 copy(model_vknife_nemesis, charsmax(model_vknife_nemesis), value)
6284 else if (equal(key, "V_M249 SURVIVOR"))
6285 copy(model_vm249_survivor, charsmax(model_vm249_survivor), value)
6286 else if (equal(key, "GRENADE INFECT"))
6287 copy(model_grenade_infect, charsmax(model_grenade_infect), value)
6288 else if (equal(key, "GRENADE FIRE"))
6289 copy(model_grenade_fire, charsmax(model_grenade_fire), value)
6290 else if (equal(key, "GRENADE FROST"))
6291 copy(model_grenade_frost, charsmax(model_grenade_frost), value)
6292 else if (equal(key, "GRENADE FLARE"))
6293 copy(model_grenade_flare, charsmax(model_grenade_flare), value)
6294 else if (equal(key, "V_KNIFE ADMIN HUMAN"))
6295 copy(model_vknife_admin_human, charsmax(model_vknife_admin_human), value)
6296 else if (equal(key, "V_KNIFE ADMIN ZOMBIE"))
6297 copy(model_vknife_admin_zombie, charsmax(model_vknife_admin_zombie), value)
6298 }
6299 case SECTION_GRENADE_SPRITES:
6300 {
6301 if (equal(key, "TRAIL"))
6302 copy(sprite_grenade_trail, charsmax(sprite_grenade_trail), value)
6303 else if (equal(key, "RING"))
6304 copy(sprite_grenade_ring, charsmax(sprite_grenade_ring), value)
6305 else if (equal(key, "FIRE"))
6306 copy(sprite_grenade_fire, charsmax(sprite_grenade_fire), value)
6307 else if (equal(key, "SMOKE"))
6308 copy(sprite_grenade_smoke, charsmax(sprite_grenade_smoke), value)
6309 else if (equal(key, "GLASS"))
6310 copy(sprite_grenade_glass, charsmax(sprite_grenade_glass), value)
6311 }
6312 case SECTION_SOUNDS:
6313 {
6314 if (equal(key, "WIN ZOMBIES"))
6315 {
6316 // Parse sounds
6317 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6318 {
6319 // Trim spaces
6320 trim(key)
6321 trim(value)
6322
6323 // Add to sounds array
6324 ArrayPushString(sound_win_zombies, key)
6325 }
6326 }
6327 else if (equal(key, "WIN HUMANS"))
6328 {
6329 // Parse sounds
6330 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6331 {
6332 // Trim spaces
6333 trim(key)
6334 trim(value)
6335
6336 // Add to sounds array
6337 ArrayPushString(sound_win_humans, key)
6338 }
6339 }
6340 else if (equal(key, "WIN NO ONE"))
6341 {
6342 // Parse sounds
6343 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6344 {
6345 // Trim spaces
6346 trim(key)
6347 trim(value)
6348
6349 // Add to sounds array
6350 ArrayPushString(sound_win_no_one, key)
6351 }
6352 }
6353 else if (equal(key, "ZOMBIE INFECT"))
6354 {
6355 // Parse sounds
6356 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6357 {
6358 // Trim spaces
6359 trim(key)
6360 trim(value)
6361
6362 // Add to sounds array
6363 ArrayPushString(zombie_infect, key)
6364 }
6365 }
6366 else if (equal(key, "ZOMBIE PAIN"))
6367 {
6368 // Parse sounds
6369 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6370 {
6371 // Trim spaces
6372 trim(key)
6373 trim(value)
6374
6375 // Add to sounds array
6376 ArrayPushString(zombie_pain, key)
6377 }
6378 }
6379 else if (equal(key, "NEMESIS PAIN"))
6380 {
6381 // Parse sounds
6382 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6383 {
6384 // Trim spaces
6385 trim(key)
6386 trim(value)
6387
6388 // Add to sounds array
6389 ArrayPushString(nemesis_pain, key)
6390 }
6391 }
6392 else if (equal(key, "ZOMBIE DIE"))
6393 {
6394 // Parse sounds
6395 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6396 {
6397 // Trim spaces
6398 trim(key)
6399 trim(value)
6400
6401 // Add to sounds array
6402 ArrayPushString(zombie_die, key)
6403 }
6404 }
6405 else if (equal(key, "ZOMBIE FALL"))
6406 {
6407 // Parse sounds
6408 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6409 {
6410 // Trim spaces
6411 trim(key)
6412 trim(value)
6413
6414 // Add to sounds array
6415 ArrayPushString(zombie_fall, key)
6416 }
6417 }
6418 else if (equal(key, "ZOMBIE MISS SLASH"))
6419 {
6420 // Parse sounds
6421 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6422 {
6423 // Trim spaces
6424 trim(key)
6425 trim(value)
6426
6427 // Add to sounds array
6428 ArrayPushString(zombie_miss_slash, key)
6429 }
6430 }
6431 else if (equal(key, "ZOMBIE MISS WALL"))
6432 {
6433 // Parse sounds
6434 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6435 {
6436 // Trim spaces
6437 trim(key)
6438 trim(value)
6439
6440 // Add to sounds array
6441 ArrayPushString(zombie_miss_wall, key)
6442 }
6443 }
6444 else if (equal(key, "ZOMBIE HIT NORMAL"))
6445 {
6446 // Parse sounds
6447 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6448 {
6449 // Trim spaces
6450 trim(key)
6451 trim(value)
6452
6453 // Add to sounds array
6454 ArrayPushString(zombie_hit_normal, key)
6455 }
6456 }
6457 else if (equal(key, "ZOMBIE HIT STAB"))
6458 {
6459 // Parse sounds
6460 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6461 {
6462 // Trim spaces
6463 trim(key)
6464 trim(value)
6465
6466 // Add to sounds array
6467 ArrayPushString(zombie_hit_stab, key)
6468 }
6469 }
6470 else if (equal(key, "ZOMBIE IDLE"))
6471 {
6472 // Parse sounds
6473 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6474 {
6475 // Trim spaces
6476 trim(key)
6477 trim(value)
6478
6479 // Add to sounds array
6480 ArrayPushString(zombie_idle, key)
6481 }
6482 }
6483 else if (equal(key, "ZOMBIE IDLE LAST"))
6484 {
6485 // Parse sounds
6486 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6487 {
6488 // Trim spaces
6489 trim(key)
6490 trim(value)
6491
6492 // Add to sounds array
6493 ArrayPushString(zombie_idle_last, key)
6494 }
6495 }
6496 else if (equal(key, "ZOMBIE MADNESS"))
6497 {
6498 // Parse sounds
6499 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6500 {
6501 // Trim spaces
6502 trim(key)
6503 trim(value)
6504
6505 // Add to sounds array
6506 ArrayPushString(zombie_madness, key)
6507 }
6508 }
6509 else if (equal(key, "ROUND NEMESIS"))
6510 {
6511 // Parse sounds
6512 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6513 {
6514 // Trim spaces
6515 trim(key)
6516 trim(value)
6517
6518 // Add to sounds array
6519 ArrayPushString(sound_nemesis, key)
6520 }
6521 }
6522 else if (equal(key, "ROUND SURVIVOR"))
6523 {
6524 // Parse sounds
6525 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6526 {
6527 // Trim spaces
6528 trim(key)
6529 trim(value)
6530
6531 // Add to sounds array
6532 ArrayPushString(sound_survivor, key)
6533 }
6534 }
6535 else if (equal(key, "ROUND SWARM"))
6536 {
6537 // Parse sounds
6538 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6539 {
6540 // Trim spaces
6541 trim(key)
6542 trim(value)
6543
6544 // Add to sounds array
6545 ArrayPushString(sound_swarm, key)
6546 }
6547 }
6548 else if (equal(key, "ROUND MULTI"))
6549 {
6550 // Parse sounds
6551 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6552 {
6553 // Trim spaces
6554 trim(key)
6555 trim(value)
6556
6557 // Add to sounds array
6558 ArrayPushString(sound_multi, key)
6559 }
6560 }
6561 else if (equal(key, "ROUND PLAGUE"))
6562 {
6563 // Parse sounds
6564 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6565 {
6566 // Trim spaces
6567 trim(key)
6568 trim(value)
6569
6570 // Add to sounds array
6571 ArrayPushString(sound_plague, key)
6572 }
6573 }
6574 else if (equal(key, "GRENADE INFECT EXPLODE"))
6575 {
6576 // Parse sounds
6577 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6578 {
6579 // Trim spaces
6580 trim(key)
6581 trim(value)
6582
6583 // Add to sounds array
6584 ArrayPushString(grenade_infect, key)
6585 }
6586 }
6587 else if (equal(key, "GRENADE INFECT PLAYER"))
6588 {
6589 // Parse sounds
6590 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6591 {
6592 // Trim spaces
6593 trim(key)
6594 trim(value)
6595
6596 // Add to sounds array
6597 ArrayPushString(grenade_infect_player, key)
6598 }
6599 }
6600 else if (equal(key, "GRENADE FIRE EXPLODE"))
6601 {
6602 // Parse sounds
6603 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6604 {
6605 // Trim spaces
6606 trim(key)
6607 trim(value)
6608
6609 // Add to sounds array
6610 ArrayPushString(grenade_fire, key)
6611 }
6612 }
6613 else if (equal(key, "GRENADE FIRE PLAYER"))
6614 {
6615 // Parse sounds
6616 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6617 {
6618 // Trim spaces
6619 trim(key)
6620 trim(value)
6621
6622 // Add to sounds array
6623 ArrayPushString(grenade_fire_player, key)
6624 }
6625 }
6626 else if (equal(key, "GRENADE FROST EXPLODE"))
6627 {
6628 // Parse sounds
6629 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6630 {
6631 // Trim spaces
6632 trim(key)
6633 trim(value)
6634
6635 // Add to sounds array
6636 ArrayPushString(grenade_frost, key)
6637 }
6638 }
6639 else if (equal(key, "GRENADE FROST PLAYER"))
6640 {
6641 // Parse sounds
6642 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6643 {
6644 // Trim spaces
6645 trim(key)
6646 trim(value)
6647
6648 // Add to sounds array
6649 ArrayPushString(grenade_frost_player, key)
6650 }
6651 }
6652 else if (equal(key, "GRENADE FROST BREAK"))
6653 {
6654 // Parse sounds
6655 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6656 {
6657 // Trim spaces
6658 trim(key)
6659 trim(value)
6660
6661 // Add to sounds array
6662 ArrayPushString(grenade_frost_break, key)
6663 }
6664 }
6665 else if (equal(key, "GRENADE FLARE"))
6666 {
6667 // Parse sounds
6668 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6669 {
6670 // Trim spaces
6671 trim(key)
6672 trim(value)
6673
6674 // Add to sounds array
6675 ArrayPushString(grenade_flare, key)
6676 }
6677 }
6678 else if (equal(key, "ANTIDOTE"))
6679 {
6680 // Parse sounds
6681 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6682 {
6683 // Trim spaces
6684 trim(key)
6685 trim(value)
6686
6687 // Add to sounds array
6688 ArrayPushString(sound_antidote, key)
6689 }
6690 }
6691 else if (equal(key, "THUNDER"))
6692 {
6693 // Parse sounds
6694 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6695 {
6696 // Trim spaces
6697 trim(key)
6698 trim(value)
6699
6700 // Add to sounds array
6701 ArrayPushString(sound_thunder, key)
6702 }
6703 }
6704 }
6705 case SECTION_AMBIENCE_SOUNDS:
6706 {
6707 if (equal(key, "INFECTION ENABLE"))
6708 g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION] = str_to_num(value)
6709 else if (g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION] && equal(key, "INFECTION SOUNDS"))
6710 {
6711 // Parse sounds
6712 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6713 {
6714 // Trim spaces
6715 trim(key)
6716 trim(value)
6717
6718 // Add to sounds array
6719 ArrayPushString(sound_ambience1, key)
6720 ArrayPushCell(sound_ambience1_ismp3, equal(key[strlen(key)-4], ".mp3") ? 1 : 0)
6721 }
6722 }
6723 else if (g_ambience_sounds[AMBIENCE_SOUNDS_INFECTION] && equal(key, "INFECTION DURATIONS"))
6724 {
6725 // Parse sounds
6726 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6727 {
6728 // Trim spaces
6729 trim(key)
6730 trim(value)
6731
6732 // Add to sounds array
6733 ArrayPushCell(sound_ambience1_duration, str_to_num(key))
6734 }
6735 }
6736 else if (equal(key, "NEMESIS ENABLE"))
6737 g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS] = str_to_num(value)
6738 else if (g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS] && equal(key, "NEMESIS SOUNDS"))
6739 {
6740 // Parse sounds
6741 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6742 {
6743 // Trim spaces
6744 trim(key)
6745 trim(value)
6746
6747 // Add to sounds array
6748 ArrayPushString(sound_ambience2, key)
6749 ArrayPushCell(sound_ambience2_ismp3, equal(key[strlen(key)-4], ".mp3") ? 1 : 0)
6750 }
6751 }
6752 else if (g_ambience_sounds[AMBIENCE_SOUNDS_NEMESIS] && equal(key, "NEMESIS DURATIONS"))
6753 {
6754 // Parse sounds
6755 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6756 {
6757 // Trim spaces
6758 trim(key)
6759 trim(value)
6760
6761 // Add to sounds array
6762 ArrayPushCell(sound_ambience2_duration, str_to_num(key))
6763 }
6764 }
6765 else if (equal(key, "SURVIVOR ENABLE"))
6766 g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR] = str_to_num(value)
6767 else if (g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR] && equal(key, "SURVIVOR SOUNDS"))
6768 {
6769 // Parse sounds
6770 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6771 {
6772 // Trim spaces
6773 trim(key)
6774 trim(value)
6775
6776 // Add to sounds array
6777 ArrayPushString(sound_ambience3, key)
6778 ArrayPushCell(sound_ambience3_ismp3, equal(key[strlen(key)-4], ".mp3") ? 1 : 0)
6779 }
6780 }
6781 else if (g_ambience_sounds[AMBIENCE_SOUNDS_SURVIVOR] && equal(key, "SURVIVOR DURATIONS"))
6782 {
6783 // Parse sounds
6784 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6785 {
6786 // Trim spaces
6787 trim(key)
6788 trim(value)
6789
6790 // Add to sounds array
6791 ArrayPushCell(sound_ambience3_duration, str_to_num(key))
6792 }
6793 }
6794 else if (equal(key, "SWARM ENABLE"))
6795 g_ambience_sounds[AMBIENCE_SOUNDS_SWARM] = str_to_num(value)
6796 else if (g_ambience_sounds[AMBIENCE_SOUNDS_SWARM] && equal(key, "SWARM SOUNDS"))
6797 {
6798 // Parse sounds
6799 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6800 {
6801 // Trim spaces
6802 trim(key)
6803 trim(value)
6804
6805 // Add to sounds array
6806 ArrayPushString(sound_ambience4, key)
6807 ArrayPushCell(sound_ambience4_ismp3, equal(key[strlen(key)-4], ".mp3") ? 1 : 0)
6808 }
6809 }
6810 else if (g_ambience_sounds[AMBIENCE_SOUNDS_SWARM] && equal(key, "SWARM DURATIONS"))
6811 {
6812 // Parse sounds
6813 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6814 {
6815 // Trim spaces
6816 trim(key)
6817 trim(value)
6818
6819 // Add to sounds array
6820 ArrayPushCell(sound_ambience4_duration, str_to_num(key))
6821 }
6822 }
6823 else if (equal(key, "PLAGUE ENABLE"))
6824 g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE] = str_to_num(value)
6825 else if (g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE] && equal(key, "PLAGUE SOUNDS"))
6826 {
6827 // Parse sounds
6828 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6829 {
6830 // Trim spaces
6831 trim(key)
6832 trim(value)
6833
6834 // Add to sounds array
6835 ArrayPushString(sound_ambience5, key)
6836 ArrayPushCell(sound_ambience5_ismp3, equal(key[strlen(key)-4], ".mp3") ? 1 : 0)
6837 }
6838 }
6839 else if (g_ambience_sounds[AMBIENCE_SOUNDS_PLAGUE] && equal(key, "PLAGUE DURATIONS"))
6840 {
6841 // Parse sounds
6842 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6843 {
6844 // Trim spaces
6845 trim(key)
6846 trim(value)
6847
6848 // Add to sounds array
6849 ArrayPushCell(sound_ambience5_duration, str_to_num(key))
6850 }
6851 }
6852 }
6853 case SECTION_BUY_MENU_WEAPONS:
6854 {
6855 if (equal(key, "PRIMARY"))
6856 {
6857 // Parse weapons
6858 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6859 {
6860 // Trim spaces
6861 trim(key)
6862 trim(value)
6863
6864 // Add to weapons array
6865 ArrayPushString(g_primary_items, key)
6866 ArrayPushCell(g_primary_weaponids, cs_weapon_name_to_id(key))
6867 }
6868 }
6869 else if (equal(key, "SECONDARY"))
6870 {
6871 // Parse weapons
6872 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6873 {
6874 // Trim spaces
6875 trim(key)
6876 trim(value)
6877
6878 // Add to weapons array
6879 ArrayPushString(g_secondary_items, key)
6880 ArrayPushCell(g_secondary_weaponids, cs_weapon_name_to_id(key))
6881 }
6882 }
6883 else if (equal(key, "ADDITIONAL ITEMS"))
6884 {
6885 // Parse weapons
6886 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6887 {
6888 // Trim spaces
6889 trim(key)
6890 trim(value)
6891
6892 // Add to weapons array
6893 ArrayPushString(g_additional_items, key)
6894 }
6895 }
6896 }
6897 case SECTION_EXTRA_ITEMS_WEAPONS:
6898 {
6899 if (equal(key, "NAMES"))
6900 {
6901 // Parse weapon items
6902 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6903 {
6904 // Trim spaces
6905 trim(key)
6906 trim(value)
6907
6908 // Add to weapons array
6909 ArrayPushString(g_extraweapon_names, key)
6910 }
6911 }
6912 else if (equal(key, "ITEMS"))
6913 {
6914 // Parse weapon items
6915 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6916 {
6917 // Trim spaces
6918 trim(key)
6919 trim(value)
6920
6921 // Add to weapons array
6922 ArrayPushString(g_extraweapon_items, key)
6923 }
6924 }
6925 else if (equal(key, "COSTS"))
6926 {
6927 // Parse weapon items
6928 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6929 {
6930 // Trim spaces
6931 trim(key)
6932 trim(value)
6933
6934 // Add to weapons array
6935 ArrayPushCell(g_extraweapon_costs, str_to_num(key))
6936 }
6937 }
6938 }
6939 case SECTION_HARD_CODED_ITEMS_COSTS:
6940 {
6941 if (equal(key, "NIGHT VISION"))
6942 g_extra_costs2[EXTRA_NVISION] = str_to_num(value)
6943 else if (equal(key, "ANTIDOTE"))
6944 g_extra_costs2[EXTRA_ANTIDOTE] = str_to_num(value)
6945 else if (equal(key, "ZOMBIE MADNESS"))
6946 g_extra_costs2[EXTRA_MADNESS] = str_to_num(value)
6947 else if (equal(key, "INFECTION BOMB"))
6948 g_extra_costs2[EXTRA_INFBOMB] = str_to_num(value)
6949 }
6950 case SECTION_WEATHER_EFFECTS:
6951 {
6952 if (equal(key, "RAIN"))
6953 g_ambience_rain = str_to_num(value)
6954 else if (equal(key, "SNOW"))
6955 g_ambience_snow = str_to_num(value)
6956 else if (equal(key, "FOG"))
6957 g_ambience_fog = str_to_num(value)
6958 else if (equal(key, "FOG DENSITY"))
6959 copy(g_fog_density, charsmax(g_fog_density), value)
6960 else if (equal(key, "FOG COLOR"))
6961 copy(g_fog_color, charsmax(g_fog_color), value)
6962 }
6963 case SECTION_SKY:
6964 {
6965 if (equal(key, "ENABLE"))
6966 g_sky_enable = str_to_num(value)
6967 else if (equal(key, "SKY NAMES"))
6968 {
6969 // Parse sky names
6970 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
6971 {
6972 // Trim spaces
6973 trim(key)
6974 trim(value)
6975
6976 // Add to skies array
6977 ArrayPushString(g_sky_names, key)
6978
6979 // Preache custom sky files
6980 formatex(linedata, charsmax(linedata), "gfx/env/%sbk.tga", key)
6981 engfunc(EngFunc_PrecacheGeneric, linedata)
6982 formatex(linedata, charsmax(linedata), "gfx/env/%sdn.tga", key)
6983 engfunc(EngFunc_PrecacheGeneric, linedata)
6984 formatex(linedata, charsmax(linedata), "gfx/env/%sft.tga", key)
6985 engfunc(EngFunc_PrecacheGeneric, linedata)
6986 formatex(linedata, charsmax(linedata), "gfx/env/%slf.tga", key)
6987 engfunc(EngFunc_PrecacheGeneric, linedata)
6988 formatex(linedata, charsmax(linedata), "gfx/env/%srt.tga", key)
6989 engfunc(EngFunc_PrecacheGeneric, linedata)
6990 formatex(linedata, charsmax(linedata), "gfx/env/%sup.tga", key)
6991 engfunc(EngFunc_PrecacheGeneric, linedata)
6992 }
6993 }
6994 }
6995 case SECTION_LIGHTNING:
6996 {
6997 if (equal(key, "LIGHTS"))
6998 {
6999 // Parse lights
7000 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
7001 {
7002 // Trim spaces
7003 trim(key)
7004 trim(value)
7005
7006 // Add to lightning array
7007 ArrayPushString(lights_thunder, key)
7008 }
7009 }
7010 }
7011 case SECTION_ZOMBIE_DECALS:
7012 {
7013 if (equal(key, "DECALS"))
7014 {
7015 // Parse decals
7016 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
7017 {
7018 // Trim spaces
7019 trim(key)
7020 trim(value)
7021
7022 // Add to zombie decals array
7023 ArrayPushCell(zombie_decals, str_to_num(key))
7024 }
7025 }
7026 }
7027 case SECTION_KNOCKBACK:
7028 {
7029 // Format weapon entity name
7030 strtolower(key)
7031 format(key, charsmax(key), "weapon_%s", key)
7032
7033 // Add value to knockback power array
7034 kb_weapon_power[cs_weapon_name_to_id(key)] = str_to_float(value)
7035 }
7036 case SECTION_OBJECTIVE_ENTS:
7037 {
7038 if (equal(key, "CLASSNAMES"))
7039 {
7040 // Parse classnames
7041 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
7042 {
7043 // Trim spaces
7044 trim(key)
7045 trim(value)
7046
7047 // Add to objective ents array
7048 ArrayPushString(g_objective_ents, key)
7049 }
7050 }
7051 }
7052 case SECTION_SVC_BAD:
7053 {
7054 if (equal(key, "MODELCHANGE DELAY"))
7055 g_modelchange_delay = str_to_float(value)
7056 else if (equal(key, "HANDLE MODELS ON SEPARATE ENT"))
7057 g_handle_models_on_separate_ent = str_to_num(value)
7058 else if (equal(key, "SET MODELINDEX OFFSET"))
7059 g_set_modelindex_offset = str_to_num(value)
7060 }
7061 }
7062 }
7063 if (file) fclose(file)
7064
7065 // Build zombie classes file path
7066 get_configsdir(path, charsmax(path))
7067 format(path, charsmax(path), "%s/%s", path, ZP_ZOMBIECLASSES_FILE)
7068
7069 // Parse if present
7070 if (file_exists(path))
7071 {
7072 // Open zombie classes file for reading
7073 file = fopen(path, "rt")
7074
7075 while (file && !feof(file))
7076 {
7077 // Read one line at a time
7078 fgets(file, linedata, charsmax(linedata))
7079
7080 // Replace newlines with a null character to prevent headaches
7081 replace(linedata, charsmax(linedata), "^n", "")
7082
7083 // Blank line or comment
7084 if (!linedata[0] || linedata[0] == ';') continue;
7085
7086 // New class starting
7087 if (linedata[0] == '[')
7088 {
7089 // Remove first and last characters (braces)
7090 linedata[strlen(linedata) - 1] = 0
7091 copy(linedata, charsmax(linedata), linedata[1])
7092
7093 // Store its real name for future reference
7094 ArrayPushString(g_zclass2_realname, linedata)
7095 continue;
7096 }
7097
7098 // Get key and value(s)
7099 strtok(linedata, key, charsmax(key), value, charsmax(value), '=')
7100
7101 // Trim spaces
7102 trim(key)
7103 trim(value)
7104
7105 if (equal(key, "NAME"))
7106 ArrayPushString(g_zclass2_name, value)
7107 else if (equal(key, "INFO"))
7108 ArrayPushString(g_zclass2_info, value)
7109 else if (equal(key, "MODELS"))
7110 {
7111 // Set models start index
7112 ArrayPushCell(g_zclass2_modelsstart, ArraySize(g_zclass2_playermodel))
7113
7114 // Parse class models
7115 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
7116 {
7117 // Trim spaces
7118 trim(key)
7119 trim(value)
7120
7121 // Add to class models array
7122 ArrayPushString(g_zclass2_playermodel, key)
7123 ArrayPushCell(g_zclass2_modelindex, -1)
7124 }
7125
7126 // Set models end index
7127 ArrayPushCell(g_zclass2_modelsend, ArraySize(g_zclass2_playermodel))
7128 }
7129 else if (equal(key, "CLAWMODEL"))
7130 ArrayPushString(g_zclass2_clawmodel, value)
7131 else if (equal(key, "HEALTH"))
7132 ArrayPushCell(g_zclass2_hp, str_to_num(value))
7133 else if (equal(key, "SPEED"))
7134 ArrayPushCell(g_zclass2_spd, str_to_num(value))
7135 else if (equal(key, "GRAVITY"))
7136 ArrayPushCell(g_zclass2_grav, str_to_float(value))
7137 else if (equal(key, "KNOCKBACK"))
7138 ArrayPushCell(g_zclass2_kb, str_to_float(value))
7139 }
7140 if (file) fclose(file)
7141 }
7142
7143 // Build extra items file path
7144 get_configsdir(path, charsmax(path))
7145 format(path, charsmax(path), "%s/%s", path, ZP_EXTRAITEMS_FILE)
7146
7147 // Parse if present
7148 if (file_exists(path))
7149 {
7150 // Open extra items file for reading
7151 file = fopen(path, "rt")
7152
7153 while (file && !feof(file))
7154 {
7155 // Read one line at a time
7156 fgets(file, linedata, charsmax(linedata))
7157
7158 // Replace newlines with a null character to prevent headaches
7159 replace(linedata, charsmax(linedata), "^n", "")
7160
7161 // Blank line or comment
7162 if (!linedata[0] || linedata[0] == ';') continue;
7163
7164 // New item starting
7165 if (linedata[0] == '[')
7166 {
7167 // Remove first and last characters (braces)
7168 linedata[strlen(linedata) - 1] = 0
7169 copy(linedata, charsmax(linedata), linedata[1])
7170
7171 // Store its real name for future reference
7172 ArrayPushString(g_extraitem2_realname, linedata)
7173 continue;
7174 }
7175
7176 // Get key and value(s)
7177 strtok(linedata, key, charsmax(key), value, charsmax(value), '=')
7178
7179 // Trim spaces
7180 trim(key)
7181 trim(value)
7182
7183 if (equal(key, "NAME"))
7184 ArrayPushString(g_extraitem2_name, value)
7185 else if (equal(key, "COST"))
7186 ArrayPushCell(g_extraitem2_cost, str_to_num(value))
7187 else if (equal(key, "TEAMS"))
7188 {
7189 // Clear teams bitsum
7190 teams = 0
7191
7192 // Parse teams
7193 while (value[0] != 0 && strtok(value, key, charsmax(key), value, charsmax(value), ','))
7194 {
7195 // Trim spaces
7196 trim(key)
7197 trim(value)
7198
7199 if (equal(key, ZP_TEAM_NAMES[ZP_TEAM_ZOMBIE]))
7200 teams |= ZP_TEAM_ZOMBIE
7201 else if (equal(key, ZP_TEAM_NAMES[ZP_TEAM_HUMAN]))
7202 teams |= ZP_TEAM_HUMAN
7203 else if (equal(key, ZP_TEAM_NAMES[ZP_TEAM_NEMESIS]))
7204 teams |= ZP_TEAM_NEMESIS
7205 else if (equal(key, ZP_TEAM_NAMES[ZP_TEAM_SURVIVOR]))
7206 teams |= ZP_TEAM_SURVIVOR
7207 }
7208
7209 // Add to teams array
7210 ArrayPushCell(g_extraitem2_team, teams)
7211 }
7212 }
7213 if (file) fclose(file)
7214 }
7215}
7216
7217save_customization()
7218{
7219 new i, k, buffer[512]
7220
7221 // Build zombie classes file path
7222 new path[64]
7223 get_configsdir(path, charsmax(path))
7224 format(path, charsmax(path), "%s/%s", path, ZP_ZOMBIECLASSES_FILE)
7225
7226 // Open zombie classes file for appending data
7227 new file = fopen(path, "at"), size = ArraySize(g_zclass_name)
7228
7229 // Add any new zombie classes data at the end if needed
7230 for (i = 0; i < size; i++)
7231 {
7232 if (ArrayGetCell(g_zclass_new, i))
7233 {
7234 // Add real name
7235 ArrayGetString(g_zclass_name, i, buffer, charsmax(buffer))
7236 format(buffer, charsmax(buffer), "^n[%s]", buffer)
7237 fputs(file, buffer)
7238
7239 // Add caption
7240 ArrayGetString(g_zclass_name, i, buffer, charsmax(buffer))
7241 format(buffer, charsmax(buffer), "^nNAME = %s", buffer)
7242 fputs(file, buffer)
7243
7244 // Add info
7245 ArrayGetString(g_zclass_info, i, buffer, charsmax(buffer))
7246 format(buffer, charsmax(buffer), "^nINFO = %s", buffer)
7247 fputs(file, buffer)
7248
7249 // Add models
7250 for (k = ArrayGetCell(g_zclass_modelsstart, i); k < ArrayGetCell(g_zclass_modelsend, i); k++)
7251 {
7252 if (k == ArrayGetCell(g_zclass_modelsstart, i))
7253 {
7254 // First model, overwrite buffer
7255 ArrayGetString(g_zclass_playermodel, k, buffer, charsmax(buffer))
7256 }
7257 else
7258 {
7259 // Successive models, append to buffer
7260 ArrayGetString(g_zclass_playermodel, k, path, charsmax(path))
7261 format(buffer, charsmax(buffer), "%s , %s", buffer, path)
7262 }
7263 }
7264 format(buffer, charsmax(buffer), "^nMODELS = %s", buffer)
7265 fputs(file, buffer)
7266
7267 // Add clawmodel
7268 ArrayGetString(g_zclass_clawmodel, i, buffer, charsmax(buffer))
7269 format(buffer, charsmax(buffer), "^nCLAWMODEL = %s", buffer)
7270 fputs(file, buffer)
7271
7272 // Add health
7273 formatex(buffer, charsmax(buffer), "^nHEALTH = %d", ArrayGetCell(g_zclass_hp, i))
7274 fputs(file, buffer)
7275
7276 // Add speed
7277 formatex(buffer, charsmax(buffer), "^nSPEED = %d", ArrayGetCell(g_zclass_spd, i))
7278 fputs(file, buffer)
7279
7280 // Add gravity
7281 formatex(buffer, charsmax(buffer), "^nGRAVITY = %.2f", Float:ArrayGetCell(g_zclass_grav, i))
7282 fputs(file, buffer)
7283
7284 // Add knockback
7285 formatex(buffer, charsmax(buffer), "^nKNOCKBACK = %.2f^n", Float:ArrayGetCell(g_zclass_kb, i))
7286 fputs(file, buffer)
7287 }
7288 }
7289 fclose(file)
7290
7291 // Build extra items file path
7292 get_configsdir(path, charsmax(path))
7293 format(path, charsmax(path), "%s/%s", path, ZP_EXTRAITEMS_FILE)
7294
7295 // Open extra items file for appending data
7296 file = fopen(path, "at")
7297 size = ArraySize(g_extraitem_name)
7298
7299 // Add any new extra items data at the end if needed
7300 for (i = EXTRAS_CUSTOM_STARTID; i < size; i++)
7301 {
7302 if (ArrayGetCell(g_extraitem_new, i))
7303 {
7304 // Add real name
7305 ArrayGetString(g_extraitem_name, i, buffer, charsmax(buffer))
7306 format(buffer, charsmax(buffer), "^n[%s]", buffer)
7307 fputs(file, buffer)
7308
7309 // Add caption
7310 ArrayGetString(g_extraitem_name, i, buffer, charsmax(buffer))
7311 format(buffer, charsmax(buffer), "^nNAME = %s", buffer)
7312 fputs(file, buffer)
7313
7314 // Add cost
7315 formatex(buffer, charsmax(buffer), "^nCOST = %d", ArrayGetCell(g_extraitem_cost, i))
7316 fputs(file, buffer)
7317
7318 // Add team
7319 formatex(buffer, charsmax(buffer), "^nTEAMS = %s^n", ZP_TEAM_NAMES[ArrayGetCell(g_extraitem_team, i)])
7320 fputs(file, buffer)
7321 }
7322 }
7323 fclose(file)
7324
7325 // Free arrays containing class/item overrides
7326 ArrayDestroy(g_zclass2_realname)
7327 ArrayDestroy(g_zclass2_name)
7328 ArrayDestroy(g_zclass2_info)
7329 ArrayDestroy(g_zclass2_modelsstart)
7330 ArrayDestroy(g_zclass2_modelsend)
7331 ArrayDestroy(g_zclass2_playermodel)
7332 ArrayDestroy(g_zclass2_modelindex)
7333 ArrayDestroy(g_zclass2_clawmodel)
7334 ArrayDestroy(g_zclass2_hp)
7335 ArrayDestroy(g_zclass2_spd)
7336 ArrayDestroy(g_zclass2_grav)
7337 ArrayDestroy(g_zclass2_kb)
7338 ArrayDestroy(g_zclass_new)
7339 ArrayDestroy(g_extraitem2_realname)
7340 ArrayDestroy(g_extraitem2_name)
7341 ArrayDestroy(g_extraitem2_cost)
7342 ArrayDestroy(g_extraitem2_team)
7343 ArrayDestroy(g_extraitem_new)
7344}
7345
7346// Register Ham Forwards for CZ bots
7347public register_ham_czbots(id)
7348{
7349 // Make sure it's a CZ bot and it's still connected
7350 if (g_hamczbots || !g_isconnected[id] || !get_pcvar_num(cvar_botquota))
7351 return;
7352
7353 RegisterHamFromEntity(Ham_Spawn, id, "fw_PlayerSpawn_Post", 1)
7354 RegisterHamFromEntity(Ham_Killed, id, "fw_PlayerKilled")
7355 RegisterHamFromEntity(Ham_Killed, id, "fw_PlayerKilled_Post", 1)
7356 RegisterHamFromEntity(Ham_TakeDamage, id, "fw_TakeDamage")
7357 RegisterHamFromEntity(Ham_TakeDamage, id, "fw_TakeDamage_Post", 1)
7358 RegisterHamFromEntity(Ham_TraceAttack, id, "fw_TraceAttack")
7359
7360 // Ham forwards for CZ bots succesfully registered
7361 g_hamczbots = true
7362
7363 // If the bot has already spawned, call the forward manually for him
7364 if (is_user_alive(id)) fw_PlayerSpawn_Post(id)
7365}
7366
7367// Disable minmodels task
7368public disable_minmodels(id)
7369{
7370 if (!g_isconnected[id]) return;
7371 client_cmd(id, "cl_minmodels 0")
7372}
7373
7374// Bots automatically buy extra items
7375public bot_buy_extras(taskid)
7376{
7377 // Nemesis or Survivor bots have nothing to buy by default
7378 if (!g_isalive[ID_SPAWN] || g_survivor[ID_SPAWN] || g_nemesis[ID_SPAWN])
7379 return;
7380
7381 if (!g_zombie[ID_SPAWN]) // human bots
7382 {
7383 // Attempt to buy Night Vision
7384 buy_extra_item(ID_SPAWN, EXTRA_NVISION)
7385
7386 // Attempt to buy a weapon
7387 buy_extra_item(ID_SPAWN, random_num(EXTRA_WEAPONS_STARTID, EXTRAS_CUSTOM_STARTID-1))
7388 }
7389 else // zombie bots
7390 {
7391 // Attempt to buy an Antidote
7392 buy_extra_item(ID_SPAWN, EXTRA_ANTIDOTE)
7393 }
7394}
7395
7396// Refill BP Ammo Task
7397public refill_bpammo(const args[], id)
7398{
7399 // Player died or turned into a zombie
7400 if (!g_isalive[id] || g_zombie[id])
7401 return;
7402
7403 set_msg_block(g_msgAmmoPickup, BLOCK_ONCE)
7404 ExecuteHamB(Ham_GiveAmmo, id, MAXBPAMMO[REFILL_WEAPONID], AMMOTYPE[REFILL_WEAPONID], MAXBPAMMO[REFILL_WEAPONID])
7405}
7406
7407// Balance Teams Task
7408balance_teams()
7409{
7410 // Get amount of users playing
7411 static iPlayersnum
7412 iPlayersnum = fnGetPlaying()
7413
7414 // No players, don't bother
7415 if (iPlayersnum < 1) return;
7416
7417 // Split players evenly
7418 static iTerrors, iMaxTerrors, id, team[33]
7419 iMaxTerrors = iPlayersnum/2
7420 iTerrors = 0
7421
7422 // First, set everyone to CT
7423 for (id = 1; id <= g_maxplayers; id++)
7424 {
7425 // Skip if not connected
7426 if (!g_isconnected[id])
7427 continue;
7428
7429 team[id] = fm_cs_get_user_team(id)
7430
7431 // Skip if not playing
7432 if (team[id] == FM_CS_TEAM_SPECTATOR || team[id] == FM_CS_TEAM_UNASSIGNED)
7433 continue;
7434
7435 // Set team
7436 remove_task(id+TASK_TEAM)
7437 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
7438 team[id] = FM_CS_TEAM_CT
7439 }
7440
7441 // Then randomly set half of the players to Terrorists
7442 while (iTerrors < iMaxTerrors)
7443 {
7444 // Keep looping through all players
7445 if (++id > g_maxplayers) id = 1
7446
7447 // Skip if not connected
7448 if (!g_isconnected[id])
7449 continue;
7450
7451 // Skip if not playing or already a Terrorist
7452 if (team[id] != FM_CS_TEAM_CT)
7453 continue;
7454
7455 // Random chance
7456 if (random_num(0, 1))
7457 {
7458 fm_cs_set_user_team(id, FM_CS_TEAM_T)
7459 team[id] = FM_CS_TEAM_T
7460 iTerrors++
7461 }
7462 }
7463}
7464
7465// Welcome Message Task
7466public welcome_msg()
7467{
7468 // Show mod info
7469 zp_colored_print(0, "^x01**** ^x04%s^x01 ****", g_modname)
7470 zp_colored_print(0, "^x04[ZP]^x01 %L", LANG_PLAYER, "NOTICE_INFO1")
7471 if (!get_pcvar_num(cvar_infammo)) zp_colored_print(0, "^x04[ZP]^x01 %L", LANG_PLAYER, "NOTICE_INFO2")
7472
7473 // Show T-virus HUD notice
7474 set_hudmessage(0, 125, 200, HUD_EVENT_X, HUD_EVENT_Y, 0, 0.0, 3.0, 2.0, 1.0, -1)
7475 ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_VIRUS_FREE")
7476}
7477
7478// Respawn Player Task
7479public respawn_player_task(taskid)
7480{
7481 // Get player's team
7482 static team
7483 team = fm_cs_get_user_team(ID_SPAWN)
7484
7485 // Respawn player automatically if allowed on current round
7486 if (!g_endround && team != FM_CS_TEAM_SPECTATOR && team != FM_CS_TEAM_UNASSIGNED && !g_isalive[ID_SPAWN] && (!g_survround || get_pcvar_num(cvar_allowrespawnsurv)) && (!g_swarmround || get_pcvar_num(cvar_allowrespawnswarm)) && (!g_nemround || get_pcvar_num(cvar_allowrespawnnem)) && (!g_plagueround || get_pcvar_num(cvar_allowrespawnplague)))
7487 {
7488 // Infection rounds = none of the above
7489 if (!get_pcvar_num(cvar_allowrespawninfection) && !g_survround && !g_nemround && !g_swarmround && !g_plagueround)
7490 return;
7491
7492 // Override respawn as zombie setting on nemesis and survivor rounds
7493 if (g_survround) g_respawn_as_zombie[ID_SPAWN] = true
7494 else if (g_nemround) g_respawn_as_zombie[ID_SPAWN] = false
7495
7496 respawn_player_manually(ID_SPAWN)
7497 }
7498}
7499
7500// Respawn Player Manually (called after respawn checks are done)
7501respawn_player_manually(id)
7502{
7503 // Set proper team before respawning, so that the TeamInfo message that's sent doesn't confuse PODBots
7504 if (g_respawn_as_zombie[id])
7505 fm_cs_set_user_team(id, FM_CS_TEAM_T)
7506 else
7507 fm_cs_set_user_team(id, FM_CS_TEAM_CT)
7508
7509 // Respawning a player has never been so easy
7510 ExecuteHamB(Ham_CS_RoundRespawn, id)
7511}
7512
7513// Check Round Task -check that we still have both zombies and humans on a round-
7514check_round(leaving_player)
7515{
7516 // Round ended or make_a_zombie task still active
7517 if (g_endround || task_exists(TASK_MAKEZOMBIE))
7518 return;
7519
7520 // Get alive players count
7521 static iPlayersnum, id
7522 iPlayersnum = fnGetAlive()
7523
7524 // Last alive player, don't bother
7525 if (iPlayersnum < 2)
7526 return;
7527
7528 // Last zombie disconnecting
7529 if (g_zombie[leaving_player] && fnGetZombies() == 1)
7530 {
7531 // Only one CT left, don't bother
7532 if (fnGetHumans() == 1 && fnGetCTs() == 1)
7533 return;
7534
7535 // Pick a random one to take his place
7536 while ((id = fnGetRandomAlive(random_num(1, iPlayersnum))) == leaving_player ) { /* keep looping */ }
7537
7538 // Show last zombie left notice
7539 zp_colored_print(0, "^x04[ZP]^x01 %L", LANG_PLAYER, "LAST_ZOMBIE_LEFT", g_playername[id])
7540
7541 // Set player leaving flag
7542 g_lastplayerleaving = true
7543
7544 // Turn into a Nemesis or just a zombie?
7545 if (g_nemesis[leaving_player])
7546 zombieme(id, 0, 1, 0, 0)
7547 else
7548 zombieme(id, 0, 0, 0, 0)
7549
7550 // Remove player leaving flag
7551 g_lastplayerleaving = false
7552
7553 // If Nemesis, set chosen player's health to that of the one who's leaving
7554 if (get_pcvar_num(cvar_keephealthondisconnect) && g_nemesis[leaving_player])
7555 fm_set_user_health(id, pev(leaving_player, pev_health))
7556 }
7557
7558 // Last human disconnecting
7559 else if (!g_zombie[leaving_player] && fnGetHumans() == 1)
7560 {
7561 // Only one T left, don't bother
7562 if (fnGetZombies() == 1 && fnGetTs() == 1)
7563 return;
7564
7565 // Pick a random one to take his place
7566 while ((id = fnGetRandomAlive(random_num(1, iPlayersnum))) == leaving_player ) { /* keep looping */ }
7567
7568 // Show last human left notice
7569 zp_colored_print(0, "^x04[ZP]^x01 %L", LANG_PLAYER, "LAST_HUMAN_LEFT", g_playername[id])
7570
7571 // Set player leaving flag
7572 g_lastplayerleaving = true
7573
7574 // Turn into a Survivor or just a human?
7575 if (g_survivor[leaving_player])
7576 humanme(id, 1, 0)
7577 else
7578 humanme(id, 0, 0)
7579
7580 // Remove player leaving flag
7581 g_lastplayerleaving = false
7582
7583 // If Survivor, set chosen player's health to that of the one who's leaving
7584 if (get_pcvar_num(cvar_keephealthondisconnect) && g_survivor[leaving_player])
7585 fm_set_user_health(id, pev(leaving_player, pev_health))
7586 }
7587}
7588
7589// Lighting Effects Task
7590public lighting_effects()
7591{
7592 // Cache some CVAR values at every 5 secs
7593 cache_cvars()
7594
7595 // Get lighting style
7596 static lighting[2]
7597 get_pcvar_string(cvar_lighting, lighting, charsmax(lighting))
7598 strtolower(lighting)
7599
7600 // Lighting disabled? ["0"]
7601 if (lighting[0] == '0')
7602 return;
7603
7604 // Darkest light settings?
7605 if (lighting[0] >= 'a' && lighting[0] <= 'd')
7606 {
7607 static thunderclap_in_progress, Float:thunder
7608 thunderclap_in_progress = task_exists(TASK_THUNDER)
7609 thunder = get_pcvar_float(cvar_thunder)
7610
7611 // Set thunderclap tasks if not existant
7612 if (thunder > 0.0 && !task_exists(TASK_THUNDER_PRE) && !thunderclap_in_progress)
7613 {
7614 g_lights_i = 0
7615 ArrayGetString(lights_thunder, random_num(0, ArraySize(lights_thunder) - 1), g_lights_cycle, charsmax(g_lights_cycle))
7616 g_lights_cycle_len = strlen(g_lights_cycle)
7617 set_task(thunder, "thunderclap", TASK_THUNDER_PRE)
7618 }
7619
7620 // Set lighting only when no thunderclaps are going on
7621 if (!thunderclap_in_progress) engfunc(EngFunc_LightStyle, 0, lighting)
7622 }
7623 else
7624 {
7625 // Remove thunderclap tasks
7626 remove_task(TASK_THUNDER_PRE)
7627 remove_task(TASK_THUNDER)
7628
7629 // Set lighting
7630 engfunc(EngFunc_LightStyle, 0, lighting)
7631 }
7632}
7633
7634// Thunderclap task
7635public thunderclap()
7636{
7637 // Play thunder sound
7638 if (g_lights_i == 0)
7639 {
7640 static sound[64]
7641 ArrayGetString(sound_thunder, random_num(0, ArraySize(sound_thunder) - 1), sound, charsmax(sound))
7642 PlaySound(sound)
7643 }
7644
7645 // Set lighting
7646 static light[2]
7647 light[0] = g_lights_cycle[g_lights_i]
7648 engfunc(EngFunc_LightStyle, 0, light)
7649
7650 g_lights_i++
7651
7652 // Lighting cycle end?
7653 if (g_lights_i >= g_lights_cycle_len)
7654 {
7655 remove_task(TASK_THUNDER)
7656 lighting_effects()
7657 }
7658 // Lighting cycle start?
7659 else if (!task_exists(TASK_THUNDER))
7660 set_task(0.1, "thunderclap", TASK_THUNDER, _, _, "b")
7661}
7662
7663// Ambience Sound Effects Task
7664public ambience_sound_effects(taskid)
7665{
7666 // Play a random sound depending on the round
7667 static sound[64], iRand, duration, ismp3
7668
7669 if (g_nemround) // Nemesis Mode
7670 {
7671 iRand = random_num(0, ArraySize(sound_ambience2) - 1)
7672 ArrayGetString(sound_ambience2, iRand, sound, charsmax(sound))
7673 duration = ArrayGetCell(sound_ambience2_duration, iRand)
7674 ismp3 = ArrayGetCell(sound_ambience2_ismp3, iRand)
7675 }
7676 else if (g_survround) // Survivor Mode
7677 {
7678 iRand = random_num(0, ArraySize(sound_ambience3) - 1)
7679 ArrayGetString(sound_ambience3, iRand, sound, charsmax(sound))
7680 duration = ArrayGetCell(sound_ambience3_duration, iRand)
7681 ismp3 = ArrayGetCell(sound_ambience3_ismp3, iRand)
7682 }
7683 else if (g_swarmround) // Swarm Mode
7684 {
7685 iRand = random_num(0, ArraySize(sound_ambience4) - 1)
7686 ArrayGetString(sound_ambience4, iRand, sound, charsmax(sound))
7687 duration = ArrayGetCell(sound_ambience4_duration, iRand)
7688 ismp3 = ArrayGetCell(sound_ambience4_ismp3, iRand)
7689 }
7690 else if (g_plagueround) // Plague Mode
7691 {
7692 iRand = random_num(0, ArraySize(sound_ambience5) - 1)
7693 ArrayGetString(sound_ambience5, iRand, sound, charsmax(sound))
7694 duration = ArrayGetCell(sound_ambience5_duration, iRand)
7695 ismp3 = ArrayGetCell(sound_ambience5_ismp3, iRand)
7696 }
7697 else // Infection Mode
7698 {
7699 iRand = random_num(0, ArraySize(sound_ambience1) - 1)
7700 ArrayGetString(sound_ambience1, iRand, sound, charsmax(sound))
7701 duration = ArrayGetCell(sound_ambience1_duration, iRand)
7702 ismp3 = ArrayGetCell(sound_ambience1_ismp3, iRand)
7703 }
7704
7705 // Play it on clients
7706 if (ismp3)
7707 client_cmd(0, "mp3 play ^"sound/%s^"", sound)
7708 else
7709 PlaySound(sound)
7710
7711 // Set the task for when the sound is done playing
7712 set_task(float(duration), "ambience_sound_effects", TASK_AMBIENCESOUNDS)
7713}
7714
7715// Ambience Sounds Stop Task
7716ambience_sound_stop()
7717{
7718 client_cmd(0, "mp3 stop; stopsound")
7719}
7720
7721// Flashlight Charge Task
7722public flashlight_charge(taskid)
7723{
7724 // Drain or charge?
7725 if (g_flashlight[ID_CHARGE])
7726 g_flashbattery[ID_CHARGE] -= get_pcvar_num(cvar_flashdrain)
7727 else
7728 g_flashbattery[ID_CHARGE] += get_pcvar_num(cvar_flashcharge)
7729
7730 // Battery fully charged
7731 if (g_flashbattery[ID_CHARGE] >= 100)
7732 {
7733 // Don't exceed 100%
7734 g_flashbattery[ID_CHARGE] = 100
7735
7736 // Update flashlight battery on HUD
7737 message_begin(MSG_ONE, g_msgFlashBat, _, ID_CHARGE)
7738 write_byte(100) // battery
7739 message_end()
7740
7741 // Task not needed anymore
7742 remove_task(taskid);
7743 return;
7744 }
7745
7746 // Battery depleted
7747 if (g_flashbattery[ID_CHARGE] <= 0)
7748 {
7749 // Turn it off
7750 g_flashlight[ID_CHARGE] = false
7751 g_flashbattery[ID_CHARGE] = 0
7752
7753 // Play flashlight toggle sound
7754 emit_sound(ID_CHARGE, CHAN_ITEM, sound_flashlight, 1.0, ATTN_NORM, 0, PITCH_NORM)
7755
7756 // Update flashlight status on HUD
7757 message_begin(MSG_ONE, g_msgFlashlight, _, ID_CHARGE)
7758 write_byte(0) // toggle
7759 write_byte(0) // battery
7760 message_end()
7761
7762 // Remove flashlight task for this player
7763 remove_task(ID_CHARGE+TASK_FLASH)
7764 }
7765 else
7766 {
7767 // Update flashlight battery on HUD
7768 message_begin(MSG_ONE_UNRELIABLE, g_msgFlashBat, _, ID_CHARGE)
7769 write_byte(g_flashbattery[ID_CHARGE]) // battery
7770 message_end()
7771 }
7772}
7773
7774// Remove Spawn Protection Task
7775public remove_spawn_protection(taskid)
7776{
7777 // Not alive
7778 if (!g_isalive[ID_SPAWN])
7779 return;
7780
7781 // Remove spawn protection
7782 g_nodamage[ID_SPAWN] = false
7783 set_pev(ID_SPAWN, pev_effects, pev(ID_SPAWN, pev_effects) & ~EF_NODRAW)
7784}
7785
7786// Hide Player's Money Task
7787public task_hide_money(taskid)
7788{
7789 // Not alive
7790 if (!g_isalive[ID_SPAWN])
7791 return;
7792
7793 // Hide money
7794 message_begin(MSG_ONE, g_msgHideWeapon, _, ID_SPAWN)
7795 write_byte(HIDE_MONEY) // what to hide bitsum
7796 message_end()
7797
7798 // Hide the HL crosshair that's drawn
7799 message_begin(MSG_ONE, g_msgCrosshair, _, ID_SPAWN)
7800 write_byte(0) // toggle
7801 message_end()
7802}
7803
7804// Turn Off Flashlight and Restore Batteries
7805turn_off_flashlight(id)
7806{
7807 // Restore batteries for the next use
7808 fm_cs_set_user_batteries(id, 100)
7809
7810 // Check if flashlight is on
7811 if (pev(id, pev_effects) & EF_DIMLIGHT)
7812 {
7813 // Turn it off
7814 set_pev(id, pev_impulse, IMPULSE_FLASHLIGHT)
7815 }
7816 else
7817 {
7818 // Clear any stored flashlight impulse (bugfix)
7819 set_pev(id, pev_impulse, 0)
7820 }
7821
7822 // Turn off custom flashlight
7823 if (g_cached_customflash)
7824 {
7825 // Turn it off
7826 g_flashlight[id] = false
7827 g_flashbattery[id] = 100
7828
7829 // Update flashlight HUD
7830 message_begin(MSG_ONE, g_msgFlashlight, _, id)
7831 write_byte(0) // toggle
7832 write_byte(100) // battery
7833 message_end()
7834
7835 // Remove previous tasks
7836 remove_task(id+TASK_CHARGE)
7837 remove_task(id+TASK_FLASH)
7838 }
7839}
7840
7841// Infection Bomb Explosion
7842infection_explode(ent)
7843{
7844 // Round ended (bugfix)
7845 if (g_endround) return;
7846
7847 // Get origin
7848 static Float:originF[3]
7849 pev(ent, pev_origin, originF)
7850
7851 // Make the explosion
7852 create_blast(originF)
7853
7854 // Infection nade explode sound
7855 static sound[64]
7856 ArrayGetString(grenade_infect, random_num(0, ArraySize(grenade_infect) - 1), sound, charsmax(sound))
7857 emit_sound(ent, CHAN_WEAPON, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
7858
7859 // Get attacker
7860 static attacker
7861 attacker = pev(ent, pev_owner)
7862
7863 // Collisions
7864 static victim
7865 victim = -1
7866
7867 while ((victim = engfunc(EngFunc_FindEntityInSphere, victim, originF, NADE_EXPLOSION_RADIUS)) != 0)
7868 {
7869 // Only effect alive non-spawnprotected humans
7870 if (!is_user_valid_alive(victim) || g_zombie[victim] || g_nodamage[victim])
7871 continue;
7872
7873 // Last human is killed
7874 if (fnGetHumans() == 1)
7875 {
7876 ExecuteHamB(Ham_Killed, victim, attacker, 0)
7877 continue;
7878 }
7879
7880 // Infected victim's sound
7881 ArrayGetString(grenade_infect_player, random_num(0, ArraySize(grenade_infect_player) - 1), sound, charsmax(sound))
7882 emit_sound(victim, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
7883
7884 // Turn into zombie
7885 zombieme(victim, attacker, 0, 1, 1)
7886 }
7887
7888 // Get rid of the grenade
7889 engfunc(EngFunc_RemoveEntity, ent)
7890}
7891
7892// Fire Grenade Explosion
7893fire_explode(ent)
7894{
7895 // Get origin
7896 static Float:originF[3]
7897 pev(ent, pev_origin, originF)
7898
7899 // Make the explosion
7900 create_blast2(originF)
7901
7902 // Fire nade explode sound
7903 static sound[64]
7904 ArrayGetString(grenade_fire, random_num(0, ArraySize(grenade_fire) - 1), sound, charsmax(sound))
7905 emit_sound(ent, CHAN_WEAPON, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
7906
7907 // Collisions
7908 static victim
7909 victim = -1
7910
7911 while ((victim = engfunc(EngFunc_FindEntityInSphere, victim, originF, NADE_EXPLOSION_RADIUS)) != 0)
7912 {
7913 // Only effect alive zombies
7914 if (!is_user_valid_alive(victim) || !g_zombie[victim] || g_nodamage[victim])
7915 continue;
7916
7917 // Heat icon?
7918 if (get_pcvar_num(cvar_hudicons))
7919 {
7920 message_begin(MSG_ONE_UNRELIABLE, g_msgDamage, _, victim)
7921 write_byte(0) // damage save
7922 write_byte(0) // damage take
7923 write_long(DMG_BURN) // damage type
7924 write_coord(0) // x
7925 write_coord(0) // y
7926 write_coord(0) // z
7927 message_end()
7928 }
7929
7930 if (g_nemesis[victim]) // fire duration (nemesis is fire resistant)
7931 g_burning_duration[victim] += get_pcvar_num(cvar_fireduration)
7932 else
7933 g_burning_duration[victim] += get_pcvar_num(cvar_fireduration) * 5
7934
7935 // Set burning task on victim if not present
7936 if (!task_exists(victim+TASK_BURN))
7937 set_task(0.2, "burning_flame", victim+TASK_BURN, _, _, "b")
7938 }
7939
7940 // Get rid of the grenade
7941 engfunc(EngFunc_RemoveEntity, ent)
7942}
7943
7944// Frost Grenade Explosion
7945frost_explode(ent)
7946{
7947 // Get origin
7948 static Float:originF[3]
7949 pev(ent, pev_origin, originF)
7950
7951 // Make the explosion
7952 create_blast3(originF)
7953
7954 // Frost nade explode sound
7955 static sound[64]
7956 ArrayGetString(grenade_frost, random_num(0, ArraySize(grenade_frost) - 1), sound, charsmax(sound))
7957 emit_sound(ent, CHAN_WEAPON, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
7958
7959 // Collisions
7960 static victim
7961 victim = -1
7962
7963 while ((victim = engfunc(EngFunc_FindEntityInSphere, victim, originF, NADE_EXPLOSION_RADIUS)) != 0)
7964 {
7965 // Only effect alive unfrozen zombies
7966 if (!is_user_valid_alive(victim) || !g_zombie[victim] || g_frozen[victim] || g_nodamage[victim])
7967 continue;
7968
7969 // Nemesis shouldn't be frozen
7970 if (g_nemesis[victim])
7971 {
7972 // Get player's origin
7973 static origin2[3]
7974 get_user_origin(victim, origin2)
7975
7976 // Broken glass sound
7977 ArrayGetString(grenade_frost_break, random_num(0, ArraySize(grenade_frost_break) - 1), sound, charsmax(sound))
7978 emit_sound(victim, CHAN_BODY, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
7979
7980 // Glass shatter
7981 message_begin(MSG_PVS, SVC_TEMPENTITY, origin2)
7982 write_byte(TE_BREAKMODEL) // TE id
7983 write_coord(origin2[0]) // x
7984 write_coord(origin2[1]) // y
7985 write_coord(origin2[2]+24) // z
7986 write_coord(16) // size x
7987 write_coord(16) // size y
7988 write_coord(16) // size z
7989 write_coord(random_num(-50, 50)) // velocity x
7990 write_coord(random_num(-50, 50)) // velocity y
7991 write_coord(25) // velocity z
7992 write_byte(10) // random velocity
7993 write_short(g_glassSpr) // model
7994 write_byte(10) // count
7995 write_byte(25) // life
7996 write_byte(BREAK_GLASS) // flags
7997 message_end()
7998
7999 continue;
8000 }
8001
8002 // Freeze icon?
8003 if (get_pcvar_num(cvar_hudicons))
8004 {
8005 message_begin(MSG_ONE_UNRELIABLE, g_msgDamage, _, victim)
8006 write_byte(0) // damage save
8007 write_byte(0) // damage take
8008 write_long(DMG_DROWN) // damage type - DMG_FREEZE
8009 write_coord(0) // x
8010 write_coord(0) // y
8011 write_coord(0) // z
8012 message_end()
8013 }
8014
8015 // Light blue glow while frozen
8016 if (g_handle_models_on_separate_ent)
8017 fm_set_rendering(g_ent_playermodel[victim], kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 25)
8018 else
8019 fm_set_rendering(victim, kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 25)
8020
8021 // Freeze sound
8022 ArrayGetString(grenade_frost_player, random_num(0, ArraySize(grenade_frost_player) - 1), sound, charsmax(sound))
8023 emit_sound(victim, CHAN_BODY, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
8024
8025 // Add a blue tint to their screen
8026 message_begin(MSG_ONE, g_msgScreenFade, _, victim)
8027 write_short(0) // duration
8028 write_short(0) // hold time
8029 write_short(FFADE_STAYOUT) // fade type
8030 write_byte(0) // red
8031 write_byte(50) // green
8032 write_byte(200) // blue
8033 write_byte(100) // alpha
8034 message_end()
8035
8036 // Prevent from jumping
8037 if (pev(victim, pev_flags) & FL_ONGROUND)
8038 set_pev(victim, pev_gravity, 999999.9) // set really high
8039 else
8040 set_pev(victim, pev_gravity, 0.000001) // no gravity
8041
8042 // Set a task to remove the freeze
8043 g_frozen[victim] = true;
8044 set_task(get_pcvar_float(cvar_freezeduration), "remove_freeze", victim)
8045 }
8046
8047 // Get rid of the grenade
8048 engfunc(EngFunc_RemoveEntity, ent)
8049}
8050
8051// Remove freeze task
8052public remove_freeze(id)
8053{
8054 // Not alive or not frozen anymore
8055 if (!g_isalive[id] || !g_frozen[id])
8056 return;
8057
8058 // Unfreeze
8059 g_frozen[id] = false;
8060
8061 // Restore gravity
8062 if (g_zombie[id])
8063 {
8064 if (g_nemesis[id])
8065 set_pev(id, pev_gravity, get_pcvar_float(cvar_nemgravity))
8066 else
8067 set_pev(id, pev_gravity, Float:ArrayGetCell(g_zclass_grav, g_zombieclass[id]))
8068 }
8069 else
8070 {
8071 if (g_survivor[id])
8072 set_pev(id, pev_gravity, get_pcvar_float(cvar_survgravity))
8073 else
8074 set_pev(id, pev_gravity, get_pcvar_float(cvar_humangravity))
8075 }
8076
8077 // Restore rendering
8078 if (g_handle_models_on_separate_ent)
8079 {
8080 // Nemesis or Survivor glow / remove glow on player model entity
8081 if (g_nemesis[id] && get_pcvar_num(cvar_nemglow))
8082 fm_set_rendering(g_ent_playermodel[id], kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 25)
8083 else if (g_survivor[id] && get_pcvar_num(cvar_survglow))
8084 fm_set_rendering(g_ent_playermodel[id], kRenderFxGlowShell, 0, 0, 255, kRenderNormal, 25)
8085 else
8086 fm_set_rendering(g_ent_playermodel[id])
8087 }
8088 else
8089 {
8090 // Nemesis or Survivor glow / remove glow
8091 if (g_nemesis[id] && get_pcvar_num(cvar_nemglow))
8092 fm_set_rendering(id, kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 25)
8093 else if (g_survivor[id] && get_pcvar_num(cvar_survglow))
8094 fm_set_rendering(id, kRenderFxGlowShell, 0, 0, 255, kRenderNormal, 25)
8095 else
8096 fm_set_rendering(id)
8097 }
8098
8099 // Gradually remove screen's blue tint
8100 message_begin(MSG_ONE, g_msgScreenFade, _, id)
8101 write_short(UNIT_SECOND) // duration
8102 write_short(0) // hold time
8103 write_short(FFADE_IN) // fade type
8104 write_byte(0) // red
8105 write_byte(50) // green
8106 write_byte(200) // blue
8107 write_byte(100) // alpha
8108 message_end()
8109
8110 // Broken glass sound
8111 static sound[64]
8112 ArrayGetString(grenade_frost_break, random_num(0, ArraySize(grenade_frost_break) - 1), sound, charsmax(sound))
8113 emit_sound(id, CHAN_BODY, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
8114
8115 // Get player's origin
8116 static origin2[3]
8117 get_user_origin(id, origin2)
8118
8119 // Glass shatter
8120 message_begin(MSG_PVS, SVC_TEMPENTITY, origin2)
8121 write_byte(TE_BREAKMODEL) // TE id
8122 write_coord(origin2[0]) // x
8123 write_coord(origin2[1]) // y
8124 write_coord(origin2[2]+24) // z
8125 write_coord(16) // size x
8126 write_coord(16) // size y
8127 write_coord(16) // size z
8128 write_coord(random_num(-50, 50)) // velocity x
8129 write_coord(random_num(-50, 50)) // velocity y
8130 write_coord(25) // velocity z
8131 write_byte(10) // random velocity
8132 write_short(g_glassSpr) // model
8133 write_byte(10) // count
8134 write_byte(25) // life
8135 write_byte(BREAK_GLASS) // flags
8136 message_end()
8137
8138 ExecuteForward(g_fwUserUnfrozen, g_fwDummyResult, id);
8139}
8140
8141// Remove Stuff Task
8142public remove_stuff()
8143{
8144 static ent
8145
8146 // Remove rotating doors
8147 if (get_pcvar_num(cvar_removedoors) > 0)
8148 {
8149 ent = -1;
8150 while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", "func_door_rotating")) != 0)
8151 engfunc(EngFunc_SetOrigin, ent, Float:{8192.0 ,8192.0 ,8192.0})
8152 }
8153
8154 // Remove all doors
8155 if (get_pcvar_num(cvar_removedoors) > 1)
8156 {
8157 ent = -1;
8158 while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", "func_door")) != 0)
8159 engfunc(EngFunc_SetOrigin, ent, Float:{8192.0 ,8192.0 ,8192.0})
8160 }
8161
8162 // Triggered lights
8163 if (!get_pcvar_num(cvar_triggered))
8164 {
8165 ent = -1
8166 while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", "light")) != 0)
8167 {
8168 dllfunc(DLLFunc_Use, ent, 0); // turn off the light
8169 set_pev(ent, pev_targetname, 0) // prevent it from being triggered
8170 }
8171 }
8172}
8173
8174// Set Custom Weapon Models
8175replace_weapon_models(id, weaponid)
8176{
8177 switch (weaponid)
8178 {
8179 case CSW_KNIFE: // Custom knife models
8180 {
8181 if (g_zombie[id])
8182 {
8183 if (g_nemesis[id]) // Nemesis
8184 {
8185 set_pev(id, pev_viewmodel2, model_vknife_nemesis)
8186 set_pev(id, pev_weaponmodel2, "")
8187 }
8188 else // Zombies
8189 {
8190 // Admin knife models?
8191 if (get_pcvar_num(cvar_adminknifemodelszombie) && get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS])
8192 {
8193 set_pev(id, pev_viewmodel2, model_vknife_admin_zombie)
8194 set_pev(id, pev_weaponmodel2, "")
8195 }
8196 else
8197 {
8198 static clawmodel[100]
8199 ArrayGetString(g_zclass_clawmodel, g_zombieclass[id], clawmodel, charsmax(clawmodel))
8200 format(clawmodel, charsmax(clawmodel), "models/zombie_plague/%s", clawmodel)
8201 set_pev(id, pev_viewmodel2, clawmodel)
8202 set_pev(id, pev_weaponmodel2, "")
8203 }
8204 }
8205 }
8206 else // Humans
8207 {
8208 // Admin knife models?
8209 if (get_pcvar_num(cvar_adminknifemodelshuman) && get_user_flags(id) & g_access_flag[ACCESS_ADMIN_MODELS])
8210 {
8211 set_pev(id, pev_viewmodel2, model_vknife_admin_human)
8212 set_pev(id, pev_weaponmodel2, "")
8213 }
8214 else
8215 {
8216 set_pev(id, pev_viewmodel2, model_vknife_human)
8217 set_pev(id, pev_weaponmodel2, "models/p_knife.mdl")
8218 }
8219 }
8220 }
8221 case CSW_M249: // Survivor's M249
8222 {
8223 if (g_survivor[id])
8224 set_pev(id, pev_viewmodel2, model_vm249_survivor)
8225 }
8226 case CSW_HEGRENADE: // Infection bomb or fire grenade
8227 {
8228 if (g_zombie[id])
8229 set_pev(id, pev_viewmodel2, model_grenade_infect)
8230 else
8231 set_pev(id, pev_viewmodel2, model_grenade_fire)
8232 }
8233 case CSW_FLASHBANG: // Frost grenade
8234 {
8235 set_pev(id, pev_viewmodel2, model_grenade_frost)
8236 }
8237 case CSW_SMOKEGRENADE: // Flare grenade
8238 {
8239 set_pev(id, pev_viewmodel2, model_grenade_flare)
8240 }
8241 }
8242
8243 // Update model on weaponmodel ent
8244 if (g_handle_models_on_separate_ent) fm_set_weaponmodel_ent(id)
8245}
8246
8247// Reset Player Vars
8248reset_vars(id, resetall)
8249{
8250 g_zombie[id] = false
8251 g_nemesis[id] = false
8252 g_survivor[id] = false
8253 g_firstzombie[id] = false
8254 g_lastzombie[id] = false
8255 g_lasthuman[id] = false
8256 g_frozen[id] = false
8257 g_nodamage[id] = false
8258 g_respawn_as_zombie[id] = false
8259 g_nvision[id] = false
8260 g_nvisionenabled[id] = false
8261 g_flashlight[id] = false
8262 g_flashbattery[id] = 100
8263 g_canbuy[id] = true
8264 g_burning_duration[id] = 0
8265
8266 if (resetall)
8267 {
8268 g_ammopacks[id] = get_pcvar_num(cvar_startammopacks)
8269 g_zombieclass[id] = ZCLASS_NONE
8270 g_zombieclassnext[id] = ZCLASS_NONE
8271 g_damagedealt[id] = 0
8272 WPN_AUTO_ON = 0
8273 }
8274}
8275
8276// Set spectators nightvision
8277public spec_nvision(id)
8278{
8279 // Not connected, alive, or bot
8280 if (!g_isconnected[id] || g_isalive[id] || g_isbot[id])
8281 return;
8282
8283 // Give Night Vision?
8284 if (get_pcvar_num(cvar_nvggive))
8285 {
8286 g_nvision[id] = true
8287
8288 // Turn on Night Vision automatically?
8289 if (get_pcvar_num(cvar_nvggive) == 1)
8290 {
8291 g_nvisionenabled[id] = true
8292
8293 // Custom nvg?
8294 if (get_pcvar_num(cvar_customnvg))
8295 {
8296 remove_task(id+TASK_NVISION)
8297 set_task(0.1, "set_user_nvision", id+TASK_NVISION, _, _, "b")
8298 }
8299 else
8300 set_user_gnvision(id, 1)
8301 }
8302 }
8303}
8304
8305// Show HUD Task
8306public ShowHUD(taskid)
8307{
8308 static id
8309 id = ID_SHOWHUD;
8310
8311 // Player died?
8312 if (!g_isalive[id])
8313 {
8314 // Get spectating target
8315 id = pev(id, PEV_SPEC_TARGET)
8316
8317 // Target not alive
8318 if (!g_isalive[id]) return;
8319 }
8320
8321 // Format classname
8322 static class[32], red, green, blue
8323
8324 if (g_zombie[id]) // zombies
8325 {
8326 red = 200
8327 green = 250
8328 blue = 0
8329
8330 if (g_nemesis[id])
8331 formatex(class, charsmax(class), "%L", ID_SHOWHUD, "CLASS_NEMESIS")
8332 else
8333 copy(class, charsmax(class), g_zombie_classname[id])
8334 }
8335 else // humans
8336 {
8337 red = 0
8338 green = 0
8339 blue = 255
8340
8341 if (g_survivor[id])
8342 formatex(class, charsmax(class), "%L", ID_SHOWHUD, "CLASS_SURVIVOR")
8343 else
8344 formatex(class, charsmax(class), "%L", ID_SHOWHUD, "CLASS_HUMAN")
8345 }
8346
8347 // Spectating someone else?
8348 if (id != ID_SHOWHUD)
8349 {
8350 // Show name, health, class, and ammo packs
8351 set_hudmessage(255, 255, 255, HUD_SPECT_X, HUD_SPECT_Y, 0, 6.0, 1.1, 0.0, 0.0, -1)
8352 ShowSyncHudMsg(ID_SHOWHUD, g_MsgSync2, "%L %s^nHP: %d - %L %s - %L %d", ID_SHOWHUD, "SPECTATING", g_playername[id], pev(id, pev_health), ID_SHOWHUD, "CLASS_CLASS", class, ID_SHOWHUD, "AMMO_PACKS1", g_ammopacks[id])
8353 }
8354 else
8355 {
8356 // Show health, class and ammo packs
8357 set_hudmessage(red, green, blue, HUD_STATS_X, HUD_STATS_Y, 0, 6.0, 1.1, 0.0, 0.0, -1)
8358 ShowSyncHudMsg(ID_SHOWHUD, g_MsgSync2, "%L: %d - %L %s - %L %d", id, "ZOMBIE_ATTRIB1", pev(ID_SHOWHUD, pev_health), ID_SHOWHUD, "CLASS_CLASS", class, ID_SHOWHUD, "AMMO_PACKS1", g_ammopacks[ID_SHOWHUD])
8359 }
8360}
8361
8362// Play idle zombie sounds
8363public zombie_play_idle(taskid)
8364{
8365 // Round ended/new one starting
8366 if (g_endround || g_newround)
8367 return;
8368
8369 static sound[64]
8370
8371 // Last zombie?
8372 if (g_lastzombie[ID_BLOOD])
8373 {
8374 ArrayGetString(zombie_idle_last, random_num(0, ArraySize(zombie_idle_last) - 1), sound, charsmax(sound))
8375 emit_sound(ID_BLOOD, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
8376 }
8377 else
8378 {
8379 ArrayGetString(zombie_idle, random_num(0, ArraySize(zombie_idle) - 1), sound, charsmax(sound))
8380 emit_sound(ID_BLOOD, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
8381 }
8382}
8383
8384// Madness Over Task
8385public madness_over(taskid)
8386{
8387 g_nodamage[ID_BLOOD] = false
8388}
8389
8390// Place user at a random spawn
8391do_random_spawn(id, regularspawns = 0)
8392{
8393 static hull, sp_index, i
8394
8395 // Get whether the player is crouching
8396 hull = (pev(id, pev_flags) & FL_DUCKING) ? HULL_HEAD : HULL_HUMAN
8397
8398 // Use regular spawns?
8399 if (!regularspawns)
8400 {
8401 // No spawns?
8402 if (!g_spawnCount)
8403 return;
8404
8405 // Choose random spawn to start looping at
8406 sp_index = random_num(0, g_spawnCount - 1)
8407
8408 // Try to find a clear spawn
8409 for (i = sp_index + 1; /*no condition*/; i++)
8410 {
8411 // Start over when we reach the end
8412 if (i >= g_spawnCount) i = 0
8413
8414 // Free spawn space?
8415 if (is_hull_vacant(g_spawns[i], hull))
8416 {
8417 // Engfunc_SetOrigin is used so ent's mins and maxs get updated instantly
8418 engfunc(EngFunc_SetOrigin, id, g_spawns[i])
8419 break;
8420 }
8421
8422 // Loop completed, no free space found
8423 if (i == sp_index) break;
8424 }
8425 }
8426 else
8427 {
8428 // No spawns?
8429 if (!g_spawnCount2)
8430 return;
8431
8432 // Choose random spawn to start looping at
8433 sp_index = random_num(0, g_spawnCount2 - 1)
8434
8435 // Try to find a clear spawn
8436 for (i = sp_index + 1; /*no condition*/; i++)
8437 {
8438 // Start over when we reach the end
8439 if (i >= g_spawnCount2) i = 0
8440
8441 // Free spawn space?
8442 if (is_hull_vacant(g_spawns2[i], hull))
8443 {
8444 // Engfunc_SetOrigin is used so ent's mins and maxs get updated instantly
8445 engfunc(EngFunc_SetOrigin, id, g_spawns2[i])
8446 break;
8447 }
8448
8449 // Loop completed, no free space found
8450 if (i == sp_index) break;
8451 }
8452 }
8453}
8454
8455// Get Zombies -returns alive zombies number-
8456fnGetZombies()
8457{
8458 static iZombies, id
8459 iZombies = 0
8460
8461 for (id = 1; id <= g_maxplayers; id++)
8462 {
8463 if (g_isalive[id] && g_zombie[id])
8464 iZombies++
8465 }
8466
8467 return iZombies;
8468}
8469
8470// Get Humans -returns alive humans number-
8471fnGetHumans()
8472{
8473 static iHumans, id
8474 iHumans = 0
8475
8476 for (id = 1; id <= g_maxplayers; id++)
8477 {
8478 if (g_isalive[id] && !g_zombie[id])
8479 iHumans++
8480 }
8481
8482 return iHumans;
8483}
8484
8485// Get Nemesis -returns alive nemesis number-
8486fnGetNemesis()
8487{
8488 static iNemesis, id
8489 iNemesis = 0
8490
8491 for (id = 1; id <= g_maxplayers; id++)
8492 {
8493 if (g_isalive[id] && g_nemesis[id])
8494 iNemesis++
8495 }
8496
8497 return iNemesis;
8498}
8499
8500// Get Survivors -returns alive survivors number-
8501fnGetSurvivors()
8502{
8503 static iSurvivors, id
8504 iSurvivors = 0
8505
8506 for (id = 1; id <= g_maxplayers; id++)
8507 {
8508 if (g_isalive[id] && g_survivor[id])
8509 iSurvivors++
8510 }
8511
8512 return iSurvivors;
8513}
8514
8515// Get Alive -returns alive players number-
8516fnGetAlive()
8517{
8518 static iAlive, id
8519 iAlive = 0
8520
8521 for (id = 1; id <= g_maxplayers; id++)
8522 {
8523 if (g_isalive[id])
8524 iAlive++
8525 }
8526
8527 return iAlive;
8528}
8529
8530// Get Random Alive -returns index of alive player number n -
8531fnGetRandomAlive(n)
8532{
8533 static iAlive, id
8534 iAlive = 0
8535
8536 for (id = 1; id <= g_maxplayers; id++)
8537 {
8538 if (g_isalive[id])
8539 iAlive++
8540
8541 if (iAlive == n)
8542 return id;
8543 }
8544
8545 return -1;
8546}
8547
8548// Get Playing -returns number of users playing-
8549fnGetPlaying()
8550{
8551 static iPlaying, id, team
8552 iPlaying = 0
8553
8554 for (id = 1; id <= g_maxplayers; id++)
8555 {
8556 if (g_isconnected[id])
8557 {
8558 team = fm_cs_get_user_team(id)
8559
8560 if (team != FM_CS_TEAM_SPECTATOR && team != FM_CS_TEAM_UNASSIGNED)
8561 iPlaying++
8562 }
8563 }
8564
8565 return iPlaying;
8566}
8567
8568// Get CTs -returns number of CTs connected-
8569fnGetCTs()
8570{
8571 static iCTs, id
8572 iCTs = 0
8573
8574 for (id = 1; id <= g_maxplayers; id++)
8575 {
8576 if (g_isconnected[id])
8577 {
8578 if (fm_cs_get_user_team(id) == FM_CS_TEAM_CT)
8579 iCTs++
8580 }
8581 }
8582
8583 return iCTs;
8584}
8585
8586// Get Ts -returns number of Ts connected-
8587fnGetTs()
8588{
8589 static iTs, id
8590 iTs = 0
8591
8592 for (id = 1; id <= g_maxplayers; id++)
8593 {
8594 if (g_isconnected[id])
8595 {
8596 if (fm_cs_get_user_team(id) == FM_CS_TEAM_T)
8597 iTs++
8598 }
8599 }
8600
8601 return iTs;
8602}
8603
8604// Get Alive CTs -returns number of CTs alive-
8605fnGetAliveCTs()
8606{
8607 static iCTs, id
8608 iCTs = 0
8609
8610 for (id = 1; id <= g_maxplayers; id++)
8611 {
8612 if (g_isalive[id])
8613 {
8614 if (fm_cs_get_user_team(id) == FM_CS_TEAM_CT)
8615 iCTs++
8616 }
8617 }
8618
8619 return iCTs;
8620}
8621
8622// Get Alive Ts -returns number of Ts alive-
8623fnGetAliveTs()
8624{
8625 static iTs, id
8626 iTs = 0
8627
8628 for (id = 1; id <= g_maxplayers; id++)
8629 {
8630 if (g_isalive[id])
8631 {
8632 if (fm_cs_get_user_team(id) == FM_CS_TEAM_T)
8633 iTs++
8634 }
8635 }
8636
8637 return iTs;
8638}
8639
8640// Last Zombie Check -check for last zombie and set its flag-
8641fnCheckLastZombie()
8642{
8643 static id
8644 for (id = 1; id <= g_maxplayers; id++)
8645 {
8646 // Last zombie
8647 if (g_isalive[id] && g_zombie[id] && !g_nemesis[id] && fnGetZombies() == 1)
8648 {
8649 if (!g_lastzombie[id])
8650 {
8651 // Last zombie forward
8652 ExecuteForward(g_fwUserLastZombie, g_fwDummyResult, id);
8653 }
8654 g_lastzombie[id] = true
8655 }
8656 else
8657 g_lastzombie[id] = false
8658
8659 // Last human
8660 if (g_isalive[id] && !g_zombie[id] && !g_survivor[id] && fnGetHumans() == 1)
8661 {
8662 if (!g_lasthuman[id])
8663 {
8664 // Last human forward
8665 ExecuteForward(g_fwUserLastHuman, g_fwDummyResult, id);
8666
8667 // Reward extra hp
8668 fm_set_user_health(id, pev(id, pev_health) + get_pcvar_num(cvar_humanlasthp))
8669 }
8670 g_lasthuman[id] = true
8671 }
8672 else
8673 g_lasthuman[id] = false
8674 }
8675}
8676
8677// Save player's stats to database
8678save_stats(id)
8679{
8680 // Check whether there is another record already in that slot
8681 if (db_name[id][0] && !equal(g_playername[id], db_name[id]))
8682 {
8683 // If DB size is exceeded, write over old records
8684 if (db_slot_i >= sizeof db_name)
8685 db_slot_i = g_maxplayers+1
8686
8687 // Move previous record onto an additional save slot
8688 copy(db_name[db_slot_i], charsmax(db_name[]), db_name[id])
8689 db_ammopacks[db_slot_i] = db_ammopacks[id]
8690 db_zombieclass[db_slot_i] = db_zombieclass[id]
8691 db_slot_i++
8692 }
8693
8694 // Now save the current player stats
8695 copy(db_name[id], charsmax(db_name[]), g_playername[id]) // name
8696 db_ammopacks[id] = g_ammopacks[id] // ammo packs
8697 db_zombieclass[id] = g_zombieclassnext[id] // zombie class
8698}
8699
8700// Load player's stats from database (if a record is found)
8701load_stats(id)
8702{
8703 // Look for a matching record
8704 static i
8705 for (i = 0; i < sizeof db_name; i++)
8706 {
8707 if (equal(g_playername[id], db_name[i]))
8708 {
8709 // Bingo!
8710 g_ammopacks[id] = db_ammopacks[i]
8711 g_zombieclass[id] = db_zombieclass[i]
8712 g_zombieclassnext[id] = db_zombieclass[i]
8713 return;
8714 }
8715 }
8716}
8717
8718// Checks if a player is allowed to be zombie
8719allowed_zombie(id)
8720{
8721 if ((g_zombie[id] && !g_nemesis[id]) || g_endround || !g_isalive[id] || task_exists(TASK_WELCOMEMSG) || (!g_newround && !g_zombie[id] && fnGetHumans() == 1))
8722 return false;
8723
8724 return true;
8725}
8726
8727// Checks if a player is allowed to be human
8728allowed_human(id)
8729{
8730 if ((!g_zombie[id] && !g_survivor[id]) || g_endround || !g_isalive[id] || task_exists(TASK_WELCOMEMSG) || (!g_newround && g_zombie[id] && fnGetZombies() == 1))
8731 return false;
8732
8733 return true;
8734}
8735
8736// Checks if a player is allowed to be survivor
8737allowed_survivor(id)
8738{
8739 if (g_endround || g_survivor[id] || !g_isalive[id] || task_exists(TASK_WELCOMEMSG) || (!g_newround && g_zombie[id] && fnGetZombies() == 1))
8740 return false;
8741
8742 return true;
8743}
8744
8745// Checks if a player is allowed to be nemesis
8746allowed_nemesis(id)
8747{
8748 if (g_endround || g_nemesis[id] || !g_isalive[id] || task_exists(TASK_WELCOMEMSG) || (!g_newround && !g_zombie[id] && fnGetHumans() == 1))
8749 return false;
8750
8751 return true;
8752}
8753
8754// Checks if a player is allowed to respawn
8755allowed_respawn(id)
8756{
8757 static team
8758 team = fm_cs_get_user_team(id)
8759
8760 if (g_endround || team == FM_CS_TEAM_SPECTATOR || team == FM_CS_TEAM_UNASSIGNED || g_isalive[id])
8761 return false;
8762
8763 return true;
8764}
8765
8766// Checks if swarm mode is allowed
8767allowed_swarm()
8768{
8769 if (g_endround || !g_newround || task_exists(TASK_WELCOMEMSG))
8770 return false;
8771
8772 return true;
8773}
8774
8775// Checks if multi infection mode is allowed
8776allowed_multi()
8777{
8778 if (g_endround || !g_newround || task_exists(TASK_WELCOMEMSG) || floatround(fnGetAlive()*get_pcvar_float(cvar_multiratio), floatround_ceil) < 2 || floatround(fnGetAlive()*get_pcvar_float(cvar_multiratio), floatround_ceil) >= fnGetAlive())
8779 return false;
8780
8781 return true;
8782}
8783
8784// Checks if plague mode is allowed
8785allowed_plague()
8786{
8787 if (g_endround || !g_newround || task_exists(TASK_WELCOMEMSG) || floatround((fnGetAlive()-(get_pcvar_num(cvar_plaguenemnum)+get_pcvar_num(cvar_plaguesurvnum)))*get_pcvar_float(cvar_plagueratio), floatround_ceil) < 1
8788 || fnGetAlive()-(get_pcvar_num(cvar_plaguesurvnum)+get_pcvar_num(cvar_plaguenemnum)+floatround((fnGetAlive()-(get_pcvar_num(cvar_plaguenemnum)+get_pcvar_num(cvar_plaguesurvnum)))*get_pcvar_float(cvar_plagueratio), floatround_ceil)) < 1)
8789 return false;
8790
8791 return true;
8792}
8793
8794// Admin Command. zp_zombie
8795command_zombie(id, player)
8796{
8797 // Show activity?
8798 switch (get_pcvar_num(cvar_showactivity))
8799 {
8800 case 1: client_print(0, print_chat, "ADMIN - %s %L", g_playername[player], LANG_PLAYER, "CMD_INFECT")
8801 case 2: client_print(0, print_chat, "ADMIN %s - %s %L", g_playername[id], g_playername[player], LANG_PLAYER, "CMD_INFECT")
8802 }
8803
8804 // Log to Zombie Plague log file?
8805 if (get_pcvar_num(cvar_logcommands))
8806 {
8807 static logdata[100], authid[32], ip[16]
8808 get_user_authid(id, authid, charsmax(authid))
8809 get_user_ip(id, ip, charsmax(ip), 1)
8810 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %s %L (Players: %d/%d)", g_playername[id], authid, ip, g_playername[player], LANG_SERVER, "CMD_INFECT", fnGetPlaying(), g_maxplayers)
8811 log_to_file("zombieplague.log", logdata)
8812 }
8813
8814 // New round?
8815 if (g_newround)
8816 {
8817 // Set as first zombie
8818 remove_task(TASK_MAKEZOMBIE)
8819 make_a_zombie(MODE_INFECTION, player)
8820 }
8821 else
8822 {
8823 // Just infect
8824 zombieme(player, 0, 0, 0, 0)
8825 }
8826}
8827
8828// Admin Command. zp_human
8829command_human(id, player)
8830{
8831 // Show activity?
8832 switch (get_pcvar_num(cvar_showactivity))
8833 {
8834 case 1: client_print(0, print_chat, "ADMIN - %s %L", g_playername[player], LANG_PLAYER, "CMD_DISINFECT")
8835 case 2: client_print(0, print_chat, "ADMIN %s - %s %L", g_playername[id], g_playername[player], LANG_PLAYER, "CMD_DISINFECT")
8836 }
8837
8838 // Log to Zombie Plague log file?
8839 if (get_pcvar_num(cvar_logcommands))
8840 {
8841 static logdata[100], authid[32], ip[16]
8842 get_user_authid(id, authid, charsmax(authid))
8843 get_user_ip(id, ip, charsmax(ip), 1)
8844 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %s %L (Players: %d/%d)", g_playername[id], authid, ip, g_playername[player], LANG_SERVER,"CMD_DISINFECT", fnGetPlaying(), g_maxplayers)
8845 log_to_file("zombieplague.log", logdata)
8846 }
8847
8848 // Turn to human
8849 humanme(player, 0, 0)
8850}
8851
8852// Admin Command. zp_survivor
8853command_survivor(id, player)
8854{
8855 // Show activity?
8856 switch (get_pcvar_num(cvar_showactivity))
8857 {
8858 case 1: client_print(0, print_chat, "ADMIN - %s %L", g_playername[player], LANG_PLAYER, "CMD_SURVIVAL")
8859 case 2: client_print(0, print_chat, "ADMIN %s - %s %L", g_playername[id], g_playername[player], LANG_PLAYER, "CMD_SURVIVAL")
8860 }
8861
8862 // Log to Zombie Plague log file?
8863 if (get_pcvar_num(cvar_logcommands))
8864 {
8865 static logdata[100], authid[32], ip[16]
8866 get_user_authid(id, authid, charsmax(authid))
8867 get_user_ip(id, ip, charsmax(ip), 1)
8868 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %s %L (Players: %d/%d)", g_playername[id], authid, ip, g_playername[player], LANG_SERVER,"CMD_SURVIVAL", fnGetPlaying(), g_maxplayers)
8869 log_to_file("zombieplague.log", logdata)
8870 }
8871
8872 // New round?
8873 if (g_newround)
8874 {
8875 // Set as first survivor
8876 remove_task(TASK_MAKEZOMBIE)
8877 make_a_zombie(MODE_SURVIVOR, player)
8878 }
8879 else
8880 {
8881 // Turn player into a Survivor
8882 humanme(player, 1, 0)
8883 }
8884}
8885
8886// Admin Command. zp_nemesis
8887command_nemesis(id, player)
8888{
8889 // Show activity?
8890 switch (get_pcvar_num(cvar_showactivity))
8891 {
8892 case 1: client_print(0, print_chat, "ADMIN - %s %L", g_playername[player], LANG_PLAYER, "CMD_NEMESIS")
8893 case 2: client_print(0, print_chat, "ADMIN %s - %s %L", g_playername[id], g_playername[player], LANG_PLAYER, "CMD_NEMESIS")
8894 }
8895
8896 // Log to Zombie Plague log file?
8897 if (get_pcvar_num(cvar_logcommands))
8898 {
8899 static logdata[100], authid[32], ip[16]
8900 get_user_authid(id, authid, charsmax(authid))
8901 get_user_ip(id, ip, charsmax(ip), 1)
8902 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %s %L (Players: %d/%d)", g_playername[id], authid, ip, g_playername[player], LANG_SERVER,"CMD_NEMESIS", fnGetPlaying(), g_maxplayers)
8903 log_to_file("zombieplague.log", logdata)
8904 }
8905
8906 // New round?
8907 if (g_newround)
8908 {
8909 // Set as first nemesis
8910 remove_task(TASK_MAKEZOMBIE)
8911 make_a_zombie(MODE_NEMESIS, player)
8912 }
8913 else
8914 {
8915 // Turn player into a Nemesis
8916 zombieme(player, 0, 1, 0, 0)
8917 }
8918}
8919
8920// Admin Command. zp_respawn
8921command_respawn(id, player)
8922{
8923 // Show activity?
8924 switch (get_pcvar_num(cvar_showactivity))
8925 {
8926 case 1: client_print(0, print_chat, "ADMIN - %s %L", g_playername[player], LANG_PLAYER, "CMD_RESPAWN")
8927 case 2: client_print(0, print_chat, "ADMIN %s - %s %L", g_playername[id], g_playername[player], LANG_PLAYER, "CMD_RESPAWN")
8928 }
8929
8930 // Log to Zombie Plague log file?
8931 if (get_pcvar_num(cvar_logcommands))
8932 {
8933 static logdata[100], authid[32], ip[16]
8934 get_user_authid(id, authid, charsmax(authid))
8935 get_user_ip(id, ip, charsmax(ip), 1)
8936 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %s %L (Players: %d/%d)", g_playername[id], authid, ip, g_playername[player], LANG_SERVER, "CMD_RESPAWN", fnGetPlaying(), g_maxplayers)
8937 log_to_file("zombieplague.log", logdata)
8938 }
8939
8940 // Respawn as zombie?
8941 if (get_pcvar_num(cvar_deathmatch) == 2 || (get_pcvar_num(cvar_deathmatch) == 3 && random_num(0, 1)) || (get_pcvar_num(cvar_deathmatch) == 4 && fnGetZombies() < fnGetAlive()/2))
8942 g_respawn_as_zombie[player] = true
8943
8944 // Override respawn as zombie setting on nemesis and survivor rounds
8945 if (g_survround) g_respawn_as_zombie[player] = true
8946 else if (g_nemround) g_respawn_as_zombie[player] = false
8947
8948 respawn_player_manually(player);
8949}
8950
8951// Admin Command. zp_swarm
8952command_swarm(id)
8953{
8954 // Show activity?
8955 switch (get_pcvar_num(cvar_showactivity))
8956 {
8957 case 1: client_print(0, print_chat, "ADMIN - %L", LANG_PLAYER, "CMD_SWARM")
8958 case 2: client_print(0, print_chat, "ADMIN %s - %L", g_playername[id], LANG_PLAYER, "CMD_SWARM")
8959 }
8960
8961 // Log to Zombie Plague log file?
8962 if (get_pcvar_num(cvar_logcommands))
8963 {
8964 static logdata[100], authid[32], ip[16]
8965 get_user_authid(id, authid, charsmax(authid))
8966 get_user_ip(id, ip, charsmax(ip), 1)
8967 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %L (Players: %d/%d)", g_playername[id], authid, ip, LANG_SERVER, "CMD_SWARM", fnGetPlaying(), g_maxplayers)
8968 log_to_file("zombieplague.log", logdata)
8969 }
8970
8971 // Call Swarm Mode
8972 remove_task(TASK_MAKEZOMBIE)
8973 make_a_zombie(MODE_SWARM, 0)
8974}
8975
8976// Admin Command. zp_multi
8977command_multi(id)
8978{
8979 // Show activity?
8980 switch (get_pcvar_num(cvar_showactivity))
8981 {
8982 case 1: client_print(0, print_chat, "ADMIN - %L", LANG_PLAYER, "CMD_MULTI")
8983 case 2: client_print(0, print_chat, "ADMIN %s - %L", g_playername[id], LANG_PLAYER, "CMD_MULTI")
8984 }
8985
8986 // Log to Zombie Plague log file?
8987 if (get_pcvar_num(cvar_logcommands))
8988 {
8989 static logdata[100], authid[32], ip[16]
8990 get_user_authid(id, authid, charsmax(authid))
8991 get_user_ip(id, ip, charsmax(ip), 1)
8992 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %L (Players: %d/%d)", g_playername[id], authid, ip, LANG_SERVER,"CMD_MULTI", fnGetPlaying(), g_maxplayers)
8993 log_to_file("zombieplague.log", logdata)
8994 }
8995
8996 // Call Multi Infection
8997 remove_task(TASK_MAKEZOMBIE)
8998 make_a_zombie(MODE_MULTI, 0)
8999}
9000
9001// Admin Command. zp_plague
9002command_plague(id)
9003{
9004 // Show activity?
9005 switch (get_pcvar_num(cvar_showactivity))
9006 {
9007 case 1: client_print(0, print_chat, "ADMIN - %L", LANG_PLAYER, "CMD_PLAGUE")
9008 case 2: client_print(0, print_chat, "ADMIN %s - %L", g_playername[id], LANG_PLAYER, "CMD_PLAGUE")
9009 }
9010
9011 // Log to Zombie Plague log file?
9012 if (get_pcvar_num(cvar_logcommands))
9013 {
9014 static logdata[100], authid[32], ip[16]
9015 get_user_authid(id, authid, charsmax(authid))
9016 get_user_ip(id, ip, charsmax(ip), 1)
9017 formatex(logdata, charsmax(logdata), "ADMIN %s <%s><%s> - %L (Players: %d/%d)", g_playername[id], authid, ip, LANG_SERVER,"CMD_PLAGUE", fnGetPlaying(), g_maxplayers)
9018 log_to_file("zombieplague.log", logdata)
9019 }
9020
9021 // Call Plague Mode
9022 remove_task(TASK_MAKEZOMBIE)
9023 make_a_zombie(MODE_PLAGUE, 0)
9024}
9025
9026/*================================================================================
9027 [Custom Natives]
9028=================================================================================*/
9029
9030// Native: zp_get_user_zombie
9031public native_get_user_zombie(id)
9032{
9033 return g_zombie[id];
9034}
9035
9036// Native: zp_get_user_nemesis
9037public native_get_user_nemesis(id)
9038{
9039 return g_nemesis[id];
9040}
9041
9042// Native: zp_get_user_survivor
9043public native_get_user_survivor(id)
9044{
9045 return g_survivor[id];
9046}
9047
9048public native_get_user_first_zombie(id)
9049{
9050 return g_firstzombie[id];
9051}
9052
9053// Native: zp_get_user_last_zombie
9054public native_get_user_last_zombie(id)
9055{
9056 return g_lastzombie[id];
9057}
9058
9059// Native: zp_get_user_last_human
9060public native_get_user_last_human(id)
9061{
9062 return g_lasthuman[id];
9063}
9064
9065// Native: zp_get_user_zombie_class
9066public native_get_user_zombie_class(id)
9067{
9068 return g_zombieclass[id];
9069}
9070
9071// Native: zp_get_user_next_class
9072public native_get_user_next_class(id)
9073{
9074 return g_zombieclassnext[id];
9075}
9076
9077// Native: zp_set_user_zombie_class
9078public native_set_user_zombie_class(id, classid)
9079{
9080 if (classid < 0 || classid >= g_zclass_i)
9081 return 0;
9082
9083 g_zombieclassnext[id] = classid
9084 return 1;
9085}
9086
9087// Native: zp_get_user_ammo_packs
9088public native_get_user_ammo_packs(id)
9089{
9090 return g_ammopacks[id];
9091}
9092
9093// Native: zp_set_user_ammo_packs
9094public native_set_user_ammo_packs(id, amount)
9095{
9096 g_ammopacks[id] = amount;
9097}
9098
9099// Native: zp_get_zombie_maxhealth
9100public native_get_zombie_maxhealth(id)
9101{
9102 // ZP disabled
9103 if (!g_pluginenabled)
9104 return -1;
9105
9106 if (g_zombie[id] && !g_nemesis[id])
9107 {
9108 if (g_firstzombie[id])
9109 return floatround(float(ArrayGetCell(g_zclass_hp, g_zombieclass[id])) * get_pcvar_float(cvar_zombiefirsthp));
9110 else
9111 return ArrayGetCell(g_zclass_hp, g_zombieclass[id]);
9112 }
9113 return -1;
9114}
9115
9116// Native: zp_get_user_batteries
9117public native_get_user_batteries(id)
9118{
9119 return g_flashbattery[id];
9120}
9121
9122// Native: zp_set_user_batteries
9123public native_set_user_batteries(id, value)
9124{
9125 // ZP disabled
9126 if (!g_pluginenabled)
9127 return;
9128
9129 g_flashbattery[id] = clamp(value, 0, 100);
9130
9131 if (g_cached_customflash)
9132 {
9133 // Set the flashlight charge task to update battery status
9134 remove_task(id+TASK_CHARGE)
9135 set_task(1.0, "flashlight_charge", id+TASK_CHARGE, _, _, "b")
9136 }
9137}
9138
9139// Native: zp_get_user_nightvision
9140public native_get_user_nightvision(id)
9141{
9142 return g_nvision[id];
9143}
9144
9145// Native: zp_set_user_nightvision
9146public native_set_user_nightvision(id, set)
9147{
9148 // ZP disabled
9149 if (!g_pluginenabled)
9150 return;
9151
9152 if (set)
9153 {
9154 g_nvision[id] = true
9155
9156 if (!g_isbot[id])
9157 {
9158 g_nvisionenabled[id] = true
9159
9160 // Custom nvg?
9161 if (get_pcvar_num(cvar_customnvg))
9162 {
9163 remove_task(id+TASK_NVISION)
9164 set_task(0.1, "set_user_nvision", id+TASK_NVISION, _, _, "b")
9165 }
9166 else
9167 set_user_gnvision(id, 1)
9168 }
9169 else
9170 cs_set_user_nvg(id, 1)
9171 }
9172 else
9173 {
9174 // Turn off NVG for bots
9175 if (g_isbot[id]) cs_set_user_nvg(id, 0);
9176 if (get_pcvar_num(cvar_customnvg)) remove_task(id+TASK_NVISION)
9177 else if (g_nvisionenabled[id]) set_user_gnvision(id, 0)
9178 g_nvision[id] = false
9179 g_nvisionenabled[id] = false
9180 }
9181}
9182
9183// Native: zp_infect_user
9184public native_infect_user(id, infector, silent, rewards)
9185{
9186 // ZP disabled
9187 if (!g_pluginenabled)
9188 return -1;
9189
9190 // Not allowed to be zombie
9191 if (!allowed_zombie(id))
9192 return 0;
9193
9194 // New round?
9195 if (g_newround)
9196 {
9197 // Set as first zombie
9198 remove_task(TASK_MAKEZOMBIE)
9199 make_a_zombie(MODE_INFECTION, id)
9200 }
9201 else
9202 {
9203 // Just infect (plus some checks)
9204 zombieme(id, is_user_valid_alive(infector) ? infector : 0, 0, (silent == 1) ? 1 : 0, (rewards == 1) ? 1 : 0)
9205 }
9206
9207 return 1;
9208}
9209
9210// Native: zp_disinfect_user
9211public native_disinfect_user(id, silent)
9212{
9213 // ZP disabled
9214 if (!g_pluginenabled)
9215 return -1;
9216
9217 // Not allowed to be human
9218 if (!allowed_human(id))
9219 return 0;
9220
9221 // Turn to human
9222 humanme(id, 0, (silent == 1) ? 1 : 0)
9223 return 1;
9224}
9225
9226// Native: zp_make_user_nemesis
9227public native_make_user_nemesis(id)
9228{
9229 // ZP disabled
9230 if (!g_pluginenabled)
9231 return -1;
9232
9233 // Not allowed to be nemesis
9234 if (!allowed_nemesis(id))
9235 return 0;
9236
9237 // New round?
9238 if (g_newround)
9239 {
9240 // Set as first nemesis
9241 remove_task(TASK_MAKEZOMBIE)
9242 make_a_zombie(MODE_NEMESIS, id)
9243 }
9244 else
9245 {
9246 // Turn player into a Nemesis
9247 zombieme(id, 0, 1, 0, 0)
9248 }
9249
9250 return 1;
9251}
9252
9253// Native: zp_make_user_survivor
9254public native_make_user_survivor(id)
9255{
9256 // ZP disabled
9257 if (!g_pluginenabled)
9258 return -1;
9259
9260 // Not allowed to be survivor
9261 if (!allowed_survivor(id))
9262 return 0;
9263
9264 // New round?
9265 if (g_newround)
9266 {
9267 // Set as first survivor
9268 remove_task(TASK_MAKEZOMBIE)
9269 make_a_zombie(MODE_SURVIVOR, id)
9270 }
9271 else
9272 {
9273 // Turn player into a Survivor
9274 humanme(id, 1, 0)
9275 }
9276
9277 return 1;
9278}
9279
9280// Native: zp_respawn_user
9281public native_respawn_user(id, team)
9282{
9283 // ZP disabled
9284 if (!g_pluginenabled)
9285 return -1;
9286
9287 // Invalid player
9288 if (!is_user_valid_connected(id))
9289 return 0;
9290
9291 // Respawn not allowed
9292 if (!allowed_respawn(id))
9293 return 0;
9294
9295 // Respawn as zombie?
9296 g_respawn_as_zombie[id] = (team == ZP_TEAM_ZOMBIE) ? true : false
9297
9298 // Respawnish!
9299 respawn_player_manually(id)
9300 return 1;
9301}
9302
9303// Native: zp_force_buy_extra_item
9304public native_force_buy_extra_item(id, itemid, ignorecost)
9305{
9306 // ZP disabled
9307 if (!g_pluginenabled)
9308 return -1;
9309
9310 if (itemid < 0 || itemid >= g_extraitem_i)
9311 return 0;
9312
9313 buy_extra_item(id, itemid, ignorecost)
9314 return 1;
9315}
9316
9317// Native: zp_has_round_started
9318public native_has_round_started()
9319{
9320 if (g_newround) return 0; // not started
9321 if (g_modestarted) return 1; // started
9322 return 2; // starting
9323}
9324
9325// Native: zp_is_nemesis_round
9326public native_is_nemesis_round()
9327{
9328 return g_nemround;
9329}
9330
9331// Native: zp_is_survivor_round
9332public native_is_survivor_round()
9333{
9334 return g_survround;
9335}
9336
9337// Native: zp_is_swarm_round
9338public native_is_swarm_round()
9339{
9340 return g_swarmround;
9341}
9342
9343// Native: zp_is_plague_round
9344public native_is_plague_round()
9345{
9346 return g_plagueround;
9347}
9348
9349// Native: zp_get_zombie_count
9350public native_get_zombie_count()
9351{
9352 return fnGetZombies();
9353}
9354
9355// Native: zp_get_human_count
9356public native_get_human_count()
9357{
9358 return fnGetHumans();
9359}
9360
9361// Native: zp_get_nemesis_count
9362public native_get_nemesis_count()
9363{
9364 return fnGetNemesis();
9365}
9366
9367// Native: zp_get_survivor_count
9368public native_get_survivor_count()
9369{
9370 return fnGetSurvivors();
9371}
9372
9373// Native: zp_register_extra_item
9374public native_register_extra_item(const name[], cost, team)
9375{
9376 // ZP disabled
9377 if (!g_pluginenabled)
9378 return -1;
9379
9380 // Arrays not yet initialized
9381 if (!g_arrays_created)
9382 return -1;
9383
9384 // For backwards compatibility
9385 if (team == ZP_TEAM_ANY)
9386 team = ZP_TEAM_ZOMBIE|ZP_TEAM_HUMAN
9387
9388 // Strings passed byref
9389 param_convert(1)
9390
9391 // Add the item
9392 ArrayPushString(g_extraitem_name, name)
9393 ArrayPushCell(g_extraitem_cost, cost)
9394 ArrayPushCell(g_extraitem_team, team)
9395
9396 // Set temporary new item flag
9397 ArrayPushCell(g_extraitem_new, 1)
9398
9399 // Override extra items data with our customizations
9400 new i, buffer[32], size = ArraySize(g_extraitem2_realname)
9401 for (i = 0; i < size; i++)
9402 {
9403 ArrayGetString(g_extraitem2_realname, i, buffer, charsmax(buffer))
9404
9405 // Check if this is the intended item to override
9406 if (!equal(name, buffer))
9407 continue;
9408
9409 // Remove new item flag
9410 ArraySetCell(g_extraitem_new, g_extraitem_i, 0)
9411
9412 // Replace caption
9413 ArrayGetString(g_extraitem2_name, i, buffer, charsmax(buffer))
9414 ArraySetString(g_extraitem_name, g_extraitem_i, buffer)
9415
9416 // Replace cost
9417 buffer[0] = ArrayGetCell(g_extraitem2_cost, i)
9418 ArraySetCell(g_extraitem_cost, g_extraitem_i, buffer[0])
9419
9420 // Replace team
9421 buffer[0] = ArrayGetCell(g_extraitem2_team, i)
9422 ArraySetCell(g_extraitem_team, g_extraitem_i, buffer[0])
9423 }
9424
9425 // Increase registered items counter
9426 g_extraitem_i++
9427
9428 // Return id under which we registered the item
9429 return g_extraitem_i-1;
9430}
9431
9432// Function: zp_register_extra_item (to be used within this plugin only)
9433native_register_extra_item2(const name[], cost, team)
9434{
9435 // Add the item
9436 ArrayPushString(g_extraitem_name, name)
9437 ArrayPushCell(g_extraitem_cost, cost)
9438 ArrayPushCell(g_extraitem_team, team)
9439
9440 // Set temporary new item flag
9441 ArrayPushCell(g_extraitem_new, 1)
9442
9443 // Increase registered items counter
9444 g_extraitem_i++
9445}
9446
9447// Native: zp_register_zombie_class
9448public native_register_zombie_class(const name[], const info[], const model[], const clawmodel[], hp, speed, Float:gravity, Float:knockback)
9449{
9450 // ZP disabled
9451 if (!g_pluginenabled)
9452 return -1;
9453
9454 // Arrays not yet initialized
9455 if (!g_arrays_created)
9456 return -1;
9457
9458 // Strings passed byref
9459 param_convert(1)
9460 param_convert(2)
9461 param_convert(3)
9462 param_convert(4)
9463
9464 // Add the class
9465 ArrayPushString(g_zclass_name, name)
9466 ArrayPushString(g_zclass_info, info)
9467
9468 // Using same zombie models for all classes?
9469 if (g_same_models_for_all)
9470 {
9471 ArrayPushCell(g_zclass_modelsstart, 0)
9472 ArrayPushCell(g_zclass_modelsend, ArraySize(g_zclass_playermodel))
9473 }
9474 else
9475 {
9476 ArrayPushCell(g_zclass_modelsstart, ArraySize(g_zclass_playermodel))
9477 ArrayPushString(g_zclass_playermodel, model)
9478 ArrayPushCell(g_zclass_modelsend, ArraySize(g_zclass_playermodel))
9479 ArrayPushCell(g_zclass_modelindex, -1)
9480 }
9481
9482 ArrayPushString(g_zclass_clawmodel, clawmodel)
9483 ArrayPushCell(g_zclass_hp, hp)
9484 ArrayPushCell(g_zclass_spd, speed)
9485 ArrayPushCell(g_zclass_grav, gravity)
9486 ArrayPushCell(g_zclass_kb, knockback)
9487
9488 // Set temporary new class flag
9489 ArrayPushCell(g_zclass_new, 1)
9490
9491 // Override zombie classes data with our customizations
9492 new i, k, buffer[32], Float:buffer2, nummodels_custom, nummodels_default, prec_mdl[100], size = ArraySize(g_zclass2_realname)
9493 for (i = 0; i < size; i++)
9494 {
9495 ArrayGetString(g_zclass2_realname, i, buffer, charsmax(buffer))
9496
9497 // Check if this is the intended class to override
9498 if (!equal(name, buffer))
9499 continue;
9500
9501 // Remove new class flag
9502 ArraySetCell(g_zclass_new, g_zclass_i, 0)
9503
9504 // Replace caption
9505 ArrayGetString(g_zclass2_name, i, buffer, charsmax(buffer))
9506 ArraySetString(g_zclass_name, g_zclass_i, buffer)
9507
9508 // Replace info
9509 ArrayGetString(g_zclass2_info, i, buffer, charsmax(buffer))
9510 ArraySetString(g_zclass_info, g_zclass_i, buffer)
9511
9512 // Replace models, unless using same models for all classes
9513 if (!g_same_models_for_all)
9514 {
9515 nummodels_custom = ArrayGetCell(g_zclass2_modelsend, i) - ArrayGetCell(g_zclass2_modelsstart, i)
9516 nummodels_default = ArrayGetCell(g_zclass_modelsend, g_zclass_i) - ArrayGetCell(g_zclass_modelsstart, g_zclass_i)
9517
9518 // Replace each player model and model index
9519 for (k = 0; k < min(nummodels_custom, nummodels_default); k++)
9520 {
9521 ArrayGetString(g_zclass2_playermodel, ArrayGetCell(g_zclass2_modelsstart, i) + k, buffer, charsmax(buffer))
9522 ArraySetString(g_zclass_playermodel, ArrayGetCell(g_zclass_modelsstart, g_zclass_i) + k, buffer)
9523
9524 // Precache player model and replace its modelindex with the real one
9525 formatex(prec_mdl, charsmax(prec_mdl), "models/player/%s/%s.mdl", buffer, buffer)
9526 ArraySetCell(g_zclass_modelindex, ArrayGetCell(g_zclass_modelsstart, g_zclass_i) + k, engfunc(EngFunc_PrecacheModel, prec_mdl))
9527 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, prec_mdl)
9528 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, prec_mdl)
9529 }
9530
9531 // We have more custom models than what we can accommodate,
9532 // Let's make some space...
9533 if (nummodels_custom > nummodels_default)
9534 {
9535 for (k = nummodels_default; k < nummodels_custom; k++)
9536 {
9537 ArrayGetString(g_zclass2_playermodel, ArrayGetCell(g_zclass2_modelsstart, i) + k, buffer, charsmax(buffer))
9538 ArrayInsertStringAfter(g_zclass_playermodel, ArrayGetCell(g_zclass_modelsstart, g_zclass_i) + k - 1, buffer)
9539
9540 // Precache player model and retrieve its modelindex
9541 formatex(prec_mdl, charsmax(prec_mdl), "models/player/%s/%s.mdl", buffer, buffer)
9542 ArrayInsertCellAfter(g_zclass_modelindex, ArrayGetCell(g_zclass_modelsstart, g_zclass_i) + k - 1, engfunc(EngFunc_PrecacheModel, prec_mdl))
9543 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, prec_mdl)
9544 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, prec_mdl)
9545 }
9546
9547 // Fix models end index for this class
9548 ArraySetCell(g_zclass_modelsend, g_zclass_i, ArrayGetCell(g_zclass_modelsend, g_zclass_i) + (nummodels_custom - nummodels_default))
9549 }
9550
9551 /* --- Not needed since classes can't have more than 1 default model for now ---
9552 // We have less custom models than what this class has by default,
9553 // Get rid of those extra entries...
9554 if (nummodels_custom < nummodels_default)
9555 {
9556 for (k = nummodels_custom; k < nummodels_default; k++)
9557 {
9558 ArrayDeleteItem(g_zclass_playermodel, ArrayGetCell(g_zclass_modelsstart, g_zclass_i) + nummodels_custom)
9559 }
9560
9561 // Fix models end index for this class
9562 ArraySetCell(g_zclass_modelsend, g_zclass_i, ArrayGetCell(g_zclass_modelsend, g_zclass_i) - (nummodels_default - nummodels_custom))
9563 }
9564 */
9565 }
9566
9567 // Replace clawmodel
9568 ArrayGetString(g_zclass2_clawmodel, i, buffer, charsmax(buffer))
9569 ArraySetString(g_zclass_clawmodel, g_zclass_i, buffer)
9570
9571 // Precache clawmodel
9572 formatex(prec_mdl, charsmax(prec_mdl), "models/zombie_plague/%s", buffer)
9573 engfunc(EngFunc_PrecacheModel, prec_mdl)
9574
9575 // Replace health
9576 buffer[0] = ArrayGetCell(g_zclass2_hp, i)
9577 ArraySetCell(g_zclass_hp, g_zclass_i, buffer[0])
9578
9579 // Replace speed
9580 buffer[0] = ArrayGetCell(g_zclass2_spd, i)
9581 ArraySetCell(g_zclass_spd, g_zclass_i, buffer[0])
9582
9583 // Replace gravity
9584 buffer2 = Float:ArrayGetCell(g_zclass2_grav, i)
9585 ArraySetCell(g_zclass_grav, g_zclass_i, buffer2)
9586
9587 // Replace knockback
9588 buffer2 = Float:ArrayGetCell(g_zclass2_kb, i)
9589 ArraySetCell(g_zclass_kb, g_zclass_i, buffer2)
9590 }
9591
9592 // If class was not overriden with customization data
9593 if (ArrayGetCell(g_zclass_new, g_zclass_i))
9594 {
9595 // If not using same models for all classes
9596 if (!g_same_models_for_all)
9597 {
9598 // Precache default class model and replace modelindex with the real one
9599 formatex(prec_mdl, charsmax(prec_mdl), "models/player/%s/%s.mdl", model, model)
9600 ArraySetCell(g_zclass_modelindex, ArrayGetCell(g_zclass_modelsstart, g_zclass_i), engfunc(EngFunc_PrecacheModel, prec_mdl))
9601 if (g_force_consistency == 1) force_unmodified(force_model_samebounds, {0,0,0}, {0,0,0}, prec_mdl)
9602 if (g_force_consistency == 2) force_unmodified(force_exactfile, {0,0,0}, {0,0,0}, prec_mdl)
9603 }
9604
9605 // Precache default clawmodel
9606 formatex(prec_mdl, charsmax(prec_mdl), "models/zombie_plague/%s", clawmodel)
9607 engfunc(EngFunc_PrecacheModel, prec_mdl)
9608 }
9609
9610 // Increase registered classes counter
9611 g_zclass_i++
9612
9613 // Return id under which we registered the class
9614 return g_zclass_i-1;
9615}
9616
9617// Native: zp_get_extra_item_id
9618public native_get_extra_item_id(const name[])
9619{
9620 // ZP disabled
9621 if (!g_pluginenabled)
9622 return -1;
9623
9624 // Strings passed byref
9625 param_convert(1)
9626
9627 // Loop through every item
9628 static i, item_name[32]
9629 for (i = 0; i < g_extraitem_i; i++)
9630 {
9631 ArrayGetString(g_extraitem_name, i, item_name, charsmax(item_name))
9632
9633 // Check if this is the item to retrieve
9634 if (equali(name, item_name))
9635 return i;
9636 }
9637
9638 return -1;
9639}
9640
9641// Native: zp_get_zombie_class_id
9642public native_get_zombie_class_id(const name[])
9643{
9644 // ZP disabled
9645 if (!g_pluginenabled)
9646 return -1;
9647
9648 // Strings passed byref
9649 param_convert(1)
9650
9651 // Loop through every class
9652 static i, class_name[32]
9653 for (i = 0; i < g_zclass_i; i++)
9654 {
9655 ArrayGetString(g_zclass_name, i, class_name, charsmax(class_name))
9656
9657 // Check if this is the class to retrieve
9658 if (equali(name, class_name))
9659 return i;
9660 }
9661
9662 return -1;
9663}
9664
9665/*================================================================================
9666 [Custom Messages]
9667=================================================================================*/
9668
9669// Custom Night Vision
9670public set_user_nvision(taskid)
9671{
9672 // Get player's origin
9673 static origin[3]
9674 get_user_origin(ID_NVISION, origin)
9675
9676 // Nightvision message
9677 message_begin(MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, _, ID_NVISION)
9678 write_byte(TE_DLIGHT) // TE id
9679 write_coord(origin[0]) // x
9680 write_coord(origin[1]) // y
9681 write_coord(origin[2]) // z
9682 write_byte(get_pcvar_num(cvar_nvgsize)) // radius
9683
9684 // Nemesis / Madness / Spectator in nemesis round
9685 if (g_nemesis[ID_NVISION] || (g_zombie[ID_NVISION] && g_nodamage[ID_NVISION]) || (!g_isalive[ID_NVISION] && g_nemround))
9686 {
9687 write_byte(get_pcvar_num(cvar_nemnvgcolor[0])) // r
9688 write_byte(get_pcvar_num(cvar_nemnvgcolor[1])) // g
9689 write_byte(get_pcvar_num(cvar_nemnvgcolor[2])) // b
9690 }
9691 // Human / Spectator in normal round
9692 else if (!g_zombie[ID_NVISION] || !g_isalive[ID_NVISION])
9693 {
9694 write_byte(get_pcvar_num(cvar_humnvgcolor[0])) // r
9695 write_byte(get_pcvar_num(cvar_humnvgcolor[1])) // g
9696 write_byte(get_pcvar_num(cvar_humnvgcolor[2])) // b
9697 }
9698 // Zombie
9699 else
9700 {
9701 write_byte(get_pcvar_num(cvar_nvgcolor[0])) // r
9702 write_byte(get_pcvar_num(cvar_nvgcolor[1])) // g
9703 write_byte(get_pcvar_num(cvar_nvgcolor[2])) // b
9704 }
9705
9706 write_byte(2) // life
9707 write_byte(0) // decay rate
9708 message_end()
9709}
9710
9711// Game Nightvision
9712set_user_gnvision(id, toggle)
9713{
9714 // Toggle NVG message
9715 message_begin(MSG_ONE, g_msgNVGToggle, _, id)
9716 write_byte(toggle) // toggle
9717 message_end()
9718}
9719
9720// Custom Flashlight
9721public set_user_flashlight(taskid)
9722{
9723 // Get player and aiming origins
9724 static Float:originF[3], Float:destoriginF[3]
9725 pev(ID_FLASH, pev_origin, originF)
9726 fm_get_aim_origin(ID_FLASH, destoriginF)
9727
9728 // Max distance check
9729 if (get_distance_f(originF, destoriginF) > get_pcvar_float(cvar_flashdist))
9730 return;
9731
9732 // Send to all players?
9733 if (get_pcvar_num(cvar_flashshowall))
9734 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, destoriginF, 0)
9735 else
9736 message_begin(MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, _, ID_FLASH)
9737
9738 // Flashlight
9739 write_byte(TE_DLIGHT) // TE id
9740 engfunc(EngFunc_WriteCoord, destoriginF[0]) // x
9741 engfunc(EngFunc_WriteCoord, destoriginF[1]) // y
9742 engfunc(EngFunc_WriteCoord, destoriginF[2]) // z
9743 write_byte(get_pcvar_num(cvar_flashsize)) // radius
9744 write_byte(get_pcvar_num(cvar_flashcolor[0])) // r
9745 write_byte(get_pcvar_num(cvar_flashcolor[1])) // g
9746 write_byte(get_pcvar_num(cvar_flashcolor[2])) // b
9747 write_byte(3) // life
9748 write_byte(0) // decay rate
9749 message_end()
9750}
9751
9752// Infection special effects
9753infection_effects(id)
9754{
9755 // Screen fade? (unless frozen)
9756 if (!g_frozen[id] && get_pcvar_num(cvar_infectionscreenfade))
9757 {
9758 message_begin(MSG_ONE_UNRELIABLE, g_msgScreenFade, _, id)
9759 write_short(UNIT_SECOND) // duration
9760 write_short(0) // hold time
9761 write_short(FFADE_IN) // fade type
9762 if (g_nemesis[id])
9763 {
9764 write_byte(get_pcvar_num(cvar_nemnvgcolor[0])) // r
9765 write_byte(get_pcvar_num(cvar_nemnvgcolor[1])) // g
9766 write_byte(get_pcvar_num(cvar_nemnvgcolor[2])) // b
9767 }
9768 else
9769 {
9770 write_byte(get_pcvar_num(cvar_nvgcolor[0])) // r
9771 write_byte(get_pcvar_num(cvar_nvgcolor[1])) // g
9772 write_byte(get_pcvar_num(cvar_nvgcolor[2])) // b
9773 }
9774 write_byte (255) // alpha
9775 message_end()
9776 }
9777
9778 // Screen shake?
9779 if (get_pcvar_num(cvar_infectionscreenshake))
9780 {
9781 message_begin(MSG_ONE_UNRELIABLE, g_msgScreenShake, _, id)
9782 write_short(UNIT_SECOND*4) // amplitude
9783 write_short(UNIT_SECOND*2) // duration
9784 write_short(UNIT_SECOND*10) // frequency
9785 message_end()
9786 }
9787
9788 // Infection icon?
9789 if (get_pcvar_num(cvar_hudicons))
9790 {
9791 message_begin(MSG_ONE_UNRELIABLE, g_msgDamage, _, id)
9792 write_byte(0) // damage save
9793 write_byte(0) // damage take
9794 write_long(DMG_NERVEGAS) // damage type - DMG_RADIATION
9795 write_coord(0) // x
9796 write_coord(0) // y
9797 write_coord(0) // z
9798 message_end()
9799 }
9800
9801 // Get player's origin
9802 static origin[3]
9803 get_user_origin(id, origin)
9804
9805 // Tracers?
9806 if (get_pcvar_num(cvar_infectiontracers))
9807 {
9808 message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
9809 write_byte(TE_IMPLOSION) // TE id
9810 write_coord(origin[0]) // x
9811 write_coord(origin[1]) // y
9812 write_coord(origin[2]) // z
9813 write_byte(128) // radius
9814 write_byte(20) // count
9815 write_byte(3) // duration
9816 message_end()
9817 }
9818
9819 // Particle burst?
9820 if (get_pcvar_num(cvar_infectionparticles))
9821 {
9822 message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
9823 write_byte(TE_PARTICLEBURST) // TE id
9824 write_coord(origin[0]) // x
9825 write_coord(origin[1]) // y
9826 write_coord(origin[2]) // z
9827 write_short(50) // radius
9828 write_byte(70) // color
9829 write_byte(3) // duration (will be randomized a bit)
9830 message_end()
9831 }
9832
9833 // Light sparkle?
9834 if (get_pcvar_num(cvar_infectionsparkle))
9835 {
9836 message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
9837 write_byte(TE_DLIGHT) // TE id
9838 write_coord(origin[0]) // x
9839 write_coord(origin[1]) // y
9840 write_coord(origin[2]) // z
9841 write_byte(20) // radius
9842 write_byte(get_pcvar_num(cvar_nvgcolor[0])) // r
9843 write_byte(get_pcvar_num(cvar_nvgcolor[1])) // g
9844 write_byte(get_pcvar_num(cvar_nvgcolor[2])) // b
9845 write_byte(2) // life
9846 write_byte(0) // decay rate
9847 message_end()
9848 }
9849}
9850
9851// Nemesis/madness aura task
9852public zombie_aura(taskid)
9853{
9854 // Not nemesis, not in zombie madness
9855 if (!g_nemesis[ID_AURA] && !g_nodamage[ID_AURA])
9856 {
9857 // Task not needed anymore
9858 remove_task(taskid);
9859 return;
9860 }
9861
9862 // Get player's origin
9863 static origin[3]
9864 get_user_origin(ID_AURA, origin)
9865
9866 // Colored Aura
9867 message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
9868 write_byte(TE_DLIGHT) // TE id
9869 write_coord(origin[0]) // x
9870 write_coord(origin[1]) // y
9871 write_coord(origin[2]) // z
9872 write_byte(20) // radius
9873 write_byte(get_pcvar_num(cvar_nemnvgcolor[0])) // r
9874 write_byte(get_pcvar_num(cvar_nemnvgcolor[1])) // g
9875 write_byte(get_pcvar_num(cvar_nemnvgcolor[2])) // b
9876 write_byte(2) // life
9877 write_byte(0) // decay rate
9878 message_end()
9879}
9880
9881// Make zombies leave footsteps and bloodstains on the floor
9882public make_blood(taskid)
9883{
9884 // Only bleed when moving on ground
9885 if (!(pev(ID_BLOOD, pev_flags) & FL_ONGROUND) || fm_get_speed(ID_BLOOD) < 80)
9886 return;
9887
9888 // Get user origin
9889 static Float:originF[3]
9890 pev(ID_BLOOD, pev_origin, originF)
9891
9892 // If ducking set a little lower
9893 if (pev(ID_BLOOD, pev_bInDuck))
9894 originF[2] -= 18.0
9895 else
9896 originF[2] -= 36.0
9897
9898 // Send the decal message
9899 engfunc(EngFunc_MessageBegin, MSG_PAS, SVC_TEMPENTITY, originF, 0)
9900 write_byte(TE_WORLDDECAL) // TE id
9901 engfunc(EngFunc_WriteCoord, originF[0]) // x
9902 engfunc(EngFunc_WriteCoord, originF[1]) // y
9903 engfunc(EngFunc_WriteCoord, originF[2]) // z
9904 write_byte(ArrayGetCell(zombie_decals, random_num(0, ArraySize(zombie_decals) - 1)) + (g_czero * 12)) // random decal number (offsets +12 for CZ)
9905 message_end()
9906}
9907
9908// Flare Lighting Effects
9909flare_lighting(entity, duration)
9910{
9911 // Get origin and color
9912 static Float:originF[3], color[3]
9913 pev(entity, pev_origin, originF)
9914 pev(entity, PEV_FLARE_COLOR, color)
9915
9916 // Lighting
9917 engfunc(EngFunc_MessageBegin, MSG_PAS, SVC_TEMPENTITY, originF, 0)
9918 write_byte(TE_DLIGHT) // TE id
9919 engfunc(EngFunc_WriteCoord, originF[0]) // x
9920 engfunc(EngFunc_WriteCoord, originF[1]) // y
9921 engfunc(EngFunc_WriteCoord, originF[2]) // z
9922 write_byte(get_pcvar_num(cvar_flaresize)) // radius
9923 write_byte(color[0]) // r
9924 write_byte(color[1]) // g
9925 write_byte(color[2]) // b
9926 write_byte(51) //life
9927 write_byte((duration < 2) ? 3 : 0) //decay rate
9928 message_end()
9929
9930 // Sparks
9931 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
9932 write_byte(TE_SPARKS) // TE id
9933 engfunc(EngFunc_WriteCoord, originF[0]) // x
9934 engfunc(EngFunc_WriteCoord, originF[1]) // y
9935 engfunc(EngFunc_WriteCoord, originF[2]) // z
9936 message_end()
9937}
9938
9939// Burning Flames
9940public burning_flame(taskid)
9941{
9942 // Get player origin and flags
9943 static origin[3], flags
9944 get_user_origin(ID_BURN, origin)
9945 flags = pev(ID_BURN, pev_flags)
9946
9947 // Madness mode - in water - burning stopped
9948 if (g_nodamage[ID_BURN] || (flags & FL_INWATER) || g_burning_duration[ID_BURN] < 1)
9949 {
9950 // Smoke sprite
9951 message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
9952 write_byte(TE_SMOKE) // TE id
9953 write_coord(origin[0]) // x
9954 write_coord(origin[1]) // y
9955 write_coord(origin[2]-50) // z
9956 write_short(g_smokeSpr) // sprite
9957 write_byte(random_num(15, 20)) // scale
9958 write_byte(random_num(10, 20)) // framerate
9959 message_end()
9960
9961 // Task not needed anymore
9962 remove_task(taskid);
9963 return;
9964 }
9965
9966 // Randomly play burning zombie scream sounds (not for nemesis)
9967 if (!g_nemesis[ID_BURN] && !random_num(0, 20))
9968 {
9969 static sound[64]
9970 ArrayGetString(grenade_fire_player, random_num(0, ArraySize(grenade_fire_player) - 1), sound, charsmax(sound))
9971 emit_sound(ID_BURN, CHAN_VOICE, sound, 1.0, ATTN_NORM, 0, PITCH_NORM)
9972 }
9973
9974 // Fire slow down, unless nemesis
9975 if (!g_nemesis[ID_BURN] && (flags & FL_ONGROUND) && get_pcvar_float(cvar_fireslowdown) > 0.0)
9976 {
9977 static Float:velocity[3]
9978 pev(ID_BURN, pev_velocity, velocity)
9979 xs_vec_mul_scalar(velocity, get_pcvar_float(cvar_fireslowdown), velocity)
9980 set_pev(ID_BURN, pev_velocity, velocity)
9981 }
9982
9983 // Get player's health
9984 static health
9985 health = pev(ID_BURN, pev_health)
9986
9987 // Take damage from the fire
9988 if (health - floatround(get_pcvar_float(cvar_firedamage), floatround_ceil) > 0)
9989 fm_set_user_health(ID_BURN, health - floatround(get_pcvar_float(cvar_firedamage), floatround_ceil))
9990
9991 // Flame sprite
9992 message_begin(MSG_PVS, SVC_TEMPENTITY, origin)
9993 write_byte(TE_SPRITE) // TE id
9994 write_coord(origin[0]+random_num(-5, 5)) // x
9995 write_coord(origin[1]+random_num(-5, 5)) // y
9996 write_coord(origin[2]+random_num(-10, 10)) // z
9997 write_short(g_flameSpr) // sprite
9998 write_byte(random_num(5, 10)) // scale
9999 write_byte(200) // brightness
10000 message_end()
10001
10002 // Decrease burning duration counter
10003 g_burning_duration[ID_BURN]--
10004}
10005
10006// Infection Bomb: Green Blast
10007create_blast(const Float:originF[3])
10008{
10009 // Smallest ring
10010 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10011 write_byte(TE_BEAMCYLINDER) // TE id
10012 engfunc(EngFunc_WriteCoord, originF[0]) // x
10013 engfunc(EngFunc_WriteCoord, originF[1]) // y
10014 engfunc(EngFunc_WriteCoord, originF[2]) // z
10015 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10016 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10017 engfunc(EngFunc_WriteCoord, originF[2]+385.0) // z axis
10018 write_short(g_exploSpr) // sprite
10019 write_byte(0) // startframe
10020 write_byte(0) // framerate
10021 write_byte(4) // life
10022 write_byte(60) // width
10023 write_byte(0) // noise
10024 write_byte(0) // red
10025 write_byte(200) // green
10026 write_byte(0) // blue
10027 write_byte(200) // brightness
10028 write_byte(0) // speed
10029 message_end()
10030
10031 // Medium ring
10032 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10033 write_byte(TE_BEAMCYLINDER) // TE id
10034 engfunc(EngFunc_WriteCoord, originF[0]) // x
10035 engfunc(EngFunc_WriteCoord, originF[1]) // y
10036 engfunc(EngFunc_WriteCoord, originF[2]) // z
10037 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10038 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10039 engfunc(EngFunc_WriteCoord, originF[2]+470.0) // z axis
10040 write_short(g_exploSpr) // sprite
10041 write_byte(0) // startframe
10042 write_byte(0) // framerate
10043 write_byte(4) // life
10044 write_byte(60) // width
10045 write_byte(0) // noise
10046 write_byte(0) // red
10047 write_byte(200) // green
10048 write_byte(0) // blue
10049 write_byte(200) // brightness
10050 write_byte(0) // speed
10051 message_end()
10052
10053 // Largest ring
10054 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10055 write_byte(TE_BEAMCYLINDER) // TE id
10056 engfunc(EngFunc_WriteCoord, originF[0]) // x
10057 engfunc(EngFunc_WriteCoord, originF[1]) // y
10058 engfunc(EngFunc_WriteCoord, originF[2]) // z
10059 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10060 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10061 engfunc(EngFunc_WriteCoord, originF[2]+555.0) // z axis
10062 write_short(g_exploSpr) // sprite
10063 write_byte(0) // startframe
10064 write_byte(0) // framerate
10065 write_byte(4) // life
10066 write_byte(60) // width
10067 write_byte(0) // noise
10068 write_byte(0) // red
10069 write_byte(200) // green
10070 write_byte(0) // blue
10071 write_byte(200) // brightness
10072 write_byte(0) // speed
10073 message_end()
10074}
10075
10076// Fire Grenade: Fire Blast
10077create_blast2(const Float:originF[3])
10078{
10079 // Smallest ring
10080 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10081 write_byte(TE_BEAMCYLINDER) // TE id
10082 engfunc(EngFunc_WriteCoord, originF[0]) // x
10083 engfunc(EngFunc_WriteCoord, originF[1]) // y
10084 engfunc(EngFunc_WriteCoord, originF[2]) // z
10085 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10086 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10087 engfunc(EngFunc_WriteCoord, originF[2]+385.0) // z axis
10088 write_short(g_exploSpr) // sprite
10089 write_byte(0) // startframe
10090 write_byte(0) // framerate
10091 write_byte(4) // life
10092 write_byte(60) // width
10093 write_byte(0) // noise
10094 write_byte(200) // red
10095 write_byte(100) // green
10096 write_byte(0) // blue
10097 write_byte(200) // brightness
10098 write_byte(0) // speed
10099 message_end()
10100
10101 // Medium ring
10102 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10103 write_byte(TE_BEAMCYLINDER) // TE id
10104 engfunc(EngFunc_WriteCoord, originF[0]) // x
10105 engfunc(EngFunc_WriteCoord, originF[1]) // y
10106 engfunc(EngFunc_WriteCoord, originF[2]) // z
10107 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10108 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10109 engfunc(EngFunc_WriteCoord, originF[2]+470.0) // z axis
10110 write_short(g_exploSpr) // sprite
10111 write_byte(0) // startframe
10112 write_byte(0) // framerate
10113 write_byte(4) // life
10114 write_byte(60) // width
10115 write_byte(0) // noise
10116 write_byte(200) // red
10117 write_byte(50) // green
10118 write_byte(0) // blue
10119 write_byte(200) // brightness
10120 write_byte(0) // speed
10121 message_end()
10122
10123 // Largest ring
10124 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10125 write_byte(TE_BEAMCYLINDER) // TE id
10126 engfunc(EngFunc_WriteCoord, originF[0]) // x
10127 engfunc(EngFunc_WriteCoord, originF[1]) // y
10128 engfunc(EngFunc_WriteCoord, originF[2]) // z
10129 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10130 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10131 engfunc(EngFunc_WriteCoord, originF[2]+555.0) // z axis
10132 write_short(g_exploSpr) // sprite
10133 write_byte(0) // startframe
10134 write_byte(0) // framerate
10135 write_byte(4) // life
10136 write_byte(60) // width
10137 write_byte(0) // noise
10138 write_byte(200) // red
10139 write_byte(0) // green
10140 write_byte(0) // blue
10141 write_byte(200) // brightness
10142 write_byte(0) // speed
10143 message_end()
10144}
10145
10146// Frost Grenade: Freeze Blast
10147create_blast3(const Float:originF[3])
10148{
10149 // Smallest ring
10150 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10151 write_byte(TE_BEAMCYLINDER) // TE id
10152 engfunc(EngFunc_WriteCoord, originF[0]) // x
10153 engfunc(EngFunc_WriteCoord, originF[1]) // y
10154 engfunc(EngFunc_WriteCoord, originF[2]) // z
10155 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10156 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10157 engfunc(EngFunc_WriteCoord, originF[2]+385.0) // z axis
10158 write_short(g_exploSpr) // sprite
10159 write_byte(0) // startframe
10160 write_byte(0) // framerate
10161 write_byte(4) // life
10162 write_byte(60) // width
10163 write_byte(0) // noise
10164 write_byte(0) // red
10165 write_byte(100) // green
10166 write_byte(200) // blue
10167 write_byte(200) // brightness
10168 write_byte(0) // speed
10169 message_end()
10170
10171 // Medium ring
10172 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10173 write_byte(TE_BEAMCYLINDER) // TE id
10174 engfunc(EngFunc_WriteCoord, originF[0]) // x
10175 engfunc(EngFunc_WriteCoord, originF[1]) // y
10176 engfunc(EngFunc_WriteCoord, originF[2]) // z
10177 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10178 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10179 engfunc(EngFunc_WriteCoord, originF[2]+470.0) // z axis
10180 write_short(g_exploSpr) // sprite
10181 write_byte(0) // startframe
10182 write_byte(0) // framerate
10183 write_byte(4) // life
10184 write_byte(60) // width
10185 write_byte(0) // noise
10186 write_byte(0) // red
10187 write_byte(100) // green
10188 write_byte(200) // blue
10189 write_byte(200) // brightness
10190 write_byte(0) // speed
10191 message_end()
10192
10193 // Largest ring
10194 engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, originF, 0)
10195 write_byte(TE_BEAMCYLINDER) // TE id
10196 engfunc(EngFunc_WriteCoord, originF[0]) // x
10197 engfunc(EngFunc_WriteCoord, originF[1]) // y
10198 engfunc(EngFunc_WriteCoord, originF[2]) // z
10199 engfunc(EngFunc_WriteCoord, originF[0]) // x axis
10200 engfunc(EngFunc_WriteCoord, originF[1]) // y axis
10201 engfunc(EngFunc_WriteCoord, originF[2]+555.0) // z axis
10202 write_short(g_exploSpr) // sprite
10203 write_byte(0) // startframe
10204 write_byte(0) // framerate
10205 write_byte(4) // life
10206 write_byte(60) // width
10207 write_byte(0) // noise
10208 write_byte(0) // red
10209 write_byte(100) // green
10210 write_byte(200) // blue
10211 write_byte(200) // brightness
10212 write_byte(0) // speed
10213 message_end()
10214}
10215
10216// Fix Dead Attrib on scoreboard
10217FixDeadAttrib(id)
10218{
10219 message_begin(MSG_BROADCAST, g_msgScoreAttrib)
10220 write_byte(id) // id
10221 write_byte(0) // attrib
10222 message_end()
10223}
10224
10225// Send Death Message for infections
10226SendDeathMsg(attacker, victim)
10227{
10228 message_begin(MSG_BROADCAST, g_msgDeathMsg)
10229 write_byte(attacker) // killer
10230 write_byte(victim) // victim
10231 write_byte(1) // headshot flag
10232 write_string("infection") // killer's weapon
10233 message_end()
10234}
10235
10236// Update Player Frags and Deaths
10237UpdateFrags(attacker, victim, frags, deaths, scoreboard)
10238{
10239 // Set attacker frags
10240 set_pev(attacker, pev_frags, float(pev(attacker, pev_frags) + frags))
10241
10242 // Set victim deaths
10243 fm_cs_set_user_deaths(victim, cs_get_user_deaths(victim) + deaths)
10244
10245 // Update scoreboard with attacker and victim info
10246 if (scoreboard)
10247 {
10248 message_begin(MSG_BROADCAST, g_msgScoreInfo)
10249 write_byte(attacker) // id
10250 write_short(pev(attacker, pev_frags)) // frags
10251 write_short(cs_get_user_deaths(attacker)) // deaths
10252 write_short(0) // class?
10253 write_short(fm_cs_get_user_team(attacker)) // team
10254 message_end()
10255
10256 message_begin(MSG_BROADCAST, g_msgScoreInfo)
10257 write_byte(victim) // id
10258 write_short(pev(victim, pev_frags)) // frags
10259 write_short(cs_get_user_deaths(victim)) // deaths
10260 write_short(0) // class?
10261 write_short(fm_cs_get_user_team(victim)) // team
10262 message_end()
10263 }
10264}
10265
10266// Remove Player Frags (when Nemesis/Survivor ignore_frags cvar is enabled)
10267RemoveFrags(attacker, victim)
10268{
10269 // Remove attacker frags
10270 set_pev(attacker, pev_frags, float(pev(attacker, pev_frags) - 1))
10271
10272 // Remove victim deaths
10273 fm_cs_set_user_deaths(victim, cs_get_user_deaths(victim) - 1)
10274}
10275
10276// Plays a sound on clients
10277PlaySound(const sound[])
10278{
10279 client_cmd(0, "spk ^"%s^"", sound)
10280}
10281
10282// Prints a colored message to target (use 0 for everyone), supports ML formatting.
10283// Note: I still need to make something like gungame's LANG_PLAYER_C to avoid unintended
10284// argument replacement when a function passes -1 (it will be considered a LANG_PLAYER)
10285zp_colored_print(target, const message[], any:...)
10286{
10287 static buffer[512], i, argscount
10288 argscount = numargs()
10289
10290 // Send to everyone
10291 if (!target)
10292 {
10293 static player
10294 for (player = 1; player <= g_maxplayers; player++)
10295 {
10296 // Not connected
10297 if (!g_isconnected[player])
10298 continue;
10299
10300 // Remember changed arguments
10301 static changed[5], changedcount // [5] = max LANG_PLAYER occurencies
10302 changedcount = 0
10303
10304 // Replace LANG_PLAYER with player id
10305 for (i = 2; i < argscount; i++)
10306 {
10307 if (getarg(i) == LANG_PLAYER)
10308 {
10309 setarg(i, 0, player)
10310 changed[changedcount] = i
10311 changedcount++
10312 }
10313 }
10314
10315 // Format message for player
10316 vformat(buffer, charsmax(buffer), message, 3)
10317
10318 // Send it
10319 message_begin(MSG_ONE_UNRELIABLE, g_msgSayText, _, player)
10320 write_byte(player)
10321 write_string(buffer)
10322 message_end()
10323
10324 // Replace back player id's with LANG_PLAYER
10325 for (i = 0; i < changedcount; i++)
10326 setarg(changed[i], 0, LANG_PLAYER)
10327 }
10328 }
10329 // Send to specific target
10330 else
10331 {
10332 /*
10333 // Not needed since you should set the ML argument
10334 // to the player's id for a targeted print message
10335
10336 // Replace LANG_PLAYER with player id
10337 for (i = 2; i < argscount; i++)
10338 {
10339 if (getarg(i) == LANG_PLAYER)
10340 setarg(i, 0, target)
10341 }
10342 */
10343
10344 // Format message for player
10345 vformat(buffer, charsmax(buffer), message, 3)
10346
10347 // Send it
10348 message_begin(MSG_ONE, g_msgSayText, _, target)
10349 write_byte(target)
10350 write_string(buffer)
10351 message_end()
10352 }
10353}
10354
10355/*================================================================================
10356 [Stocks]
10357=================================================================================*/
10358
10359// Set an entity's key value (from fakemeta_util)
10360stock fm_set_kvd(entity, const key[], const value[], const classname[])
10361{
10362 set_kvd(0, KV_ClassName, classname)
10363 set_kvd(0, KV_KeyName, key)
10364 set_kvd(0, KV_Value, value)
10365 set_kvd(0, KV_fHandled, 0)
10366
10367 dllfunc(DLLFunc_KeyValue, entity, 0)
10368}
10369
10370// Set entity's rendering type (from fakemeta_util)
10371stock fm_set_rendering(entity, fx = kRenderFxNone, r = 255, g = 255, b = 255, render = kRenderNormal, amount = 16)
10372{
10373 static Float:color[3]
10374 color[0] = float(r)
10375 color[1] = float(g)
10376 color[2] = float(b)
10377
10378 set_pev(entity, pev_renderfx, fx)
10379 set_pev(entity, pev_rendercolor, color)
10380 set_pev(entity, pev_rendermode, render)
10381 set_pev(entity, pev_renderamt, float(amount))
10382}
10383
10384// Get entity's speed (from fakemeta_util)
10385stock fm_get_speed(entity)
10386{
10387 static Float:velocity[3]
10388 pev(entity, pev_velocity, velocity)
10389
10390 return floatround(vector_length(velocity));
10391}
10392
10393// Get entity's aim origins (from fakemeta_util)
10394stock fm_get_aim_origin(id, Float:origin[3])
10395{
10396 static Float:origin1F[3], Float:origin2F[3]
10397 pev(id, pev_origin, origin1F)
10398 pev(id, pev_view_ofs, origin2F)
10399 xs_vec_add(origin1F, origin2F, origin1F)
10400
10401 pev(id, pev_v_angle, origin2F);
10402 engfunc(EngFunc_MakeVectors, origin2F)
10403 global_get(glb_v_forward, origin2F)
10404 xs_vec_mul_scalar(origin2F, 9999.0, origin2F)
10405 xs_vec_add(origin1F, origin2F, origin2F)
10406
10407 engfunc(EngFunc_TraceLine, origin1F, origin2F, 0, id, 0)
10408 get_tr2(0, TR_vecEndPos, origin)
10409}
10410
10411// Find entity by its owner (from fakemeta_util)
10412stock fm_find_ent_by_owner(entity, const classname[], owner)
10413{
10414 while ((entity = engfunc(EngFunc_FindEntityByString, entity, "classname", classname)) && pev(entity, pev_owner) != owner) { /* keep looping */ }
10415 return entity;
10416}
10417
10418// Set player's health (from fakemeta_util)
10419stock fm_set_user_health(id, health)
10420{
10421 (health > 0) ? set_pev(id, pev_health, float(health)) : dllfunc(DLLFunc_ClientKill, id);
10422}
10423
10424// Give an item to a player (from fakemeta_util)
10425stock fm_give_item(id, const item[])
10426{
10427 static ent
10428 ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, item))
10429 if (!pev_valid(ent)) return;
10430
10431 static Float:originF[3]
10432 pev(id, pev_origin, originF)
10433 set_pev(ent, pev_origin, originF)
10434 set_pev(ent, pev_spawnflags, pev(ent, pev_spawnflags) | SF_NORESPAWN)
10435 dllfunc(DLLFunc_Spawn, ent)
10436
10437 static save
10438 save = pev(ent, pev_solid)
10439 dllfunc(DLLFunc_Touch, ent, id)
10440 if (pev(ent, pev_solid) != save)
10441 return;
10442
10443 engfunc(EngFunc_RemoveEntity, ent)
10444}
10445
10446// Strip user weapons (from fakemeta_util)
10447stock fm_strip_user_weapons(id)
10448{
10449 static ent
10450 ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "player_weaponstrip"))
10451 if (!pev_valid(ent)) return;
10452
10453 dllfunc(DLLFunc_Spawn, ent)
10454 dllfunc(DLLFunc_Use, ent, id)
10455 engfunc(EngFunc_RemoveEntity, ent)
10456}
10457
10458// Collect random spawn points
10459stock load_spawns()
10460{
10461 // Check for CSDM spawns of the current map
10462 new cfgdir[32], mapname[32], filepath[100], linedata[64]
10463 get_configsdir(cfgdir, charsmax(cfgdir))
10464 get_mapname(mapname, charsmax(mapname))
10465 formatex(filepath, charsmax(filepath), "%s/csdm/%s.spawns.cfg", cfgdir, mapname)
10466
10467 // Load CSDM spawns if present
10468 if (file_exists(filepath))
10469 {
10470 new csdmdata[10][6], file = fopen(filepath,"rt")
10471
10472 while (file && !feof(file))
10473 {
10474 fgets(file, linedata, charsmax(linedata))
10475
10476 // invalid spawn
10477 if(!linedata[0] || str_count(linedata,' ') < 2) continue;
10478
10479 // get spawn point data
10480 parse(linedata,csdmdata[0],5,csdmdata[1],5,csdmdata[2],5,csdmdata[3],5,csdmdata[4],5,csdmdata[5],5,csdmdata[6],5,csdmdata[7],5,csdmdata[8],5,csdmdata[9],5)
10481
10482 // origin
10483 g_spawns[g_spawnCount][0] = floatstr(csdmdata[0])
10484 g_spawns[g_spawnCount][1] = floatstr(csdmdata[1])
10485 g_spawns[g_spawnCount][2] = floatstr(csdmdata[2])
10486
10487 // increase spawn count
10488 g_spawnCount++
10489 if (g_spawnCount >= sizeof g_spawns) break;
10490 }
10491 if (file) fclose(file)
10492 }
10493 else
10494 {
10495 // Collect regular spawns
10496 collect_spawns_ent("info_player_start")
10497 collect_spawns_ent("info_player_deathmatch")
10498 }
10499
10500 // Collect regular spawns for non-random spawning unstuck
10501 collect_spawns_ent2("info_player_start")
10502 collect_spawns_ent2("info_player_deathmatch")
10503}
10504
10505// Collect spawn points from entity origins
10506stock collect_spawns_ent(const classname[])
10507{
10508 new ent = -1
10509 while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", classname)) != 0)
10510 {
10511 // get origin
10512 new Float:originF[3]
10513 pev(ent, pev_origin, originF)
10514 g_spawns[g_spawnCount][0] = originF[0]
10515 g_spawns[g_spawnCount][1] = originF[1]
10516 g_spawns[g_spawnCount][2] = originF[2]
10517
10518 // increase spawn count
10519 g_spawnCount++
10520 if (g_spawnCount >= sizeof g_spawns) break;
10521 }
10522}
10523
10524// Collect spawn points from entity origins
10525stock collect_spawns_ent2(const classname[])
10526{
10527 new ent = -1
10528 while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", classname)) != 0)
10529 {
10530 // get origin
10531 new Float:originF[3]
10532 pev(ent, pev_origin, originF)
10533 g_spawns2[g_spawnCount2][0] = originF[0]
10534 g_spawns2[g_spawnCount2][1] = originF[1]
10535 g_spawns2[g_spawnCount2][2] = originF[2]
10536
10537 // increase spawn count
10538 g_spawnCount2++
10539 if (g_spawnCount2 >= sizeof g_spawns2) break;
10540 }
10541}
10542
10543// Drop primary/secondary weapons
10544stock drop_weapons(id, dropwhat)
10545{
10546 // Get user weapons
10547 static weapons[32], num, i, weaponid
10548 num = 0 // reset passed weapons count (bugfix)
10549 get_user_weapons(id, weapons, num)
10550
10551 // Loop through them and drop primaries or secondaries
10552 for (i = 0; i < num; i++)
10553 {
10554 // Prevent re-indexing the array
10555 weaponid = weapons[i]
10556
10557 if ((dropwhat == 1 && ((1<<weaponid) & PRIMARY_WEAPONS_BIT_SUM)) || (dropwhat == 2 && ((1<<weaponid) & SECONDARY_WEAPONS_BIT_SUM)))
10558 {
10559 // Get weapon entity
10560 static wname[32], weapon_ent
10561 get_weaponname(weaponid, wname, charsmax(wname))
10562 weapon_ent = fm_find_ent_by_owner(-1, wname, id)
10563
10564 // Hack: store weapon bpammo on PEV_ADDITIONAL_AMMO
10565 set_pev(weapon_ent, PEV_ADDITIONAL_AMMO, cs_get_user_bpammo(id, weaponid))
10566
10567 // Player drops the weapon and looses his bpammo
10568 engclient_cmd(id, "drop", wname)
10569 cs_set_user_bpammo(id, weaponid, 0)
10570 }
10571 }
10572}
10573
10574// Stock by (probably) Twilight Suzuka -counts number of chars in a string
10575stock str_count(const str[], searchchar)
10576{
10577 new count, i, len = strlen(str)
10578
10579 for (i = 0; i <= len; i++)
10580 {
10581 if(str[i] == searchchar)
10582 count++
10583 }
10584
10585 return count;
10586}
10587
10588// Checks if a space is vacant (credits to VEN)
10589stock is_hull_vacant(Float:origin[3], hull)
10590{
10591 engfunc(EngFunc_TraceHull, origin, origin, 0, hull, 0, 0)
10592
10593 if (!get_tr2(0, TR_StartSolid) && !get_tr2(0, TR_AllSolid) && get_tr2(0, TR_InOpen))
10594 return true;
10595
10596 return false;
10597}
10598
10599// Check if a player is stuck (credits to VEN)
10600stock is_player_stuck(id)
10601{
10602 static Float:originF[3]
10603 pev(id, pev_origin, originF)
10604
10605 engfunc(EngFunc_TraceHull, originF, originF, 0, (pev(id, pev_flags) & FL_DUCKING) ? HULL_HEAD : HULL_HUMAN, id, 0)
10606
10607 if (get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen))
10608 return true;
10609
10610 return false;
10611}
10612
10613// Simplified get_weaponid (CS only)
10614stock cs_weapon_name_to_id(const weapon[])
10615{
10616 static i
10617 for (i = 0; i < sizeof WEAPONENTNAMES; i++)
10618 {
10619 if (equal(weapon, WEAPONENTNAMES[i]))
10620 return i;
10621 }
10622
10623 return 0;
10624}
10625
10626// Get User Current Weapon Entity
10627stock fm_cs_get_current_weapon_ent(id)
10628{
10629 return get_pdata_cbase(id, OFFSET_ACTIVE_ITEM, OFFSET_LINUX);
10630}
10631
10632// Get Weapon Entity's Owner
10633stock fm_cs_get_weapon_ent_owner(ent)
10634{
10635 return get_pdata_cbase(ent, OFFSET_WEAPONOWNER, OFFSET_LINUX_WEAPONS);
10636}
10637
10638// Set User Deaths
10639stock fm_cs_set_user_deaths(id, value)
10640{
10641 set_pdata_int(id, OFFSET_CSDEATHS, value, OFFSET_LINUX)
10642}
10643
10644// Get User Team
10645stock fm_cs_get_user_team(id)
10646{
10647 return get_pdata_int(id, OFFSET_CSTEAMS, OFFSET_LINUX);
10648}
10649
10650// Set a Player's Team
10651stock fm_cs_set_user_team(id, team)
10652{
10653 set_pdata_int(id, OFFSET_CSTEAMS, team, OFFSET_LINUX)
10654}
10655
10656// Set User Money
10657stock fm_cs_set_user_money(id, value)
10658{
10659 set_pdata_int(id, OFFSET_CSMONEY, value, OFFSET_LINUX)
10660}
10661
10662// Set User Flashlight Batteries
10663stock fm_cs_set_user_batteries(id, value)
10664{
10665 set_pdata_int(id, OFFSET_FLASHLIGHT_BATTERY, value, OFFSET_LINUX)
10666}
10667
10668// Update Player's Team on all clients (adding needed delays)
10669stock fm_user_team_update(id)
10670{
10671 static Float:current_time
10672 current_time = get_gametime()
10673
10674 if (current_time - g_teams_targettime >= 0.1)
10675 {
10676 set_task(0.1, "fm_cs_set_user_team_msg", id+TASK_TEAM)
10677 g_teams_targettime = current_time + 0.1
10678 }
10679 else
10680 {
10681 set_task((g_teams_targettime + 0.1) - current_time, "fm_cs_set_user_team_msg", id+TASK_TEAM)
10682 g_teams_targettime = g_teams_targettime + 0.1
10683 }
10684}
10685
10686// Send User Team Message
10687public fm_cs_set_user_team_msg(taskid)
10688{
10689 // Note to self: this next message can now be received by other plugins
10690
10691 // Set the switching team flag
10692 g_switchingteam = true
10693
10694 // Tell everyone my new team
10695 emessage_begin(MSG_ALL, g_msgTeamInfo)
10696 ewrite_byte(ID_TEAM) // player
10697 ewrite_string(CS_TEAM_NAMES[fm_cs_get_user_team(ID_TEAM)]) // team
10698 emessage_end()
10699
10700 // Done switching team
10701 g_switchingteam = false
10702}
10703
10704// Set the precached model index (updates hitboxes server side)
10705stock fm_cs_set_user_model_index(id, value)
10706{
10707 set_pdata_int(id, OFFSET_MODELINDEX, value, OFFSET_LINUX)
10708}
10709
10710// Set Player Model on Entity
10711stock fm_set_playermodel_ent(id)
10712{
10713 // Make original player entity invisible without hiding shadows or firing effects
10714 fm_set_rendering(id, kRenderFxNone, 255, 255, 255, kRenderTransTexture, 1)
10715
10716 // Format model string
10717 static model[100]
10718 formatex(model, charsmax(model), "models/player/%s/%s.mdl", g_playermodel[id], g_playermodel[id])
10719
10720 // Set model on entity or make a new one if unexistant
10721 if (!pev_valid(g_ent_playermodel[id]))
10722 {
10723 g_ent_playermodel[id] = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
10724 if (!pev_valid(g_ent_playermodel[id])) return;
10725
10726 set_pev(g_ent_playermodel[id], pev_classname, MODEL_ENT_CLASSNAME)
10727 set_pev(g_ent_playermodel[id], pev_movetype, MOVETYPE_FOLLOW)
10728 set_pev(g_ent_playermodel[id], pev_aiment, id)
10729 set_pev(g_ent_playermodel[id], pev_owner, id)
10730 }
10731
10732 engfunc(EngFunc_SetModel, g_ent_playermodel[id], model)
10733}
10734
10735// Set Weapon Model on Entity
10736stock fm_set_weaponmodel_ent(id)
10737{
10738 // Get player's p_ weapon model
10739 static model[100]
10740 pev(id, pev_weaponmodel2, model, charsmax(model))
10741
10742 // Set model on entity or make a new one if unexistant
10743 if (!pev_valid(g_ent_weaponmodel[id]))
10744 {
10745 g_ent_weaponmodel[id] = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
10746 if (!pev_valid(g_ent_weaponmodel[id])) return;
10747
10748 set_pev(g_ent_weaponmodel[id], pev_classname, WEAPON_ENT_CLASSNAME)
10749 set_pev(g_ent_weaponmodel[id], pev_movetype, MOVETYPE_FOLLOW)
10750 set_pev(g_ent_weaponmodel[id], pev_aiment, id)
10751 set_pev(g_ent_weaponmodel[id], pev_owner, id)
10752 }
10753
10754 engfunc(EngFunc_SetModel, g_ent_weaponmodel[id], model)
10755}
10756
10757// Remove Custom Model Entities
10758stock fm_remove_model_ents(id)
10759{
10760 // Remove "playermodel" ent if present
10761 if (pev_valid(g_ent_playermodel[id]))
10762 {
10763 engfunc(EngFunc_RemoveEntity, g_ent_playermodel[id])
10764 g_ent_playermodel[id] = 0
10765 }
10766 // Remove "weaponmodel" ent if present
10767 if (pev_valid(g_ent_weaponmodel[id]))
10768 {
10769 engfunc(EngFunc_RemoveEntity, g_ent_weaponmodel[id])
10770 g_ent_weaponmodel[id] = 0
10771 }
10772}
10773
10774// Set User Model
10775public fm_cs_set_user_model(taskid)
10776{
10777 set_user_info(ID_MODEL, "model", g_playermodel[ID_MODEL])
10778}
10779
10780// Get User Model -model passed byref-
10781stock fm_cs_get_user_model(player, model[], len)
10782{
10783 get_user_info(player, "model", model, len)
10784}
10785
10786// Update Player's Model on all clients (adding needed delays)
10787public fm_user_model_update(taskid)
10788{
10789 static Float:current_time
10790 current_time = get_gametime()
10791
10792 if (current_time - g_models_targettime >= g_modelchange_delay)
10793 {
10794 fm_cs_set_user_model(taskid)
10795 g_models_targettime = current_time
10796 }
10797 else
10798 {
10799 set_task((g_models_targettime + g_modelchange_delay) - current_time, "fm_cs_set_user_model", taskid)
10800 g_models_targettime = g_models_targettime + g_modelchange_delay
10801 }
10802}