Raised This Month: $ Target: $400
 0% 

[EDIT] ZP 3.59 Main Function


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
sl4y62
Junior Member
Join Date: Dec 2009
Old 05-16-2012 , 13:53   [EDIT] ZP 3.59 Main Function
Reply With Quote #1

Hello, I'm thinking about getting the main function of ZP 3:59 and "paste" in a new sma to create a style "Infection HideNSeek"
Main Events:
[/QUOTE]here the plugin zombie_plague35.sma

or
Code:
/*================================================================================
    
        ***********************************************
        ********** [Zombie Plague Mod 3.5] ************
        ***********************************************
    
    (c) Copyright 2008 by MeRcyLeZZ
    
    ----------------------
    -*- Licensing Info -*-
    ----------------------
    
    This file is provided "as-is" and comes with no warranties of any kind.
    
    You are free to use, redistribute and/or modify this plugin to your
    liking with the following restrictions:
    
    * You cannot alter/remove/replace the register_plugin() call
    * You cannot alter/remove/replace any credits info
    * You cannot alter/remove/replace this license
    
    -------------------
    -*- Description -*-
    -------------------
    
    Zombie Plague is a Counter-Strike plugin developed under AMX Mod X, in
    where humans and zombies are confronted. It is based on the original
    infection mod, but it takes the concept to a new level by introducing:
    
    * New Gameplay Modes: Nemesis, Survivor, Multiple Infection, Swarm Mode
    * Zombie Classes: a total of six, each with his own strengths
    * Ammo Packs: awarded to skilled players, can be used to purchase goods
    * Custom Grenades: Fire, Frost, Flare, and Infection Bomb
    * Deathmatch Mode: players may respawn as zombies, humans, or randomly
    * Admin commands: can be performed through an easy ingame menu
    * Special GFX Effects: from the HL Engine
    * Customizable Models and Sounds
    
    It also brings many customization through CVARS:
    
    * Change nightvision/flashlight colors and size
    * Adjust in-game lightning (thunderclaps available on pitch black mode)
    * Set zombies/humans health, speed, gravity, ammo pack rewards, and more
    * Enable unlimited ammo or knockback for weapons
    * Enable random spawning (CSDM-spawns friendly)
    * Separately enable and customize the new gameplay modes to your liking
    
    ---------------
    -*- History -*-
    ---------------
    
    As only few people may know, I started this project back on December
    2007, when the free infection mods around were quite buggy. When I first
    decided to remake one of those, I came across many problems and realized
    I had little to no experience at AMXX scripting, and therefore, agreed
    that the best way to get it done would be starting from scratch.
    
    Not after spending over a week looking at many plugins (mainly Zombie
    Swarm) and scarce tutorials, I managed to have all the basic stuff
    working quite well (even though the code itself was really messy). The
    following months were spent polishing things up and trying to fulfill new
    original ideas.
    
    In the meantime I had the chance to try the plugin out on a 32 man server.
    This meant a huge progress on development, we ran into lots of testing
    and fixing server crashes, but finally this turned out to be more than
    the simple infection mod I had originally planned it to be.
    
    The reason this project was kept private for such a long time was not
    only for its uniqueness, but because I felt like it wasn't ready to take
    the next step yet. However, the plugin has come a long way now. I've
    spent the last days improving and commenting the source code, adding as
    many customization as I could, and I'm glad to say I'm finally making it
    freely available.
    
    The only thing I ask for return is to keep my name in the plugin.
    
    -Enjoy!
    
    -------------
    -*- Media -*-
    -------------
    
    * Gameplay Video 1: http://www.youtube.com/watch?v=HFUyF7-_uzw
    * Gameplay Video 2: http://www.youtube.com/watch?v=XByif6Mti-w
    
    --------------------
    -*- Requirements -*-
    --------------------
    
    * Mods: Counter-Strike 1.6 or Condition-Zero
    * AMXX: version 1.8.0 (build 3660) or higher
    * Modules: FakeMeta, HamSandwich
    
    --------------------
    -*- Installation -*-
    --------------------
    
    Just extract all the contents from the .zip file to your server's mod
    directory ("cstrike" or "czero"). Make sure to keep folder structure.
    
    -------------------------------
    -*- CVARS and Customization -*-
    -------------------------------
    
    For a complete and in-depth cvar list, look at the zombieplague.cfg file
    located in the amxmodx\configs directory.
    
    Additionally, you can change player models, sounds, weather effects,
    and some other stuff by looking at the beginning of the .sma for the
    "Plugin Customization" section. Once you're done making your changes,
    remember to recompile!
    
    ----------------------
    -*- Admin Commands -*-
    ----------------------
    
    The following console commands are available:
    
    * zp_zombie <name> - Turn someone into a Zombie
    * zp_human <name> - Turn someone back to Human
    * zp_nemesis <name> - Turn someone into a Nemesis (*)
    * zp_survivor <name> - Turn someone into a Survivor (*)
    * zp_respawn <name> - Respawn someone
    * zp_swarm - Start Swarm Mode (*)
    * zp_multi - Start Multi Infection (*)
    
    (*) - These commands can only be used at round start, that is, when
    the T-Virus notice is shown on screen.
    
    -------------------
    -*- InGame Menu -*-
    -------------------
    
    Players can access the mod menu by typing "zpmenu" in the chat, or by
    pressing the "M" (chooseteam) key. The menu allows players to choose
    their zombie class, buy extra items, get unstuck, or see the ingame
    help. Admins will find an additional option to easily perform any
    of the console commands.
    
    ----------------------
    -*- Infection Mode -*-
    ----------------------
    
    On every round players start out as humans, equip themselves with a few
    weapons and grenades, and head to the closest cover they find, knowing
    that one of them is infected with the T-Virus, and will suddenly turn
    into a vicious brain eating creature.
    
    Only little time after, the battle for survival begins. The first zombie
    has to infect as many humans as possible to cluster a numerous zombie
    horde and take over the world.
    
    Maps are set in the dark by default. Humans must use flashlights to
    light their way and spot any enemies. Zombies, on the other hand, have
    night vision but can only attack melee.
    
    --------------------------
    -*- New Gameplay Modes -*-
    --------------------------
    
    * Nemesis:
       The first zombie may turn into a Nemesis, a powerful fast-moving
       beast. His goal is to kill every human while sustaining the gunfire.
    
    * Survivor:
       Everyone became a zombie except him. The survivor gets a machinegun
       with unlimited ammo and has to stop the never-ending army of undead.
    
    * Multiple Infection:
       The round starts with many humans infected, so the remaining players
       will have to act quickly in order to control the situation.
    
    * Swarm Mode:
       Half of the players turn into zombies, the rest become immune and
       cannot be infected. It's a battle to death.
    
    ----------------------
    -*- Zombie Classes -*-
    ----------------------
    
    There are six zombie classes by default:
    
    * Classic Zombie: well balanced zombie for beginners.
    * Raptor Zombie: fast moving zombie, but also the weakest.
    * Poison Zombie: light weighed zombie, jumps higher.
    * Big Zombie: slow but strong zombie, with lots of hit points.
    * Leech Zombie: regains the most health when infecting.
    * Rage Zombie: emits a radioactive green glow.
    
    -------------------
    -*- Extra Items -*-
    -------------------
    
    Besides weapons, players can buy the following items:
    
    * Night Vision: makes you able to see in the dark, lasts a single round.
    * T-Virus Antidote: makes you turn back to your human form.
    * Zombie Madness: you develop a powerful shield for a short time.
    * Infection Bomb: infects anyone within its explosion radius.
    
    --------------------
    -*- Contact Info -*-
    --------------------
    
    For the official Zombie Plague thread visit:
    http://forums.alliedmods.net/showthread.php?t=72505
    
    For personal contact you can send me an email at:
    [email protected]
    
    ---------------
    -*- Credits -*-
    ---------------
    
    * AMXX Dev Team: for all the hard work which made this possible
    * Imperio LNJ Community: for providing the first server where I
       could really test the plugin and for everyone's support
    * Mini_Midget: for his Zombie Swarm plugin which I used for reference
       on earliest stages of development
    * Avalanche: for the random spawning code I got from GunGame and the
       original Frostnades concept that I ported in here
    * cheap_suit: for some modelchange and knockback codes that I got from
       Biohazard
    * Simon Logic: for the Pain Shock Free feature
    * KRoT@L: for some code from Follow the Wounded, used to make the zombie
       bleeding feature
    * VEN: for Fakemeta Utilities and some useful stocks
    * RaaPuar and Goltark: for the custom grenade models
    * ML Translations: DKs (BP), JahMan (PL), DA (DE)
    * Everyone who enjoys killing zombies!
    
    ------------------
    -*- Change Log -*-
    ------------------
    
    * till 1.0: (Dec 2007)
       - First Release: most of the basic stuff done.
       - Added: random spawning, HP display on hud, lightning setting,
          simple buy menu, custom nightvision, admin commands, Nemesis
          and Survivor modes, glow and leap settings for them.
    
    * till 2.2: (Jan 2008)
       - Added: zombie classes, ammo packs system, buying ammo for weapons,
          custom flashlight, admin skins setting, zombieplague.cfg file
       - Upgraded: weapons menu improved, flashlight and nightvision colors
          now customizable, HamSandwich module used to handle damage.
       - Fixed various bugs.
    
    * till 3.0: (Mar 2008)
       - Added: door removal setting, unstuck feature, human cvars, armor
          cvar for zombies, weapon knockback, zombie bleeding, flares,
          extra items (weapons, antidote, infection bomb), Pain Shock
          Free setting, Multiple Infection and Swarm modes.
       - Upgraded: dumped Engine, Fun and Cstrike modules, code optimized,
          new model change method, new gfx effects for zombie infections.
       - Fixed a bunch of gameplay bugs.
    
    * till 3.5: (May 2008)
       - Added: deathmatch setting with spawn protection, unlimited ammo
          setting, fire and frost grenades, additional customization cvars,
          new extra items, help menu.
       - Upgraded: better objectives removal method, dropped weapons now
          keep their bpammo, code optimized a lot.
       - Fixed: no more game commencing bug when last zombie/human leaves,
          no more hegrenade infection bug, reduced svc_bad errors, and
          many more.

=================================================================================*/

#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <hamsandwich>
#include <xs>

/*================================================================================
 [Plugin Customization]
=================================================================================*/

// Access Flag Required to use Admin Commands
const ACCESS_FLAG = ADMIN_BAN

// Player Models (randomly chosen, add as many as you want) -32 chars max per model-
new const model_zombie_class1[][] = { "zombie_source" } // Zombie (Classic)
new const model_zombie_class2[][] = { "zombie_source" } // Zombie (Raptor)
new const model_zombie_class3[][] = { "zombie_source" } // Zombie (Poison)
new const model_zombie_class4[][] = { "zombie_source" } // Zombie (Fat)
new const model_zombie_class5[][] = { "zombie_source" } // Zombie (Leech)
new const model_zombie_class6[][] = { "zombie_source" } // Zombie (Rage)
new const model_nemesis[][] = { "zombie_source" } // Nemesis
new const model_human[][] = { "arctic", "guerilla", "leet", "terror", "gign", "gsg9", "sas", "urban" } // Human
new const model_admin[][] = { "vip" } // Admin

// Custom Weapon Models
new const model_vknife_zombie[] = { "models/zombie_plague/v_knife_zombie.mdl" }
new const model_pknife_zombie[] = { "" }
new const model_vknife_nemesis[] = { "models/zombie_plague/v_knife_zombie.mdl" }
new const model_pknife_nemesis[] = { "" }
new const model_grenade_infect[] = { "models/zombie_plague/v_grenade_infect.mdl" }
new const model_grenade_fire[] = { "models/zombie_plague/v_grenade_fire.mdl" }
new const model_grenade_frost[] = { "models/zombie_plague/v_grenade_frost.mdl" }
new const model_grenade_flare[] = { "models/zombie_plague/v_grenade_flare.mdl" }

// Sound list (randomly chosen, add as many as you want)
new const sound_win_zombies[][] = { "ambience/the_horror1.wav", "ambience/the_horror3.wav", "ambience/the_horror4.wav" }
new const sound_win_humans[][] = { "zombie_plague/win_humans1.wav", "zombie_plague/win_humans2.wav" }
new const sound_win_no_one[][] = { "" }
new const zombie_infect[][] = { "zombie_plague/zombie_infec1.wav", "zombie_plague/zombie_infec2.wav", "zombie_plague/zombie_infec3.wav", "scientist/c1a0_sci_catscream.wav", "scientist/scream01.wav" }
new const zombie_pain[][] = { "zombie_plague/zombie_pain1.wav", "zombie_plague/zombie_pain2.wav", "zombie_plague/zombie_pain3.wav", "zombie_plague/zombie_pain4.wav", "zombie_plague/zombie_pain5.wav" }
new const nemesis_pain[][] = { "zombie_plague/nemesis_pain1.wav", "zombie_plague/nemesis_pain2.wav", "zombie_plague/nemesis_pain3.wav" }
new const zombie_die[][] = { "zombie_plague/zombie_die1.wav", "zombie_plague/zombie_die2.wav", "zombie_plague/zombie_die3.wav", "zombie_plague/zombie_die4.wav", "zombie_plague/zombie_die5.wav" }
new const zombie_fall[][] = { "zombie_plague/zombie_fall1.wav" }
new const zombie_idle[][] = { "nihilanth/nil_now_die.wav", "nihilanth/nil_slaves.wav", "nihilanth/nil_alone.wav", "zombie_plague/zombie_brains1.wav", "zombie_plague/zombie_brains2.wav" }
new const zombie_idle_last[][] = { "nihilanth/nil_thelast.wav" }
new const zombie_madness[][] = { "zombie_plague/zombie_madness1.wav" }
new const sound_nemesis[][] = { "zombie_plague/nemesis1.wav", "zombie_plague/nemesis2.wav" }
new const sound_survivor[][] = { "zombie_plague/survivor1.wav", "zombie_plague/survivor2.wav" }
new const sound_swarm[][] = { "ambience/the_horror2.wav" }
new const sound_multi[][] = { "ambience/the_horror2.wav" }
new const grenade_infect[][] = { "zombie_plague/grenade_infect.wav" }
new const grenade_infect_player[][] = { "scientist/scream20.wav", "scientist/scream22.wav", "scientist/scream05.wav" }
new const grenade_fire[][] = { "zombie_plague/grenade_explode.wav" }
new const grenade_fire_player[][] = { "zombie_plague/zombie_burn3.wav","zombie_plague/zombie_burn4.wav","zombie_plague/zombie_burn5.wav","zombie_plague/zombie_burn6.wav","zombie_plague/zombie_burn7.wav" }
new const grenade_frost[][] = { "warcraft3/frostnova.wav" }
new const grenade_frost_player[][] = { "warcraft3/impalehit.wav" }
new const grenade_frost_break[][] = { "warcraft3/impalelaunch1.wav" }
new const grenade_flare[][] = { "items/nvg_on.wav" }
new const sound_antidote[][] = { "items/smallmedkit1.wav" }
new const sound_thunder[][] = { "zombie_plague/thunder1.wav", "zombie_plague/thunder2.wav" }

// Uncomment the following line to enable ambience sounds
//#define AMBIENCE_SOUNDS

#if defined AMBIENCE_SOUNDS // Ambience Sounds List (only .wav and .mp3 formats supported)
new const sound_ambience[][] = { "zombie_plague/ambience.wav" }
new const Float:sound_ambience_duration[] = { 17.0 } // duration in seconds
#endif

// Buy Menu: Primary Weapons (9 max)
new const g_primary_names[][] = { "SG-552 Commando", "Steyr AUG A1", "M4A1 Carbine", "AK-47 Kalashnikov", "IMI Galil", "Famas", "M3 Super 90", "XM1014 M4", "MP5 Navy" }
new const g_primary_classes[][] = { "Rifle", "Rifle", "Rifle", "Rifle", "Rifle", "Rifle", "Shotgun", "Shotgun", "SMG" }
new const g_primary_items[][] = { "weapon_sg552", "weapon_aug", "weapon_m4a1", "weapon_ak47", "weapon_galil", "weapon_famas", "weapon_m3", "weapon_xm1014", "weapon_mp5navy" }

// Buy Menu: Secondary Weapons (9 max)
new const g_secondary_names[][] = { "Glock 18C", "USP .45 ACP Tactical", "P228", "Desert Eagle .50 AE", "FiveseveN", "Dual 96G Elite Berettas" }
new const g_secondary_classes[][] = { "Pistol", "Pistol", "Pistol", "Pistol", "Pistol", "Pistol" }
new const g_secondary_items[][] = { "weapon_glock18", "weapon_usp", "weapon_p228", "weapon_deagle", "weapon_fiveseven", "weapon_elite" }

// Additional Items to give after buying all weapons (e.g. grenades)
new const g_additional_items[][] = { "weapon_hegrenade", "weapon_flashbang", "weapon_smokegrenade" }

// Extra Items: Weapons and their costs (5 max)
new const g_extra_names[][] = { "Fire Grenade", "AWP Sniper", "M249 Machinegun", "SG550 Auto-Sniper", "G3SG1 Auto-Sniper" }
new const g_extra_items[][] = { "weapon_hegrenade", "weapon_awp", "weapon_m249", "weapon_sg550", "weapon_g3sg1" }
new const g_extra_costs[] = { 6, 8, 10, 12, 12 }

// Extra Items: costs for Night Vision, Antidote, Zombie Madness, and Infection Bomb
new const g_extra_costs2[] = { 15, 15, 17, 20 }

// Weather Effects: uncomment a line to have the desired effect
//#define AMBIENCE_RAIN // Rain
//#define AMBIENCE_SNOW // Snow
//#define AMBIENCE_FOG // Fog

#if defined AMBIENCE_FOG // Fog Customization (if enabled)
new const FOG_DENSITY[] = "0.0018" // Density
new const FOG_COLOR[] = "128 128 128" // Color: Red Green Blue
#endif

// Sky Names (randomly chosen, add as many as you want)
new const skynames[][] = { "space" }

// Thunderclap Lightning Cycles
new const lights_thunder1[][] = { "i" ,"j", "k", "l", "m", "n", "o", "n", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"}
new const lights_thunder2[][] = { "k", "l", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a", "a", "b", "c", "d", "e", "d", "c", "b", "a"}
new const lights_thunder3[][] = { "b", "c", "d", "e", "f", "e", "d", "c", "i" ,"j", "k", "l", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"}

// Decal List for Zombie Bloodstains/Footsteps
new const zombie_decals[] = { 99, 107, 108, 184, 185, 186, 187, 188, 189 }

// Multipliers for zombie classes as follows: (percents)
// Classic, Raptor, Poison, Fat, Leech, Rage
new const zombie_multihp[] = { 100, 50, 75, 150, 75, 125 }
new const zombie_multispd[] = { 100, 120, 100, 80, 100, 110 }
new const zombie_multigrav[] = { 100, 100, 75, 100, 100, 87 }
new const zombie_multiknockback[] = { 100, 150, 125, 50, 125, 100 }

// Knockback Power values for all weapons
// Note: to disable knockback power on a specific weapon use a negative value
new const Float:kb_weapon_power[] = 
{
    -1.0,    // ---
    2.4,    // P228
    -1.0,    // ---
    6.5,    // SCOUT
    -1.0,    // ---
    8.0,    // XM1014
    -1.0,    // ---
    2.3,    // MAC10
    5.0,    // AUG
    -1.0,    // ---
    2.4,    // ELITE
    2.0,    // FIVESEVEN
    2.4,    // UMP45
    5.3,    // SG550
    5.5,    // GALIL
    5.5,    // FAMAS
    2.2,    // USP
    2.0,    // GLOCK18
    10.0,    // AWP
    2.5,    // MP5NAVY
    5.2,    // M249
    8.0,    // M3
    5.0,    // M4A1
    2.4,    // TMP
    6.5,    // G3SG1
    -1.0,    // ---
    5.3,    // DEAGLE
    5.0,    // SG552
    6.0,    // AK47
    -1.0,    // ---
    2.0    // P90
}

// Objective entites and anything that would affect plugin gameplay
new const g_objective_ents[][] = { "func_bomb_target", "info_bomb_target", "info_vip_start", "func_vip_safetyzone", "func_escapezone", "hostage_entity",
        "monster_scientist", "func_hostage_rescue", "info_hostage_rescue", "env_fog", "env_rain", "env_snow", "func_vehicle", "item_longjump" }

// ***************************************************************
// *** If you experience many SVC_BAD kicks, try the following ***
// ***************************************************************
// 1. Increase the delay between model changes here: (eg. set it to 0.5)
const Float:MODELCHANGE_DELAY = 0.2
// 2. If the above doesn't help, uncomment the following line:
//#define HANDLE_MODELS_ON_SEPARATE_ENT // experimental, uses more CPU

// ---------------------------------------------------------------
// ------------------ Customization ends here!! ------------------
// ---------------------------------------------------------------

/*================================================================================
 [Offsets and Constants]
=================================================================================*/

// Plugin Version
new const PLUGIN_VERSION[] = "3.59"

// Task offsets
const MODEL_TASK = 2000
const TEAM_TASK = 2100
const SPAWN_TASK = 2200
const BLOOD_TASK = 2300
const NVISION_TASK = 2400
const CHARGE_TASK = 2500
const SHOWHUD_TASK = 2600
const NADES_TASK = 2700
const MAKEZOMBIE_TASK = 2800
const WELCOMEMSG_TASK = 2900
const THUNDER_PRE_TASK = 3000
const THUNDER_TASK = 3100

// IDs inside tasks
#define ID_MODEL (taskid - MODEL_TASK)
#define ID_TEAM (taskid - TEAM_TASK)
#define ID_SPAWN (taskid - SPAWN_TASK)
#define ID_BLOOD (taskid - BLOOD_TASK)
#define ID_NVISION (taskid - NVISION_TASK)
#define ID_CHARGE (taskid - CHARGE_TASK)
#define ID_SHOWHUD (taskid - SHOWHUD_TASK)

// For player list menu handlers
#define PL_STARTID (g_menu_item[id][0])
#define PL_ACTION (g_menu_item[id][1])
#define PL_TARGET (key+g_menu_item[id][0]+1)

// Hard coded extra items
enum
{
    EXTRA_NVISION = 0,
    EXTRA_ANTIDOTE,
    EXTRA_MADNESS,
    EXTRA_INFBOMB
}

// CS Offsets
#if cellbits == 32
const OFFSET_CSTEAMS = 114
const OFFSET_CSMONEY = 115
const OFFSET_NVGOGGLES = 129
const OFFSET_ZOOMTYPE = 363
const OFFSET_CSDEATHS = 444
const OFFSET_AWM_AMMO  = 377 
const OFFSET_SCOUT_AMMO = 378
const OFFSET_PARA_AMMO = 379
const OFFSET_FAMAS_AMMO = 380
const OFFSET_M3_AMMO = 381
const OFFSET_USP_AMMO = 382
const OFFSET_FIVESEVEN_AMMO = 383
const OFFSET_DEAGLE_AMMO = 384
const OFFSET_P228_AMMO = 385
const OFFSET_GLOCK_AMMO = 386
const OFFSET_FLASH_AMMO = 387
const OFFSET_HE_AMMO = 388
const OFFSET_SMOKE_AMMO = 389
const OFFSET_C4_AMMO = 390
const OFFSET_CLIPAMMO = 51
#else
const OFFSET_CSTEAMS = 139
const OFFSET_CSMONEY = 140
const OFFSET_NVGOGGLES = 155
const OFFSET_ZOOMTYPE = 402
const OFFSET_CSDEATHS = 493
const OFFSET_AWM_AMMO  = 426
const OFFSET_SCOUT_AMMO = 427
const OFFSET_PARA_AMMO = 428
const OFFSET_FAMAS_AMMO = 429
const OFFSET_M3_AMMO = 430
const OFFSET_USP_AMMO = 431
const OFFSET_FIVESEVEN_AMMO = 432
const OFFSET_DEAGLE_AMMO = 433
const OFFSET_P228_AMMO = 434
const OFFSET_GLOCK_AMMO = 435
const OFFSET_FLASH_AMMO = 46
const OFFSET_HE_AMMO = 437
const OFFSET_SMOKE_AMMO = 438
const OFFSET_C4_AMMO = 439
const OFFSET_CLIPAMMO = 65
#endif
const OFFSET_LINUX = 5 // offsets 5 higher in Linux builds
const OFFSET_LINUX_WEAPONS = 4 // weapon offsets are only 4 steps higher on Linux

// CS Teams
enum
{
    CS_TEAM_UNASSIGNED = 0,
    CS_TEAM_T,
    CS_TEAM_CT,
    CS_TEAM_SPECTATOR
}

// Other consts
const HIDE_MONEY = (1<<5)
const ATTRIB_BOMB = (1<<1)
const DMG_HEGRENADE = (1<<24)
const CS_NO_ZOOM = 0x5A

// Max BP ammo for weapons
new const MAXAMMO[] = { -1, 52, -1, 90, -1, 32, 1, 100, 90, -1, 120, 100, 100, 90, 90, 90, 100, 120,
            30, 120, 200, 32, 90, 120, 90, -1, 35, 90, 90, -1, 100 }

// Max Clip for weapons
new const MAXCLIP[] = { -1, 13, -1, 10, 1, 7, -1, 30, 30, 1, 30, 20, 25, 30, 35, 25, 12, 20,
            10, 30, 100, 8, 30, 30, 20, 2, 7, 30, 30, -1, 50 }

// Amount of ammo to give when buying additional clips for weapons
new const BUYAMMO[] = { -1, 13, -1, 30, -1, 8, -1, 12, 30, -1, 30, 50, 12, 30, 30, 30, 12, 30,
            10, 30, 30, 8, 30, 30, 30, -1, 7, 30, 30, -1, 50 }

// Weapon bitsums
const 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))
const SECONDARY_WEAPONS_BIT_SUM = ((1<<CSW_P228)|(1<<CSW_ELITE)|(1<<CSW_FIVESEVEN)|(1<<CSW_USP)|(1<<CSW_GLOCK18)|(1<<CSW_DEAGLE))

// Menu keys
const KEYSMENU = (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)

/*================================================================================
 [Global Variables]
=================================================================================*/

// Stupid compiler
#pragma unused g_models_i

// Player vars
new g_zombie[33] // is zombie
new g_nemesis[33] // is nemesis
new g_survivor[33] // is surivor
new g_lastzombie[33] // is last zombie
new g_lasthuman[33] // is last human
new g_frozen[33] // is frozen (can't move)
new g_nodamage[33] // has spawn protection/zombie madness
new g_respawn_as_zombie[33] // should respawn as zombie
new g_nvision[33] // has night vision
new g_nvisionenabled[33] // has night vision turned on
new g_zombieclass[33] // zombie class
new g_zombieclassnext[33] // zombie class for next infection
new g_flashlight[33] // has custom flashlight turned on
new g_flashbattery[33] = { 100, ... } // custom flashlight battery
new g_currentweapon[33] // current weapon the player is holding
new g_canbuy[33] // is allowed to buy a new weapon through the menu
new g_ammopacks[33] // ammo pack count
new g_damagedealt[33] // damage dealt to zombies (used to calculate ammo packs reward)
new g_spec_target[33] // last spectator target (for game nvg fix)
new g_spec_first_person[33] // was player spectating on first person (for game nvg fix)
new g_restorevel[33], Float:g_velocity[33][3] // Pain Shock Free vars
new g_switchingteam[33] // is switching team
new g_playermodel[33][33] // current model's short name [player][model]

// Game vars
new g_newround // new round starting
new g_endround // round ended
new g_nemround // nemesis round
new g_survround // survivor round
new g_swarmround // swarm round
new g_scorezombies, g_scorehumans // team scores
new g_spawnCount // available spawn points count
new Float:g_spawns[128][3] // spawn points data
new g_lights_i // thunderclap lightning cycle counter
new Float:g_models_i // delay between Model Change messages
new Float:g_teams_i // delay between Team Change messages
new g_MsgSync, g_MsgSync2 // message sync objects
new g_trailSpr, g_exploSpr, g_flameSpr, g_glassSpr // grenade sprites
new g_modname[32]
new g_maxplayers // max players count
new g_czero // whether we are running on a CZ server
new g_hamczbots // whether ham forwards are registered for CZ bots
new g_fwSpawn // spawn forward handle
new g_menu_item[33][2] // starting indices for player list menus

// Database vars (used to save players stats in case they get disconnected)
new db_name[100][64] // player name [slot id][player name]
new db_ammopacks[100] // ammo pack count [slot id]
new db_zombieclass[100] // zombie class [slot id]
new db_slot_i // additional saved slots count (should start on maxplayers+1)

// Message IDs vars
new g_msgScoreInfo, g_msgSetFOV, g_msgScreenFade, g_msgDeathMsg, g_msgScoreAttrib, g_msgAmmoPickup,
g_msgNVGToggle, g_msgFlashlight, g_msgFlashBat, g_msgTeamInfo, g_msgSendAudio

// CVAR pointers
new cvar_hp, cvar_firsthp, cvar_speed, cvar_lightning, cvar_gravity, cvar_removemoney,
cvar_thunder, cvar_fov_angle, cvar_bonushp, cvar_cflash,  cvar_knockback, cvar_survleap,
cvar_nem, cvar_nemchance, cvar_nemhp, cvar_nemglow,  cvar_surv, cvar_cnvg, cvar_nemleap,
cvar_nemgravity, cvar_flashsize, cvar_survglow, cvar_ammodamage, cvar_armor, cvar_dropwpn,
cvar_nempainfree, cvar_nemspd, cvar_survchance, cvar_survhp, cvar_survspd, cvar_humanspd,
cvar_swarmchance, cvar_flashdrain, cvar_bleeding, cvar_removedoors, cvar_survpainfree,
cvar_randspawn, cvar_multi, cvar_multichance, cvar_infammo, cvar_swarm, cvar_ammoinfect,
cvar_painfree, cvar_knockbackpower, cvar_freezeduration, cvar_triggered, cvar_deathmatch,
cvar_firegrenades, cvar_frostgrenades, cvar_survgravity, cvar_humanhp, cvar_logcommands,
cvar_humangravity, cvar_spawnprotection,  cvar_nvgsize,cvar_flareduration, cvar_zclasses,
cvar_extraitems, cvar_showactivity, cvar_humanlasthp, cvar_nemignorefrags, cvar_warmup,
cvar_flashdist, cvar_flarecolor, cvar_survignorefrags, cvar_fireduration, cvar_firedamage,
cvar_flaregrenades, cvar_knockbackducking, cvar_knockbackdamage, cvar_knockbackzvel,
cvar_multiratio, cvar_firstleap, cvar_flaresize, cvar_botquota, cvar_spawndelay,
cvar_nemnvgcolor[3], cvar_flashcolor[3], cvar_nvgcolor[3]

/*================================================================================
 [Precache and Init]
=================================================================================*/

public plugin_precache()
{
    new i, playermodel[100]
    
    // Custom player models
    for (i = 0; i < sizeof model_zombie_class1; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_zombie_class1[i], model_zombie_class1[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_zombie_class2; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_zombie_class2[i], model_zombie_class2[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_zombie_class3; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_zombie_class3[i], model_zombie_class3[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_zombie_class4; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_zombie_class4[i], model_zombie_class4[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_zombie_class5; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_zombie_class5[i], model_zombie_class5[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_zombie_class6; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_zombie_class6[i], model_zombie_class6[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_nemesis; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_nemesis[i], model_nemesis[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_human; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_human[i], model_human[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    for (i = 0; i < sizeof model_admin; i++)
    {
        formatex(playermodel, sizeof playermodel - 1, "models/player/%s/%s.mdl", model_admin[i], model_admin[i])
        engfunc(EngFunc_PrecacheModel, playermodel)
    }
    
    // Custom weapon models
    engfunc(EngFunc_PrecacheModel, model_vknife_zombie)
    engfunc(EngFunc_PrecacheModel, model_pknife_zombie)
    engfunc(EngFunc_PrecacheModel, model_vknife_nemesis)
    engfunc(EngFunc_PrecacheModel, model_pknife_nemesis)
    engfunc(EngFunc_PrecacheModel, model_grenade_infect)
    engfunc(EngFunc_PrecacheModel, model_grenade_fire)
    engfunc(EngFunc_PrecacheModel, model_grenade_frost)
    engfunc(EngFunc_PrecacheModel, model_grenade_flare)
    
    // Custom sounds
    for (i = 0; i < sizeof sound_thunder; i++)
        engfunc(EngFunc_PrecacheSound, sound_thunder[i])
    for (i = 0; i < sizeof zombie_idle; i++)
        engfunc(EngFunc_PrecacheSound, zombie_idle[i])
    for (i = 0; i < sizeof zombie_idle_last; i++)
        engfunc(EngFunc_PrecacheSound, zombie_idle_last[i])
    for (i = 0; i < sizeof zombie_infect; i++)
        engfunc(EngFunc_PrecacheSound, zombie_infect[i])
    for (i = 0; i < sizeof zombie_die; i++)
        engfunc(EngFunc_PrecacheSound, zombie_die[i])
    for (i = 0; i < sizeof zombie_pain; i++)
        engfunc(EngFunc_PrecacheSound, zombie_pain[i])
    for (i = 0; i < sizeof nemesis_pain; i++)
        engfunc(EngFunc_PrecacheSound, nemesis_pain[i])
    for (i = 0; i < sizeof zombie_fall; i++)
        engfunc(EngFunc_PrecacheSound, zombie_fall[i])
    for (i = 0; i < sizeof zombie_madness; i++)
        engfunc(EngFunc_PrecacheSound, zombie_madness[i])
    for (i = 0; i < sizeof sound_nemesis; i++)
        engfunc(EngFunc_PrecacheSound, sound_nemesis[i])
    for (i = 0; i < sizeof sound_survivor; i++)
        engfunc(EngFunc_PrecacheSound, sound_survivor[i])
    for (i = 0; i < sizeof sound_swarm; i++)
        engfunc(EngFunc_PrecacheSound, sound_swarm[i])
    for (i = 0; i < sizeof sound_multi; i++)
        engfunc(EngFunc_PrecacheSound, sound_multi[i])
    for (i = 0; i < sizeof grenade_infect; i++)
        engfunc(EngFunc_PrecacheSound, grenade_infect[i])
    for (i = 0; i < sizeof grenade_infect_player; i++)
        engfunc(EngFunc_PrecacheSound, grenade_infect_player[i])
    for (i = 0; i < sizeof grenade_fire; i++)
        engfunc(EngFunc_PrecacheSound, grenade_fire[i])
    for (i = 0; i < sizeof grenade_fire_player; i++)
        engfunc(EngFunc_PrecacheSound, grenade_fire_player[i])
    for (i = 0; i < sizeof grenade_frost; i++)
        engfunc(EngFunc_PrecacheSound, grenade_frost[i])
    for (i = 0; i < sizeof grenade_frost_player; i++)
        engfunc(EngFunc_PrecacheSound, grenade_frost_player[i])
    for (i = 0; i < sizeof grenade_frost_break; i++)
        engfunc(EngFunc_PrecacheSound, grenade_frost_break[i])
    for (i = 0; i < sizeof grenade_flare; i++)
        engfunc(EngFunc_PrecacheSound, grenade_flare[i])
    for (i = 0; i < sizeof sound_antidote; i++)
        engfunc(EngFunc_PrecacheSound, sound_antidote[i])
    #if defined AMBIENCE_SOUNDS
    for (i = 0; i < sizeof sound_ambience; i++)
        engfunc(EngFunc_PrecacheSound, sound_ambience[i])
    #endif
    
    // Custom models/sprites for grenades
    g_trailSpr = engfunc(EngFunc_PrecacheModel, "sprites/laserbeam.spr")
    g_exploSpr = engfunc(EngFunc_PrecacheModel, "sprites/shockwave.spr")
    g_flameSpr = engfunc(EngFunc_PrecacheModel, "sprites/flame.spr")
    g_glassSpr = engfunc(EngFunc_PrecacheModel, "models/glassgibs.mdl")
    
    #if defined AMBIENCE_FOG
    new ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "env_fog"))
    fm_set_kvd(ent, "density", FOG_DENSITY, "env_fog")
    fm_set_kvd(ent, "rendercolor", FOG_COLOR, "env_fog")
    #endif
    
    #if defined AMBIENCE_RAIN
    engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "env_rain"))
    #endif

    #if defined AMBIENCE_SNOW
    engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "env_snow"))
    #endif
    
    // Fake BombSite (to force round ending)
    engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_bomb_target"))
    
    // Prevent some entities from spawning
    g_fwSpawn = register_forward(FM_Spawn, "fw_Spawn")
}

public plugin_init()
{
    // Register plugin call
    register_plugin("Zombie Plague", PLUGIN_VERSION, "MeRcyLeZZ")
    
    // Language files
    register_dictionary("zombie_plague.txt")
    
    // Events
    register_event("HLTV", "event_round_start", "a", "1=0", "2=0")
    register_logevent("event_round_end", 2, "1=Round_End")
    register_event("SpecHealth2", "event_spect_target", "bd")
    register_event("TextMsg", "event_spect_mode", "b")
    
    // Forwards
    RegisterHam(Ham_Spawn, "player", "fw_PlayerSpawn", 1)
    RegisterHam(Ham_Killed, "player", "fw_Killed")
    RegisterHam(Ham_TakeDamage, "player", "fw_TakeDamage")
    RegisterHam(Ham_TraceAttack, "player", "fw_TraceAttack")
    RegisterHam(Ham_Use, "func_tank", "fw_UseStationary")
    RegisterHam(Ham_Use, "func_tankmortar", "fw_UseStationary")
    RegisterHam(Ham_Use, "func_tankrocket", "fw_UseStationary")
    RegisterHam(Ham_Use, "func_tanklaser", "fw_UseStationary")
    RegisterHam(Ham_Use, "func_pushable", "fw_PushBox")
    RegisterHam(Ham_Touch,"weaponbox", "fw_WeaponTouch")
    RegisterHam(Ham_Touch,"armoury_entity", "fw_WeaponTouch")
    RegisterHam(Ham_Touch,"weapon_shield", "fw_WeaponTouch")
    register_forward(FM_EmitSound, "fw_EmitSound")
    #if !defined HANDLE_MODELS_ON_SEPARATE_ENT
    register_forward(FM_SetClientKeyValue, "fw_SetClientKeyValue")
    register_forward(FM_ClientUserInfoChanged, "fw_ClientUserInfoChanged")
    #endif
    register_forward(FM_GetGameDescription, "fw_GameDesc")
    register_forward(FM_CmdStart, "fw_CmdStart")
    register_forward(FM_CreateNamedEntity, "fw_CreateNamedEntity")
    register_forward(FM_SetModel, "fw_SetModel")
    register_forward(FM_PlayerPreThink, "fw_PreThink")
    register_forward(FM_PlayerPreThink, "fw_PreThink_Post", 1)
    unregister_forward(FM_Spawn, g_fwSpawn)
    
    // Client commands
    register_clcmd("say","clcmd_say")
    register_clcmd("say_team","clcmd_say")
    register_clcmd("nightvision","clcmd_nvgtoggle")
    register_clcmd("drop", "clcmd_drop")
    register_clcmd("buyammo1","clcmd_buyammo")
    register_clcmd("buyammo2","clcmd_buyammo")
    register_clcmd("chooseteam","clcmd_changeteam")
    register_clcmd("jointeam","clcmd_changeteam")
    
    // Menus
    register_menu("Buy Menu 1", KEYSMENU, "menu_buy1")
    register_menu("Buy Menu 2", KEYSMENU, "menu_buy2")
    register_menu("Zombie Menu", KEYSMENU, "menu_zombie")
    register_menu("Game Menu", KEYSMENU, "menu_game")
    register_menu("Extra Items", KEYSMENU, "menu_extras")
    register_menu("Mod Info", KEYSMENU, "menu_info")
    register_menu("Admin Menu", KEYSMENU, "menu_admin")
    register_menu("Player List Menu", KEYSMENU, "menu_player_list")
    
    // Admin commands
    register_concmd("zp_zombie", "cmd_zombie", ACCESS_FLAG, "<name> - Turn someone into a Zombie")
    register_concmd("zp_human", "cmd_human", ACCESS_FLAG, "<name> - Turn someone back to Human")
    register_concmd("zp_nemesis", "cmd_nemesis", ACCESS_FLAG, "<name> - Turn someone into a Nemesis")
    register_concmd("zp_survivor", "cmd_survivor", ACCESS_FLAG, "<name> - Turn someone into a Survivor")
    register_concmd("zp_respawn", "cmd_respawn", ACCESS_FLAG, "<name> - Respawn someone")
    register_concmd("zp_swarm", "cmd_swarm", ACCESS_FLAG, " - Start Swarm Mode")
    register_concmd("zp_multi", "cmd_multi", ACCESS_FLAG, " - Start Multi Infection")    
    
    // Message IDs
    g_msgScoreInfo = get_user_msgid("ScoreInfo")
    g_msgTeamInfo = get_user_msgid("TeamInfo")
    g_msgDeathMsg = get_user_msgid("DeathMsg")
    g_msgScoreAttrib = get_user_msgid("ScoreAttrib")
    g_msgSetFOV = get_user_msgid("SetFOV")
    g_msgScreenFade = get_user_msgid("ScreenFade")
    g_msgNVGToggle = get_user_msgid("NVGToggle")
    g_msgFlashlight = get_user_msgid("Flashlight")
    g_msgFlashBat = get_user_msgid("FlashBat")
    g_msgAmmoPickup = get_user_msgid("AmmoPickup")
    g_msgSendAudio = get_user_msgid("SendAudio")
    
    // Message hooks
    register_message(get_user_msgid("CurWeapon"), "message_cur_weapon")
    register_message(get_user_msgid("Money"), "message_money")
    register_message(get_user_msgid("Health"), "message_health")
    register_message(get_user_msgid("HideWeapon"), "message_hideweapon")
    register_message(g_msgFlashBat, "message_flashbat")
    register_message(g_msgScreenFade, "message_screenfade")
    register_message(g_msgScoreAttrib, "message_scoreattrib")
    register_message(get_user_msgid("StatusIcon"), "message_statusicon")
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT
    register_message(get_user_msgid("ClCorpse"), "message_clcorpse")
    #endif
    register_message(get_user_msgid("WeapPickup"), "message_weappickup")
    register_message(g_msgAmmoPickup, "message_ammopickup")
    register_message(get_user_msgid("Scenario"), "message_scenario")
    register_message(get_user_msgid("HostagePos"), "message_hostagepos")
    register_message(get_user_msgid("BombDrop"), "message_bombdrop")
    register_message(get_user_msgid("TextMsg"), "message_textmsg")
    register_message(g_msgSendAudio, "message_sendaudio")
    register_message(get_user_msgid("TeamScore"), "message_teamscore")
    register_message(g_msgTeamInfo, "message_teaminfo")
    
    // CVARS - General Purpose
    cvar_warmup = register_cvar("zp_delay", "10")
    cvar_lightning = register_cvar("zp_lightning", "a")
    cvar_thunder = register_cvar("zp_thunderclap", "90")
    cvar_triggered = register_cvar("zp_triggered_lights", "1")
    cvar_removedoors = register_cvar("zp_remove_doors", "0")
    cvar_deathmatch = register_cvar("zp_deathmatch", "0")
    cvar_spawndelay = register_cvar("zp_spawn_delay", "5")
    cvar_spawnprotection = register_cvar("zp_spawn_protection", "5")
    cvar_randspawn = register_cvar("zp_random_spawn", "1")
    cvar_removemoney = register_cvar("zp_remove_money", "1")
    cvar_extraitems = register_cvar("zp_extra_items", "1")
    cvar_zclasses = register_cvar("zp_zombie_classes", "1")
    
    // CVARS - Flashlight and Nightvision
    cvar_cnvg = register_cvar("zp_nvg_custom", "1")
    cvar_nvgsize = register_cvar("zp_nvg_size", "80")
    cvar_nvgcolor[0] = register_cvar("zp_nvg_color_R", "0")
    cvar_nvgcolor[1] = register_cvar("zp_nvg_color_G", "150")
    cvar_nvgcolor[2] = register_cvar("zp_nvg_color_B", "0")
    cvar_nemnvgcolor[0] = register_cvar("zp_nvg_nem_color_R", "150")
    cvar_nemnvgcolor[1] = register_cvar("zp_nvg_nem_color_G", "0")
    cvar_nemnvgcolor[2] = register_cvar("zp_nvg_nem_color_B", "0")
    cvar_cflash = register_cvar("zp_flash_custom", "0")
    cvar_flashsize = register_cvar("zp_flash_size", "10")
    cvar_flashdrain = register_cvar("zp_flash_drain", "1")
    cvar_flashdist = register_cvar("zp_flash_distance", "1000")
    cvar_flashcolor[0] = register_cvar("zp_flash_color_R", "100")
    cvar_flashcolor[1] = register_cvar("zp_flash_color_G", "100")
    cvar_flashcolor[2] = register_cvar("zp_flash_color_B", "100")
    
    // CVARS - Humans
    cvar_humanhp = register_cvar("zp_human_health", "100")
    cvar_humanlasthp = register_cvar("zp_human_last_extrahp", "0")
    cvar_humanspd = register_cvar("zp_human_speed", "240")
    cvar_humangravity = register_cvar("zp_human_gravity", "1.0")
    cvar_infammo = register_cvar("zp_human_unlimited_ammo", "0")
    cvar_dropwpn = register_cvar("zp_human_drop_weapons", "2")
    cvar_ammodamage = register_cvar("zp_human_damage_reward", "500")
    cvar_firegrenades = register_cvar("zp_fire_grenades", "1")
    cvar_fireduration = register_cvar("zp_fire_duration", "10")
    cvar_firedamage = register_cvar("zp_fire_damage", "5")
    cvar_frostgrenades = register_cvar("zp_frost_grenades", "1")
    cvar_freezeduration = register_cvar("zp_frost_duration", "3")
    cvar_flaregrenades = register_cvar("zp_flare_grenades","1")
    cvar_flareduration = register_cvar("zp_flare_duration", "60")
    cvar_flaresize = register_cvar("zp_flare_size", "25")
    cvar_flarecolor = register_cvar("zp_flare_color", "0")
    
    // CVARS - Knockback
    cvar_knockback = register_cvar("zp_knockback", "0")
    cvar_knockbackdamage = register_cvar("zp_knockback_damage", "1")
    cvar_knockbackpower = register_cvar("zp_knockback_power", "1")
    cvar_knockbackzvel = register_cvar("zp_knockback_zvel", "0")
    cvar_knockbackducking = register_cvar("zp_knockback_ducking", "0")
    
    // CVARS - Zombies
    cvar_hp = register_cvar("zp_zombie_health","1800")
    cvar_firsthp = register_cvar("zp_zombie_fhealth","3600")
    cvar_armor = register_cvar("zp_zombie_armor","0.75")
    cvar_speed = register_cvar("zp_zombie_speed","190")
    cvar_gravity = register_cvar("zp_zombie_gravity","1.0")
    cvar_bonushp = register_cvar("zp_zombie_infect_health","100")
    cvar_fov_angle = register_cvar("zp_zombie_fov","110")
    cvar_firstleap = register_cvar("zp_zombie_first_leap", "0")
    cvar_painfree = register_cvar("zp_zombie_painfree","2")
    cvar_bleeding = register_cvar("zp_zombie_bleeding","1")
    cvar_ammoinfect = register_cvar("zp_zombie_infect_reward","1")
    
    // CVARS - Nemesis
    cvar_nem = register_cvar("zp_nem_enabled","1")
    cvar_nemchance = register_cvar("zp_nem_chance","20")
    cvar_nemhp = register_cvar("zp_nem_health","0")
    cvar_nemspd = register_cvar("zp_nem_speed","250")
    cvar_nemgravity = register_cvar("zp_nem_gravity","0.5")
    cvar_nemglow = register_cvar("zp_nem_glow","1")
    cvar_nemleap = register_cvar("zp_nem_leap","1")    
    cvar_nempainfree = register_cvar("zp_nem_painfree", "0")
    cvar_nemignorefrags = register_cvar("zp_nem_ignore_frags", "0")
    
    // CVARS - Survivor
    cvar_surv = register_cvar("zp_surv_enabled","1")
    cvar_survchance = register_cvar("zp_surv_chance","20")
    cvar_survhp = register_cvar("zp_surv_health","0")
    cvar_survspd = register_cvar("zp_surv_speed","230")
    cvar_survgravity = register_cvar("zp_surv_gravity", "1.25")
    cvar_survglow = register_cvar("zp_surv_glow","1")
    cvar_survleap = register_cvar("zp_surv_leap","0")
    cvar_survpainfree = register_cvar("zp_surv_painfree", "1")
    cvar_survignorefrags = register_cvar("zp_surv_ignore_frags", "0")
    
    // CVARS - Swarm Mode
    cvar_swarm = register_cvar("zp_swarm_enabled", "1")
    cvar_swarmchance = register_cvar("zp_swarm_chance","20")
    
    // CVARS - Multi Infection
    cvar_multi = register_cvar("zp_multi_enabled", "1")
    cvar_multichance = register_cvar("zp_multi_chance","20")
    cvar_multiratio = register_cvar("zp_multi_ratio", "0.15")
    
    // CVARS - Others
    cvar_logcommands = register_cvar("zp_logcommands", "1")
    cvar_showactivity = get_cvar_pointer("amx_show_activity")
    cvar_botquota = get_cvar_pointer("bot_quota")
    register_cvar("zp_version", PLUGIN_VERSION, FCVAR_SERVER|FCVAR_SPONLY)
    
    // Check for CSDM spawns of the current map
    new mapname[32], cfgdir[32], filepath[100], linedata[64]
    get_configsdir(cfgdir, sizeof cfgdir - 1);
    get_mapname(mapname, sizeof mapname - 1);
    formatex(filepath, sizeof filepath - 1, "%s/csdm/%s.spawns.cfg", cfgdir, mapname);
    
    // Load CSDM spawns if present
    if (file_exists(filepath))
    {
        new csdmdata[10][6], file = fopen(filepath,"rt")
        
        while (file && !feof(file))
        {
            fgets(file, linedata, sizeof linedata - 1);

            // invalid spawn
            if(!linedata[0] || str_count(linedata,' ') < 2) continue;

            // get spawn point data
            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);

            // origin
            g_spawns[g_spawnCount][0] = floatstr(csdmdata[0]);
            g_spawns[g_spawnCount][1] = floatstr(csdmdata[1]);
            g_spawns[g_spawnCount][2] = floatstr(csdmdata[2]);

            // increase spawn count
            g_spawnCount++;
            if (g_spawnCount >= sizeof g_spawns) break;
        }
        if (file) fclose(file);
    }
    else
    {
        // if not, collect regular spawns
        collect_spawns("info_player_start")
        collect_spawns("info_player_deathmatch")
    }
    
    // Execute config file (zombieplague.cfg)
    server_cmd("exec %s/zombieplague.cfg", cfgdir)
    
    // Set a random skybox
    set_cvar_string("sv_skyname", skynames[random_num(0, sizeof skynames - 1)])
    
    // Disable sky lightning so it doesn't mess up our custom lightning
    set_cvar_num("sv_skycolor_r", 0)
    set_cvar_num("sv_skycolor_g", 0)
    set_cvar_num("sv_skycolor_b", 0)
    
    // Create the HUD Sync Objects
    g_MsgSync = CreateHudSyncObj()
    g_MsgSync2 = CreateHudSyncObj()
    
    // Call Round Start
    set_task(1.0, "event_round_start");
    
    // Lightning task
    set_task(2.0, "lightning_effects")
    
    #if defined AMBIENCE_SOUNDS
    set_task(3.0, "ambience_sound_effects")
    #endif
    
    // Format mod name
    formatex(g_modname, sizeof g_modname - 1, "Zombie Plague %s", PLUGIN_VERSION)
    
    // Get Max Players
    g_maxplayers = get_maxplayers()
    
    // Reserved saving slots starts on maxplayers+1
    db_slot_i = g_maxplayers+1
    
    // Check if it's a CZ server
    new mymod[6]; get_modname(mymod, sizeof mymod - 1);
    if (equal(mymod, "czero")) g_czero = 1;
}

/*================================================================================
 [Main Events]
=================================================================================*/

// Round Start Event
public event_round_start()
{
    // Remove all tasks bound to custom nades (as they're removed on new round)
    remove_task(NADES_TASK);
    
    // Remove doors and/or lights
    set_task(0.2, "remove_stuff")
    
    // New round starting
    g_newround = true
    g_endround = false
    g_survround = false
    g_nemround = false
    g_swarmround = false    
    g_teams_i = 0.0 // reset team change count
    g_models_i = 0.0 // reset model change count
    
    // Show welcome message and T-Virus notice
    remove_task(WELCOMEMSG_TASK)
    set_task(2.0, "welcome_msg", WELCOMEMSG_TASK)
    
    // Set a new "Make Zombie Task"
    remove_task(MAKEZOMBIE_TASK)
    set_task(2.0+random_float(get_pcvar_float(cvar_warmup), get_pcvar_float(cvar_warmup)+3.0), "make_zombie_task", MAKEZOMBIE_TASK)
}

// Round End Event
public event_round_end()
{
    // Save player's stats on round end
    static id
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (!is_user_connected(id)) 
            continue; // skip if not connected
        
        if (fm_get_user_team(id) == CS_TEAM_SPECTATOR || fm_get_user_team(id) == CS_TEAM_UNASSIGNED)
            continue; // skip if not playing
        
        save_stats(id)
    }
    
    // Round ended
    g_endround = true    
    g_teams_i = 0.0 // reset teams change count
    g_models_i = 0.0 // reset model change count
    
    // Stop old tasks (if any)
    remove_task(WELCOMEMSG_TASK)
    remove_task(MAKEZOMBIE_TASK)
    
    // Balance the teams
    set_task(0.1, "balance_teams")
    
    // Show HUD notice, play win sound, update team scores...
    if (!fnGetZombies())
    {
        // Human team wins
        set_hudmessage(0, 0, 200, -1.0, 0.25, 0, 0.0, 3.0, 2.0, 1.0, -1)
        ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "WIN_HUMAN")
        PlaySound(sound_win_humans[random_num(0, sizeof sound_win_humans -1)])
        g_scorehumans++
    }
    else if (!fnGetHumans())
    {
        // Zombie team wins
        set_hudmessage(200, 0, 0, -1.0, 0.25, 0, 0.0, 3.0, 2.0, 1.0, -1)
        ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "WIN_ZOMBIE")
        PlaySound(sound_win_zombies[random_num(0, sizeof sound_win_zombies -1)])
        g_scorezombies++
    }
    else
    {
        // No one wins
        set_hudmessage(0, 200, 0, -1.0, 0.25, 0, 0.0, 3.0, 2.0, 1.0, -1)
        ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "WIN_NO_ONE")
        PlaySound(sound_win_no_one[random_num(0, sizeof sound_win_no_one -1)])
        
        // Screen fade effect
        message_begin(MSG_BROADCAST, g_msgScreenFade)
        write_short(12288)    // Duration
        write_short(12288)    // Hold time
        write_short(0x0001)    // Fade type
        write_byte (0)        // Red
        write_byte (0)        // Green
        write_byte (0)        // Blue
        write_byte (255)    // Alpha
        message_end()
    }
}

// Event Spectator Mode
public event_spect_mode(id)
{
    if (get_pcvar_num(cvar_cnvg)) // custom night vision isn't affected by this
        return;
    
    static specmode[12] // get text message string
    read_data(2, specmode, sizeof specmode - 1)
    
    if (!equal(specmode, "#Spec_Mode", 10)) // not a spectator mode event
        return;
    
    if (g_spec_first_person[id]) // if last mode was first person
    {
        if (g_nvisionenabled[id]) // and nvg was on
            set_user_gnvision(id, 1); // we need to restore it
        g_spec_first_person[id] = false;
    }
    
    if (specmode[10] == '4') // First Person Mode
        g_spec_first_person[id] = true
}

// Event Spectator Target
public event_spect_target(id)
{
    if (get_pcvar_num(cvar_cnvg) || !id) // custom night vision isn't affected by this
        return;
    
    // If previous target died and nvg was on
    if (g_nvisionenabled[id] && !is_user_alive(g_spec_target[id]))
        set_user_gnvision(id, 1); // we need to restore it
    
    g_spec_target[id] = read_data(2); // update last spectator target
}

/*================================================================================
 [Main Forwards]
=================================================================================*/

// Client disconnect
public client_disconnect(id)
{    
    // Check that we still have both humans and zombies to keep the round going
    if (is_user_alive(id)) check_round(id);
    
    // Save player stats
    save_stats(id)
    
    // Remove previous tasks
    remove_task(id+TEAM_TASK)
    remove_task(id+MODEL_TASK)
    remove_task(id+CHARGE_TASK)
    remove_task(id+SPAWN_TASK)
    remove_task(id+BLOOD_TASK)
    remove_task(id+NVISION_TASK)
    remove_task(id+SHOWHUD_TASK)
    
    // Last Zombie Check
    set_task(0.1, "fnCheckLastZombie")
}

// Client joins the game
public client_putinserver(id)
{    
    // Initialize player vars
    reset_vars(id, 1)
    
    // Try load player stats
    load_stats(id)
    
    // Set the custom HUD display task
    set_task(3.0, "ShowHUD", id+SHOWHUD_TASK)
    
    // CZ bots seem to use a different "classtype" for player entities
    // (or something like that) which needs to be hooked separately
    if (cvar_botquota && !g_hamczbots) set_task(0.1, "register_ham_czbots", id)
}

// Entity Spawn Forward
public fw_Spawn(entity)
{
    // Get classname
    static classname[32]
    pev(entity, pev_classname, classname, sizeof classname - 1)
    
    // Check whether it needs to be removed
    static i
    for (i = 0; i < sizeof g_objective_ents; i++)
    {
        if (equal(classname, g_objective_ents[i]))
        {
            engfunc(EngFunc_RemoveEntity, entity)
            return FMRES_SUPERCEDE
        }
    }

    return FMRES_IGNORED
}

// Ham Player Spawn Forward
public fw_PlayerSpawn(id)
{    
    if (!is_user_alive(id)) // not alive
        return;
    
    // Remove previous tasks
    remove_task(id+SPAWN_TASK)
    remove_task(id+TEAM_TASK)
    remove_task(id+MODEL_TASK)
    remove_task(id+BLOOD_TASK)
    remove_task(id+CHARGE_TASK)
    
    // Spawn randomly?
    if (get_pcvar_num(cvar_randspawn)) do_random_spawn(id)
    
    // Respawn as zombie?
    if (g_respawn_as_zombie[id] && !g_newround)
    {
        reset_vars(id, 0) // reset player vars
        zombieme(id, 0, 0) // make him zombie right away
        return;
    }
    
    // Reset player vars
    reset_vars(id, 0)
    
    // Turn off NVG for bots
    if (is_user_bot(id)) fm_set_bot_nvg(id, 0);
    
    fm_set_user_health(id, get_pcvar_num(cvar_humanhp)) // set health
    set_pev(id, pev_gravity, get_pcvar_float(cvar_humangravity)) // set gravity    
    
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT
    
    // Set the right model    
    if (get_user_flags(id) & ACCESS_FLAG)
        copy(g_playermodel[id], 32, model_admin[random_num(0, sizeof model_admin -1)])
    else
        copy(g_playermodel[id], 32, model_human[random_num(0, sizeof model_human -1)])
    
    fm_set_user_model_ent(id) // set model on player_model entity
    fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id)); // remove glow on player_model entity
    fm_set_rendering(id, kRenderFxNone, 255, 255, 255, kRenderTransTexture, 0) // make original player entity invisible
    
    #else
    
    // Set the right model, but check that we don't have it already
    static currentmodel[33], already_has_model, i
    already_has_model = false;
    fm_get_user_model(id, currentmodel, sizeof currentmodel - 1); // get current model
    
    if (get_user_flags(id) & ACCESS_FLAG)
    {
        for (i = 0; i < sizeof model_admin; i++)
            if (equal(model_admin[i], currentmodel)) already_has_model = true;
        
        if (!already_has_model) copy(g_playermodel[id], 32, model_admin[random_num(0, sizeof model_admin -1)])
    }
    else
    {
        for (i = 0; i < sizeof model_human; i++)
            if (equal(model_human[i], currentmodel)) already_has_model = true;
        
        if (!already_has_model) copy(g_playermodel[id], 32, model_human[random_num(0, sizeof model_human -1)])
    }
    
    if (!already_has_model) // need to change the model?
    {
        if (g_newround) // round start?
        {
            set_task(0.1+(MODELCHANGE_DELAY*2)+g_models_i, "fm_set_user_model", id+MODEL_TASK) // set model with a delay
            g_models_i += MODELCHANGE_DELAY;
        }
        else
        {
            fm_set_user_model(id+MODEL_TASK) // set model instantly
        }
    }
    
    fm_set_rendering(id); // remove glow
    
    #endif
    
    set_task(0.5, "show_menu_buy1", id+SPAWN_TASK); // show buy menu
    set_task(10.5, "show_menu_buy1", id+SPAWN_TASK); // re-show in case it gets overlapped
    
    // Respawn player in case he dies by worldspawn or something
    set_task(5.0, "respawn_player", id+SPAWN_TASK)
    
    // Enable spawn protection for humans spawning mid-round
    if (!g_newround && get_pcvar_num(cvar_spawnprotection))
    {
        g_nodamage[id] = true; // dont take any damage
        set_pev(id, pev_effects, pev(id, pev_effects) | EF_NODRAW) // make temporarily invisible
        set_task(get_pcvar_float(cvar_spawnprotection), "remove_spawn_protection", id+SPAWN_TASK)
    }
    
    // Last Zombie Check
    set_task(0.1, "fnCheckLastZombie")
}

// Ham Player Killed Forward
public fw_Killed(victim, attacker, shouldgib)
{    
    set_task(0.2, "spec_nvision", victim) // enable dead players nightvision
    set_task(0.1, "fnCheckLastZombie") // Last Zombie Check
    
    if (g_zombie[victim]) // stop bleeding/burning when killed
        remove_task(victim+BLOOD_TASK) 
    
    if (g_nemesis[victim]) // nemesis explodes!
        SetHamParamInteger(3, 2)
    
    // Killed by a non-player entity or self killed
    if (victim == attacker || !is_user_connected(attacker))
        return;
    
    // Ignore Nemesis/Survivor Frags?
    if ((g_nemesis[attacker] && get_pcvar_num(cvar_nemignorefrags)) || (g_survivor[attacker] && get_pcvar_num(cvar_survignorefrags)))
        RemoveFrags(attacker, victim)
    
    if (g_zombie[attacker] && !g_nemesis[attacker]) // zombie killed human, reward ammo packs
        g_ammopacks[attacker] += get_pcvar_num(cvar_ammoinfect)
    
    // Respawn if Deathmatch is enabled
    if (get_pcvar_num(cvar_deathmatch))
    {
        set_task(get_pcvar_float(cvar_spawndelay), "respawn_player", victim+SPAWN_TASK)
        if ((get_pcvar_num(cvar_deathmatch) == 2) || (get_pcvar_num(cvar_deathmatch) == 3 && random_num(0, 1)))
            g_respawn_as_zombie[victim] = true // respawn as zombie
    }
}

// Ham Take Damage Forward (inflictor = weapon)
public fw_TakeDamage(victim, inflictor, attacker, Float:damage, damage_type)
{    
    if (victim == attacker || !is_user_connected(attacker)) // non-player damage or self damage
        return HAM_IGNORED
    
    if (g_newround || g_endround) // new round starting or round ended, cant attack
        return HAM_SUPERCEDE
    
    if (g_nodamage[victim] || g_frozen[victim]) // victim shouldn't take damage or victim is frozen
        return HAM_SUPERCEDE
    
    if (g_zombie[attacker] == g_zombie[victim]) // prevent team killing
        return HAM_SUPERCEDE
    
    if (!g_zombie[attacker] && g_zombie[victim]) // human attacks zombie
    {        
        if (!g_nemesis[victim]) // armor multiplier for the final damage on normal zombies
        {
            damage *= get_pcvar_float(cvar_armor)
            SetHamParamFloat(4, damage)
        }
        
        // Reward ammo packs (survivor doesn't get any)
        if (!g_survivor[attacker])
        {
            // Store damage dealt
            g_damagedealt[attacker] += floatround(damage);
            
            // Reward ammo packs for every [ammo damage] dealt
            while (g_damagedealt[attacker] >= get_pcvar_num(cvar_ammodamage))
            {
                g_ammopacks[attacker]++
                g_damagedealt[attacker] -= get_pcvar_num(cvar_ammodamage);
                
                // Bots automatically buy extra items
                if (is_user_bot(attacker)) set_task(0.1, "bot_buy_extras", attacker)
            }            
        }
        
        return HAM_IGNORED
    }
    
    if (g_nemesis[attacker] && !g_zombie[victim]) // nemesis one-hit kill
    {
        SetHamParamFloat(4, 255.0) // set a higher damage
        return HAM_IGNORED
    }
    
    if (g_zombie[attacker] && !g_zombie[victim]) // zombie attacks human
    {
        if (damage_type & DMG_HEGRENADE) // prevent infection by HE grenade (bugfix)
            return HAM_SUPERCEDE
        
        if (g_swarmround || fnGetHumans() == 1) // last human or swarm round
        {
            return HAM_IGNORED // human is killed
        }
        else
        {
            SendDeathMsg(attacker, victim); // send death notice
            FixDeadAttrib(victim) // fix the "dead" attrib on scoreboard
            UpdateFrags(attacker, victim); // add corresponding frags and deaths
            
            // Infect the victim
            zombieme(victim, 0, 0)
            
            // Ammo packs given to zombie for infection
            g_ammopacks[attacker] += get_pcvar_num(cvar_ammoinfect)
            
            // Attacker gets HP bonus for the infection
            if (g_zombieclass[attacker] != 4) // 3x bonus for leech zombie
                fm_set_user_health(attacker, pev(attacker, pev_health)+get_pcvar_num(cvar_bonushp))
            else
                fm_set_user_health(attacker, pev(attacker, pev_health)+(get_pcvar_num(cvar_bonushp)*3))
            
            return HAM_SUPERCEDE
        }
    }
    
    return HAM_IGNORED
}

// Ham Trace Attack Forward
public fw_TraceAttack(victim, attacker, Float:damage, Float:direction[3], tracehandle, damagetype)
{    
    if (!get_pcvar_num(cvar_knockback)) // knockback disabled
        return;
    
    if (!(damagetype & DMG_BULLET)) // not bullet damage
        return;
    
    if (victim == attacker || !is_user_connected(attacker)) // non-player damage or self damage
        return;
    
    if (g_zombie[attacker] || !g_zombie[victim] || g_nemesis[victim]) // only for humans attacking a zombie
        return;
    
    if (!get_pcvar_num(cvar_knockbackducking) && (pev(victim, pev_flags) & FL_DUCKING)) // zombie is ducking
        return;
    
    // Get victim's velocity
    static Float:velocity1[3], Float:velocity2[3]
    pev(victim, pev_velocity, velocity1)
    xs_vec_copy(velocity1, velocity2)
    
    // Use damage on knockback calculation
    if (get_pcvar_num(cvar_knockbackdamage))
        xs_vec_mul_scalar(direction, damage, direction)
    
    // Use weapon power on knockback calculation
    if (get_pcvar_num(cvar_knockbackpower) && kb_weapon_power[g_currentweapon[attacker]] > 0.0)
        xs_vec_mul_scalar(direction, kb_weapon_power[g_currentweapon[attacker]], direction)
    
    // Add up the new vector
    xs_vec_add(direction, velocity1, velocity1)
    
    // Should knockback affect vertical velocity?
    if (!get_pcvar_num(cvar_knockbackzvel))
        velocity1[2] = velocity2[2]
    
    // Set the knockback'd victim's velocity
    set_pev(victim, pev_velocity, velocity1)
}

// Ham Use Stationary Gun Forward
public fw_UseStationary(entity, caller, activator, use_type)
{    
    if (!is_user_connected(caller)) // not a player
        return HAM_IGNORED;
    
    if (use_type == 2 && g_zombie[caller]) // prevent zombies from using stationary guns
        return HAM_SUPERCEDE
    
    if (use_type == 0) // someone stopped using stationary gun
        set_task(0.1, "replace_models", caller); // replace weapon models (bugfix)
    
    return HAM_IGNORED
}

// Ham Use Pushable
public fw_PushBox()
{
    // prevent speed bug with boxes, etc.
    return HAM_SUPERCEDE
}

// Ham Weapon Touch Forward
public fw_WeaponTouch(weapon, id)
{
    if (!is_user_connected(id)) // not a player
        return HAM_IGNORED
    
    if (g_zombie[id] || g_survivor[id]) // dont pickup weapons if zombie or survivor
        return HAM_SUPERCEDE
    
    return HAM_IGNORED
}

// Emit Sound Forward
public fw_EmitSound(id, channel, const sample[], Float:volume, Float:attn, flags, pitch)
{
    if (!is_user_connected(id) || !g_zombie[id]) // replace these sounds for zombies only
        return FMRES_IGNORED
    
    if ((sample[7] == 'd' && sample[8] == 'i' && sample[9] == 'e') || (sample[7] == 'd' && sample[8] == 'e' && sample[9] == 'a') || (sample[10] == 'd' && sample[11] == 'i' && sample[12] == 'e')) // die
    {
        engfunc(EngFunc_EmitSound, id, channel, zombie_die[random_num(0, sizeof zombie_die - 1)], volume, attn, flags, pitch)
        return FMRES_SUPERCEDE
    }
    
    if (sample[7] == 'b' && sample[8] == 'h' && sample[9] == 'i' && sample[10] == 't') // being hit
    {
        if (g_nemesis[id])
            engfunc(EngFunc_EmitSound, id, channel, nemesis_pain[random_num(0, sizeof nemesis_pain - 1)], volume, attn, flags, pitch)
        else
            engfunc(EngFunc_EmitSound, id, channel, zombie_pain[random_num(0, sizeof zombie_pain - 1)], volume, attn, flags, pitch)
        return FMRES_SUPERCEDE
    }
    
    if (sample[10] == 'f' && sample[11] == 'a' && sample[12] == 'l' && sample[13] == 'l') // fall off
    {
        engfunc(EngFunc_EmitSound, id, channel, zombie_fall[random_num(0, sizeof zombie_fall - 1)], volume, attn, flags, pitch)
        return FMRES_SUPERCEDE
    }
    
    return FMRES_IGNORED
}

#if !defined HANDLE_MODELS_ON_SEPARATE_ENT
// Forward Set ClientKey Value -prevent CS from changing player models-
public fw_SetClientKeyValue(id, infobuffer, const key[])
{    
    if (equal(key, "model")) // block CS model change
        return FMRES_SUPERCEDE;
    
    return FMRES_IGNORED;
}

// Forward Client User Info Changed -prevent players from changing models-
public fw_ClientUserInfoChanged(id)
{    
    static currentmodel[33] // get current model
    fm_get_user_model(id, currentmodel, sizeof currentmodel - 1);
    
    // If they're different, set model again
    if (!equal(currentmodel, g_playermodel[id]) && !task_exists(id+MODEL_TASK))
        fm_set_user_model(id+MODEL_TASK) 
}
#endif

// Forward Game Description
public fw_GameDesc()
{
    // Return the new mod name
    forward_return(FMV_STRING, g_modname)
    
    return FMRES_SUPERCEDE
}

// Forward CmdStart
public fw_CmdStart(id, handle)
{
    if (!is_user_alive(id)) // not alive
        return FMRES_IGNORED
    
    // Get impulse id
    static impulseid
    impulseid = get_uc(handle, UC_Impulse)
    
    if (impulseid == 100) // impulse 100 = flashlight
    {
        // Block it for zombies, survivor (and override it for humans when custom flashlight is on)
        if (g_zombie[id] || g_survivor[id] || (!g_zombie[id] && get_pcvar_num(cvar_cflash)))
        {
            if (!g_zombie[id] && !g_survivor[id]) // human's custom flashlight should be turned on instead
            {
                // Turn custom flashlight on/off
                g_flashlight[id] ? (g_flashlight[id] = 0) : (g_flashlight[id] = 1)
                
                // Set the flashlight charge task if not running yet
                if (!task_exists(id+CHARGE_TASK)) set_task(1.0, "flashlight_charge", id+CHARGE_TASK)
                
                // Update flashlight status on the HUD
                message_begin(MSG_ONE, g_msgFlashlight, _, id);
                write_byte(g_flashlight[id]);    // toggle
                write_byte(g_flashbattery[id]);    // battery
                message_end();
                
                // Turn off original flashlight if active
                set_pev(id, pev_effects, pev(id, pev_effects) & ~EF_DIMLIGHT);
                
                // Play flashlight toggle sound
                engfunc(EngFunc_EmitSound, id, CHAN_ITEM, "items/flashlight1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
                
                // Finally call our custom flashlight task
                set_user_flashlight(id)
            }
            
            set_uc(handle, UC_Impulse, 0) // block the impulse
            return FMRES_SUPERCEDE
        }
    }
    return FMRES_IGNORED
}

// Forward Create Named Entity
public fw_CreateNamedEntity(classid)
{
    static classname[10]
    engfunc(EngFunc_SzFromIndex, classid, classname, sizeof classname - 1)
    
    // Prevent c4 from spawning
    if (classname[7] == 'c' && classname[8] == '4')
        return FMRES_SUPERCEDE
    
    return FMRES_IGNORED
}

// Forward Set Model
public fw_SetModel(entity, const model[])
{    
    // Get grenade's gravity
    static Float:gravity
    pev(entity, pev_gravity, gravity)
    
    // Grenade not yet thrown
    if (gravity == 0.0)
        return;
    
    if (equal(model[7], "w_hegrenade.mdl"))
    {
        static id // get grenade owner
        id = pev(entity, pev_owner)
        
        if (g_zombie[id]) // [ZOMBIE] - Infection Grenade
        {
            // Give it a glow
            fm_set_rendering(entity, kRenderFxGlowShell, 0, 200, 0, kRenderNormal, 16);
    
            // And a colored trail
            message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
            write_byte(TE_BEAMFOLLOW);
            write_short(entity);    // entity
            write_short(g_trailSpr);// sprite
            write_byte(10);        // life
            write_byte(10);     // width
            write_byte(0);         // r
            write_byte(200);     // g
            write_byte(0);         // b
            write_byte(200);    // brightness
            message_end();
        
            // Prevent hegrenade from exploding
            set_pev(entity, pev_nextthink, get_gametime()+10.0);
        
            static args[1] // our task params
            args[0] = entity; // entity id
            
            // Actual explode event
            set_task(1.5, "infection_explode", NADES_TASK, args, sizeof args)
        
        }
        else if (get_pcvar_num(cvar_firegrenades)) // [HUMAN] - Fire Grenade
        {    
            // Give it a glow
            fm_set_rendering(entity, kRenderFxGlowShell, 200, 0, 0, kRenderNormal, 16);
            
            // And a colored trail
            message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
            write_byte(TE_BEAMFOLLOW);
            write_short(entity);    // entity
            write_short(g_trailSpr);// sprite
            write_byte(10);        // life
            write_byte(10);        // width
            write_byte(200);    // r
            write_byte(0);         // g
            write_byte(0);        // b
            write_byte(200);    // brightness
            message_end();
            
            // Prevent hegrenade from exploding
            set_pev(entity, pev_nextthink, get_gametime()+10.0);
            
            static args[1] // our task params
            args[0] = entity; // entity id
            
            // Actual explode event
            set_task(1.5, "fire_explode", NADES_TASK, args, sizeof args)
        }
    }    
    else if (equal(model[7], "w_flashbang.mdl") && get_pcvar_num(cvar_frostgrenades)) // Frost Grenade
    {
        // Give it a glow
        fm_set_rendering(entity, kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 16);
        
        // And a colored trail
        message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
        write_byte(TE_BEAMFOLLOW);
        write_short(entity);    // entity
        write_short(g_trailSpr);// sprite
        write_byte(10);        // life
        write_byte(10);        // width
        write_byte(0);        // r
        write_byte(100);    // g
        write_byte(200);    // b
        write_byte(200);    // brightness
        message_end();
        
        // Prevent flashbang from exploding
        set_pev(entity, pev_nextthink, get_gametime()+10.0);
        
        static args[1] // our task params
        args[0] = entity; // entity id
        
        // Actual explode event
        set_task(1.5, "frost_explode", NADES_TASK, args, sizeof args)
    }
    else if (equal(model[7], "w_smokegrenade.mdl") && get_pcvar_num(cvar_flaregrenades)) // Flare
    {        
        static args[5] // our task params
        args[0] = entity; // entity id
        args[1] = get_pcvar_num(cvar_flareduration)/5; // duration
        
        switch (get_pcvar_num(cvar_flarecolor))
        {
            case 0: // white
            {
                args[2] = 255 // r
                args[3] = 255 // g
                args[4] = 255 // b
            }
            case 1: // red
            {
                args[2] = random_num(50,255) // r
                args[3] = 0 // g
                args[4] = 0 // b
            }
            case 2: // green
            {
                args[2] = 0 // r
                args[3] = random_num(50,255) // g
                args[4] = 0 // b
            }
            case 3: // blue
            {
                args[2] = 0 // r
                args[3] = 0 // g
                args[4] = random_num(50,255) // b
            }
            case 4: // random
            {
                args[2] = random_num(50,200) // r
                args[3] = random_num(50,200) // g
                args[4] = random_num(50,200) // b
            }
        }
        
        // Give it a glow
        fm_set_rendering(entity, kRenderFxGlowShell, args[2], args[3], args[4], kRenderNormal, 16);
        
        // And a colored trail
        message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
        write_byte(TE_BEAMFOLLOW);
        write_short(entity);    // entity
        write_short(g_trailSpr);// sprite
        write_byte(10);        // life
        write_byte(10);        // width
        write_byte(args[2]);    // r
        write_byte(args[3]);    // g
        write_byte(args[4]);    // b
        write_byte(200);    // brightness
        message_end();
        
        // Prevent smokegrenade from exploding
        set_pev(entity, pev_nextthink, get_gametime()+10.0);
        
        // Actual explode event
        set_task(1.5, "flare_explode", NADES_TASK, args, sizeof args)
    }
}

// Forward Client PreThink
public fw_PreThink(id)
{    
    if (!is_user_alive(id)) // not alive
        return;
    
    // Silent footsteps
    if (g_zombie[id] && !g_nemesis[id] && g_zombieclass[id] != 5)
        set_pev(id, pev_flTimeStepSound, 999)
    
    // Set Player MaxSpeed
    if (g_frozen[id])
    {
        set_pev(id, pev_velocity, Float:{0.0,0.0,0.0}) // stop motion
        set_pev(id, pev_maxspeed, 1.0) // prevent from moving
    }
    else
    {
        if (g_zombie[id])
        {
            if (g_nemesis[id])
                set_pev(id, pev_maxspeed, get_pcvar_float(cvar_nemspd))
            else
                set_pev(id, pev_maxspeed, get_pcvar_float(cvar_speed)*zombie_multispd[g_zombieclass[id]]/100)
        }
        else
        {
            if (g_survivor[id])
                set_pev(id, pev_maxspeed, get_pcvar_float(cvar_survspd))
            else
                set_pev(id, pev_maxspeed, get_pcvar_float(cvar_humanspd))
        }
    }
    
    // Pain Shock Free for zombies/nemesis/survivor (if enabled)
    if (g_zombie[id] || g_survivor[id])
    {
        if (pev(id, pev_flags) & FL_ONGROUND)
        {
            if (g_nemesis[id] && !get_pcvar_num(cvar_nempainfree)) // Nemesis cvar not enabled
                return;
        
            if (g_survivor[id] && !get_pcvar_num(cvar_survpainfree)) // Survivor cvar not enabled
                return;
        
            if (get_pcvar_num(cvar_painfree) == 0 && g_zombie[id] && !g_nemesis[id]) // Zombie cvar not enabled
                return;
            
            if (get_pcvar_num(cvar_painfree) == 2 && g_zombie[id] && !g_nemesis[id] && !g_lastzombie[id]) // not the last zombie
                return;
            
            pev(id, pev_velocity, g_velocity[id])
            g_restorevel[id] = true
        }
    }
}

// Forward Client PreThink Post
public fw_PreThink_Post(id) {
    
    if (!is_user_alive(id)) // not alive
        return FMRES_IGNORED
    
    if (g_restorevel[id]) // Pain Shock Free: restore velocity if needed
    {
        g_restorevel[id] = false

        if (!(FL_ONTRAIN & pev(id, pev_flags)))
        {
            // NOTE: within DLL PlayerPreThink Jump() function is called;
            // there is a conveyor velocity addiction we should care of

            static ent
            ent = pev(id, pev_groundentity);
            
            if (pev_valid(ent) && (FL_CONVEYOR & pev(ent, pev_flags)))
            {
                static Float:tempvel[3]
                pev(id, pev_basevelocity, tempvel)
                xs_vec_add(g_velocity[id], tempvel, g_velocity[id])
            }
            
            set_pev(id, pev_velocity, g_velocity[id])
            
            return FMRES_HANDLED
        }
    }
    
    return FMRES_IGNORED
}

/*================================================================================
 [Client Commands]
=================================================================================*/

// Say
public clcmd_say(id)
{
    static text[8]
    read_args(text, sizeof text - 1)
    
    if (equali(text[1], "zpmenu", 6) || equali(text[2], "zpmenu", 6))
        show_menu_game(id); // show game menu
}

// Nightvision toggle
public clcmd_nvgtoggle(id)
{
    /*
    // PODBots didn't trigger this event after all, guess this isn't necessary...
    if (is_user_bot(id)) return PLUGIN_CONTINUE;
    */
    
    if (g_nvision[id])
    {
        // Enable-disable
        g_nvisionenabled[id] ? (g_nvisionenabled[id] = 0) : (g_nvisionenabled[id] = 1);
        
        if (get_pcvar_num(cvar_cnvg)) // custom nvg?
        {
            remove_task(id+NVISION_TASK);
            set_user_nvision(id+NVISION_TASK)
        }
        else set_user_gnvision(id, g_nvisionenabled[id])
    }
    
    return PLUGIN_HANDLED
}

// Drop a Weapon
public clcmd_drop(id)
{
    if (g_survivor[id]) // survivor should stick with m249
        return PLUGIN_HANDLED
    
    return PLUGIN_CONTINUE
}

// Buy BP Ammo
public clcmd_buyammo(id)
{    
    if (get_pcvar_num(cvar_infammo) || !is_user_alive(id)) // dont bother
        return PLUGIN_HANDLED
    
    if (!g_zombie[id]) // only humans buy ammo
    {
        if (g_ammopacks[id] > 0) // enough ammo packs?
        {
            g_ammopacks[id]--
            engfunc(EngFunc_EmitSound, id, CHAN_ITEM, "items/9mmclip1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM) // ammo sound
            client_print(id, print_chat, "[ZP] %L", id, "AMMO_BOUGHT") // tell the player
            
            static weapons[32], num, i
            num = 0 // reset passed weapons count (bugfix)
            get_user_weapons(id, weapons, num)
            
            for (i = 0; i < num; i++)
            {
                if ((1<<weapons[i]) & PRIMARY_WEAPONS_BIT_SUM)
                {
                    static currentammo
                    currentammo = fm_get_user_bpammo(id, weapons[i]) // get current ammo
                    
                    if (currentammo <= MAXAMMO[weapons[i]]-BUYAMMO[weapons[i]])
                    {
                        message_begin(MSG_ONE_UNRELIABLE, g_msgAmmoPickup, _, id); // flash ammo in hud
                        write_byte(3);    // ammo id
                        write_byte(BUYAMMO[weapons[i]]) // ammo amount
                        message_end();
                        fm_set_user_bpammo(id, weapons[i], currentammo + BUYAMMO[weapons[i]]); // increase BP ammo
                    }
                    else
                    {
                        message_begin(MSG_ONE_UNRELIABLE, g_msgAmmoPickup, _, id); // flash ammo in hud
                        write_byte(3);    // ammo id
                        write_byte(MAXAMMO[weapons[i]]-currentammo) // ammo amount
                        message_end();
                        fm_set_user_bpammo(id, weapons[i], MAXAMMO[weapons[i]]); // reached the limit
                    }
                }
                else if ((1<<weapons[i]) & SECONDARY_WEAPONS_BIT_SUM)
                {
                    static currentammo
                    currentammo = fm_get_user_bpammo(id, weapons[i]) // get current ammo
                    
                    if (currentammo <= MAXAMMO[weapons[i]]-BUYAMMO[weapons[i]])
                    {
                        message_begin(MSG_ONE_UNRELIABLE, g_msgAmmoPickup, _, id); // flash ammo in hud
                        write_byte(10);    // ammo id
                        write_byte(BUYAMMO[weapons[i]]) // ammo amount
                        message_end();
                        fm_set_user_bpammo(id, weapons[i], currentammo + BUYAMMO[weapons[i]]); // increase BP ammo
                    }
                    else
                    {
                        message_begin(MSG_ONE_UNRELIABLE, g_msgAmmoPickup, _, id); // flash ammo in hud
                        write_byte(10);    // ammo id
                        write_byte(MAXAMMO[weapons[i]]-currentammo) // ammo amount
                        message_end();
                        fm_set_user_bpammo(id, weapons[i], MAXAMMO[weapons[i]]); // reached the limit
                    }
                }
            }
        }
        else
        {
            client_print(id, print_chat, "[ZP] %L", id, "NOT_ENOUGH_AMMO")
        }
    }
    else
    {
        client_print(id, print_chat, "[ZP] %L", id, "CMD_HUMAN_ONLY")
    }
    
    return PLUGIN_HANDLED
}

// Block Team Change
public clcmd_changeteam(id)
{    
    // Spectator joins the game
    if (fm_get_user_team(id) == CS_TEAM_SPECTATOR || fm_get_user_team(id) == CS_TEAM_UNASSIGNED)
        return PLUGIN_CONTINUE
    
    show_menu_game(id); // pressing 'M' (chooseteam) ingame should show the main menu
    return PLUGIN_HANDLED
}

/*================================================================================
 [Menus]
=================================================================================*/

// Game Menu
public show_menu_game(id)
{
    static menu[150]
    
    formatex(menu, sizeof menu - 1, "\yZombie Plague\w^n^n1. %L^n2. %L^n3. %L^n4. %L^n^n5. %L^n^n0. %L", id, "MENU_EXTRABUY", id,"MENU_ZCLASS", id, "MENU_UNSTUCK", id, "MENU_INFO", id, "MENU_ADMIN", id, "MENU_EXIT")
    show_menu(id, KEYSMENU, menu, -1, "Game Menu")
}

// Buy Menu 1
public show_menu_buy1(taskid)
{
    // Zombies or survivors get no guns
    if (g_zombie[ID_SPAWN] || g_survivor[ID_SPAWN] || !g_canbuy[ID_SPAWN] || !is_user_alive(ID_SPAWN))
        return;
    
    // Bots pick their weapons randomly
    if (is_user_bot(ID_SPAWN))
    {
        menu_buy1(ID_SPAWN, random_num(0, sizeof g_primary_names - 1))
        return;
    }
    
    static menu[300], len, i
    len = 0
    
    len += formatex(menu[len], sizeof menu - 1 - len, "\y%L^n", ID_SPAWN, "MENU_BUY1_TITLE")
    for (i = 0; i < sizeof g_primary_names; i++) len += formatex(menu[len], sizeof menu - 1 - len, "\w^n%d. %s \y%s", i+1, g_primary_names[i], g_primary_classes[i])
    len += formatex(menu[len], sizeof menu - 1 - len, "\w^n^n0. %L", ID_SPAWN, "MENU_EXIT")
    show_menu(ID_SPAWN, KEYSMENU, menu, 10, "Buy Menu 1")
}

// Buy Menu 2
show_menu_buy2(id)
{
    // Bots pick their weapons randomly
    if (is_user_bot(id))
    {
        menu_buy2(id, random_num(0, sizeof g_secondary_names - 1))
        return;
    }
    
    static menu[300], len, i
    len = 0
    
    len += formatex(menu[len], sizeof menu - 1 - len, "\y%L^n", id, "MENU_BUY2_TITLE")
    for (i = 0; i < sizeof g_secondary_names; i++) len += formatex(menu[len], sizeof menu - 1 - len, "\w^n%d. %s \y%s", i+1, g_secondary_names[i], g_secondary_classes[i])
    len += formatex(menu[len], sizeof menu - 1 - len, "\w^n^n0. %L", id, "MENU_EXIT")
    show_menu(id, KEYSMENU, menu, 10, "Buy Menu 2")
}

// Zombie Class Menu
public show_menu_zombie(id)
{
    if (!is_user_connected(id)) // player not connected
        return;
    
    // Bots pick their zombie class randomly
    if (is_user_bot(id))
    {
        menu_zombie(id, random_num(0, sizeof zombie_multihp - 1))
        return;
    }
    
    static menu[250], len
    len = 0
    
    len += formatex(menu[len], sizeof menu - 1 - len, "\y%L\w^n^n1. %L \y%L\w^n2. %L \y%L\w^n3. %L \y%L\w^n4. %L \y%L\w^n5. %L \y%L", id, "MENU_ZCLASS_TITLE", id, "CLASS_ZOMBIE1", id, "MENU_ZCLASS1", id, "CLASS_ZOMBIE2", id, "MENU_ZCLASS2", id, "CLASS_ZOMBIE3", id, "MENU_ZCLASS3", id, "CLASS_ZOMBIE4", id, "MENU_ZCLASS4", id, "CLASS_ZOMBIE5", id, "MENU_ZCLASS5")
    len += formatex(menu[len], sizeof menu - 1 - len, "\w^n6. %L \y %L \w^n^n0. %L", id, "CLASS_ZOMBIE6", id, "MENU_ZCLASS6", id, "MENU_EXIT")
    show_menu(id, KEYSMENU, menu, -1, "Zombie Menu")
}

// Extra Items Menu
show_menu_extras(id)
{
    static menu[450], len, i
    len = 0
    
    len += formatex(menu[len], sizeof menu - 1 - len, "\y%L\r^n^n%L:", id, "MENU_EXTRA_TITLE", id, "CLASS_HUMAN")    
    for (i = 0; i < sizeof g_extra_names; i++) len += formatex(menu[len], sizeof menu - 1 - len, "\w^n%d. %s \y%d %L", i+1, g_extra_names[i], g_extra_costs[i], id, "AMMO_PACKS2")
    len += formatex(menu[len], sizeof menu - 1 - len, "\w^n%d. %L \y%d %L\r^n^n%L:\w^n%d. %L \y%d %L\w^n%d. %L \y%d %L\w^n%d. %L \y%d %L\w^n^n0. %L", i+1, id, "MENU_EXTRA1", g_extra_costs2[EXTRA_NVISION], id, "AMMO_PACKS2", id, "CLASS_ZOMBIE", i+2, id, "MENU_EXTRA2", g_extra_costs2[EXTRA_ANTIDOTE], id, "AMMO_PACKS2", i+3, id, "MENU_EXTRA3", g_extra_costs2[EXTRA_MADNESS], id, "AMMO_PACKS2", i+4, id, "MENU_EXTRA4", g_extra_costs2[EXTRA_INFBOMB], id, "AMMO_PACKS2", id, "MENU_EXIT")
    show_menu(id, KEYSMENU, menu, -1, "Extra Items")
}

// Help Menu
public show_menu_info(id)
{
    if (!is_user_connected(id)) // player not connected
        return;
    
    static menu[100]
    
    formatex(menu, sizeof menu - 1, "\y%L\w^n^n1. %L^n2. %L^n3. %L^n4. %L^n^n0. %L", id, "MENU_INFO_TITLE", id, "MENU_INFO1", id,"MENU_INFO2", id,"MENU_INFO3", id,"MENU_INFO4", id, "MENU_EXIT")
    show_menu(id, KEYSMENU, menu, -1, "Mod Info")
}

// Admin Menu
show_menu_admin(id)
{
    static menu[200]
    
    formatex(menu, sizeof menu - 1, "\y%L\w^n^n1. %L^n2. %L^n3. %L^n4. %L^n5. %L^n6. %L^n^n0. %L", id, "MENU_ADMIN_TITLE", id, "MENU_ADMIN1", id,"MENU_ADMIN2", id,"MENU_ADMIN3", id,"MENU_ADMIN4", id,"MENU_ADMIN5", id,"MENU_ADMIN6", id, "MENU_EXIT")
    show_menu(id, KEYSMENU, menu, -1, "Admin Menu")
}

// Player List Menu
show_menu_player_list(id)
{
    static menu[400], len
    len = 0
    
    // Title
    switch (PL_ACTION)
    {
        case 0: len += formatex(menu[len], sizeof menu - 1 - len, "\y%L ", id, "MENU_ADMIN1")
        case 1: len += formatex(menu[len], sizeof menu - 1 - len, "\y%L ", id, "MENU_ADMIN2")
        case 2: len += formatex(menu[len], sizeof menu - 1 - len, "\y%L ", id, "MENU_ADMIN3")
        case 3: len += formatex(menu[len], sizeof menu - 1 - len, "\y%L ", id, "MENU_ADMIN4")
    }
    
    len += formatex(menu[len], sizeof menu - 1 - len, "\r[%d-%d]^n^n", PL_STARTID+1, (PL_STARTID+7 <= g_maxplayers) ? PL_STARTID+7 : g_maxplayers)
    
    // 1-7. player list
    static player
    for (player = PL_STARTID+1; (PL_STARTID+7 <= g_maxplayers) ? (player <= PL_STARTID+7) : (player <= g_maxplayers); player++)
    {
        if (is_user_connected(player))
        {
            static name[32]
            get_user_name(player, name, sizeof name - 1)
            
            switch (PL_ACTION)
            {
                case 0:
                {
                    if (g_zombie[player])
                    {
                        if (allowed_human(player))
                            len += formatex(menu[len], sizeof menu - 1 - len, "\w%d. %s \r[%L]^n", player-PL_STARTID, name, id, "CLASS_ZOMBIE")
                        else
                            len += formatex(menu[len], sizeof menu - 1 - len, "\d%d. %s [%L]^n", player-PL_STARTID, name, id, "CLASS_ZOMBIE")
                    }
                    else
                    {
                        if (allowed_zombie(player))
                            len += formatex(menu[len], sizeof menu - 1 - len, "\w%d. %s \y[%L]^n", player-PL_STARTID, name, id, "CLASS_HUMAN")
                        else
                            len += formatex(menu[len], sizeof menu - 1 - len, "\d%d. %s [%L]^n", player-PL_STARTID, name, id, "CLASS_HUMAN")
                    }
                }
                case 1:
                {
                    if (allowed_nemesis(player))
                        len += formatex(menu[len], sizeof menu - 1 - len, "\w%d. %s^n", player-PL_STARTID, name)
                    else
                        len += formatex(menu[len], sizeof menu - 1 - len, "\d%d. %s^n", player-PL_STARTID, name)
                }
                case 2:
                {
                    if (allowed_survivor(player))
                        len += formatex(menu[len], sizeof menu - 1 - len, "\w%d. %s^n", player-PL_STARTID, name)
                    else
                        len += formatex(menu[len], sizeof menu - 1 - len, "\d%d. %s^n", player-PL_STARTID, name)
                }
                case 3:
                {
                    if (allowed_respawn(player))
                        len += formatex(menu[len], sizeof menu - 1 - len, "\w%d. %s^n", player-PL_STARTID, name)
                    else
                        len += formatex(menu[len], sizeof menu - 1 - len, "\d%d. %s^n", player-PL_STARTID, name)
                }        
            }
        }
        else
        {
            len += formatex(menu[len], sizeof menu - 1 - len, "\d%d. -----^n", player-PL_STARTID)
        }
    }
    
    // 8. Back - 9. Next - 0. Exit
    len += formatex(menu[len], sizeof menu - 1 - len, "\w^n8. %L^n9. %L^n^n0. %L", id, "MENU_BACK", id, "MENU_NEXT", id, "MENU_EXIT")
    
    show_menu(id, KEYSMENU, menu, -1, "Player List Menu")
}

/*================================================================================
 [Menu Handlers]
=================================================================================*/

// Game Menu
public menu_game(id, key)
{
    switch (key)
    {
        case 0: get_pcvar_num(cvar_extraitems) ? show_menu_extras(id) : client_print(id, print_chat, "[ZP] %L", id ,"CMD_NOT_EXTRAS")
        case 1: get_pcvar_num(cvar_zclasses) ? show_menu_zombie(id) : client_print(id, print_chat, "[ZP] %L", id ,"CMD_NOT_ZCLASSES")
        case 2: is_user_alive(id) && is_player_stuck(id) ? do_random_spawn(id) : client_print(id, print_chat, "[ZP] %L", id ,"CMD_NOT_STUCK")
        case 3: show_menu_info(id)
        case 4: get_user_flags(id) & ACCESS_FLAG ? show_menu_admin(id) : client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT_ACCESS")
    }
    
    return PLUGIN_HANDLED;
}

// Admin Menu
public menu_admin(id, key)
{    
    switch (key)
    {
        case 0 .. 3:
        {
            PL_ACTION = key;
            show_menu_player_list(id);
        }
        case 4:
        {
            allowed_swarm() ? command_swarm(id) : client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
            show_menu_admin(id);
        }
        case 5:
        {
            allowed_multi() ? command_multi(id) : client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
            show_menu_admin(id);
        }
    }
    
    return PLUGIN_HANDLED
}

// Player List Menu
public menu_player_list(id, key)
{
    switch (key)
    {
        case 7: if (PL_STARTID-7 >= 0) PL_STARTID -= 7 // back
        case 8: if (PL_STARTID+7 < g_maxplayers) PL_STARTID += 7 // next
        case 9: // go back to admin menu
        {
            show_menu_admin(id);
            return PLUGIN_HANDLED
        }
        default: // perform action on player
        {
            if (is_user_connected(PL_TARGET))
            {
                switch (PL_ACTION)
                {
                    case 0:
                    {
                        if (g_zombie[PL_TARGET])
                        {
                            if (allowed_human(PL_TARGET))
                                command_human(id, PL_TARGET)
                            else
                                client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
                        }
                        else
                        {
                            if (allowed_zombie(PL_TARGET))
                                command_zombie(id, PL_TARGET)
                            else
                                client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
                        }
                    }
                    case 1:
                    {
                        if (allowed_nemesis(PL_TARGET))
                            command_nemesis(id, PL_TARGET)
                        else
                            client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
                    }
                    case 2:
                    {
                        if (allowed_survivor(PL_TARGET))
                            command_survivor(id, PL_TARGET)
                        else
                            client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
                    }
                    case 3:
                    {
                        if (allowed_respawn(PL_TARGET))
                            command_respawn(id, PL_TARGET)
                        else
                            client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
                    }        
                }
            }
            else
            {
                client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
            }
        }
    }
    
    show_menu_player_list(id);
    return PLUGIN_HANDLED
}

// Info Menu
public menu_info(id, key)
{    
    switch (key)
    {
        case 0: 
        {
            static motd[1000], len, weather, lights[2]
            len = 0
            weather = 0
            
            get_pcvar_string(cvar_lightning, lights, sizeof lights - 1)
            strtolower(lights)
            
            len += formatex(motd[len], sizeof motd - 1 - len, "%L ", id, "MOTD_INFO11", "Zombie Plague", PLUGIN_VERSION, "MeRcyLeZZ")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO12")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_A")
            
            #if defined AMBIENCE_FOG
            len += formatex(motd[len], sizeof motd - 1 - len, (weather < 1) ? " %L" : ". %L", id, "MOTD_FOG")
            weather++
            #endif
            #if defined AMBIENCE_RAIN
            len += formatex(motd[len], sizeof motd - 1 - len, (weather < 1) ? " %L" : ". %L", id, "MOTD_RAIN")
            weather++
            #endif
            #if defined AMBIENCE_SNOW
            len += formatex(motd[len], sizeof motd - 1 - len, (weather < 1) ? " %L" : ". %L", id, "MOTD_SNOW")
            weather++
            #endif
            if (weather < 1) len += formatex(motd[len], sizeof motd - 1 - len, " %L", id, "MOTD_DISABLED")
            
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_B", lights)
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_C", id, get_pcvar_num(cvar_triggered) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (lights[0] == 'a' && get_pcvar_num(cvar_thunder)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_D", get_pcvar_num(cvar_thunder))
            len += formatex(motd[len], sizeof motd - 1 - 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")
            len += formatex(motd[len], sizeof motd - 1 - 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")
            if (get_pcvar_num(cvar_deathmatch)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_G", get_pcvar_num(cvar_spawnprotection))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_H", id, get_pcvar_num(cvar_randspawn) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_I", id, get_pcvar_num(cvar_extraitems) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_J", id, get_pcvar_num(cvar_zclasses) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_K", id, get_pcvar_num(cvar_cnvg) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO1_L", id, get_pcvar_num(cvar_cflash) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            
            show_motd(id, motd)
        }
        case 1:
        {
            static motd[650], len
            len = 0
            
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_A", get_pcvar_num(cvar_humanhp))
            if (get_pcvar_num(cvar_humanlasthp) > 0) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_B", get_pcvar_num(cvar_humanlasthp))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_C", get_pcvar_num(cvar_humanspd))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_D", floatround(get_pcvar_float(cvar_humangravity)*800))
            len += formatex(motd[len], sizeof motd - 1 - 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")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_F", get_pcvar_num(cvar_ammodamage))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_G", id, get_pcvar_num(cvar_firegrenades) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_H", id, get_pcvar_num(cvar_frostgrenades) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_I", id, get_pcvar_num(cvar_flaregrenades) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO2_J", id, get_pcvar_num(cvar_knockback) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            
            show_motd(id, motd)
        }
        case 2: 
        {
            static motd[650], len
            len = 0
            
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_A", get_pcvar_num(cvar_hp))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_B", get_pcvar_num(cvar_firsthp))            
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_C", floatround(get_pcvar_float(cvar_armor)*100))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_D", get_pcvar_num(cvar_speed))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_E", floatround(get_pcvar_float(cvar_gravity)*800))
            if (get_pcvar_num(cvar_bonushp)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_F", get_pcvar_num(cvar_bonushp))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_G", id, get_pcvar_num(cvar_painfree) > 0 ? get_pcvar_num(cvar_painfree) > 1 ? "MOTD_LASTZOMBIE" : "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_H", id, get_pcvar_num(cvar_bleeding) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO3_I", get_pcvar_num(cvar_ammoinfect))
            
            show_motd(id, motd)
        }
        case 3:
        {
            static motd[900], len, nemhp[5], survhp[5]
            len = 0
            
            num_to_str(get_pcvar_num(cvar_nemhp), nemhp, sizeof nemhp - 1) // nemesis health 
            num_to_str(get_pcvar_num(cvar_survhp), survhp, sizeof survhp - 1) // survivor health
            
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_A", id, get_pcvar_num(cvar_nem) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (get_pcvar_num(cvar_nem)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_B", get_pcvar_num(cvar_nemchance))
            if (get_pcvar_num(cvar_nem)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_C", get_pcvar_num(cvar_nemhp) > 0 ? nemhp : "[Auto]")
            if (get_pcvar_num(cvar_nem)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_D", get_pcvar_num(cvar_nemspd))
            if (get_pcvar_num(cvar_nem)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_E", floatround(get_pcvar_float(cvar_nemgravity)*800))
            if (get_pcvar_num(cvar_nem)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_F", id, get_pcvar_num(cvar_nemleap) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (get_pcvar_num(cvar_nem)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_G", id, get_pcvar_num(cvar_nempainfree) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_H", id, get_pcvar_num(cvar_surv) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (get_pcvar_num(cvar_surv)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_I", get_pcvar_num(cvar_survchance))
            if (get_pcvar_num(cvar_surv)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_J", get_pcvar_num(cvar_survhp) > 0 ? survhp : "[Auto]")
            if (get_pcvar_num(cvar_surv)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_K", get_pcvar_num(cvar_survspd))
            if (get_pcvar_num(cvar_surv)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_L", floatround(get_pcvar_float(cvar_survgravity)*800))
            if (get_pcvar_num(cvar_surv)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_M", id, get_pcvar_num(cvar_survleap) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (get_pcvar_num(cvar_surv)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_N", id, get_pcvar_num(cvar_survpainfree) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_O", id, get_pcvar_num(cvar_swarm) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (get_pcvar_num(cvar_swarm)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_P", get_pcvar_num(cvar_swarmchance))
            len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_Q", id, get_pcvar_num(cvar_multi) ? "MOTD_ENABLED" : "MOTD_DISABLED")
            if (get_pcvar_num(cvar_multi)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_R", get_pcvar_num(cvar_multichance))
            if (get_pcvar_num(cvar_multi)) len += formatex(motd[len], sizeof motd - 1 - len, "%L", id, "MOTD_INFO4_S", floatround(get_pcvar_float(cvar_multiratio)*100))
            
            show_motd(id, motd)
        }
        default: return PLUGIN_HANDLED;
    }
    
    set_task(0.2, "show_menu_info", id);
    
    return PLUGIN_HANDLED;
}

// Extra Items Menu
public menu_extras(id, key)
{
    // Nemesis or Survivor shouldnt get extra items
    if (!is_user_alive(id) || g_survivor[id] || g_nemesis[id])
    {
        client_print(id, print_chat, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED;
    }
    
    switch (key)
    {
        case 0 .. sizeof g_extra_items - 1:
        {
            if (!g_zombie[id])
            {
                if  (g_ammopacks[id] >= g_extra_costs[key])
                {
                    if (MAXAMMO[get_weaponid(g_extra_items[key])] > 1) // (this adds grenades support)
                    {
                        // Drop previous weapon and give BP ammo for the new one
                        (1<<get_weaponid(g_extra_items[key])) & PRIMARY_WEAPONS_BIT_SUM ? drop_weapons(id, 1) : drop_weapons(id, 2);
                        fm_set_user_bpammo(id, get_weaponid(g_extra_items[key]), MAXAMMO[get_weaponid(g_extra_items[key])]);
                    }
                    fm_give_item(id, g_extra_items[key]);
                    g_ammopacks[id] -= g_extra_costs[key];
                }
                else
                {
                    client_print(id, print_chat, "[ZP] %L", id ,"NOT_ENOUGH_AMMO")
                }
            }
            else
            {
                client_print(id, print_chat, "[ZP] %L", id ,"CMD_HUMAN_ONLY")
            }
        }
        case sizeof g_extra_items + 0:
        {
            if (!g_zombie[id])
            {
                if  (g_ammopacks[id] >= g_extra_costs2[EXTRA_NVISION])
                {
                    g_nvision[id] = true; // Night Vision
                    g_nvisionenabled[id] = 1
                    
                    if (get_pcvar_num(cvar_cnvg)) // custom nvg?
                    {
                        remove_task(id+NVISION_TASK);
                        set_user_nvision(id+NVISION_TASK)
                    }
                    else set_user_gnvision(id, 1)
                    
                    g_ammopacks[id] -= g_extra_costs2[EXTRA_NVISION];
                }
                else
                {
                    client_print(id, print_chat, "[ZP] %L", id ,"NOT_ENOUGH_AMMO")
                }
            }
            else
            {
                client_print(id, print_chat, "[ZP] %L", id ,"CMD_HUMAN_ONLY")
            }
        }
        case sizeof g_extra_items + 1:
        {
            if (g_zombie[id])
            {
                if (!g_endround && !g_swarmround && !g_nemround && !g_survround && fnGetZombies() > 1)
                {
                    if  (g_ammopacks[id] >= g_extra_costs2[EXTRA_ANTIDOTE])
                    {
                        humanme(id) // Antidote
                        g_ammopacks[id] -= g_extra_costs2[EXTRA_ANTIDOTE];
                    }
                    else
                    {
                        client_print(id, print_chat, "[ZP] %L", id ,"NOT_ENOUGH_AMMO")
                    }
                }
                else
                {
                    client_print(id, print_chat, "[ZP] %L", id ,"CMD_NOT_CANTUSE")
                }
            }
            else
            {
                client_print(id, print_chat, "[ZP] %L", id ,"CMD_ZOMBIE_ONLY")
            }
        }
        case sizeof g_extra_items + 2:
        {
            if (g_zombie[id])
            {
                if (!g_swarmround && !g_nemround && !g_survround && !g_nodamage[id])
                {
                    if  (g_ammopacks[id] >= g_extra_costs2[EXTRA_MADNESS])
                    {
                        g_nodamage[id] = true // Zombie Madness
                        infectionFX(id) // aura
                        engfunc(EngFunc_EmitSound, id, CHAN_VOICE, zombie_madness[random_num(0, sizeof zombie_madness - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
                        set_task(5.0, "madness_over", id+BLOOD_TASK)                        
                        g_ammopacks[id] -= g_extra_costs2[EXTRA_MADNESS];
                    }
                    else
                    {
                        client_print(id, print_chat, "[ZP] %L", id ,"NOT_ENOUGH_AMMO")
                    }
                }
                else
                {
                    client_print(id, print_chat, "[ZP] %L", id ,"CMD_NOT_CANTUSE")
                }
            }
            else
            {
                client_print(id, print_chat, "[ZP] %L", id ,"CMD_ZOMBIE_ONLY")
            }
        }
        case sizeof g_extra_items + 3:
        {
            if (g_zombie[id])
            {
                if (!g_swarmround && !g_nemround && !g_survround)
                {
                    if  (g_ammopacks[id] >= g_extra_costs2[EXTRA_INFBOMB])
                    {
                        fm_give_item(id, "weapon_hegrenade"); // Infection Bomb
                        g_ammopacks[id] -= g_extra_costs2[EXTRA_INFBOMB];
                    }
                    else
                    {
                        client_print(id, print_chat, "[ZP] %L", id ,"NOT_ENOUGH_AMMO")
                    }
                }
                else
                {
                    client_print(id, print_chat, "[ZP] %L", id ,"CMD_NOT_CANTUSE")
                }
            }
            else
            {
                client_print(id, print_chat, "[ZP] %L", id ,"CMD_ZOMBIE_ONLY")
            }
        }
    }
    
    return PLUGIN_HANDLED;
}

// Buy Menu 1
public menu_buy1(id, key)
{
    // Zombies or survivors get no guns
    if (!is_user_alive(id) || g_zombie[id] || g_survivor[id] || !g_canbuy[id])
        return PLUGIN_HANDLED;
    
    // Exit
    if (key >= sizeof g_primary_names) return PLUGIN_HANDLED;
    
    // Drop previous weapons
    drop_weapons(id, 1);
    drop_weapons(id, 2);
    
    // Strip off from weapons
    fm_strip_user_weapons(id)
    fm_give_item(id, "weapon_knife")
    
    // Give the new weapon
    fm_give_item(id, g_primary_items[key])
    fm_set_user_bpammo(id, get_weaponid(g_primary_items[key]), MAXAMMO[get_weaponid(g_primary_items[key])])
    
    static i // weapons bought, give additional items
    for (i = 0; i < sizeof g_additional_items; i++) fm_give_item(id, g_additional_items[i])
    
    g_canbuy[id] = false
    show_menu_buy2(id); // show pistol menu
    
    return PLUGIN_HANDLED;
}

// Buy Menu 2
public menu_buy2(id, key)
{    
    // Zombies or survivors get no guns
    if (!is_user_alive(id) || g_zombie[id] || g_survivor[id])
        return PLUGIN_HANDLED;
    
    // Exit
    if (key >= sizeof g_secondary_names) return PLUGIN_HANDLED;
    
    // Drop secondary gun again, in case we picked another (bugfix)
    drop_weapons(id, 2);
    
    // Give the new weapon
    fm_give_item(id, g_secondary_items[key])
    fm_set_user_bpammo(id, get_weaponid(g_secondary_items[key]), MAXAMMO[get_weaponid(g_secondary_items[key])])
    
    return PLUGIN_HANDLED;
}

// Zombie Menu
public menu_zombie(id, key)
{    
    switch (key)
    {
        case 0: client_print(id, print_chat, "[ZP] %L: %L", id, "ZOMBIE_SELECT", id, "CLASS_ZOMBIE1")
        case 1: client_print(id, print_chat, "[ZP] %L: %L", id, "ZOMBIE_SELECT", id, "CLASS_ZOMBIE2")
        case 2: client_print(id, print_chat, "[ZP] %L: %L", id, "ZOMBIE_SELECT", id, "CLASS_ZOMBIE3")
        case 3: client_print(id, print_chat, "[ZP] %L: %L", id, "ZOMBIE_SELECT", id, "CLASS_ZOMBIE4")
        case 4: client_print(id, print_chat, "[ZP] %L: %L", id, "ZOMBIE_SELECT", id, "CLASS_ZOMBIE5")
        case 5: client_print(id, print_chat, "[ZP] %L: %L", id, "ZOMBIE_SELECT", id, "CLASS_ZOMBIE6")
        default: return PLUGIN_HANDLED;
    }
    
    g_zombieclassnext[id] = key;
    
    client_print(id, print_chat, "[ZP] %L: %d %L: %d %L: %d %L: %d%%", id, "ZOMBIE_ATTRIB1", floatround(get_pcvar_float(cvar_hp)*zombie_multihp[g_zombieclassnext[id]]/100), id, "ZOMBIE_ATTRIB2", floatround(get_pcvar_float(cvar_speed)*zombie_multispd[g_zombieclassnext[id]]/100),
    id, "ZOMBIE_ATTRIB3", floatround(get_pcvar_float(cvar_gravity)*zombie_multigrav[g_zombieclassnext[id]]/100*800), id, "ZOMBIE_ATTRIB4", zombie_multiknockback[g_zombieclassnext[id]])
    
    return PLUGIN_HANDLED;
}

/*================================================================================
 [Admin Commands]
=================================================================================*/

// zp_zombie [target]
public cmd_zombie(id, level, cid)
{
    if (!cmd_access(id, level, cid, 2)) // check for access flag
        return PLUGIN_HANDLED
    
    static arg[32], player
    read_argv(1, arg, sizeof arg - 1)
    player = cmd_target(id, arg, 4 | 2) // find target
    
    if (!player) return PLUGIN_HANDLED
    
    if (!allowed_zombie(player))
    {    
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_zombie(id, player)
    
    return PLUGIN_HANDLED
}

// zp_human [target]
public cmd_human(id, level, cid)
{    
    if (!cmd_access(id, level, cid, 2)) // check for access flag
        return PLUGIN_HANDLED
    
    static arg[32], player
    read_argv(1, arg, sizeof arg - 1)
    player = cmd_target(id, arg, 4 | 2) // find target
    
    if (!player) return PLUGIN_HANDLED
    
    if (!allowed_human(player))
    {
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_human(id, player)
    
    return PLUGIN_HANDLED
}

// zp_survivor [target]
public cmd_survivor(id, level, cid)
{    
    if (!cmd_access(id, level, cid, 2)) // check for access flag
        return PLUGIN_HANDLED
    
    static arg[32], player
    read_argv(1, arg, sizeof arg - 1)
    player = cmd_target(id, arg, 4 | 2) // find target
    
    if (!player) return PLUGIN_HANDLED
    
    if (!allowed_survivor(player))
    {
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_survivor(id, player)
    
    return PLUGIN_HANDLED
}

// zp_nemesis [target]
public cmd_nemesis(id, level, cid)
{    
    if (!cmd_access(id, level, cid, 2)) // check for access flag
        return PLUGIN_HANDLED
    
    static arg[32], player
    read_argv(1, arg, sizeof arg - 1)
    player = cmd_target(id, arg, 4 | 2) // find target
    
    if (!player) return PLUGIN_HANDLED
    
    if (!allowed_nemesis(player))
    {
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_nemesis(id, player)
    
    return PLUGIN_HANDLED
}

// zp_respawn [target]
public cmd_respawn(id, level, cid)
{    
    if (!cmd_access(id, level, cid, 2)) // check for access flag
        return PLUGIN_HANDLED
    
    static arg[32], player
    read_argv(1, arg, sizeof arg - 1)
    player = cmd_target(id, arg, 2) // find target
    
    if (!player) return PLUGIN_HANDLED
    
    if (!allowed_respawn(player))
    {
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_respawn(id, player)
    
    return PLUGIN_HANDLED
}

// zp_swarm
public cmd_swarm(id, level, cid)
{    
    if (!cmd_access(id, level, cid, 1)) // check for access flag
        return PLUGIN_HANDLED
    
    if (!allowed_swarm())
    {
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_swarm(id)
    
    return PLUGIN_HANDLED
}

// zp_multi
public cmd_multi(id, level, cid)
{
    if (!cmd_access(id, level, cid, 1)) // check for access flag
        return PLUGIN_HANDLED
    
    if (!allowed_multi())
    {
        client_print(id, print_console, "[ZP] %L", id, "CMD_NOT")
        return PLUGIN_HANDLED
    }
    
    command_multi(id)
    
    return PLUGIN_HANDLED
}

/*================================================================================
 [Message Hooks]
=================================================================================*/

// Current Weapon
public message_cur_weapon(msg_id, msg_dest, msg_entity)
{    
    // Player not alive or not an active weapon
    if (!is_user_alive(msg_entity) || get_msg_arg_int(1) != 1)
        return;
    
    static weapon, clip
    weapon = get_msg_arg_int(2) // get weapon ID
    clip = get_msg_arg_int(3) // get weapon clip
    
    g_currentweapon[msg_entity] = weapon // store weapon id for reference
    
    // Retrieve our custom bpammo from the weapon entity
    static wname[32], weapon_ent, extra_ammo
    get_weaponname(weapon, wname, sizeof wname - 1);
    weapon_ent = fm_find_ent_by_owner(-1, wname, msg_entity); // get weapon entity
    extra_ammo = pev(weapon_ent, pev_iuser1); // get additional ammo (hack)
    
    if (extra_ammo) // spare ammo goes to our bpammo (dont exceed max though)
    {
        fm_set_user_bpammo(msg_entity, weapon, min(fm_get_user_bpammo(msg_entity, weapon)+extra_ammo, MAXAMMO[weapon]))
        set_pev(weapon_ent, pev_iuser1, 0)
    }
    
    // Unlimited BPAmmo
    if ((get_pcvar_num(cvar_infammo) && MAXAMMO[weapon] > 1) || (g_survivor[msg_entity] && weapon == CSW_M249))
    {
        if (fm_get_user_bpammo(msg_entity, weapon) < MAXAMMO[weapon])
            fm_set_user_bpammo(msg_entity, weapon, MAXAMMO[weapon])
    }
    
    // Unlimited Clip Ammo
    if (get_pcvar_num(cvar_infammo) > 1 || (g_survivor[msg_entity] && weapon == CSW_M249))
    {
        if (MAXCLIP[weapon] > 2) // skip grenades
        {
            set_msg_arg_int(3, get_msg_argtype(3), MAXCLIP[weapon]) // HUD should show full clip all the time
            if (clip < 2) fm_set_weapon_ammo(weapon_ent, MAXCLIP[weapon]) // refill
        }
    }
    
    // Bots automatically buy ammo when needed
    if (is_user_bot(msg_entity) && !g_zombie[msg_entity] && !g_survround && g_ammopacks[msg_entity] > 0 && MAXCLIP[weapon] > 2 && fm_get_user_bpammo(msg_entity, weapon) <= BUYAMMO[weapon])
            clcmd_buyammo(msg_entity);
    
    // Replace custom weapon models
    replace_models(msg_entity);
    
    // If zombie is not holding a knife/infection bomb for some reason
    if (g_zombie[msg_entity] && weapon != CSW_KNIFE && weapon != CSW_HEGRENADE)
        engclient_cmd(msg_entity, "weapon_knife"); // force him to do so
}

// Take off player's money
public message_money(msg_id, msg_dest, msg_entity)
{
    if (!get_pcvar_num(cvar_removemoney))
        return PLUGIN_CONTINUE;
    
    set_pdata_int(msg_entity, OFFSET_CSMONEY, 0, OFFSET_LINUX);
    return PLUGIN_HANDLED;
}

// Fix the HL engine bug when HP is multiples of 256
public message_health(msg_id, msg_dest, msg_entity)
{
    static health
    health = get_msg_arg_int(1) // get health
    
    if (health < 256) return; // dont bother
    
    if (floatfract(float(health)/256.0) == 0.0) // check if we need to fix it
        fm_set_user_health(msg_entity, health+1)
    
    set_msg_arg_int(1, get_msg_argtype(1), 255); // HUD only shows as much as 255 hp
}

// Hide player's money
public message_hideweapon()
{
    if (!get_pcvar_num(cvar_removemoney))
        return;
    
    set_msg_arg_int(1, get_msg_argtype(1), get_msg_arg_int(1) | HIDE_MONEY);
}

// Block flashlight battery messages when it's not available, or if custom flashlight is enabled instead
public message_flashbat(msg_id, msg_dest, msg_entity)
{    
    if (get_pcvar_num(cvar_cflash) || !is_user_alive(msg_entity) || g_zombie[msg_entity] || g_survivor[msg_entity])
        return PLUGIN_HANDLED;
    
    return PLUGIN_CONTINUE;
}

// Flashbangs should only affect zombies
public message_screenfade(msg_id, msg_dest, msg_entity)
{
    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)
        return PLUGIN_CONTINUE;
    
    if (g_zombie[msg_entity] && !g_nemesis[msg_entity]) // nemesis shouldn't be FBed
    {
        // Set flash color to nighvision's
        set_msg_arg_int(4, get_msg_argtype(4), get_pcvar_num(cvar_nvgcolor[0]))
        set_msg_arg_int(5, get_msg_argtype(5), get_pcvar_num(cvar_nvgcolor[1]))
        set_msg_arg_int(6, get_msg_argtype(6), get_pcvar_num(cvar_nvgcolor[2]))
        return PLUGIN_CONTINUE;
    }
    
    return PLUGIN_HANDLED;
}

// Hide bomb carrier on the scoreboard
public message_scoreattrib()
{
    if (get_msg_arg_int(2) & ATTRIB_BOMB)
        set_msg_arg_int(2, get_msg_argtype(2), get_msg_arg_int(2) & ~ATTRIB_BOMB)
}

// Hide C4 icon display on HUD
public message_statusicon()
{
    static sprite[3]
    get_msg_arg_string(2, sprite, sizeof sprite - 1)
    
    if(sprite[0] == 'c' && sprite[1] == '4')
        return PLUGIN_HANDLED;
    
    return PLUGIN_CONTINUE;
}

#if defined HANDLE_MODELS_ON_SEPARATE_ENT
// Set correct model for player corpses
public message_clcorpse()
{
    set_msg_arg_string(1, g_playermodel[get_msg_arg_int(12)])
}
#endif

// Block weapon pickup messsages
public message_weappickup(msg_id, msg_dest, msg_entity)
{    
    if (g_zombie[msg_entity]) // prevent zombies from seeing any weapon pickup icon
        return PLUGIN_HANDLED;
    
    return PLUGIN_CONTINUE;
}

// Block ammo pickup messages
public message_ammopickup(msg_id, msg_dest, msg_entity)
{    
    if (g_zombie[msg_entity]) // prevent zombies from seeing any ammo pickup icon
        return PLUGIN_HANDLED;

    return PLUGIN_CONTINUE;
}

// Block hostage HUD display
public message_scenario()
{     
    if (get_msg_args() > 1)
    {
        static sprite[8]
        get_msg_arg_string(2, sprite, sizeof sprite - 1)

        if (equal(sprite, "hostage"))
            return PLUGIN_HANDLED;
    }

    return PLUGIN_CONTINUE;
}

// Block hostages from appearing on radar
public message_hostagepos()
{
    return PLUGIN_HANDLED;
}

// Block bomb dropped message so it doesn't show up in the radar
public message_bombdrop()
{
    return PLUGIN_HANDLED;
}

// Block some text messages
public message_textmsg()
{ 
    static textmsg[22]
    get_msg_arg_string(2, textmsg, sizeof textmsg - 1);
    
    // Game restarting, reset scores and call round end to balance the teams
    if (equal(textmsg, "#Game_will_restart_in"))
    {
        g_scorehumans = 0
        g_scorezombies = 0
        event_round_end()
    }
    // Block bomb/round related messages
    else if (equal(textmsg, "#Game_bomb_drop") || equal(textmsg, "#Target_Saved") || equal(textmsg, "#Round_Draw") || equal(textmsg, "#Terrorists_Win") || equal(textmsg, "#CTs_Win"))
    {
        return PLUGIN_HANDLED;
    }
    
    return PLUGIN_CONTINUE;
}

// Block round win audio messages
public message_sendaudio()
{
    static audio[17]
    get_msg_arg_string(2, audio, sizeof audio - 1)
    
    if(equal(audio, "%!MRAD_terwin") || equal(audio, "%!MRAD_ctwin") || equal(audio, "%!MRAD_rounddraw"))
        return PLUGIN_HANDLED;
    
    return PLUGIN_CONTINUE;
}

// Send actual team scores (T = zombies // CT = humans)
public message_teamscore()
{
    static team[2]
    get_msg_arg_string(1, team, sizeof team - 1)
    
    if (team[0] == 'C') // CT
        set_msg_arg_int(2, get_msg_argtype(2), g_scorehumans);
    
    else if (team[0] == 'T') // Terrorist
        set_msg_arg_int(2, get_msg_argtype(2), g_scorezombies);
}

// Team Switch (or player joining a team for first time)
public message_teaminfo(msg_id, msg_dest)
{
    // Only hook global messages
    if (msg_dest != MSG_ALL && msg_dest != MSG_BROADCAST) return;
    
    static id // get player id
    id = get_msg_arg_int(1)
    
    // Don't pick up our own TeamInfo messages for this player (bugfix)
    if (g_switchingteam[id]) return;
    
    static team[2] // get his new team
    get_msg_arg_string(2, team, sizeof team - 1)
    
    if (team[0] == 'C') // CT
    {
        if (!g_newround) // round started
        {
            if (g_survround && fnGetHumans()) // survivor alive --> switch to T and spawn as zombie
            {
                g_respawn_as_zombie[id] = true;
                remove_task(id+TEAM_TASK)
                fm_set_user_team(id, CS_TEAM_T)
                set_msg_arg_string(2, "Terrorist")
            }
            else if (!fnGetZombies()) // no zombies alive --> switch to T and spawn as zombie
            {
                g_respawn_as_zombie[id] = true;
                remove_task(id+TEAM_TASK)
                fm_set_user_team(id, CS_TEAM_T)
                set_msg_arg_string(2, "Terrorist")
            }
        }
    }
    else if (team[0] == 'T') // Terrorist
    {
        if (!g_newround) // round started
        {
            if ((g_swarmround || g_survround) && fnGetHumans()) // survivor alive or swarm round w/ humans --> spawn as zombie
            {
                g_respawn_as_zombie[id] = true;
            }
            else if (fnGetZombies()) // zombies alive --> switch to CT
            {
                remove_task(id+TEAM_TASK)
                fm_set_user_team(id, CS_TEAM_CT)
                set_msg_arg_string(2, "CT")
            }
        }
    }
    
    // Enable spectators' nightvision if not spawning
    set_task(0.2, "spec_nvision", id)
}

/*================================================================================
 [Main Functions]
=================================================================================*/

// Make Zombie Task
public make_zombie_task()
{
    // Call make a zombie with playerid MAKEZOMBIE_TASK and no further args
    make_a_zombie(MAKEZOMBIE_TASK, 0, 0, 0, 0);
}

// Make a Zombie Function
make_a_zombie(playerid, nem, surv, swarm, multi)
{    
    // Get alive players count
    static iPlayersnum
    iPlayersnum = fnGetAlive()
    
    if (iPlayersnum < 1) // not enough players
    {
        set_task(10.0, "make_zombie_task", MAKEZOMBIE_TASK)
        return;
    }
    
    g_models_i = 0.0 // reset model change count
    g_teams_i = 0.0 // reset teams change count
    
    // round starting
    g_newround = false
    g_survround = false
    g_nemround = false
    g_swarmround = false
    
    static id
    
    if ((playerid == MAKEZOMBIE_TASK && random_num(1, get_pcvar_num(cvar_survchance)) == get_pcvar_num(cvar_surv)) || surv) // Survival Chance
    {        
        g_survround = true // survivor round
        
        if (playerid == MAKEZOMBIE_TASK) // choose player randomly?
            id = fnGetRandomAlive(random_num(1, iPlayersnum))
        else
            id = playerid
        
        g_survivor[id] = true // set var
        
        if (get_pcvar_num(cvar_survleap)) // give survivor leap?
            fm_give_item(id, "item_longjump")
        
        // Remove previous tasks
        remove_task(id+TEAM_TASK)
        
        // Switch to CT
        if (fm_get_user_team(id) != CS_TEAM_CT) // need to change team?
        {        
            fm_set_user_team(id, CS_TEAM_CT)
            fm_set_user_team_msg(id+TEAM_TASK)
        }
        
        // Set Health [0 = auto]
        if (!get_pcvar_num(cvar_survhp))
            fm_set_user_health(id, fnGetHumans()*get_pcvar_num(cvar_humanhp))
        else
            fm_set_user_health(id, get_pcvar_num(cvar_survhp))
        
        // Set Gravity
        set_pev(id, pev_gravity, get_pcvar_float(cvar_survgravity))
        
        // glow?
        if (get_pcvar_num(cvar_survglow))
        {
            #if defined HANDLE_MODELS_ON_SEPARATE_ENT
            fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id), kRenderFxGlowShell, 0, 0, 255, kRenderNormal, 25)
            #else
            fm_set_rendering(id, kRenderFxGlowShell, 0, 0, 255, kRenderNormal, 25)
            #endif
        }
        
        fm_strip_user_weapons(id) // strip player from weapons
        fm_give_item(id, "weapon_m249") // give M249 machinegun only
        
        // Turn off his flashlight
        turn_off_flashlight(id)
        set_task(3.0, "turn_off_flashlight", id) // bugfix
        
        // Give the survivor a bright light
        set_pev(id, pev_effects, pev(id, pev_effects) | EF_BRIGHTLIGHT)
        
        // Survivor bots will also need nightvision to see in the dark
        if (is_user_bot(id)) fm_set_bot_nvg(id, 1);
        
        // Play survivor sound
        PlaySound(sound_survivor[random_num(0, sizeof sound_survivor -1)]);
        
        static name[32]
        get_user_name(id, name, sizeof name - 1)
        
        // show Survivor HUD notice
        set_hudmessage(20, 20, 255, -1.0, 0.25, 1, 0.0, 5.0, 1.0, 1.0, -1)
        ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_SURVIVOR", name)
    
        // Turn the rest of players into zombies
        for (id = 1; id <= g_maxplayers; id++)
        {
            if (!is_user_alive(id)) // skip dead
                continue;
        
            if (g_survivor[id]) // skip the survivor
                continue;            
            
            zombieme(id, 0, 1) // turn into a zombie
        }
    }
    else if ((playerid == MAKEZOMBIE_TASK && random_num(1, get_pcvar_num(cvar_swarmchance)) == get_pcvar_num(cvar_swarm)) || swarm) // Swarm Chance
    {        
        g_swarmround = true // swarm round
        
        // Turn all the T into zombies
        for (id = 1; id <= g_maxplayers; id++)
        {            
            if (!is_user_alive(id)) // not alive
                continue;
            
            if (fm_get_user_team(id) == CS_TEAM_T) // only Terrorists
                zombieme(id, 0, 1) // turn into a zombie
        }
        
        // Play swarm sound
        PlaySound(sound_swarm[random_num(0, sizeof sound_swarm -1)]);
        
        // Show Swarm HUD notice
        set_hudmessage(20, 255, 20, -1.0, 0.25, 1, 0.0, 5.0, 1.0, 1.0, -1)
        ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_SWARM")
    }
    else if ((playerid == MAKEZOMBIE_TASK && random_num(1, get_pcvar_num(cvar_multichance)) == get_pcvar_num(cvar_multi) && floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil) > 1) || multi) // Multi Infection Chance
    {
        static iZombies, iMaxZombies
        
        // iMaxZombies is rounded up, in case there aren't enough players
        iMaxZombies = floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil)
        iZombies = 0
        
        // Randomly turn iMaxZombies players into zombies
        while (iZombies < iMaxZombies)
        {
            if (id < g_maxplayers)
                id++
            else
                id = 1
            
            if (!is_user_alive(id) || g_zombie[id]) // dead or already a zombie
                continue;
            
            if (random_num(0, 1))
            {
                zombieme(id, 0, 1) // turn into a zombie
                iZombies++
            }
        }
        
        // Turn the rest of players into humans
        for (id = 1; id <= g_maxplayers; id++)
        {            
            if (!is_user_alive(id) || g_zombie[id]) // only those of them who arent zombies
                continue
            
            // remove previous tasks
            remove_task(id+TEAM_TASK)
            
            // Switch to CT
            if (fm_get_user_team(id) != CS_TEAM_CT) // need to change team?
            {
                fm_set_user_team(id, CS_TEAM_CT)
                set_task(0.1+g_teams_i, "fm_set_user_team_msg", id+TEAM_TASK)
                g_teams_i += 0.1; // increase teams task count
            }
        }
        
        // Play multi infection sound
        PlaySound(sound_multi[random_num(0, sizeof sound_multi -1)]);
        
        // Show Multi Infection HUD notice
        set_hudmessage(200, 50, 0, -1.0, 0.25, 1, 0.0, 5.0, 1.0, 1.0, -1)
        ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_MULTI")
    }
    else
    {
        // Infection mode (only 1 zombie)
        
        if (playerid == MAKEZOMBIE_TASK) // choose player randomly?
            id = fnGetRandomAlive(random_num(1, iPlayersnum))
        else
            id = playerid
        
        // Nemesis Chance
        if ((playerid == MAKEZOMBIE_TASK && random_num(1, get_pcvar_num(cvar_nemchance)) == get_pcvar_num(cvar_nem)) || nem)
        {
            g_nemround = true // nemesis round
            zombieme(id, 1, 0)
        }
        else
        {
            zombieme(id, 0, 0) // boring first zombie
        }
        
        // Rest of players left as humans (CTs)
        for (id = 1; id <= g_maxplayers; id++)
        {
            if (!is_user_alive(id)) // skip dead
                continue;
                
            if (g_zombie[id]) // skip our first zombie
                continue;
            
            // Remove previous tasks
            remove_task(id+TEAM_TASK)
            
            // Switch to CT
            if (fm_get_user_team(id) != CS_TEAM_CT) // need to change team?
            {
                fm_set_user_team(id, CS_TEAM_CT)
                set_task(0.1+g_teams_i, "fm_set_user_team_msg", id+TEAM_TASK)
                g_teams_i += 0.1; // increase teams task count
            }
        }
    }
    
    // Last Zombie Check
    set_task(0.1, "fnCheckLastZombie")
}

// Zombie Me Function (player id, turn into a nemesis, special mode)
zombieme(id, nemesis, specialmode)
{    
    if (g_zombie[id]) // already a zombie
        return;
    
    // Show zombie class menu if they haven't chosen any (e.g. just connected)
    if (g_zombieclass[id] == -1 && get_pcvar_num(cvar_zclasses))
        set_task(2.0, "show_menu_zombie", id);
    
    // Set our new zombie class
    g_zombieclass[id] = g_zombieclassnext[id];
    
    // Way to go...
    g_zombie[id] = true
    
    if (!specialmode) // no special mode
    {
        if (fnGetZombies() == 1) // first zombie
        {
            if (nemesis) // nemesis?
            {
                g_nemesis[id] = true
                
                if (get_pcvar_num(cvar_nemleap)) // give nemesis leap?
                    fm_give_item(id, "item_longjump")
                
                if (!get_pcvar_num(cvar_nemhp)) // nemesis health
                    fm_set_user_health(id, 1+get_pcvar_num(cvar_hp)*fnGetHumans())
                else
                    fm_set_user_health(id, get_pcvar_num(cvar_nemhp))
                    
                set_pev(id, pev_gravity, get_pcvar_float(cvar_nemgravity)) // nemesis gravity
                
                // Play Nemesis sound
                PlaySound(sound_nemesis[random_num(0, sizeof sound_nemesis -1)]);
                
                static name[32]
                get_user_name(id, name, sizeof name - 1)
                
                // Show Nemesis HUD notice
                set_hudmessage(255, 20, 20, -1.0, 0.25, 1, 0.0, 5.0, 1.0, 1.0, -1)
                ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_NEMESIS", name)
            }
            else
            {
                // First zombie
                fm_set_user_health(id, get_pcvar_num(cvar_firsthp)*zombie_multihp[g_zombieclass[id]]/100)
                set_pev(id, pev_gravity, get_pcvar_float(cvar_gravity)*zombie_multigrav[g_zombieclass[id]]/100)
                                
                // Play First Zombie sound
                engfunc(EngFunc_EmitSound, id, CHAN_VOICE, zombie_infect[random_num(0, sizeof zombie_infect - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
                
                if (get_pcvar_num(cvar_firstleap)) // give leap to First Zombie?
                    fm_give_item(id, "item_longjump")
                
                static name[32]
                get_user_name(id, name, sizeof name - 1)
                
                // Show First Zombie HUD notice
                set_hudmessage(255, 0, 0, -1.0, 0.25, 0, 0.0, 5.0, 1.0, 1.0, -1)
                ShowSyncHudMsg(0, g_MsgSync, "%L",LANG_PLAYER, "NOTICE_FIRST", name)
            }
        }
        else
        {
            // Not the first zombie
            fm_set_user_health(id, get_pcvar_num(cvar_hp)*zombie_multihp[g_zombieclass[id]]/100)
            set_pev(id, pev_gravity, get_pcvar_float(cvar_gravity)*zombie_multigrav[g_zombieclass[id]]/100)
            
            // Play Infection sound
            engfunc(EngFunc_EmitSound, id, CHAN_VOICE, zombie_infect[random_num(0, sizeof zombie_infect - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
            
            static name[32]
            get_user_name(id, name, sizeof name - 1)
            
            // Show Infection HUD notice
            set_hudmessage(255, 0, 0, 0.05, 0.45, 0, 0.0, 5.0, 1.0, 1.0, -1)
            ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_INFECT", name)
        }
    }
    else
    {
        // Survivor/multi infection mode/infection grenade: normal zombie stats, no HUD notice
        fm_set_user_health(id, get_pcvar_num(cvar_hp)*zombie_multihp[g_zombieclass[id]]/100)
        set_pev(id, pev_gravity, get_pcvar_float(cvar_gravity)*zombie_multigrav[g_zombieclass[id]]/100)
    }
    
    // Remove previous tasks
    remove_task(id+TEAM_TASK)
    remove_task(id+MODEL_TASK)
    remove_task(id+BLOOD_TASK)
    
    // Switch to T
    if (fm_get_user_team(id) != CS_TEAM_T) // need to change team?
    {
        if (specialmode) // set a longer delay for survivor/multi infection mode/infection grenade
        {
            fm_set_user_team(id, CS_TEAM_T);
            set_task(0.1+g_teams_i, "fm_set_user_team_msg", id+TEAM_TASK)
            g_teams_i += 0.1;
        }
        else
        {
            fm_set_user_team(id, CS_TEAM_T)
            set_task(0.1, "fm_set_user_team_msg", id+TEAM_TASK)
        }
    }
    
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT

    // Set the right model
    if (g_nemesis[id])
        copy(g_playermodel[id], 32, model_nemesis[random_num(0, sizeof model_nemesis -1)])
    else
    {
        switch (g_zombieclass[id])
        {
            case 0: copy(g_playermodel[id], 32, model_zombie_class1[random_num(0, sizeof model_zombie_class1 -1)])
            case 1: copy(g_playermodel[id], 32, model_zombie_class2[random_num(0, sizeof model_zombie_class2 -1)])
            case 2: copy(g_playermodel[id], 32, model_zombie_class3[random_num(0, sizeof model_zombie_class3 -1)])
            case 3: copy(g_playermodel[id], 32, model_zombie_class4[random_num(0, sizeof model_zombie_class4 -1)])
            case 4: copy(g_playermodel[id], 32, model_zombie_class5[random_num(0, sizeof model_zombie_class5 -1)])
            case 5: copy(g_playermodel[id], 32, model_zombie_class6[random_num(0, sizeof model_zombie_class6 -1)])
        }
    }
    
    fm_set_user_model_ent(id) // set model on entity
    
    #else
    
    // Set the right model, but check that we don't have it already
    static currentmodel[33], already_has_model, i
    already_has_model = false;
    fm_get_user_model(id, currentmodel, sizeof currentmodel - 1); // get current model
    
    if (g_nemesis[id])
    {
        for (i = 0; i < sizeof model_nemesis; i++)
            if (equal(model_nemesis[i], currentmodel)) already_has_model = true;
        
        if (!already_has_model) copy(g_playermodel[id], 32, model_nemesis[random_num(0, sizeof model_nemesis -1)])
    }
    else
    {
        switch (g_zombieclass[id])
        {
            case 0:
            {
                for (i = 0; i < sizeof model_zombie_class1; i++)
                    if (equal(model_zombie_class1[i], currentmodel)) already_has_model = true;
                
                if (!already_has_model) copy(g_playermodel[id], 32, model_zombie_class1[random_num(0, sizeof model_zombie_class1 -1)])
            }
            case 1:
            {
                for (i = 0; i < sizeof model_zombie_class2; i++)
                    if (equal(model_zombie_class2[i], currentmodel)) already_has_model = true;
                
                if (!already_has_model) copy(g_playermodel[id], 32, model_zombie_class2[random_num(0, sizeof model_zombie_class2 -1)])
            }
            case 2:
            {
                for (i = 0; i < sizeof model_zombie_class3; i++)
                    if (equal(model_zombie_class3[i], currentmodel)) already_has_model = true;
                
                if (!already_has_model) copy(g_playermodel[id], 32, model_zombie_class3[random_num(0, sizeof model_zombie_class3 -1)])
            }
            case 3:
            {
                for (i = 0; i < sizeof model_zombie_class4; i++)
                    if (equal(model_zombie_class4[i], currentmodel)) already_has_model = true;
                
                if (!already_has_model) copy(g_playermodel[id], 32, model_zombie_class4[random_num(0, sizeof model_zombie_class4 -1)])
            }
            case 4:
            {
                for (i = 0; i < sizeof model_zombie_class5; i++)
                    if (equal(model_zombie_class5[i], currentmodel)) already_has_model = true;
                
                if (!already_has_model) copy(g_playermodel[id], 32, model_zombie_class5[random_num(0, sizeof model_zombie_class5 -1)])
            }
            case 5:
            {
                for (i = 0; i < sizeof model_zombie_class6; i++)
                    if (equal(model_zombie_class6[i], currentmodel)) already_has_model = true;
                
                if (!already_has_model) copy(g_playermodel[id], 32, model_zombie_class6[random_num(0, sizeof model_zombie_class6 -1)])
            }
        }
    }
    
    if (!already_has_model) // need to change the model?
    {        
        if (specialmode) // longer delay for survivor/multi infection mode/infection grenade
        {
            set_task(0.1+MODELCHANGE_DELAY+g_models_i, "fm_set_user_model", id+MODEL_TASK) // set model with a delay
            g_models_i += MODELCHANGE_DELAY;
        }
        else
        {
            fm_set_user_model(id+MODEL_TASK) // set model instantly
        }
    }
    
    #endif
    
    // Remove any zoom (bugfix)
    fm_remove_user_zoom(id)
    
    // Drop weapons when infected?
    if (get_pcvar_num(cvar_dropwpn) > 0) drop_weapons(id, 1);
    if (get_pcvar_num(cvar_dropwpn) > 1) drop_weapons(id, 2);
    
    fm_strip_user_weapons(id) // strip zombies from guns
    fm_give_item(id, "weapon_knife")
    
    // Some fancy effects
    fadeFX(id);
    infectionFX(id);
    
    // Enable Zombie Night Vision
    if (!is_user_bot(id))
    {
        g_nvision[id] = true
        g_nvisionenabled[id] = 1
        
        if (get_pcvar_num(cvar_cnvg)) // custom nvg?
        {
            remove_task(id+NVISION_TASK);
            set_user_nvision(id+NVISION_TASK)
        }
        else set_user_gnvision(id, 1)
    }
    else fm_set_bot_nvg(id, 1); // turn on NVG for bots
    
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT
    if (g_nemesis[id] && get_pcvar_num(cvar_nemglow)) // nemesis glow
        fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id), kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 25) 
    if (!g_nemesis[id] && g_zombieclass[id] == 5) // rage zombie glow
        fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id), kRenderFxGlowShell, 0, 255, 0, kRenderNormal, 25)
    #else
    if (g_nemesis[id] && get_pcvar_num(cvar_nemglow)) // nemesis glow
        fm_set_rendering(id, kRenderFxGlowShell, 255, 0, 0, kRenderNormal, 25) 
    if (!g_nemesis[id] && g_zombieclass[id] == 5) // rage zombie glow
        fm_set_rendering(id, kRenderFxGlowShell, 0, 255, 0, kRenderNormal, 25)
    #endif
    
    // Set custom FOV
    if (get_pcvar_num(cvar_fov_angle) != 90 && get_pcvar_num(cvar_fov_angle) != 0)
    {
        message_begin(MSG_ONE, g_msgSetFOV, _, id);
        write_byte(get_pcvar_num(cvar_fov_angle));
        message_end();
    }
    
    // Call the bloody task
    if (!g_nemesis[id] && get_pcvar_num(cvar_bleeding))
        set_task(0.5, "make_blood", id+BLOOD_TASK)
    
    // Idle sounds task
    if (!g_nemesis[id])
        set_task(random_float(60.0, 120.0), "zombie_play_idle", id+BLOOD_TASK);
    
    // Turn off zombie's flashlight
    turn_off_flashlight(id)
    set_task(3.0, "turn_off_flashlight", id) // bugfix
    
    // Last Zombie Check
    set_task(0.1, "fnCheckLastZombie")
}

// Function Human Me (player id)
humanme(id)
{    
    if (!g_zombie[id]) // already human
        return;
    
    // Remove previous tasks
    remove_task(id+TEAM_TASK)
    remove_task(id+MODEL_TASK)
    remove_task(id+BLOOD_TASK)
    
    // Reset some vars
    g_zombie[id] = false
    g_nodamage[id] = false
    g_canbuy[id] = true
    g_nvision[id] = false
    g_nvisionenabled[id] = 0
    
    // Switch to CT
    if (fm_get_user_team(id) != CS_TEAM_CT) // need to change team?
    {
        fm_set_user_team(id, CS_TEAM_CT);
        fm_set_user_team_msg(id+TEAM_TASK);
    }
    
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT
    
    // Set the right model
    if (get_user_flags(id) & ACCESS_FLAG)
        copy(g_playermodel[id], 32, model_admin[random_num(0, sizeof model_admin -1)])
    else
        copy(g_playermodel[id], 32, model_human[random_num(0, sizeof model_human -1)])
    
    fm_set_user_model_ent(id) // set model
    if (!g_frozen[id]) fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id)) // remove glow, unless frozen
    
    #else
    
    // Set the right model, but check that we don't have it already
    static currentmodel[33], already_has_model, i
    already_has_model = false;
    fm_get_user_model(id, currentmodel, sizeof currentmodel - 1); // get current model
    
    if (get_user_flags(id) & ACCESS_FLAG)
    {
        for (i = 0; i < sizeof model_admin; i++)
            if (equal(model_admin[i], currentmodel)) already_has_model = true;
        
        if (!already_has_model) copy(g_playermodel[id], 32, model_admin[random_num(0, sizeof model_admin -1)])
    }
    else
    {
        for (i = 0; i < sizeof model_human; i++)
            if (equal(model_human[i], currentmodel)) already_has_model = true;
        
        if (!already_has_model) copy(g_playermodel[id], 32, model_human[random_num(0, sizeof model_human -1)])        
    }
    
    if (!already_has_model) fm_set_user_model(id+MODEL_TASK) // set model instantly
    if (!g_frozen[id]) fm_set_rendering(id) // remove glow, unless frozen
    
    #endif
    
    // Restore FOV
    if (get_pcvar_num(cvar_fov_angle) != 90 && get_pcvar_num(cvar_fov_angle) != 0)
    {
        message_begin(MSG_ONE, g_msgSetFOV, _, id);
        write_byte(90);
        message_end();
    }
    
    // Disable nightvision
    if (is_user_bot(id)) fm_set_bot_nvg(id, 0)
    else if (!get_pcvar_num(cvar_cnvg)) set_user_gnvision(id, 0)
    
    // Antidote sound
    engfunc(EngFunc_EmitSound, id, CHAN_ITEM, sound_antidote[random_num(0, sizeof sound_antidote - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
    
     // Restore health
    fm_set_user_health(id, get_pcvar_num(cvar_humanhp))
    
    // Restore gravity, unless frozen
    if (!g_frozen[id]) set_pev(id, pev_gravity, get_pcvar_float(cvar_humangravity))
    
    // Replace custom weapon models
    replace_models(id);
    
    // Show buy menu
    set_task(0.5, "show_menu_buy1", id+SPAWN_TASK);
    set_task(10.5, "show_menu_buy1", id+SPAWN_TASK);
    
    static name[32]
    get_user_name(id, name, sizeof name - 1)
    
    // show Antidote HUD notice
    set_hudmessage(0, 0, 255, 0.05, 0.45, 0, 0.0, 5.0, 1.0, 1.0, -1)
    ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_ANTIDOTE", name)
    
    // Last Zombie Check
    set_task(0.1, "fnCheckLastZombie")
}

/*================================================================================
 [Other Functions and Tasks]
=================================================================================*/

// Register Ham Forwards for CZ bots
public register_ham_czbots(id)
{    
    // make sure it's a CZ bot and it's still connected
    if (g_hamczbots || !get_pcvar_num(cvar_botquota) || !is_user_connected(id) || !is_user_bot(id))
        return;
    
    RegisterHamFromEntity(Ham_Spawn, id, "fw_PlayerSpawn", 1)
    RegisterHamFromEntity(Ham_Killed, id, "fw_Killed")
    RegisterHamFromEntity(Ham_TakeDamage, id, "fw_TakeDamage")
    RegisterHamFromEntity(Ham_TraceAttack, id, "fw_TraceAttack")
        
    g_hamczbots = true;
}

// Bots automatically buy extra items
public bot_buy_extras(id)
{
    if (!is_user_alive(id) || g_zombie[id] || g_survivor[id]) // not alive, zombie, or survivor
        return;
    
    // Make a random weapon selection
    static key
    key = random_num(0, sizeof g_extra_items - 1)
    
    // Attempt to buy the weapon
    if (g_ammopacks[id] >= g_extra_costs[key])
    {        
        if (MAXAMMO[get_weaponid(g_extra_items[key])] > 1) // (this adds grenades support)
        {
            // Drop previous weapon and give BP ammo for the new one
            (1<<get_weaponid(g_extra_items[key])) & PRIMARY_WEAPONS_BIT_SUM ? drop_weapons(id, 1) : drop_weapons(id, 2);
            fm_set_user_bpammo(id, get_weaponid(g_extra_items[key]), MAXAMMO[get_weaponid(g_extra_items[key])]);
        }
        fm_give_item(id, g_extra_items[key]);
        g_ammopacks[id] -= g_extra_costs[key];
    }
    
    // Attempt to buy Night Vision
    if (!g_nvision[id] && g_ammopacks[id] >= g_extra_costs2[EXTRA_NVISION])
    {
        fm_set_bot_nvg(id, 1)
        g_ammopacks[id] -= g_extra_costs2[EXTRA_NVISION]
    }
}

// Balance Teams Task
public balance_teams()
{
    // Get users playing
    static iPlayersnum
    iPlayersnum = fnGetPlaying()
    
    // No players, don't bother
    if (iPlayersnum < 1) return;
    
    static g_team[33], id, iTerrors, iMaxTerrors
    iMaxTerrors = iPlayersnum/2
    iTerrors = 0
    
    for (id = 1; id <= g_maxplayers; id++) // mark everyone's team as CT
        g_team[id] = CS_TEAM_CT;
    
    while (iTerrors < iMaxTerrors) // then randomly mark half of players as Terrorists
    {
        if (id < g_maxplayers)
            id++
        else
            id = 1
        
        if (!is_user_connected(id))
            continue; // skip if not connected
        
        if (fm_get_user_team(id) == CS_TEAM_SPECTATOR || fm_get_user_team(id) == CS_TEAM_UNASSIGNED)
            continue; // skip if not playing
        
        if (g_team[id] == CS_TEAM_T)
            continue; // already a Terrorist
        
        if (random_num(0, 1))
        {
            g_team[id] = CS_TEAM_T
            iTerrors++
        }
    }
    
    for (id = 1; id <= g_maxplayers; id++) // actually set everyone's team
    {
        if (!is_user_connected(id))
            continue; // skip if not connected
        
        if (fm_get_user_team(id) == CS_TEAM_SPECTATOR || fm_get_user_team(id) == CS_TEAM_UNASSIGNED)
            continue // skip if not playing
        
        // set correct team
        remove_task(id+TEAM_TASK)
        fm_set_user_team(id, g_team[id])
    }
}

// Welcome Message Task
public welcome_msg(id)
{
    client_print(0, print_chat, "**** %s by MeRcyLeZZ ****", g_modname)
    client_print(0, print_chat, "[ZP] %L", LANG_PLAYER, "NOTICE_INFO1")
    if (!get_pcvar_num(cvar_infammo)) client_print(0, print_chat, "[ZP] %L", LANG_PLAYER, "NOTICE_INFO2")
    
    set_hudmessage(0, 125, 200, -1.0, 0.25, 0, 0.0, 3.0, 2.0, 1.0, -1)
    ShowSyncHudMsg(0, g_MsgSync, "%L", LANG_PLAYER, "NOTICE_VIRUS_FREE") // show T-virus HUD notice
}

// Respawn Player Task
public respawn_player(taskid)
{    
    // Respawn on infection rounds only
    if (!is_user_alive(ID_SPAWN) && !g_endround && !g_survround && !g_swarmround && !g_nemround)
    {
        g_respawn_as_zombie[ID_SPAWN] ? fm_set_user_team(ID_SPAWN, CS_TEAM_T) : fm_set_user_team(ID_SPAWN, CS_TEAM_CT);
        ExecuteHam(Ham_CS_RoundRespawn, ID_SPAWN);
    }
}

// Check Round Task -check that we still have both zombies & humans on a round-
check_round(leaving_player)
{
    if (!g_endround && !task_exists(MAKEZOMBIE_TASK)) // only if no round end and no make_a_zombie task
    {    
        if (fnGetZombies() == 1 && g_zombie[leaving_player]) // last zombie disconnecting    
        {            
            static iPlayersnum
            iPlayersnum = fnGetAlive() // how many players alive
            
            if (iPlayersnum <= 1) // dont bother
                return;
            
            static id
            id = fnGetRandomAlive(random_num(1, iPlayersnum)) // pick random one
            
            if (id != leaving_player)
            {
                static name[32]
                get_user_name(id, name, sizeof name - 1)
                client_print(0, print_chat, "[ZP] %L", LANG_PLAYER, "LAST_ZOMBIE_LEFT", name)
                zombieme(id, 0, 0); // turn him into a zombie                
            }
            else check_round(leaving_player) // call back function until we pick someone else
        }
        else if (fnGetHumans() == 1 && !g_zombie[leaving_player]) // last human disconnecting
        {
            static iPlayersnum
            iPlayersnum = fnGetAlive() // how many players alive
            
            if (iPlayersnum <= 1) // dont bother
                return;
            
            static id
            id = fnGetRandomAlive(random_num(1, iPlayersnum)) // pick random one
            
            if (id != leaving_player)
            {
                static name[32]
                get_user_name(id, name, sizeof name - 1)
                client_print(0, print_chat, "[ZP] %L", LANG_PLAYER, "LAST_HUMAN_LEFT", name)
                humanme(id); // turn him into a human
            }
            else check_round(leaving_player) // call back function until we pick someone else
        }
    }
}

// Lightning Effects Task
public lightning_effects()
{
    // Lightning style ["a"-"z"]
    static lights[2]
    get_pcvar_string(cvar_lightning, lights, sizeof lights - 1)
    strtolower(lights)
    
    if (lights[0] == 'a') // pitch black mode
    {
        if (!task_exists(THUNDER_PRE_TASK) && get_pcvar_num(cvar_thunder)) // enable thunderclaps?
        {
            switch (random_num(0,2))
            {
                case 0: set_task(get_pcvar_float(cvar_thunder), "thunder_clap1", THUNDER_PRE_TASK)
                case 1: set_task(get_pcvar_float(cvar_thunder), "thunder_clap2", THUNDER_PRE_TASK)
                case 2: set_task(get_pcvar_float(cvar_thunder), "thunder_clap3", THUNDER_PRE_TASK)
            }
        }
        if (!task_exists(THUNDER_TASK))
            engfunc(EngFunc_LightStyle, 0, lights)
    }
    else
    {
        engfunc(EngFunc_LightStyle, 0, lights)
        remove_task(THUNDER_PRE_TASK)
        remove_task(THUNDER_TASK)
    }
    
    set_task(5.0, "lightning_effects")
}

// ThunderClap 1
public thunder_clap1()
{    
    if (g_lights_i == 0) // play sound
        PlaySound(sound_thunder[random_num(0, sizeof sound_thunder - 1)])
    
    engfunc(EngFunc_LightStyle, 0, lights_thunder1[g_lights_i])
    g_lights_i++
    
    if (g_lights_i < sizeof lights_thunder1)
        set_task(0.1, "thunder_clap1", THUNDER_TASK)
    else
        g_lights_i = 0;
}

// ThunderClap 2
public thunder_clap2()
{    
    if (g_lights_i == 0) // play sound
        PlaySound(sound_thunder[random_num(0, sizeof sound_thunder - 1)])
    
    engfunc(EngFunc_LightStyle, 0, lights_thunder2[g_lights_i])
    g_lights_i++
    
    if (g_lights_i < sizeof lights_thunder2)
        set_task(0.1, "thunder_clap2", THUNDER_TASK)
    else
        g_lights_i = 0;
}

// ThunderClap 3
public thunder_clap3()
{    
    if (g_lights_i == 0) // play sound
        PlaySound(sound_thunder[random_num(0, sizeof sound_thunder - 1)])
    
    engfunc(EngFunc_LightStyle, 0, lights_thunder3[g_lights_i])
    g_lights_i++
    
    if (g_lights_i < sizeof lights_thunder3)
        set_task(0.1, "thunder_clap3", THUNDER_TASK)
    else
        g_lights_i = 0;
}

#if defined AMBIENCE_SOUNDS
// Ambience Sound Effects Task
public ambience_sound_effects()
{
    static isound
    isound = random_num(0, sizeof sound_ambience - 1)
    
    if (equal(sound_ambience[isound][strlen(sound_ambience[isound])-4], ".mp3"))
        client_cmd(0, "mp3 play sound/%s", sound_ambience[isound])
    else
        client_cmd(0, "spk %s", sound_ambience[isound])
    
    set_task(sound_ambience_duration[isound], "ambience_sound_effects")
}
#endif

// Flashlight Charge Task
public flashlight_charge(taskid)
{
    // Custom flashlight disabled
    if (!get_pcvar_num(cvar_cflash))
        return;
    
    // No use for this task when flashlight is not avalable
    if (!is_user_alive(ID_CHARGE) || g_zombie[ID_CHARGE] || g_survivor[ID_CHARGE])
        return;
    
    // Drain or recharge
    if (g_flashlight[ID_CHARGE])
        g_flashbattery[ID_CHARGE] -= get_pcvar_num(cvar_flashdrain)
    else
        g_flashbattery[ID_CHARGE] += 5
    
    // Battery fully charged
    if (g_flashbattery[ID_CHARGE] >= 100)
    {
        // Don't exceed 100%
        g_flashbattery[ID_CHARGE] = 100;
        
        // Update flashlight battery on HUD
        message_begin(MSG_ONE_UNRELIABLE, g_msgFlashBat, _, ID_CHARGE);
        write_byte(g_flashbattery[ID_CHARGE]);
        message_end();
        
        return;
    }
    
    // Battery depleted
    if (g_flashbattery[ID_CHARGE] <= 0)
    {
        // Turn it off
        g_flashlight[ID_CHARGE] = 0;
        g_flashbattery[ID_CHARGE] = 0;
        
        // Update flashlight status on HUD
        message_begin(MSG_ONE, g_msgFlashlight, _, ID_CHARGE);
        write_byte(g_flashlight[ID_CHARGE]);
        write_byte(g_flashbattery[ID_CHARGE]);
        message_end();
    }
    else
    {
        // Update flashlight battery on HUD
        message_begin(MSG_ONE_UNRELIABLE, g_msgFlashBat, _, ID_CHARGE);
        write_byte(g_flashbattery[ID_CHARGE]);
        message_end();
    }
    
    // Keep calling back the task
    set_task(1.0, "flashlight_charge", taskid);
}

// Remove Spawn Protection Task
public remove_spawn_protection(taskid)
{    
    if (!is_user_alive(ID_SPAWN)) // not alive
        return;
    
    // Remove spawn protection
    g_nodamage[ID_SPAWN] = false;
    set_pev(ID_SPAWN, pev_effects, pev(ID_SPAWN, pev_effects) & ~EF_NODRAW)
}

// Madness Over Task
public madness_over(taskid)
{    
    g_nodamage[ID_BLOOD] = false;
}

// Turn Off Game Flashlight
public turn_off_flashlight(id)
{    
    if (!is_user_alive(id) || (!g_zombie[id] && !g_survivor[id]))
        return;
    
    // Check if flashlight is on
    if (pev(id, pev_effects) & EF_DIMLIGHT)
    {
        // Turn it off
        set_pev(id, pev_effects, pev(id, pev_effects) & ~EF_DIMLIGHT);
        // Update HUD
        message_begin(MSG_ONE, g_msgFlashlight, _, id);
        write_byte(0);
        write_byte(0);
        message_end();
    }
}

// Infection Grenade Explosion
public infection_explode(const args[1])
{     
    // args[0] = entity id
    
    static ent
    ent = args[0]
    
    // invalid entity
    if (!pev_valid(ent)) return;
    
    // get origin
    static origin[3], Float:originF[3]
    pev(ent, pev_origin, originF);
    FVecIVec(originF, origin);

    // explosion
    create_blast(origin);
    
    // infection nade sound
    engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, grenade_infect[random_num(0, sizeof grenade_infect - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)

    // round ended (bugfix)
    if (g_endround) return;
    
    // get grenade's owner
    static attacker
    attacker = pev(ent, pev_owner);
    
    g_models_i = 0.0 // reset model change count
    g_teams_i = 0.0 // reset team change count
    
    // collisions
    static victim
    victim = -1;
    
    while ((victim = engfunc(EngFunc_FindEntityInSphere, victim, originF, 240.0)) != 0)
    {        
        // only effect alive non-spawnprotected humans
        if (!is_user_alive(victim) || g_zombie[victim] || g_nodamage[victim])
            continue;
        
        if (fnGetHumans() == 1) // last human can't be infected
        {
            ExecuteHamB(Ham_Killed, victim, attacker, 0) // now it's killed
            continue;
        }
        
        // infection nade sound
        engfunc(EngFunc_EmitSound, victim, CHAN_VOICE, grenade_infect_player[random_num(0, sizeof grenade_infect_player - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
        
        zombieme(victim, 0, 1) // turn into zombie
        
        SendDeathMsg(attacker, victim); // send death notice
        UpdateFrags(attacker, victim); // add corresponding frags & deaths
        FixDeadAttrib(victim) // fix the "dead" attrib on scoreboard
        
        // ammo packs given to zombie for infection
        g_ammopacks[attacker] += get_pcvar_num(cvar_ammoinfect)
        
        // infection HP bonus
        if (g_zombieclass[attacker] != 4)
            fm_set_user_health(attacker, pev(attacker, pev_health)+get_pcvar_num(cvar_bonushp))
        else
            fm_set_user_health(attacker, pev(attacker, pev_health)+(get_pcvar_num(cvar_bonushp)*3))
    }
    
    // get rid of the old grenade
    engfunc(EngFunc_RemoveEntity, ent)
}

// Fire Grenade Explosion
public fire_explode(const args[1])
{     
    // args[0] = entity id
    
    static ent
    ent = args[0]
    
    // invalid entity
    if (!pev_valid(ent)) return;
    
    // get origin
    static origin[3], Float:originF[3]
    pev(ent, pev_origin, originF);
    FVecIVec(originF, origin);

    // explosion
    create_blast2(origin);

    // flame nade sound
    engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, grenade_fire[random_num(0, sizeof grenade_fire - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
    
    // collisions
    static victim
    victim = -1;
    
    while ((victim = engfunc(EngFunc_FindEntityInSphere, victim, originF, 240.0)) != 0)
    {
        // only effect alive zombies (nemesis takes less burning)
        if (!is_user_alive(victim) || !g_zombie[victim] || g_nodamage[victim])
            continue;
        
        // our task params
        static params[2]
        params[0] = victim; // target
        
        if (g_nemesis[victim]) // fire duration
            params[1] = get_pcvar_num(cvar_fireduration);
        else
            params[1] = get_pcvar_num(cvar_fireduration)*5;
        
        // set burning task on him
        set_task(0.1, "burning_flame", victim+BLOOD_TASK, params, sizeof params)
    }

    // get rid of the old grenade
    engfunc(EngFunc_RemoveEntity, ent)
}

// Frost Grenade Explosion
public frost_explode(const args[1])
{     
    // args[0] = entity id
    
    static ent
    ent = args[0]
    
    // invalid entity
    if (!pev_valid(ent)) return;
    
    // get origin
    static origin[3], Float:originF[3]
    pev(ent, pev_origin, originF);
    FVecIVec(originF, origin);

    // explosion
    create_blast3(origin);

    // frost nade explode sound
    engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, grenade_frost[random_num(0, sizeof grenade_frost - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
    
    // collisions
    static victim
    victim = -1;
    
    while ((victim = engfunc(EngFunc_FindEntityInSphere, victim, originF, 240.0)) != 0)
    {
        // only effect alive unfrozen zombies (nemesis is immune)
        if (!is_user_alive(victim) || !g_zombie[victim] || g_nemesis[victim] || g_frozen[victim] || g_nodamage[victim])
            continue;
        
        // light blue glow while frozen
        #if defined HANDLE_MODELS_ON_SEPARATE_ENT
        fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", victim), kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 25)
        #else
        fm_set_rendering(victim, kRenderFxGlowShell, 0, 100, 200, kRenderNormal, 25)
        #endif
        
        // play freeze sound
        engfunc(EngFunc_EmitSound, victim, CHAN_BODY, grenade_frost_player[random_num(0, sizeof grenade_frost_player - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
        
        // add a blue tint to their screen
        message_begin(MSG_ONE, g_msgScreenFade, _, victim);
        write_short(~0); // duration
        write_short(~0); // hold time
        write_short(0x0004); // flags: FFADE_STAYOUT
        write_byte(0); // red
        write_byte(50); // green
        write_byte(200); // blue
        write_byte(100); // alpha
        message_end();
        
        // prevent from jumping
        if (pev(victim, pev_flags) & FL_ONGROUND)
            set_pev(victim, pev_gravity, 999999.9) // set really high
        else
            set_pev(victim, pev_gravity, 0.000001) // no gravity
        
        // freeze the victim, and set a task to remove the freeze
        g_frozen[victim] = true;
        set_task(get_pcvar_float(cvar_freezeduration), "remove_freeze", victim)
    }
    
    // get rid of the old grenade
    engfunc(EngFunc_RemoveEntity, ent)
}

// Remove freeze task
public remove_freeze(id)
{
    if (!is_user_alive(id) || !g_frozen[id]) // not alive / not frozen anymore
        return;
    
    // unfreeze
    g_frozen[id] = false;
    
    // restore normal gravity
    if (g_zombie[id])
        set_pev(id, pev_gravity, get_pcvar_float(cvar_gravity)*zombie_multigrav[g_zombieclass[id]]/100)
    else
        set_pev(id, pev_gravity, get_pcvar_float(cvar_humangravity))
    
    // play the broken glass sound
    engfunc(EngFunc_EmitSound, id, CHAN_BODY, grenade_frost_break[random_num(0, sizeof grenade_frost_break - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
    
    // remove glow (or just replace it if it's a rage zombie)
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT
    if (g_zombie[id] && g_zombieclass[id] == 5)
        fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id), kRenderFxGlowShell, 0, 255, 0, kRenderNormal, 25) 
    else
        fm_set_rendering(fm_find_ent_by_owner(-1, "player_model", id))
    #else
    if (g_zombie[id] && g_zombieclass[id] == 5)
        fm_set_rendering(id, kRenderFxGlowShell, 0, 255, 0, kRenderNormal, 25) 
    else
        fm_set_rendering(id)
    #endif
    
    // clear screen fade
    message_begin(MSG_ONE, g_msgScreenFade, _, id);
    write_short(0); // duration
    write_short(0); // hold time
    write_short(0); // flags
    write_byte(0); // red
    write_byte(0); // green
    write_byte(0); // blue
    write_byte(0); // alpha
    message_end();
    
    // get player's origin
    static origin[3], Float:originF[3]
    pev(id, pev_origin, originF)
    FVecIVec(originF, origin)

    // glass shatter
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BREAKMODEL);
    write_coord(origin[0]);        // x
    write_coord(origin[1]);        // y
    write_coord(origin[2] + 24);    // z
    write_coord(16);        // size x
    write_coord(16);        // size y
    write_coord(16);        // size z
    write_coord(random_num(-50,50));// velocity x
    write_coord(random_num(-50,50));// velocity y
    write_coord(25);        // velocity z
    write_byte(10);            // random velocity
    write_short(g_glassSpr);    // model
    write_byte(10);            // count
    write_byte(25);            // life
    write_byte(0x01);        // flags: BREAK_GLASS
    message_end();
}

// Flare "Explosion"
public flare_explode(const args[5])
{     
    // args[0] = entity id
    // args[1] = duration
    // args[2] = r
    // args[3] = g
    // args[4] = b
    
    static ent
    ent = args[0]
    
    // invalid entity
    if (!pev_valid(ent)) return;
    
    // Light up when it's stopped on ground
    if ((pev(ent, pev_flags) & FL_ONGROUND) && fm_get_speed(ent) < 10)
    {
        // flare sound
        engfunc(EngFunc_EmitSound, ent, CHAN_WEAPON, grenade_flare[random_num(0, sizeof grenade_flare - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
        
        // prevent smokegrenade from exploding
        set_pev(ent, pev_nextthink, get_gametime()+10.0);
        
        // call our lightning task
        set_task(0.1, "flare_lightning", NADES_TASK, args, sizeof args)
    }
    else
    {
        // prevent smokegrenade from exploding
        set_pev(ent, pev_nextthink, get_gametime()+10.0);
        
        // not on ground, keep looping
        set_task(0.5, "flare_explode", NADES_TASK, args, sizeof args)
    }
}

// Remove Stuff Task
public remove_stuff()
{    
    static ent
    
    // Doors
    if (get_pcvar_num(cvar_removedoors) > 0) // remove rotating doors only
    {
        ent = -1;
        while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", "func_door_rotating")) != 0)
            engfunc(EngFunc_SetOrigin, ent, Float:{8192.0,8192.0,8192.0})
    }
    if (get_pcvar_num(cvar_removedoors) > 1) // remove all doors
    {
        ent = -1;
        while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", "func_door")) != 0)
            engfunc(EngFunc_SetOrigin, ent, Float:{8192.0,8192.0,8192.0})
    }
    
    // Triggered lights
    if (!get_pcvar_num(cvar_triggered))
    {
        ent = -1
        while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", "light")) != 0) // disable light triggers
        {
            dllfunc(DLLFunc_Use, ent, 0); // turn off the light
            set_pev(ent, pev_targetname, 0) // prevent it from being triggered
        }
    }    
}

// Set Custom Weapon Models
public replace_models(id)
{
    if (!is_user_alive(id)) // not alive
        return;
    
    if (g_currentweapon[id] == CSW_KNIFE) // custom knife models
    {
        if (g_zombie[id] && !g_newround && !g_endround)
        {
            if (g_nemesis[id])
            {
                set_pev(id, pev_viewmodel2, model_vknife_nemesis)
                set_pev(id, pev_weaponmodel2, model_pknife_nemesis)
            }
            else
            {
                set_pev(id, pev_viewmodel2, model_vknife_zombie)
                set_pev(id, pev_weaponmodel2, model_pknife_zombie)
            }
        }
        else
        {
            set_pev(id, pev_viewmodel2, "models/v_knife.mdl")
            set_pev(id, pev_weaponmodel2, "models/p_knife.mdl")
        }
    }
    else if (g_currentweapon[id] == CSW_HEGRENADE) // infection bomb or fire grenade
    {
        if (g_zombie[id] && !g_newround && !g_endround)
            set_pev(id, pev_viewmodel2, model_grenade_infect)
        else
            set_pev(id, pev_viewmodel2, model_grenade_fire)
    }
    else if (g_currentweapon[id] == CSW_FLASHBANG) // frost grenade
        set_pev(id, pev_viewmodel2, model_grenade_frost)
    
    else if (g_currentweapon[id] == CSW_SMOKEGRENADE) // flare grenade
        set_pev(id, pev_viewmodel2, model_grenade_flare)
    
    #if defined HANDLE_MODELS_ON_SEPARATE_ENT
    static model[64]
    pev(id, pev_weaponmodel2, model, sizeof model - 1) // get weapon model
    fm_set_weapon_model_ent(id, model) // set correct model on weapon_model entity
    #endif
}

// Reset Player Vars
reset_vars(id, resetall)
{
    g_zombie[id] = false
    g_nemesis[id] = false
    g_survivor[id] = false
    g_lastzombie[id] = false
    g_lasthuman[id] = false
    g_frozen[id] = false
    g_nodamage[id] = false
    g_respawn_as_zombie[id] = false
    g_nvision[id] = false
    g_nvisionenabled[id] = 0
    g_flashlight[id] = 0
    g_flashbattery[id] = 100
    g_canbuy[id] = true
    
    if (resetall)
    {
        g_ammopacks[id] = 5
        g_zombieclass[id] = -1
        g_zombieclassnext[id] = 0
        g_damagedealt[id] = 0
    }
}

// Set spectators nightvision
public spec_nvision(id)
{
    if (!is_user_connected(id) || is_user_alive(id) || is_user_bot(id)) // not connected, not dead, or bot
        return;
    
    g_nvision[id] = true; // give spectator nvision
    g_nvisionenabled[id] = 1
    
    if (get_pcvar_num(cvar_cnvg)) // custom nvg?
    {
        remove_task(id+NVISION_TASK);
        set_user_nvision(id+NVISION_TASK)
    }
    else set_user_gnvision(id, 1)
}

// Show HUD Task
public ShowHUD(taskid)
{    
    static id
    id = ID_SHOWHUD;
    
    if (!is_user_alive(id)) // player dead
    {
        id = pev(id, pev_iuser2); // get spectating target
        if (!is_user_alive(id)) // target not alive
        {
            set_task(1.0 , "ShowHUD" , taskid) // call back the task
            return;
        }
    }
    
    static class[16], red, green, blue
    
    if (!g_zombie[id])
    {
        red = 0
        green = 0
        blue = 255
        
        if (g_survivor[id])
            copy(class, sizeof class - 1, "CLASS_SURVIVOR")
        else
            copy(class, sizeof class - 1, "CLASS_HUMAN")
    }
    else
    {
        red = 200
        green = 250
        blue = 0
        
        if (g_nemesis[id])
            copy(class, sizeof class - 1,  "CLASS_NEMESIS")
        else
        {    
            switch (g_zombieclass[id])
            {
                case 0:    copy(class, sizeof class - 1, "CLASS_ZOMBIE1")
                case 1:    copy(class, sizeof class - 1, "CLASS_ZOMBIE2")
                case 2:    copy(class, sizeof class - 1, "CLASS_ZOMBIE3")
                case 3:    copy(class, sizeof class - 1, "CLASS_ZOMBIE4")
                case 4:    copy(class, sizeof class - 1, "CLASS_ZOMBIE5")
                case 5:    copy(class, sizeof class - 1, "CLASS_ZOMBIE6")
            }
        }
    }
    
    if (id != ID_SHOWHUD) // spectating
    {
        static name[32]
        get_user_name(id, name, sizeof name - 1) // get target name
        set_hudmessage(255, 255, 255, 0.7, 0.8, 0, 6.0, 1.1, 0.0, 0.0, -1)
        ShowSyncHudMsg(ID_SHOWHUD, g_MsgSync2, "%L %s^nHP: %d - %L %L", ID_SHOWHUD, "SPECTATING", name, pev(id, pev_health), ID_SHOWHUD, "CLASS_CLASS", ID_SHOWHUD, class) // show name, health, and class
    }
    else // player's own HUD display
    {
        set_hudmessage(red, green, blue, 0.02, 0.9, 0, 6.0, 1.1, 0.0, 0.0, -1)
        ShowSyncHudMsg(id, g_MsgSync2, "%L: %d - %L %L - %L %d", id, "ZOMBIE_ATTRIB1", pev(id, pev_health), id, "CLASS_CLASS", id, class, id, "AMMO_PACKS1", g_ammopacks[id]) // show health, class and ammo
    }
    
    set_task(1.0, "ShowHUD", taskid) // keep calling back the task
}

// Make zombies leave footsteps and bloodstains on the floor task
public make_blood(taskid)
{
    // only bleed when moving on ground
    if (fm_get_speed(ID_BLOOD) < 100 || !(pev(ID_BLOOD, pev_flags) & FL_ONGROUND))
    {
        set_task(0.5, "make_blood", taskid) // keep calling back the task
        return;
    }
    
    // get user origin
    static origin[3], Float:originF[3]
    pev(ID_BLOOD, pev_origin, originF);
    FVecIVec(originF, origin);
    
    // if ducking set a little lower
    if (pev(ID_BLOOD, pev_bInDuck))
        origin[2] -= 18
    else
        origin[2] -= 36
    
    // send the decal message
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
    write_byte(TE_WORLDDECAL)
    write_coord(origin[0])        // x
    write_coord(origin[1])        // y
    write_coord(origin[2])        // z
    write_byte(zombie_decals[random_num(0, sizeof zombie_decals - 1)] + (g_czero*12)) // random decal number (offsets +12 for CZ)
    message_end()
    
    // keep calling back the task
    set_task(0.5, "make_blood", taskid);
}

// Play idle zombie sounds
public zombie_play_idle(taskid)
{    
    if (g_endround || g_newround) // round ended/new one starting
        return;
    
    if (g_lastzombie[ID_BLOOD]) // last zombie sound
        engfunc(EngFunc_EmitSound, ID_BLOOD, CHAN_VOICE, zombie_idle_last[random_num(0, sizeof zombie_idle_last - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
    else
        engfunc(EngFunc_EmitSound, ID_BLOOD, CHAN_VOICE, zombie_idle[random_num(0, sizeof zombie_idle - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)

    set_task(random_float(60.0, 120.0), "zombie_play_idle", taskid);
}

// Place a user at a random spawn
do_random_spawn(id)
{
    if (!g_spawnCount) // no spawns
        return;

    static sp_index, i
    sp_index = random_num(0, g_spawnCount-1); // random spawn
    
    for (i = sp_index + 1; i != 999; i++)
    {
        // start over when we reach the end
        if (i >= g_spawnCount) i = 0;
        
        // loop completed, no free space found
        if (i == sp_index) break;

        // free spawn space?
        if (is_hull_vacant(g_spawns[i],HULL_HUMAN))
        {
            engfunc(EngFunc_SetOrigin, id, g_spawns[i]);
            break;
        }
    }
}

// Get Zombies -returns alive zombies number-
fnGetZombies()
{
    static iZombies, id
    iZombies = 0 // our counter
    
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (is_user_alive(id) && g_zombie[id])
            iZombies++
    }
    
    return iZombies
}

// Get Humans -returns alive humans number-
fnGetHumans()
{
    static iHumans, id
    iHumans = 0 // our counter
    
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (is_user_alive(id) && !g_zombie[id])
            iHumans++
    }
    
    return iHumans
}

// Get Alive -returns alive players number-
fnGetAlive()
{
    static iAlive, id
    iAlive = 0 // our counter
    
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (is_user_alive(id))
            iAlive++
    }
    
    return iAlive
}

// Get Playing -returns number of users playing-
fnGetPlaying()
{
    static iPlaying, id
    iPlaying = 0 // our counter
    
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (is_user_connected(id))
        {            
            if (fm_get_user_team(id) != CS_TEAM_SPECTATOR && fm_get_user_team(id) != CS_TEAM_UNASSIGNED)
                iPlaying++
        }
    }
    
    return iPlaying
}

// Get Random Alive -returns index of alive player number n -
fnGetRandomAlive(n)
{
    static iAlive, id
    iAlive = 0 // our counter
    
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (is_user_alive(id))
            iAlive++
        
        if (iAlive == n)
            return id
    }
    
    return -1;
}

// Last Zombie Check -check for last zombie and set its flag-
public fnCheckLastZombie()
{
    static id
    for (id = 1; id <= g_maxplayers; id++)
    {
        if (is_user_alive(id) && g_zombie[id] && fnGetZombies() == 1) // last zombie
            g_lastzombie[id] = true
        else
            g_lastzombie[id] = false
        
        if (is_user_alive(id) && !g_zombie[id] && !g_survivor[id] && fnGetHumans() == 1) // last human
        {
            if (!g_lasthuman[id]) fm_set_user_health(id, pev(id, pev_health)+get_pcvar_num(cvar_humanlasthp))
            g_lasthuman[id] = true
        }
        else g_lasthuman[id] = false
    }
}

// Save player's stats into the database
save_stats(id)
{
    static name[64]
    get_user_name(id, name, sizeof name - 1) // get his name
    
    // check whether there is another record already in that slot
    if (db_name[id][0] && !equal(name, db_name[id]))
    {
        if (db_slot_i >= sizeof db_name) // if DB size is exceeded
            db_slot_i = g_maxplayers+1 // write over old records
        
        // move previous record onto an additional save slot
        copy(db_name[db_slot_i], 63, db_name[id])
        db_ammopacks[db_slot_i] = db_ammopacks[id]
        db_zombieclass[db_slot_i] = db_zombieclass[id]
        db_slot_i++
    }
    
    // now save the current player stats
    copy(db_name[id], 63, name) // store the name
    db_ammopacks[id] = g_ammopacks[id]  // store ammo packs
    db_zombieclass[id] = g_zombieclassnext[id] // store zombie type
}

// Load player's stats from the database (if a record is found)
load_stats(id)
{
    static name[64], i
    get_user_name(id, name, sizeof name - 1) // get player name
    
    for (i = 0; i < sizeof db_name; i++) // look for a matching record in the DB
    {
        if (equal(name, db_name[i]))
        {
            g_ammopacks[id] = db_ammopacks[i];
            g_zombieclass[id] = db_zombieclass[i];
            g_zombieclassnext[id] = db_zombieclass[i];
            return;
        }
    }
}

// Checks if a player is allowed to be a zombie
allowed_zombie(id)
{
    if (!is_user_alive(id) || g_zombie[id] || g_swarmround || g_nemround || g_survround || g_endround || task_exists(WELCOMEMSG_TASK) || (!g_zombie[id] && fnGetHumans() == 1))
        return false
    
    return true
}

// Checks if a player is allowed to be a human
allowed_human(id)
{
    if (!is_user_alive(id) || !g_zombie[id] || g_swarmround || g_nemround || g_survround || g_endround || (g_zombie[id] && fnGetZombies() == 1))
        return false
    
    return true
}

// Checks if a player is allowed to be a survivor
allowed_survivor(id)
{
    if (!is_user_alive(id) || !get_pcvar_num(cvar_surv) || g_endround || !g_newround || task_exists(WELCOMEMSG_TASK))
        return false
    
    return true
}

// Checks if a player is allowed to be a nemesis
allowed_nemesis(id)
{
    if (!is_user_alive(id) || !get_pcvar_num(cvar_nem) || g_endround || !g_newround || task_exists(WELCOMEMSG_TASK))
        return false
    
    return true
}

// Checks if a player is allowed to respawn
allowed_respawn(id)
{
    if (!is_user_connected(id) || is_user_alive(id) || g_endround || g_survround || g_swarmround || g_nemround)
        return false
    
    return true
}

// Checks if swarm mode is allowed
allowed_swarm()
{
    if (!get_pcvar_num(cvar_swarm) || g_endround || !g_newround || task_exists(WELCOMEMSG_TASK))
        return false
    
    return true
}

// Checks if multi infection mode is allowed
allowed_multi()
{
    if (!get_pcvar_num(cvar_multi) || g_endround || !g_newround || task_exists(WELCOMEMSG_TASK))
        return false
    
    return true
}

// Admin Command. zp_zombie
command_zombie(id, player)
{
    static name1[32], name2[32]
    get_user_name(id, name1, sizeof name1 - 1)
    get_user_name(player, name2, sizeof name2 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %s %L", name2, LANG_PLAYER, "CMD_INFECT")
        case 2: client_print(0, print_chat, "ADMIN %s - %s %L", name1, name2, LANG_PLAYER, "CMD_INFECT")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %s %L (Players: %d/%d)", name1, name2, LANG_SERVER, "CMD_INFECT", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    if (g_newround) // new round, set as first zombie
    {
        remove_task(MAKEZOMBIE_TASK)
        make_a_zombie(player, 0, 0, 0, 0);
    }
    else
    {
        zombieme(player, 0, 0) // just infect
    }
}

// Admin Command. zp_human
command_human(id, player)
{
    static name1[32], name2[32]
    get_user_name(id, name1, sizeof name1 - 1)
    get_user_name(player, name2, sizeof name2 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %s %L", name2, LANG_PLAYER, "CMD_DISINFECT")
        case 2: client_print(0, print_chat, "ADMIN %s - %s %L", name1, name2, LANG_PLAYER, "CMD_DISINFECT")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %s %L (Players: %d/%d)", name1, name2, LANG_SERVER,"CMD_DISINFECT", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    humanme(player) // turn back to human
}

// Admin Command. zp_survivor
command_survivor(id, player)
{
    static name1[32], name2[32]
    get_user_name(id, name1, sizeof name1 - 1)
    get_user_name(player, name2, sizeof name2 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %s %L", name2, LANG_PLAYER, "CMD_SURVIVAL")
        case 2: client_print(0, print_chat, "ADMIN %s - %s %L", name1, name2, LANG_PLAYER, "CMD_SURVIVAL")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %s %L (Players: %d/%d)", name1, name2, LANG_SERVER,"CMD_SURVIVAL", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    remove_task(MAKEZOMBIE_TASK)
    make_a_zombie(player,0,1, 0, 0)
}

// Admin Command. zp_nemesis
command_nemesis(id, player)
{
    static name1[32], name2[32]
    get_user_name(id, name1, sizeof name1 - 1)
    get_user_name(player, name2, sizeof name2 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %s %L", name2, LANG_PLAYER, "CMD_NEMESIS")
        case 2: client_print(0, print_chat, "ADMIN %s - %s %L", name1, name2, LANG_PLAYER, "CMD_NEMESIS")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %s %L (Players: %d/%d)", name1, name2, LANG_SERVER,"CMD_NEMESIS", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    remove_task(MAKEZOMBIE_TASK)
    make_a_zombie(player, 1, 0, 0, 0) // turn into a Nemesis zombie
}

// Admin Command. zp_swarm
command_swarm(id)
{
    static name1[32]
    get_user_name(id, name1, sizeof name1 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %L", LANG_PLAYER, "CMD_SWARM")
        case 2: client_print(0, print_chat, "ADMIN %s - %L", name1, LANG_PLAYER, "CMD_SWARM")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %L (Players: %d/%d)", name1, LANG_SERVER, "CMD_SWARM", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    remove_task(MAKEZOMBIE_TASK)
    make_a_zombie(0, 0, 0, 1, 0) // Call Swarm Mode
}

// Admin Command. zp_multi
command_multi(id)
{
    static name1[32]
    get_user_name(id, name1, sizeof name1 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %L", LANG_PLAYER, "CMD_MULTI")
        case 2: client_print(0, print_chat, "ADMIN %s - %L", name1, LANG_PLAYER, "CMD_MULTI")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %L (Players: %d/%d)", name1, LANG_SERVER,"CMD_MULTI", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    remove_task(MAKEZOMBIE_TASK)
    make_a_zombie(0, 0, 0, 0, 1) // Call Multi Infection
}

// Admin Command. zp_respawn
command_respawn(id, player)
{
    static name1[32], name2[32]
    get_user_name(id, name1, sizeof name1 - 1)
    get_user_name(player, name2, sizeof name2 - 1)
    
    switch (get_pcvar_num(cvar_showactivity))  // show activity?
    {
        case 1: client_print(0, print_chat, "ADMIN - %s %L", name2, LANG_PLAYER, "CMD_RESPAWN")
        case 2: client_print(0, print_chat, "ADMIN %s - %s %L", name1, name2, LANG_PLAYER, "CMD_RESPAWN")
    }
    
    if (get_pcvar_num(cvar_logcommands)) // Log to Zombie Plague log file?
    {
        static logdata[100]
        formatex(logdata, sizeof logdata - 1, "ADMIN %s - %s %L (Players: %d/%d)", name1, name2, LANG_SERVER, "CMD_RESPAWN", fnGetPlaying(), g_maxplayers)
        log_to_file("zombieplague.log", logdata)
    }
    
    respawn_player(player+SPAWN_TASK);
}

/*================================================================================
 [Custom Messages]
=================================================================================*/

// Custom Night Vision
public set_user_nvision(taskid)
{    
    // Not meant to have nvision or not enabled
    if (!g_nvision[ID_NVISION] || !g_nvisionenabled[ID_NVISION])
        return;
    
    // Get player origin
    static origin[3], Float:originF[3]
    pev(ID_NVISION, pev_origin, originF)
    FVecIVec(originF, origin);
    
    // Nightvision message
    message_begin(MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, _, ID_NVISION);
    write_byte(TE_DLIGHT);
    write_coord(origin[0]);        //x
    write_coord(origin[1]);        //y
    write_coord(origin[2]);        //z
    write_byte(get_pcvar_num(cvar_nvgsize)); //visible distance
    write_byte((g_nemround || g_nodamage[ID_NVISION]) ? get_pcvar_num(cvar_nemnvgcolor[0]) : get_pcvar_num(cvar_nvgcolor[0])); //red
    write_byte((g_nemround || g_nodamage[ID_NVISION]) ? get_pcvar_num(cvar_nemnvgcolor[1]) : get_pcvar_num(cvar_nvgcolor[1])); //green
    write_byte((g_nemround || g_nodamage[ID_NVISION]) ? get_pcvar_num(cvar_nemnvgcolor[2]) : get_pcvar_num(cvar_nvgcolor[2])); //blue
    write_byte(2);            //life
    write_byte(0);            //decay rate
    message_end();
    
    // Keep calling back the task
    set_task(0.1, "set_user_nvision", taskid)
}

// Game Nightvision
set_user_gnvision(id, toggle)
{
    message_begin(MSG_ONE, g_msgNVGToggle, _, id)
    write_byte(toggle) // 1 On - 0 Off
    message_end()
}

// Custom Flashlight
public set_user_flashlight(id)
{
    // Not meant to have flashlight or not enabled
    if (!get_pcvar_num(cvar_cflash) || !is_user_connected(id) || !g_flashlight[id])
        return;
    
    // Disable flashlight if it shouldn't be available
    if (!is_user_alive(id) || g_zombie[id] || g_survivor[id])
    {
        // Turn it off
        g_flashlight[id] = 0;
        g_flashbattery[id] = 0;
        
        // Update flashlight HUD
        message_begin(MSG_ONE, g_msgFlashlight, _, id);
        write_byte(g_flashlight[id]);
        write_byte(g_flashbattery[id]);
        message_end();
        
        return;
    }
    
    // Get player and aiming origins
    static origin[3], Float:originF[3], destorigin[3], Float:destoriginF[3]
    pev(id, pev_origin, originF)
    FVecIVec(originF, origin)
    fm_get_aim_origin(id, destoriginF)
    FVecIVec(destoriginF, destorigin)
    
    // Make sure it doesn't exceed the max distance
    if (get_distance(origin, destorigin) < get_pcvar_num(cvar_flashdist))
    {
        // Flashlight message
        message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
        write_byte(TE_DLIGHT);
        write_coord(destorigin[0]);    // X
        write_coord(destorigin[1]);    // Y
        write_coord(destorigin[2]);    // Z
        write_byte(get_pcvar_num(cvar_flashsize)); // radius
        write_byte(get_pcvar_num(cvar_flashcolor[0])); // R
        write_byte(get_pcvar_num(cvar_flashcolor[1])); // G
        write_byte(get_pcvar_num(cvar_flashcolor[2])); // B
        write_byte(3);        // life
        write_byte(0);        // decay rate
        message_end();
    }
    
    // Keep calling back the task
    set_task(0.1, "set_user_flashlight", id)
}

// Screen Fade Effect on infection
fadeFX(id)
{
    message_begin(MSG_ONE_UNRELIABLE, g_msgScreenFade, _, id)
    write_short(4096)    // Duration
    write_short(0)        // Hold time
    write_short(0x0000)    // Fade type
    write_byte(g_nemesis[id] ? get_pcvar_num(cvar_nemnvgcolor[0]) : get_pcvar_num(cvar_nvgcolor[0])); // Red
    write_byte(g_nemesis[id] ? get_pcvar_num(cvar_nemnvgcolor[1]) : get_pcvar_num(cvar_nvgcolor[1])); // Green
    write_byte(g_nemesis[id] ? get_pcvar_num(cvar_nemnvgcolor[2]) : get_pcvar_num(cvar_nvgcolor[2])); // Blue
    write_byte (255)    // Alpha
    message_end()
}

// Infection special effects
public infectionFX(id)
{
    if (!is_user_alive(id)) // not alive
        return;
    
    // get player origin
    static origin[3], Float:originF[3]
    pev(id, pev_origin, originF)
    FVecIVec(originF, origin)
    
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_DLIGHT);
    write_coord(origin[0]);        //x
    write_coord(origin[1]);        //y
    write_coord(origin[2]);        //z
    write_byte(20);            //visible distance
    write_byte((g_nemesis[id] || g_nodamage[id]) ? get_pcvar_num(cvar_nemnvgcolor[0]) : get_pcvar_num(cvar_nvgcolor[0])); //red
    write_byte((g_nemesis[id] || g_nodamage[id]) ? get_pcvar_num(cvar_nemnvgcolor[1]) : get_pcvar_num(cvar_nvgcolor[1])); //green
    write_byte((g_nemesis[id] || g_nodamage[id]) ? get_pcvar_num(cvar_nemnvgcolor[2]) : get_pcvar_num(cvar_nvgcolor[2])); //blue
    write_byte(2);            //life
    write_byte(0);            //decay rate
    message_end();
    
    if (!g_nemesis[id] && !g_nodamage[id]) // skip for nemesis or zombie madness
    {
        message_begin(MSG_BROADCAST, SVC_TEMPENTITY); // tracers
        write_byte(TE_IMPLOSION);
        write_coord(origin[0]);        // x
        write_coord(origin[1]);        // y
        write_coord(origin[2]);        // z
        write_byte(128);        // radius
        write_byte(20);            // count
        write_byte(3);            // duration
        message_end();
        
        message_begin(MSG_BROADCAST, SVC_TEMPENTITY); // particles
        write_byte(TE_PARTICLEBURST);
        write_coord(origin[0]);        // x
        write_coord(origin[1]);        // y
        write_coord(origin[2]);        // z
        write_short(50);        // radius
        write_byte(70);            // color
        write_byte(3);            // duration (will be randomized a bit)
        message_end();

    }
    else // keep sending for nemesis/madness aura
        set_task(0.1, "infectionFX", id)
}

// Flare Lightning
public flare_lightning(args[5])
{    
    // args[0] = entity id
    // args[1] = duration
    // args[2] = r
    // args[3] = g
    // args[4] = b
    
    static ent
    ent = args[0]
    
    // invalid entity
    if (!pev_valid(ent)) return;
    
    if (args[1] <= 0) // flare depleted -clean up the mess-
    {
        engfunc(EngFunc_RemoveEntity, ent)
        return;
    }
    
    // get origin
    static origin[3], Float:originF[3]
    pev(ent, pev_origin, originF)
    FVecIVec(originF, origin)
    
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_DLIGHT);
    write_coord(origin[0]);        //x
    write_coord(origin[1]);        //y
    write_coord(origin[2]);        //z
    write_byte(get_pcvar_num(cvar_flaresize)); //visible distance
    write_byte(args[2]);        //red
    write_byte(args[3]);        //green
    write_byte(args[4]);        //blue
    write_byte(51);            //life
    write_byte(args[1] < 2 ? 3 : 0);    //decay rate
    message_end();
    
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_SPARKS);
    write_coord(origin[0]);    // x
    write_coord(origin[1]);    // y
    write_coord(origin[2]);    // z
    message_end();
    
    // decrease task cycle count
    args[1]--
    
    // prevent smokegrenade from exploding
    set_pev(ent, pev_nextthink, get_gametime()+10.0);
    
    // keep sending flare messaegs
    set_task(5.0, "flare_lightning", NADES_TASK, args, sizeof args)
}

// Burning Flames
public burning_flame(args[2])
{    
    // args[0] = player id
    // args[1] = cycles left (duration)
    
    static id
    id = args[0]
    
     // not alive - madness mode - in water - burning stopped
    if (!is_user_alive(id) || g_nodamage[id] || pev(id, pev_flags) & FL_INWATER || args[1] < 1)
        return;
    
    if (!random_num(0, 20) && !g_nemesis[id]) // randomly play burning zombie scream sounds
        engfunc(EngFunc_EmitSound, id, CHAN_VOICE, grenade_fire_player[random_num(0, sizeof grenade_fire_player - 1)], 1.0, ATTN_NORM, 0, PITCH_NORM)
    
    // take damage from the fire
    if (pev(id, pev_health) > get_pcvar_num(cvar_firedamage))
        fm_set_user_health(id, pev(id, pev_health) - get_pcvar_num(cvar_firedamage))
    
    // get player origin
    static origin[3], Float:originF[3]
    pev(id, pev_origin, originF)
    FVecIVec(originF, origin)
    
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
    write_byte(TE_SPRITE);
    write_coord(-5+origin[0]+random_num(0, 10))    // x
    write_coord(-5+origin[1]+random_num(0, 10))    // y
    write_coord(-10+origin[2]+random_num(0, 20))    // z
    write_short(g_flameSpr)                // sprite name
    write_byte(random_num(5,10))            // scale
    write_byte(200)                    // brightness
    message_end()
    
    // decrease task cycle count
    args[1]--;
    
    // keep sending flame messaegs
    set_task(0.2, "burning_flame", id+BLOOD_TASK, args, sizeof args)
}

// Infection Grenade: Green Blast
create_blast(const origin[3])
{
    // smallest ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 385); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(0); // red
    write_byte(200); // green
    write_byte(0); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();

    // medium ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 470); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(0); // red
    write_byte(200); // green
    write_byte(0); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();

    // largest ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 555); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(0); // red
    write_byte(200); // green
    write_byte(0); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();
}

// Fire Grenade: Fire Blast
create_blast2(const origin[3])
{
    // smallest ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 385); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(200); // red
    write_byte(100); // green
    write_byte(0); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();

    // medium ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 470); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(200); // red
    write_byte(50); // green
    write_byte(0); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();

    // largest ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 555); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(200); // red
    write_byte(0); // green
    write_byte(0); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();
}

// Frost Grenade: Freeze Blast
create_blast3(const origin[3])
{
    // smallest ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 385); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(0); // red
    write_byte(100); // green
    write_byte(200); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();

    // medium ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 470); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(0); // red
    write_byte(100); // green
    write_byte(200); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();

    // largest ring
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
    write_byte(TE_BEAMCYLINDER);
    write_coord(origin[0]); // start X
    write_coord(origin[1]); // start Y
    write_coord(origin[2]); // start Z
    write_coord(origin[0]); // something X
    write_coord(origin[1]); // something Y
    write_coord(origin[2] + 555); // something Z
    write_short(g_exploSpr); // sprite
    write_byte(0); // startframe
    write_byte(0); // framerate
    write_byte(4); // life
    write_byte(60); // width
    write_byte(0); // noise
    write_byte(0); // red
    write_byte(100); // green
    write_byte(200); // blue
    write_byte(200); // brightness
    write_byte(0); // speed
    message_end();
}

// Fix Dead Attrib on scoreboard
FixDeadAttrib(id)
{
    message_begin(MSG_BROADCAST, g_msgScoreAttrib) // send message to everyone
    write_byte(id)    // target
    write_byte(0)    // 0 nothing - (1<<0) dead
    message_end()
}

// Send Death Message for Zombies
SendDeathMsg(attacker, victim)
{
    message_begin(MSG_BROADCAST, g_msgDeathMsg)
    write_byte(attacker)        // killer (0 - world)
    write_byte(victim)        // victim
    write_byte(1)            // headshot flag
    write_string("infection")    // killer's weapon
    message_end()
}

// Update Player Frags
UpdateFrags(attacker, victim)
{
    // set attacker frags
    set_pev(attacker, pev_frags, float(pev(attacker, pev_frags) + 1))
    
    // Update scoreboard with attacker info
    message_begin(MSG_BROADCAST, g_msgScoreInfo)
    write_byte(attacker)
    write_short(pev(attacker, pev_frags))
    write_short(fm_get_user_deaths(attacker))
    write_short(0)
    write_short(fm_get_user_team(attacker))
    message_end()
    
    // set victim deaths
    fm_set_user_deaths(victim, fm_get_user_deaths(victim) + 1)
    
    // Update scoreboard with victim info
    message_begin(MSG_BROADCAST, g_msgScoreInfo)
    write_byte(victim)
    write_short(pev(victim, pev_frags))
    write_short(fm_get_user_deaths(victim))
    write_short(0)
    write_short(fm_get_user_team(victim))
    message_end()
}

// Remove Player Frags (when Nemesis/Survivor ignore_frags cvar is enabled)
RemoveFrags(attacker, victim)
{
    // remove attacker frags
    set_pev(attacker, pev_frags, float(pev(attacker, pev_frags) - 1))
    
    // remove victim deaths
    fm_set_user_deaths(victim, fm_get_user_deaths(victim) - 1)
}

// Play a sound on clients
PlaySound(const sound[])
{
    message_begin(MSG_BROADCAST, g_msgSendAudio)
    write_byte(0)        // sender id
    write_string(sound)    // sound string
    write_short(100)    // pitch
    message_end()
}

/*================================================================================
 [Stocks]
=================================================================================*/

// Set an entity's key value (from fakemeta_util)
stock fm_set_kvd(entity, const key[], const value[], const classname[])
{
    set_kvd(0, KV_ClassName, classname);
    set_kvd(0, KV_KeyName, key);
    set_kvd(0, KV_Value, value);
    set_kvd(0, KV_fHandled, 0);

    dllfunc(DLLFunc_KeyValue, entity, 0);
}

// Set entity's rendering type (from fakemeta_util)
stock fm_set_rendering(entity, fx = kRenderFxNone, r = 255, g = 255, b = 255, render = kRenderNormal, amount = 16)
{
    static Float:color[3]
    color[0] = float(r);
    color[1] = float(g);
    color[2] = float(b);
    
    set_pev(entity, pev_renderfx, fx);
    set_pev(entity, pev_rendercolor, color);
    set_pev(entity, pev_rendermode, render);
    set_pev(entity, pev_renderamt, float(amount));
}

// Get entity's speed (from fakemeta_util)
stock fm_get_speed(entity)
{
    static Float:velocity[3]
    pev(entity, pev_velocity, velocity);
    
    return floatround(vector_length(velocity));
}

// Get entity's aim origins (from fakemeta_util)
stock fm_get_aim_origin(id, Float:origin[3])
{
    static Float:origin1F[3], Float:origin2F[3]
    pev(id, pev_origin, origin1F);
    pev(id, pev_view_ofs, origin2F);
    xs_vec_add(origin1F, origin2F, origin1F);

    pev(id, pev_v_angle, origin2F);
    engfunc(EngFunc_MakeVectors, origin2F);
    global_get(glb_v_forward, origin2F);
    xs_vec_mul_scalar(origin2F, 9999.0, origin2F);
    xs_vec_add(origin1F, origin2F, origin2F);

    engfunc(EngFunc_TraceLine, origin1F, origin2F, 0, id, 0);
    get_tr2(0, TR_vecEndPos, origin);
}

// Find entity by its owner (from fakemeta_util)
stock fm_find_ent_by_owner(entity, const classname[], owner)
{
    while ((entity = engfunc(EngFunc_FindEntityByString, entity, "classname", classname)) && pev(entity, pev_owner) != owner) {}
    
    return entity;
}

// Set player's health (from fakemeta_util)
stock fm_set_user_health(id, health)
{
    health > 0 ? set_pev(id, pev_health, float(health)) : dllfunc(DLLFunc_ClientKill, id);
}

// Give an item to a player (from fakemeta_util)
stock fm_give_item(id, const item[])
{
    static ent
    ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, item));
    if (!pev_valid(ent)) return;
    
    static Float:originF[3]
    pev(id, pev_origin, originF);
    set_pev(ent, pev_origin, originF);
    set_pev(ent, pev_spawnflags, pev(ent, pev_spawnflags) | SF_NORESPAWN);
    dllfunc(DLLFunc_Spawn, ent);
    
    static save
    save = pev(ent, pev_solid);
    dllfunc(DLLFunc_Touch, ent, id);
    if (pev(ent, pev_solid) != save)
        return;
    
    engfunc(EngFunc_RemoveEntity, ent);
}

// Strip user weapons (from fakemeta_util)
stock fm_strip_user_weapons(id)
{
    static ent
    ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "player_weaponstrip"))
    if (!pev_valid(ent)) return;
    
    dllfunc(DLLFunc_Spawn, ent);
    dllfunc(DLLFunc_Use, ent, id);
    engfunc(EngFunc_RemoveEntity, ent);
}

// Collect spawn points from entity origins
stock collect_spawns(const classname[])
{
    static ent
    ent = -1;
    
    while ((ent = engfunc(EngFunc_FindEntityByString, ent, "classname", classname)) != 0)
    {
        // get origin
        static Float:originF[3]
        pev(ent, pev_origin, originF);
        g_spawns[g_spawnCount][0] = originF[0];
        g_spawns[g_spawnCount][1] = originF[1];
        g_spawns[g_spawnCount][2] = originF[2];

        // increase spawn count
        g_spawnCount++;
        if(g_spawnCount >= sizeof g_spawns) break;
    }
}

// Drop primary/secondary weapons
stock drop_weapons(id, dropwhat)
{
    static weapons[32], num, i
    num = 0 // reset passed weapons count (bugfix)
    get_user_weapons(id, weapons, num)
    
    for (i = 0; i < num; i++)
    {
        if ((dropwhat == 1 && ((1<<weapons[i]) & PRIMARY_WEAPONS_BIT_SUM)) || (dropwhat == 2 && ((1<<weapons[i]) & SECONDARY_WEAPONS_BIT_SUM)))
        {
            static wname[32], weapon_ent
            get_weaponname(weapons[i], wname, sizeof wname - 1)
            weapon_ent = fm_find_ent_by_owner(-1, wname, id); // get weapon entity
            
            // hack: store weapon bpammo on pev_iuser1
            set_pev(weapon_ent, pev_iuser1, fm_get_user_bpammo(id, weapons[i]))
            
            engclient_cmd(id, "drop", wname) // player drops it
            fm_set_user_bpammo(id, weapons[i], 0); // and looses his bpammo        
        }
    }    
}

// Stock by (probably) Twilight Suzuka -counts number of chars in a string
stock str_count(const str[], searchchar)
{
    static count, i
    count = 0
    
    for (i = 0; i <= strlen(str); i++)
    {
        if(str[i] == searchchar)
            count++
    }
    
    return count
}

// Checks if a space is vacant (credits to VEN)
stock is_hull_vacant(Float:origin[3], hull)
{
    engfunc(EngFunc_TraceHull, origin, origin, 0, hull, 0, 0);

    if (!get_tr2(0, TR_StartSolid) && !get_tr2(0, TR_AllSolid) && get_tr2(0, TR_InOpen))
        return true;
    
    return false;
}

// Check if a player is stuck (credits to VEN)
stock is_player_stuck(id)
{
    static Float:originF[3]
    pev(id, pev_origin, originF)
    
    engfunc(EngFunc_TraceHull, originF, originF, 0, pev(id, pev_flags) & FL_DUCKING ? HULL_HEAD : HULL_HUMAN, id, 0)

    if (get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen))
        return true;
    
    return false;
}

// Get User BP Ammo
stock fm_get_user_bpammo(id, weapon)
{
    static offset
    
    switch(weapon)
    {
        case CSW_AWP: offset = OFFSET_AWM_AMMO;
        case CSW_SCOUT,CSW_AK47,CSW_G3SG1: offset = OFFSET_SCOUT_AMMO;
        case CSW_M249: offset = OFFSET_PARA_AMMO;
        case CSW_M4A1,CSW_FAMAS,CSW_AUG,CSW_SG550,CSW_GALI,CSW_SG552: offset = OFFSET_FAMAS_AMMO;
        case CSW_M3,CSW_XM1014: offset = OFFSET_M3_AMMO;
        case CSW_USP,CSW_UMP45,CSW_MAC10: offset = OFFSET_USP_AMMO;
        case CSW_FIVESEVEN,CSW_P90: offset = OFFSET_FIVESEVEN_AMMO;
        case CSW_DEAGLE: offset = OFFSET_DEAGLE_AMMO;
        case CSW_P228: offset = OFFSET_P228_AMMO;
        case CSW_GLOCK18,CSW_MP5NAVY,CSW_TMP,CSW_ELITE: offset = OFFSET_GLOCK_AMMO;
        case CSW_FLASHBANG: offset = OFFSET_FLASH_AMMO;
        case CSW_HEGRENADE: offset = OFFSET_HE_AMMO;
        case CSW_SMOKEGRENADE: offset = OFFSET_SMOKE_AMMO;
        case CSW_C4: offset = OFFSET_C4_AMMO;
        default: return -1;
    }
    
    return get_pdata_int(id, offset, OFFSET_LINUX);
}

// Set User BP Ammo
stock fm_set_user_bpammo(id, weapon, amount)
{
    static offset
    
    switch(weapon)
    {
        case CSW_AWP: offset = OFFSET_AWM_AMMO;
        case CSW_SCOUT,CSW_AK47,CSW_G3SG1: offset = OFFSET_SCOUT_AMMO;
        case CSW_M249: offset = OFFSET_PARA_AMMO;
        case CSW_M4A1,CSW_FAMAS,CSW_AUG,CSW_SG550,CSW_GALI,CSW_SG552: offset = OFFSET_FAMAS_AMMO;
        case CSW_M3,CSW_XM1014: offset = OFFSET_M3_AMMO;
        case CSW_USP,CSW_UMP45,CSW_MAC10: offset = OFFSET_USP_AMMO;
        case CSW_FIVESEVEN,CSW_P90: offset = OFFSET_FIVESEVEN_AMMO;
        case CSW_DEAGLE: offset = OFFSET_DEAGLE_AMMO;
        case CSW_P228: offset = OFFSET_P228_AMMO;
        case CSW_GLOCK18,CSW_MP5NAVY,CSW_TMP,CSW_ELITE: offset = OFFSET_GLOCK_AMMO;
        case CSW_FLASHBANG: offset = OFFSET_FLASH_AMMO;
        case CSW_HEGRENADE: offset = OFFSET_HE_AMMO;
        case CSW_SMOKEGRENADE: offset = OFFSET_SMOKE_AMMO;
        case CSW_C4: offset = OFFSET_C4_AMMO;
        default: return;
    }
    
    set_pdata_int(id, offset, amount, OFFSET_LINUX);
}

// Set Weapon Clip Ammo
stock fm_set_weapon_ammo(entity, amount)
{
    set_pdata_int(entity, OFFSET_CLIPAMMO, amount, OFFSET_LINUX_WEAPONS);
}

// Get Weapon Clip Ammo
stock fm_get_weapon_ammo(entity)
{
    return get_pdata_int(entity, OFFSET_CLIPAMMO, OFFSET_LINUX_WEAPONS);
}

// Set User Zoom
stock fm_remove_user_zoom(id)
{
    set_pdata_int(id, OFFSET_ZOOMTYPE, CS_NO_ZOOM, OFFSET_LINUX);
}

// Get User Deaths
stock fm_get_user_deaths(id)
{
    return get_pdata_int(id, OFFSET_CSDEATHS, OFFSET_LINUX);
}

// Set User Deaths
stock fm_set_user_deaths(id, value)
{
    set_pdata_int(id, OFFSET_CSDEATHS, value, OFFSET_LINUX);
}

// Set Bots NVG
stock fm_set_bot_nvg(id, value)
{    
    g_nvision[id] = true;
    
    set_pdata_int(id, OFFSET_NVGOGGLES, value, OFFSET_LINUX);
    
    /*
    // Seems this isn't really needed either
    emessage_begin(MSG_ONE, g_msgNVGToggle, _, id)
    ewrite_byte(value) // 1 On - 0 Off
    emessage_end()
    */
}

// Get User Team
stock fm_get_user_team(id)
{
    return get_pdata_int(id, OFFSET_CSTEAMS, OFFSET_LINUX);
}

// Set a Player's Team
stock fm_set_user_team(id, team)
{
    set_pdata_int(id, OFFSET_CSTEAMS, team, OFFSET_LINUX);
}

// Send User Team Message
public fm_set_user_team_msg(taskid)
{    
    // Beware: this message can now be picked up by other metamod
    // plugins (yeah, that includes AMXX plugins as well)
    
    // Set the switching team flag
    g_switchingteam[ID_TEAM] = true;
    
    // Tell everyone my new team
    emessage_begin(MSG_ALL, g_msgTeamInfo)
    ewrite_byte(ID_TEAM)
    
    switch (fm_get_user_team(ID_TEAM))
    {
        case CS_TEAM_UNASSIGNED: ewrite_string("UNASSIGNED");
        case CS_TEAM_T: ewrite_string("TERRORIST");
        case CS_TEAM_CT: ewrite_string("CT");
        case CS_TEAM_SPECTATOR: ewrite_string("SPECTATOR");
    }
    
    emessage_end()
    
    // Done switching team
    g_switchingteam[ID_TEAM] = false;
}

#if defined HANDLE_MODELS_ON_SEPARATE_ENT
// Set User Model on Entity
stock fm_set_user_model_ent(id)
{
    static model[100], ent
    
    // format model string
    formatex(model, sizeof model - 1, "models/player/%s/%s.mdl", g_playermodel[id], g_playermodel[id])
    
    ent = fm_find_ent_by_owner(-1, "player_model", id) // get player_model entity
    
    if (!ent) // doesn't exist, make a new one
    {
        ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
        set_pev(ent, pev_classname, "player_model")
        set_pev(ent, pev_movetype, MOVETYPE_FOLLOW)
        engfunc(EngFunc_SetModel, ent, model)
        set_pev(ent, pev_aiment, id)
        set_pev(ent, pev_owner, id)
    }
    else
    {
        engfunc(EngFunc_SetModel, ent, model)
    }
}

// Set Weapon Model on Entity
stock fm_set_weapon_model_ent(id, const model[])
{
    static ent
    ent = fm_find_ent_by_owner(-1, "weapon_model", id) // get weapon_model entity
    
    if (!ent) // doesn't exist, make a new one
    {
        ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
        set_pev(ent, pev_classname, "weapon_model")
        set_pev(ent, pev_movetype, MOVETYPE_FOLLOW)
        engfunc(EngFunc_SetModel, ent, model)
        set_pev(ent, pev_aiment, id)
        set_pev(ent, pev_owner, id)
    }
    else
    {
        engfunc(EngFunc_SetModel, ent, model)
    }
}
#else
// Set User Model by ClientKeyValue
public fm_set_user_model(taskid)
{
    engfunc(EngFunc_SetClientKeyValue, ID_MODEL, engfunc(EngFunc_GetInfoKeyBuffer, ID_MODEL), "model", g_playermodel[ID_MODEL])
}

// Get User Model -model passed byref-
stock fm_get_user_model(player, model[], len)
{    
    engfunc(EngFunc_InfoKeyValue, engfunc(EngFunc_GetInfoKeyBuffer, player), "model", model, len)
}
#endif
/* AMXX-Studio Notes - DO NOT MODIFY BELOW HERE
*{\\ rtf1\\ ansi\\ deff0{\\ fonttbl{\\ f0\\ fnil Tahoma;}}\n\\ viewkind4\\ uc1\\ pard\\ lang3082\\ f0\\ fs16 \n\\ par }
*/

Last edited by sl4y62; 05-16-2012 at 14:19.
sl4y62 is offline
sl4y62
Junior Member
Join Date: Dec 2009
Old 05-17-2012 , 21:55   Re: [EDIT] ZP 3.59 Main Function
Reply With Quote #2

up !
This is a function that i need , "Make a Zombie" and disregarded the other functions (class , menus ...)
And i dont need the g_survround , only the normal infection

its possible?

PHP Code:
// Make Zombie Task
public make_zombie_task()
{
    
// Call make a zombie with playerid MAKEZOMBIE_TASK and no further args
    
make_a_zombie(MAKEZOMBIE_TASK0000);
}

// Make a Zombie Function
make_a_zombie(playeridnemsurvswarmmulti)
{    
    
// Get alive players count
    
static iPlayersnum
    iPlayersnum 
fnGetAlive()
    
    if (
iPlayersnum 1// not enough players
    
{
        
set_task(10.0"make_zombie_task"MAKEZOMBIE_TASK)
        return;
    }
    
    
g_models_i 0.0 // reset model change count
    
g_teams_i 0.0 // reset teams change count
    
    // round starting
    
g_newround false
    g_survround 
false
    g_nemround 
false
    g_swarmround 
false
    
    
static id
    
    
if ((playerid == MAKEZOMBIE_TASK && random_num(1get_pcvar_num(cvar_survchance)) == get_pcvar_num(cvar_surv)) || surv// Survival Chance
    
{        
        
g_survround true // survivor round
        
        
if (playerid == MAKEZOMBIE_TASK// choose player randomly?
            
id fnGetRandomAlive(random_num(1iPlayersnum))
        else
            
id playerid
        
        g_survivor
[id] = true // set var
        
        
if (get_pcvar_num(cvar_survleap)) // give survivor leap?
            
fm_give_item(id"item_longjump")
        
        
// Remove previous tasks
        
remove_task(id+TEAM_TASK)
        
        
// Switch to CT
        
if (fm_get_user_team(id) != CS_TEAM_CT// need to change team?
        
{        
            
fm_set_user_team(idCS_TEAM_CT)
            
fm_set_user_team_msg(id+TEAM_TASK)
        }
        
        
// Set Health [0 = auto]
        
if (!get_pcvar_num(cvar_survhp))
            
fm_set_user_health(idfnGetHumans()*get_pcvar_num(cvar_humanhp))
        else
            
fm_set_user_health(idget_pcvar_num(cvar_survhp))
        
        
// Set Gravity
        
set_pev(idpev_gravityget_pcvar_float(cvar_survgravity))
        
        
// glow?
        
if (get_pcvar_num(cvar_survglow))
        {
            
#if defined HANDLE_MODELS_ON_SEPARATE_ENT
            
fm_set_rendering(fm_find_ent_by_owner(-1"player_model"id), kRenderFxGlowShell00255kRenderNormal25)
            
#else
            
fm_set_rendering(idkRenderFxGlowShell00255kRenderNormal25)
            
#endif
        
}
        
        
fm_strip_user_weapons(id// strip player from weapons
        
fm_give_item(id"weapon_m249"// give M249 machinegun only
        
        // Turn off his flashlight
        
turn_off_flashlight(id)
        
set_task(3.0"turn_off_flashlight"id// bugfix
        
        // Give the survivor a bright light
        
set_pev(idpev_effectspev(idpev_effects) | EF_BRIGHTLIGHT)
        
        
// Survivor bots will also need nightvision to see in the dark
        
if (is_user_bot(id)) fm_set_bot_nvg(id1);
        
        
// Play survivor sound
        
PlaySound(sound_survivor[random_num(0sizeof sound_survivor -1)]);
        
        static 
name[32]
        
get_user_name(idnamesizeof name 1)
        
        
// show Survivor HUD notice
        
set_hudmessage(2020255, -1.00.2510.05.01.01.0, -1)
        
ShowSyncHudMsg(0g_MsgSync"%L"LANG_PLAYER"NOTICE_SURVIVOR"name)
    
        
// Turn the rest of players into zombies
        
for (id 1id <= g_maxplayersid++)
        {
            if (!
is_user_alive(id)) // skip dead
                
continue;
        
            if (
g_survivor[id]) // skip the survivor
                
continue;            
            
            
zombieme(id01// turn into a zombie
        
}
    }
    else if ((
playerid == MAKEZOMBIE_TASK && random_num(1get_pcvar_num(cvar_swarmchance)) == get_pcvar_num(cvar_swarm)) || swarm// Swarm Chance
    
{        
        
g_swarmround true // swarm round
        
        // Turn all the T into zombies
        
for (id 1id <= g_maxplayersid++)
        {            
            if (!
is_user_alive(id)) // not alive
                
continue;
            
            if (
fm_get_user_team(id) == CS_TEAM_T// only Terrorists
                
zombieme(id01// turn into a zombie
        
}
        
        
// Play swarm sound
        
PlaySound(sound_swarm[random_num(0sizeof sound_swarm -1)]);
        
        
// Show Swarm HUD notice
        
set_hudmessage(2025520, -1.00.2510.05.01.01.0, -1)
        
ShowSyncHudMsg(0g_MsgSync"%L"LANG_PLAYER"NOTICE_SWARM")
    }
    else if ((
playerid == MAKEZOMBIE_TASK && random_num(1get_pcvar_num(cvar_multichance)) == get_pcvar_num(cvar_multi) && floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil) > 1) || multi// Multi Infection Chance
    
{
        static 
iZombiesiMaxZombies
        
        
// iMaxZombies is rounded up, in case there aren't enough players
        
iMaxZombies floatround(iPlayersnum*get_pcvar_float(cvar_multiratio), floatround_ceil)
        
iZombies 0
        
        
// Randomly turn iMaxZombies players into zombies
        
while (iZombies iMaxZombies)
        {
            if (
id g_maxplayers)
                
id++
            else
                
id 1
            
            
if (!is_user_alive(id) || g_zombie[id]) // dead or already a zombie
                
continue;
            
            if (
random_num(01))
            {
                
zombieme(id01// turn into a zombie
                
iZombies++
            }
        }
        
        
// Turn the rest of players into humans
        
for (id 1id <= g_maxplayersid++)
        {            
            if (!
is_user_alive(id) || g_zombie[id]) // only those of them who arent zombies
                
continue
            
            
// remove previous tasks
            
remove_task(id+TEAM_TASK)
            
            
// Switch to CT
            
if (fm_get_user_team(id) != CS_TEAM_CT// need to change team?
            
{
                
fm_set_user_team(idCS_TEAM_CT)
                
set_task(0.1+g_teams_i"fm_set_user_team_msg"id+TEAM_TASK)
                
g_teams_i += 0.1// increase teams task count
            
}
        }
        
        
// Play multi infection sound
        
PlaySound(sound_multi[random_num(0sizeof sound_multi -1)]);
        
        
// Show Multi Infection HUD notice
        
set_hudmessage(200500, -1.00.2510.05.01.01.0, -1)
        
ShowSyncHudMsg(0g_MsgSync"%L"LANG_PLAYER"NOTICE_MULTI")
    }
    else
    {
        
// Infection mode (only 1 zombie)
        
        
if (playerid == MAKEZOMBIE_TASK// choose player randomly?
            
id fnGetRandomAlive(random_num(1iPlayersnum))
        else
            
id playerid
        
        
// Nemesis Chance
        
if ((playerid == MAKEZOMBIE_TASK && random_num(1get_pcvar_num(cvar_nemchance)) == get_pcvar_num(cvar_nem)) || nem)
        {
            
g_nemround true // nemesis round
            
zombieme(id10)
        }
        else
        {
            
zombieme(id00// boring first zombie
        
}
        
        
// Rest of players left as humans (CTs)
        
for (id 1id <= g_maxplayersid++)
        {
            if (!
is_user_alive(id)) // skip dead
                
continue;
                
            if (
g_zombie[id]) // skip our first zombie
                
continue;
            
            
// Remove previous tasks
            
remove_task(id+TEAM_TASK)
            
            
// Switch to CT
            
if (fm_get_user_team(id) != CS_TEAM_CT// need to change team?
            
{
                
fm_set_user_team(idCS_TEAM_CT)
                
set_task(0.1+g_teams_i"fm_set_user_team_msg"id+TEAM_TASK)
                
g_teams_i += 0.1// increase teams task count
            
}
        }
    }
    
    
// Last Zombie Check
    
set_task(0.1"fnCheckLastZombie")


Last edited by sl4y62; 05-17-2012 at 21:56.
sl4y62 is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 17:12.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode