]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/weapons
authorMario <zacjardine@y7mail.com>
Fri, 14 Nov 2014 02:05:18 +0000 (13:05 +1100)
committerMario <zacjardine@y7mail.com>
Fri, 14 Nov 2014 02:05:18 +0000 (13:05 +1100)
Conflicts:
mutators.cfg
qcsrc/client/hud.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/notifications.qh
qcsrc/server/cl_weapons.qc
qcsrc/server/mutators/mutator_nades.qc
qcsrc/server/progs.src
qcsrc/server/w_electro.qc
qcsrc/server/w_minelayer.qc

39 files changed:
1  2 
defaultXonotic.cfg
mutators.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/progs.src
qcsrc/client/waypointsprites.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/constants.qh
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/notifications.qh
qcsrc/common/stats.qh
qcsrc/common/util.qh
qcsrc/common/weapons/w_arc.qc
qcsrc/common/weapons/w_minelayer.qc
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c
qcsrc/server/autocvars.qh
qcsrc/server/bot/aim.qc
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_ca.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/mutator_minstagib.qc
qcsrc/server/mutators/mutator_nades.qc
qcsrc/server/mutators/mutator_touchexplode.qc
qcsrc/server/progs.src
qcsrc/server/t_quake3.qc
qcsrc/server/teamplay.qc
qcsrc/server/weapons/csqcprojectile.qc
qcsrc/server/weapons/weaponsystem.qc

diff --combined defaultXonotic.cfg
index 21f8e65e79df010a8acc84ef7bff7fd306a607f6,18af3e8951af7491b789083dc8c097f249327cc2..9480860c6e6bb45ef7b4e15d9b227ed6267dbd5e
@@@ -57,7 -57,7 +57,7 @@@ _cl_playerskin 
  
  seta cl_reticle 1 "control for toggling whether ANY zoom reticles are shown"
  seta cl_reticle_stretch 0 "whether to stretch reticles so they fit the screen (breaks image proportions)"
 -seta cl_reticle_item_nex 1 "draw aiming reticle for the nex weapon's zoom, 0 disables and values between 0 and 1 change alpha"
 +seta cl_reticle_item_vortex 1 "draw aiming reticle for the vortex weapon's zoom, 0 disables and values between 0 and 1 change alpha"
  seta cl_reticle_item_normal 1 "draw reticle when zooming with the zoom button, 0 disables and values between 0 and 1 change alpha"
  fov 100
  seta cl_velocityzoom 0        "velocity based zooming of fov, negative values zoom out"
@@@ -360,9 -360,9 +360,9 @@@ set bot_ai_keyboard_threshold 0.5
  set bot_ai_aimskill_offset 0.3 "Amount of error induced to the bots aim"
  set bot_ai_aimskill_think 1 "Aiming velocity. Use values below 1 for slower aiming"
  set bot_ai_custom_weapon_priority_distances "300 850" "Define close and far distances in any order. Based on the distance to the enemy bots will choose different weapons"
 -set bot_ai_custom_weapon_priority_far   "minstanex nex rifle electro rocketlauncher grenadelauncher hagar hlac crylink laser uzi fireball seeker shotgun tuba minelayer"      "Desired weapons for far distances ordered by priority"
 -set bot_ai_custom_weapon_priority_mid   "minstanex rocketlauncher nex fireball seeker grenadelauncher electro uzi crylink hlac hagar shotgun laser rifle tuba minelayer"      "Desired weapons for middle distances ordered by priority"
 -set bot_ai_custom_weapon_priority_close "minstanex shotgun nex uzi hlac tuba seeker hagar crylink grenadelauncher electro rocketlauncher laser fireball rifle minelayer"      "Desired weapons for close distances ordered by priority"
 +set bot_ai_custom_weapon_priority_far   "vaporizer vortex rifle electro devastator grenadelauncher hagar hlac crylink laser machinegun fireball seeker shotgun tuba minelayer"        "Desired weapons for far distances ordered by priority"
 +set bot_ai_custom_weapon_priority_mid   "vaporizer devastator vortex fireball seeker mortar electro machinegun crylink hlac hagar shotgun blaster rifle tuba minelayer arc shockwave" "Desired weapons for middle distances ordered by priority"
 +set bot_ai_custom_weapon_priority_close "vaporizer shotgun vortex machinegun hlac tuba seeker hagar crylink mortar electro rocketlauncher blaster fireball rifle minelayer arc shockwave"     "Desired weapons for close distances ordered by priority"
  set bot_ai_weapon_combo 1     "Enable bots to do weapon combos"
  set bot_ai_weapon_combo_threshold 0.4 "Try to make a combo N seconds after the last attack"
  set bot_ai_friends_aware_pickup_radius "500"  "Bots will not pickup items if a team mate is this distance near the item"
@@@ -544,12 -544,12 +544,12 @@@ r_mipsprites 
  r_mipskins 1
  r_shadow_realtime_world_lightmaps 1
  cl_decals_fadetime 5
- cl_decals_time 2
+ cl_decals_time 1
  seta cl_gunalign 3 "Gun alignment; 1 = center (if allowed by g_shootfromclient) or right, 2 = center (if allowed by g_shootfromclient) or left, 3 = right only, 4 = left only"
  seta cl_nogibs 0 "reduce number of violence effects, or remove them totally"
  seta cl_particlegibs 0 "simpler gibs"
  seta cl_gibs_damageforcescale 3.5 "force to push around gibs"
- seta cl_gibs_lifetime 5 "average lifetime of gibs"
+ seta cl_gibs_lifetime 2.5 "average lifetime of gibs"
  seta cl_gibs_velocity_scale 1 "gib throw velocity force scale"
  seta cl_gibs_velocity_random 1 "gib throw velocity randomness scale"
  seta cl_gibs_velocity_up 1 "extra z velocity for gibs"
@@@ -561,7 -561,7 +561,7 @@@ seta cl_casings_shell_time 30 "shell ca
  seta cl_casings_bronze_time 10 "bullet casings lifetime"
  seta cl_casings_ticrate 0.1 "ticrate for casings"
  seta cl_casings_sloppy 1 "sloppy casings, may temporarily penetrate walls"
- seta cl_projectiles_sloppy 0 "sloppy projectiles, may temporarily penetrate walls"
+ seta cl_projectiles_sloppy 1 "sloppy projectiles, may temporarily penetrate walls"
  cl_stainmaps 0
  cl_particles_smoke 1
  vid_gl20 1
@@@ -813,6 -813,12 +813,12 @@@ seta g_maplist_votable_nodetail 1        "node
  seta g_maplist_votable_abstain 0      "when 1, you can abstain from your vote"
  seta g_maplist_votable_screenshot_dir "maps levelshots"       "where to look for map screenshots"
  
+ set sv_vote_gametype 0 "show a vote screen for gametypes before map vote screen"
+ set sv_vote_gametype_keeptwotime 10 "show only 2 options for this amount of time during gametype vote screen"
+ set sv_vote_gametype_options "dm ctf ca lms tdm ft"
+ set sv_vote_gametype_timeout 20
+ set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes"
  set g_chat_flood_spl 3        "normal chat: seconds between lines to not count as flooding"
  set g_chat_flood_lmax 2       "normal chat: maximum number of lines per chat message at once"
  set g_chat_flood_burst 2      "normal chat: allow bursts of so many chat lines"
@@@ -1056,7 -1062,7 +1062,7 @@@ set con_completion_gotomap      ma
  set con_completion_vmap               map
  set con_completion_vnextmap   map
  set con_completion_vdomap     map
- set con_completion_playermodel        models/player/*.iqm
+ set con_completion_playermodel        "models/player/*.iqm"
  
  // helper
  // these non-saved engine cvars shall be saved
@@@ -1138,12 -1144,12 +1144,12 @@@ sv_allowdownloads 0 // download protoco
  
  set g_jump_grunt 0    "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?"
  
 -seta cl_weaponpriority "minstanex nex fireball grenadelauncher uzi hagar rifle electro rocketlauncher crylink minelayer shotgun hlac tuba laser porto seeker hook" "weapon priority list"
 +seta cl_weaponpriority "minstanex vortex fireball grenadelauncher machinegun hagar rifle electro rocketlauncher crylink minelayer shotgun hlac tuba laser porto seeker hook" "weapon priority list"
  seta cl_weaponpriority_useforcycling 0 "when set, weapon cycling by the mouse wheel makes use of the weapon priority list (the special value 2 uses the weapon ID list for cycling)"
  seta cl_weaponpriority0 "rocketlauncher grenadelauncher hagar seeker fireball" "use impulse 200 for prev gun from this list, 210 for best gun, 220 for next gun.  Default value: explosives"
 -seta cl_weaponpriority1 "minstanex nex crylink hlac electro laser"             "use impulse 201 for prev gun from this list, 211 for best gun, 221 for next gun.  Default value: energy"
 -seta cl_weaponpriority2 "minstanex nex rifle"                           "use impulse 202 for prev gun from this list, 212 for best gun, 222 for next gun.  Default value: hitscan exact"
 -seta cl_weaponpriority3 "minstanex nex rifle uzi shotgun"               "use impulse 203 for prev gun from this list, 213 for best gun, 223 for next gun.  Default value: hitscan all"
 +seta cl_weaponpriority1 "minstanex vortex crylink hlac electro laser"             "use impulse 201 for prev gun from this list, 211 for best gun, 221 for next gun.  Default value: energy"
 +seta cl_weaponpriority2 "minstanex vortex rifle"                           "use impulse 202 for prev gun from this list, 212 for best gun, 222 for next gun.  Default value: hitscan exact"
 +seta cl_weaponpriority3 "minstanex vortex rifle machinegun shotgun"               "use impulse 203 for prev gun from this list, 213 for best gun, 223 for next gun.  Default value: hitscan all"
  seta cl_weaponpriority4 "grenadelauncher minelayer hlac hagar crylink seeker shotgun"    "use impulse 204 for prev gun from this list, 214 for best gun, 224 for next gun.  Default value: spam weapons"
  seta cl_weaponpriority5 "laser hook porto"                                     "use impulse 205 for prev gun from this list, 215 for best gun, 225 for next gun.  Default value: weapons for moving"
  seta cl_weaponpriority6 "" "use impulse 206 for prev gun from this list, 216 for best gun, 226 for next gun"
@@@ -1258,7 -1264,7 +1264,7 @@@ set bot_sound_monopoly 0 "when enabled
  
  set cl_loddistance1 1024
  set cl_loddistance2 3072
- seta cl_playerdetailreduction 1       "the higher, the less detailed player models are displayed (LOD)"
+ seta cl_playerdetailreduction 4       "the higher, the less detailed player models are displayed (LOD)"
  seta cl_modeldetailreduction 1        "the higher, the less detailed certain map models are displayed (LOD)"
  
  set g_mapinfo_settemp_acl "+*" "ACL for mapinfo setting cvars"
@@@ -1377,19 -1383,19 +1383,19 @@@ cl_decals_newsystem 
  // set the cvars to "0" to totally disable a weapon
  set g_weaponreplace_laser ""
  set g_weaponreplace_shotgun ""
 -set g_weaponreplace_uzi ""
 +set g_weaponreplace_machinegun ""
  set g_weaponreplace_grenadelauncher ""
  set g_weaponreplace_electro ""
  set g_weaponreplace_crylink ""
 -set g_weaponreplace_nex ""
 +set g_weaponreplace_vortex ""
  set g_weaponreplace_hagar ""
  set g_weaponreplace_rocketlauncher ""
  set g_weaponreplace_porto ""
 -set g_weaponreplace_minstanex ""
 +set g_weaponreplace_vaporizer ""
  set g_weaponreplace_hook ""
  set g_weaponreplace_tuba ""
  set g_weaponreplace_fireball ""
 -set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping uzi and shotgun (for Q3A map compatibility in mapinfo files)"
 +set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping machinegun and shotgun (for Q3A map compatibility in mapinfo files)"
  
  set g_movement_highspeed 1 "movement speed modification factor (only changes movement when above maxspeed)"
  
@@@ -1436,6 -1442,7 +1442,7 @@@ r_shadow_glossintensity 
  r_fakelight 1
  
  r_water_hideplayer 1 // hide your own feet/player model in refraction views, this way you don't see half of your body under water
+ r_water_refractdistort 0.019
  
  // strength sound settings
  set sv_strengthsound_antispam_time 0.1 "minimum distance of strength sounds"
@@@ -1542,7 -1549,8 +1549,7 @@@ scr_loadingscreen_scale_base 
  scr_loadingscreen_scale_limit 2
  
  // other config files
 -exec mutator_new_toys.cfg // run BEFORE balance to make sure balance wins
 -exec balanceXonotic.cfg
 +exec balance-xonotic.cfg
  exec effects-normal.cfg
  exec physicsX.cfg
  exec turrets.cfg
diff --combined mutators.cfg
index 0dde22eedad304914abc1587f9726d96a8e612df,4b79048677499c02e4af4a2ff9071b05702d72c7..6873aa89ea604ca1c3af89f99dbae187af404791
@@@ -138,10 -138,11 +138,11 @@@ set g_random_gravity_negative 1000 "neg
  
  
  // =======
- //  nades
+ //  Nades
  // =======
  set g_nades 0 "enable off-hand grenades"
  set g_nades_spawn 1 "give nades right away when player spawns rather than delaying entire refire"
+ set g_nades_client_select 0 "allow client side selection of nade type"
  set g_nades_nade_lifetime 3.5
  set g_nades_nade_minforce 400
  set g_nades_nade_maxforce 2000
@@@ -152,6 -153,73 +153,73 @@@ set g_nades_nade_edgedamage 9
  set g_nades_nade_radius 300
  set g_nades_nade_force 650
  set g_nades_nade_newton_style 0
+ set g_nades_nade_type 1 "Type of the off-hand grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade"
+ seta cl_nade_timer 1 "show a visual timer for nades, 1 = only circle, 2 = circle with text"
+ seta cl_nade_type 3
+ seta cl_pokenade_type "zombie"
+ // ------------
+ //  Nade bonus
+ // ------------
+ //
+ // How the nade bonus system works:
+ // Each player has a score counter that is increased by some actions (eg: capping, fragging...)
+ // Once this counter reaches its maximum, the player will receive a bonus grenade and the score counter resets
+ // If the player dies all the bonus nades will be lost and the score counter resets
+ // If g_nades_bonus_score_time is not zero, this score will increase or decrease over time
+ //
+ set g_nades_bonus 0 "Enable bonus grenades"
+ set g_nades_bonus_client_select 0 "Allow client side selection of bonus nade type"
+ set g_nades_bonus_type 2 "Type of the bonus grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade"
+ set g_nades_bonus_onstrength 1 "Always give bonus grenades to players that have the strength powerup"
+ set g_nades_bonus_max 3 "Maximum number of bonus grenades"
+ // Bonus score
+ set g_nades_bonus_score_max   120 "Score value that will give a bonus nade"
+ set g_nades_bonus_score_minor   5 "Score given for minor actions (pickups, regular frags etc.)"
+ set g_nades_bonus_score_low    20 "Score given for frags and unfreezes"
+ set g_nades_bonus_score_medium 30 "Score given for flag returns and flag carrier kills"
+ set g_nades_bonus_score_high   60 "Score given for flag captures"
+ set g_nades_bonus_score_spree  40 "Score given every spree of this many frags"
+ set g_nades_bonus_score_time   -1 "Bonus nade score given per second (negative to have the score decay)"
+ set g_nades_bonus_score_time_flagcarrier 2 "Bonus nade score given per second as flag carrier (negative to have the score decay)"
+ // Napalm (2)
+ set g_nades_napalm_blast 1 "Whether the napalm grenades also give damage with the usual grenade explosion"
+ set g_nades_napalm_burntime 0.5 "Time that the fire from napalm will stick to the player"
+ set g_nades_napalm_selfdamage 1 "Whether the player that tossed the nade can be harmed by its fire"
+ // Napalm fireballs
+ set g_nades_napalm_ball_count 6 "Number of fireballs emitted during the explosion"
+ set g_nades_napalm_ball_spread 500 "Maximum force which the fireballs will have on explosion"
+ set g_nades_napalm_ball_damageforcescale 4
+ set g_nades_napalm_ball_damage 40
+ set g_nades_napalm_ball_lifetime 7
+ set g_nades_napalm_ball_radius 100 "Distance from the fireball within which you may get burned"
+ // Napalm Fire fountain
+ set g_nades_napalm_fountain_lifetime 3 "Time period during which extra fire mines are ejected"
+ set g_nades_napalm_fountain_delay 0.5 "Delay between emissions by the fountain"
+ set g_nades_napalm_fountain_damage 50 "Damage caused by the center of the fountain"
+ set g_nades_napalm_fountain_edgedamage 20 "Damage caused by the edge of the fountain"
+ set g_nades_napalm_fountain_radius 130
+ // Ice (3)
+ set g_nades_ice_freeze_time 3 "How long the ice field will last"
+ set g_nades_ice_health      0 "How much health the player will have after being unfrozen"
+ set g_nades_ice_explode     0 "Whether the ice nade should explode again once the ice field dissipated"
+ set g_nades_ice_teamcheck   0 "Don't freeze teammates"
+ // Spawn (5)
+ set g_nades_spawn_count 3 "Number of times player will spawn at their spawn nade explosion location"
+ // Heal (6)
+ set g_nades_heal_time 5 "How long the heling field will last"
+ set g_nades_heal_rate 30 "Health given per second"
+ set g_nades_heal_friend 1 "Multiplier of health given to team mates"
+ set g_nades_heal_foe   -2 "Multiplier of health given to enemies"
+ // Pokenade (7)
+ set g_nades_pokenade_monster_lifetime 150 "How long pokenade monster will survive"
+ set g_nades_pokenade_monster_type "zombie" "Monster to spawn"
  
  
  // ============
@@@ -163,8 -231,49 +231,57 @@@ set g_campcheck_damage 10
  set g_campcheck_distance 1800
  
  
++<<<<<<< HEAD
 +// ==========
 +//  New Toys
 +// ==========
 +set g_new_toys 0 "Mutator 'New Toys': enable extra fun guns"
 +set g_new_toys_autoreplace 2 "0: never replace, 1: always auto replace guns by available new toys, 2: randomly auto replace guns by available new toys"
++=======
+ // =======
+ //  buffs
+ // =======
+ set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another"
+ set g_buffs 0 "enable buffs (requires buff items or powerups)"
+ set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item"
+ set g_buffs_randomize 1 "randomize buff type when player drops buff"
+ set g_buffs_random_lifetime 30 "re-spawn the buff again if it hasn't been touched after this time in seconds"
+ set g_buffs_random_location 0 "randomize buff location on start and when reset"
+ set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up"
+ set g_buffs_spawn_count 5 "how many buffs to spawn on the map if none exist already"
+ set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs"
+ set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated"
+ set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading"
+ set g_buffs_ammo 1 "ammo buff: infinite ammunition"
+ set g_buffs_resistance 1 "resistance buff: greatly reduces damage taken"
+ set g_buffs_resistance_blockpercent 0.7 "damage reduction multiplier, higher values mean less damage"
+ set g_buffs_medic 1 "medic buff: increased regeneration speed, extra health, chance to survive a fatal attack"
+ set g_buffs_medic_survive_chance 0.6 "multiplier chance of player surviving a fatal hit"
+ set g_buffs_medic_survive_health 5 "amount of health player survives with after taking a fatal hit"
+ set g_buffs_medic_rot 0.7 "health rot rate multiplier"
+ set g_buffs_medic_max 1.5 "stable health medic limit multiplier"
+ set g_buffs_medic_regen 1.7 "health medic rate multiplier"
+ set g_buffs_vengeance 1 "vengeance buff: attackers also take damage"
+ set g_buffs_vengeance_damage_multiplier 0.6 "amount of damage dealt the attacker takes when hitting a target with vengeance"
+ set g_buffs_bash 1 "bash buff: increased knockback force and immunity to knockback"
+ set g_buffs_bash_force 2 "bash force multiplier"
+ set g_buffs_bash_force_self 1.2 "bash self force multiplier"
+ set g_buffs_disability 1 "disability buff: attacks to players and monsters deal slowness (decreased movement/attack speed) for a few seconds"
+ set g_buffs_disability_time 3 "time in seconds for target disability"
+ set g_buffs_disability_speed 0.5 "player speed multiplier while disabled"
+ set g_buffs_disability_rate 1.7 "player weapon rate multiplier while disabled"
+ set g_buffs_speed 1 "speed buff: increased movement/attack/health regeneration speed, carrier takes slightly more damage"
+ set g_buffs_speed_speed 1.7 "player speed multiplier while holding speed buff"
+ set g_buffs_speed_rate 0.8 "player weapon rate multiplier while holding speed buff"
+ set g_buffs_speed_damage_take 1.2 "damage taken multiplier while holding speed buff"
+ set g_buffs_speed_regen 1.2 "regeneration speed multiplier while holding speed buff"
+ set g_buffs_vampire 1 "vampire buff: attacks to players and monsters heal the carrier"
+ set g_buffs_vampire_damage_steal 0.6 "damage stolen multiplier while holding vampire buff"
+ set g_buffs_jump 1 "jump buff: greatly increased jump height"
+ set g_buffs_jump_height 600 "jump height while holding jump buff"
+ set g_buffs_flight 1 "flight buff: greatly decreased gravity"
+ set g_buffs_flight_gravity 0.3 "player gravity multiplier while holding flight buff"
+ set g_buffs_invisible 1 "invisible buff: carrier becomes invisible"
+ set g_buffs_invisible_alpha 0.4 "player invisibility multiplier while holding invisible buff"
++>>>>>>> master
diff --combined qcsrc/client/Main.qc
index 83098ea29d4dbf0824be5d7390630aeb174f2903,c0e37ae62177b536a0d60a085ae6b056684241b8..7d85532ba54e51a84f4756ab0ecc65a26f574e23
@@@ -87,6 -87,9 +87,9 @@@ void CSQC_Init(void
        registercvar("hud_usecsqc", "1");
        registercvar("scoreboard_columns", "default");
  
+       registercvar("cl_nade_type", "3");
+       registercvar("cl_pokenade_type", "zombie");
        gametype = 0;
  
        // hud_fields uses strunzone on the titles!
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
        CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
  
        WaypointSprite_Load();
  
        Hook_Precache();
        GibSplash_Precache();
        Casings_Precache();
 -      DamageInfo_Precache();
        Vehicles_Precache();
        turrets_precache();
        Tuba_Precache();
  
        if(autocvar_cl_reticle)
        {
 -              if(autocvar_cl_reticle_item_normal) { precache_pic("gfx/reticle_normal"); }
 -              if(autocvar_cl_reticle_item_nex) { precache_pic("gfx/reticle_nex"); }
 +              precache_pic("gfx/reticle_normal");
 +              // weapon reticles are precached in weapon files
        }
  
        get_mi_min_max_texcoords(1); // try the CLEVER way first
@@@ -818,7 -823,8 +822,7 @@@ void CSQC_Ent_Update(float bIsNewEntity
                case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break;
                case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break;
                case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break;
 -              case ENT_CLIENT_LGBEAM: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_LGBEAM); break;
 -              case ENT_CLIENT_GAUNTLET: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_GAUNTLET); break;
 +              case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break;
                case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break;
                case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break;
                case ENT_CLIENT_TURRET: ent_turret(); break;
                case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break;
                case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break;
                case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break;
+               case ENT_CLIENT_HEALING_ORB: ent_healer(); break;
  
                default:
                        //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
@@@ -957,10 -964,14 +962,10 @@@ void Ent_Init(
        hook_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
        hook_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
        hook_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
 -      electro_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
 -      gauntlet_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
 +      arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
  
        if(forcefog)
                strunzone(forcefog);
        g_balance_electro_secondary_bouncefactor = ReadCoord();
        g_balance_electro_secondary_bouncestop = ReadCoord();
  
 -      nex_scope = !ReadByte();
 +      vortex_scope = !ReadByte();
        rifle_scope = !ReadByte();
  
        serverflags = ReadByte();
@@@ -1170,7 -1181,7 +1175,7 @@@ void Net_WeaponComplain(
  
        if(complain_weapon_name)
                strunzone(complain_weapon_name);
 -      complain_weapon_name = strzone(ReadString());
 +      complain_weapon_name = strzone(WEP_NAME(complain_weapon));
  
        complain_weapon_type = ReadByte();
  
@@@ -1214,16 -1225,16 +1219,16 @@@ float CSQC_Parse_TempEntity(
                        Net_ReadRace();
                        bHandled = true;
                        break;
 -              case TE_CSQC_NEXGUNBEAMPARTICLE:
 -                      Net_ReadNexgunBeamParticle();
 +              case TE_CSQC_VORTEXBEAMPARTICLE:
 +                      Net_ReadVortexBeamParticle();
                        bHandled = true;
                        break;
                case TE_CSQC_TEAMNAGGER:
                        Net_TeamNagger();
                        bHandled = true;
                        break;
 -              case TE_CSQC_LIGHTNINGARC:
 -                      Net_ReadLightningarc();
 +              case TE_CSQC_ARC:
 +                      Net_ReadArc();
                        bHandled = true;
                        break;
                case TE_CSQC_PINGPLREPORT:
                        cl_notice_read();
                        bHandled = true;
                        break;
 +              case TE_CSQC_SHOCKWAVEPARTICLE:
 +                      Net_ReadShockwaveParticle();
 +                      bHandled = true;
 +                      break;
                default:
                        // No special logic for this temporary entity; return 0 so the engine can handle it
                        bHandled = false;
diff --combined qcsrc/client/View.qc
index 5e162cb7c96fe4c4267592ff482f4726e85f8344,a069faa46578dd07f5aa3600220bd233400185b6..f68ea32e53ec7781f05ed2f5e46c8d449ef1eb3e
@@@ -104,7 -104,7 +104,7 @@@ vector GetCurrentFov(float fov
  
        zoomdir = button_zoom;
        if(hud == HUD_NORMAL)
 -      if((activeweapon == WEP_NEX && nex_scope) || (activeweapon == WEP_RIFLE && rifle_scope)) // do NOT use switchweapon here
 +      if((activeweapon == WEP_VORTEX && vortex_scope) || (activeweapon == WEP_RIFLE && rifle_scope)) // do NOT use switchweapon here
                zoomdir += button_attack2;
        if(spectatee_status > 0 || isdemo())
        {
@@@ -269,7 -269,7 +269,7 @@@ float EnemyHitCheck(
  
  float TrueAimCheck()
  {
 -      float nudge = 1; // added to traceline target and subtracted from result
 +      float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us?
        vector vecs, trueaimpoint, w_shotorg;
        vector mi, ma, dv;
        float shottype;
        ta = trueaim;
        mv = MOVE_NOMONSTERS;
  
 -      switch(activeweapon)
 +      switch(activeweapon) // WEAPONTODO
        {
                case WEP_TUBA: // no aim
                case WEP_PORTO: // shoots from eye
                case WEP_HOOK: // no trueaim
 -              case WEP_GRENADE_LAUNCHER: // toss curve
 +              case WEP_MORTAR: // toss curve
                        return SHOTTYPE_HITWORLD;
 -              case WEP_NEX:
 -              case WEP_MINSTANEX:
 +              case WEP_VORTEX:
 +              case WEP_VAPORIZER:
                        mv = MOVE_NORMAL;
                        break;
                case WEP_RIFLE:
                                return EnemyHitCheck();
                        }
                        break;
 -              case WEP_ROCKET_LAUNCHER: // projectile has a size!
 +              case WEP_DEVASTATOR: // projectile has a size!
                        mi = '-3 -3 -3';
                        ma = '3 3 3';
                        break;
@@@ -365,7 -365,6 +365,7 @@@ float camera_mode
  const float CAMERA_FREE = 1;
  const float CAMERA_CHASE = 2;
  float reticle_type;
 +string reticle_image;
  string NextFrameCommand;
  void CSQC_SPIDER_HUD();
  void CSQC_RAPTOR_HUD();
@@@ -378,7 -377,7 +378,7 @@@ float pickup_crosshair_time, pickup_cro
  float hit_time, typehit_time;
  float nextsound_hit_time, nextsound_typehit_time;
  float hitindication_crosshair_time, hitindication_crosshair_size;
 -float use_nex_chargepool;
 +float use_vortex_chargepool;
  
  float myhealth, myhealth_prev;
  float myhealth_flash;
@@@ -868,64 -867,50 +868,64 @@@ void CSQC_UpdateView(float w, float h
                R_EndPolygon();
        }
  
 -      // Draw the aiming reticle for weapons that use it
 -      // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use
 -      // It must be a persisted float for fading out to work properly (you let go of the zoom button for
 -      // the view to go back to normal, so reticle_type would become 0 as we fade out)
 -      if(spectatee_status || is_dead || hud != HUD_NORMAL)
 -              reticle_type = 0; // prevent reticle from showing during the respawn zoom effect or for spectators
 -      else if((activeweapon == WEP_NEX || activeweapon == WEP_RIFLE || activeweapon == WEP_MINSTANEX) && (button_zoom || zoomscript_caught))
 -              reticle_type = 2; // nex zoom
 -      else if(button_zoom || zoomscript_caught)
 -              reticle_type = 1; // normal zoom
 -      else if((activeweapon == WEP_NEX) && button_attack2)
 -              reticle_type = 2; // nex zoom
 -
 -      if(reticle_type && autocvar_cl_reticle)
 +      if(autocvar_cl_reticle)
        {
 -              if(autocvar_cl_reticle_stretch)
 +              // Draw the aiming reticle for weapons that use it
 +              // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use
 +              // It must be a persisted float for fading out to work properly (you let go of the zoom button for
 +              // the view to go back to normal, so reticle_type would become 0 as we fade out)
 +              if(spectatee_status || is_dead || hud != HUD_NORMAL)
                {
 -                      reticle_size_x = vid_conwidth;
 -                      reticle_size_y = vid_conheight;
 -                      reticle_pos_x = 0;
 -                      reticle_pos_y = 0;
 +                      // no zoom reticle while dead
 +                      reticle_type = 0;
                }
 -              else
 +              else if(WEP_ACTION(activeweapon, WR_ZOOMRETICLE) && autocvar_cl_reticle_weapon)
                {
 -                      reticle_size_x = max(vid_conwidth, vid_conheight);
 -                      reticle_size_y = max(vid_conwidth, vid_conheight);
 -                      reticle_pos_x = (vid_conwidth - reticle_size_x) / 2;
 -                      reticle_pos_y = (vid_conheight - reticle_size_y) / 2;
 +                      if(reticle_image != "") { reticle_type = 2; }
 +                      else { reticle_type = 0; }
                }
 -
 -              f = current_zoomfraction;
 -              if(zoomscript_caught)
 -                      f = 1;
 -              if(autocvar_cl_reticle_item_normal)
 +              else if(button_zoom || zoomscript_caught)
                {
 -                      if(reticle_type == 1 && f)
 -                              drawpic(reticle_pos, "gfx/reticle_normal", reticle_size, '1 1 1', f * autocvar_cl_reticle_item_normal, DRAWFLAG_NORMAL);
 +                      // normal zoom
 +                      reticle_type = 1;
                }
 -              if(autocvar_cl_reticle_item_nex)
 +
 +              if(reticle_type)
                {
 -                      if(reticle_type == 2 && f)
 -                              drawpic(reticle_pos, "gfx/reticle_nex", reticle_size, '1 1 1', f * autocvar_cl_reticle_item_nex, DRAWFLAG_NORMAL);
 +                      if(autocvar_cl_reticle_stretch)
 +                      {
 +                              reticle_size_x = vid_conwidth;
 +                              reticle_size_y = vid_conheight;
 +                              reticle_pos_x = 0;
 +                              reticle_pos_y = 0;
 +                      }
 +                      else
 +                      {
 +                              reticle_size_x = max(vid_conwidth, vid_conheight);
 +                              reticle_size_y = max(vid_conwidth, vid_conheight);
 +                              reticle_pos_x = (vid_conwidth - reticle_size_x) / 2;
 +                              reticle_pos_y = (vid_conheight - reticle_size_y) / 2;
 +                      }
 +
 +                      if(zoomscript_caught)
 +                              f = 1;
 +                      else 
 +                              f = current_zoomfraction;
 +
 +                      if(f)
 +                      {
 +                              switch(reticle_type)
 +                              {
 +                                      case 1: drawpic(reticle_pos, "gfx/reticle_normal", reticle_size, '1 1 1', f * autocvar_cl_reticle_normal_alpha, DRAWFLAG_NORMAL); break;
 +                                      case 2: drawpic(reticle_pos, reticle_image, reticle_size, '1 1 1', f * autocvar_cl_reticle_weapon_alpha, DRAWFLAG_NORMAL); break;
 +                              }
 +                      }
                }
        }
 +      else
 +      {
 +              if(reticle_type != 0) { reticle_type = 0; }
 +      }
  
  
        // improved polyblend
  
        //else
        {
-               if(gametype == MAPINFO_TYPE_FREEZETAG)
+               if(getstati(STAT_FROZEN))
+                       drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, ((getstatf(STAT_REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * getstatf(STAT_REVIVE_PROGRESS)) + ('0 1 1' * getstatf(STAT_REVIVE_PROGRESS) * -1)) : '0.25 0.90 1'), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+               else if (getstatf(STAT_HEALING_ORB)>time)
+                       drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, Nade_Color(NADE_TYPE_HEAL), autocvar_hud_colorflash_alpha*getstatf(STAT_HEALING_ORB_ALPHA), DRAWFLAG_ADDITIVE);
+               if(!intermission)
+               if(getstatf(STAT_NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death
                {
-                       if(getstati(STAT_FROZEN))
-                               drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
-                       if(getstatf(STAT_REVIVE_PROGRESS))
-                       {
-                               DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
-                               drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
-                       }
+                       DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * getstatf(STAT_NADE_TIMER)) - ('0 1 1' * getstatf(STAT_NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+                       drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
+               }
+               else if(getstatf(STAT_REVIVE_PROGRESS))
+               {
+                       DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+                       drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
                }
  
                if(autocvar_r_letterbox == 0)
                                shottype = SHOTTYPE_HITWORLD;
  
                        vector wcross_color = '0 0 0', wcross_size = '0 0 0';
 -                      string wcross_wep = "", wcross_name;
 +                      string wcross_name = "";
                        float wcross_scale, wcross_blur;
  
 -                      if (autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1))
 +                      if(autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1))
                        {
                                e = get_weaponinfo(switchingweapon);
 -                              if (e && e.netname != "")
 +                              if(e)
                                {
 -                                      wcross_wep = e.netname;
                                        if(autocvar_crosshair_per_weapon)
                                        {
 -                                              wcross_resolution *= cvar(strcat("crosshair_", wcross_wep, "_size"));
 -                                              if (wcross_resolution == 0)
 -                                                      return;
 -                                              wcross_alpha *= cvar(strcat("crosshair_", wcross_wep, "_alpha"));
 -                                              if (wcross_alpha == 0)
 -                                                      return;
 -
 -                                              wcross_style = cvar_string(strcat("crosshair_", wcross_wep));
 -                                              if(wcross_style == "" || wcross_style == "0")
 -                                                      wcross_style = wcross_wep;
 +                                              // WEAPONTODO: access these through some general settings (with non-balance config settings)
 +                                              //wcross_resolution *= cvar(strcat("crosshair_", wcross_wep, "_size"));
 +                                              //if (wcross_resolution == 0)
 +                                                      //return;
 +
 +                                              //wcross_style = cvar_string(strcat("crosshair_", wcross_wep));
 +                                              wcross_resolution *= e.w_crosshair_size;
 +                                              wcross_name = e.w_crosshair;
                                        }
                                }
                        }
  
 -                      //printf("crosshair style: %s\n", wcross_style);
 -                      wcross_name = strcat("gfx/crosshair", wcross_style);
 +                      if(wcross_name == "")
 +                              wcross_name = strcat("gfx/crosshair", wcross_style);
  
                        // MAIN CROSSHAIR COLOR DECISION
                        switch(autocvar_crosshair_color_special)
                        {
                                case 1: // crosshair_color_per_weapon
                                {
 -                                      if(wcross_wep != "")
 +                                      if(e)
                                        {
 -                                              wcross_color = stov(cvar_string(sprintf("crosshair_%s_color", wcross_wep)));
 +                                              wcross_color = e.wpcolor;
                                                break;
                                        }
                                        else { goto normalcolor; }
                                        weapon_clipload = getstati(STAT_WEAPON_CLIPLOAD);
                                        weapon_clipsize = getstati(STAT_WEAPON_CLIPSIZE);
  
 -                                      float nex_charge, nex_chargepool;
 -                                      nex_charge = getstatf(STAT_NEX_CHARGE);
 -                                      nex_chargepool = getstatf(STAT_NEX_CHARGEPOOL);
 +                                      float vortex_charge, vortex_chargepool;
 +                                      vortex_charge = getstatf(STAT_VORTEX_CHARGE);
 +                                      vortex_chargepool = getstatf(STAT_VORTEX_CHARGEPOOL);
  
 -                                      if(nex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
 -                                              nex_charge_movingavg = nex_charge;
 +                                      if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
 +                                              vortex_charge_movingavg = vortex_charge;
  
  
                                        // handle the values
 -                                      if (autocvar_crosshair_ring && activeweapon == WEP_NEX && nex_charge && autocvar_crosshair_ring_nex) // ring around crosshair representing velocity-dependent damage for the nex
 +                                      if (autocvar_crosshair_ring && activeweapon == WEP_VORTEX && vortex_charge && autocvar_crosshair_ring_vortex) // ring around crosshair representing velocity-dependent damage for the vortex
                                        {
 -                                              if (nex_chargepool || use_nex_chargepool) {
 -                                                      use_nex_chargepool = 1;
 -                                                      ring_inner_value = nex_chargepool;
 +                                              if (vortex_chargepool || use_vortex_chargepool) {
 +                                                      use_vortex_chargepool = 1;
 +                                                      ring_inner_value = vortex_chargepool;
                                                } else {
 -                                                      nex_charge_movingavg = (1 - autocvar_crosshair_ring_nex_currentcharge_movingavg_rate) * nex_charge_movingavg + autocvar_crosshair_ring_nex_currentcharge_movingavg_rate * nex_charge;
 -                                                      ring_inner_value = bound(0, autocvar_crosshair_ring_nex_currentcharge_scale * (nex_charge - nex_charge_movingavg), 1);
 +                                                      vortex_charge_movingavg = (1 - autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate) * vortex_charge_movingavg + autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate * vortex_charge;
 +                                                      ring_inner_value = bound(0, autocvar_crosshair_ring_vortex_currentcharge_scale * (vortex_charge - vortex_charge_movingavg), 1);
                                                }
  
 -                                              ring_inner_alpha = autocvar_crosshair_ring_nex_inner_alpha;
 -                                              ring_inner_rgb = eX * autocvar_crosshair_ring_nex_inner_color_red + eY * autocvar_crosshair_ring_nex_inner_color_green + eZ * autocvar_crosshair_ring_nex_inner_color_blue;
 +                                              ring_inner_alpha = autocvar_crosshair_ring_vortex_inner_alpha;
 +                                              ring_inner_rgb = eX * autocvar_crosshair_ring_vortex_inner_color_red + eY * autocvar_crosshair_ring_vortex_inner_color_green + eZ * autocvar_crosshair_ring_vortex_inner_color_blue;
                                                ring_inner_image = "gfx/crosshair_ring_inner.tga";
  
                                                // draw the outer ring to show the current charge of the weapon
 -                                              ring_value = nex_charge;
 -                                              ring_alpha = autocvar_crosshair_ring_nex_alpha;
 +                                              ring_value = vortex_charge;
 +                                              ring_alpha = autocvar_crosshair_ring_vortex_alpha;
                                                ring_rgb = wcross_color;
                                                ring_image = "gfx/crosshair_ring_nexgun.tga";
                                        }
index 053499a985606fd7c2d411107953e29b3a08350e,28ef157e50d74bfde213a46b1c62d47b24adc0db..e33ea8081afba7819a2ce59ba86bf3619d74a00b
@@@ -62,9 -62,8 +62,9 @@@ float autocvar_cl_particles_quality
  float autocvar_cl_projectiles_sloppy;
  float autocvar_cl_readpicture_force;
  var float autocvar_cl_reticle = 1;
 -float autocvar_cl_reticle_item_nex;
 -float autocvar_cl_reticle_item_normal;
 +var float autocvar_cl_reticle_normal_alpha = 1;
 +var float autocvar_cl_reticle_weapon = 1;
 +var float autocvar_cl_reticle_weapon_alpha = 1;
  float autocvar_cl_reticle_stretch;
  float autocvar_cl_spawn_event_particles;
  var float autocvar_cl_spawn_event_sound = 1;
@@@ -126,14 -125,14 +126,14 @@@ float autocvar_crosshair_ring_minelayer
  float autocvar_crosshair_ring_minelayer_alpha;
  float autocvar_crosshair_ring_hagar;
  float autocvar_crosshair_ring_hagar_alpha;
 -float autocvar_crosshair_ring_nex;
 -float autocvar_crosshair_ring_nex_alpha;
 -float autocvar_crosshair_ring_nex_currentcharge_movingavg_rate;
 -float autocvar_crosshair_ring_nex_currentcharge_scale;
 -float autocvar_crosshair_ring_nex_inner_alpha;
 -float autocvar_crosshair_ring_nex_inner_color_blue;
 -float autocvar_crosshair_ring_nex_inner_color_green;
 -float autocvar_crosshair_ring_nex_inner_color_red;
 +float autocvar_crosshair_ring_vortex;
 +float autocvar_crosshair_ring_vortex_alpha;
 +float autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate;
 +float autocvar_crosshair_ring_vortex_currentcharge_scale;
 +float autocvar_crosshair_ring_vortex_inner_alpha;
 +float autocvar_crosshair_ring_vortex_inner_color_blue;
 +float autocvar_crosshair_ring_vortex_inner_color_green;
 +float autocvar_crosshair_ring_vortex_inner_color_red;
  float autocvar_crosshair_ring_size;
  float autocvar_crosshair_ring_reload;
  float autocvar_crosshair_ring_reload_alpha;
@@@ -292,6 -291,8 +292,8 @@@ float autocvar_hud_panel_powerups_baral
  float autocvar_hud_panel_powerups_flip;
  float autocvar_hud_panel_powerups_iconalign;
  float autocvar_hud_panel_powerups_progressbar;
+ float autocvar_hud_panel_buffs;
+ //float autocvar_hud_panel_buffs_iconalign;
  string autocvar_hud_panel_powerups_progressbar_shield;
  string autocvar_hud_panel_powerups_progressbar_strength;
  string autocvar_hud_panel_powerups_progressbar_superweapons;
@@@ -323,7 -324,6 +325,7 @@@ float autocvar_hud_panel_weapons_ammo
  float autocvar_hud_panel_weapons_ammo_alpha;
  string autocvar_hud_panel_weapons_ammo_color;
  float autocvar_hud_panel_weapons_ammo_full_cells;
 +float autocvar_hud_panel_weapons_ammo_full_plasma;
  float autocvar_hud_panel_weapons_ammo_full_fuel;
  float autocvar_hud_panel_weapons_ammo_full_nails;
  float autocvar_hud_panel_weapons_ammo_full_rockets;
@@@ -443,3 -443,4 +445,4 @@@ string autocvar__cl_playermodel
  float autocvar_cl_deathglow;
  float autocvar_developer_csqcentities;
  float autocvar_g_jetpack_attenuation;
+ float autocvar_cl_nade_timer;
diff --combined qcsrc/client/hud.qc
index acd22fef82d35f29e28a75dbc3067f14479602d9,ab61eb3c3eb4c071c549467e1b9b556deefadc76..701990d6b64a597dbc7a2c8fd6f3ced293fff867
@@@ -408,6 -408,42 +408,6 @@@ float weaponorder_cmp(float i, float j
        return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string)
  }
  
 -float GetAmmoStat(float i)
 -{
 -      switch(i)
 -      {
 -              case 0: return STAT_SHELLS;
 -              case 1: return STAT_NAILS;
 -              case 2: return STAT_ROCKETS;
 -              case 3: return STAT_CELLS;
 -              case 4: return STAT_FUEL;
 -              default: return -1;
 -      }
 -}
 -
 -float GetAmmoTypeForWep(float i)
 -{
 -      switch(i)
 -      {
 -              case WEP_SHOTGUN: return 0;
 -              case WEP_UZI: return 1;
 -              case WEP_GRENADE_LAUNCHER: return 2;
 -              case WEP_MINE_LAYER: return 2;
 -              case WEP_ELECTRO: return 3;
 -              case WEP_CRYLINK: return 3;
 -              case WEP_HLAC: return 3;
 -              case WEP_MINSTANEX: return 3;
 -              case WEP_NEX: return 3;
 -              case WEP_RIFLE: return 1;
 -              case WEP_HAGAR: return 2;
 -              case WEP_ROCKET_LAUNCHER: return 2;
 -              case WEP_SEEKER: return 2;
 -              case WEP_FIREBALL: return 4;
 -              case WEP_HOOK: return 3;
 -              default: return -1;
 -      }
 -}
 -
  void HUD_Weapons(void)
  {
        // declarations
        float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0);
        float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0);
  
 -      float ammo_type, ammo_full;
 +      float ammo_full;
        float barsize_x = 0, barsize_y = 0, baroffset_x = 0, baroffset_y = 0;
        vector ammo_color = '1 0 1';
        float ammo_alpha = 1;
                if(weapons_stat & WepSet_FromWeapon(self.weapon))
                {
                        // draw the weapon image
 -                      drawpic_aspect_skin(weapon_pos, strcat("weapon", self.netname), weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +                      drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
  
                        // draw weapon label string
                        switch(autocvar_hud_panel_weapons_label)
                                        break;
  
                                case 3: // weapon name
 -                                      drawstring(weapon_pos, self.netname, '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
 +                                      drawstring(weapon_pos, strtolower(self.message), '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
                                        break;
  
                                default: // nothing
                        }
  
                        // draw ammo status bar
 -                      if(autocvar_hud_panel_weapons_ammo && self.weapon != WEP_TUBA && self.weapon != WEP_LASER && self.weapon != WEP_PORTO)
 +                      if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none))
                        {
 -                              a = 0;
 -                              ammo_type = GetAmmoTypeForWep(self.weapon);
 -                              if(ammo_type != -1)
 -                                      a = getstati(GetAmmoStat(ammo_type)); // how much ammo do we have?
 +                              a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have?
  
                                if(a > 0)
                                {
 -                                      switch(ammo_type) {
 -                                              case 0: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break;
 -                                              case 1: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break;
 -                                              case 2: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
 -                                              case 3: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break;
 -                                              case 4: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break;
 +                                      switch(self.ammo_field)
 +                                      {
 +                                              case ammo_shells:  ammo_full = autocvar_hud_panel_weapons_ammo_full_shells;  break;
 +                                              case ammo_nails:   ammo_full = autocvar_hud_panel_weapons_ammo_full_nails;   break;
 +                                              case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
 +                                              case ammo_cells:   ammo_full = autocvar_hud_panel_weapons_ammo_full_cells;   break;
 +                                              case ammo_plasma:  ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma;  break;
 +                                              case ammo_fuel:    ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel;    break;
                                                default: ammo_full = 60;
                                        }
  
                                                weapon_pos_x + baroffset_x,
                                                weapon_pos_y + baroffset_y,
                                                barsize_x * bound(0, a/ammo_full, 1),
 -                                              barsize_y);
 -                                      drawpic_aspect_skin(weapon_pos, "weapon_ammo", weapon_size, ammo_color, ammo_alpha, DRAWFLAG_NORMAL);
 +                                              barsize_y
 +                                      );
 +
 +                                      drawpic_aspect_skin(
 +                                              weapon_pos,
 +                                              "weapon_ammo",
 +                                              weapon_size,
 +                                              ammo_color,
 +                                              ammo_alpha,
 +                                              DRAWFLAG_NORMAL
 +                                      );
 +
                                        drawresetcliparea();
                                }
                        }
                }
                else // draw a "ghost weapon icon" if you don't have the weapon
                {
 -                      drawpic_aspect_skin(weapon_pos, strcat("weapon", self.netname), weapon_size, '0 0 0', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
 +                      drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '0 0 0', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
                }
  
                // draw the complain message
  }
  
  // Ammo (#1)
 -//
 -// TODO: macro
 -float GetAmmoItemCode(float i)
 -{
 -      switch(i)
 -      {
 -              case 0: return IT_SHELLS;
 -              case 1: return IT_NAILS;
 -              case 2: return IT_ROCKETS;
 -              case 3: return IT_CELLS;
 -              case 4: return IT_FUEL;
 -              default: return -1;
 -      }
 -}
 -
 -string GetAmmoPicture(float i)
 -{
 -      switch(i)
 -      {
 -              case 0: return "ammo_shells";
 -              case 1: return "ammo_bullets";
 -              case 2: return "ammo_rockets";
 -              case 3: return "ammo_cells";
 -              case 4: return "ammo_fuel";
 -              default: return "";
 -      }
 -}
 -
+ void DrawNadeScoreBar(vector myPos, vector mySize, vector color)
+ {
+       
+       HUD_Panel_DrawProgressBar(
+               myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize_x, 
+               mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize_x, 
+               autocvar_hud_panel_ammo_progressbar_name, 
+               getstatf(STAT_NADE_BONUS_SCORE), 0, 0, color, 
+               autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ void DrawAmmoNades(vector myPos, vector mySize, float draw_expanding, float expand_time)
+ {
+       float theAlpha = 1, a, b;
+       vector nade_color, picpos, numpos;
+       
+       nade_color = Nade_Color(getstati(STAT_NADE_BONUS_TYPE));
+       
+       a = getstatf(STAT_NADE_BONUS);
+       b = getstatf(STAT_NADE_BONUS_SCORE);
+       
+       if(autocvar_hud_panel_ammo_iconalign)
+       {
+               numpos = myPos;
+               picpos = myPos + eX * 2 * mySize_y;
+       }
+       else
+       {
+               numpos = myPos + eX * mySize_y;
+               picpos = myPos;
+       }
+       DrawNadeScoreBar(myPos, mySize, nade_color);
+       if(b > 0 || a > 0)
+       {
+               if(autocvar_hud_panel_ammo_text)
+                       drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+               
+               if(draw_expanding)
+                       drawpic_aspect_skin_expanding(picpos, "nade_nbg", '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, expand_time);
+                       
+               drawpic_aspect_skin(picpos, "nade_bg" , '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+               drawpic_aspect_skin(picpos, "nade_nbg" , '1 1 0' * mySize_y, nade_color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+       }
+ }
 -void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_selected, float infinite_ammo)
 +void DrawAmmoItem(vector myPos, vector mySize, .float ammotype, float currently_selected, float infinite_ammo)
  {
 -      float a;
 -      if(autocvar__hud_configure)
 +      float a = 0;
 +      if(ammotype != ammo_none)
        {
 -              currently_selected = (itemcode == 2); //rockets always selected
 -              a = 31 + mod(itemcode*93, 128);
 +              if(autocvar__hud_configure)
 +              {
 +                      currently_selected = (ammotype == ammo_rockets); //rockets always selected
 +                      a = 60;
 +              }
 +              else
 +              {
 +                      // how much ammo do we have of this ammotype?
 +                      a = getstati(GetAmmoStat(ammotype));
 +              }
        }
        else
 -              a = getstati(GetAmmoStat(itemcode)); // how much ammo do we have of type itemcode?
 +      {
 +              #if 0
 +              infinite_ammo = TRUE;
 +              #else
 +              return; // just don't draw infinite ammo at all.
 +              #endif
 +      }
  
        vector color;
        if(infinite_ammo)
                picpos = myPos;
        }
  
 -      if (currently_selected)
 +      if(currently_selected)
                drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
  
      if(a > 0 && autocvar_hud_panel_ammo_progressbar)
              drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize_x + eY * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL);
      }
        if(a > 0 || infinite_ammo)
 -              drawpic_aspect_skin(picpos, GetAmmoPicture(itemcode), '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
 +              drawpic_aspect_skin(picpos, GetAmmoPicture(ammotype), '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
        else // "ghost" ammo icon
 -              drawpic_aspect_skin(picpos, GetAmmoPicture(itemcode), '1 1 0' * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL);
 +              drawpic_aspect_skin(picpos, GetAmmoPicture(ammotype), '1 1 0' * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL);
  }
  
+ float nade_prevstatus;
+ float nade_prevframe;
+ float nade_statuschange_time;
  void HUD_Ammo(void)
  {
        if(hud != HUD_NORMAL) return;
                mySize -= '2 2 0' * panel_bg_padding;
        }
  
-       const float AMMO_COUNT = 5;
        float rows = 0, columns, row, column;
+       float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE);
+       float draw_nades = (nade_cnt > 0 || nade_score > 0), nade_statuschange_elapsedtime;
+       float total_ammo_count;
        vector ammo_size;
-       if(autocvar_hud_panel_ammo_onlycurrent)
-               ammo_size = mySize;
 -      float AMMO_COUNT = 4;
+       if (autocvar_hud_panel_ammo_onlycurrent)
+               total_ammo_count = 1;
        else
+               total_ammo_count = AMMO_COUNT - 1; // fuel
+       if(draw_nades)
        {
-               rows = mySize_y/mySize_x;
-               rows = bound(1, floor((sqrt(4 * (3/1) * rows * AMMO_COUNT + rows * rows) + rows + 0.5) / 2), AMMO_COUNT);
-               //                               ^^^ ammo item aspect goes here
+               ++total_ammo_count;
+               if (nade_cnt != nade_prevframe)
+               {
+                       nade_statuschange_time = time;
+                       nade_prevstatus = nade_prevframe;
+                       nade_prevframe = nade_cnt;
+               }
+       }
+       else
+               nade_prevstatus = nade_prevframe = nade_statuschange_time = 0;
  
-               columns = ceil(AMMO_COUNT/rows);
+       rows = mySize_y/mySize_x;
+       rows = bound(1, floor((sqrt(4 * (3/1) * rows * (total_ammo_count) + rows * rows) + rows + 0.5) / 2), (total_ammo_count));
+       //                               ^^^ ammo item aspect goes here
  
-               ammo_size = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
-       }
+       columns = ceil((total_ammo_count)/rows);
+       ammo_size = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+       
  
        local vector offset = '0 0 0'; // fteqcc sucks
        float newSize;
                ammo_size_y = newSize;
        }
  
 -      float i, stat_items, currently_selected, infinite_ammo;
 -      infinite_ammo = FALSE;
 -
 +      float i;
 +      float infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO);
+       row = column = 0;
 -
 -      if (autocvar_hud_panel_ammo_onlycurrent)
 +      if(autocvar_hud_panel_ammo_onlycurrent)
        {
                if(autocvar__hud_configure)
                {
 -                      DrawAmmoItem(pos, ammo_size, 2, true, FALSE); //show rockets
 +                      DrawAmmoItem(pos, ammo_size, ammo_rockets, TRUE, FALSE);
                }
                else
                {
 -                      stat_items = getstati(STAT_ITEMS, 0, 24);
 -                      if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
 -                              infinite_ammo = TRUE;
 -                      for (i = 0; i < AMMO_COUNT; ++i) {
 -                              currently_selected = stat_items & GetAmmoItemCode(i);
 -                              if (currently_selected)
 -                              {
 -                                      DrawAmmoItem(pos, ammo_size, i, true, infinite_ammo);
 -                                      break;
 -                              }
 -                      }
 -              }
 +                      DrawAmmoItem(
 +                              pos,
 +                              ammo_size,
 +                              (get_weaponinfo(switchweapon)).ammo_field,
 +                              TRUE,
 +                              infinite_ammo
 +                      );
+               ++row;
+               if(row >= rows)
+               {
+                       row = 0;
+                       column = column + 1;
+               }
 +              }
        }
        else
        {
 -              stat_items = getstati(STAT_ITEMS, 0, 24);
 -              if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
 -                      infinite_ammo = TRUE;
 -              for (i = 0; i < AMMO_COUNT; ++i) {
 -                      currently_selected = stat_items & GetAmmoItemCode(i);
 -                      DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo);
 +              .float ammotype;
 +              row = column = 0;
 +              for(i = 0; i < AMMO_COUNT; ++i)
 +              {
 +                      ammotype = GetAmmoFieldFromNum(i);
 +                      DrawAmmoItem(
 +                              pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y),
 +                              ammo_size,
 +                              ammotype,
 +                              ((get_weaponinfo(switchweapon)).ammo_field == ammotype),
 +                              infinite_ammo
 +                      );
 +
                        ++row;
                        if(row >= rows)
                        {
                }
        }
  
+       if (draw_nades)
+       {
+               nade_statuschange_elapsedtime = time - nade_statuschange_time;
+               float f = bound(0, nade_statuschange_elapsedtime*2, 1);
+               DrawAmmoNades(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f);
+       }
        draw_endBoldFont();
  }
  
@@@ -1665,7 -1791,7 +1750,7 @@@ void HUD_Notify(void
                {
                        attacker = sprintf(_("Player %d"), count + 1);
                        victim = sprintf(_("Player %d"), count + 2);
 -                      icon = strcat("weapon", get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).netname);
 +                      icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2;
                        alpha = bound(0, 1.2 - count / entry_count, 1);
                }
                else
@@@ -4333,6 -4459,66 +4418,66 @@@ void HUD_CenterPrint (void
        }
  }
  
+ // Buffs (#18)
+ //
+ void HUD_Buffs(void)
+ {
+       float buffs = getstati(STAT_BUFFS, 0, 24);
+       if(!autocvar__hud_configure)
+       {
+               if(!autocvar_hud_panel_buffs) return;
+               if(spectatee_status == -1) return;
+               if(getstati(STAT_HEALTH) <= 0) return;
+               if(!buffs) return;
+       }
+       else
+       {
+               buffs = Buff_Type_first.items; // force first buff
+       }
+       
+       float b = 0; // counter to tell other functions that we have buffs
+       entity e;
+       string s = "";
+       for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
+       {
+               ++b;
+               string o = strcat(rgb_to_hexcolor(Buff_Color(e.items)), Buff_PrettyName(e.items));
+               if(s == "")
+                       s = o;
+               else
+                       s = strcat(s, " ", o);
+       }
+       HUD_Panel_UpdateCvars();
+       draw_beginBoldFont();
+       vector pos, mySize;
+       pos = panel_pos;
+       mySize = panel_size;
+       HUD_Panel_DrawBg(bound(0, b, 1));
+       if(panel_bg_padding)
+       {
+               pos += '1 1 0' * panel_bg_padding;
+               mySize -= '2 2 0' * panel_bg_padding;
+       }
+       //float panel_ar = mySize_x/mySize_y;
+       //float is_vertical = (panel_ar < 1);
+       //float buff_iconalign = autocvar_hud_panel_buffs_iconalign;
+       vector buff_offset = '0 0 0';
+       
+       for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
+       {
+               //DrawNumIcon(pos + buff_offset, mySize, shield, "shield", is_vertical, buff_iconalign, '1 1 1', 1);
+               drawcolorcodedstring_aspect(pos + buff_offset, s, mySize, panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
+       }
+       draw_endBoldFont();
+ }
  /*
  ==================
  Main HUD system
diff --combined qcsrc/client/progs.src
index 36c20b6585a9e88e63f2b69dda8e5f165a34e1e4,86a8b43395e199d83a1dc1676f5a7d234cf1a27f..90f445c517b4623b70672f9a8ae2f90c09197338
@@@ -8,6 -8,7 +8,7 @@@ sys-post.q
  Defs.qc
  ../dpdefs/keycodes.qc
  ../common/constants.qh
+ ../common/stats.qh
  
  ../warpzonelib/anglestransform.qh
  ../warpzonelib/mathlib.qh
  
  ../common/teams.qh
  ../common/util.qh
+ ../common/nades.qh
+ ../common/buffs.qh
  ../common/test.qh
  ../common/counting.qh
 -../common/items.qh
 -../common/explosion_equation.qh
 +../common/weapons/weapons.qh // TODO
  ../common/mapinfo.qh
  ../common/command/markup.qh
  ../common/command/rpn.qh
@@@ -55,12 -59,11 +58,12 @@@ vehicles/vehicles.q
  ../csqcmodellib/common.qh
  ../csqcmodellib/cl_model.qh
  ../csqcmodellib/cl_player.qh
 -projectile.qh
 +weapons/projectile.qh // TODO
  player_skeleton.qh
  
  sortlist.qc
  miscfunctions.qc
 +../server/t_items.qh
  ../server/t_items.qc
  
  teamradar.qc
@@@ -75,7 -78,7 +78,7 @@@ rubble.q
  hook.qc
  particles.qc
  laser.qc
 -projectile.qc
 +weapons/projectile.qc // TODO
  gibs.qc
  damage.qc
  casings.qc
@@@ -109,12 -112,17 +112,15 @@@ noise.q
  ../common/command/rpn.qc
  ../common/command/generic.qc
  ../common/mapinfo.qc
 -../common/items.qc
 -../server/w_all.qc
 -../common/explosion_equation.qc
 +../common/weapons/weapons.qc // TODO
  ../common/urllib.qc
  command/cl_cmd.qc
  
  ../common/monsters/monsters.qc
  
+ ../common/nades.qc
+ ../common/buffs.qc
  ../warpzonelib/anglestransform.qc
  ../warpzonelib/mathlib.qc
  ../warpzonelib/common.qc
index 24b200d3467d0d3378b50632ed09672a3e6cc402,7ee34672c5caa087b242ef27aad7b8d3e64f8363..c99fc5d51c0a593a2668f6ea22dd8f461b0ac17e
@@@ -217,20 -217,31 +217,20 @@@ float spritelookupblinkvalue(string s
  }
  vector spritelookupcolor(string s, vector def)
  {
 +      if(substring(s, 0, 4) == "wpn-")
 +              return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor);
 +
        switch(s)
        {
                case "keycarrier-friend": return '0 1 0';
 -              case "wpn-laser":         return '1 0.5 0.5';
 -              case "wpn-shotgun":       return '0.5 0.25 0';
 -              case "wpn-uzi":           return '1 1 0';
 -              case "wpn-gl":            return '1 0 0';
 -              case "wpn-electro":       return '0 0.5 1';
 -              case "wpn-crylink":       return '1 0.5 1';
 -              case "wpn-nex":           return '0.5 1 1';
 -              case "wpn-hagar":         return '1 1 0.5';
 -              case "wpn-rl":            return '1 1 0';
 -              case "wpn-porto":         return '0.5 0.5 0.5';
 -              case "wpn-minstanex":     return '0.5 1 1';
 -              case "wpn-hookgun":       return '0 0.5 0';
 -              case "wpn-fireball":      return '1 0.5 0';
 -              case "wpn-hlac":          return '0 1 0';
 -              case "wpn-campingrifle":  return '0.5 1 0';
 -              case "wpn-minelayer":     return '0.75 1 0';
                default:                  return def;
        }
  }
  string spritelookuptext(string s)
  {
-       if(substring(s, 0, 4) == "wpn-")
-               return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message);
++      if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); }
+       if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
 +
        switch(s)
        {
                case "as-push": return _("Push");
                case "nb-ball": return _("Ball");
                case "ka-ball": return _("Ball");
                case "ka-ballcarrier": return _("Ball carrier");
 -              case "wpn-laser": return _("Laser");
 -              case "wpn-shotgun": return _("Shotgun");
 -              case "wpn-uzi": return _("Machine Gun");
 -              case "wpn-gl": return _("Mortar");
 -              case "wpn-electro": return _("Electro");
 -              case "wpn-crylink": return _("Crylink");
 -              case "wpn-nex": return _("Nex");
 -              case "wpn-hagar": return _("Hagar");
 -              case "wpn-rl": return _("Rocket Launcher");
 -              case "wpn-porto": return _("Port-O-Launch");
 -              case "wpn-minstanex": return _("Minstanex");
 -              case "wpn-hookgun": return _("Hook");
 -              case "wpn-fireball": return _("Fireball");
 -              case "wpn-hlac": return _("HLAC");
 -              case "wpn-campingrifle": return _("Rifle");
 -              case "wpn-minelayer": return _("Mine Layer");
                case "dom-neut": return _("Control point");
                case "dom-red": return _("Control point");
                case "dom-blue": return _("Control point");
                case "item-shield": return _("Shield");
                case "item-fuelregen": return _("Fuel regen");
                case "item-jetpack": return _("Jet Pack");
-               case "freezetag_frozen": return _("Frozen!");
+               case "frozen": return _("Frozen!");
                case "tagged-target": return _("Tagged");
                case "vehicle": return _("Vehicle");
                default: return s;
index c98ab7bf48f57669738a50b813aebea6eeec7fc6,0000000000000000000000000000000000000000..58bd11ee43c6bdbf78cdb9c13330b841a6243ea9
mode 100644,000000..100644
--- /dev/null
@@@ -1,538 -1,0 +1,505 @@@
-                       case PROJECTILE_NADE_RED_BURN:
-                       case PROJECTILE_NADE_RED:
-                       case PROJECTILE_NADE_BLUE_BURN:
-                       case PROJECTILE_NADE_BLUE:
-                       case PROJECTILE_NADE_YELLOW_BURN:
-                       case PROJECTILE_NADE_YELLOW:
-                       case PROJECTILE_NADE_PINK_BURN:
-                       case PROJECTILE_NADE_PINK:
-                       case PROJECTILE_NADE_BURN:
-                       case PROJECTILE_NADE:
-                               rot = self.avelocity;
-                               break;
 +.vector iorigin1, iorigin2;
 +.float spawntime;
 +.vector trail_oldorigin;
 +.float trail_oldtime;
 +.float fade_time, fade_rate;
 +
 +void SUB_Stop()
 +{
 +      self.move_velocity = self.move_avelocity = '0 0 0';
 +      self.move_movetype = MOVETYPE_NONE;
 +}
 +
 +.float alphamod;
 +.float count; // set if clientside projectile
 +.float cnt; // sound index
 +.float gravity;
 +.float snd_looping;
 +.float silent;
 +
 +void Projectile_ResetTrail(vector to)
 +{
 +      self.trail_oldorigin = to;
 +      self.trail_oldtime = time;
 +}
 +
 +void Projectile_DrawTrail(vector to)
 +{
 +      vector from;
 +      float t0;
 +
 +      from = self.trail_oldorigin;
 +      t0 = self.trail_oldtime;
 +      self.trail_oldorigin = to;
 +      self.trail_oldtime = time;
 +
 +      // force the effect even for stationary firemine
 +      if(self.cnt == PROJECTILE_FIREMINE)
 +              if(from == to)
 +                      from_z += 1;
 +
 +      if (self.traileffect)
 +      {
 +              particles_alphamin = particles_alphamax = particles_fade = sqrt(self.alpha);
 +              boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL);
 +      }
 +}
 +
 +void Projectile_Draw()
 +{
 +      vector rot;
 +      vector trailorigin;
 +      float f;
 +      float drawn;
 +      float t;
 +      float a;
 +
 +      f = self.move_flags;
 +
 +      if(self.count & 0x80)
 +      {
 +              //self.move_flags &= ~FL_ONGROUND;
 +              if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY)
 +                      Movetype_Physics_NoMatchServer();
 +                      // the trivial movetypes do not have to match the
 +                      // server's ticrate as they are ticrate independent
 +                      // NOTE: this assumption is only true if MOVETYPE_FLY
 +                      // projectiles detonate on impact. If they continue
 +                      // moving, we might still be ticrate dependent.
 +              else
 +                      Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
 +              if(!(self.move_flags & FL_ONGROUND))
 +                      if(self.velocity != '0 0 0')
 +                              self.move_angles = self.angles = vectoangles(self.velocity);
 +      }
 +      else
 +      {
 +              InterpolateOrigin_Do();
 +      }
 +
 +      if(self.count & 0x80)
 +      {
 +              drawn = (time >= self.spawntime - 0.02);
 +              t = max(time, self.spawntime);
 +      }
 +      else
 +      {
 +              drawn = (self.iflags & IFLAG_VALID);
 +              t = time;
 +      }
 +
 +      if(!(f & FL_ONGROUND))
 +      {
 +              rot = '0 0 0';
 +              switch(self.cnt)
 +              {
 +                      /*
 +                      case PROJECTILE_GRENADE:
 +                              rot = '-2000 0 0'; // forward
 +                              break;
 +                      */
 +                      case PROJECTILE_GRENADE_BOUNCING:
 +                              rot = '0 -1000 0'; // sideways
 +                              break;
-           case PROJECTILE_NADE_RED_BURN:
-               case PROJECTILE_NADE_RED:
-               case PROJECTILE_NADE_BLUE_BURN:
-               case PROJECTILE_NADE_BLUE:
-               case PROJECTILE_NADE_YELLOW_BURN:
-               case PROJECTILE_NADE_YELLOW:
-               case PROJECTILE_NADE_PINK_BURN:
-               case PROJECTILE_NADE_PINK:
-               case PROJECTILE_NADE_BURN:
-               case PROJECTILE_NADE:
-                       trailorigin += v_up * 4;
-                       break;
 +                      case PROJECTILE_HOOKBOMB:
 +                              rot = '1000 0 0'; // forward
 +                              break;
 +                      default:
 +                              break;
 +              }
++
++              if(Nade_IDFromProjectile(self.cnt) != 0)
++                      rot = self.avelocity;
++
 +              self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime)));
 +      }
 +
 +      vector ang;
 +      ang = self.angles;
 +      ang_x = -ang_x;
 +      makevectors(ang);
 +
 +      a = 1 - (time - self.fade_time) * self.fade_rate;
 +      self.alpha = bound(0, self.alphamod * a, 1);
 +      if(self.alpha <= 0)
 +              drawn = 0;
 +      self.renderflags = 0;
 +
 +      trailorigin = self.origin;
 +      switch(self.cnt)
 +      {
-                       case PROJECTILE_NADE_RED: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red"); break;
-                       case PROJECTILE_NADE_RED_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red_burn"); break;
-                       case PROJECTILE_NADE_BLUE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue"); break;
-                       case PROJECTILE_NADE_BLUE_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue_burn"); break;
-                       case PROJECTILE_NADE_YELLOW: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_yellow"); break;
-                       case PROJECTILE_NADE_YELLOW_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_yellow_burn"); break;
-                       case PROJECTILE_NADE_PINK: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_pink"); break;
-                       case PROJECTILE_NADE_PINK_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_pink_burn"); break;
-                       case PROJECTILE_NADE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade"); break;
-                       case PROJECTILE_NADE_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_burn"); break;
 +              case PROJECTILE_GRENADE:
 +              case PROJECTILE_GRENADE_BOUNCING:
 +                      trailorigin += v_right * 1 + v_forward * -10;
 +                      break;
 +              default:
 +                      break;
 +      }
++
++      if(Nade_IDFromProjectile(self.cnt) != 0)
++              trailorigin += v_up * 4;
++
 +      if(drawn)
 +              Projectile_DrawTrail(trailorigin);
 +      else
 +              Projectile_ResetTrail(trailorigin);
 +
 +      self.drawmask = 0;
 +
 +      if(!drawn)
 +              return;
 +
 +      switch(self.cnt)
 +      {
 +              // Possibly add dlights here.
 +              default:
 +                      break;
 +      }
 +
 +      self.drawmask = MASK_NORMAL;
 +}
 +
 +void loopsound(entity e, float ch, string samp, float vol, float attn)
 +{
 +      if(self.silent)
 +              return;
 +
 +      sound(e, ch, samp, vol, attn);
 +      e.snd_looping = ch;
 +}
 +
 +void Ent_RemoveProjectile()
 +{
 +      if(self.count & 0x80)
 +      {
 +              tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self);
 +              Projectile_DrawTrail(trace_endpos);
 +      }
 +}
 +
 +void Ent_Projectile()
 +{
 +      float f;
 +
 +      // projectile properties:
 +      //   kind (interpolated, or clientside)
 +      //
 +      //   modelindex
 +      //   origin
 +      //   scale
 +      //   if clientside:
 +      //     velocity
 +      //     gravity
 +      //   soundindex (hardcoded list)
 +      //   effects
 +      //
 +      // projectiles don't send angles, because they always follow the velocity
 +
 +      f = ReadByte();
 +      self.count = (f & 0x80);
 +      self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
 +      self.solid = SOLID_TRIGGER;
 +      //self.effects = EF_NOMODELFLAGS;
 +
 +      // this should make collisions with bmodels more exact, but it leads to
 +      // projectiles no longer being able to lie on a bmodel
 +      self.move_nomonsters = MOVE_WORLDONLY;
 +      if(f & 0x40)
 +              self.move_flags |= FL_ONGROUND;
 +      else
 +              self.move_flags &= ~FL_ONGROUND;
 +
 +      if(!self.move_time)
 +      {
 +              // for some unknown reason, we don't need to care for
 +              // sv_gameplayfix_delayprojectiles here.
 +              self.move_time = time;
 +              self.spawntime = time;
 +      }
 +      else
 +              self.move_time = max(self.move_time, time);
 +
 +      if(!(self.count & 0x80))
 +              InterpolateOrigin_Undo();
 +
 +      if(f & 1)
 +      {
 +              self.origin_x = ReadCoord();
 +              self.origin_y = ReadCoord();
 +              self.origin_z = ReadCoord();
 +              setorigin(self, self.origin);
 +              if(self.count & 0x80)
 +              {
 +                      self.velocity_x = ReadCoord();
 +                      self.velocity_y = ReadCoord();
 +                      self.velocity_z = ReadCoord();
 +                      if(f & 0x10)
 +                              self.gravity = ReadCoord();
 +                      else
 +                              self.gravity = 0; // none
 +                      self.move_origin = self.origin;
 +                      self.move_velocity = self.velocity;
 +              }
 +
 +              if(time == self.spawntime || (self.count & 0x80) || (f & 0x08))
 +              {
 +                      self.trail_oldorigin = self.origin;
 +                      if(!(self.count & 0x80))
 +                              InterpolateOrigin_Reset();
 +              }
 +
 +              if(f & 0x20)
 +              {
 +                      self.fade_time = time + ReadByte() * ticrate;
 +                      self.fade_rate = 1 / (ReadByte() * ticrate);
 +              }
 +              else
 +              {
 +                      self.fade_time = 0;
 +                      self.fade_rate = 0;
 +              }
++
++              self.team = ReadByte() - 1;
 +      }
 +
 +      if(f & 2)
 +      {
 +              self.cnt = ReadByte();
 +
 +              self.silent = (self.cnt & 0x80);
 +              self.cnt = (self.cnt & 0x7F);
 +
 +              self.scale = 1;
 +              self.traileffect = 0;
 +              switch(self.cnt)
 +              {
 +                      case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
 +                      case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
 +                      case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
 +                      case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
 +                      case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
 +                      case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
 +                      case PROJECTILE_BLASTER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
 +                      case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
 +                      case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
 +                      case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
 +                      case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
++                      case PROJECTILE_NAPALM_FOUNTAIN: //self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("torch_small"); break;
 +                      case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough
 +                      case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough
 +                      case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break;
 +                      case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break;
 +                      case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break;
 +
 +                      case PROJECTILE_MAGE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break;
 +                      case PROJECTILE_SHAMBLER_LIGHTNING: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +
 +                      case PROJECTILE_RAPTORBOMB:    setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3");     self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_RAPTORCANNON:  setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
 +
 +                      case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break;
 +                      case PROJECTILE_WAKIROCKET:   setmodel(self, "models/vehicles/rocket01.md3");  self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break;
 +                      case PROJECTILE_WAKICANNON:   setmodel(self, "models/laser.mdl");  self.traileffect = particleeffectnum(""); break;
 +
 +                      case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +
-                       case PROJECTILE_NADE_RED_BURN:
-                       case PROJECTILE_NADE_RED:
-                       case PROJECTILE_NADE_BLUE_BURN:
-                       case PROJECTILE_NADE_BLUE:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.scale = 1.5;
-                               self.avelocity = randomvec() * 720;
-                               break;
 +                      default:
++                              if(Nade_IDFromProjectile(self.cnt) != 0) { setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum(Nade_TrailEffect(self.cnt, self.team)); break; }
 +                              error("Received invalid CSQC projectile, can't work with this!");
 +                              break;
 +              }
 +
 +              self.mins = '0 0 0';
 +              self.maxs = '0 0 0';
 +              self.colormod = '0 0 0';
 +              self.move_touch = SUB_Stop;
 +              self.move_movetype = MOVETYPE_TOSS;
 +              self.alphamod = 1;
 +
 +              switch(self.cnt)
 +              {
 +                      case PROJECTILE_ELECTRO:
 +                              // only new engines support sound moving with object
 +                              loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '0 0 -4';
 +                              self.maxs = '0 0 -4';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
 +                              self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
 +                              break;
 +                      case PROJECTILE_ROCKET:
 +                              loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              break;
 +                      case PROJECTILE_GRENADE:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              break;
-                       case PROJECTILE_NADE_RED_BURN:
-                       case PROJECTILE_NADE_RED:
-                       case PROJECTILE_NADE_BLUE_BURN:
-                       case PROJECTILE_NADE_BLUE:
-                       case PROJECTILE_NADE_YELLOW_BURN:
-                       case PROJECTILE_NADE_YELLOW:
-                       case PROJECTILE_NADE_PINK_BURN:
-                       case PROJECTILE_NADE_PINK:
-                       case PROJECTILE_NADE_BURN:
-                       case PROJECTILE_NADE:
-                               self.mins = '-16 -16 -16';
-                               self.maxs = '16 16 16';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.scale = 1.5;
-                               self.avelocity = randomvec() * 720;
-                               break;
 +                      case PROJECTILE_GRENADE_BOUNCING:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.move_bounce_factor = g_balance_grenadelauncher_bouncefactor;
 +                              self.move_bounce_stopspeed = g_balance_grenadelauncher_bouncestop;
 +                              break;
 +                      case PROJECTILE_SHAMBLER_LIGHTNING:
 +                              self.mins = '-8 -8 -8';
 +                              self.maxs = '8 8 8';
 +                              self.scale = 2.5;
 +                              self.avelocity = randomvec() * 720;
 +                              break;
 +                      case PROJECTILE_MINE:
 +                              self.mins = '-4 -4 -4';
 +                              self.maxs = '4 4 4';
 +                              break;
 +                      case PROJECTILE_PORTO_RED:
 +                              self.colormod = '2 1 1';
 +                              self.alphamod = 0.5;
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_PORTO_BLUE:
 +                              self.colormod = '1 1 2';
 +                              self.alphamod = 0.5;
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_HAGAR_BOUNCING:
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_CRYLINK_BOUNCING:
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
++                      case PROJECTILE_NAPALM_FOUNTAIN:
 +                      case PROJECTILE_FIREBALL:
 +                              loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '-16 -16 -16';
 +                              self.maxs = '16 16 16';
 +                              break;
 +                      case PROJECTILE_FIREMINE:
 +                              loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.mins = '-4 -4 -4';
 +                              self.maxs = '4 4 4';
 +                              break;
 +                      case PROJECTILE_TAG:
 +                              self.mins = '-2 -2 -2';
 +                              self.maxs = '2 2 2';
 +                              break;
 +                      case PROJECTILE_FLAC:
 +                              self.mins = '-2 -2 -2';
 +                              self.maxs = '2 2 2';
 +                              break;
 +                      case PROJECTILE_SEEKER:
 +                              loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '-4 -4 -4';
 +                              self.maxs = '4 4 4';
 +                              break;
 +            case PROJECTILE_RAPTORBOMB:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              break;
 +            case PROJECTILE_RAPTORBOMBLET:
 +                              break;
 +            case PROJECTILE_RAPTORCANNON:
 +                              break;
 +            case PROJECTILE_SPIDERROCKET:
 +                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              break;
 +            case PROJECTILE_WAKIROCKET:
 +                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              break;
 +            /*
 +            case PROJECTILE_WAKICANNON:
 +                              break;
 +                      case PROJECTILE_BUMBLE_GUN:
 +                              // only new engines support sound moving with object
 +                              loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '0 0 -4';
 +                              self.maxs = '0 0 -4';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
 +                              self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
 +                              break;
 +                      */
 +                      default:
 +                              break;
 +              }
++
++              if(Nade_IDFromProjectile(self.cnt) != 0)
++              {
++                      self.mins = '-16 -16 -16';
++                      self.maxs = '16 16 16';
++                      self.colormod = Nade_Color(Nade_IDFromProjectile(self.cnt));
++                      self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
++                      self.move_movetype = MOVETYPE_BOUNCE;
++                      self.move_touch = func_null;
++                      self.scale = 1.5;
++                      self.avelocity = randomvec() * 720;
++                      
++                      if(Nade_IDFromProjectile(self.cnt) == NADE_TYPE_TRANSLOCATE)
++                              self.solid = SOLID_TRIGGER;
++              }
++
 +              setsize(self, self.mins, self.maxs);
 +      }
 +
 +      if(self.gravity)
 +      {
 +              if(self.move_movetype == MOVETYPE_FLY)
 +                      self.move_movetype = MOVETYPE_TOSS;
 +              if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
 +                      self.move_movetype = MOVETYPE_BOUNCE;
 +      }
 +      else
 +      {
 +              if(self.move_movetype == MOVETYPE_TOSS)
 +                      self.move_movetype = MOVETYPE_FLY;
 +              if(self.move_movetype == MOVETYPE_BOUNCE)
 +                      self.move_movetype = MOVETYPE_BOUNCEMISSILE;
 +      }
 +
 +      if(!(self.count & 0x80))
 +              InterpolateOrigin_Note();
 +
 +      self.draw = Projectile_Draw;
 +      self.entremove = Ent_RemoveProjectile;
 +}
 +
 +void Projectile_Precache()
 +{
 +      precache_model("models/ebomb.mdl");
 +      precache_model("models/elaser.mdl");
 +      precache_model("models/grenademodel.md3");
 +      precache_model("models/mine.md3");
 +      precache_model("models/hagarmissile.mdl");
 +      precache_model("models/hlac_bullet.md3");
 +      precache_model("models/laser.mdl");
 +      precache_model("models/plasmatrail.mdl");
 +      precache_model("models/rocket.md3");
 +      precache_model("models/tagrocket.md3");
 +      precache_model("models/tracer.mdl");
++      precache_model("models/sphere/sphere.md3");
 +
 +      precache_model("models/weapons/v_ok_grenade.md3");
 +
 +      precache_sound("weapons/electro_fly.wav");
 +      precache_sound("weapons/rocket_fly.wav");
 +      precache_sound("weapons/fireball_fly.wav");
 +      precache_sound("weapons/fireball_fly2.wav");
 +      precache_sound("weapons/tag_rocket_fly.wav");
 +
 +}
index ee8a228ed9a7905772e275fa37064056c04e2e54,fb6d781c0c1e16e10e5cfb4fa3a591537b311050..2d0c83a498584a4f247a5ff76c893ac9f3c73e38
@@@ -30,18 -30,17 +30,18 @@@ const float AS_FLOAT = 8
  
  const float TE_CSQC_PICTURE = 100;
  const float TE_CSQC_RACE = 101;
 -const float TE_CSQC_NEXGUNBEAMPARTICLE = 103;
 -const float TE_CSQC_LIGHTNINGARC = 104;
 +const float TE_CSQC_VORTEXBEAMPARTICLE = 103;
 +const float TE_CSQC_ARC = 104;
  const float TE_CSQC_TEAMNAGGER = 105;
  const float TE_CSQC_PINGPLREPORT = 106;
  const float TE_CSQC_TARGET_MUSIC = 107;
  const float TE_CSQC_WEAPONCOMPLAIN = 108;
 -const float TE_CSQC_NEX_SCOPE = 109;
 +const float TE_CSQC_VORTEX_SCOPE = 109;
  const float TE_CSQC_MINELAYER_MAXMINES = 110;
  const float TE_CSQC_HAGAR_MAXROCKETS = 111;
  const float TE_CSQC_VEHICLESETUP = 112;
  const float TE_CSQC_SVNOTICE = 113;
 +const float TE_CSQC_SHOCKWAVEPARTICLE = 114;
  
  const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
  const float RACE_NET_CHECKPOINT_CLEAR = 1;
@@@ -86,7 -85,8 +86,7 @@@ const float ENT_CLIENT_WARPZONE = 24
  const float ENT_CLIENT_WARPZONE_CAMERA = 25;
  const float ENT_CLIENT_TRIGGER_MUSIC = 26;
  const float ENT_CLIENT_HOOK = 27;
 -const float ENT_CLIENT_LGBEAM = 28;
 -const float ENT_CLIENT_GAUNTLET = 29;
 +const float ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers
  const float ENT_CLIENT_ACCURACY = 30;
  const float ENT_CLIENT_SHOWNAMES = 31;
  const float ENT_CLIENT_WARPZONE_TELEPORTED = 32;
@@@ -101,6 -101,8 +101,8 @@@ const float ENT_CLIENT_TURRET = 40
  const float ENT_CLIENT_AUXILIARYXHAIR = 50;
  const float ENT_CLIENT_VEHICLE = 60;
  
+ const float ENT_CLIENT_HEALING_ORB = 80;
  const float SPRITERULE_DEFAULT = 0;
  const float SPRITERULE_TEAMPLAY = 1;
  
@@@ -139,80 -141,6 +141,6 @@@ const float CVAR_READONLY = 4
  ///////////////////////////
  // csqc communication stuff
  
- const float STAT_KH_KEYS = 32;
- const float STAT_CTF_STATE = 33;
- const float STAT_WEAPONS = 35;
- const float STAT_SWITCHWEAPON = 36;
- const float STAT_GAMESTARTTIME = 37;
- const float STAT_STRENGTH_FINISHED = 38;
- const float STAT_INVINCIBLE_FINISHED = 39;
- const float STAT_PRESSED_KEYS = 42;
- const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
- const float STAT_FUEL = 44;
- const float STAT_NB_METERSTART = 45;
- const float STAT_SHOTORG = 46; // compressShotOrigin
- const float STAT_LEADLIMIT = 47;
- const float STAT_WEAPON_CLIPLOAD = 48;
- const float STAT_WEAPON_CLIPSIZE = 49;
- const float STAT_VORTEX_CHARGE = 50;
- const float STAT_LAST_PICKUP = 51;
- const float STAT_HUD = 52;
- const float STAT_VORTEX_CHARGEPOOL = 53;
- const float STAT_HIT_TIME = 54;
- const float STAT_TYPEHIT_TIME = 55;
- const float STAT_LAYED_MINES = 56;
- const float STAT_HAGAR_LOAD = 57;
- const float STAT_SWITCHINGWEAPON = 58;
- const float STAT_SUPERWEAPONS_FINISHED = 59;
- const float STAT_VEHICLESTAT_HEALTH = 60;
- const float STAT_VEHICLESTAT_SHIELD = 61;
- const float STAT_VEHICLESTAT_ENERGY = 62;
- const float STAT_VEHICLESTAT_AMMO1 = 63;
- const float STAT_VEHICLESTAT_RELOAD1 = 64;
- const float STAT_VEHICLESTAT_AMMO2 = 65;
- const float STAT_VEHICLESTAT_RELOAD2 = 66;
- const float STAT_SECRETS_TOTAL = 70;
- const float STAT_SECRETS_FOUND = 71;
- const float STAT_RESPAWN_TIME = 72;
- const float STAT_ROUNDSTARTTIME = 73;
- const float STAT_WEAPONS2 = 74;
- const float STAT_WEAPONS3 = 75;
- const float STAT_MONSTERS_TOTAL = 76;
- const float STAT_MONSTERS_KILLED = 77;
- const float STAT_PLASMA = 80;
- // mod stats (1xx)
- const float STAT_REDALIVE = 100;
- const float STAT_BLUEALIVE = 101;
- const float STAT_YELLOWALIVE = 102;
- const float STAT_PINKALIVE = 103;
- // freeze tag
- const float STAT_FROZEN = 104;
- const float STAT_REVIVE_PROGRESS = 105;
- // domination
- const float STAT_DOM_TOTAL_PPS = 100;
- const float STAT_DOM_PPS_RED = 101;
- const float STAT_DOM_PPS_BLUE = 102;
- const float STAT_DOM_PPS_PINK = 103;
- const float STAT_DOM_PPS_YELLOW = 104;
- //const float STAT_SPIDERBOT_AIM 53 // compressShotOrigin
- //const float STAT_SPIDERBOT_TARGET 54 // compressShotOrigin
- // see DP source, quakedef.h
- const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
- const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
- const float STAT_MOVEVARS_MAXSPEED = 244;
- const float STAT_MOVEVARS_AIRACCEL_QW = 254;
  const float CTF_STATE_ATTACK = 1;
  const float CTF_STATE_DEFEND = 2;
  const float CTF_STATE_COMMANDER = 3;
@@@ -232,7 -160,7 +160,7 @@@ const vector eZ = '0 0 1'
  
  // moved that here so the client knows the max.
  // # of maps, I'll use arrays for them :P
- #define MAPVOTE_COUNT 10
+ #define MAPVOTE_COUNT 30
  
  /**
   * Lower scores are better (e.g. suicides)
  #define SP_SCORE 3
  // game mode specific indices are not in common/, but in server/scores_rules.qc!
  
- #ifdef COMPAT_XON010_CHANNELS
- const float CH_INFO = 0; // only on world and csqc
- const float CH_TRIGGER = 0; // only on players; compat: FALSELY CONTROLLED BY "Info"
- const float CH_WEAPON_A = 1; // only on players and entities
- const float CH_WEAPON_SINGLE = 5; // only on players and entities
- const float CH_VOICE = 2; // only on players
- const float CH_BGM_SINGLE = 2; // only on csqc; compat: FALSELY CONTROLLED BY "Voice"
- const float CH_AMBIENT = 2; // only on csqc; compat: FALSELY CONTROLLED BY "Voice"
- const float CH_TRIGGER_SINGLE = 3; // only on players, entities, csqc
- const float CH_SHOTS = 4; // only on players, entities, csqc
- const float CH_SHOTS_SINGLE = 4; // only on players, entities, csqc
- const float CH_WEAPON_B = 5; // only on players and entities
- const float CH_PAIN = 6; // only on players and csqc
- const float CH_PAIN_SINGLE = 6; // only on players and csqc
- const float CH_PLAYER = 7; // only on players and entities
- const float CH_TUBA = 5; // only on csqc
- #else
  const float CH_INFO = 0;
  const float CH_TRIGGER = -3;
  const float CH_WEAPON_A = -1;
@@@ -313,8 -224,8 +224,8 @@@ const float CH_WEAPON_B = -1
  const float CH_PAIN = -6;
  const float CH_PAIN_SINGLE = 6;
  const float CH_PLAYER = -7;
- const float CH_TUBA = 5;
- #endif
+ const float CH_PLAYER_SINGLE = 7;
+ const float CH_TUBA_SINGLE = 5;
  
  const float ATTEN_NONE = 0;
  const float ATTEN_MIN = 0.015625;
@@@ -327,7 -238,7 +238,7 @@@ const float ATTEN_MAX = 3.984375
  #define VOL_BASE 0.7
  #define VOL_BASEVOICE 1.0
  
 -// this sets sounds and other properties of the projectiles in csqc
 +// WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc
  const float PROJECTILE_ELECTRO = 1;
  const float PROJECTILE_ROCKET = 2;
  const float PROJECTILE_TAG = 3;
@@@ -336,7 -247,7 +247,7 @@@ const float PROJECTILE_ELECTRO_BEAM = 6
  const float PROJECTILE_GRENADE = 7;
  const float PROJECTILE_GRENADE_BOUNCING = 8;
  const float PROJECTILE_MINE = 9;
 -const float PROJECTILE_LASER = 10;
 +const float PROJECTILE_BLASTER = 10;
  const float PROJECTILE_HLAC = 11;
  const float PROJECTILE_SEEKER = 12;
  const float PROJECTILE_FLAC = 13;
@@@ -362,17 -273,6 +273,6 @@@ const float PROJECTILE_BUMBLE_BEAM = 31
  const float PROJECTILE_MAGE_SPIKE = 32;
  const float PROJECTILE_SHAMBLER_LIGHTNING = 33;
  
- const float PROJECTILE_NADE_RED = 50;
- const float PROJECTILE_NADE_RED_BURN = 51;
- const float PROJECTILE_NADE_BLUE = 52;
- const float PROJECTILE_NADE_BLUE_BURN = 53;
- const float PROJECTILE_NADE_YELLOW = 54;
- const float PROJECTILE_NADE_YELLOW_BURN = 55;
- const float PROJECTILE_NADE_PINK = 56;
- const float PROJECTILE_NADE_PINK_BURN = 57;
- const float PROJECTILE_NADE = 58;
- const float PROJECTILE_NADE_BURN = 59;
  const float SPECIES_HUMAN = 0;
  const float SPECIES_ROBOT_SOLID = 1;
  const float SPECIES_ALIEN = 2;
@@@ -392,6 -292,25 +292,6 @@@ const float WATERLEVEL_NONE = 0
  const float WATERLEVEL_WETFEET = 1;
  const float WATERLEVEL_SWIMMING = 2;
  const float WATERLEVEL_SUBMERGED = 3;
 -
 -const float MAX_SHOT_DISTANCE = 32768;
 -
 -// weapon requests
 -const float WR_SETUP = 1; // (SVQC) setup weapon data
 -const float WR_THINK = 2; // (SVQC) logic to run every frame
 -const float WR_CHECKAMMO1 = 3; // (SVQC) checks ammo for weapon
 -const float WR_CHECKAMMO2 = 4; // (SVQC) checks ammo for weapon
 -const float WR_AIM = 5; // (SVQC) runs bot aiming code for this weapon
 -const float WR_PRECACHE = 6; // (CSQC and SVQC) precaches models/sounds used by this weapon
 -const float WR_SUICIDEMESSAGE = 7; // (SVQC) notification number for suicide message (may inspect w_deathtype for details)
 -const float WR_KILLMESSAGE = 8; // (SVQC) notification number for kill message (may inspect w_deathtype for details)
 -const float WR_RELOAD = 9; // (SVQC) does not need to do anything
 -const float WR_RESETPLAYER = 10; // (SVQC) does not need to do anything
 -const float WR_IMPACTEFFECT = 11; // (CSQC) impact effect
 -const float WR_SWITCHABLE = 12; // (CSQC) impact effect
 -const float WR_PLAYERDEATH = 13; // (SVQC) does not need to do anything
 -const float WR_GONETHINK = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
 -
  #define SERVERFLAG_ALLOW_FULLBRIGHT 1
  #define SERVERFLAG_TEAMPLAY 2
  #define SERVERFLAG_PLAYERSTATS 4
@@@ -438,3 -357,8 +338,8 @@@ noref var vector autocvar_sv_player_hea
  #define URI_GET_UPDATENOTIFICATION 33
  #define URI_GET_URLLIB 128
  #define URI_GET_URLLIB_END 191
+ // gametype votes
+ #define GTV_AVAILABLE 0
+ // for later use in per-map gametype filtering
+ #define GTV_FORBIDDEN 2
index fd86dbbfc4e7595dbfad9a5c4f155fb810e3a9eb,26bb0a9afda23c20909e5e35c5c7ed03a5df10a6..3adc59a3020c8fc7cc1c8da0341284672dbf41bd
@@@ -60,7 -60,7 +60,7 @@@ float friend_needshelp(entity e
                return FALSE;
        if(DIFF_TEAM(e, self) && e != self.monster_owner)
                return FALSE;
-       if(e.freezetag_frozen)
+       if(e.frozen)
                return FALSE;
        if(!IS_PLAYER(e))
                return ((e.flags & FL_MONSTER) && e.health < e.max_health);
@@@ -70,7 -70,7 +70,7 @@@
        switch(self.skin)
        {
                case 0: return (e.health < autocvar_g_balance_health_regenstable);
 -              case 1: return ((e.ammo_cells && e.ammo_cells < g_pickup_cells_max) || (e.ammo_rockets && e.ammo_rockets < g_pickup_rockets_max) || (e.ammo_nails && e.ammo_nails < g_pickup_nails_max) || (e.ammo_shells && e.ammo_shells < g_pickup_shells_max));
 +              case 1: return ((e.ammo_cells && e.ammo_cells < g_pickup_cells_max) || (e.ammo_plasma && e.ammo_plasma < g_pickup_plasma_max) || (e.ammo_rockets && e.ammo_rockets < g_pickup_rockets_max) || (e.ammo_nails && e.ammo_nails < g_pickup_nails_max) || (e.ammo_shells && e.ammo_shells < g_pickup_shells_max));
                case 2: return (e.armorvalue < autocvar_g_balance_armor_regenstable);
                case 3: return (e.health > 0);
        }
@@@ -87,7 -87,7 +87,7 @@@ void mage_spike_explode(
        self.realowner.mage_spike = world;
  
        pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1);
 -      RadiusDamage (self, self.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), world, 0, DEATH_MONSTER_MAGE, other);
 +      RadiusDamage (self, self.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), world, world, 0, DEATH_MONSTER_MAGE, other);
  
        remove (self);
  }
@@@ -209,7 -209,6 +209,7 @@@ void mage_heal(
                                        break;
                                case 1:
                                        if(head.ammo_cells) head.ammo_cells = bound(head.ammo_cells, head.ammo_cells + 1, g_pickup_cells_max);
 +                                      if(head.ammo_plasma) head.ammo_plasma = bound(head.ammo_plasma, head.ammo_plasma + 1, g_pickup_plasma_max);
                                        if(head.ammo_rockets) head.ammo_rockets = bound(head.ammo_rockets, head.ammo_rockets + 1, g_pickup_rockets_max);
                                        if(head.ammo_shells) head.ammo_shells = bound(head.ammo_shells, head.ammo_shells + 2, g_pickup_shells_max);
                                        if(head.ammo_nails) head.ammo_nails = bound(head.ammo_nails, head.ammo_nails + 5, g_pickup_nails_max);
  void mage_push()
  {
        sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM);
 -      RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy);
 +      RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy);
        pointparticles(particleeffectnum("TE_EXPLOSION"), self.origin, '0 0 0', 1);
  
        self.frame = mage_anim_attack;
index 5d52bc18c4e16c962991448cf48490666d037982,6d06de50ebb30e9c8ff98ee6c5fefcb5542dbcf7..fd966f5d2edf20499aa7adf681b3aebd2a88fc4a
@@@ -94,7 -94,7 +94,7 @@@ float monster_isvalidtarget (entity tar
        if(SAME_TEAM(targ, ent))
                return FALSE; // enemy is on our team
  
-       if (targ.freezetag_frozen)
+       if (targ.frozen)
                return FALSE; // ignore frozen
  
        if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT))
@@@ -335,7 -335,7 +335,7 @@@ void Monster_CheckMinibossFlag (
                self.health += autocvar_g_monsters_miniboss_healthboost;
                self.effects |= EF_RED;
                if(!self.weapon)
 -                      self.weapon = WEP_NEX;
 +                      self.weapon = WEP_VORTEX;
        }
  }
  
@@@ -480,7 -480,7 +480,7 @@@ vector monster_pickmovetarget(entity ta
                
                if((self.enemy == world)
                        || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
-                       || (self.enemy.freezetag_frozen)
+                       || (self.enemy.frozen)
                        || (self.enemy.flags & FL_NOTARGET)
                        || (self.enemy.alpha < 0.5)
                        || (self.enemy.takedamage == DAMAGE_NO)
@@@ -605,6 -605,51 +605,51 @@@ void monster_move(float runspeed, floa
  
        entity targ;
  
+       if(self.frozen == 2)
+       {
+               self.revive_progress = bound(0, self.revive_progress + self.ticrate * self.revive_speed, 1);
+               self.health = max(1, self.revive_progress * self.max_health);
+               self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+               movelib_beak_simple(stopspeed);
+               self.frame = manim_idle;
+               self.enemy = world;
+               self.nextthink = time + self.ticrate;
+               if(self.revive_progress >= 1)
+                       Unfreeze(self);
+               return;
+       }
+       else if(self.frozen == 3)
+       {
+               self.revive_progress = bound(0, self.revive_progress - self.ticrate * self.revive_speed, 1);
+               self.health = max(0, autocvar_g_nades_ice_health + (self.max_health-autocvar_g_nades_ice_health) * self.revive_progress );
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+               movelib_beak_simple(stopspeed);
+               self.frame = manim_idle;
+               self.enemy = world;
+               self.nextthink = time + self.ticrate;
+               if(self.health < 1)
+               {
+                       Unfreeze(self);
+                       self.health = 0;
+                       self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+               }
+               else if ( self.revive_progress <= 0 )
+                       Unfreeze(self);
+               return;
+       }
        if(self.flags & FL_SWIM)
        {
                if(self.waterlevel < WATERLEVEL_WETFEET)
@@@ -776,6 -821,9 +821,9 @@@ void monster_remove(entity mon
        if(mon.weaponentity)
                remove(mon.weaponentity);
  
+       if(mon.iceblock)
+               remove(mon.iceblock);
        WaypointSprite_Kill(mon.sprite);
  
        remove(mon);
@@@ -827,6 -875,8 +875,8 @@@ void monsters_reset(
        setorigin(self, self.pos1);
        self.angles = self.pos2;
  
+       Unfreeze(self); // remove any icy remains
        self.health = self.max_health;
        self.velocity = '0 0 0';
        self.enemy = world;
@@@ -860,6 -910,12 +910,12 @@@ void monster_die(entity attacker, floa
        self.nextthink = time;
        self.monster_lifetime = time + 5;
  
+       if(self.frozen)
+       {
+               Unfreeze(self); // remove any icy remains
+               self.health = 0; // reset by Unfreeze
+       }
        monster_dropitem();
  
        MonsterSound(monstersound_death, 0, FALSE, CH_VOICE);
  
  void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
+       if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
+               return;
        if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
                return;
  
index 3e2b40528ddeef0fc12f09192fa7fb606d1ecd09,60cc622fa59509a87543ab82a18fe50a4d70ef47..27e2f7ede1c4b170c0f940af1e3b8193e19eb3dd
@@@ -360,7 -360,11 +360,11 @@@ void Send_Notification_WOCOVA
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_LAVA,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_lava",          _("^BG%s%s^K1 was cooked by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_MONSTER,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was pushed infront of a monster by ^BG%s^K1%s%s"), "") \
-       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade",          _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_NAPALM,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_napalm",   _("^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s"), _("^BG%s%s^K1 got too close to a napalm explosion%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_ice",      _("^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE_FREEZE,   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_ice",      _("^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_HEAL,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_heal",     _("^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SHOOTING_STAR,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_shootingstar",  _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SLIME,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SWAMP,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was preserved by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_DEATH,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_GUN,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_ROCKET,    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VENGEANCE,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was destroyed by the vengeful ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VOID,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_void",          _("^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_AUTOTEAMCHANGE,      2, 1, "s1 s2loc death_team", "",         "",                     _("^BG%s^K1 was moved into the %s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_BETRAYAL,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_teamkill_red",  _("^BG%s^K1 became enemies with the Lord of Teamplay%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FIRE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 became a bit too crispy%s%s"), _("^BG%s^K1 felt a little hot%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC,             2, 1, "s1 s2loc spree_lost", "s1",       "notify_selfkill",      _("^BG%s^K1 died%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_lava",          _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
-       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE,                2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW,   2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH,  2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN,          2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP,     2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 joins the Zombies%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade",          _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_NAPALM,         2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_napalm",   _("^BG%s^K1 was burned to death by their own Napalm Nade%s%s"), _("^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_ice",      _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE_FREEZE,     2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_ice",      _("^BG%s^K1 was frozen to death by their own Ice Nade%s%s"), _("^BG%s^K1 felt a little chilly%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_HEAL,           2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_heal",     _("^BG%s^K1's Healing Nade didn't quite heal them%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NOAMMO,              2, 1, "s1 s2loc spree_lost", "s1",       "notify_outofammo",     _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_ROT,                 2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 rotted away%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_SHOOTING_STAR,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_shootingstar",  _("^BG%s^K1 became a shooting star%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET,      2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_void",          _("^BG%s^K1 was in the wrong place%s%s"), "") \
        MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4,             3, 1, "s1 s2 s3loc spree_end", "s2 s1",  "notify_teamkill_%s",   _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DOMINATION_CAPTURE_TIME,        2, 2, "s1 s2 f1 f2", "",                 "",                     _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_FALL,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by falling"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_NADE,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by their Nade explosion"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED,         1, 1, "s1 f1", "",                       "",                     _("^BG%s^K3 was automatically revived after %s second(s)"), "") \
        MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4,             0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round"), "") \
        MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN,               1, 0, "s1", "",                          "",                     _("^BG%s^BG wins the round"), "") \
        MSG_INFO_NOTIF(1, INFO_ROUND_OVER,                     0, 0, "", "",                            "",                     _("^BGRound over, there's no winner"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF,                 1, 0, "s1", "",                          "",                     _("^BG%s^K1 froze themself"), "") \
        MSG_INFO_NOTIF(1, INFO_GODMODE_OFF,                    0, 1, "f1", "",                          "",                     _("^BGGodmode saved you %s units of damage, cheater!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF,                      1, 1, "s1 item_buffname", "",            "",                     _("^BG%s^BG got the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_LOST,                 1, 1, "s1 item_buffname", "",            "",                     _("^BG%s^BG lost the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_DROP,                 0, 1, "item_buffname", "",               "",                     _("^BGYou dropped the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_GOT,                  0, 1, "item_buffname", "",               "",                     _("^BGYou got the %s^BG Buff!"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE,           0, 1, "item_wepname", "",                      "",               _("^BGYou do not have the ^F1%s"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DROP,               1, 1, "item_wepname item_wepammo", "",         "",               _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_GOT,                0, 1, "item_wepname", "",                      "",               _("^BGYou got the ^F1%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WATERMARK,                      1, 0, "s1", "",                          "",                     _("^F3SVQC Build information: ^F4%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_SUICIDE,             2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ARC_MURDER,                    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhlac",             _("^BG%s%s^K1 was electrocuted by ^BG%s^K1's Arc%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_BLASTER_MURDER,                3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponlaser",            _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Blaster%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_BLASTER_SUICIDE,               2, 1, "s1 s2loc spree_lost", "s1",                 "weaponlaser",            _("^BG%s^K1 shot themself to hell with their Blaster%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_CRYLINK_MURDER,                3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponcrylink",          _("^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_CRYLINK_SUICIDE,               2, 1, "s1 s2loc spree_lost", "s1",                 "weaponcrylink",          _("^BG%s^K1 felt the strong pull of their Crylink%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_MURDER_DIRECT,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_MURDER_SPLASH,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_SUICIDE,            2, 1, "s1 s2loc spree_lost", "s1",                 "weaponrocketlauncher",   _("^BG%s^K1 blew themself up with their Devastator%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_BOLT,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_COMBO,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_ORBS,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 got too close to ^BG%s^K1's Electro plasma%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_BOLT,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 played with Electro plasma%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_ORBS,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 could not remember where they put their Electro plasma%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_ORBS,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponelectro",          _("^BG%s%s^K1 got too close to ^BG%s^K1's Electro orb%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_BOLT,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 played with Electro bolts%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_ORBS,          2, 1, "s1 s2loc spree_lost", "s1",                 "weaponelectro",          _("^BG%s^K1 could not remember where they put their Electro orb%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_BLAST,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponfireball",         _("^BG%s%s^K1 got too close to ^BG%s^K1's fireball%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_FIREMINE,      3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponfireball",         _("^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_SUICIDE_BLAST,        2, 1, "s1 s2loc spree_lost", "s1",                 "weaponfireball",         _("^BG%s^K1 should have used a smaller gun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhook",             _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_MURDER,            3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_SUICIDE,           2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_MURDER,                  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponlaser",            _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Laser%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_SUICIDE,                 2, 1, "s1 s2loc spree_lost", "s1",                 "weaponlaser",            _("^BG%s^K1 shot themself to hell with their Laser%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminelayer",        _("^BG%s%s^K1 got too close to ^BG%s^K1's mine%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_SUICIDE,             2, 1, "s1 s2loc spree_lost", "s1",                 "weaponminelayer",        _("^BG%s^K1 forgot about their mine%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_MINSTANEX_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminstanex",        _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Minstanex%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_BOUNCE,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapongrenadelauncher",  _("^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_EXPLODE,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapongrenadelauncher",  _("^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE,         2, 1, "s1 s2loc spree_lost", "s1",                 "weapongrenadelauncher",  _("^BG%s^K1 didn't see their own Mortar grenade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE,        2, 1, "s1 s2loc spree_lost", "s1",                 "weapongrenadelauncher",  _("^BG%s^K1 blew themself up with their own Mortar%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_NEX_MURDER,                    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponnex",              _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Nex%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER,                  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING,    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_PIERCING,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrifle",            _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrocketlauncher",   _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_SUICIDE,        2, 1, "s1 s2loc spree_lost", "s1",                 "weaponrocketlauncher",   _("^BG%s^K1 blew themself up with their Rocketlauncher%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_MURDER_SPRAY,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponseeker",           _("^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_MURDER_TAG,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponseeker",           _("^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_SUICIDE,                2, 1, "s1 s2loc spree_lost", "s1",                 "weaponseeker",           _("^BG%s^K1 played with tiny Seeker rockets%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_SHOCKWAVE_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponshotgun",          _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_SHOCKWAVE_MURDER_SLAP,         3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1",  "notify_melee_shotgun",   _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SHOTGUN_MURDER,                3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponshotgun",          _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_SHOTGUN_MURDER_SLAP,           3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1",  "notify_melee_shotgun",   _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_THINKING_WITH_PORTALS,         2, 1, "s1 s2loc spree_lost", "s1",                 "notify_selfkill",        _("^BG%s^K1 is now thinking with portals%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_SUICIDE,                  2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SNIPE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
 -      MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SPRAY,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "")
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_VAPORIZER_MURDER,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponminstanex",        _("^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_WEAPON_VORTEX_MURDER,                 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponnex",              _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s"), "")
  
  #define MULTITEAM_CENTER2(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \
        MSG_CENTER_NOTIF(default, prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE,  1, 4, "spree_cen s1 frag_stats",  NO_CPID, "0 0", _("^K1%sYou were typefragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing^BG%s")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE,     1, 2, "spree_cen s1 frag_ping",   NO_CPID, "0 0", _("^K1%sYou typefragged ^BG%s^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing^BG%s")) \
        MSG_CENTER_NOTIF(1, CENTER_NADE_THROW,                          0, 0, "",             CPID_NADES,          "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the nade!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_NADE_BONUS,                          0, 0, "",             CPID_NADES,          "0 0", _("^F2You got a ^K1BONUS GRENADE^F2!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_AUTOTEAMCHANGE,   0, 1, "death_team",   NO_CPID,             "0 0", _("^BGYou have been moved into a different team\nYou are now on: %s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_BETRAYAL,         0, 0, "",             NO_CPID,             "0 0", _("^K1Don't shoot your team mates!"), _("^K1Don't go against your team mates!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_CAMP,             0, 0, "",             NO_CPID,             "0 0", _("^K1Die camper!"), _("^K1Reconsider your tactics, camper!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_LAVA,             0, 0, "",             NO_CPID,             "0 0", _("^K1You couldn't stand the heat!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_MONSTER,          0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed by a monster!"), _("^K1You need to watch out for monsters!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE,                             0, 0, "",                         NO_CPID,                         "0 0", _("^K1You forgot to put the pin back in!"), _("^K1Tastes like chicken!")) \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_NAPALM,              0, 0, "",                         NO_CPID,                         "0 0", _("^K1Hanging around a napalm explosion is bad!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_ICE_FREEZE,  0, 0, "",                         NO_CPID,                         "0 0", _("^K1You got a little bit too cold!"), _("^K1You felt a little chilly!")) \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_HEAL,        0, 0, "",             NO_CPID,             "0 0", _("^K1Your Healing Nade is a bit defective"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NOAMMO,           0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_ROT,              0, 0, "",             NO_CPID,             "0 0", _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_SHOOTING_STAR,    0, 0, "",             NO_CPID,             "0 0", _("^K1You became a shooting star!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You froze ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN,            1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were frozen by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You revived ^BG%s"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_FALL,       0, 0, "",             NO_CPID,             "0 0", _("^K3You revived yourself"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_SELF,       0, 0, "",             NO_CPID,             "0 0", _("^K3You revived yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED,           1, 0, "s1",           NO_CPID,             "0 0", _("^K3You were revived by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED,      0, 1, "f1",           NO_CPID,             "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
        MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4,          0, 0, "",             CPID_ROUND,          "0 0", _("^TC^TT^BG team wins the round"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF,              0, 0, "",             NO_CPID,             "0 0", _("^K1You froze yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1Round already started, you spawn as frozen"), "") \
        MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER,       1, 0, "s1",           NO_CPID,             "0 0", _("^K1A %s has arrived!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_DROP,              0, 1, "item_buffname",                     CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG Buff!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_GOT,               0, 1, "item_buffname",                     CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG Buff!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE,        0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP,            1, 1, "item_wepname item_wepammo",         CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT,             0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA,                    NO_MSG,        INFO_DEATH_MURDER_LAVA,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_MONSTER,                 NO_MSG,        INFO_DEATH_MURDER_MONSTER,                 CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE,                    NO_MSG,        INFO_DEATH_MURDER_NADE,                    NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_NAPALM,             NO_MSG,        INFO_DEATH_MURDER_NADE_NAPALM,             NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE,                NO_MSG,        INFO_DEATH_MURDER_NADE_ICE,                NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE_FREEZE,         NO_MSG,        INFO_DEATH_MURDER_NADE_ICE_FREEZE,         NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_HEAL,               NO_MSG,        INFO_DEATH_MURDER_NADE_HEAL,               NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR,           NO_MSG,        INFO_DEATH_MURDER_SHOOTING_STAR,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME,                   NO_MSG,        INFO_DEATH_MURDER_SLIME,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SWAMP,                   NO_MSG,        INFO_DEATH_MURDER_SWAMP,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH,           NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_DEATH,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN,             NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_GUN,             NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET,          NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_ROCKET,          NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_VENGEANCE,               NO_MSG,        INFO_DEATH_MURDER_VENGEANCE,               NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID,                    NO_MSG,        INFO_DEATH_MURDER_VOID,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE,            NO_MSG,        INFO_DEATH_SELF_AUTOTEAMCHANGE,            CENTER_DEATH_SELF_AUTOTEAMCHANGE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL,                  NO_MSG,        INFO_DEATH_SELF_BETRAYAL,                  CENTER_DEATH_SELF_BETRAYAL) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP,                   NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_JUMP,                   CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE,                  NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_MELEE,                  CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NADE,                                              NO_MSG,                INFO_DEATH_SELF_NADE,                                      CENTER_DEATH_SELF_NADE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_NAPALM,                               NO_MSG,                INFO_DEATH_SELF_NADE_NAPALM,                       CENTER_DEATH_SELF_NADE_NAPALM) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE,                                  NO_MSG,                INFO_DEATH_SELF_NADE_ICE,                                  CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE_FREEZE,           NO_MSG,                INFO_DEATH_SELF_NADE_ICE_FREEZE,           CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_HEAL,                 NO_MSG,        INFO_DEATH_SELF_NADE_HEAL,                 CENTER_DEATH_SELF_NADE_HEAL) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO,                    NO_MSG,        INFO_DEATH_SELF_NOAMMO,                    CENTER_DEATH_SELF_NOAMMO) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_ROT,                       NO_MSG,        INFO_DEATH_SELF_ROT,                       CENTER_DEATH_SELF_ROT) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_SHOOTING_STAR,             NO_MSG,        INFO_DEATH_SELF_SHOOTING_STAR,             CENTER_DEATH_SELF_SHOOTING_STAR) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH,             NO_MSG,        INFO_DEATH_SELF_VH_WAKI_DEATH,             CENTER_DEATH_SELF_VH_WAKI_DEATH) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET,            NO_MSG,        INFO_DEATH_SELF_VH_WAKI_ROCKET,            CENTER_DEATH_SELF_VH_WAKI_ROCKET) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VOID,                      NO_MSG,        INFO_DEATH_SELF_VOID,                      CENTER_DEATH_SELF_VOID) \
+       MSG_MULTI_NOTIF(1, ITEM_BUFF_DROP,                       NO_MSG,        INFO_ITEM_BUFF_DROP,                       CENTER_ITEM_BUFF_DROP) \
+       MSG_MULTI_NOTIF(1, ITEM_BUFF_GOT,                        NO_MSG,        INFO_ITEM_BUFF_GOT,                        CENTER_ITEM_BUFF_GOT) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_DONTHAVE,                 NO_MSG,        INFO_ITEM_WEAPON_DONTHAVE,                 CENTER_ITEM_WEAPON_DONTHAVE) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_DROP,                     NO_MSG,        INFO_ITEM_WEAPON_DROP,                     CENTER_ITEM_WEAPON_DROP) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_GOT,                      NO_MSG,        INFO_ITEM_WEAPON_GOT,                      CENTER_ITEM_WEAPON_GOT) \
        MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO,                ANNCE_NUM_10,  NO_MSG,                                    CENTER_MINSTA_FINDAMMO_FIRST) \
        MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER,              NO_MSG,        INFO_WEAPON_ACCORDEON_MURDER,              NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE,             NO_MSG,        INFO_WEAPON_ACCORDEON_SUICIDE,             CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_ARC_MURDER,                    NO_MSG,        INFO_WEAPON_ARC_MURDER,                    NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_BLASTER_MURDER,                NO_MSG,        INFO_WEAPON_BLASTER_MURDER,                NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_BLASTER_SUICIDE,               NO_MSG,        INFO_WEAPON_BLASTER_SUICIDE,               CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_MURDER,                NO_MSG,        INFO_WEAPON_CRYLINK_MURDER,                NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_SUICIDE,               NO_MSG,        INFO_WEAPON_CRYLINK_SUICIDE,               CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_DIRECT,      NO_MSG,        INFO_WEAPON_DEVASTATOR_MURDER_DIRECT,      NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_SPLASH,      NO_MSG,        INFO_WEAPON_DEVASTATOR_MURDER_SPLASH,      NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_SUICIDE,            NO_MSG,        INFO_WEAPON_DEVASTATOR_SUICIDE,            CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_BOLT,           NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_BOLT,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_COMBO,          NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_COMBO,          NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_ORBS,           NO_MSG,        INFO_WEAPON_ELECTRO_MURDER_ORBS,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER,                   NO_MSG,        INFO_WEAPON_HOOK_MURDER,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER,            NO_MSG,        INFO_WEAPON_KLEINBOTTLE_MURDER,            NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE,           NO_MSG,        INFO_WEAPON_KLEINBOTTLE_SUICIDE,           CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_LASER_MURDER,                  NO_MSG,        INFO_WEAPON_LASER_MURDER,                  NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_LASER_SUICIDE,                 NO_MSG,        INFO_WEAPON_LASER_SUICIDE,                 CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SNIPE,       NO_MSG,        INFO_WEAPON_MACHINEGUN_MURDER_SNIPE,       NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SPRAY,       NO_MSG,        INFO_WEAPON_MACHINEGUN_MURDER_SPRAY,       NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_MURDER,              NO_MSG,        INFO_WEAPON_MINELAYER_MURDER,              NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_SUICIDE,             NO_MSG,        INFO_WEAPON_MINELAYER_SUICIDE,             CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_MINSTANEX_MURDER,              NO_MSG,        INFO_WEAPON_MINSTANEX_MURDER,              NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_BOUNCE,          NO_MSG,        INFO_WEAPON_MORTAR_MURDER_BOUNCE,          NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE,         NO_MSG,        INFO_WEAPON_MORTAR_MURDER_EXPLODE,         NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE,         NO_MSG,        INFO_WEAPON_MORTAR_SUICIDE_BOUNCE,         CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_EXPLODE,        NO_MSG,        INFO_WEAPON_MORTAR_SUICIDE_EXPLODE,        CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_NEX_MURDER,                    NO_MSG,        INFO_WEAPON_NEX_MURDER,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER,                  NO_MSG,        INFO_WEAPON_RIFLE_MURDER,                  NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL,             NO_MSG,        INFO_WEAPON_RIFLE_MURDER_HAIL,             NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL_PIERCING,    NO_MSG,        INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING,    NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_PIERCING,         NO_MSG,        INFO_WEAPON_RIFLE_MURDER_PIERCING,         NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_DIRECT,  NO_MSG,        INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT,  NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_SPLASH,  NO_MSG,        INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH,  NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_SUICIDE,        NO_MSG,        INFO_WEAPON_ROCKETLAUNCHER_SUICIDE,        CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_SPRAY,           NO_MSG,        INFO_WEAPON_SEEKER_MURDER_SPRAY,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_TAG,             NO_MSG,        INFO_WEAPON_SEEKER_MURDER_TAG,             NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SEEKER_SUICIDE,                NO_MSG,        INFO_WEAPON_SEEKER_SUICIDE,                CENTER_DEATH_SELF_GENERIC) \
 +      MSG_MULTI_NOTIF(1, WEAPON_SHOCKWAVE_MURDER,              NO_MSG,        INFO_WEAPON_SHOCKWAVE_MURDER,              NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_SHOCKWAVE_MURDER_SLAP,         NO_MSG,        INFO_WEAPON_SHOCKWAVE_MURDER_SLAP,         NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER,                NO_MSG,        INFO_WEAPON_SHOTGUN_MURDER,                NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER_SLAP,           NO_MSG,        INFO_WEAPON_SHOTGUN_MURDER_SLAP,           NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_THINKING_WITH_PORTALS,         NO_MSG,        INFO_WEAPON_THINKING_WITH_PORTALS,         CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, WEAPON_TUBA_MURDER,                   NO_MSG,        INFO_WEAPON_TUBA_MURDER,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, WEAPON_TUBA_SUICIDE,                  NO_MSG,        INFO_WEAPON_TUBA_SUICIDE,                  CENTER_DEATH_SELF_GENERIC) \
 -      MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SNIPE,              NO_MSG,        INFO_WEAPON_UZI_MURDER_SNIPE,              NO_MSG) \
 -      MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SPRAY,              NO_MSG,        INFO_WEAPON_UZI_MURDER_SPRAY,              NO_MSG)
 +      MSG_MULTI_NOTIF(1, WEAPON_VAPORIZER_MURDER,              NO_MSG,        INFO_WEAPON_VAPORIZER_MURDER,              NO_MSG) \
 +      MSG_MULTI_NOTIF(1, WEAPON_VORTEX_MURDER,                 NO_MSG,        INFO_WEAPON_VORTEX_MURDER,                 NO_MSG)
  
  #define MULTITEAM_CHOICE2(default,challow,prefix,chtype,optiona,optionb) \
        MSG_CHOICE_NOTIF(default, challow, prefix##RED, chtype, optiona##RED, optionb##RED) \
@@@ -940,6 -966,7 +972,7 @@@ var float autocvar_notification_show_sp
      item_wepname: return full name of a weapon from weaponid
      item_wepammo: ammo display for weapon from string
      item_centime: amount of time to display weapon message in centerprint
+     item_buffname: return full name of a buff from buffid
      death_team: show the full name of the team a player is switching from
  */
  
@@@ -991,7 -1018,8 +1024,8 @@@ string arg_slot[NOTIF_MAX_ARGS]
      ARG_CASE(ARG_CS_SV,     "spree_inf",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(1, input, s2, f2) : "")) \
      ARG_CASE(ARG_CS_SV,     "spree_end",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
      ARG_CASE(ARG_CS_SV,     "spree_lost",    (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
 -    ARG_CASE(ARG_CS_SV,     "item_wepname",  W_Name(f1)) \
 +    ARG_CASE(ARG_CS_SV,     "item_wepname",  WEP_NAME(f1)) \
+     ARG_CASE(ARG_CS_SV,     "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \
      ARG_CASE(ARG_CS_SV,     "item_wepammo",  (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
      ARG_CASE(ARG_DC,        "item_centime",  ftos(autocvar_notification_item_centerprinttime)) \
      ARG_CASE(ARG_SV,        "death_team",    Team_ColoredFullName(f1)) \
diff --combined qcsrc/common/stats.qh
index 0000000000000000000000000000000000000000,793582e1264b092483e2b7e9839dd27e847b61a1..44a540b65a636540b9cfea13d0684b614d1b666a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,290 +1,290 @@@
 -const float STAT_NEX_CHARGE             = 50;
+ // Full list of all stat constants, icnluded in a single location for easy reference
+ // 255 is the current limit (MAX_CL_STATS - 1), engine will need to be modified if you wish to add more stats
+ const float MAX_CL_STATS                = 256;
+ const float STAT_HEALTH                 = 0;
+ // 1 empty?
+ const float STAT_WEAPON                 = 2;
+ const float STAT_AMMO                   = 3;
+ const float STAT_ARMOR                  = 4;
+ const float STAT_WEAPONFRAME            = 5;
+ const float STAT_SHELLS                 = 6;
+ const float STAT_NAILS                  = 7;
+ const float STAT_ROCKETS                = 8;
+ const float STAT_CELLS                  = 9;
+ const float STAT_ACTIVEWEAPON           = 10;
+ const float STAT_TOTALSECRETS           = 11;
+ const float STAT_TOTALMONSTERS          = 12;
+ const float STAT_SECRETS                = 13;
+ const float STAT_MONSTERS               = 14;
+ const float STAT_ITEMS                  = 15;
+ const float STAT_VIEWHEIGHT             = 16;
+ // 17 empty?
+ // 18 empty?
+ // 19 empty?
+ // 20 empty?
+ const float STAT_VIEWZOOM               = 21;
+ // 22 empty?
+ // 23 empty?
+ // 24 empty?
+ // 25 empty?
+ // 26 empty?
+ // 27 empty?
+ // 28 empty?
+ // 29 empty?
+ // 30 empty?
+ // 31 empty?
+ const float STAT_KH_KEYS                = 32;
+ const float STAT_CTF_STATE              = 33;
+ // 34 empty?
+ const float STAT_WEAPONS                = 35;
+ const float STAT_SWITCHWEAPON           = 36;
+ const float STAT_GAMESTARTTIME          = 37;
+ const float STAT_STRENGTH_FINISHED      = 38;
+ const float STAT_INVINCIBLE_FINISHED    = 39;
+ // 40 empty?
+ // 41 empty?
+ const float STAT_PRESSED_KEYS           = 42;
+ const float STAT_ALLOW_OLDNEXBEAM       = 43; // this stat could later contain some other bits of info, like, more server-side particle config
+ const float STAT_FUEL                   = 44;
+ const float STAT_NB_METERSTART          = 45;
+ const float STAT_SHOTORG                = 46; // compressShotOrigin
+ const float STAT_LEADLIMIT              = 47;
+ const float STAT_WEAPON_CLIPLOAD        = 48;
+ const float STAT_WEAPON_CLIPSIZE        = 49;
 -const float STAT_NEX_CHARGEPOOL         = 53;
++const float STAT_VORTEX_CHARGE          = 50;
+ const float STAT_LAST_PICKUP            = 51;
+ const float STAT_HUD                    = 52;
 -// 84 empty?
++const float STAT_VORTEX_CHARGEPOOL      = 53;
+ const float STAT_HIT_TIME               = 54;
+ const float STAT_TYPEHIT_TIME           = 55;
+ const float STAT_LAYED_MINES            = 56;
+ const float STAT_HAGAR_LOAD             = 57;
+ const float STAT_SWITCHINGWEAPON        = 58;
+ const float STAT_SUPERWEAPONS_FINISHED  = 59;
+ const float STAT_VEHICLESTAT_HEALTH     = 60;
+ const float STAT_VEHICLESTAT_SHIELD     = 61;
+ const float STAT_VEHICLESTAT_ENERGY     = 62;
+ const float STAT_VEHICLESTAT_AMMO1      = 63;
+ const float STAT_VEHICLESTAT_RELOAD1    = 64;
+ const float STAT_VEHICLESTAT_AMMO2      = 65;
+ const float STAT_VEHICLESTAT_RELOAD2    = 66;
+ const float STAT_VEHICLESTAT_W2MODE     = 67;
+ // 68 empty?
+ const float STAT_NADE_TIMER             = 69;
+ const float STAT_SECRETS_TOTAL          = 70;
+ const float STAT_SECRETS_FOUND          = 71;
+ const float STAT_RESPAWN_TIME           = 72;
+ const float STAT_ROUNDSTARTTIME         = 73;
+ const float STAT_WEAPONS2               = 74;
+ const float STAT_WEAPONS3               = 75;
+ const float STAT_MONSTERS_TOTAL         = 76;
+ const float STAT_MONSTERS_KILLED        = 77;
+ const float STAT_BUFFS                  = 78;
+ const float STAT_NADE_BONUS             = 79;
+ const float STAT_NADE_BONUS_TYPE        = 80;
+ const float STAT_NADE_BONUS_SCORE       = 81;
+ const float STAT_HEALING_ORB            = 82;
+ const float STAT_HEALING_ORB_ALPHA      = 83;
++const float STAT_PLASMA                 = 84;
+ // 85 empty?
+ // 86 empty?
+ // 87 empty?
+ // 88 empty?
+ // 89 empty?
+ // 90 empty?
+ // 91 empty?
+ // 92 empty?
+ // 93 empty?
+ // 94 empty?
+ // 95 empty?
+ // 96 empty?
+ // 97 empty?
+ // 98 empty?
+ // 99 empty?
+ /* The following stats change depending on the gamemode, so can share the same ID */
+ // IDs 100 to 104 reserved for gamemodes
+ // freeze tag, clan arena, jailbreak
+ const float STAT_REDALIVE               = 100;
+ const float STAT_BLUEALIVE              = 101;
+ const float STAT_YELLOWALIVE            = 102;
+ const float STAT_PINKALIVE              = 103;
+ // domination
+ const float STAT_DOM_TOTAL_PPS          = 100;
+ const float STAT_DOM_PPS_RED            = 101;
+ const float STAT_DOM_PPS_BLUE           = 102;
+ const float STAT_DOM_PPS_YELLOW         = 103;
+ const float STAT_DOM_PPS_PINK           = 104;
+ // vip
+ const float STAT_VIP                    = 100;
+ const float STAT_VIP_RED                = 101;
+ const float STAT_VIP_BLUE               = 102;
+ const float STAT_VIP_YELLOW             = 103;
+ const float STAT_VIP_PINK               = 104;
+ // key hunt
+ const float STAT_KH_REDKEY_TEAM         = 100;
+ const float STAT_KH_BLUEKEY_TEAM        = 101;
+ const float STAT_KH_YELLOWKEY_TEAM      = 102;
+ const float STAT_KH_PINKKEY_TEAM        = 103;
+ /* Gamemode-specific stats end here */
+ const float STAT_FROZEN                 = 105;
+ const float STAT_REVIVE_PROGRESS        = 106;
+ // 107 empty?
+ // 108 empty?
+ // 109 empty?
+ // 110 empty?
+ // 111 empty?
+ // 112 empty?
+ // 113 empty?
+ // 114 empty?
+ // 115 empty?
+ // 116 empty?
+ // 117 empty?
+ // 118 empty?
+ // 119 empty?
+ // 120 empty?
+ // 121 empty?
+ // 122 empty?
+ // 123 empty?
+ // 124 empty?
+ // 125 empty?
+ // 126 empty?
+ // 127 empty?
+ // 128 empty?
+ // 129 empty?
+ // 130 empty?
+ // 131 empty?
+ // 132 empty?
+ // 133 empty?
+ // 134 empty?
+ // 135 empty?
+ // 136 empty?
+ // 137 empty?
+ // 138 empty?
+ // 139 empty?
+ // 140 empty?
+ // 141 empty?
+ // 142 empty?
+ // 143 empty?
+ // 144 empty?
+ // 145 empty?
+ // 146 empty?
+ // 147 empty?
+ // 148 empty?
+ // 149 empty?
+ // 150 empty?
+ // 151 empty?
+ // 152 empty?
+ // 153 empty?
+ // 154 empty?
+ // 155 empty?
+ // 156 empty?
+ // 157 empty?
+ // 158 empty?
+ // 159 empty?
+ // 160 empty?
+ // 161 empty?
+ // 162 empty?
+ // 162 empty?
+ // 163 empty?
+ // 164 empty?
+ // 165 empty?
+ // 166 empty?
+ // 167 empty?
+ // 168 empty?
+ // 169 empty?
+ // 170 empty?
+ // 171 empty?
+ // 172 empty?
+ // 173 empty?
+ // 174 empty?
+ // 175 empty?
+ // 176 empty?
+ // 177 empty?
+ // 178 empty?
+ // 179 empty?
+ // 180 empty?
+ // 181 empty?
+ // 182 empty?
+ // 183 empty?
+ // 184 empty?
+ // 185 empty?
+ // 186 empty?
+ // 187 empty?
+ // 188 empty?
+ // 189 empty?
+ // 190 empty?
+ // 191 empty?
+ // 192 empty?
+ // 193 empty?
+ // 194 empty?
+ // 195 empty?
+ // 196 empty?
+ // 197 empty?
+ // 198 empty?
+ // 199 empty?
+ // 200 empty?
+ // 201 empty?
+ // 202 empty?
+ // 203 empty?
+ // 204 empty?
+ // 205 empty?
+ // 206 empty?
+ // 207 empty?
+ // 208 empty?
+ // 209 empty?
+ // 210 empty?
+ // 211 empty?
+ // 212 empty?
+ // 213 empty?
+ // 214 empty?
+ // 215 empty?
+ // 216 empty?
+ // 217 empty?
+ // 218 empty?
+ // 219 empty?
+ const float STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR     = 220;
+ const float STAT_MOVEVARS_AIRCONTROL_PENALTY            = 221;
+ const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW           = 222;
+ const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW             = 223;
+ const float STAT_MOVEVARS_AIRCONTROL_POWER              = 224;
+ const float STAT_MOVEFLAGS                              = 225;
+ const float STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL   = 226;
+ const float STAT_MOVEVARS_WARSOWBUNNY_ACCEL             = 227;
+ const float STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED          = 228;
+ const float STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL         = 229;
+ const float STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO   = 230;
+ const float STAT_MOVEVARS_AIRSTOPACCELERATE             = 231;
+ const float STAT_MOVEVARS_AIRSTRAFEACCELERATE           = 232;
+ const float STAT_MOVEVARS_MAXAIRSTRAFESPEED             = 233;
+ const float STAT_MOVEVARS_AIRCONTROL                    = 234;
+ const float STAT_FRAGLIMIT                              = 235;
+ const float STAT_TIMELIMIT                              = 236;
+ const float STAT_MOVEVARS_WALLFRICTION                  = 237;
+ const float STAT_MOVEVARS_FRICTION                      = 238;
+ const float STAT_MOVEVARS_WATERFRICTION                 = 239;
+ const float STAT_MOVEVARS_TICRATE                       = 240;
+ const float STAT_MOVEVARS_TIMESCALE                     = 241;
+ const float STAT_MOVEVARS_GRAVITY                       = 242;
+ const float STAT_MOVEVARS_STOPSPEED                     = 243;
+ const float STAT_MOVEVARS_MAXSPEED                      = 244;
+ const float STAT_MOVEVARS_SPECTATORMAXSPEED             = 245;
+ const float STAT_MOVEVARS_ACCELERATE                    = 246;
+ const float STAT_MOVEVARS_AIRACCELERATE                 = 247;
+ const float STAT_MOVEVARS_WATERACCELERATE               = 248;
+ const float STAT_MOVEVARS_ENTGRAVITY                    = 249;
+ const float STAT_MOVEVARS_JUMPVELOCITY                  = 250;
+ const float STAT_MOVEVARS_EDGEFRICTION                  = 251;
+ const float STAT_MOVEVARS_MAXAIRSPEED                   = 252;
+ const float STAT_MOVEVARS_STEPHEIGHT                    = 253;
+ const float STAT_MOVEVARS_AIRACCEL_QW                   = 254;
+ const float STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION    = 255;
diff --combined qcsrc/common/util.qh
index 570a41e0107b3434a00949e568ea00ce47bc2d5b,9b969134a56ef21ed1968505b3008aae94bd37ac..a959931c3d52700b09c8abffa6e08bb2b8688c17
@@@ -1,3 -1,4 +1,3 @@@
 -#define WANT_CONST
  // commonly used, but better make them macros
  #define TRUE 1
  #define FALSE 0
@@@ -304,9 -305,9 +304,9 @@@ const float XENCODE_LEN = 5
  string xencode(float f);
  float xdecode(string s);
  
- #ifndef COMPAT_XON010_CHANNELS
+ // Play all sounds via sound7, for access to the extra channels.
+ // Otherwise, channels 8 to 15 would be blocked for a weird QW feature.
  #define sound(e,c,s,v,a) sound7(e,c,s,v,a,0,0)
- #endif
  
  float lowestbit(float f);
  
@@@ -363,11 -364,6 +363,11 @@@ typedef entity(entity cur, entity near
  typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
  void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
  
 +#ifdef SVQC
 +vector combine_to_vector(float x, float y, float z);
 +vector get_corner_position(entity box, float corner);
 +#endif
 +
  // expand multiple arguments into one argument by stripping parenthesis
  #define XPD(...) __VA_ARGS__
  
@@@ -449,5 -445,3 +449,5 @@@ float Mod_Q1BSP_NativeContentsFromSuper
  // Quadratic splines (bezier)
  vector bezier_quadratic_getpoint(vector a, vector p, vector b, float t);
  vector bezier_quadratic_getderivative(vector a, vector p, vector b, float t);
 +
 +#define APPEND_TO_STRING(list,sep,add) ((list) = (((list) != "") ? strcat(list, sep, add) : (add)))
index e01d30be9b5e15d73a2b0c04143e058ff231d3d6,0000000000000000000000000000000000000000..c666619e5c7b4fa9be19512b07aca27a19b8fa84
mode 100644,000000..100644
--- /dev/null
@@@ -1,1427 -1,0 +1,1427 @@@
-               self.owner.freezetag_frozen
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id  */ ARC,
 +/* function  */ W_Arc,
 +/* ammotype  */ ammo_cells,
 +/* impulse   */ 3,
 +/* flags     */ WEP_FLAG_NORMAL,
 +/* rating    */ BOT_PICKUP_RATING_HIGH,
 +/* color     */ '1 1 1',
 +/* modelname */ "arc",
 +/* simplemdl */ "foobar",
 +/* crosshair */ "gfx/crosshairhlac 0.7",
 +/* wepimg    */ "weaponhlac",
 +/* refname   */ "arc",
 +/* wepname   */ _("Arc")
 +);
 +
 +#define ARC_SETTINGS(w_cvar,w_prop) ARC_SETTINGS_LIST(w_cvar, w_prop, ARC, arc)
 +#define ARC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
 +      w_cvar(id, sn, NONE, beam_ammo) \
 +      w_cvar(id, sn, NONE, beam_animtime) \
 +      w_cvar(id, sn, NONE, beam_botaimspeed) \
 +      w_cvar(id, sn, NONE, beam_botaimlifetime) \
 +      w_cvar(id, sn, NONE, beam_damage) \
 +      w_cvar(id, sn, NONE, beam_degreespersegment) \
 +      w_cvar(id, sn, NONE, beam_distancepersegment) \
 +      w_cvar(id, sn, NONE, beam_falloff_halflifedist) \
 +      w_cvar(id, sn, NONE, beam_falloff_maxdist) \
 +      w_cvar(id, sn, NONE, beam_falloff_mindist) \
 +      w_cvar(id, sn, NONE, beam_force) \
 +      w_cvar(id, sn, NONE, beam_healing_amax) \
 +      w_cvar(id, sn, NONE, beam_healing_aps) \
 +      w_cvar(id, sn, NONE, beam_healing_hmax) \
 +      w_cvar(id, sn, NONE, beam_healing_hps) \
 +      w_cvar(id, sn, NONE, beam_maxangle) \
 +      w_cvar(id, sn, NONE, beam_nonplayerdamage) \
 +      w_cvar(id, sn, NONE, beam_range) \
 +      w_cvar(id, sn, NONE, beam_refire) \
 +      w_cvar(id, sn, NONE, beam_returnspeed) \
 +      w_cvar(id, sn, NONE, beam_tightness) \
 +      w_cvar(id, sn, NONE, burst_ammo) \
 +      w_cvar(id, sn, NONE, burst_damage) \
 +      w_cvar(id, sn, NONE, burst_healing_aps) \
 +      w_cvar(id, sn, NONE, burst_healing_hps) \
 +      w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
 +      w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
 +      w_prop(id, sn, string, weaponreplace, weaponreplace) \
 +      w_prop(id, sn, float,  weaponstart, weaponstart) \
 +      w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
 +      w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
 +
 +#ifndef MENUQC
 +#define ARC_MAX_SEGMENTS 20
 +vector arc_shotorigin[4];
 +.vector beam_start;
 +.vector beam_dir;
 +.vector beam_wantdir;
 +.float beam_type;
 +
 +#define ARC_BT_MISS        0
 +#define ARC_BT_WALL        1
 +#define ARC_BT_HEAL        2
 +#define ARC_BT_HIT         3
 +#define ARC_BT_BURST_MISS  10
 +#define ARC_BT_BURST_WALL  11
 +#define ARC_BT_BURST_HEAL  12
 +#define ARC_BT_BURST_HIT   13
 +#define ARC_BT_BURSTMASK   10
 +
 +#define ARC_SF_SETTINGS    1
 +#define ARC_SF_START       2
 +#define ARC_SF_WANTDIR     4
 +#define ARC_SF_BEAMDIR     8
 +#define ARC_SF_BEAMTYPE    16
 +#define ARC_SF_LOCALMASK   14
 +#endif
 +#ifdef SVQC
 +ARC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
 +.entity arc_beam;
 +.float BUTTON_ATCK_prev; // for better animation control
 +.float beam_prev;
 +.float beam_initialized;
 +.float beam_bursting;
 +.float beam_teleporttime;
 +#endif
 +#ifdef CSQC
 +void Ent_ReadArcBeam(float isnew);
 +
 +.vector beam_color;
 +.float beam_alpha;
 +.float beam_thickness;
 +.float beam_traileffect;
 +.float beam_hiteffect;
 +.float beam_hitlight[4]; // 0: radius, 123: rgb
 +.float beam_muzzleeffect;
 +.float beam_muzzlelight[4]; // 0: radius, 123: rgb
 +.string beam_image;
 +
 +.entity beam_muzzleentity;
 +
 +.float beam_degreespersegment;
 +.float beam_distancepersegment;
 +.float beam_usevieworigin;
 +.float beam_initialized;
 +.float beam_maxangle;
 +.float beam_range;
 +.float beam_returnspeed;
 +.float beam_tightness;
 +.vector beam_shotorigin;
 +
 +entity Draw_ArcBeam_callback_entity;
 +float Draw_ArcBeam_callback_last_thickness;
 +vector Draw_ArcBeam_callback_last_top; // NOTE: in same coordinate system as player.
 +vector Draw_ArcBeam_callback_last_bottom; // NOTE: in same coordinate system as player.
 +#endif
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC); }
 +
 +float W_Arc_Beam_Send(entity to, float sf)
 +{
 +      WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM);
 +
 +      // Truncate information when this beam is displayed to the owner client
 +      // - The owner client has no use for beam start position or directions,
 +      //    it always figures this information out for itself with csqc code.
 +      // - Spectating the owner also truncates this information.
 +      float drawlocal = ((to == self.owner) || ((to.enemy == self.owner) && IS_SPEC(to)));
 +      if(drawlocal) { sf &= ~ARC_SF_LOCALMASK; }
 +
 +      WriteByte(MSG_ENTITY, sf);
 +
 +      if(sf & ARC_SF_SETTINGS) // settings information
 +      {
 +              WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_degreespersegment));
 +              WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_distancepersegment));
 +              WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_maxangle));
 +              WriteCoord(MSG_ENTITY, WEP_CVAR(arc, beam_range));
 +              WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_returnspeed));
 +              WriteByte(MSG_ENTITY, WEP_CVAR(arc, beam_tightness) * 10);
 +
 +              WriteByte(MSG_ENTITY, drawlocal);
 +      }
 +      if(sf & ARC_SF_START) // starting location
 +      {
 +              WriteCoord(MSG_ENTITY, self.beam_start_x);
 +              WriteCoord(MSG_ENTITY, self.beam_start_y);
 +              WriteCoord(MSG_ENTITY, self.beam_start_z);
 +      }
 +      if(sf & ARC_SF_WANTDIR) // want/aim direction
 +      {
 +              WriteCoord(MSG_ENTITY, self.beam_wantdir_x);
 +              WriteCoord(MSG_ENTITY, self.beam_wantdir_y);
 +              WriteCoord(MSG_ENTITY, self.beam_wantdir_z);
 +      }
 +      if(sf & ARC_SF_BEAMDIR) // beam direction
 +      {
 +              WriteCoord(MSG_ENTITY, self.beam_dir_x);
 +              WriteCoord(MSG_ENTITY, self.beam_dir_y);
 +              WriteCoord(MSG_ENTITY, self.beam_dir_z);
 +      }
 +      if(sf & ARC_SF_BEAMTYPE) // beam type
 +      {
 +              WriteByte(MSG_ENTITY, self.beam_type);
 +      }
 +
 +      return TRUE;
 +}
 +
 +void Reset_ArcBeam(entity player, vector forward)
 +{
 +      if (!player.arc_beam) {
 +              return;
 +      }
 +      player.arc_beam.beam_dir = forward;
 +      player.arc_beam.beam_teleporttime = time;
 +}
 +
 +void W_Arc_Beam_Think(void)
 +{
 +      if(self != self.owner.arc_beam)
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      if(
 +              !IS_PLAYER(self.owner)
 +              ||
 +              (self.owner.WEP_AMMO(ARC) <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO))
 +              ||
 +              self.owner.deadflag != DEAD_NO
 +              ||
 +              (!self.owner.BUTTON_ATCK /* FIXME(Samual): && !self.beam_bursting */)
 +              ||
++              self.owner.frozen
 +      )
 +      {
 +              if(self == self.owner.arc_beam) { self.owner.arc_beam = world; }
 +              entity oldself = self;
 +              self = self.owner;
 +              if(!WEP_ACTION(WEP_ARC, WR_CHECKAMMO1) && !WEP_ACTION(WEP_ARC, WR_CHECKAMMO2))
 +              {
 +                      // note: this doesn't force the switch
 +                      W_SwitchToOtherWeapon(self);
 +              }
 +              self = oldself;
 +              remove(self);
 +              return;
 +      }
 +
 +      float burst = 0;
 +      if(/*self.owner.BUTTON_ATCK2 || */self.beam_bursting)
 +      {
 +              if(!self.beam_bursting)
 +                      self.beam_bursting = TRUE;
 +              burst = ARC_BT_BURSTMASK;
 +      }
 +
 +      // decrease ammo
 +      float coefficient = frametime;
 +      if(!(self.owner.items & IT_UNLIMITED_WEAPON_AMMO))
 +      {
 +              float rootammo;
 +              if(burst)
 +                      { rootammo = WEP_CVAR(arc, burst_ammo); }
 +              else
 +                      { rootammo = WEP_CVAR(arc, beam_ammo); }
 +
 +              if(rootammo)
 +              {
 +                      coefficient = min(coefficient, self.owner.WEP_AMMO(ARC) / rootammo);
 +                      self.owner.WEP_AMMO(ARC) = max(0, self.owner.WEP_AMMO(ARC) - (rootammo * frametime));
 +              }
 +      }
 +
 +      makevectors(self.owner.v_angle);
 +
 +      W_SetupShot_Range(
 +              self.owner,
 +              TRUE,
 +              0,
 +              "",
 +              0,
 +              WEP_CVAR(arc, beam_damage) * coefficient,
 +              WEP_CVAR(arc, beam_range)
 +      );
 +
 +      // After teleport, "lock" the beam until the teleport is confirmed.
 +      if (time < self.beam_teleporttime + ANTILAG_LATENCY(self.owner)) {
 +              w_shotdir = self.beam_dir;
 +      }
 +
 +      // network information: shot origin and want/aim direction
 +      if(self.beam_start != w_shotorg)
 +      {
 +              self.SendFlags |= ARC_SF_START;
 +              self.beam_start = w_shotorg;
 +      }
 +      if(self.beam_wantdir != w_shotdir)
 +      {
 +              self.SendFlags |= ARC_SF_WANTDIR;
 +              self.beam_wantdir = w_shotdir;
 +      }
 +
 +      if(!self.beam_initialized)
 +      {
 +              self.beam_dir = w_shotdir;
 +              self.beam_initialized = TRUE;
 +      }
 +
 +      // WEAPONTODO: Detect player velocity so that the beam curves when moving too
 +      // idea: blend together self.beam_dir with the inverted direction the player is moving in
 +      // might have to make some special accomodation so that it only uses view_right and view_up
 +
 +      // note that if we do this, it'll always be corrected to a maximum angle by beam_maxangle handling
 +
 +      float segments; 
 +      if(self.beam_dir != w_shotdir)
 +      {
 +              // calculate how much we're going to move the end of the beam to the want position
 +              // WEAPONTODO (server and client):
 +              // blendfactor never actually becomes 0 in this situation, which is a problem
 +              // regarding precision... this means that self.beam_dir and w_shotdir approach
 +              // eachother, however they never actually become the same value with this method.
 +              // Perhaps we should do some form of rounding/snapping?
 +              float angle = vlen(w_shotdir - self.beam_dir) * RAD2DEG;
 +              if(angle && (angle > WEP_CVAR(arc, beam_maxangle)))
 +              {
 +                      // if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
 +                      float blendfactor = bound(
 +                              0,
 +                              (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)),
 +                              min(WEP_CVAR(arc, beam_maxangle) / angle, 1)
 +                      );
 +                      self.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
 +              }
 +              else
 +              {
 +                      // the radius is not too far yet, no worries :D
 +                      float blendfactor = bound(
 +                              0,
 +                              (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)),
 +                              1
 +                      );
 +                      self.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
 +              }
 +
 +              // network information: beam direction
 +              self.SendFlags |= ARC_SF_BEAMDIR;
 +
 +              // calculate how many segments are needed
 +              float max_allowed_segments;
 +
 +              if(WEP_CVAR(arc, beam_distancepersegment))
 +              {
 +                      max_allowed_segments = min(
 +                              ARC_MAX_SEGMENTS,
 +                              1 + (vlen(w_shotdir / WEP_CVAR(arc, beam_distancepersegment)))
 +                      );
 +              }
 +              else { max_allowed_segments = ARC_MAX_SEGMENTS; }
 +
 +              if(WEP_CVAR(arc, beam_degreespersegment))
 +              {
 +                      segments = bound(
 +                              1, 
 +                              (
 +                                      min(
 +                                              angle,
 +                                              WEP_CVAR(arc, beam_maxangle)
 +                                      )
 +                                      /
 +                                      WEP_CVAR(arc, beam_degreespersegment)
 +                              ),
 +                              max_allowed_segments
 +                      );
 +              }
 +              else { segments = 1; }
 +      }
 +      else { segments = 1; }
 +
 +      vector beam_endpos = (w_shotorg + (self.beam_dir * WEP_CVAR(arc, beam_range)));
 +      vector beam_controlpoint = w_shotorg + w_shotdir * (WEP_CVAR(arc, beam_range) * (1 - WEP_CVAR(arc, beam_tightness)));
 +
 +      float i;
 +      float new_beam_type = 0;
 +      vector last_origin = w_shotorg;
 +      for(i = 1; i <= segments; ++i)
 +      {
 +              // WEAPONTODO (client):
 +              // In order to do nice fading and pointing on the starting segment, we must always
 +              // have that drawn as a separate triangle... However, that is difficult to do when
 +              // keeping in mind the above problems and also optimizing the amount of segments
 +              // drawn on screen at any given time. (Automatic beam quality scaling, essentially)
 +
 +              vector new_origin = bezier_quadratic_getpoint(
 +                      w_shotorg,
 +                      beam_controlpoint,
 +                      beam_endpos,
 +                      i / segments);
 +              vector new_dir = normalize(new_origin - last_origin);
 +
 +              WarpZone_traceline_antilag(
 +                      self.owner,
 +                      last_origin,
 +                      new_origin,
 +                      MOVE_NORMAL,
 +                      self.owner,
 +                      ANTILAG_LATENCY(self.owner)
 +              );
 +
 +              // Do all the transforms for warpzones right now, as we already
 +              // "are" in the post-trace system (if we hit a player, that's
 +              // always BEHIND the last passed wz).
 +              last_origin = trace_endpos;
 +              w_shotorg = WarpZone_TransformOrigin(WarpZone_trace_transform, w_shotorg);
 +              beam_controlpoint = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_controlpoint);
 +              beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos);
 +              new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir);
 +
 +              float is_player = (
 +                      trace_ent.classname == "player"
 +                      ||
 +                      trace_ent.classname == "body"
 +                      ||
 +                      (trace_ent.flags & FL_MONSTER)
 +              );
 +
 +              if(trace_ent && trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage)))
 +              {
 +                      // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
 +                      // NO. trace_endpos should be just fine. If not,
 +                      // that's an engine bug that needs proper debugging.
 +                      vector hitorigin = trace_endpos;
 +
 +                      float falloff = ExponentialFalloff(
 +                              WEP_CVAR(arc, beam_falloff_mindist),
 +                              WEP_CVAR(arc, beam_falloff_maxdist),
 +                              WEP_CVAR(arc, beam_falloff_halflifedist),
 +                              vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg)
 +                      );
 +
 +                      if(is_player && SAME_TEAM(self.owner, trace_ent))
 +                      {
 +                              float roothealth, rootarmor;
 +                              if(burst)
 +                              {
 +                                      roothealth = WEP_CVAR(arc, burst_healing_hps);
 +                                      rootarmor = WEP_CVAR(arc, burst_healing_aps);
 +                              }
 +                              else
 +                              {
 +                                      roothealth = WEP_CVAR(arc, beam_healing_hps);
 +                                      rootarmor = WEP_CVAR(arc, beam_healing_aps);
 +                              }
 +
 +                              if(trace_ent.health <= WEP_CVAR(arc, beam_healing_hmax) && roothealth)
 +                              {
 +                                      trace_ent.health = min(
 +                                              trace_ent.health + (roothealth * coefficient),
 +                                              WEP_CVAR(arc, beam_healing_hmax)
 +                                      );
 +                              }
 +                              if(trace_ent.armorvalue <= WEP_CVAR(arc, beam_healing_amax) && rootarmor)
 +                              {
 +                                      trace_ent.armorvalue = min(
 +                                              trace_ent.armorvalue + (rootarmor * coefficient),
 +                                              WEP_CVAR(arc, beam_healing_amax)
 +                                      );
 +                              }
 +
 +                              // stop rot, set visual effect
 +                              if(roothealth || rootarmor)
 +                              {
 +                                      trace_ent.pauserothealth_finished = max(
 +                                              trace_ent.pauserothealth_finished,
 +                                              time + autocvar_g_balance_pause_health_rot
 +                                      );
 +                                      trace_ent.pauserotarmor_finished = max(
 +                                              trace_ent.pauserotarmor_finished,
 +                                              time + autocvar_g_balance_pause_armor_rot
 +                                      );
 +                                      new_beam_type = ARC_BT_HEAL;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              float rootdamage;
 +                              if(is_player)
 +                              {
 +                                      if(burst)
 +                                              { rootdamage = WEP_CVAR(arc, burst_damage); }
 +                                      else
 +                                              { rootdamage = WEP_CVAR(arc, beam_damage); }
 +                              }
 +                              else
 +                                      { rootdamage = WEP_CVAR(arc, beam_nonplayerdamage); }
 +
 +                              if(accuracy_isgooddamage(self.owner, trace_ent))
 +                              {
 +                                      accuracy_add(
 +                                              self.owner,
 +                                              WEP_ARC,
 +                                              0,
 +                                              rootdamage * coefficient * falloff
 +                                      );
 +                              }
 +
 +                              Damage(
 +                                      trace_ent,
 +                                      self.owner,
 +                                      self.owner,
 +                                      rootdamage * coefficient * falloff,
 +                                      WEP_ARC,
 +                                      hitorigin,
 +                                      WEP_CVAR(arc, beam_force) * new_dir * coefficient * falloff
 +                              );
 +
 +                              new_beam_type = ARC_BT_HIT;
 +                      }
 +                      break; 
 +              }
 +              else if(trace_fraction != 1)
 +              {
 +                      // we collided with geometry
 +                      new_beam_type = ARC_BT_WALL;
 +                      break;
 +              }
 +      }
 +
 +      // te_explosion(trace_endpos);
 +
 +      // if we're bursting, use burst visual effects
 +      new_beam_type += burst;
 +
 +      // network information: beam type
 +      if(new_beam_type != self.beam_type)
 +      {
 +              self.SendFlags |= ARC_SF_BEAMTYPE;
 +              self.beam_type = new_beam_type;
 +      }
 +
 +      self.owner.beam_prev = time;
 +      self.nextthink = time;
 +}
 +
 +void W_Arc_Beam(float burst)
 +{
 +      // FIXME(Samual): remove this when overheat and burst work.
 +      if (burst)
 +      {
 +              centerprint(self, "^4NOTE:^7 Arc burst (secondary) is not implemented yet.");
 +      }
 +
 +      // only play fire sound if 1 sec has passed since player let go the fire button
 +      if(time - self.beam_prev > 1)
 +      {
 +              sound(self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTN_NORM);
 +      }
 +
 +      entity beam = self.arc_beam = spawn();
 +      beam.classname = "W_Arc_Beam";
 +      beam.solid = SOLID_NOT;
 +      beam.think = W_Arc_Beam_Think;
 +      beam.owner = self;
 +      beam.movetype = MOVETYPE_NONE;
 +      beam.bot_dodge = TRUE;
 +      beam.bot_dodgerating = WEP_CVAR(arc, beam_damage);
 +      beam.beam_bursting = burst;
 +      Net_LinkEntity(beam, FALSE, 0, W_Arc_Beam_Send);
 +
 +      entity oldself = self;
 +      self = beam;
 +      self.think();
 +      self = oldself;
 +}
 +
 +float W_Arc(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      if(WEP_CVAR(arc, beam_botaimspeed))
 +                      {
 +                              self.BUTTON_ATCK = bot_aim(
 +                                      WEP_CVAR(arc, beam_botaimspeed),
 +                                      0,
 +                                      WEP_CVAR(arc, beam_botaimlifetime),
 +                                      FALSE
 +                              );
 +                      }
 +                      else
 +                      {
 +                              self.BUTTON_ATCK = bot_aim(
 +                                      1000000,
 +                                      0,
 +                                      0.001,
 +                                      FALSE
 +                              );
 +                      }
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      #if 0
 +                      if(self.arc_beam.beam_heat > threshold)
 +                      {
 +                              stop the beam somehow
 +                              play overheat animation
 +                      }
 +                      #endif
 +
 +                      if(self.BUTTON_ATCK || self.BUTTON_ATCK2 /* FIXME(Samual): || self.arc_beam.beam_bursting */)
 +                      {
 +                              if(self.BUTTON_ATCK_prev)
 +                              {
 +                                      #if 0
 +                                      if(self.animstate_startframe == self.anim_shoot_x && self.animstate_numframes == self.anim_shoot_y)
 +                                              weapon_thinkf(WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
 +                                      else
 +                                      #endif
 +                                              weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 +                              }
 +
 +                              if((!self.arc_beam) || wasfreed(self.arc_beam))
 +                              {
 +                                      if(weapon_prepareattack(!!self.BUTTON_ATCK2, 0))
 +                                      {
 +                                              W_Arc_Beam(!!self.BUTTON_ATCK2);
 +                                              
 +                                              if(!self.BUTTON_ATCK_prev)
 +                                              {
 +                                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 +                                                      self.BUTTON_ATCK_prev = 1;
 +                                              }
 +                                      }
 +                              }
 +                      } 
 +                      else // todo
 +                      {
 +                              if(self.BUTTON_ATCK_prev != 0)
 +                              {
 +                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
 +                                      ATTACK_FINISHED(self) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor();
 +                              }
 +                              self.BUTTON_ATCK_prev = 0;
 +                      }
 +
 +                      #if 0
 +                      if(self.BUTTON_ATCK2)
 +                      if(weapon_prepareattack(1, autocvar_g_balance_arc_secondary_refire))
 +                      {
 +                              W_Arc_Attack2();
 +                              self.arc_count = autocvar_g_balance_arc_secondary_count;
 +                              weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
 +                              self.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor();
 +                      }
 +                      #endif
 +
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model("models/weapons/g_arc.md3");
 +                      precache_model("models/weapons/v_arc.md3");
 +                      precache_model("models/weapons/h_arc.iqm");
 +                      precache_sound("weapons/lgbeam_fire.wav");
 +                      if(!arc_shotorigin[0])
 +                      {
 +                              arc_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 1);
 +                              arc_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 2);
 +                              arc_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 3);
 +                              arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 4);
 +                      }
 +                      ARC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              {
 +                      return ((!WEP_CVAR(arc, beam_ammo)) || (self.WEP_AMMO(ARC) > 0));
 +              }
 +              case WR_CHECKAMMO2:
 +              {
 +                      // arc currently has no secondary attack
 +                      return FALSE;
 +                      //return ((!WEP_CVAR(arc, burst_ammo)) || (self.WEP_AMMO(ARC) > 0));
 +              }
 +              case WR_CONFIG:
 +              {
 +                      ARC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
 +                      return TRUE;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      return WEAPON_ARC_MURDER;
 +              }
 +      }
 +      return FALSE;
 +}
 +#endif
 +#ifdef CSQC
 +void Draw_ArcBeam_callback(vector start, vector hit, vector end)
 +{
 +      entity beam = Draw_ArcBeam_callback_entity;
 +      vector transformed_view_org;
 +      transformed_view_org = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin);
 +
 +      // Thickdir shall be perpendicular to the beam and to the view-to-beam direction (WEAPONTODO: WHY)
 +      // WEAPONTODO: Wouldn't it be better to be perpendicular to the beam and to the view FORWARD direction?
 +      vector thickdir = normalize(cross(normalize(start - hit), transformed_view_org - start));
 +
 +      vector hitorigin;
 +
 +      // draw segment
 +      #if 0
 +      if(trace_fraction != 1)
 +      {
 +              // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
 +              hitorigin = start + (Draw_ArcBeam_callback_new_dir * Draw_ArcBeam_callback_segmentdist * trace_fraction);
 +              hitorigin = WarpZone_TransformOrigin(WarpZone_trace_transform, hitorigin);
 +      }
 +      else
 +      {
 +              hitorigin = hit;
 +      }
 +      #else
 +      hitorigin = hit;
 +      #endif
 +
 +      // decide upon thickness
 +      float thickness = beam.beam_thickness;
 +
 +      // draw primary beam render
 +      vector top    = hitorigin + (thickdir * thickness);
 +      vector bottom = hitorigin - (thickdir * thickness);
 +      
 +      vector last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top);
 +      vector last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom);
 +
 +      R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE
 +      R_PolygonVertex(
 +              top,
 +              '0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)),
 +              beam.beam_color,
 +              beam.beam_alpha
 +      );
 +      R_PolygonVertex(
 +              last_top,
 +              '0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
 +              beam.beam_color,
 +              beam.beam_alpha
 +      );
 +      R_PolygonVertex(
 +              last_bottom,
 +              '0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
 +              beam.beam_color,
 +              beam.beam_alpha
 +      );
 +      R_PolygonVertex(
 +              bottom,
 +              '0 0.5 0' * (1 - (thickness / beam.beam_thickness)),
 +              beam.beam_color,
 +              beam.beam_alpha
 +      );
 +      R_EndPolygon();
 +
 +      // draw trailing particles
 +      // NOTES:
 +      //  - Don't use spammy particle counts here, use a FEW small particles around the beam
 +      //  - We're not using WarpZone_TrailParticles here because we will handle warpzones ourselves.
 +      if(beam.beam_traileffect)
 +      {
 +              trailparticles(beam, beam.beam_traileffect, start, hitorigin);
 +      }
 +
 +      // set up for the next 
 +      Draw_ArcBeam_callback_last_thickness = thickness;
 +      Draw_ArcBeam_callback_last_top = WarpZone_UnTransformOrigin(WarpZone_trace_transform, top);
 +      Draw_ArcBeam_callback_last_bottom = WarpZone_UnTransformOrigin(WarpZone_trace_transform, bottom);
 +}
 +
 +void Reset_ArcBeam(void)
 +{
 +      entity e;
 +      for (e = world; (e = findfloat(e, beam_usevieworigin, 1)); ) {
 +              e.beam_initialized = FALSE;
 +      }
 +      for (e = world; (e = findfloat(e, beam_usevieworigin, 2)); ) {
 +              e.beam_initialized = FALSE;
 +      }
 +}
 +
 +void Draw_ArcBeam(void)
 +{
 +      if(!self.beam_usevieworigin)
 +      {
 +              InterpolateOrigin_Do();
 +      }
 +
 +      // origin = beam starting origin
 +      // v_angle = wanted/aim direction
 +      // angles = current direction of beam
 +
 +      vector start_pos;
 +      vector wantdir; //= view_forward;
 +      vector beamdir; //= self.beam_dir;
 +
 +      float segments;
 +      if(self.beam_usevieworigin)
 +      {
 +              // WEAPONTODO:
 +              // Currently we have to replicate nearly the same method of figuring
 +              // out the shotdir that the server does... Ideally in the future we
 +              // should be able to acquire this from a generalized function built
 +              // into a weapon system for client code. 
 +
 +              // find where we are aiming
 +              makevectors(warpzone_save_view_angles);
 +              vector forward = v_forward;
 +              vector right = v_right;
 +              vector up = v_up;
 +
 +              // decide upon start position
 +              if(self.beam_usevieworigin == 2)
 +                      { start_pos = warpzone_save_view_origin; }
 +              else
 +                      { start_pos = self.origin; }
 +
 +              // trace forward with an estimation
 +              WarpZone_TraceLine(
 +                      start_pos,
 +                      start_pos + forward * self.beam_range,
 +                      MOVE_NOMONSTERS,
 +                      self
 +              );
 +
 +              // untransform in case our trace went through a warpzone
 +              vector end_pos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
 +
 +              // un-adjust trueaim if shotend is too close
 +              if(vlen(end_pos - start_pos) < g_trueaim_minrange)
 +                      end_pos = start_pos + (forward * g_trueaim_minrange);
 +
 +              // move shot origin to the actual gun muzzle origin
 +              vector origin_offset =
 +                        right * -self.beam_shotorigin_y 
 +                      + up * self.beam_shotorigin_z;
 +
 +              start_pos = start_pos + origin_offset;
 +
 +              // Move it also forward, but only as far as possible without hitting anything. Don't poke into walls!
 +              traceline(start_pos, start_pos + forward * self.beam_shotorigin_x, MOVE_NORMAL, self);
 +              start_pos = trace_endpos;
 +
 +              // calculate the aim direction now
 +              wantdir = normalize(end_pos - start_pos);
 +
 +              if(!self.beam_initialized)
 +              {
 +                      self.beam_dir = wantdir;
 +                      self.beam_initialized = TRUE;
 +              }
 +
 +              if(self.beam_dir != wantdir)
 +              {
 +                      // calculate how much we're going to move the end of the beam to the want position
 +                      // WEAPONTODO (server and client):
 +                      // blendfactor never actually becomes 0 in this situation, which is a problem
 +                      // regarding precision... this means that self.beam_dir and w_shotdir approach
 +                      // eachother, however they never actually become the same value with this method.
 +                      // Perhaps we should do some form of rounding/snapping?
 +                      float angle = vlen(wantdir - self.beam_dir) * RAD2DEG;
 +                      if(angle && (angle > self.beam_maxangle))
 +                      {
 +                              // if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
 +                              float blendfactor = bound(
 +                                      0,
 +                                      (1 - (self.beam_returnspeed * frametime)),
 +                                      min(self.beam_maxangle / angle, 1)
 +                              );
 +                              self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
 +                      }
 +                      else
 +                      {
 +                              // the radius is not too far yet, no worries :D
 +                              float blendfactor = bound(
 +                                      0,
 +                                      (1 - (self.beam_returnspeed * frametime)),
 +                                      1
 +                              );
 +                              self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
 +                      }
 +
 +                      // calculate how many segments are needed
 +                      float max_allowed_segments;
 +
 +                      if(self.beam_distancepersegment)
 +                      {
 +                              max_allowed_segments = min(
 +                                      ARC_MAX_SEGMENTS,
 +                                      1 + (vlen(wantdir / self.beam_distancepersegment))
 +                              );
 +                      }
 +                      else { max_allowed_segments = ARC_MAX_SEGMENTS; }
 +
 +                      if(self.beam_degreespersegment)
 +                      {
 +                              segments = bound(
 +                                      1, 
 +                                      (
 +                                              min(
 +                                                      angle,
 +                                                      self.beam_maxangle
 +                                              )
 +                                              /
 +                                              self.beam_degreespersegment
 +                                      ),
 +                                      max_allowed_segments
 +                              );
 +                      }
 +                      else { segments = 1; }
 +              }
 +              else { segments = 1; }
 +
 +              // set the beam direction which the rest of the code will refer to
 +              beamdir = self.beam_dir;
 +
 +              // finally, set self.angles to the proper direction so that muzzle attachment points in proper direction
 +              self.angles = fixedvectoangles2(forward, up); // TODO(Samual): is this == warpzone_save_view_angles?
 +      }
 +      else
 +      {
 +              // set the values from the provided info from the networked entity
 +              start_pos = self.origin;
 +              wantdir = self.v_angle;
 +              beamdir = self.angles;
 +
 +              if(beamdir != wantdir)
 +              {
 +                      float angle = vlen(wantdir - beamdir) * RAD2DEG;
 +
 +                      // calculate how many segments are needed
 +                      float max_allowed_segments;
 +
 +                      if(self.beam_distancepersegment)
 +                      {
 +                              max_allowed_segments = min(
 +                                      ARC_MAX_SEGMENTS,
 +                                      1 + (vlen(wantdir / self.beam_distancepersegment))
 +                              );
 +                      }
 +                      else { max_allowed_segments = ARC_MAX_SEGMENTS; }
 +
 +                      if(self.beam_degreespersegment)
 +                      {
 +                              segments = bound(
 +                                      1, 
 +                                      (
 +                                              min(
 +                                                      angle,
 +                                                      self.beam_maxangle
 +                                              )
 +                                              /
 +                                              self.beam_degreespersegment
 +                                      ),
 +                                      max_allowed_segments
 +                              );
 +                      }
 +                      else { segments = 1; }
 +              }
 +              else { segments = 1; }
 +      }
 +
 +      setorigin(self, start_pos);
 +      self.beam_muzzleentity.angles_z = random() * 360; // WEAPONTODO: use avelocity instead?
 +
 +      vector beam_endpos = (start_pos + (beamdir * self.beam_range));
 +      vector beam_controlpoint = start_pos + wantdir * (self.beam_range * (1 - self.beam_tightness));
 +
 +      Draw_ArcBeam_callback_entity = self;
 +      Draw_ArcBeam_callback_last_thickness = 0;
 +      Draw_ArcBeam_callback_last_top = start_pos;
 +      Draw_ArcBeam_callback_last_bottom = start_pos;
 +
 +      vector last_origin = start_pos;
 +      vector original_start_pos = start_pos;
 +
 +      float i;
 +      for(i = 1; i <= segments; ++i)
 +      {
 +              // WEAPONTODO (client):
 +              // In order to do nice fading and pointing on the starting segment, we must always
 +              // have that drawn as a separate triangle... However, that is difficult to do when
 +              // keeping in mind the above problems and also optimizing the amount of segments
 +              // drawn on screen at any given time. (Automatic beam quality scaling, essentially)
 +
 +              vector new_origin = bezier_quadratic_getpoint(
 +                      start_pos,
 +                      beam_controlpoint,
 +                      beam_endpos,
 +                      i / segments);
 +
 +              WarpZone_TraceBox_ThroughZone(
 +                      last_origin,
 +                      '0 0 0',
 +                      '0 0 0',
 +                      new_origin,
 +                      MOVE_NORMAL,
 +                      world,
 +                      world,
 +                      Draw_ArcBeam_callback
 +              );
 +
 +              // Do all the transforms for warpzones right now, as we already "are" in the post-trace
 +              // system (if we hit a player, that's always BEHIND the last passed wz).
 +              last_origin = trace_endpos;
 +              start_pos = WarpZone_TransformOrigin(WarpZone_trace_transform, start_pos);
 +              beam_controlpoint = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_controlpoint);
 +              beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos);
 +              beamdir = WarpZone_TransformVelocity(WarpZone_trace_transform, beamdir);
 +              Draw_ArcBeam_callback_last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top);
 +              Draw_ArcBeam_callback_last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom);
 +
 +              if(trace_fraction < 1) { break; }
 +      }
 +
 +      // visual effects for startpoint and endpoint
 +      if(self.beam_hiteffect)
 +      {
 +              // FIXME we really should do this on the server so it actually
 +              // matches gameplay. What this client side stuff is doing is no
 +              // more than guesswork.
 +              pointparticles(
 +                      self.beam_hiteffect,
 +                      last_origin,
 +                      beamdir * -1,
 +                      frametime * 2
 +              );
 +      }
 +      if(self.beam_hitlight[0])
 +      {
 +              adddynamiclight(
 +                      last_origin,
 +                      self.beam_hitlight[0],
 +                      vec3(
 +                              self.beam_hitlight[1],
 +                              self.beam_hitlight[2],
 +                              self.beam_hitlight[3]
 +                      )
 +              );
 +      }
 +      if(self.beam_muzzleeffect)
 +      {
 +              pointparticles(
 +                      self.beam_muzzleeffect,
 +                      original_start_pos + wantdir * 20,
 +                      wantdir * 1000,
 +                      frametime * 0.1
 +              );
 +      }
 +      if(self.beam_muzzlelight[0])
 +      {
 +              adddynamiclight(
 +                      original_start_pos + wantdir * 20,
 +                      self.beam_muzzlelight[0],
 +                      vec3(
 +                              self.beam_muzzlelight[1],
 +                              self.beam_muzzlelight[2],
 +                              self.beam_muzzlelight[3]
 +                      )
 +              );
 +      }
 +
 +      // cleanup
 +      Draw_ArcBeam_callback_entity = world;
 +      Draw_ArcBeam_callback_last_thickness = 0;
 +      Draw_ArcBeam_callback_last_top = '0 0 0';
 +      Draw_ArcBeam_callback_last_bottom = '0 0 0';
 +}
 +
 +void Remove_ArcBeam(void)
 +{
 +      remove(self.beam_muzzleentity);
 +      sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
 +}
 +
 +void Ent_ReadArcBeam(float isnew)
 +{
 +      float sf = ReadByte();
 +      entity flash;
 +
 +      if(isnew)
 +      {
 +              // calculate shot origin offset from gun alignment
 +              float gunalign = autocvar_cl_gunalign;
 +              if(gunalign != 1 && gunalign != 2 && gunalign != 4)
 +                      gunalign = 3; // default value
 +              --gunalign;
 +
 +              self.beam_shotorigin = arc_shotorigin[gunalign];
 +
 +              // set other main attributes of the beam
 +              self.draw = Draw_ArcBeam;
 +              self.entremove = Remove_ArcBeam;
 +              sound(self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
 +
 +              flash = spawn();
 +              flash.owner = self;
 +              flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
 +              flash.drawmask = MASK_NORMAL;
 +              flash.solid = SOLID_NOT;
 +              flash.avelocity_z = 5000;
 +              setattachment(flash, self, "");
 +              setorigin(flash, '0 0 0');
 +
 +              self.beam_muzzleentity = flash;
 +      }
 +      else
 +      {
 +              flash = self.beam_muzzleentity;
 +      }
 +
 +      if(sf & ARC_SF_SETTINGS) // settings information
 +      {
 +              self.beam_degreespersegment = ReadShort();
 +              self.beam_distancepersegment = ReadShort();
 +              self.beam_maxangle = ReadShort();
 +              self.beam_range = ReadCoord();
 +              self.beam_returnspeed = ReadShort();
 +              self.beam_tightness = (ReadByte() / 10);
 +
 +              if(ReadByte())
 +              {
 +                      if(autocvar_chase_active)
 +                              { self.beam_usevieworigin = 1; }
 +                      else // use view origin
 +                              { self.beam_usevieworigin = 2; }
 +              }
 +              else
 +              {
 +                      self.beam_usevieworigin = 0;
 +              }
 +      }
 +
 +      if(!self.beam_usevieworigin)
 +      {
 +              // self.iflags = IFLAG_ORIGIN | IFLAG_ANGLES | IFLAG_V_ANGLE; // why doesn't this work?
 +              self.iflags = IFLAG_ORIGIN;
 +
 +              InterpolateOrigin_Undo();
 +      }
 +
 +      if(sf & ARC_SF_START) // starting location
 +      {
 +              self.origin_x = ReadCoord();
 +              self.origin_y = ReadCoord();
 +              self.origin_z = ReadCoord();
 +      }
 +      else if(self.beam_usevieworigin) // infer the location from player location
 +      {
 +              if(self.beam_usevieworigin == 2)
 +              {
 +                      // use view origin
 +                      self.origin = view_origin;
 +              }
 +              else
 +              {
 +                      // use player origin so that third person display still works
 +                      self.origin = getplayerorigin(player_localnum) + ('0 0 1' * getstati(STAT_VIEWHEIGHT));
 +              }
 +      }
 +
 +      setorigin(self, self.origin);
 +
 +      if(sf & ARC_SF_WANTDIR) // want/aim direction
 +      {
 +              self.v_angle_x = ReadCoord();
 +              self.v_angle_y = ReadCoord();
 +              self.v_angle_z = ReadCoord();
 +      }
 +
 +      if(sf & ARC_SF_BEAMDIR) // beam direction
 +      {
 +              self.angles_x = ReadCoord();
 +              self.angles_y = ReadCoord();
 +              self.angles_z = ReadCoord();
 +      }
 +
 +      if(sf & ARC_SF_BEAMTYPE) // beam type
 +      {
 +              self.beam_type = ReadByte();
 +              switch(self.beam_type)
 +              {
 +                      case ARC_BT_MISS:
 +                      {
 +                              self.beam_color = '-1 -1 1';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 8;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning");
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +                      case ARC_BT_WALL: // grenadelauncher_muzzleflash healray_muzzleflash
 +                      {
 +                              self.beam_color = '0.5 0.5 1';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 8;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning");
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; // particleeffectnum("grenadelauncher_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +                      case ARC_BT_HEAL:
 +                      {
 +                              self.beam_color = '0 1 0';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 8;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("healray_impact"); 
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +                      case ARC_BT_HIT:
 +                      {
 +                              self.beam_color = '1 0 1';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 8;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning"); 
 +                              self.beam_hitlight[0] = 20;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 0;
 +                              self.beam_hitlight[3] = 0;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 50;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 0;
 +                              self.beam_muzzlelight[3] = 0;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +                      case ARC_BT_BURST_MISS:
 +                      {
 +                              self.beam_color = '-1 -1 1';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 14;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning"); 
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              setmodel(flash, "models/flash.md3");
 +                              flash.alpha = self.beam_alpha;
 +                              flash.colormod = self.beam_color;
 +                              flash.scale = 0.5;
 +                              break;
 +                      }
 +                      case ARC_BT_BURST_WALL:
 +                      {
 +                              self.beam_color = '0.5 0.5 1';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 14;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning"); 
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +                      case ARC_BT_BURST_HEAL:
 +                      {
 +                              self.beam_color = '0 1 0';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 14;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning"); 
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +                      case ARC_BT_BURST_HIT:
 +                      {
 +                              self.beam_color = '1 0 1';
 +                              self.beam_alpha = 0.5;
 +                              self.beam_thickness = 14;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = particleeffectnum("electro_lightning"); 
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +
 +                      // shouldn't be possible, but lets make it colorful if it does :D
 +                      default:
 +                      {
 +                              self.beam_color = randomvec();
 +                              self.beam_alpha = 1;
 +                              self.beam_thickness = 8;
 +                              self.beam_traileffect = FALSE;
 +                              self.beam_hiteffect = FALSE; 
 +                              self.beam_hitlight[0] = 0;
 +                              self.beam_hitlight[1] = 1;
 +                              self.beam_hitlight[2] = 1;
 +                              self.beam_hitlight[3] = 1;
 +                              self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash");
 +                              self.beam_muzzlelight[0] = 0;
 +                              self.beam_muzzlelight[1] = 1;
 +                              self.beam_muzzlelight[2] = 1;
 +                              self.beam_muzzlelight[3] = 1;
 +                              self.beam_image = "particles/lgbeam";
 +                              if(self.beam_muzzleeffect >= 0)
 +                              {
 +                                      self.beam_image = "particles/lgbeam";
 +                                      setmodel(flash, "models/flash.md3");
 +                                      flash.alpha = self.beam_alpha;
 +                                      flash.colormod = self.beam_color;
 +                                      flash.scale = 0.5;
 +                              }
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      if(!self.beam_usevieworigin)
 +      {
 +              InterpolateOrigin_Note();
 +      }
 +}
 +
 +float W_Arc(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      // todo
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/lgbeam_fly.wav");
 +                      return TRUE;
 +              }
 +              case WR_ZOOMRETICLE:
 +              {
 +                      // no weapon specific image for this weapon
 +                      return FALSE;
 +              }
 +      }
 +      return FALSE;
 +}
 +#endif
 +#endif
index 198097bfb903fa8a7420eac9ef64ced6e33ceb38,0000000000000000000000000000000000000000..e0e9c6256f8abc5a982fe5e81acfc87144d29006
mode 100644,000000..100644
--- /dev/null
@@@ -1,620 -1,0 +1,620 @@@
-       if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.freezetag_frozen)
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id  */ MINE_LAYER,
 +/* function  */ W_MineLayer,
 +/* ammotype  */ ammo_rockets,
 +/* impulse   */ 4,
 +/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 +/* rating    */ BOT_PICKUP_RATING_HIGH,
 +/* color     */ '0.75 1 0',
 +/* modelname */ "minelayer",
 +/* simplemdl */ "foobar",
 +/* crosshair */ "gfx/crosshairminelayer 0.9",
 +/* wepimg    */ "weaponminelayer",
 +/* refname   */ "minelayer",
 +/* wepname   */ _("Mine Layer")
 +);
 +
 +#define MINELAYER_SETTINGS(w_cvar,w_prop) MINELAYER_SETTINGS_LIST(w_cvar, w_prop, MINE_LAYER, minelayer)
 +#define MINELAYER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
 +      w_cvar(id, sn, NONE, ammo) \
 +      w_cvar(id, sn, NONE, animtime) \
 +      w_cvar(id, sn, NONE, damage) \
 +      w_cvar(id, sn, NONE, damageforcescale) \
 +      w_cvar(id, sn, NONE, detonatedelay) \
 +      w_cvar(id, sn, NONE, edgedamage) \
 +      w_cvar(id, sn, NONE, force) \
 +      w_cvar(id, sn, NONE, health) \
 +      w_cvar(id, sn, NONE, lifetime) \
 +      w_cvar(id, sn, NONE, lifetime_countdown) \
 +      w_cvar(id, sn, NONE, limit) \
 +      w_cvar(id, sn, NONE, protection) \
 +      w_cvar(id, sn, NONE, proximityradius) \
 +      w_cvar(id, sn, NONE, radius) \
 +      w_cvar(id, sn, NONE, refire) \
 +      w_cvar(id, sn, NONE, remote_damage) \
 +      w_cvar(id, sn, NONE, remote_edgedamage) \
 +      w_cvar(id, sn, NONE, remote_force) \
 +      w_cvar(id, sn, NONE, remote_radius) \
 +      w_cvar(id, sn, NONE, speed) \
 +      w_cvar(id, sn, NONE, time) \
 +      w_prop(id, sn, float,  reloading_ammo, reload_ammo) \
 +      w_prop(id, sn, float,  reloading_time, reload_time) \
 +      w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
 +      w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
 +      w_prop(id, sn, string, weaponreplace, weaponreplace) \
 +      w_prop(id, sn, float,  weaponstart, weaponstart) \
 +      w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
 +      w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
 +
 +#ifdef SVQC
 +MINELAYER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
 +void W_MineLayer_Think(void);
 +.float minelayer_detonate, mine_explodeanyway;
 +.float mine_time;
 +.vector mine_orientation;
 +#endif
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_minelayer(void) { weapon_defaultspawnfunc(WEP_MINE_LAYER); }
 +
 +void W_MineLayer_Stick(entity to)
 +{
 +      spamsound(self, CH_SHOTS, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
 +
 +      // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
 +
 +      entity newmine;
 +      newmine = spawn();
 +      newmine.classname = self.classname;
 +
 +      newmine.bot_dodge = self.bot_dodge;
 +      newmine.bot_dodgerating = self.bot_dodgerating;
 +
 +      newmine.owner = self.owner;
 +      newmine.realowner = self.realowner;
 +      setsize(newmine, '-4 -4 -4', '4 4 4');
 +      setorigin(newmine, self.origin);
 +      setmodel(newmine, "models/mine.md3");
 +      newmine.angles = vectoangles(-trace_plane_normal); // face against the surface
 +
 +      newmine.mine_orientation = -trace_plane_normal;
 +
 +      newmine.takedamage = self.takedamage;
 +      newmine.damageforcescale = self.damageforcescale;
 +      newmine.health = self.health;
 +      newmine.event_damage = self.event_damage;
 +      newmine.spawnshieldtime = self.spawnshieldtime;
 +      newmine.damagedbycontents = TRUE;
 +
 +      newmine.movetype = MOVETYPE_NONE; // lock the mine in place
 +      newmine.projectiledeathtype = self.projectiledeathtype;
 +
 +      newmine.mine_time = self.mine_time;
 +
 +      newmine.touch = func_null;
 +      newmine.think = W_MineLayer_Think;
 +      newmine.nextthink = time;
 +      newmine.cnt = self.cnt;
 +      newmine.flags = self.flags;
 +
 +      remove(self);
 +      self = newmine;
 +
 +      if(to)
 +              SetMovetypeFollow(self, to);
 +}
 +
 +void W_MineLayer_Explode(void)
 +{
 +      if(other.takedamage == DAMAGE_AIM)
 +              if(IS_PLAYER(other))
 +                      if(DIFF_TEAM(self.realowner, other))
 +                              if(other.deadflag == DEAD_NO)
 +                                      if(IsFlying(other))
 +                                              Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
 +
 +      self.event_damage = func_null;
 +      self.takedamage = DAMAGE_NO;
 +
 +      RadiusDamage(self, self.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), world, world, WEP_CVAR(minelayer, force), self.projectiledeathtype, other);
 +
 +      if(self.realowner.weapon == WEP_MINE_LAYER)
 +      {
 +              entity oldself;
 +              oldself = self;
 +              self = self.realowner;
 +              if(!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1))
 +              {
 +                      self.cnt = WEP_MINE_LAYER;
 +                      ATTACK_FINISHED(self) = time;
 +                      self.switchweapon = w_getbestweapon(self);
 +              }
 +              self = oldself;
 +      }
 +      self.realowner.minelayer_mines -= 1;
 +      remove(self);
 +}
 +
 +void W_MineLayer_DoRemoteExplode(void)
 +{
 +      self.event_damage = func_null;
 +      self.takedamage = DAMAGE_NO;
 +
 +      if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
 +              self.velocity = self.mine_orientation; // particle fx and decals need .velocity
 +
 +      RadiusDamage(self, self.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), world, world, WEP_CVAR(minelayer, remote_force), self.projectiledeathtype | HITTYPE_BOUNCE, world);
 +
 +      if(self.realowner.weapon == WEP_MINE_LAYER)
 +      {
 +              entity oldself;
 +              oldself = self;
 +              self = self.realowner;
 +              if(!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1))
 +              {
 +                      self.cnt = WEP_MINE_LAYER;
 +                      ATTACK_FINISHED(self) = time;
 +                      self.switchweapon = w_getbestweapon(self);
 +              }
 +              self = oldself;
 +      }
 +      self.realowner.minelayer_mines -= 1;
 +      remove(self);
 +}
 +
 +void W_MineLayer_RemoteExplode(void)
 +{
 +      if(self.realowner.deadflag == DEAD_NO)
 +              if((self.spawnshieldtime >= 0)
 +                      ? (time >= self.spawnshieldtime) // timer
 +                      : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(minelayer, remote_radius)) // safety device
 +              )
 +              {
 +                      W_MineLayer_DoRemoteExplode();
 +              }
 +}
 +
 +void W_MineLayer_ProximityExplode(void)
 +{
 +      // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance
 +      if(WEP_CVAR(minelayer, protection) && self.mine_explodeanyway == 0)
 +      {
 +              entity head;
 +              head = findradius(self.origin, WEP_CVAR(minelayer, radius));
 +              while(head)
 +              {
 +                      if(head == self.realowner || SAME_TEAM(head, self.realowner))
 +                              return;
 +                      head = head.chain;
 +              }
 +      }
 +
 +      self.mine_time = 0;
 +      W_MineLayer_Explode();
 +}
 +
 +float W_MineLayer_Count(entity e)
 +{
 +      float minecount = 0;
 +      entity mine;
 +      for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e)
 +              minecount += 1;
 +
 +      return minecount;
 +}
 +
 +void W_MineLayer_Think(void)
 +{
 +      entity head;
 +
 +      self.nextthink = time;
 +
 +      if(self.movetype == MOVETYPE_FOLLOW)
 +      {
 +              if(LostMovetypeFollow(self))
 +              {
 +                      UnsetMovetypeFollow(self);
 +                      self.movetype = MOVETYPE_NONE;
 +              }
 +      }
 +      
 +      // our lifetime has expired, it's time to die - mine_time just allows us to play a sound for this
 +      // TODO: replace this mine_trigger.wav sound with a real countdown
 +      if((time > self.cnt) && (!self.mine_time))
 +      {
 +              if(WEP_CVAR(minelayer, lifetime_countdown) > 0)
 +                      spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
 +              self.mine_time = time + WEP_CVAR(minelayer, lifetime_countdown);
 +              self.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near.
 +      }
 +
 +      // a player's mines shall explode if he disconnects or dies
 +      // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
-               if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.freezetag_frozen)
++      if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen)
 +      {
 +              other = world;
 +              self.projectiledeathtype |= HITTYPE_BOUNCE;
 +              W_MineLayer_Explode();
 +              return;
 +      }
 +
 +      // set the mine for detonation when a foe gets close enough
 +      head = findradius(self.origin, WEP_CVAR(minelayer, proximityradius));
 +      while(head)
 +      {
++              if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen)
 +              if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
 +              if(!self.mine_time)
 +              {
 +                      spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
 +                      self.mine_time = time + WEP_CVAR(minelayer, time);
 +              }
 +              head = head.chain;
 +      }
 +
 +      // explode if it's time to
 +      if(self.mine_time && time >= self.mine_time)
 +      {
 +              W_MineLayer_ProximityExplode();
 +              return;
 +      }
 +
 +      // remote detonation
 +      if(self.realowner.weapon == WEP_MINE_LAYER)
 +      if(self.realowner.deadflag == DEAD_NO)
 +      if(self.minelayer_detonate)
 +              W_MineLayer_RemoteExplode();
 +}
 +
 +void W_MineLayer_Touch(void)
 +{
 +      if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
 +              return; // we're already a stuck mine, why do we get called? TODO does this even happen?
 +
 +      if(WarpZone_Projectile_Touch())
 +      {
 +              if(wasfreed(self))
 +                      self.realowner.minelayer_mines -= 1;
 +              return;
 +      }
 +
 +      if(other && IS_PLAYER(other) && other.deadflag == DEAD_NO)
 +      {
 +              // hit a player
 +              // don't stick
 +      }
 +      else
 +      {
 +              W_MineLayer_Stick(other);
 +      }
 +}
 +
 +void W_MineLayer_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if(self.health <= 0)
 +              return;
 +              
 +      float is_from_enemy = (inflictor.realowner != self.realowner);
 +              
 +      if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_from_enemy ? 1 : -1)))
 +              return; // g_projectiles_damage says to halt
 +              
 +      self.health = self.health - damage;
 +      self.angles = vectoangles(self.velocity);
 +      
 +      if(self.health <= 0)
 +              W_PrepareExplosionByDamage(attacker, W_MineLayer_Explode);
 +}
 +
 +void W_MineLayer_Attack(void)
 +{
 +      entity mine;
 +      entity flash;
 +
 +      // scan how many mines we placed, and return if we reached our limit
 +      if(WEP_CVAR(minelayer, limit))
 +      {
 +              if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
 +              {
 +                      // the refire delay keeps this message from being spammed
 +                      sprint(self, strcat("minelayer: You cannot place more than ^2", ftos(WEP_CVAR(minelayer, limit)), " ^7mines at a time\n") );
 +                      play2(self, "weapons/unavailable.wav");
 +                      return;
 +              }
 +      }
 +
 +      W_DecreaseAmmo(WEP_CVAR(minelayer, ammo));
 +
 +      W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 5, "weapons/mine_fire.wav", CH_WEAPON_A, WEP_CVAR(minelayer, damage));
 +      pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      mine = WarpZone_RefSys_SpawnSameRefSys(self);
 +      mine.owner = mine.realowner = self;
 +      if(WEP_CVAR(minelayer, detonatedelay) >= 0)
 +              mine.spawnshieldtime = time + WEP_CVAR(minelayer, detonatedelay);
 +      else
 +              mine.spawnshieldtime = -1;
 +      mine.classname = "mine";
 +      mine.bot_dodge = TRUE;
 +      mine.bot_dodgerating = WEP_CVAR(minelayer, damage) * 2; // * 2 because it can detonate inflight which makes it even more dangerous
 +
 +      mine.takedamage = DAMAGE_YES;
 +      mine.damageforcescale = WEP_CVAR(minelayer, damageforcescale);
 +      mine.health = WEP_CVAR(minelayer, health);
 +      mine.event_damage = W_MineLayer_Damage;
 +      mine.damagedbycontents = TRUE;
 +
 +      mine.movetype = MOVETYPE_TOSS;
 +      PROJECTILE_MAKETRIGGER(mine);
 +      mine.projectiledeathtype = WEP_MINE_LAYER;
 +      setsize(mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot
 +
 +      setorigin(mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point
 +      W_SetupProjVelocity_Basic(mine, WEP_CVAR(minelayer, speed), 0);
 +      mine.angles = vectoangles(mine.velocity);
 +
 +      mine.touch = W_MineLayer_Touch;
 +      mine.think = W_MineLayer_Think;
 +      mine.nextthink = time;
 +      mine.cnt = time + (WEP_CVAR(minelayer, lifetime) - WEP_CVAR(minelayer, lifetime_countdown));
 +      mine.flags = FL_PROJECTILE;
 +      mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY;
 +
 +      CSQCProjectile(mine, TRUE, PROJECTILE_MINE, TRUE);
 +
 +      // muzzle flash for 1st person view
 +      flash = spawn();
 +      setmodel(flash, "models/flash.md3"); // precision set below
 +      SUB_SetFade(flash, time, 0.1);
 +      flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 +      W_AttachToShotorg(flash, '5 0 0');
 +
 +      // common properties
 +
 +      other = mine; MUTATOR_CALLHOOK(EditProjectile);
 +      
 +      self.minelayer_mines = W_MineLayer_Count(self);
 +}
 +
 +float W_MineLayer_PlacedMines(float detonate)
 +{
 +      entity mine;
 +      float minfound = 0;
 +
 +      for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == self)
 +      {
 +              if(detonate)
 +              {
 +                      if(!mine.minelayer_detonate)
 +                      {
 +                              mine.minelayer_detonate = TRUE;
 +                              minfound = 1;
 +                      }
 +              }
 +              else
 +                      minfound = 1;
 +      }
 +      return minfound;
 +}
 +
 +float W_MineLayer(float req)
 +{
 +      entity mine;
 +      float ammo_amount;
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      // aim and decide to fire if appropriate
 +                      if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
 +                              self.BUTTON_ATCK = FALSE;
 +                      else
 +                              self.BUTTON_ATCK = bot_aim(WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), FALSE);
 +                      if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
 +                      {
 +                              // decide whether to detonate mines
 +                              entity targetlist, targ;
 +                              float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
 +                              float selfdamage, teamdamage, enemydamage;
 +                              edgedamage = WEP_CVAR(minelayer, edgedamage);
 +                              coredamage = WEP_CVAR(minelayer, damage);
 +                              edgeradius = WEP_CVAR(minelayer, radius);
 +                              recipricoledgeradius = 1 / edgeradius;
 +                              selfdamage = 0;
 +                              teamdamage = 0;
 +                              enemydamage = 0;
 +                              targetlist = findchainfloat(bot_attack, TRUE);
 +                              mine = find(world, classname, "mine");
 +                              while(mine)
 +                              {
 +                                      if(mine.realowner != self)
 +                                      {
 +                                              mine = find(mine, classname, "mine");
 +                                              continue;
 +                                      }
 +                                      targ = targetlist;
 +                                      while(targ)
 +                                      {
 +                                              d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin);
 +                                              d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
 +                                              // count potential damage according to type of target
 +                                              if(targ == self)
 +                                                      selfdamage = selfdamage + d;
 +                                              else if(targ.team == self.team && teamplay)
 +                                                      teamdamage = teamdamage + d;
 +                                              else if(bot_shouldattack(targ))
 +                                                      enemydamage = enemydamage + d;
 +                                              targ = targ.chain;
 +                                      }
 +                                      mine = find(mine, classname, "mine");
 +                              }
 +                              float desirabledamage;
 +                              desirabledamage = enemydamage;
 +                              if(time > self.invincible_finished && time > self.spawnshieldtime)
 +                                      desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent;
 +                              if(teamplay && self.team)
 +                                      desirabledamage = desirabledamage - teamdamage;
 +
 +                              mine = find(world, classname, "mine");
 +                              while(mine)
 +                              {
 +                                      if(mine.realowner != self)
 +                                      {
 +                                              mine = find(mine, classname, "mine");
 +                                              continue;
 +                                      }
 +                                      makevectors(mine.v_angle);
 +                                      targ = targetlist;
 +                                      if(skill > 9) // normal players only do this for the target they are tracking
 +                                      {
 +                                              targ = targetlist;
 +                                              while(targ)
 +                                              {
 +                                                      if(
 +                                                              (v_forward * normalize(mine.origin - targ.origin)< 0.1)
 +                                                              && desirabledamage > 0.1*coredamage
 +                                                      )self.BUTTON_ATCK2 = TRUE;
 +                                                      targ = targ.chain;
 +                                              }
 +                                      }else{
 +                                              float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
 +                                              //As the distance gets larger, a correct detonation gets near imposible
 +                                              //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
 +                                              if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1)
 +                                                      if(IS_PLAYER(self.enemy))
 +                                                              if(desirabledamage >= 0.1*coredamage)
 +                                                                      if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
 +                                                                              self.BUTTON_ATCK2 = TRUE;
 +                                      //      dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
 +                                      }
 +
 +                                      mine = find(mine, classname, "mine");
 +                              }
 +                              // if we would be doing at X percent of the core damage, detonate it
 +                              // but don't fire a new shot at the same time!
 +                              if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
 +                                      self.BUTTON_ATCK2 = TRUE;
 +                              if((skill > 6.5) && (selfdamage > self.health))
 +                                      self.BUTTON_ATCK2 = FALSE;
 +                              //if(self.BUTTON_ATCK2 == TRUE)
 +                              //      dprint(ftos(desirabledamage),"\n");
 +                              if(self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE;
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if(autocvar_g_balance_minelayer_reload_ammo && self.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
 +                      {
 +                              // not if we're holding the minelayer without enough ammo, but can detonate existing mines
 +                              if(!(W_MineLayer_PlacedMines(FALSE) && self.WEP_AMMO(MINE_LAYER) < WEP_CVAR(minelayer, ammo)))
 +                                      WEP_ACTION(self.weapon, WR_RELOAD);
 +                      }
 +                      else if(self.BUTTON_ATCK)
 +                      {
 +                              if(weapon_prepareattack(0, WEP_CVAR(minelayer, refire)))
 +                              {
 +                                      W_MineLayer_Attack();
 +                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
 +                              }
 +                      }
 +
 +                      if(self.BUTTON_ATCK2)
 +                      {
 +                              if(W_MineLayer_PlacedMines(TRUE))
 +                                      sound(self, CH_WEAPON_B, "weapons/mine_det.wav", VOL_BASE, ATTN_NORM);
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model("models/flash.md3");
 +                      precache_model("models/mine.md3");
 +                      precache_model("models/weapons/g_minelayer.md3");
 +                      precache_model("models/weapons/v_minelayer.md3");
 +                      precache_model("models/weapons/h_minelayer.iqm");
 +                      precache_sound("weapons/mine_det.wav");
 +                      precache_sound("weapons/mine_fire.wav");
 +                      precache_sound("weapons/mine_stick.wav");
 +                      precache_sound("weapons/mine_trigger.wav");
 +                      MINELAYER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              {
 +                      // don't switch while placing a mine
 +                      if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER)
 +                      {
 +                              ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo);
 +                              ammo_amount += self.(weapon_load[WEP_MINE_LAYER]) >= WEP_CVAR(minelayer, ammo);
 +                              return ammo_amount;
 +                      }
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO2:
 +              {
 +                      if(W_MineLayer_PlacedMines(FALSE))
 +                              return TRUE;
 +                      else
 +                              return FALSE;
 +              }
 +              case WR_CONFIG:
 +              {
 +                      MINELAYER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
 +                      return TRUE;
 +              }
 +              case WR_RESETPLAYER:
 +              {
 +                      self.minelayer_mines = 0;
 +                      return TRUE;
 +              }
 +              case WR_RELOAD:
 +              {
 +                      W_Reload(WEP_CVAR(minelayer, ammo), "weapons/reload.wav");
 +                      return TRUE;
 +              }
 +              case WR_SUICIDEMESSAGE:
 +              {
 +                      return WEAPON_MINELAYER_SUICIDE;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      return WEAPON_MINELAYER_MURDER;
 +              }
 +      }
 +      return FALSE;
 +}
 +#endif
 +#ifdef CSQC
 +float W_MineLayer(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      org2 = w_org + w_backoff * 12;
 +                      pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
 +                      if(!w_issilent)
 +                              sound(self, CH_SHOTS, "weapons/mine_exp.wav", VOL_BASE, ATTN_NORM);
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/mine_exp.wav");
 +                      return TRUE;
 +              }
 +              case WR_ZOOMRETICLE:
 +              {
 +                      // no weapon specific image for this weapon
 +                      return FALSE;
 +              }
 +      }
 +      return FALSE;
 +}
 +#endif
 +#endif
index 6f2ee96b0e8df86ad16be2e8df0c140ecc92cf92,055f5449f302319ed6c230e017f306345e95dc58..2b4bb6b58c1a39b6594f38c8fca904067df33cd9
@@@ -29,7 -29,7 +29,7 @@@ string WeaponArenaString(
        s = cvar_string("g_weaponarena");
        if(s == "0")
                return "";
-       if(s == "all")
+       if(s == "all" || s == "1")
                return _("All Weapons Arena");
        if(s == "most")
                return _("Most Weapons Arena");
@@@ -130,7 -130,7 +130,7 @@@ float checkCompatibility_newtoys(entit
                return 0;
        if(cvar_string("g_weaponarena") == "most")
                return 1;
-       if(cvar_string("g_weaponarena") == "all")
+       if(cvar_string("g_weaponarena") == "all" || cvar_string("g_weaponarena") == "1")
                return 1;
        if(cvar_string("g_weaponarena") != "0")
                return 0;
@@@ -142,11 -142,11 +142,11 @@@ float checkCompatibility_weaponarena_we
                return 0;
        if(cvar_string("g_weaponarena") == "most")
                return 0;
-       if(cvar_string("g_weaponarena") == "all")
+       if(cvar_string("g_weaponarena") == "all" || cvar_string("g_weaponarena") == "1")
                return 0;
        if(cvar_string("g_weaponarena") == "0")
                return 0;
 -      if(cvar_string("g_start_weapon_laser") == "0")
 +      if(cvar_string("g_balance_blaster_weaponstart") == "0")
                return 0;
        return 1;
  }
@@@ -263,9 -263,9 +263,9 @@@ void XonoticMutatorsDialog_fill(entity 
                        setDependent(e, "g_nix", 1, 1);
        me.TR(me);
                me.TDempty(me, 0.2);
 -              me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons")));
 +              me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_balance_blaster_weaponstart", "0", _("No start weapons")));
                        e.cvarOffValue = "-1";
 -                      makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_porto g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_hlac g_start_weapon_rifle g_start_weapon_fireball g_start_weapon_seeker g_start_weapon_tuba");
 +                      makeMulti(e, "g_balance_shotgun_weaponstart g_balance_machinegun_weaponstart g_balance_devastator_weaponstart g_balance_minelayer_weaponstart g_balance_electro_weaponstart g_balance_crylink_weaponstart g_balance_hagar_weaponstart g_balance_porto_weaponstart g_balance_vaporizer_weaponstart g_balance_hook_weaponstart g_balance_rifle_weaponstart g_balance_fireball_weaponstart g_balance_seeker_weaponstart g_balance_tuba_weaponstart g_balance_shockwave_weaponstart g_balance_arc_weaponstart g_balance_vortex_weaponstart g_balance_mortar_weaponstart");
  
        me.gotoRC(me, me.rows - 1, 0);
                me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
index 717a41ae1f9242e4c2dae8b856fd2b21431a8706,0b5a17f6824dbaccab8db861d777ef44a6025496..22ab63181bc12d490834b02a9a8f49912a9d8953
@@@ -80,6 -80,33 +80,6 @@@ float autocvar_g_balance_armor_rot
  float autocvar_g_balance_armor_rotlinear;
  float autocvar_g_balance_armor_rotstable;
  float autocvar_g_balance_armor_start;
 -float autocvar_g_balance_rifle_bursttime;
 -float autocvar_g_balance_rifle_primary_ammo;
 -float autocvar_g_balance_rifle_primary_animtime;
 -float autocvar_g_balance_rifle_primary_bullethail;
 -float autocvar_g_balance_rifle_primary_burstcost;
 -float autocvar_g_balance_rifle_primary_damage;
 -float autocvar_g_balance_rifle_primary_force;
 -float autocvar_g_balance_rifle_primary_refire;
 -float autocvar_g_balance_rifle_primary_shots;
 -float autocvar_g_balance_rifle_primary_solidpenetration;
 -float autocvar_g_balance_rifle_primary_spread;
 -float autocvar_g_balance_rifle_primary_tracer;
 -float autocvar_g_balance_rifle_secondary;
 -float autocvar_g_balance_rifle_secondary_ammo;
 -float autocvar_g_balance_rifle_secondary_animtime;
 -float autocvar_g_balance_rifle_secondary_bullethail;
 -float autocvar_g_balance_rifle_secondary_burstcost;
 -float autocvar_g_balance_rifle_secondary_damage;
 -float autocvar_g_balance_rifle_secondary_force;
 -float autocvar_g_balance_rifle_secondary_reload;
 -float autocvar_g_balance_rifle_secondary_refire;
 -float autocvar_g_balance_rifle_secondary_shots;
 -float autocvar_g_balance_rifle_secondary_solidpenetration;
 -float autocvar_g_balance_rifle_secondary_spread;
 -float autocvar_g_balance_rifle_secondary_tracer;
 -float autocvar_g_balance_rifle_reload_ammo;
 -float autocvar_g_balance_rifle_reload_time;
  float autocvar_g_balance_cloaked_alpha;
  float autocvar_g_balance_contents_damagerate;
  float autocvar_g_balance_contents_drowndelay;
@@@ -87,11 -114,134 +87,11 @@@ float autocvar_g_balance_contents_playe
  float autocvar_g_balance_contents_playerdamage_lava;
  float autocvar_g_balance_contents_playerdamage_slime;
  float autocvar_g_balance_contents_projectiledamage;
 -float autocvar_g_balance_crylink_primary_ammo;
 -float autocvar_g_balance_crylink_primary_animtime;
 -float autocvar_g_balance_crylink_primary_bouncedamagefactor;
 -float autocvar_g_balance_crylink_primary_bounces;
 -float autocvar_g_balance_crylink_primary_damage;
 -float autocvar_g_balance_crylink_primary_edgedamage;
 -float autocvar_g_balance_crylink_primary_force;
 -float autocvar_g_balance_crylink_primary_joindelay;
 -float autocvar_g_balance_crylink_primary_joinexplode;
 -float autocvar_g_balance_crylink_primary_joinexplode_damage;
 -float autocvar_g_balance_crylink_primary_joinexplode_edgedamage;
 -float autocvar_g_balance_crylink_primary_joinexplode_force;
 -float autocvar_g_balance_crylink_primary_joinexplode_radius;
 -float autocvar_g_balance_crylink_primary_joinspread;
 -float autocvar_g_balance_crylink_primary_linkexplode;
 -float autocvar_g_balance_crylink_primary_middle_fadetime;
 -float autocvar_g_balance_crylink_primary_middle_lifetime;
 -float autocvar_g_balance_crylink_primary_other_fadetime;
 -float autocvar_g_balance_crylink_primary_other_lifetime;
 -float autocvar_g_balance_crylink_primary_radius;
 -float autocvar_g_balance_crylink_primary_refire;
 -float autocvar_g_balance_crylink_primary_shots;
 -float autocvar_g_balance_crylink_primary_speed;
 -float autocvar_g_balance_crylink_primary_spread;
 -float autocvar_g_balance_crylink_secondary;
 -float autocvar_g_balance_crylink_secondary_ammo;
 -float autocvar_g_balance_crylink_secondary_animtime;
 -float autocvar_g_balance_crylink_secondary_bouncedamagefactor;
 -float autocvar_g_balance_crylink_secondary_bounces;
 -float autocvar_g_balance_crylink_secondary_damage;
 -float autocvar_g_balance_crylink_secondary_edgedamage;
 -float autocvar_g_balance_crylink_secondary_force;
 -float autocvar_g_balance_crylink_secondary_joindelay;
 -float autocvar_g_balance_crylink_secondary_joinexplode;
 -float autocvar_g_balance_crylink_secondary_joinexplode_damage;
 -float autocvar_g_balance_crylink_secondary_joinexplode_edgedamage;
 -float autocvar_g_balance_crylink_secondary_joinexplode_force;
 -float autocvar_g_balance_crylink_secondary_joinexplode_radius;
 -float autocvar_g_balance_crylink_secondary_joinspread;
 -float autocvar_g_balance_crylink_secondary_line_fadetime;
 -float autocvar_g_balance_crylink_secondary_line_lifetime;
 -float autocvar_g_balance_crylink_secondary_linkexplode;
 -float autocvar_g_balance_crylink_secondary_middle_fadetime;
 -float autocvar_g_balance_crylink_secondary_middle_lifetime;
 -float autocvar_g_balance_crylink_secondary_radius;
 -float autocvar_g_balance_crylink_secondary_refire;
 -float autocvar_g_balance_crylink_secondary_shots;
 -float autocvar_g_balance_crylink_secondary_speed;
 -float autocvar_g_balance_crylink_secondary_spread;
 -float autocvar_g_balance_crylink_secondary_spreadtype;
 -float autocvar_g_balance_crylink_reload_ammo;
 -float autocvar_g_balance_crylink_reload_time;
  float autocvar_g_balance_damagepush_speedfactor;
 -float autocvar_g_balance_electro_combo_comboradius;
 -float autocvar_g_balance_electro_combo_damage;
 -float autocvar_g_balance_electro_combo_edgedamage;
 -float autocvar_g_balance_electro_combo_force;
 -float autocvar_g_balance_electro_combo_radius;
 -float autocvar_g_balance_electro_combo_speed;
 -float autocvar_g_balance_electro_combo_safeammocheck;
 -float autocvar_g_balance_electro_lightning;
 -float autocvar_g_balance_electro_primary_ammo;
 -float autocvar_g_balance_electro_primary_animtime;
 -float autocvar_g_balance_electro_primary_comboradius;
 -float autocvar_g_balance_electro_primary_damage;
 -float autocvar_g_balance_electro_primary_edgedamage;
 -float autocvar_g_balance_electro_primary_falloff_halflifedist;
 -float autocvar_g_balance_electro_primary_falloff_maxdist;
 -float autocvar_g_balance_electro_primary_falloff_mindist;
 -float autocvar_g_balance_electro_primary_force;
 -float autocvar_g_balance_electro_primary_force_up;
 -float autocvar_g_balance_electro_primary_lifetime;
 -float autocvar_g_balance_electro_primary_radius;
 -float autocvar_g_balance_electro_primary_range;
 -float autocvar_g_balance_electro_primary_refire;
 -float autocvar_g_balance_electro_primary_speed;
 -float autocvar_g_balance_electro_secondary_ammo;
 -float autocvar_g_balance_electro_secondary_animtime;
 -float autocvar_g_balance_electro_secondary_bouncefactor;
 -float autocvar_g_balance_electro_secondary_bouncestop;
 -float autocvar_g_balance_electro_secondary_count;
 -float autocvar_g_balance_electro_secondary_damage;
 -float autocvar_g_balance_electro_secondary_damageforcescale;
 -float autocvar_g_balance_electro_secondary_damagedbycontents;
 -float autocvar_g_balance_electro_secondary_edgedamage;
 -float autocvar_g_balance_electro_secondary_force;
 -float autocvar_g_balance_electro_secondary_health;
 -float autocvar_g_balance_electro_secondary_lifetime;
 -float autocvar_g_balance_electro_secondary_radius;
 -float autocvar_g_balance_electro_secondary_refire;
 -float autocvar_g_balance_electro_secondary_refire2;
 -float autocvar_g_balance_electro_secondary_speed;
 -float autocvar_g_balance_electro_reload_ammo;
 -float autocvar_g_balance_electro_reload_time;
  float autocvar_g_balance_falldamage_deadminspeed;
  float autocvar_g_balance_falldamage_factor;
  float autocvar_g_balance_falldamage_maxdamage;
  float autocvar_g_balance_falldamage_minspeed;
 -float autocvar_g_balance_fireball_primary_animtime;
 -float autocvar_g_balance_fireball_primary_bfgdamage;
 -float autocvar_g_balance_fireball_primary_bfgforce;
 -float autocvar_g_balance_fireball_primary_bfgradius;
 -float autocvar_g_balance_fireball_primary_damage;
 -float autocvar_g_balance_fireball_primary_damageforcescale;
 -float autocvar_g_balance_fireball_primary_edgedamage;
 -float autocvar_g_balance_fireball_primary_force;
 -float autocvar_g_balance_fireball_primary_health;
 -float autocvar_g_balance_fireball_primary_laserburntime;
 -float autocvar_g_balance_fireball_primary_laserdamage;
 -float autocvar_g_balance_fireball_primary_laseredgedamage;
 -float autocvar_g_balance_fireball_primary_laserradius;
 -float autocvar_g_balance_fireball_primary_lifetime;
 -float autocvar_g_balance_fireball_primary_radius;
 -float autocvar_g_balance_fireball_primary_refire;
 -float autocvar_g_balance_fireball_primary_refire2;
 -float autocvar_g_balance_fireball_primary_speed;
 -float autocvar_g_balance_fireball_secondary_animtime;
 -float autocvar_g_balance_fireball_secondary_damage;
 -float autocvar_g_balance_fireball_secondary_damageforcescale;
 -float autocvar_g_balance_fireball_secondary_damagetime;
 -float autocvar_g_balance_fireball_secondary_laserburntime;
 -float autocvar_g_balance_fireball_secondary_laserdamage;
 -float autocvar_g_balance_fireball_secondary_laseredgedamage;
 -float autocvar_g_balance_fireball_secondary_laserradius;
 -float autocvar_g_balance_fireball_secondary_lifetime;
 -float autocvar_g_balance_fireball_secondary_refire;
 -float autocvar_g_balance_fireball_secondary_speed;
 -float autocvar_g_balance_fireball_secondary_speed_up;
  float autocvar_g_balance_firetransfer_damage;
  float autocvar_g_balance_firetransfer_time;
  float autocvar_g_balance_fuel_limit;
@@@ -111,6 -261,75 +111,6 @@@ float autocvar_g_balance_grapplehook_sp
  float autocvar_g_balance_grapplehook_stretch;
  float autocvar_g_balance_grapplehook_damagedbycontents;
  float autocvar_g_balance_grapplehook_refire;
 -float autocvar_g_balance_grenadelauncher_bouncefactor;
 -float autocvar_g_balance_grenadelauncher_bouncestop;
 -float autocvar_g_balance_grenadelauncher_primary_ammo;
 -float autocvar_g_balance_grenadelauncher_primary_animtime;
 -float autocvar_g_balance_grenadelauncher_primary_damage;
 -float autocvar_g_balance_grenadelauncher_primary_damageforcescale;
 -float autocvar_g_balance_grenadelauncher_primary_edgedamage;
 -float autocvar_g_balance_grenadelauncher_primary_force;
 -float autocvar_g_balance_grenadelauncher_primary_health;
 -float autocvar_g_balance_grenadelauncher_primary_lifetime;
 -float autocvar_g_balance_grenadelauncher_primary_lifetime_stick;
 -float autocvar_g_balance_grenadelauncher_primary_radius;
 -float autocvar_g_balance_grenadelauncher_primary_refire;
 -float autocvar_g_balance_grenadelauncher_primary_remote_minbouncecnt;
 -float autocvar_g_balance_grenadelauncher_primary_speed;
 -float autocvar_g_balance_grenadelauncher_primary_speed_up;
 -float autocvar_g_balance_grenadelauncher_primary_type;
 -float autocvar_g_balance_grenadelauncher_secondary_ammo;
 -float autocvar_g_balance_grenadelauncher_secondary_animtime;
 -float autocvar_g_balance_grenadelauncher_secondary_damage;
 -float autocvar_g_balance_grenadelauncher_secondary_damageforcescale;
 -float autocvar_g_balance_grenadelauncher_secondary_edgedamage;
 -float autocvar_g_balance_grenadelauncher_secondary_force;
 -float autocvar_g_balance_grenadelauncher_secondary_health;
 -float autocvar_g_balance_grenadelauncher_secondary_lifetime;
 -float autocvar_g_balance_grenadelauncher_secondary_lifetime_bounce;
 -float autocvar_g_balance_grenadelauncher_secondary_lifetime_stick;
 -float autocvar_g_balance_grenadelauncher_secondary_radius;
 -float autocvar_g_balance_grenadelauncher_secondary_refire;
 -float autocvar_g_balance_grenadelauncher_secondary_speed;
 -float autocvar_g_balance_grenadelauncher_secondary_speed_up;
 -float autocvar_g_balance_grenadelauncher_secondary_type;
 -float autocvar_g_balance_grenadelauncher_reload_ammo;
 -float autocvar_g_balance_grenadelauncher_reload_time;
 -float autocvar_g_balance_hagar_primary_ammo;
 -float autocvar_g_balance_hagar_primary_damage;
 -float autocvar_g_balance_hagar_primary_edgedamage;
 -float autocvar_g_balance_hagar_primary_force;
 -float autocvar_g_balance_hagar_primary_health;
 -float autocvar_g_balance_hagar_primary_damageforcescale;
 -float autocvar_g_balance_hagar_primary_lifetime;
 -float autocvar_g_balance_hagar_primary_radius;
 -float autocvar_g_balance_hagar_primary_refire;
 -float autocvar_g_balance_hagar_primary_speed;
 -float autocvar_g_balance_hagar_secondary;
 -float autocvar_g_balance_hagar_secondary_load;
 -float autocvar_g_balance_hagar_secondary_load_speed;
 -float autocvar_g_balance_hagar_secondary_load_spread;
 -float autocvar_g_balance_hagar_secondary_load_spread_bias;
 -float autocvar_g_balance_hagar_secondary_load_max;
 -float autocvar_g_balance_hagar_secondary_load_hold;
 -float autocvar_g_balance_hagar_secondary_load_releasedeath;
 -float autocvar_g_balance_hagar_secondary_load_abort;
 -float autocvar_g_balance_hagar_secondary_load_linkexplode;
 -float autocvar_g_balance_hagar_secondary_load_animtime;
 -float autocvar_g_balance_hagar_secondary_ammo;
 -float autocvar_g_balance_hagar_secondary_damage;
 -float autocvar_g_balance_hagar_secondary_edgedamage;
 -float autocvar_g_balance_hagar_secondary_force;
 -float autocvar_g_balance_hagar_secondary_health;
 -float autocvar_g_balance_hagar_secondary_damageforcescale;
 -float autocvar_g_balance_hagar_secondary_lifetime_min;
 -float autocvar_g_balance_hagar_secondary_lifetime_rand;
 -float autocvar_g_balance_hagar_secondary_radius;
 -float autocvar_g_balance_hagar_secondary_refire;
 -float autocvar_g_balance_hagar_secondary_speed;
 -float autocvar_g_balance_hagar_secondary_spread;
 -float autocvar_g_balance_hagar_reload_ammo;
 -float autocvar_g_balance_hagar_reload_time;
  float autocvar_g_balance_health_limit;
  float autocvar_g_balance_health_regen;
  float autocvar_g_balance_health_regenlinear;
@@@ -118,6 -337,54 +118,6 @@@ float autocvar_g_balance_health_regenst
  float autocvar_g_balance_health_rot;
  float autocvar_g_balance_health_rotlinear;
  float autocvar_g_balance_health_rotstable;
 -float autocvar_g_balance_hlac_primary_ammo;
 -float autocvar_g_balance_hlac_primary_animtime;
 -float autocvar_g_balance_hlac_primary_damage;
 -float autocvar_g_balance_hlac_primary_edgedamage;
 -float autocvar_g_balance_hlac_primary_force;
 -float autocvar_g_balance_hlac_primary_lifetime;
 -float autocvar_g_balance_hlac_primary_radius;
 -float autocvar_g_balance_hlac_primary_refire;
 -float autocvar_g_balance_hlac_primary_speed;
 -float autocvar_g_balance_hlac_primary_spread_add;
 -float autocvar_g_balance_hlac_primary_spread_crouchmod;
 -float autocvar_g_balance_hlac_primary_spread_max;
 -float autocvar_g_balance_hlac_primary_spread_min;
 -float autocvar_g_balance_hlac_secondary;
 -float autocvar_g_balance_hlac_secondary_ammo;
 -float autocvar_g_balance_hlac_secondary_animtime;
 -float autocvar_g_balance_hlac_secondary_damage;
 -float autocvar_g_balance_hlac_secondary_edgedamage;
 -float autocvar_g_balance_hlac_secondary_force;
 -float autocvar_g_balance_hlac_secondary_lifetime;
 -float autocvar_g_balance_hlac_secondary_radius;
 -float autocvar_g_balance_hlac_secondary_refire;
 -float autocvar_g_balance_hlac_secondary_shots;
 -float autocvar_g_balance_hlac_secondary_speed;
 -float autocvar_g_balance_hlac_secondary_spread;
 -float autocvar_g_balance_hlac_secondary_spread_crouchmod;
 -float autocvar_g_balance_hlac_reload_ammo;
 -float autocvar_g_balance_hlac_reload_time;
 -float autocvar_g_balance_hook_primary_animtime;
 -float autocvar_g_balance_hook_primary_fuel;
 -float autocvar_g_balance_hook_primary_hooked_fuel;
 -float autocvar_g_balance_hook_primary_hooked_time_free;
 -float autocvar_g_balance_hook_primary_hooked_time_max;
 -float autocvar_g_balance_hook_primary_refire;
 -float autocvar_g_balance_hook_secondary_ammo;
 -float autocvar_g_balance_hook_secondary_animtime;
 -float autocvar_g_balance_hook_secondary_damage;
 -float autocvar_g_balance_hook_secondary_duration;
 -float autocvar_g_balance_hook_secondary_edgedamage;
 -float autocvar_g_balance_hook_secondary_force;
 -float autocvar_g_balance_hook_secondary_gravity;
 -float autocvar_g_balance_hook_secondary_lifetime;
 -float autocvar_g_balance_hook_secondary_power;
 -float autocvar_g_balance_hook_secondary_radius;
 -float autocvar_g_balance_hook_secondary_refire;
 -float autocvar_g_balance_hook_secondary_speed;
 -float autocvar_g_balance_hook_secondary_health;
 -float autocvar_g_balance_hook_secondary_damageforcescale;
  float autocvar_g_balance_keyhunt_damageforcescale;
  float autocvar_g_balance_keyhunt_delay_collect;
  float autocvar_g_balance_keyhunt_delay_return;
@@@ -135,6 -402,102 +135,6 @@@ float autocvar_g_balance_keyhunt_score_
  float autocvar_g_balance_keyhunt_throwvelocity;
  float autocvar_g_balance_kill_delay;
  float autocvar_g_balance_kill_antispam;
 -float autocvar_g_balance_laser_primary_animtime;
 -float autocvar_g_balance_laser_primary_damage;
 -float autocvar_g_balance_laser_primary_delay;
 -float autocvar_g_balance_laser_primary_edgedamage;
 -float autocvar_g_balance_laser_primary_force;
 -float autocvar_g_balance_laser_primary_force_other_scale;
 -float autocvar_g_balance_laser_primary_force_velocitybias;
 -float autocvar_g_balance_laser_primary_force_zscale;
 -float autocvar_g_balance_laser_primary_lifetime;
 -float autocvar_g_balance_laser_primary_radius;
 -float autocvar_g_balance_laser_primary_refire;
 -float autocvar_g_balance_laser_primary_shotangle;
 -float autocvar_g_balance_laser_primary_speed;
 -float autocvar_g_balance_laser_secondary;
 -float autocvar_g_balance_laser_secondary_animtime;
 -float autocvar_g_balance_laser_secondary_damage;
 -float autocvar_g_balance_laser_secondary_edgedamage;
 -float autocvar_g_balance_laser_secondary_force;
 -float autocvar_g_balance_laser_secondary_force_other_scale;
 -float autocvar_g_balance_laser_secondary_force_velocitybias;
 -float autocvar_g_balance_laser_secondary_force_zscale;
 -float autocvar_g_balance_laser_secondary_lifetime;
 -float autocvar_g_balance_laser_secondary_radius;
 -float autocvar_g_balance_laser_secondary_speed;
 -float autocvar_g_balance_laser_reload_ammo;
 -float autocvar_g_balance_laser_reload_time;
 -float autocvar_g_balance_minelayer_ammo;
 -float autocvar_g_balance_minelayer_animtime;
 -float autocvar_g_balance_minelayer_damage;
 -float autocvar_g_balance_minelayer_damageforcescale;
 -float autocvar_g_balance_minelayer_detonatedelay;
 -float autocvar_g_balance_minelayer_edgedamage;
 -float autocvar_g_balance_minelayer_force;
 -float autocvar_g_balance_minelayer_health;
 -float autocvar_g_balance_minelayer_lifetime;
 -float autocvar_g_balance_minelayer_lifetime_countdown;
 -float autocvar_g_balance_minelayer_limit;
 -float autocvar_g_balance_minelayer_protection;
 -float autocvar_g_balance_minelayer_proximityradius;
 -float autocvar_g_balance_minelayer_radius;
 -float autocvar_g_balance_minelayer_refire;
 -float autocvar_g_balance_minelayer_remote_damage;
 -float autocvar_g_balance_minelayer_remote_edgedamage;
 -float autocvar_g_balance_minelayer_remote_force;
 -float autocvar_g_balance_minelayer_remote_radius;
 -float autocvar_g_balance_minelayer_speed;
 -float autocvar_g_balance_minelayer_time;
 -float autocvar_g_balance_minelayer_reload_ammo;
 -float autocvar_g_balance_minelayer_reload_time;
 -float autocvar_g_balance_minstanex_ammo;
 -float autocvar_g_balance_minstanex_laser_ammo;
 -float autocvar_g_balance_minstanex_laser_animtime;
 -float autocvar_g_balance_minstanex_laser_refire;
 -float autocvar_g_balance_minstanex_animtime;
 -float autocvar_g_balance_minstanex_refire;
 -float autocvar_g_balance_minstanex_reload_ammo;
 -float autocvar_g_balance_minstanex_reload_time;
 -float autocvar_g_balance_nex_charge;
 -float autocvar_g_balance_nex_charge_animlimit;
 -float autocvar_g_balance_nex_charge_limit;
 -float autocvar_g_balance_nex_charge_maxspeed;
 -float autocvar_g_balance_nex_charge_mindmg;
 -float autocvar_g_balance_nex_charge_minspeed;
 -float autocvar_g_balance_nex_charge_rate;
 -float autocvar_g_balance_nex_charge_rot_pause;
 -float autocvar_g_balance_nex_charge_rot_rate;
 -float autocvar_g_balance_nex_charge_shot_multiplier;
 -float autocvar_g_balance_nex_charge_start;
 -float autocvar_g_balance_nex_charge_velocity_rate;
 -float autocvar_g_balance_nex_primary_ammo;
 -float autocvar_g_balance_nex_primary_animtime;
 -float autocvar_g_balance_nex_primary_damage;
 -float autocvar_g_balance_nex_primary_damagefalloff_forcehalflife;
 -float autocvar_g_balance_nex_primary_damagefalloff_halflife;
 -float autocvar_g_balance_nex_primary_damagefalloff_maxdist;
 -float autocvar_g_balance_nex_primary_damagefalloff_mindist;
 -float autocvar_g_balance_nex_primary_force;
 -float autocvar_g_balance_nex_primary_refire;
 -float autocvar_g_balance_nex_secondary;
 -float autocvar_g_balance_nex_secondary_ammo;
 -float autocvar_g_balance_nex_secondary_animtime;
 -float autocvar_g_balance_nex_secondary_charge;
 -float autocvar_g_balance_nex_secondary_charge_rate;
 -float autocvar_g_balance_nex_secondary_chargepool;
 -float autocvar_g_balance_nex_secondary_chargepool_pause_health_regen;
 -float autocvar_g_balance_nex_secondary_chargepool_pause_regen;
 -float autocvar_g_balance_nex_secondary_chargepool_regen;
 -float autocvar_g_balance_nex_secondary_damage;
 -float autocvar_g_balance_nex_secondary_damagefalloff_forcehalflife;
 -float autocvar_g_balance_nex_secondary_damagefalloff_halflife;
 -float autocvar_g_balance_nex_secondary_damagefalloff_maxdist;
 -float autocvar_g_balance_nex_secondary_damagefalloff_mindist;
 -float autocvar_g_balance_nex_secondary_force;
 -float autocvar_g_balance_nex_secondary_refire;
 -float autocvar_g_balance_nex_reload_ammo;
 -float autocvar_g_balance_nex_reload_time;
  float autocvar_g_balance_nexball_primary_animtime;
  float autocvar_g_balance_nexball_primary_refire;
  float autocvar_g_balance_nexball_primary_speed;
@@@ -144,13 -507,11 +144,13 @@@ float autocvar_g_balance_nexball_second
  float autocvar_g_balance_nexball_secondary_refire;
  float autocvar_g_balance_nexball_secondary_speed;
  float autocvar_g_balance_nix_ammo_cells;
 +float autocvar_g_balance_nix_ammo_plasma;
  float autocvar_g_balance_nix_ammo_fuel;
  float autocvar_g_balance_nix_ammo_nails;
  float autocvar_g_balance_nix_ammo_rockets;
  float autocvar_g_balance_nix_ammo_shells;
  float autocvar_g_balance_nix_ammoincr_cells;
 +float autocvar_g_balance_nix_ammoincr_plasma;
  float autocvar_g_balance_nix_ammoincr_fuel;
  float autocvar_g_balance_nix_ammoincr_nails;
  float autocvar_g_balance_nix_ammoincr_rockets;
@@@ -168,6 -529,15 +168,6 @@@ float autocvar_g_balance_pause_health_r
  float autocvar_g_balance_pause_health_rot_spawn;
  float autocvar_g_balance_portal_health;
  float autocvar_g_balance_portal_lifetime;
 -float autocvar_g_balance_porto_primary_animtime;
 -float autocvar_g_balance_porto_primary_lifetime;
 -float autocvar_g_balance_porto_primary_refire;
 -float autocvar_g_balance_porto_primary_speed;
 -float autocvar_g_balance_porto_secondary;
 -float autocvar_g_balance_porto_secondary_animtime;
 -float autocvar_g_balance_porto_secondary_lifetime;
 -float autocvar_g_balance_porto_secondary_refire;
 -float autocvar_g_balance_porto_secondary_speed;
  float autocvar_g_balance_powerup_invincible_takedamage;
  float autocvar_g_balance_powerup_invincible_time;
  float autocvar_g_balance_powerup_strength_damage;
@@@ -176,10 -546,133 +176,10 @@@ float autocvar_g_balance_powerup_streng
  float autocvar_g_balance_powerup_strength_selfforce;
  float autocvar_g_balance_powerup_strength_time;
  float autocvar_g_balance_superweapons_time;
 -float autocvar_g_balance_rocketlauncher_ammo;
 -float autocvar_g_balance_rocketlauncher_animtime;
 -float autocvar_g_balance_rocketlauncher_damage;
 -float autocvar_g_balance_rocketlauncher_damageforcescale;
 -float autocvar_g_balance_rocketlauncher_detonatedelay;
 -float autocvar_g_balance_rocketlauncher_edgedamage;
 -float autocvar_g_balance_rocketlauncher_force;
 -float autocvar_g_balance_rocketlauncher_guidedelay;
 -float autocvar_g_balance_rocketlauncher_guidegoal;
 -float autocvar_g_balance_rocketlauncher_guiderate;
 -float autocvar_g_balance_rocketlauncher_guideratedelay;
 -float autocvar_g_balance_rocketlauncher_guidestop;
 -float autocvar_g_balance_rocketlauncher_health;
 -float autocvar_g_balance_rocketlauncher_lifetime;
 -float autocvar_g_balance_rocketlauncher_radius;
 -float autocvar_g_balance_rocketlauncher_refire;
 -float autocvar_g_balance_rocketlauncher_remote_damage;
 -float autocvar_g_balance_rocketlauncher_remote_edgedamage;
 -float autocvar_g_balance_rocketlauncher_remote_force;
 -float autocvar_g_balance_rocketlauncher_remote_radius;
 -float autocvar_g_balance_rocketlauncher_speed;
 -float autocvar_g_balance_rocketlauncher_speedaccel;
 -float autocvar_g_balance_rocketlauncher_speedstart;
 -float autocvar_g_balance_rocketlauncher_reload_ammo;
 -float autocvar_g_balance_rocketlauncher_reload_time;
 -float autocvar_g_balance_seeker_type;
 -float autocvar_g_balance_seeker_flac_ammo;
 -float autocvar_g_balance_seeker_flac_animtime;
 -float autocvar_g_balance_seeker_flac_damage;
 -float autocvar_g_balance_seeker_flac_edgedamage;
 -float autocvar_g_balance_seeker_flac_force;
 -float autocvar_g_balance_seeker_flac_lifetime;
 -float autocvar_g_balance_seeker_flac_lifetime_rand;
 -float autocvar_g_balance_seeker_flac_radius;
 -float autocvar_g_balance_seeker_flac_refire;
 -float autocvar_g_balance_seeker_missile_accel;
 -float autocvar_g_balance_seeker_missile_ammo;
 -float autocvar_g_balance_seeker_missile_animtime;
 -float autocvar_g_balance_seeker_missile_count;
 -float autocvar_g_balance_seeker_missile_damage;
 -float autocvar_g_balance_seeker_missile_damageforcescale;
 -float autocvar_g_balance_seeker_missile_decel;
 -float autocvar_g_balance_seeker_missile_delay;
 -float autocvar_g_balance_seeker_missile_edgedamage;
 -float autocvar_g_balance_seeker_missile_force;
 -float autocvar_g_balance_seeker_missile_health;
 -float autocvar_g_balance_seeker_missile_lifetime;
 -float autocvar_g_balance_seeker_missile_proxy;
 -float autocvar_g_balance_seeker_missile_proxy_delay;
 -float autocvar_g_balance_seeker_missile_proxy_maxrange;
 -float autocvar_g_balance_seeker_missile_radius;
 -float autocvar_g_balance_seeker_missile_refire;
 -float autocvar_g_balance_seeker_missile_smart;
 -float autocvar_g_balance_seeker_missile_smart_mindist;
 -float autocvar_g_balance_seeker_missile_smart_trace_max;
 -float autocvar_g_balance_seeker_missile_smart_trace_min;
 -float autocvar_g_balance_seeker_missile_speed_max;
 -float autocvar_g_balance_seeker_missile_turnrate;
 -float autocvar_g_balance_seeker_tag_ammo;
 -float autocvar_g_balance_seeker_tag_animtime;
 -float autocvar_g_balance_seeker_tag_damageforcescale;
 -float autocvar_g_balance_seeker_tag_health;
 -float autocvar_g_balance_seeker_tag_lifetime;
 -float autocvar_g_balance_seeker_tag_refire;
 -float autocvar_g_balance_seeker_tag_speed;
 -float autocvar_g_balance_seeker_tag_tracker_lifetime;
 -float autocvar_g_balance_seeker_reload_ammo;
 -float autocvar_g_balance_seeker_reload_time;
  float autocvar_g_balance_selfdamagepercent;
 -float autocvar_g_balance_shotgun_primary_ammo;
 -float autocvar_g_balance_shotgun_primary_animtime;
 -float autocvar_g_balance_shotgun_primary_bullets;
 -float autocvar_g_balance_shotgun_primary_damage;
 -float autocvar_g_balance_shotgun_primary_force;
 -float autocvar_g_balance_shotgun_primary_refire;
 -float autocvar_g_balance_shotgun_primary_solidpenetration;
 -float autocvar_g_balance_shotgun_primary_spread;
 -float autocvar_g_balance_shotgun_secondary;
 -float autocvar_g_balance_shotgun_secondary_animtime;
 -float autocvar_g_balance_shotgun_secondary_damage;
 -float autocvar_g_balance_shotgun_secondary_force;
 -float autocvar_g_balance_shotgun_secondary_melee_delay;
 -float autocvar_g_balance_shotgun_secondary_melee_range;
 -float autocvar_g_balance_shotgun_secondary_melee_swing_side;
 -float autocvar_g_balance_shotgun_secondary_melee_swing_up;
 -float autocvar_g_balance_shotgun_secondary_melee_time;
 -float autocvar_g_balance_shotgun_secondary_melee_traces;
 -float autocvar_g_balance_shotgun_secondary_melee_no_doubleslap;
 -float autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage;
 -float autocvar_g_balance_shotgun_secondary_melee_multihit;
 -float autocvar_g_balance_shotgun_secondary_refire;
 -float autocvar_g_balance_shotgun_reload_ammo;
 -float autocvar_g_balance_shotgun_reload_time;
  float autocvar_g_balance_teams;
  float autocvar_g_balance_teams_prevent_imbalance;
  float autocvar_g_balance_teams_scorefactor;
 -float autocvar_g_balance_tuba_animtime;
 -float autocvar_g_balance_tuba_attenuation;
 -float autocvar_g_balance_tuba_damage;
 -float autocvar_g_balance_tuba_edgedamage;
 -float autocvar_g_balance_tuba_force;
 -float autocvar_g_balance_tuba_radius;
 -float autocvar_g_balance_tuba_refire;
 -float autocvar_g_balance_uzi_burst;
 -float autocvar_g_balance_uzi_burst_ammo;
 -float autocvar_g_balance_uzi_burst_animtime;
 -float autocvar_g_balance_uzi_burst_refire;
 -float autocvar_g_balance_uzi_burst_refire2;
 -float autocvar_g_balance_uzi_burst_spread;
 -float autocvar_g_balance_uzi_first;
 -float autocvar_g_balance_uzi_first_ammo;
 -float autocvar_g_balance_uzi_first_damage;
 -float autocvar_g_balance_uzi_first_force;
 -float autocvar_g_balance_uzi_first_refire;
 -float autocvar_g_balance_uzi_first_spread;
 -float autocvar_g_balance_uzi_mode;
 -float autocvar_g_balance_uzi_solidpenetration;
 -float autocvar_g_balance_uzi_spread_add;
 -float autocvar_g_balance_uzi_spread_max;
 -float autocvar_g_balance_uzi_spread_min;
 -float autocvar_g_balance_uzi_sustained_ammo;
 -float autocvar_g_balance_uzi_sustained_damage;
 -float autocvar_g_balance_uzi_sustained_force;
 -float autocvar_g_balance_uzi_sustained_refire;
 -float autocvar_g_balance_uzi_sustained_spread;
 -float autocvar_g_balance_uzi_reload_ammo;
 -float autocvar_g_balance_uzi_reload_time;
  float autocvar_g_ballistics_density_corpse;
  float autocvar_g_ballistics_density_player;
  float autocvar_g_ballistics_mindistance;
@@@ -299,6 -792,10 +299,10 @@@ float autocvar_g_domination_disable_fra
  float autocvar_g_domination_point_amt;
  float autocvar_g_domination_point_fullbright;
  float autocvar_g_domination_point_leadlimit;
+ float autocvar_g_domination_roundbased;
+ float autocvar_g_domination_roundbased_point_limit;
+ float autocvar_g_domination_round_timelimit;
+ float autocvar_g_domination_warmup;
  #define autocvar_g_domination_point_limit cvar("g_domination_point_limit")
  float autocvar_g_domination_point_rate;
  float autocvar_g_domination_teams_override;
@@@ -308,10 -805,13 +312,13 @@@ string autocvar_g_forced_team_otherwise
  string autocvar_g_forced_team_pink;
  string autocvar_g_forced_team_red;
  string autocvar_g_forced_team_yellow;
+ float autocvar_g_freezetag_frozen_damage_trigger;
  float autocvar_g_freezetag_frozen_force;
  float autocvar_g_freezetag_frozen_maxtime;
  float autocvar_g_freezetag_revive_falldamage;
  float autocvar_g_freezetag_revive_falldamage_health;
+ float autocvar_g_freezetag_revive_nade;
+ float autocvar_g_freezetag_revive_nade_health;
  float autocvar_g_freezetag_point_leadlimit;
  float autocvar_g_freezetag_point_limit;
  float autocvar_g_freezetag_revive_extra_size;
@@@ -431,7 -931,6 +438,7 @@@ float autocvar_g_onslaught_cp_health
  float autocvar_g_onslaught_cp_regen;
  float autocvar_g_onslaught_gen_health;
  float autocvar_g_pickup_cells_max;
 +float autocvar_g_pickup_plasma_max;
  float autocvar_g_pickup_fuel_max;
  float autocvar_g_pickup_items;
  float autocvar_g_pickup_nails_max;
@@@ -472,6 -971,7 +479,6 @@@ float autocvar_g_spawn_furthest
  float autocvar_g_spawn_useallspawns;
  float autocvar_g_spawnpoints_auto_move_out_of_solid;
  #define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
 -#define autocvar_g_start_weapon_laser cvar("g_start_weapon_laser")
  float autocvar_g_tdm_team_spawns;
  float autocvar_g_tdm_teams;
  float autocvar_g_tdm_teams_override;
@@@ -613,6 -1113,7 +620,7 @@@ float autocvar_sv_dodging_up_speed
  float autocvar_sv_dodging_wall_distance_threshold;
  float autocvar_sv_dodging_wall_dodging;
  float autocvar_sv_dodging_frozen;
+ float autocvar_sv_dodging_frozen_doubletap;
  float autocvar_sv_doublejump;
  float autocvar_sv_eventlog;
  float autocvar_sv_eventlog_console;
@@@ -641,7 -1142,7 +649,7 @@@ float autocvar_sv_maxairstrafespeed
  float autocvar_sv_maxspeed;
  string autocvar_sv_motd;
  float autocvar_sv_precacheplayermodels;
 -float autocvar_sv_precacheweapons;
 +//float autocvar_sv_precacheweapons; // WEAPONTODO?
  float autocvar_sv_q3acompat_machineshotgunswap;
  float autocvar_sv_ready_restart;
  float autocvar_sv_ready_restart_after_countdown;
@@@ -663,6 -1164,11 +671,11 @@@ float autocvar_sv_timeout_resumetime
  float autocvar_sv_vote_call;
  float autocvar_sv_vote_change;
  string autocvar_sv_vote_commands;
+ float autocvar_sv_vote_gametype;
+ float autocvar_sv_vote_gametype_timeout;
+ string autocvar_sv_vote_gametype_options;
+ float autocvar_sv_vote_gametype_keeptwotime;
+ float autocvar_sv_vote_gametype_default_current;
  float autocvar_sv_vote_limit;
  float autocvar_sv_vote_majority_factor;
  float autocvar_sv_vote_majority_factor_of_voted;
@@@ -770,6 -1276,8 +783,8 @@@ float autocvar_g_random_gravity_negativ
  float autocvar_g_random_gravity_delay;
  float autocvar_g_nades;
  float autocvar_g_nades_spawn;
+ float autocvar_g_nades_spawn_count;
+ float autocvar_g_nades_client_select;
  float autocvar_g_nades_nade_lifetime;
  float autocvar_g_nades_nade_minforce;
  float autocvar_g_nades_nade_maxforce;
@@@ -780,6 -1288,44 +795,44 @@@ float autocvar_g_nades_nade_edgedamage
  float autocvar_g_nades_nade_radius;
  float autocvar_g_nades_nade_force;
  float autocvar_g_nades_nade_newton_style;
+ float autocvar_g_nades_napalm_ball_count;
+ float autocvar_g_nades_napalm_ball_spread;
+ float autocvar_g_nades_napalm_ball_damage;
+ float autocvar_g_nades_napalm_ball_damageforcescale;
+ float autocvar_g_nades_napalm_ball_lifetime;
+ float autocvar_g_nades_napalm_ball_radius;
+ float autocvar_g_nades_napalm_blast;
+ float autocvar_g_nades_napalm_fountain_lifetime;
+ float autocvar_g_nades_napalm_fountain_delay;
+ float autocvar_g_nades_napalm_fountain_radius;
+ float autocvar_g_nades_napalm_fountain_damage;
+ float autocvar_g_nades_napalm_fountain_edgedamage;
+ float autocvar_g_nades_napalm_burntime;
+ float autocvar_g_nades_napalm_selfdamage;
+ float autocvar_g_nades_nade_type;
+ float autocvar_g_nades_bonus_type;
+ float autocvar_g_nades_bonus;
+ float autocvar_g_nades_bonus_onstrength;
+ float autocvar_g_nades_bonus_client_select;
+ float autocvar_g_nades_bonus_max;
+ float autocvar_g_nades_bonus_score_max;
+ float autocvar_g_nades_bonus_score_time;
+ float autocvar_g_nades_bonus_score_time_flagcarrier;
+ float autocvar_g_nades_bonus_score_minor;
+ float autocvar_g_nades_bonus_score_low;
+ float autocvar_g_nades_bonus_score_high;
+ float autocvar_g_nades_bonus_score_medium;
+ float autocvar_g_nades_bonus_score_spree;
+ float autocvar_g_nades_ice_freeze_time;
+ float autocvar_g_nades_ice_health;
+ float autocvar_g_nades_ice_explode;
+ float autocvar_g_nades_ice_teamcheck;
+ float autocvar_g_nades_heal_time;
+ float autocvar_g_nades_heal_rate;
+ float autocvar_g_nades_heal_friend;
+ float autocvar_g_nades_heal_foe;
+ string autocvar_g_nades_pokenade_monster_type;
+ float autocvar_g_nades_pokenade_monster_lifetime;
  float autocvar_g_campcheck_damage;
  float autocvar_g_campcheck_distance;
  float autocvar_g_campcheck_interval;
@@@ -790,3 -1336,33 +843,33 @@@ float autocvar_g_spawn_near_teammate_ig
  float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
  float autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
  float autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
+ float autocvar_g_buffs_waypoint_distance;
+ float autocvar_g_buffs_randomize;
+ float autocvar_g_buffs_random_lifetime;
+ float autocvar_g_buffs_random_location;
+ float autocvar_g_buffs_random_location_attempts;
+ float autocvar_g_buffs_spawn_count;
+ float autocvar_g_buffs_replace_powerups;
+ float autocvar_g_buffs_cooldown_activate;
+ float autocvar_g_buffs_cooldown_respawn;
+ float autocvar_g_buffs_resistance_blockpercent;
+ float autocvar_g_buffs_medic_survive_chance;
+ float autocvar_g_buffs_medic_survive_health;
+ float autocvar_g_buffs_medic_rot;
+ float autocvar_g_buffs_medic_max;
+ float autocvar_g_buffs_medic_regen;
+ float autocvar_g_buffs_vengeance_damage_multiplier;
+ float autocvar_g_buffs_bash_force;
+ float autocvar_g_buffs_bash_force_self;
+ float autocvar_g_buffs_disability_time;
+ float autocvar_g_buffs_disability_speed;
+ float autocvar_g_buffs_disability_rate;
+ float autocvar_g_buffs_speed_speed;
+ float autocvar_g_buffs_speed_rate;
+ float autocvar_g_buffs_speed_damage_take;
+ float autocvar_g_buffs_speed_regen;
+ float autocvar_g_buffs_vampire_damage_steal;
+ float autocvar_g_buffs_invisible_alpha;
+ float autocvar_g_buffs_flight_gravity;
+ float autocvar_g_buffs_jump_height;
diff --combined qcsrc/server/bot/aim.qc
index 14e9177c3bea060114ee8fce88b22ae68c43b01d,61f4ab5e8f307b8bc421b4f250090c5169652c0e..0ae33124717ba894248ec42f55b928117baf8b4c
@@@ -111,7 -111,7 +111,7 @@@ float bot_shouldattack(entity e
                        return FALSE;
        }
  
-       if(e.freezetag_frozen)
+       if(e.frozen)
                return FALSE;
  
        // If neither player has ball then don't attack unless the ball is on the
@@@ -339,12 -339,12 +339,12 @@@ float bot_aim(float shotspeed, float sh
        shotspeedupward *= g_weaponspeedfactor;
        if (!shotspeed)
        {
 -              dprint("bot_aim: WARNING: weapon ", W_Name(self.weapon), " shotspeed is zero!\n");
 +              dprint("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " shotspeed is zero!\n");
                shotspeed = 1000000;
        }
        if (!maxshottime)
        {
 -              dprint("bot_aim: WARNING: weapon ", W_Name(self.weapon), " maxshottime is zero!\n");
 +              dprint("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " maxshottime is zero!\n");
                maxshottime = 1;
        }
        makevectors(self.v_angle);
index f682b692630449eef191a779bb9ea8a981e81cd9,2104c3443a00f79930061b0e63975c54c7b6dd33..7e3ddbb4340dd820253fccc87b5d2d2449ae2af1
@@@ -98,10 -98,7 +98,10 @@@ void havocbot_goalrating_items(float ra
                                        if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
                                                continue;
  
 -                                      if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
 +                                      if (head.ammo_cells && player.ammo_cells > self.ammo_cells)
 +                                              continue;
 +
 +                                      if (head.ammo_plasma && player.ammo_plasma > self.ammo_plasma)
                                                continue;
  
                                        discard = FALSE;
@@@ -224,50 -221,11 +224,11 @@@ void havocbot_role_dm(
        }
  }
  
- //Race:
- //go to next checkpoint, and annoy enemies
- .float race_checkpoint;
- void havocbot_role_race()
- {
-       if(self.deadflag != DEAD_NO)
-               return;
-       entity e;
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               /*
-               havocbot_goalrating_items(100, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(500, self.origin, 20000);
-               */
-               for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
-               {
-                       if(e.cnt == self.race_checkpoint)
-                       {
-                               navigation_routerating(e, 1000000, 5000);
-                       }
-                       else if(self.race_checkpoint == -1)
-                       {
-                               navigation_routerating(e, 1000000, 5000);
-                       }
-               }
-               navigation_goalrating_end();
-       }
- }
  void havocbot_chooserole_dm()
  {
        self.havocbot_role = havocbot_role_dm;
  }
  
- void havocbot_chooserole_race()
- {
-       self.havocbot_role = havocbot_role_race;
- }
  void havocbot_chooserole()
  {
        dprint("choosing a role...\n");
                return;
        else if (g_keyhunt)
                havocbot_chooserole_kh();
-       else if (g_race || g_cts)
-               havocbot_chooserole_race();
        else if (g_onslaught)
                havocbot_chooserole_ons();
        else // assume anything else is deathmatch
index c29523bfce730fee240f67ef86c8938a0d6632ca,b3c6b65775d5121c8a6394cd199b51ff3b08363a..2167bdb551f4e9a12f7c0ed624e0f69db63f59fe
@@@ -1,6 -1,3 +1,3 @@@
- void race_send_recordtime(float msg);
- void race_SendRankings(float pos, float prevpos, float del, float msg);
  void send_CSQC_teamnagger() {
        WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
        WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
@@@ -140,7 -137,6 +137,6 @@@ void PutObserverInServer (void
  {
        entity  spot;
      self.hud = HUD_NORMAL;
-       race_PreSpawnObserver();
  
        spot = SelectSpawnPoint (TRUE);
        if(!spot)
                WriteEntity(MSG_ONE, self);
        }
  
-       if((g_race && g_race_qualifying) || g_cts)
-       {
-               if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
-                       self.frags = FRAGS_LMS_LOSER;
-               else
-                       self.frags = FRAGS_SPECTATOR;
-       }
-       else
-               self.frags = FRAGS_SPECTATOR;
+       self.frags = FRAGS_SPECTATOR;
  
        MUTATOR_CALLHOOK(MakePlayerObserver);
  
        Portal_ClearAll(self);
  
+       Unfreeze(self);
        if(self.alivetime)
        {
                if(!warmup_stage)
        self.angles_z = 0;
        self.fixangle = TRUE;
        self.crouch = FALSE;
+       self.revival_time = 0;
  
        setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
        self.prevorigin = self.origin;
@@@ -391,8 -382,6 +382,6 @@@ void PutClientInServer (void
                if(self.team < 0)
                        JoinBestTeam(self, FALSE, TRUE);
  
-               race_PreSpawn();
                spot = SelectSpawnPoint (FALSE);
                if(!spot)
                {
                self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
                self.air_finished = time + 12;
                self.dmg = 2;
 -              if(autocvar_g_balance_nex_charge)
 +              if(WEP_CVAR(vortex, charge))
                {
 -                      if(autocvar_g_balance_nex_secondary_chargepool)
 -                              self.nex_chargepool_ammo = 1;
 -                      self.nex_charge = autocvar_g_balance_nex_charge_start;
 +                      if(WEP_CVAR_SEC(vortex, chargepool))
 +                              self.vortex_chargepool_ammo = 1;
 +                      self.vortex_charge = WEP_CVAR(vortex, charge_start);
                }
  
                if(warmup_stage)
                        self.ammo_nails = warmup_start_ammo_nails;
                        self.ammo_rockets = warmup_start_ammo_rockets;
                        self.ammo_cells = warmup_start_ammo_cells;
 +                      self.ammo_plasma = warmup_start_ammo_plasma;
                        self.ammo_fuel = warmup_start_ammo_fuel;
                        self.health = warmup_start_health;
                        self.armorvalue = warmup_start_armorvalue;
                        self.ammo_nails = start_ammo_nails;
                        self.ammo_rockets = start_ammo_rockets;
                        self.ammo_cells = start_ammo_cells;
 +                      self.ammo_plasma = start_ammo_plasma;
                        self.ammo_fuel = start_ammo_fuel;
                        self.health = start_health;
                        self.armorvalue = start_armorvalue;
                else
                        self.superweapons_finished = 0;
  
 -              if(g_weaponarena_random)
 +              if(g_weaponarena_random) // WEAPONTODO: more stuff that should be in a mutator. also: rename those cvars
                {
                        if(g_weaponarena_random_with_laser)
 -                              self.weapons &= ~WEPSET_LASER;
 +                              self.weapons &= ~WEPSET_BLASTER;
                        W_RandomWeapons(self, g_weaponarena_random);
                        if(g_weaponarena_random_with_laser)
 -                              self.weapons |= WEPSET_LASER;
 +                              self.weapons |= WEPSET_BLASTER;
                }
  
                self.items = start_items;
                self.punchvector = '0 0 0';
                self.oldvelocity = self.velocity;
                self.fire_endtime = -1;
+               self.revival_time = 0;
  
                entity spawnevent = spawn();
                spawnevent.owner = self;
                Net_LinkEntity(spawnevent, FALSE, 0.5, SpawnEvent_Send);
  
+               // Cut off any still running player sounds.
+               stopsound(self, CH_PLAYER_SINGLE);
                self.model = "";
                FixPlayermodel();
                self.drawonlytoclient = world;
  
                self.speedrunning = FALSE;
  
-               race_PostSpawn(spot);
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
  
                // reset fields the weapons may use
                for (j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
 -                      weapon_action(j, WR_RESETPLAYER);
 +                      WEP_ACTION(j, WR_RESETPLAYER);
  
                        // all weapons must be fully loaded when we spawn
                        entity e;
                        e = get_weaponinfo(j);
                        if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
 -                              self.(weapon_load[j]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
 +                              self.(weapon_load[j]) = e.reloading_ammo;
                }
  
                oldself = self;
                        activator = world;
                self = oldself;
  
+               Unfreeze(self);
                spawn_spot = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
  
@@@ -632,27 -623,30 +625,27 @@@ float ClientInit_SendEntity(entity to, 
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[1]));
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[2]));
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[3]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[0]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[1]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[2]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[3]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[0]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[1]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[2]));
 -      WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[3]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2]));
 +      WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3]));
 +
        if(sv_foginterval && world.fog != "")
                WriteString(MSG_ENTITY, world.fog);
        else
                WriteString(MSG_ENTITY, "");
        WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent
 -      WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_bouncefactor
 -      WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_bouncestop
 -      WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_grenadelauncher_bouncefactor
 -      WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_grenadelauncher_bouncestop
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_nex_secondary); // client has to know if it should zoom or not
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_rifle_secondary); // client has to know if it should zoom or not
 +      WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO
 +      WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop
 +      WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor
 +      WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop
 +      WriteByte(MSG_ENTITY, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO
 +      WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO
        WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_minelayer_limit); // minelayer max mines
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_hagar_secondary_load_max); // hagar max loadable rockets
 +      WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO
 +      WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
        WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
 -      WriteByte(MSG_ENTITY, autocvar_g_balance_porto_secondary);
 +      WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO
        return TRUE;
  }
  
@@@ -664,14 -658,14 +657,14 @@@ void ClientInit_CheckUpdate(
                self.count = autocvar_g_balance_armor_blockpercent;
                self.SendFlags |= 1;
        }
 -      if(self.bouncefactor != autocvar_g_balance_grenadelauncher_bouncefactor)
 +      if(self.bouncefactor != autocvar_g_balance_mortar_bouncefactor) // WEAPONTODO
        {
 -              self.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor;
 +              self.bouncefactor = autocvar_g_balance_mortar_bouncefactor;
                self.SendFlags |= 1;
        }
 -      if(self.bouncestop != autocvar_g_balance_grenadelauncher_bouncestop)
 +      if(self.bouncestop != autocvar_g_balance_mortar_bouncestop)
        {
 -              self.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop;
 +              self.bouncestop = autocvar_g_balance_mortar_bouncestop;
                self.SendFlags |= 1;
        }
        if(self.ebouncefactor != autocvar_g_balance_electro_secondary_bouncefactor)
@@@ -940,7 -934,7 +933,7 @@@ void ClientKill (void
  {
        if(gameover) return;
        if(self.player_blocked) return;
-       if(self.freezetag_frozen) return;
+       if(self.frozen) return;
  
        ClientKill_TeamChange(0);
  }
@@@ -1056,8 -1050,6 +1049,6 @@@ void ClientConnect (void
  
        anticheat_init();
  
-       race_PreSpawnObserver();
        // identify the right forced team
        if(autocvar_g_campaign)
        {
        if(!sv_foginterval && world.fog != "")
                stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
  
 -      if(autocvar_g_hitplots || strstrofs(strcat(" ", autocvar_g_hitplots_individuals, " "), strcat(" ", self.netaddress, " "), 0) >= 0)
 -      {
 -              self.hitplotfh = fopen(strcat("hits-", matchid, "-", self.netaddress, "-", ftos(self.playerid), ".plot"), FILE_WRITE);
 -              fputs(self.hitplotfh, strcat("#name ", self.netname, "\n"));
 -      }
 -      else
 -              self.hitplotfh = -1;
 +      W_HitPlotOpen(self);
  
-       if(g_race || g_cts) {
-               string rr;
-               if(g_cts)
-                       rr = CTS_RECORD;
-               else
-                       rr = RACE_RECORD;
-               msg_entity = self;
-               race_send_recordtime(MSG_ONE);
-               race_send_speedaward(MSG_ONE);
-               speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed")));
-               speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp")));
-               race_send_speedaward_alltimebest(MSG_ONE);
-               float i;
-               for (i = 1; i <= RANKINGS_CNT; ++i) {
-                       race_SendRankings(i, 0, 0, MSG_ONE);
-               }
-       }
-       else if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca) // teamnagger is currently bad for ca
+       if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
                send_CSQC_teamnagger();
  
        CheatInitClient();
@@@ -1260,7 -1238,11 +1231,7 @@@ void ClientDisconnect (void
  
        CheatShutdownClient();
  
 -      if(self.hitplotfh >= 0)
 -      {
 -              fclose(self.hitplotfh);
 -              self.hitplotfh = -1;
 -      }
 +      W_HitPlotClose(self);
  
        anticheat_report();
        anticheat_shutdown();
  
        Portal_ClearAll(self);
  
+       Unfreeze(self);
        RemoveGrapplingHook(self);
  
        // Here, everything has been done that requires this player to be a client.
@@@ -1574,17 -1558,27 +1547,27 @@@ float CalcRotRegen(float current, floa
  
  void player_regen (void)
  {
+       float max_mod, regen_mod, rot_mod, limit_mod;
+       max_mod = regen_mod = rot_mod = limit_mod = 1;
+       regen_mod_max = max_mod;
+       regen_mod_regen = regen_mod;
+       regen_mod_rot = rot_mod;
+       regen_mod_limit = limit_mod;
        if(!MUTATOR_CALLHOOK(PlayerRegen))
+       if(!self.frozen)
        {
-               float minh, mina, maxh, maxa, limith, limita, max_mod, regen_mod, rot_mod, limit_mod;
+               float minh, mina, maxh, maxa, limith, limita;
                maxh = autocvar_g_balance_health_rotstable;
                maxa = autocvar_g_balance_armor_rotstable;
                minh = autocvar_g_balance_health_regenstable;
                mina = autocvar_g_balance_armor_regenstable;
                limith = autocvar_g_balance_health_limit;
                limita = autocvar_g_balance_armor_limit;
-               max_mod = regen_mod = rot_mod = limit_mod = 1;
+               
+               max_mod = regen_mod_max;
+               regen_mod = regen_mod_regen;
+               rot_mod = regen_mod_rot;
+               limit_mod = regen_mod_limit;
  
                maxh = maxh * max_mod;
                minh = minh * max_mod;
@@@ -1688,7 -1682,6 +1671,7 @@@ void SpectateCopy(entity spectatee) 
        self.armortype = spectatee.armortype;
        self.armorvalue = spectatee.armorvalue;
        self.ammo_cells = spectatee.ammo_cells;
 +      self.ammo_plasma = spectatee.ammo_plasma;
        self.ammo_shells = spectatee.ammo_shells;
        self.ammo_nails = spectatee.ammo_nails;
        self.ammo_rockets = spectatee.ammo_rockets;
        self.switchweapon = spectatee.switchweapon;
        self.switchingweapon = spectatee.switchingweapon;
        self.weapon = spectatee.weapon;
 -      self.nex_charge = spectatee.nex_charge;
 -      self.nex_chargepool_ammo = spectatee.nex_chargepool_ammo;
 +      self.vortex_charge = spectatee.vortex_charge;
 +      self.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
        self.hagar_load = spectatee.hagar_load;
        self.minelayer_mines = spectatee.minelayer_mines;
        self.punchangle = spectatee.punchangle;
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.v_angle = spectatee.v_angle;
        self.angles = spectatee.v_angle;
+       self.frozen = spectatee.frozen;
+       self.revive_progress = spectatee.revive_progress;
        if(!self.BUTTON_USE)
                self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
@@@ -1926,6 -1921,7 +1911,7 @@@ void LeaveSpectatorMode(
                if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
                {
                        self.classname = "player";
+                       nades_RemoveBonus(self);
  
                        if(autocvar_g_campaign || autocvar_g_balance_teams)
                                { JoinBestTeam(self, FALSE, TRUE); }
@@@ -2231,6 -2227,30 +2217,30 @@@ void PlayerPreThink (void
                return;
  #endif
  
+       if(self.frozen == 2)
+       {
+               self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
+               self.health = max(1, self.revive_progress * start_health);
+               self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+               if(self.revive_progress >= 1)
+                       Unfreeze(self);
+       }
+       else if(self.frozen == 3)
+       {
+               self.revive_progress = bound(0, self.revive_progress - frametime * self.revive_speed, 1);
+               self.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * self.revive_progress );
+               
+               if(self.health < 1)
+               {
+                       if(self.vehicle)
+                               vehicles_exit(VHEF_RELESE);
+                       self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+               }
+               else if ( self.revive_progress <= 0 )
+                       Unfreeze(self);
+       }
        MUTATOR_CALLHOOK(PlayerPreThink);
  
        if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
  
                if(frametime)
                {
 -                      if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
 +                      if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge))
                        {
 -                              self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 -                              self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 -                              self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
 +                              self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
 +                              self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
 +                              self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
  
 -                              if(self.nex_charge > autocvar_g_balance_nex_charge_animlimit)
 +                              if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit))
                                {
 -                                      self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
 -                                      self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
 -                                      self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
 +                                      self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
 +                                      self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
 +                                      self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
                                }
                        }
                        else
                        do_crouch = 0;
                if(self.vehicle)
                        do_crouch = 0;
-               if(self.freezetag_frozen)
+               if(self.frozen)
                        do_crouch = 0;
 -              if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
 +
 +              // WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY
 +              // It cannot be predicted by the engine! 
 +              if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
                        do_crouch = 0;
  
                if (do_crouch)
  
                player_regen();
  
 +              // WEAPONTODO: Add a weapon request for this 
                // rot nex charge to the charge limit
 -              if(autocvar_g_balance_nex_charge_rot_rate && self.nex_charge > autocvar_g_balance_nex_charge_limit && self.nex_charge_rottime < time)
 -                      self.nex_charge = bound(autocvar_g_balance_nex_charge_limit, self.nex_charge - autocvar_g_balance_nex_charge_rot_rate * frametime / W_TICSPERFRAME, 1);
 +              if(WEP_CVAR(vortex, charge_rot_rate) && self.vortex_charge > WEP_CVAR(vortex, charge_limit) && self.vortex_charge_rottime < time)
 +                      self.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), self.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
  
                if(frametime)
                        player_anim();
                SpectatorThink();
        }
  
 +      // WEAPONTODO: Add weapon request for this
        if(!zoomstate_set)
 -              SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_NEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && autocvar_g_balance_rifle_secondary == 0));
 +              SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_VORTEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)); // WEAPONTODO
  
        float oldspectatee_status;
        oldspectatee_status = self.spectatee_status;
        if(self.spectatee_status != oldspectatee_status)
        {
                ClientData_Touch(self);
-               if(g_race || g_cts)
-                       race_InitSpectator();
        }
  
        if(self.teamkill_soundtime)
  
        target_voicescript_next(self);
  
 +      // WEAPONTODO: Move into weaponsystem somehow
        // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
        if(!self.weapon)
                self.clip_load = self.clip_size = 0;
@@@ -2611,22 -2623,5 +2619,5 @@@ void PlayerPostThink (void
  
        playerdemo_write();
  
-       if((g_cts || g_race) && self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1)
-       {
-               if (!self.stored_netname)
-                       self.stored_netname = strzone(uid2name(self.crypto_idfp));
-               if(self.stored_netname != self.netname)
-               {
-                       db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname);
-                       strunzone(self.stored_netname);
-                       self.stored_netname = strzone(self.netname);
-               }
-       }
-       /*
-       if(g_race)
-               dprintf("%f %.6f\n", time, race_GetFractionalLapCount(self));
-       */
        CSQCMODEL_AUTOUPDATE();
  }
index 6f05c456804e73a8ba4371067dd21ff3ad3e3967,14747fa99aabe535d9074e7e03ae6391adcebf80..0cae2a261d8f41f5d9d9998a402e8ef14713b931
@@@ -19,18 -19,22 +19,22 @@@ When you press the jump ke
  */
  void PlayerJump (void)
  {
+       if(self.frozen)
+               return; // no jumping in freezetag when frozen
        if(self.player_blocked)
                return; // no jumping while blocked
  
        float doublejump = FALSE;
+       float mjumpheight = autocvar_sv_jumpvelocity;
  
        player_multijump = doublejump;
+       player_jumpheight = mjumpheight;
        if(MUTATOR_CALLHOOK(PlayerJump))
                return;
  
        doublejump = player_multijump;
-       float mjumpheight;
+       mjumpheight = player_jumpheight;
  
        if (autocvar_sv_doublejump)
        {
@@@ -47,7 -51,6 +51,6 @@@
                }
        }
  
-       mjumpheight = autocvar_sv_jumpvelocity;
        if (self.waterlevel >= WATERLEVEL_SWIMMING)
        {
                self.velocity_z = self.stat_sv_maxspeed * 0.7;
@@@ -792,6 -795,28 +795,28 @@@ void SV_PlayerPhysics(
                self.stat_sv_airspeedlimit_nonqw *= 0.5;
        }
  
+       if(self.frozen)
+       {
+               if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
+               {
+                       self.movement_x = bound(-5, self.movement_x, 5);
+                       self.movement_y = bound(-5, self.movement_y, 5);
+                       self.movement_z = bound(-5, self.movement_z, 5);
+               }
+               else
+                       self.movement = '0 0 0';
+               self.disableclientprediction = 1;
+               vector midpoint = ((self.absmin + self.absmax) * 0.5);
+               if(pointcontents(midpoint) == CONTENT_WATER)
+               {
+                       self.velocity = self.velocity * 0.5;
+                       if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                               { self.velocity_z = 200; }
+               }
+       }
        MUTATOR_CALLHOOK(PlayerPhysics);
  
        if(self.player_blocked)
                        PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
                }
        }
-       else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.freezetag_frozen)
+       else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.frozen)
        {
                //makevectors(self.v_angle_y * '0 1 0');
                makevectors(self.v_angle);
                }
        }
  
-       if((g_cts || g_race) && !IS_OBSERVER(self)) {
-               if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed) {
+       if((g_cts || g_race) && !IS_OBSERVER(self))
+       {
+               if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed)
+               {
                        speedaward_speed = vlen(self.velocity - self.velocity_z * '0 0 1');
                        speedaward_holder = self.netname;
                        speedaward_uid = self.crypto_idfp;
                        speedaward_lastupdate = time;
                }
-               if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) {
-                       string rr;
-                       if(g_cts)
-                               rr = CTS_RECORD;
-                       else
-                               rr = RACE_RECORD;
+               if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1)
+               {
+                       string rr = (g_cts) ? CTS_RECORD : RACE_RECORD;
                        race_send_speedaward(MSG_ALL);
                        speedaward_lastsent = speedaward_speed;
-                       if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") {
+                       if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "")
+                       {
                                speedaward_alltimebest = speedaward_speed;
                                speedaward_alltimebest_holder = speedaward_holder;
                                speedaward_alltimebest_uid = speedaward_uid;
                }
        }
  
 +      // WEAPONTODO
        float xyspeed;
        xyspeed = vlen('1 0 0' * self.velocity_x + '0 1 0' * self.velocity_y);
 -      if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge && autocvar_g_balance_nex_charge_velocity_rate && xyspeed > autocvar_g_balance_nex_charge_minspeed)
 +      if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge) && WEP_CVAR(vortex, charge_velocity_rate) && xyspeed > WEP_CVAR(vortex, charge_minspeed))
        {
                // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed
 -              xyspeed = min(xyspeed, autocvar_g_balance_nex_charge_maxspeed);
 -              f = (xyspeed - autocvar_g_balance_nex_charge_minspeed) / (autocvar_g_balance_nex_charge_maxspeed - autocvar_g_balance_nex_charge_minspeed);
 +              xyspeed = min(xyspeed, WEP_CVAR(vortex, charge_maxspeed));
 +              f = (xyspeed - WEP_CVAR(vortex, charge_minspeed)) / (WEP_CVAR(vortex, charge_maxspeed) - WEP_CVAR(vortex, charge_minspeed));
                // add the extra charge
 -              self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_velocity_rate * f * frametime);
 +              self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_velocity_rate) * f * frametime);
        }
  :end
        if(self.flags & FL_ONGROUND)
index 630cad61fbc1f0c7bde31a9fc6800f4e3a79ca06,c11e92051074b7fcbf15b208b774f14d96c00d88..7d7ba087aba536ccb0bba51873a2afc2ec1ae13c
@@@ -1,3 -1,126 +1,3 @@@
 -.entity accuracy;
 -.float accuracy_frags[WEP_MAXCOUNT];
 -
 -float weaponstats_buffer;
 -
 -void WeaponStats_Init()
 -{
 -      if(autocvar_sv_weaponstats_file != "")
 -              weaponstats_buffer = buf_create();
 -      else
 -              weaponstats_buffer = -1;
 -}
 -
 -#define WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot) (((vwep) + (awep) * (WEP_LAST - WEP_FIRST + 1) - (WEP_FIRST + WEP_FIRST * (WEP_LAST - WEP_FIRST + 1))) * 4 + (abot) * 2 + (vbot))
 -
 -void WeaponStats_ready(entity fh, entity pass, float status)
 -{
 -      float i, j, n, ibot, jbot, idx;
 -      vector v;
 -      string prefix, s;
 -      switch(status)
 -      {
 -              case URL_READY_CANWRITE:
 -                      // we can write
 -                      prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t");
 -                      url_fputs(fh, "#begin statsfile\n");
 -                      url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
 -#ifdef WATERMARK
 -                      url_fputs(fh, strcat("#version ", WATERMARK, "\n"));
 -#endif
 -                      url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n"));
 -                      url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n"));
 -                      n = tokenizebyseparator(cvar_purechanges, "\n");
 -                      for(i = 0; i < n; ++i)
 -                              url_fputs(fh, strcat("#cvar_purechange ", argv(i), "\n"));
 -                      for(i = WEP_FIRST; i <= WEP_LAST; ++i) for(ibot = 0; ibot <= 1; ++ibot)
 -                              for(j = WEP_FIRST; j <= WEP_LAST; ++j) for(jbot = 0; jbot <= 1; ++jbot)
 -                              {
 -                                      idx = WEAPONSTATS_GETINDEX(i, ibot, j, jbot);
 -                                      v = stov(bufstr_get(weaponstats_buffer, idx));
 -                                      if(v != '0 0 0')
 -                                      {
 -                                              //vector is: kills hits damage
 -                                              url_fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot));
 -                                              url_fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z));
 -                                      }
 -                              }
 -                      url_fputs(fh, "#end\n\n");
 -                      url_fclose(fh);
 -                      break;
 -              case URL_READY_CANREAD:
 -                      // url_fclose is processing, we got a response for writing the data
 -                      // this must come from HTTP
 -                      print("Got response from weapon stats server:\n");
 -                      while((s = url_fgets(fh)))
 -                              print("  ", s, "\n");
 -                      print("End of response.\n");
 -                      url_fclose(fh);
 -                      break;
 -              case URL_READY_CLOSED:
 -                      // url_fclose has finished
 -                      print("Weapon stats written\n");
 -                      buf_del(weaponstats_buffer);
 -                      weaponstats_buffer = -1;
 -                      break;
 -              case URL_READY_ERROR:
 -              default:
 -                      print("Weapon stats writing failed: ", ftos(status), "\n");
 -                      buf_del(weaponstats_buffer);
 -                      weaponstats_buffer = -1;
 -                      break;
 -      }
 -}
 -
 -void WeaponStats_Shutdown()
 -{
 -      if(weaponstats_buffer < 0)
 -              return;
 -      if(autocvar_sv_weaponstats_file != "")
 -      {
 -              url_multi_fopen(autocvar_sv_weaponstats_file, FILE_APPEND, WeaponStats_ready, world);
 -      }
 -      else
 -      {
 -              buf_del(weaponstats_buffer);
 -              weaponstats_buffer = -1;
 -      }
 -}
 -
 -void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item)
 -{
 -      float idx;
 -      if(weaponstats_buffer < 0)
 -              return;
 -      if(awep < WEP_FIRST || vwep < WEP_FIRST)
 -              return;
 -      if(awep > WEP_LAST || vwep > WEP_LAST)
 -              return;
 -      idx = WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot);
 -      bufstr_set(weaponstats_buffer, idx, vtos(stov(bufstr_get(weaponstats_buffer, idx)) + item));
 -}
 -void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage)
 -{
 -      if(damage < 0)
 -              error("negative damage?");
 -      WeaponStats_LogItem(awep, abot, vwep, vbot, '0 0 1' * damage + '0 1 0');
 -}
 -void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot)
 -{
 -      WeaponStats_LogItem(awep, abot, vwep, vbot, '1 0 0');
 -}
 -
 -// changes by LordHavoc on 03/29/04 and 03/30/04 at Vermeulen's request
 -// merged player_run and player_stand to player_anim
 -// added death animations to player_anim
 -// can now spawn thrown weapons from anywhere, not just from players
 -// thrown weapons now fade out after 20 seconds
 -// created PlayerGib function
 -// PlayerDie no longer uses hitloc or damage
 -// PlayerDie now supports dying animations as well as gibbing
 -// cleaned up PlayerDie a lot
 -// added CopyBody
 -
  .entity pusher;
  .float pushltime;
  .float istypefrag;
@@@ -123,7 -246,7 +123,7 @@@ void player_anim (void
                else
                        deadbits = ANIMSTATE_DEAD2;
        float animbits = deadbits;
-       if(self.freezetag_frozen)
+       if(self.frozen)
                animbits |= ANIMSTATE_FROZEN;
        if(self.crouch)
                animbits |= ANIMSTATE_DUCK;
        }
  }
  
 -void SpawnThrownWeapon (vector org, float w)
 -{
 -      if(self.weapons & WepSet_FromWeapon(self.weapon))
 -              if(W_IsWeaponThrowable(self.weapon))
 -                      W_ThrowNewWeapon(self, self.weapon, FALSE, org, randomvec() * 125 + '0 0 200');
 -}
 -
  void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
        float take, save;
@@@ -284,7 -414,7 +284,7 @@@ void calculate_player_respawn_time(
        else
                self.respawn_countdown = -1; // do not count down
  
-       if(g_cts || autocvar_g_forced_respawn)
+       if(autocvar_g_forced_respawn)
                self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
  }
  
@@@ -424,7 -554,7 +424,7 @@@ void PlayerDamage (entity inflictor, en
                                        }
  
                                        if(sound_allowed(MSG_BROADCAST, attacker))
 -                                      if(!DEATH_ISWEAPON(deathtype, WEP_LASER) || attacker != self || self.health < 2 * autocvar_g_balance_laser_primary_damage * autocvar_g_balance_selfdamagepercent + 1)
 +                                      if(!DEATH_ISWEAPON(deathtype, WEP_BLASTER) || attacker != self || self.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) // WEAPONTODO: create separate limit for pain notification with laser
                                        if(self.health > 1)
                                        // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
                                        {
  
                // print an obituary message
                Obituary (attacker, inflictor, self, deathtype);
-               race_PreDie();
  
          // increment frag counter for used weapon type
          float w;
                frag_deathtype = deathtype;
                MUTATOR_CALLHOOK(PlayerDies);
  
 -              weapon_action(self.weapon, WR_PLAYERDEATH);
 +              WEP_ACTION(self.weapon, WR_PLAYERDEATH);
  
                RemoveGrapplingHook(self);
  
  
                // when we get here, player actually dies
  
+               Unfreeze(self); // remove any icy remains
+               self.health = 0; // Unfreeze resets health, so we need to set it back
                // clear waypoints
                WaypointSprite_PlayerDead();
                // throw a weapon
                // reset fields the weapons may use just in case
                for (j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
 -                      weapon_action(j, WR_RESETPLAYER);
 +                      WEP_ACTION(j, WR_RESETPLAYER);
                        ATTACK_FINISHED_FOR(self, j) = 0;
                }
        }
diff --combined qcsrc/server/defs.qh
index 4edfa586ae7bf14488dd18517d2f2e5f68ed4542,dd0b5c22bb13792e6ffc89c4a98b302cbed6b02a..0d03d52cccc51322dda1a9b36699fd6cc21a8245
@@@ -20,7 -20,6 +20,6 @@@ float g_cloaked, g_footsteps, g_grappli
  float g_warmup_limit;
  float g_warmup_allguns;
  float g_warmup_allow_timeout;
- float g_race_qualifying;
  float warmup_stage;
  float g_pickup_respawntime_weapon;
  float g_pickup_respawntime_superweapon;
@@@ -173,7 -172,7 +172,7 @@@ void setanim(entity e, vector anim, flo
  .string item_pickupsound;
  
  // definitions for weaponsystem
 -
 +// more WEAPONTODO: move these to their proper files
  .entity weaponentity;
  .entity exteriorweaponentity;
  .vector weaponentity_glowmod;
  .float switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible)
  .string weaponname; // name of .weapon
  
 +// WEAPONTODO
  .float autoswitch;
 -float weapon_action(float wpn, float wrequest);
 +//float WEP_ACTION(float wpn, float wrequest);
  float client_hasweapon(entity cl, float wpn, float andammo, float complain);
  void w_clear();
  void w_ready();
  .float weapon_nextthink;
  .void() weapon_think;
  
 -//float       PLAYER_WEAPONSELECTION_DELAY = );
 -const float   PLAYER_WEAPONSELECTION_SPEED = 18;
 -const vector  PLAYER_WEAPONSELECTION_RANGE = '0 20 -40';
  
  // weapon states (self.weaponentity.state)
  const float WS_CLEAR                  = 0; // no weapon selected
@@@ -287,12 -288,16 +286,12 @@@ string gamemode_name
  
  float startitem_failed;
  
 -typedef .float floatfield;
 -floatfield Item_CounterField(float it);
 -
 -float W_AmmoItemCode(float wpn);
 -string W_Name(float weaponid);
  string W_Apply_Weaponreplace(string in);
  
  void FixIntermissionClient(entity e);
  void FixClientCvars(entity e);
  
 +// WEAPONTODO: remove this
  WepSet weaponsInMap;
  
  .float respawn_countdown; // next number to count
@@@ -507,6 -512,7 +506,6 @@@ float servertime, serverprevtime, serve
  .float stat_shotorg; // networked stat for trueaim HUD
  
  string matchid;
 -.float hitplotfh;
  
  .float last_pickup;
  
@@@ -522,6 -528,8 +521,6 @@@ float client_cefc_accumulator
  float client_cefc_accumulatortime;
  #endif
  
 -..float current_ammo;
 -
  .float weapon_load[WEP_MAXCOUNT];
  .float ammo_none; // used by the reloading system, must always be 0
  .float clip_load;
  
  .entity lastrocket;
  .float minelayer_mines;
 -.float nex_charge;
 -.float nex_charge_rottime;
 -.float nex_chargepool_ammo;
 +.float vortex_charge;
 +.float vortex_charge_rottime;
 +.float vortex_chargepool_ammo;
  .float hagar_load;
  
  .float grab; // 0 = can't grab, 1 = owner can grab, 2 = owner and team mates can grab, 3 = anyone can grab
@@@ -552,6 -560,9 +551,6 @@@ string deathmessage
  
  .float just_joined;
  
 -.float cvar_cl_accuracy_data_share;
 -.float cvar_cl_accuracy_data_receive;
 -
  .float cvar_cl_weaponimpulsemode;
  .float selectweapon; // last selected weapon of the player
  
@@@ -572,7 -583,12 +571,12 @@@ float serverflags
  
  .float player_blocked;
  
- .float freezetag_frozen;
+ .float frozen; // for freeze attacks
+ .float revive_progress;
+ .float revival_time; // time at which player was last revived
+ .float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
+ .entity iceblock;
+ .entity frozen_by; // for ice fields
  
  .entity muzzle_flash;
  .float misc_bulletcounter;    // replaces uzi & hlac bullet counter.
diff --combined qcsrc/server/g_damage.qc
index eadba3c16290355b9a88fd1939a924da5c83908a,47443e6d70f34acfdc175d3a16efb71a6823ef18..ec8333070a905b759d4d4dc9fb56b86169ba6095
@@@ -115,7 -115,7 +115,7 @@@ void GiveFrags (entity attacker, entit
                else if(!(attacker.weapons & WepSet_FromWeapon(culprit)))
                        culprit = attacker.weapon;
  
 -              if(g_weaponarena_random_with_laser && culprit == WEP_LASER)
 +              if(g_weaponarena_random_with_laser && culprit == WEP_BLASTER) // WEAPONTODO: Shouldn't this be in a mutator?
                {
                        // no exchange
                }
@@@ -287,7 -287,7 +287,7 @@@ float Obituary_WeaponDeath
        if(death_weapon)
        {
                w_deathtype = deathtype;
 -              float death_message = weapon_action(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE));
 +              float death_message = WEP_ACTION(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE));
                w_deathtype = FALSE;
  
                if(death_message)
@@@ -549,6 -549,86 +549,86 @@@ void Obituary(entity attacker, entity i
        if(targ.killcount) { targ.killcount = 0; }
  }
  
+ void Ice_Think()
+ {
+       if(!self.owner.frozen || self.owner.iceblock != self)
+       {
+               remove(self);
+               return;
+       }
+       setorigin(self, self.owner.origin - '0 0 16');
+       self.nextthink = time;
+ }
+ void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint)
+ {
+       if(!IS_PLAYER(targ) && !(targ.flags & FL_MONSTER)) // only specified entities can be freezed
+               return;
+       if(targ.frozen)
+               return;
+       float targ_maxhealth = ((targ.flags & FL_MONSTER) ? targ.max_health : start_health);
+       targ.frozen = frozen_type;
+       targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
+       targ.health = ((frozen_type == 3) ? targ_maxhealth : 1);
+       targ.revive_speed = freeze_time;
+       entity ice, head;
+       ice = spawn();
+       ice.owner = targ;
+       ice.classname = "ice";
+       ice.scale = targ.scale;
+       ice.think = Ice_Think;
+       ice.nextthink = time;
+       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+       setmodel(ice, "models/ice/ice.md3");
+       ice.alpha = 1;
+       ice.colormod = Team_ColorRGB(targ.team);
+       ice.glowmod = ice.colormod;
+       targ.iceblock = ice;
+       targ.revival_time = 0;
+       entity oldself;
+       oldself = self;
+       self = ice;
+       Ice_Think();
+       self = oldself;
+       RemoveGrapplingHook(targ);
+       FOR_EACH_PLAYER(head)
+       if(head.hook.aiment == targ)
+               RemoveGrapplingHook(head);
+       // add waypoint
+       if(show_waypoint)       
+               WaypointSprite_Spawn("frozen", 0, 0, targ, '0 0 64', world, targ.team, targ, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
+ }
+ void Unfreeze (entity targ)
+ {
+       if(targ.frozen && targ.frozen != 3) // only reset health if target was frozen
+               targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health);
+       entity head;
+       targ.frozen = 0;
+       targ.revive_progress = 0;
+       targ.revival_time = time;
+       
+       WaypointSprite_Kill(targ.waypointsprite_attached);
+       
+       FOR_EACH_PLAYER(head)
+       if(head.hook.aiment == targ)
+               RemoveGrapplingHook(head);
+       // remove the ice block
+       if(targ.iceblock)
+               remove(targ.iceblock);
+       targ.iceblock = world;
+ }
  // these are updated by each Damage call for use in button triggering and such
  entity damage_targ;
  entity damage_inflictor;
@@@ -690,7 -770,63 +770,63 @@@ void Damage (entity targ, entity inflic
                mirrordamage = frag_mirrordamage;
                force = frag_force;
  
-               if (!g_minstagib)
+               if(targ.frozen)
+               if(deathtype != DEATH_HURTTRIGGER && deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_AUTOTEAMCHANGE)
+               {
+                       if(autocvar_g_freezetag_revive_falldamage > 0)
+                       if(deathtype == DEATH_FALL)
+                       if(damage >= autocvar_g_freezetag_revive_falldamage)
+                       {
+                               Unfreeze(targ);
+                               targ.health = autocvar_g_freezetag_revive_falldamage_health;
+                               pointparticles(particleeffectnum("iceorglass"), targ.origin, '0 0 0', 3);
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
+                               Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+                       }
+                       damage = 0;
+                       force *= autocvar_g_freezetag_frozen_force;
+               }
+               
+               if(targ.frozen && deathtype == DEATH_HURTTRIGGER && !autocvar_g_freezetag_frozen_damage_trigger)
+               {
+                       pointparticles(particleeffectnum("teleport"), targ.origin, '0 0 0', 1);
+               
+                       entity oldself = self;
+                       self = targ;
+                       entity spot = SelectSpawnPoint (FALSE);
+                       
+                       if(spot)
+                       {
+                               damage = 0;
+                               self.deadflag = DEAD_NO;
+                               self.angles = spot.angles;
+                               
+                               self.effects = 0;
+                               self.effects |= EF_TELEPORT_BIT;
+                               self.angles_z = 0; // never spawn tilted even if the spot says to
+                               self.fixangle = TRUE; // turn this way immediately
+                               self.velocity = '0 0 0';
+                               self.avelocity = '0 0 0';
+                               self.punchangle = '0 0 0';
+                               self.punchvector = '0 0 0';
+                               self.oldvelocity = self.velocity;
+                               
+                               self.spawnorigin = spot.origin;
+                               setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
+                               // don't reset back to last position, even if new position is stuck in solid
+                               self.oldorigin = self.origin;
+                               self.prevorigin = self.origin;
+                               
+                               pointparticles(particleeffectnum("teleport"), self.origin, '0 0 0', 1);
+                       }
+                       
+                       self = oldself;
+               }
+               if(!g_minstagib)
                {
                        // apply strength multiplier
                        if (attacker.items & IT_STRENGTH)
                }
  
                if (targ == attacker)
-               {
-                       if(g_cts && !autocvar_g_cts_selfdamage)
-                               damage = 0;
-                       else
-                               damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
-               }
+                       damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
  
                // count the damage
                if(attacker)
  }
  
  float RadiusDamage_running;
 -float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity)
 +float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float deathtype, entity directhitentity)
        // Returns total damage applies to creatures
  {
        entity  targ;
 -      vector  blastorigin;
        vector  force;
        float   total_damage_to_creatures;
        entity  next;
        tfloordmg = autocvar_g_throughfloor_damage;
        tfloorforce = autocvar_g_throughfloor_force;
  
 -      blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5);
        total_damage_to_creatures = 0;
  
        if(deathtype != (WEP_HOOK | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once
                if(DEATH_WEAPONOF(deathtype) != WEP_TUBA) // do not send tuba damage (bandwidth hog)
                {
 -                      force = inflictor.velocity;
 +                      force = inflictorvelocity;
                        if(vlen(force) == 0)
                                force = '0 0 -1';
                        else
                                force = normalize(force);
                        if(forceintensity >= 0)
 -                              Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
 +                              Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
                        else
 -                              Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
 +                              Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
                }
  
        stat_damagedone = 0;
  
 -      targ = WarpZone_FindRadius (blastorigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE);
 +      targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE);
        while (targ)
        {
                next = targ.chain;
 -              if (targ != inflictor)
 -                      if (ignore != targ) if(targ.takedamage)
 +              if ((targ != inflictor) || inflictorselfdamage)
 +              if (((cantbe != targ) && !mustbe) || (mustbe == targ))
 +              if (targ.takedamage)
 +              {
 +                      vector nearest;
 +                      vector diff;
 +                      float power;
 +
 +                      // LordHavoc: measure distance to nearest point on target (not origin)
 +                      // (this guarentees 100% damage on a touch impact)
 +                      nearest = targ.WarpZone_findradius_nearest;
 +                      diff = targ.WarpZone_findradius_dist;
 +                      // round up a little on the damage to ensure full damage on impacts
 +                      // and turn the distance into a fraction of the radius
 +                      power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
 +                      //bprint(" ");
 +                      //bprint(ftos(power));
 +                      //if (targ == attacker)
 +                      //      print(ftos(power), "\n");
 +                      if (power > 0)
                        {
 -                              vector nearest;
 -                              vector diff;
 -                              float power;
 -
 -                              // LordHavoc: measure distance to nearest point on target (not origin)
 -                              // (this guarentees 100% damage on a touch impact)
 -                              nearest = targ.WarpZone_findradius_nearest;
 -                              diff = targ.WarpZone_findradius_dist;
 -                              // round up a little on the damage to ensure full damage on impacts
 -                              // and turn the distance into a fraction of the radius
 -                              power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
 -                              //bprint(" ");
 -                              //bprint(ftos(power));
 -                              //if (targ == attacker)
 -                              //      print(ftos(power), "\n");
 -                              if (power > 0)
 +                              float finaldmg;
 +                              if (power > 1)
 +                                      power = 1;
 +                              finaldmg = coredamage * power + edgedamage * (1 - power);
 +                              if (finaldmg > 0)
                                {
 -                                      float finaldmg;
 -                                      if (power > 1)
 -                                              power = 1;
 -                                      finaldmg = coredamage * power + edgedamage * (1 - power);
 -                                      if (finaldmg > 0)
 -                                      {
 -                                              float a;
 -                                              float c;
 -                                              vector hitloc;
 -                                              vector myblastorigin;
 -                                              vector center;
 +                                      float a;
 +                                      float c;
 +                                      vector hitloc;
 +                                      vector myblastorigin;
 +                                      vector center;
  
 -                                              myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
 +                                      myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
  
 -                                              // if it's a player, use the view origin as reference
 -                                              center = CENTER_OR_VIEWOFS(targ);
 +                                      // if it's a player, use the view origin as reference
 +                                      center = CENTER_OR_VIEWOFS(targ);
  
 -                                              force = normalize(center - myblastorigin);
 -                                              force = force * (finaldmg / coredamage) * forceintensity;
 -                                              hitloc = nearest;
 +                                      force = normalize(center - myblastorigin);
 +                                      force = force * (finaldmg / coredamage) * forceintensity;
 +                                      hitloc = nearest;
  
 -                                              if(targ != directhitentity)
 -                                              {
 -                                                      float hits;
 -                                                      float total;
 -                                                      float hitratio;
 -                                                      float mininv_f, mininv_d;
 +                                      if(deathtype & WEP_BLASTER)
 +                                              force *= WEP_CVAR_BOTH(blaster, !(deathtype & HITTYPE_SECONDARY), force_zscale);
  
 -                                                      // test line of sight to multiple positions on box,
 -                                                      // and do damage if any of them hit
 -                                                      hits = 0;
 +                                      if(targ != directhitentity)
 +                                      {
 +                                              float hits;
 +                                              float total;
 +                                              float hitratio;
 +                                              float mininv_f, mininv_d;
  
 -                                                      // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
 -                                                      // so for a given max stddev:
 -                                                      // n = (1 / (2 * max stddev of hitratio))^2
 +                                              // test line of sight to multiple positions on box,
 +                                              // and do damage if any of them hit
 +                                              hits = 0;
  
 -                                                      mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
 -                                                      mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
 +                                              // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
 +                                              // so for a given max stddev:
 +                                              // n = (1 / (2 * max stddev of hitratio))^2
  
 -                                                      if(autocvar_g_throughfloor_debug)
 -                                                              printf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
 +                                              mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
 +                                              mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
  
 -                                                      total = 0.25 * pow(max(mininv_f, mininv_d), 2);
 +                                              if(autocvar_g_throughfloor_debug)
 +                                                      printf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
  
 -                                                      if(autocvar_g_throughfloor_debug)
 -                                                              printf(" steps=%f", total);
  
 -                                                      if (IS_PLAYER(targ))
 -                                                              total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
 -                                                      else
 -                                                              total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
 +                                              total = 0.25 * pow(max(mininv_f, mininv_d), 2);
  
 -                                                      if(autocvar_g_throughfloor_debug)
 -                                                              printf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
 +                                              if(autocvar_g_throughfloor_debug)
 +                                                      printf(" steps=%f", total);
  
 -                                                      for(c = 0; c < total; ++c)
 -                                                      {
 -                                                              //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
 -                                                              WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
 -                                                              if (trace_fraction == 1 || trace_ent == targ)
 -                                                              {
 -                                                                      ++hits;
 -                                                                      if (hits > 1)
 -                                                                              hitloc = hitloc + nearest;
 -                                                                      else
 -                                                                              hitloc = nearest;
 -                                                              }
 -                                                              nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x;
 -                                                              nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y;
 -                                                              nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z;
 -                                                      }
  
 -                                                      nearest = hitloc * (1 / max(1, hits));
 -                                                      hitratio = (hits / total);
 -                                                      a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
 -                                                      finaldmg = finaldmg * a;
 -                                                      a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
 -                                                      force = force * a;
 +                                              if (IS_PLAYER(targ))
 +                                                      total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
 +                                              else
 +                                                      total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
  
 -                                                      if(autocvar_g_throughfloor_debug)
 -                                                              printf(" D=%f F=%f\n", finaldmg, vlen(force));
 -                                              }
 +                                              if(autocvar_g_throughfloor_debug)
 +                                                      printf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
  
 -                                              // laser force adjustments :P
 -                                              if(DEATH_WEAPONOF(deathtype) == WEP_LASER)
 +                                              for(c = 0; c < total; ++c)
                                                {
 -                                                      if (targ == attacker)
 +                                                      //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
 +                                                      WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
 +                                                      if (trace_fraction == 1 || trace_ent == targ)
                                                        {
 -                                                              vector vel;
 -
 -                                                              float force_zscale;
 -                                                              float force_velocitybiasramp;
 -                                                              float force_velocitybias;
 -
 -                                                              force_velocitybiasramp = autocvar_sv_maxspeed;
 -                                                              if(deathtype & HITTYPE_SECONDARY)
 -                                                              {
 -                                                                      force_zscale = autocvar_g_balance_laser_secondary_force_zscale;
 -                                                                      force_velocitybias = autocvar_g_balance_laser_secondary_force_velocitybias;
 -                                                              }
 +                                                              ++hits;
 +                                                              if (hits > 1)
 +                                                                      hitloc = hitloc + nearest;
                                                                else
 -                                                              {
 -                                                                      force_zscale = autocvar_g_balance_laser_primary_force_zscale;
 -                                                                      force_velocitybias = autocvar_g_balance_laser_primary_force_velocitybias;
 -                                                              }
 -
 -                                                              vel = targ.velocity;
 -                                                              vel_z = 0;
 -                                                              vel = normalize(vel) * bound(0, vlen(vel) / force_velocitybiasramp, 1) * force_velocitybias;
 -                                                              force =
 -                                                                      vlen(force)
 -                                                                      *
 -                                                                      normalize(normalize(force) + vel);
 -
 -                                                              force_z *= force_zscale;
 -                                                      }
 -                                                      else
 -                                                      {
 -                                                              if(deathtype & HITTYPE_SECONDARY)
 -                                                              {
 -                                                                      force *= autocvar_g_balance_laser_secondary_force_other_scale;
 -                                                              }
 -                                                              else
 -                                                              {
 -                                                                      force *= autocvar_g_balance_laser_primary_force_other_scale;
 -                                                              }
 +                                                                      hitloc = nearest;
                                                        }
 +                                                      nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x;
 +                                                      nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y;
 +                                                      nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z;
                                                }
  
 -                                              //if (targ == attacker)
 -                                              //{
 -                                              //      print("hits ", ftos(hits), " / ", ftos(total));
 -                                              //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
 -                                              //      print(" (", ftos(a), ")\n");
 -                                              //}
 -                                              if(finaldmg || vlen(force))
 -                                              {
 -                                                      if(targ.iscreature)
 -                                                      {
 -                                                              total_damage_to_creatures += finaldmg;
 +                                              nearest = hitloc * (1 / max(1, hits));
 +                                              hitratio = (hits / total);
 +                                              a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
 +                                              finaldmg = finaldmg * a;
 +                                              a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
 +                                              force = force * a;
  
 -                                                              if(accuracy_isgooddamage(attacker, targ))
 -                                                                      stat_damagedone += finaldmg;
 -                                                      }
 +                                              if(autocvar_g_throughfloor_debug)
 +                                                      printf(" D=%f F=%f\n", finaldmg, vlen(force));
 +                                      }
  
 -                                                      if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
 -                                                              Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force);
 -                                                      else
 -                                                              Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, nearest, force);
 +                                      //if (targ == attacker)
 +                                      //{
 +                                      //      print("hits ", ftos(hits), " / ", ftos(total));
 +                                      //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
 +                                      //      print(" (", ftos(a), ")\n");
 +                                      //}
 +                                      if(finaldmg || vlen(force))
 +                                      {
 +                                              if(targ.iscreature)
 +                                              {
 +                                                      total_damage_to_creatures += finaldmg;
 +
 +                                                      if(accuracy_isgooddamage(attacker, targ))
 +                                                              stat_damagedone += finaldmg;
                                                }
 +
 +                                              if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
 +                                                      Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force);
 +                                              else
 +                                                      Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, nearest, force);
                                        }
                                }
                        }
 +              }
                targ = next;
        }
  
        return total_damage_to_creatures;
  }
  
 +float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, float deathtype, entity directhitentity)
 +{
 +      return RadiusDamageForSource (inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, FALSE, forceintensity, deathtype, directhitentity);
 +}
 +
  .float fire_damagepersec;
  .float fire_endtime;
  .float fire_deathtype;
@@@ -1163,7 -1331,7 +1294,7 @@@ void Fire_ApplyDamage(entity e
                e.fire_endtime = 0;
  
        // ice stops fire
-       if(e.freezetag_frozen)
+       if(e.frozen)
                e.fire_endtime = 0;
  
        t = min(frametime, e.fire_endtime - time);
        e.fire_hitsound = TRUE;
  
        if (!IS_INDEPENDENT_PLAYER(e))
-       if(!e.freezetag_frozen)
+       if(!e.frozen)
        FOR_EACH_PLAYER(other) if(e != other)
        {
                if(IS_PLAYER(other))
diff --combined qcsrc/server/g_world.qc
index b7c970715fe7e2e175d651bd98fc3b176911a5f6,a1460ded8aa0e5bf6f19952754ba83d3213ee5be..bd685ba1f032c60546b8eac92db68d307665ebae
@@@ -55,7 -55,6 +55,6 @@@ const float SPAWNFLAG_NO_WAYPOINTS_FOR_
  string redirection_target;
  float world_initialized;
  
- string GetMapname();
  string GetGametype();
  void GotoNextMap(float reinit);
  void ShuffleMaplist();
@@@ -216,7 -215,6 +215,6 @@@ void cvar_changes_init(
                // private
                BADCVAR("developer");
                BADCVAR("log_dest_udp");
-               BADCVAR("log_file");
                BADCVAR("net_address");
                BADCVAR("net_address_ipv6");
                BADCVAR("port");
                BADPREFIX("g_playerstats_");
                BADPREFIX("g_respawn_ghosts");
                BADPREFIX("g_voice_flood_");
+               BADPREFIX("log_file");
                BADPREFIX("rcon_");
                BADPREFIX("sv_allowdownloads");
                BADPREFIX("sv_autodemo");
@@@ -546,6 -545,7 +545,7 @@@ void spawnfunc___init_dedicated_server(
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
  
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
@@@ -595,6 -595,7 +595,7 @@@ void spawnfunc_worldspawn (void
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
  
        ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
  
        InitGameplayMode();
        readlevelcvars();
        GrappleHookInit();
 -      ElectroInit();
 -      LaserInit();
  
        player_count = 0;
        bot_waypoints_for_items = autocvar_g_waypoints_for_items;
        addstat(STAT_SUPERWEAPONS_FINISHED, AS_FLOAT, superweapons_finished);
        addstat(STAT_PRESSED_KEYS, AS_FLOAT, pressedkeys);
        addstat(STAT_FUEL, AS_INT, ammo_fuel);
 +      addstat(STAT_PLASMA, AS_INT, ammo_plasma);
        addstat(STAT_SHOTORG, AS_INT, stat_shotorg);
        addstat(STAT_LEADLIMIT, AS_FLOAT, stat_leadlimit);
        addstat(STAT_WEAPON_CLIPLOAD, AS_INT, clip_load);
        addstat(STAT_TYPEHIT_TIME, AS_FLOAT, typehit_time);
        addstat(STAT_LAYED_MINES, AS_INT, minelayer_mines);
  
 -      addstat(STAT_NEX_CHARGE, AS_FLOAT, nex_charge);
 -      addstat(STAT_NEX_CHARGEPOOL, AS_FLOAT, nex_chargepool_ammo);
 +      addstat(STAT_VORTEX_CHARGE, AS_FLOAT, vortex_charge);
 +      addstat(STAT_VORTEX_CHARGEPOOL, AS_FLOAT, vortex_chargepool_ammo);
  
        addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
  
+       // freeze attacks
+       addstat(STAT_FROZEN, AS_INT, frozen);
+       addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
        // g_movementspeed hack
        addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
        addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
@@@ -895,7 -901,6 +900,6 @@@ string GetGametype(
        return MapInfo_Type_ToString(MapInfo_LoadedGametype);
  }
  
- string getmapname_stored;
  string GetMapname()
  {
        return mapname;
@@@ -1220,13 -1225,27 +1224,27 @@@ float DoNextMapOverride(float reinit
                return TRUE;
        }
        if(autocvar_nextmap != "")
-               if(MapInfo_CheckMap(autocvar_nextmap))
+       {
+               string m;
+               m = GameTypeVote_MapInfo_FixName(autocvar_nextmap);
+               cvar_set("nextmap",m);
+       
+               if(!m || gametypevote)
+                       return FALSE;
+               if(autocvar_sv_vote_gametype)
+               {
+                       Map_Goto_SetStr(m);
+                       return FALSE;
+               }
+               
+               if(MapInfo_CheckMap(m))
                {
-                       Map_Goto_SetStr(autocvar_nextmap);
+                       Map_Goto_SetStr(m);
                        Map_Goto(reinit);
                        alreadychangedlevel = TRUE;
                        return TRUE;
                }
+       }
        if(!reinit && autocvar_lastlevel)
        {
                cvar_settemp_restore();
@@@ -1263,9 -1282,6 +1281,6 @@@ When the player presses attack or jump
  ============
  */
  .float autoscreenshot;
- void() MapVote_Start;
- void() MapVote_Think;
- float mapvote_initialized;
  void IntermissionThink()
  {
        FixIntermissionClient(self);
@@@ -2030,17 -2046,6 +2045,6 @@@ void CheckRules_World(
  
        SetDefaultAlpha();
  
-       /*
-       MapVote_Think should now do that part
-       if (intermission_running)
-               if (time >= intermission_exittime + 60)
-               {
-                       if(!DoNextMapOverride())
-                               GotoNextMap();
-                       return;
-               }
-       */
        if (gameover)   // someone else quit the game already
        {
                if(player_count == 0) // Nobody there? Then let's go to the next map
        }
  }
  
- float mapvote_nextthink;
- float mapvote_initialized;
- float mapvote_keeptwotime;
- float mapvote_timeout;
- string mapvote_message;
- #define MAPVOTE_SCREENSHOT_DIRS_COUNT 4
- string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
- float mapvote_screenshot_dirs_count;
- float mapvote_count;
- float mapvote_count_real;
- string mapvote_maps[MAPVOTE_COUNT];
- float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
- string mapvote_maps_pakfile[MAPVOTE_COUNT];
- float mapvote_maps_suggested[MAPVOTE_COUNT];
- string mapvote_suggestions[MAPVOTE_COUNT];
- float mapvote_suggestion_ptr;
- float mapvote_voters;
- float mapvote_selections[MAPVOTE_COUNT];
- float mapvote_run;
- float mapvote_detail;
- float mapvote_abstain;
- .float mapvote;
- void MapVote_ClearAllVotes()
- {
-       FOR_EACH_CLIENT(other)
-               other.mapvote = 0;
- }
- string MapVote_Suggest(string m)
+ string GotoMap(string m)
  {
-       float i;
-       if(m == "")
-               return "That's not how to use this command.";
-       if(!autocvar_g_maplist_votable_suggestions)
-               return "Suggestions are not accepted on this server.";
-       if(mapvote_initialized)
-               return "Can't suggest - voting is already in progress!";
-       m = MapInfo_FixName(m);
+       m = GameTypeVote_MapInfo_FixName(m);
        if (!m)
                return "The map you suggested is not available on this server.";
-       if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
-               if(Map_IsRecent(m))
-                       return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
+       if (!autocvar_sv_vote_gametype)
        if(!MapInfo_CheckMap(m))
                return "The map you suggested does not support the current game mode.";
-       for(i = 0; i < mapvote_suggestion_ptr; ++i)
-               if(mapvote_suggestions[i] == m)
-                       return "This map was already suggested.";
-       if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
-       {
-               i = floor(random() * mapvote_suggestion_ptr);
-       }
-       else
-       {
-               i = mapvote_suggestion_ptr;
-               mapvote_suggestion_ptr += 1;
-       }
-       if(mapvote_suggestions[i] != "")
-               strunzone(mapvote_suggestions[i]);
-       mapvote_suggestions[i] = strzone(m);
-       if(autocvar_sv_eventlog)
-               GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
-       return strcat("Suggestion of ", m, " accepted.");
- }
- void MapVote_AddVotable(string nextMap, float isSuggestion)
- {
-       float j, i, o;
-       string pakfile, mapfile;
-       if(nextMap == "")
-               return;
-       for(j = 0; j < mapvote_count; ++j)
-               if(mapvote_maps[j] == nextMap)
-                       return;
-       // suggestions might be no longer valid/allowed after gametype switch!
-       if(isSuggestion)
-               if(!MapInfo_CheckMap(nextMap))
-                       return;
-       mapvote_maps[mapvote_count] = strzone(nextMap);
-       mapvote_maps_suggested[mapvote_count] = isSuggestion;
-       pakfile = string_null;
-       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
-       {
-               mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
-               pakfile = whichpack(strcat(mapfile, ".tga"));
-               if(pakfile == "")
-                       pakfile = whichpack(strcat(mapfile, ".jpg"));
-               if(pakfile == "")
-                       pakfile = whichpack(strcat(mapfile, ".png"));
-               if(pakfile != "")
-                       break;
-       }
-       if(i >= mapvote_screenshot_dirs_count)
-               i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
-       for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
-               pakfile = substring(pakfile, o, -1);
-       mapvote_maps_screenshot_dir[mapvote_count] = i;
-       mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
-       mapvote_count += 1;
- }
- void MapVote_Spawn();
- void MapVote_Init()
- {
-       float i;
-       float nmax, smax;
-       MapVote_ClearAllVotes();
-       mapvote_count = 0;
-       mapvote_detail = !autocvar_g_maplist_votable_nodetail;
-       mapvote_abstain = autocvar_g_maplist_votable_abstain;
-       if(mapvote_abstain)
-               nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
-       else
-               nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
-       smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
-       // we need this for AddVotable, as that cycles through the screenshot dirs
-       mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
-       if(mapvote_screenshot_dirs_count == 0)
-               mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
-       mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
-       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
-               mapvote_screenshot_dirs[i] = strzone(argv(i));
-       if(mapvote_suggestion_ptr)
-               for(i = 0; i < 100 && mapvote_count < smax; ++i)
-                       MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
-       for(i = 0; i < 100 && mapvote_count < nmax; ++i)
-               MapVote_AddVotable(GetNextMap(), FALSE);
-       if(mapvote_count == 0)
-       {
-               bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
-               cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               localcmd("\nmenu_cmd sync\n");
-               for(i = 0; i < 100 && mapvote_count < nmax; ++i)
-                       MapVote_AddVotable(GetNextMap(), FALSE);
-       }
-       mapvote_count_real = mapvote_count;
-       if(mapvote_abstain)
-               MapVote_AddVotable("don't care", 0);
-       //dprint("mapvote count is ", ftos(mapvote_count), "\n");
-       mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
-       mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
-       if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
-               mapvote_keeptwotime = 0;
-       mapvote_message = "Choose a map and press its key!";
-       MapVote_Spawn();
- }
- void MapVote_SendPicture(float id)
- {
-       msg_entity = self;
-       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-       WriteByte(MSG_ONE, TE_CSQC_PICTURE);
-       WriteByte(MSG_ONE, id);
-       WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
- }
- float MapVote_GetMapMask()
- {
-       float mask, i, power;
-       mask = 0;
-       for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
-               if(mapvote_maps[i] != "")
-                       mask |= power;
-       return mask;
- }
- entity mapvote_ent;
- float MapVote_SendEntity(entity to, float sf)
- {
-       float i;
-       if(sf & 1)
-               sf &= ~2; // if we send 1, we don't need to also send 2
-       WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
-       WriteByte(MSG_ENTITY, sf);
-       if(sf & 1)
-       {
-               // flag 1 == initialization
-               for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
-                       WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
-               WriteString(MSG_ENTITY, "");
-               WriteByte(MSG_ENTITY, mapvote_count);
-               WriteByte(MSG_ENTITY, mapvote_abstain);
-               WriteByte(MSG_ENTITY, mapvote_detail);
-               WriteCoord(MSG_ENTITY, mapvote_timeout);
-               if(mapvote_count <= 8)
-                       WriteByte(MSG_ENTITY, MapVote_GetMapMask());
-               else
-                       WriteShort(MSG_ENTITY, MapVote_GetMapMask());
-               for(i = 0; i < mapvote_count; ++i)
-                       if(mapvote_maps[i] != "")
-                       {
-                               if(mapvote_abstain && i == mapvote_count - 1)
-                               {
-                                       WriteString(MSG_ENTITY, ""); // abstain needs no text
-                                       WriteString(MSG_ENTITY, ""); // abstain needs no pack
-                                       WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
-                               }
-                               else
-                               {
-                                       WriteString(MSG_ENTITY, mapvote_maps[i]);
-                                       WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
-                                       WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
-                               }
-                       }
-       }
-       if(sf & 2)
-       {
-               // flag 2 == update of mask
-               if(mapvote_count <= 8)
-                       WriteByte(MSG_ENTITY, MapVote_GetMapMask());
-               else
-                       WriteShort(MSG_ENTITY, MapVote_GetMapMask());
-       }
-       if(sf & 4)
-       {
-               if(mapvote_detail)
-                       for(i = 0; i < mapvote_count; ++i)
-                               if(mapvote_maps[i] != "")
-                                       WriteByte(MSG_ENTITY, mapvote_selections[i]);
-               WriteByte(MSG_ENTITY, to.mapvote);
-       }
-       return TRUE;
- }
- void MapVote_Spawn()
- {
-       Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
- }
- void MapVote_TouchMask()
- {
-       mapvote_ent.SendFlags |= 2;
- }
- void MapVote_TouchVotes(entity voter)
- {
-       mapvote_ent.SendFlags |= 4;
- }
- float MapVote_Finished(float mappos)
- {
-       string result;
-       float i;
-       float didntvote;
-       if(autocvar_sv_eventlog)
-       {
-               result = strcat(":vote:finished:", mapvote_maps[mappos]);
-               result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
-               didntvote = mapvote_voters;
-               for(i = 0; i < mapvote_count; ++i)
-                       if(mapvote_maps[i] != "")
-                       {
-                               didntvote -= mapvote_selections[i];
-                               if(i != mappos)
-                               {
-                                       result = strcat(result, ":", mapvote_maps[i]);
-                                       result = strcat(result, ":", ftos(mapvote_selections[i]));
-                               }
-                       }
-               result = strcat(result, ":didn't vote:", ftos(didntvote));
-               GameLogEcho(result);
-               if(mapvote_maps_suggested[mappos])
-                       GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
-       }
-       FOR_EACH_REALCLIENT(other)
-               FixClientCvars(other);
-       Map_Goto_SetStr(mapvote_maps[mappos]);
-       Map_Goto(0);
-       alreadychangedlevel = TRUE;
-       return TRUE;
- }
- void MapVote_CheckRules_1()
- {
-       float i;
-       for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
-       {
-               //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
-               mapvote_selections[i] = 0;
-       }
-       mapvote_voters = 0;
-       FOR_EACH_REALCLIENT(other)
-       {
-               ++mapvote_voters;
-               if(other.mapvote)
-               {
-                       i = other.mapvote - 1;
-                       //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
-                       mapvote_selections[i] = mapvote_selections[i] + 1;
-               }
-       }
- }
- float MapVote_CheckRules_2()
- {
-       float i;
-       float firstPlace, secondPlace;
-       float firstPlaceVotes, secondPlaceVotes;
-       float mapvote_voters_real;
-       string result;
-       if(mapvote_count_real == 1)
-               return MapVote_Finished(0);
-       mapvote_voters_real = mapvote_voters;
-       if(mapvote_abstain)
-               mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
-       RandomSelection_Init();
-       for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
-               RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
-       firstPlace = RandomSelection_chosen_float;
-       firstPlaceVotes = RandomSelection_best_priority;
-       //dprint("First place: ", ftos(firstPlace), "\n");
-       //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
-       RandomSelection_Init();
-       for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
-               if(i != firstPlace)
-                       RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
-       secondPlace = RandomSelection_chosen_float;
-       secondPlaceVotes = RandomSelection_best_priority;
-       //dprint("Second place: ", ftos(secondPlace), "\n");
-       //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
-       if(firstPlace == -1)
-               error("No first place in map vote... WTF?");
-       if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
-               return MapVote_Finished(firstPlace);
-       if(mapvote_keeptwotime)
-               if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
-               {
-                       float didntvote;
-                       MapVote_TouchMask();
-                       mapvote_message = "Now decide between the TOP TWO!";
-                       mapvote_keeptwotime = 0;
-                       result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
-                       result = strcat(result, ":", ftos(firstPlaceVotes));
-                       result = strcat(result, ":", mapvote_maps[secondPlace]);
-                       result = strcat(result, ":", ftos(secondPlaceVotes), "::");
-                       didntvote = mapvote_voters;
-                       for(i = 0; i < mapvote_count; ++i)
-                               if(mapvote_maps[i] != "")
-                               {
-                                       didntvote -= mapvote_selections[i];
-                                       if(i != firstPlace)
-                                               if(i != secondPlace)
-                                               {
-                                                       result = strcat(result, ":", mapvote_maps[i]);
-                                                       result = strcat(result, ":", ftos(mapvote_selections[i]));
-                                                       if(i < mapvote_count_real)
-                                                       {
-                                                               strunzone(mapvote_maps[i]);
-                                                               mapvote_maps[i] = "";
-                                                               strunzone(mapvote_maps_pakfile[i]);
-                                                               mapvote_maps_pakfile[i] = "";
-                                                       }
-                                               }
-                               }
-                       result = strcat(result, ":didn't vote:", ftos(didntvote));
-                       if(autocvar_sv_eventlog)
-                               GameLogEcho(result);
-               }
-       return FALSE;
- }
- void MapVote_Tick()
- {
-       float keeptwo;
-       float totalvotes;
-       keeptwo = mapvote_keeptwotime;
-       MapVote_CheckRules_1(); // count
-       if(MapVote_CheckRules_2()) // decide
-               return;
-       totalvotes = 0;
-       FOR_EACH_REALCLIENT(other)
-       {
-               // hide scoreboard again
-               if(other.health != 2342)
-               {
-                       other.health = 2342;
-                       other.impulse = 0;
-                       if(IS_REAL_CLIENT(other))
-                       {
-                               msg_entity = other;
-                               WriteByte(MSG_ONE, SVC_FINALE);
-                               WriteString(MSG_ONE, "");
-                       }
-               }
-               // clear possibly invalid votes
-               if(mapvote_maps[other.mapvote - 1] == "")
-                       other.mapvote = 0;
-               // use impulses as new vote
-               if(other.impulse >= 1 && other.impulse <= mapvote_count)
-                       if(mapvote_maps[other.impulse - 1] != "")
-                       {
-                               other.mapvote = other.impulse;
-                               MapVote_TouchVotes(other);
-                       }
-               other.impulse = 0;
-               if(other.mapvote)
-                       ++totalvotes;
-       }
-       MapVote_CheckRules_1(); // just count
- }
- void MapVote_Start()
- {
-       if(mapvote_run)
-               return;
-       // wait for stats to be sent first
-       if(!playerstats_waitforme)
-               return;
-       MapInfo_Enumerate();
-       if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
-               mapvote_run = TRUE;
- }
- void MapVote_Think()
- {
-       if(!mapvote_run)
-               return;
-       if(alreadychangedlevel)
-               return;
-       if(time < mapvote_nextthink)
-               return;
-       //dprint("tick\n");
-       mapvote_nextthink = time + 0.5;
-       if(!mapvote_initialized)
-       {
-               if(autocvar_rescan_pending == 1)
-               {
-                       cvar_set("rescan_pending", "2");
-                       localcmd("fs_rescan\nrescan_pending 3\n");
-                       return;
-               }
-               else if(autocvar_rescan_pending == 2)
-               {
-                       return;
-               }
-               else if(autocvar_rescan_pending == 3)
-               {
-                       // now build missing mapinfo files
-                       if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
-                               return;
-                       // we're done, start the timer
-                       cvar_set("rescan_pending", "0");
-               }
-               mapvote_initialized = TRUE;
-               if(DoNextMapOverride(0))
-                       return;
-               if(!autocvar_g_maplist_votable || player_count <= 0)
-               {
-                       GotoNextMap(0);
-                       return;
-               }
-               MapVote_Init();
-       }
-       MapVote_Tick();
- }
- string GotoMap(string m)
- {
-       if(!MapInfo_CheckMap(m))
-               return "The map you chose is not available on this server.";
        cvar_set("nextmap", m);
        cvar_set("timelimit", "-1");
        if(mapvote_initialized || alreadychangedlevel)
index 77ebe6426094fd9a6b49cbe9c542adb19925b541,40421bbc0fcd9345490cc5c63dcdac2ef368d481..368c27076f7dea9920b85935871626cc48627fac
@@@ -355,13 -355,12 +355,13 @@@ string formatmessage(string msg
                                wep = self.switchweapon;
                        if (!wep)
                                wep = self.cnt;
 -                      replacement = W_Name(wep);
 +                      replacement = WEP_NAME(wep);
                } else if (escape == "W") {
                        if (self.items & IT_SHELLS) replacement = "shells";
                        else if (self.items & IT_NAILS) replacement = "bullets";
                        else if (self.items & IT_ROCKETS) replacement = "rockets";
                        else if (self.items & IT_CELLS) replacement = "cells";
 +                      else if (self.items & IT_PLASMA) replacement = "plasma";
                        else replacement = "batteries"; // ;)
                } else if (escape == "x") {
                        replacement = cursor_ent.netname;
@@@ -461,6 -460,7 +461,6 @@@ void GetCvars_handleFloatOnce(string th
                        stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
        }
  }
 -float w_getbestweapon(entity e);
  string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
  {
        string o;
@@@ -563,8 -563,6 +563,8 @@@ float g_pickup_rockets
  float g_pickup_rockets_max;
  float g_pickup_cells;
  float g_pickup_cells_max;
 +float g_pickup_plasma;
 +float g_pickup_plasma_max;
  float g_pickup_fuel;
  float g_pickup_fuel_jetpack;
  float g_pickup_fuel_max;
@@@ -613,7 -611,6 +613,7 @@@ float start_ammo_shells
  float start_ammo_nails;
  float start_ammo_rockets;
  float start_ammo_cells;
 +float start_ammo_plasma;
  float start_ammo_fuel;
  float start_health;
  float start_armorvalue;
@@@ -625,13 -622,14 +625,13 @@@ float warmup_start_ammo_shells
  float warmup_start_ammo_nails;
  float warmup_start_ammo_rockets;
  float warmup_start_ammo_cells;
 +float warmup_start_ammo_plasma;
  float warmup_start_ammo_fuel;
  float warmup_start_health;
  float warmup_start_armorvalue;
  float g_weapon_stay;
  
 -entity get_weaponinfo(float w);
 -
 -float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
 +float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? 
  {
        var float i = weaponinfo.weapon;
        var float d = 0;
        else if (g_nexball)
                d = 0; // weapon is set a few lines later
        else
 -              d = (i == WEP_LASER || i == WEP_SHOTGUN);
 +              d = !(!weaponinfo.weaponstart);
  
        if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
                d |= (i == WEP_HOOK);
 -      if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
 +      if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
                d = 0;
  
 -      var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
 +      var float t = weaponinfo.weaponstartoverride;
  
        //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
  
@@@ -689,7 -687,6 +689,7 @@@ void readplayerstartcvars(
        start_ammo_nails = 0;
        start_ammo_rockets = 0;
        start_ammo_cells = 0;
 +      start_ammo_plasma = 0;
        start_health = cvar("g_balance_health_start");
        start_armorvalue = cvar("g_balance_armor_start");
  
        {
                // forcibly turn off weaponarena
        }
-       else if (s == "all")
+       else if (s == "all" || s == "1")
        {
                g_weaponarena = 1;
                g_weaponarena_list = "All Weapons";
                for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                {
                        e = get_weaponinfo(i);
 -                      float w = want_weapon("g_start_weapon_", e, FALSE);
 +                      float w = want_weapon(e, FALSE);
                        if(w & 1)
                                start_weapons |= WepSet_FromWeapon(i);
                        if(w & 2)
                start_ammo_rockets = 999;
                start_ammo_shells = 999;
                start_ammo_cells = 999;
 +              start_ammo_plasma = 999;
                start_ammo_nails = 999;
                start_ammo_fuel = 999;
        }
                start_ammo_nails = cvar("g_start_ammo_nails");
                start_ammo_rockets = cvar("g_start_ammo_rockets");
                start_ammo_cells = cvar("g_start_ammo_cells");
 +              start_ammo_plasma = cvar("g_start_ammo_plasma");
                start_ammo_fuel = cvar("g_start_ammo_fuel");
        }
  
                        for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                        {
                                e = get_weaponinfo(i);
 -                              float w = want_weapon("g_start_weapon_", e, g_warmup_allguns);
 +                              float w = want_weapon(e, g_warmup_allguns);
                                if(w & 1)
                                        warmup_start_weapons |= WepSet_FromWeapon(i);
                                if(w & 2)
        {
                e = get_weaponinfo(i);
                if(precache_weapons & WepSet_FromWeapon(i))
 -                      weapon_action(i, WR_PRECACHE);
 +                      WEP_ACTION(i, WR_INIT);
        }
  
        start_ammo_shells = max(0, start_ammo_shells);
        start_ammo_nails = max(0, start_ammo_nails);
        start_ammo_cells = max(0, start_ammo_cells);
 +      start_ammo_plasma = max(0, start_ammo_plasma);
        start_ammo_rockets = max(0, start_ammo_rockets);
        start_ammo_fuel = max(0, start_ammo_fuel);
  
        warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
        warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
        warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
 +      warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
        warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
        warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
  }
@@@ -911,34 -904,11 +911,11 @@@ float sv_autotaunt
  float sv_taunt;
  
  string GetGametype(); // g_world.qc
+ void mutators_add(); // mutators.qc
  void readlevelcvars(void)
  {
        // load mutators
-       #define CHECK_MUTATOR_ADD(mut_cvar,mut_name,dependence) \
-               { if(cvar(mut_cvar) && dependence) { MUTATOR_ADD(mut_name); } }
-       CHECK_MUTATOR_ADD("g_dodging", mutator_dodging, 1);
-       CHECK_MUTATOR_ADD("g_spawn_near_teammate", mutator_spawn_near_teammate, teamplay);
-       CHECK_MUTATOR_ADD("g_physical_items", mutator_physical_items, 1);
-       CHECK_MUTATOR_ADD("g_touchexplode", mutator_touchexplode, 1);
-       CHECK_MUTATOR_ADD("g_minstagib", mutator_minstagib, 1);
-       CHECK_MUTATOR_ADD("g_invincible_projectiles", mutator_invincibleprojectiles, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_new_toys", mutator_new_toys, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_nix", mutator_nix, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_rocket_flying", mutator_rocketflying, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_vampire", mutator_vampire, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_superspectate", mutator_superspec, 1);
-       CHECK_MUTATOR_ADD("g_pinata", mutator_pinata, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_midair", mutator_midair, 1);
-       CHECK_MUTATOR_ADD("g_bloodloss", mutator_bloodloss, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_random_gravity", mutator_random_gravity, 1);
-       CHECK_MUTATOR_ADD("g_multijump", mutator_multijump, 1);
-       CHECK_MUTATOR_ADD("g_melee_only", mutator_melee_only, !cvar("g_minstagib"));
-       CHECK_MUTATOR_ADD("g_nades", mutator_nades, 1);
-       CHECK_MUTATOR_ADD("g_sandbox", sandbox, 1);
-       CHECK_MUTATOR_ADD("g_campcheck", mutator_campcheck, 1);
-       #undef CHECK_MUTATOR_ADD
+       mutators_add();
  
        if(cvar("sv_allow_fullbright"))
                serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
        sv_clones = cvar("sv_clones");
        sv_foginterval = cvar("sv_foginterval");
        g_cloaked = cvar("g_cloaked");
-     if(g_cts)
-         g_cloaked = 1; // always enable cloak in CTS
        g_footsteps = cvar("g_footsteps");
        g_grappling_hook = cvar("g_grappling_hook");
        g_jetpack = cvar("g_jetpack");
        g_pickup_rockets_max = cvar("g_pickup_rockets_max");
        g_pickup_cells = cvar("g_pickup_cells");
        g_pickup_cells_max = cvar("g_pickup_cells_max");
 +      g_pickup_plasma = cvar("g_pickup_plasma");
 +      g_pickup_plasma_max = cvar("g_pickup_plasma_max");
        g_pickup_fuel = cvar("g_pickup_fuel");
        g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
        g_pickup_fuel_max = cvar("g_pickup_fuel_max");
@@@ -1091,15 -1057,6 +1066,6 @@@ float sound_allowed(float dest, entity 
      return TRUE;
  }
  
- #ifdef COMPAT_XON010_CHANNELS
- void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
- void sound(entity e, float chan, string samp, float vol, float atten)
- {
-     if (!sound_allowed(MSG_BROADCAST, e))
-         return;
-     builtin_sound(e, chan, samp, vol, atten);
- }
- #else
  #undef sound
  void sound(entity e, float chan, string samp, float vol, float atten)
  {
          return;
      sound7(e, chan, samp, vol, atten, 0, 0);
  }
- #endif
  
  void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
  {
@@@ -1312,6 -1268,7 +1277,7 @@@ void precache(
  {
      // gamemode related things
      precache_model ("models/misc/chatbubble.spr");
+       precache_model("models/ice/ice.md3");
  
  #ifdef TTURRETS_ENABLED
      if (autocvar_g_turrets)
          precache_sound ("weapons/hook_impact.wav"); // hook
      }
  
 -    if(autocvar_sv_precacheweapons)
 -    {
 -        //precache weapon models/sounds
 -        float wep;
 -        wep = WEP_FIRST;
 -        while (wep <= WEP_LAST)
 -        {
 -            weapon_action(wep, WR_PRECACHE);
 -            wep = wep + 1;
 -        }
 -    }
 -
      precache_model("models/elaser.mdl");
      precache_model("models/laser.mdl");
      precache_model("models/ebomb.mdl");
@@@ -1823,82 -1792,6 +1789,6 @@@ string uid2name(string myuid) 
        return s;
  }
  
- float race_readTime(string map, float pos)
- {
-       string rr;
-       if(g_cts)
-               rr = CTS_RECORD;
-       else
-               rr = RACE_RECORD;
-       return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
- }
- string race_readUID(string map, float pos)
- {
-       string rr;
-       if(g_cts)
-               rr = CTS_RECORD;
-       else
-               rr = RACE_RECORD;
-       return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
- }
- float race_readPos(string map, float t) {
-       float i;
-       for (i = 1; i <= RANKINGS_CNT; ++i)
-               if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
-                       return i;
-       return 0; // pos is zero if unranked
- }
- void race_writeTime(string map, float t, string myuid)
- {
-       string rr;
-       if(g_cts)
-               rr = CTS_RECORD;
-       else
-               rr = RACE_RECORD;
-       float newpos;
-       newpos = race_readPos(map, t);
-       float i, prevpos = 0;
-       for(i = 1; i <= RANKINGS_CNT; ++i)
-       {
-               if(race_readUID(map, i) == myuid)
-                       prevpos = i;
-       }
-       if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
-               for (i = prevpos; i > newpos; --i) {
-                       db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
-                       db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
-               }
-       } else { // player has no ranked record yet
-               for (i = RANKINGS_CNT; i > newpos; --i) {
-                       db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
-                       db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
-               }
-       }
-       // store new time itself
-       db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
-       db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
- }
- string race_readName(string map, float pos)
- {
-       string rr;
-       if(g_cts)
-               rr = CTS_RECORD;
-       else
-               rr = RACE_RECORD;
-       return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
- }
  float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
  {
      float m, i;
index 4ee54f0493468291e82e1b4c89b98b46c2c23520,f4021eb4c0a45b33a2f2741363ea50d36cc68848..95d85d793963181309ad6e63f182106c055717cb
@@@ -77,6 -77,7 +77,7 @@@ MUTATOR_HOOKABLE(PlayerJump)
        // called when a player presses the jump key
        // INPUT, OUTPUT:
                float player_multijump;
+               float player_jumpheight;
  
  MUTATOR_HOOKABLE(GiveFragsForKill);
        // called when someone was fragged by "self", and is expected to change frag_score to adjust scoring for the kill
@@@ -102,8 -103,13 +103,13 @@@ MUTATOR_HOOKABLE(SpectateCopy)
  MUTATOR_HOOKABLE(ForbidThrowCurrentWeapon);
        // returns 1 if throwing the current weapon shall not be allowed
  
+ MUTATOR_HOOKABLE(WeaponRateFactor);
+       // allows changing attack rate
+       // INPUT, OUTPUT:
+               float weapon_rate;
  MUTATOR_HOOKABLE(SetStartItems);
 -      // adjusts {warmup_}start_{items,weapons,ammo_{cells,rockets,nails,shells,fuel}}
 +      // adjusts {warmup_}start_{items,weapons,ammo_{cells,plasma,rockets,nails,shells,fuel}}
  
  MUTATOR_HOOKABLE(BuildMutatorsString);
        // appends ":mutatorname" to ret_string for logging
@@@ -222,6 -228,11 +228,11 @@@ MUTATOR_HOOKABLE(PlayerPowerups)
  MUTATOR_HOOKABLE(PlayerRegen);
        // called every player think frame
        // return 1 to disable regen
+       // INPUT, OUTPUT:
+               float regen_mod_max;
+               float regen_mod_regen;
+               float regen_mod_rot;
+               float regen_mod_limit;
  
  MUTATOR_HOOKABLE(PlayerUseKey);
        // called when the use key is pressed
index 2b30c8a5ce38989b446e36ed1619133fe1790eac,8b38ceb096a8cd537eda2238aad552d6fb7ae103..0c5e01a6acbb20dff70de980a39e7884cd27dc8d
@@@ -67,12 -67,15 +67,15 @@@ float CA_GetWinnerTeam(
  #define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
  float CA_CheckWinner()
  {
+       entity e;
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
        {
                Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
                Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
                allowed_to_spawn = FALSE;
                round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+               FOR_EACH_PLAYER(e)
+                       nades_Clear(e);
                return 1;
        }
  
  
        allowed_to_spawn = FALSE;
        round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+       FOR_EACH_PLAYER(e)
+               nades_Clear(e);
        return 1;
  }
  
@@@ -229,7 -236,6 +236,7 @@@ MUTATOR_HOOKFUNCTION(ca_SetStartItems
        start_ammo_nails   = warmup_start_ammo_nails   = cvar("g_lms_start_ammo_nails");
        start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
        start_ammo_cells   = warmup_start_ammo_cells   = cvar("g_lms_start_ammo_cells");
 +      start_ammo_plasma  = warmup_start_ammo_plasma  = cvar("g_lms_start_ammo_plasma");
        start_ammo_fuel    = warmup_start_ammo_fuel    = cvar("g_lms_start_ammo_fuel");
  
        return 0;
index 176fb1eac5e9fcee4270c01fdc705dd25e28d3f8,8695ca31b2b95b13103f2735d46a0141ffc18a86..85377836d5f432887b77737c25a8036978cc96b1
@@@ -142,7 -142,7 +142,7 @@@ void GiveBall(entity plyr, entity ball
        self.weaponentity.weapons = self.weapons;
        self.weaponentity.switchweapon = self.weapon;
        self.weapons = WEPSET_PORTO;
 -      weapon_action(WEP_PORTO, WR_RESETPLAYER);
 +      WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
        self.switchweapon = WEP_PORTO;
        W_SwitchWeapon(WEP_PORTO);
        self = ownr;
@@@ -288,7 -288,7 +288,7 @@@ void basketball_touch(void
                football_touch();
                return;
        }
-       if(!self.cnt && IS_PLAYER(other) && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect))
+       if(!self.cnt && IS_PLAYER(other) && !other.frozen && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect))
        {
                if(other.health <= 0)
                        return;
@@@ -784,7 -784,7 +784,7 @@@ void W_Nexball_Attack2(void
        setsize(missile, '0 0 0', '0 0 0');
        setorigin(missile, w_shotorg);
  
 -      W_SetupProjectileVelocity(missile, autocvar_g_balance_nexball_secondary_speed, 0);
 +      W_SetupProjVelocity_Basic(missile, autocvar_g_balance_nexball_secondary_speed, 0);
        missile.angles = vectoangles(missile.velocity);
        missile.touch = W_Nexball_Touch;
        missile.think = SUB_Remove;
@@@ -855,7 -855,7 +855,7 @@@ float w_nexball_weapon(float req
                        weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                }
        }
 -      else if(req == WR_PRECACHE)
 +      else if(req == WR_INIT)
        {
                precache_model("models/weapons/g_porto.md3");
                precache_model("models/weapons/v_porto.md3");
        }
        else if(req == WR_SETUP)
        {
 -              weapon_setup(WEP_PORTO);
 +              //weapon_setup(WEP_PORTO);
        }
        // No need to check WR_CHECKAMMO* or WR_AIM, it should always return TRUE
        return TRUE;
@@@ -930,7 -930,7 +930,7 @@@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreT
                        if(self.weaponentity.weapons)
                        {
                                self.weapons = self.weaponentity.weapons;
 -                              weapon_action(WEP_PORTO, WR_RESETPLAYER);
 +                              WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
                                self.switchweapon = self.weaponentity.switchweapon;
                                W_SwitchWeapon(self.switchweapon);
  
@@@ -976,7 -976,7 +976,7 @@@ MUTATOR_HOOKFUNCTION(nexball_SetStartIt
  
  MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing)
  {
 -      if(self.weapon == WEP_GRENADE_LAUNCHER)
 +      if(self.weapon == WEP_MORTAR)
                return TRUE;
  
        return FALSE;
  MUTATOR_HOOKFUNCTION(nexball_FilterItem)
  {
        if(self.classname == "droppedweapon")
 -      if(self.weapon == WEP_GRENADE_LAUNCHER)
 +      if(self.weapon == WEP_MORTAR)
                return TRUE;
  
        return FALSE;
@@@ -1011,7 -1011,7 +1011,7 @@@ MUTATOR_DEFINITION(gamemode_nexball
                g_nexball_meter_period = rint(g_nexball_meter_period * 32) / 32; //Round to 1/32ths to send as a byte multiplied by 32
                addstat(STAT_NB_METERSTART, AS_FLOAT, metertime);
  
 -              w_porto(WR_PRECACHE); // abuse
 +              W_Porto(WR_INIT); // abuse
  
                // General settings
                /*
index 7217aaaac09d9d1e7bae00018b07bf8d3d280b00,20811ad50815a4906ef0a37eb5c9a95de5f6111b..d05e7e9c4261c48c76f08325b440fb06f0bd640c
@@@ -6,7 -6,7 +6,7 @@@ void spawnfunc_item_minst_cells (void
  
        StartItem ("models/items/a_cells.md3",
                           "misc/itempickup.wav", 45, 0,
 -                         "MinstaNex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
 +                         "Vaporizer Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
  }
  
  void minstagib_health_mega()
@@@ -225,7 -225,6 +225,6 @@@ MUTATOR_HOOKFUNCTION(minstagib_SplitHea
  MUTATOR_HOOKFUNCTION(minstagib_ForbidThrowing)
  {
        // weapon dropping on death handled by FilterItem
-       nades_CheckThrow();
  
        return TRUE;
  }
@@@ -246,7 -245,7 +245,7 @@@ MUTATOR_HOOKFUNCTION(minstagib_PlayerDa
                }
  
                if(IS_PLAYER(frag_attacker))
 -              if(DEATH_ISWEAPON(frag_deathtype, WEP_MINSTANEX))
 +              if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
                if(frag_target.armorvalue)
                {
                        frag_target.armorvalue -= 1;
                }
  
                if(IS_PLAYER(frag_attacker))
 -              if (DEATH_ISWEAPON(frag_deathtype, WEP_LASER))
 +              if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
                {
                        frag_damage = 0;
                        frag_mirrordamage = 0;
@@@ -297,8 -296,8 +296,8 @@@ MUTATOR_HOOKFUNCTION(minstagib_SetStart
  
        start_health = 100;
        start_armorvalue = 0;
 -      start_weapons = WEPSET_MINSTANEX;
 -      warmup_start_weapons = WEPSET_MINSTANEX;
 +      start_weapons = WEPSET_VAPORIZER;
 +      warmup_start_weapons = WEPSET_VAPORIZER;
        start_items |= IT_UNLIMITED_SUPERWEAPONS;
  
        return FALSE;
@@@ -309,13 -308,13 +308,13 @@@ MUTATOR_HOOKFUNCTION(minstagib_FilterIt
        if(self.classname == "item_cells")
                return TRUE; // no normal cells?
  
 -      if(self.weapon == WEP_MINSTANEX && self.classname == "droppedweapon")
 +      if(self.weapon == WEP_VAPORIZER && self.classname == "droppedweapon")
        {
                self.ammo_cells = autocvar_g_minstagib_ammo_drop;
                return FALSE;
        }
  
 -      if(self.weapon == WEP_ROCKET_LAUNCHER || self.weapon == WEP_NEX)
 +      if(self.weapon == WEP_DEVASTATOR || self.weapon == WEP_VORTEX)
        {
                entity e = spawn();
                setorigin(e, self.origin);
index 515d3901dea02fb65ecb11fb2d12b493799558ab,da75e25ac23679fdaeea6bab190f0bc0311b2d62..70c4578bd02baea2c19b7185add6ddaade69913b
@@@ -1,31 -1,20 +1,20 @@@
+ .entity nade_spawnloc;
  void nade_timer_think()
  {
        self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
        self.nextthink = time;
        if(!self.owner || wasfreed(self.owner))
                remove(self);
  }
  
  void nade_burn_spawn(entity _nade)
  {
-       float p;
-       switch(_nade.realowner.team)
-       {
-               case NUM_TEAM_1: p = PROJECTILE_NADE_RED_BURN; break;
-               case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE_BURN; break;
-               case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW_BURN; break;
-               case NUM_TEAM_4: p = PROJECTILE_NADE_PINK_BURN; break;
-               default:                 p = PROJECTILE_NADE_BURN; break;
-       }
-       CSQCProjectile(_nade, TRUE, p, TRUE);
+       CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, TRUE), TRUE);
  }
  
  void nade_spawn(entity _nade)
  {
-       float p;
        entity timer = spawn();
        setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
        setattachment(timer, _nade, "");
        timer.owner = _nade;
        timer.skin = 10;
  
-       switch(_nade.realowner.team)
+       _nade.effects |= EF_LOWPRECISION;
+       CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, FALSE), TRUE);
+ }
+ void napalm_damage(float dist, float damage, float edgedamage, float burntime)
+ {
+       entity e;
+       float d;
+       vector p;
+       if ( damage < 0 )
+               return;
+       RandomSelection_Init();
+       for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
+               if(e.takedamage == DAMAGE_AIM)
+               if(self.realowner != e || autocvar_g_nades_napalm_selfdamage)
+               if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
+               if(!e.frozen)
+               {
+                       p = e.origin;
+                       p_x += e.mins_x + random() * (e.maxs_x - e.mins_x);
+                       p_y += e.mins_y + random() * (e.maxs_y - e.mins_y);
+                       p_z += e.mins_z + random() * (e.maxs_z - e.mins_z);
+                       d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
+                       if(d < dist)
+                       {
+                               e.fireball_impactvec = p;
+                               RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+                       }
+               }
+       if(RandomSelection_chosen_ent)
+       {
+               d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
+               d = damage + (edgedamage - damage) * (d / dist);
+               Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
+               //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
+               pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
+       }
+ }
+ void napalm_ball_think()
+ {
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+       {
+               remove(self);
+               return;
+       }
+       if(time > self.pushltime)
+       {
+               remove(self);
+               return;
+       }
+       vector midpoint = ((self.absmin + self.absmax) * 0.5);
+       if(pointcontents(midpoint) == CONTENT_WATER)
+       {
+               self.velocity = self.velocity * 0.5;
+               if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                       { self.velocity_z = 200; }
+       }
+       self.angles = vectoangles(self.velocity);
+       napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage,
+                                 autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime);
+       self.nextthink = time + 0.1;
+ }
+ void nade_napalm_ball()
+ {
+       entity proj;
+       vector kick;
+       spamsound(self, CH_SHOTS, "weapons/fireball_fire.wav", VOL_BASE, ATTEN_NORM);
+       proj = spawn ();
+       proj.owner = self.owner;
+       proj.realowner = self.realowner;
+       proj.team = self.owner.team;
+       proj.classname = "grenade";
+       proj.bot_dodge = TRUE;
+       proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
+       proj.movetype = MOVETYPE_BOUNCE;
+       proj.projectiledeathtype = DEATH_NADE_NAPALM;
+       PROJECTILE_MAKETRIGGER(proj);
+       setmodel(proj, "null");
+       proj.scale = 1;//0.5;
+       setsize(proj, '-4 -4 -4', '4 4 4');
+       setorigin(proj, self.origin);
+       proj.think = napalm_ball_think;
+       proj.nextthink = time;
+       proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale;
+       proj.effects = EF_LOWPRECISION | EF_FLAME;
+       kick_x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+       kick_y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+       kick_z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread;
+       proj.velocity = kick;
+       proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime;
+       proj.angles = vectoangles(proj.velocity);
+       proj.flags = FL_PROJECTILE;
+       proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
+       //CSQCProjectile(proj, TRUE, PROJECTILE_NAPALM_FIRE, TRUE);
+ }
+ void napalm_fountain_think()
+ {
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+       {
+               remove(self);
+               return;
+       }
+       if(time >= self.ltime)
+       {
+               remove(self);
+               return;
+       }
+       vector midpoint = ((self.absmin + self.absmax) * 0.5);
+       if(pointcontents(midpoint) == CONTENT_WATER)
+       {
+               self.velocity = self.velocity * 0.5;
+               if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                       { self.velocity_z = 200; }
+               UpdateCSQCProjectile(self);
+       }
+       napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage,
+               autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime);
+       self.nextthink = time + 0.1;
+       if(time >= self.nade_special_time)
+       {
+               self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay;
+               nade_napalm_ball();
+       }
+ }
+ void nade_napalm_boom()
+ {
+       entity fountain;
+       local float c;
+       for (c = 0; c < autocvar_g_nades_napalm_ball_count; c ++)
+               nade_napalm_ball();
+       fountain = spawn();
+       fountain.owner = self.owner;
+       fountain.realowner = self.realowner;
+       fountain.origin = self.origin;
+       setorigin(fountain, fountain.origin);
+       fountain.think = napalm_fountain_think;
+       fountain.nextthink = time;
+       fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
+       fountain.pushltime = fountain.ltime;
+       fountain.team = self.team;
+       fountain.movetype = MOVETYPE_TOSS;
+       fountain.projectiledeathtype = DEATH_NADE_NAPALM;
+       fountain.bot_dodge = TRUE;
+       fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage;
+       fountain.nade_special_time = time;
+       setsize(fountain, '-16 -16 -16', '16 16 16');
+       CSQCProjectile(fountain, TRUE, PROJECTILE_NAPALM_FOUNTAIN, TRUE);
+ }
+ void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time)
+ {
+       frost_target.frozen_by = freezefield.realowner;
+       pointparticles(particleeffectnum("electro_impact"), frost_target.origin, '0 0 0', 1);
+       Freeze(frost_target, 1/freeze_time, 3, FALSE);
+       if(frost_target.ballcarried)
+       if(g_keepaway) { ka_DropEvent(frost_target); }
+       else { DropBall(frost_target.ballcarried, frost_target.origin, frost_target.velocity);}
+       if(frost_target.flagcarried) { ctf_Handle_Throw(frost_target, world, DROP_THROW); }
+       if(frost_target.nade) { toss_nade(frost_target, '0 0 0', time + 0.05); }
+       
+       kh_Key_DropAll(frost_target, FALSE);
+ }
+ void nade_ice_think()
+ {
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+       {
+               remove(self);
+               return;
+       }
+       if(time >= self.ltime)
+       {
+               if ( autocvar_g_nades_ice_explode )
+               {
+                       string expef;
+                       switch(self.realowner.team)
+                       {
+                               case NUM_TEAM_1: expef = "nade_red_explode"; break;
+                               case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+                               case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+                               case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+                               default:                 expef = "nade_neutral_explode"; break;
+                       }
+                       pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
+                       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+                       RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
 -                              autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
++                              autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+                       Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+                               autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+               }
+               remove(self);
+               return;
+       }
+       self.nextthink = time+0.1;
+       // gaussian
+       float randomr;
+       randomr = random();
+       randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius;
+       float randomw;
+       randomw = random()*M_PI*2;
+       vector randomp;
+       randomp_x = randomr*cos(randomw);
+       randomp_y = randomr*sin(randomw);
+       randomp_z = 1;
+       pointparticles(particleeffectnum("electro_muzzleflash"), self.origin + randomp, '0 0 0', 1);
+       if(time >= self.nade_special_time)
+       {
+               self.nade_special_time = time+0.7;
+               pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
+               pointparticles(particleeffectnum("icefield"), self.origin, '0 0 0', 1);
+       }
+       float current_freeze_time = self.ltime - time - 0.1;
+       entity e;
+       for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
+       if(e != self)
+       if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
+       if(e.takedamage && e.deadflag == DEAD_NO)
+       if(e.health > 0)
+       if(!e.revival_time || ((time - e.revival_time) >= 1.5))
+       if(!e.frozen)
+       if(current_freeze_time > 0)
+               nade_ice_freeze(self, e, current_freeze_time);
+ }
+ void nade_ice_boom()
+ {
+       entity fountain;
+       fountain = spawn();
+       fountain.owner = self.owner;
+       fountain.realowner = self.realowner;
+       fountain.origin = self.origin;
+       setorigin(fountain, fountain.origin);
+       fountain.think = nade_ice_think;
+       fountain.nextthink = time;
+       fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
+       fountain.pushltime = fountain.wait = fountain.ltime;
+       fountain.team = self.team;
+       fountain.movetype = MOVETYPE_TOSS;
+       fountain.projectiledeathtype = DEATH_NADE_ICE;
+       fountain.bot_dodge = FALSE;
+       setsize(fountain, '-16 -16 -16', '16 16 16');
+       fountain.nade_special_time = time+0.3;
+       fountain.angles = self.angles;
+       if ( autocvar_g_nades_ice_explode )
+       {
+               setmodel(fountain, "models/grenademodel.md3");
+               entity timer = spawn();
+               setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
+               setattachment(timer, fountain, "");
+               timer.classname = "nade_timer";
+               timer.colormap = self.colormap;
+               timer.glowmod = self.glowmod;
+               timer.think = nade_timer_think;
+               timer.nextthink = time;
+               timer.wait = fountain.ltime;
+               timer.owner = fountain;
+               timer.skin = 10;
+       }
+       else
+               setmodel(fountain, "null");
+ }
+ void nade_translocate_boom()
+ {
+       if(self.realowner.vehicle)
+               return;
+       vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins_z - 24);
+       tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner);
+       locout = trace_endpos;
+       makevectors(self.realowner.angles);
+       entity oldself = self;
+       self = self.realowner;
+       MUTATOR_CALLHOOK(PortalTeleport);
+       self.realowner = self;
+       self = oldself;
+       TeleportPlayer(self, self.realowner, locout, self.realowner.mangle, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
+ }
+ void nade_spawn_boom()
+ {
+       entity spawnloc = spawn();
+       setorigin(spawnloc, self.origin);
+       setsize(spawnloc, self.realowner.mins, self.realowner.maxs);
+       spawnloc.movetype = MOVETYPE_NONE;
+       spawnloc.solid = SOLID_NOT;
+       spawnloc.drawonlytoclient = self.realowner;
+       spawnloc.effects = EF_STARDUST;
+       spawnloc.cnt = autocvar_g_nades_spawn_count;
+       if(self.realowner.nade_spawnloc)
+       {
+               remove(self.realowner.nade_spawnloc);
+               self.realowner.nade_spawnloc = world;
+       }
+       self.realowner.nade_spawnloc = spawnloc;
+ }
+ void nade_heal_think()
+ {
+       if(time >= self.ltime)
+       {
+               remove(self);
+               return;
+       }
+       
+       self.nextthink = time;
+       
+       if(time >= self.nade_special_time)
+       {
+               self.nade_special_time = time+0.25;
+               self.nade_show_particles = 1;
+       }
+       else
+               self.nade_show_particles = 0;
+ }
+ void nade_heal_touch()
+ {
+       float maxhealth;
+       float health_factor;
+       if(IS_PLAYER(other) || (other.flags & FL_MONSTER))
+       if(other.deadflag == DEAD_NO)
+       if(!other.frozen)
+       {
+               health_factor = autocvar_g_nades_heal_rate*frametime/2;
+               if ( other != self.realowner )
+               {
+                       if ( SAME_TEAM(other,self) )
+                               health_factor *= autocvar_g_nades_heal_friend;
+                       else
+                               health_factor *= autocvar_g_nades_heal_foe;
+               }
+               if ( health_factor > 0 )
+               {
+                       maxhealth = (other.flags & FL_MONSTER) ? other.max_health : g_pickup_healthmega_max;
+                       if ( other.health < maxhealth )
+                       {
+                               if ( self.nade_show_particles )
+                                       pointparticles(particleeffectnum("healing_fx"), other.origin, '0 0 0', 1);
+                               other.health = min(other.health+health_factor, maxhealth);
+                       }
+                       other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);  
+               }
+               else if ( health_factor < 0 )
+               {
+                       Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL,other.origin,'0 0 0');
+               }
+               
+       }
+       
+       if ( IS_REAL_CLIENT(other) || (other.vehicle_flags & VHF_ISVEHICLE) )
        {
-               case NUM_TEAM_1: p = PROJECTILE_NADE_RED; break;
-               case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE; break;
-               case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW; break;
-               case NUM_TEAM_4: p = PROJECTILE_NADE_PINK; break;
-               default:                 p = PROJECTILE_NADE; break;
+               entity show_red = (other.vehicle_flags & VHF_ISVEHICLE) ? other.owner : other;
+               show_red.stat_healing_orb = time+0.1;
+               show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
        }
+ }
  
-       CSQCProjectile(_nade, TRUE, p, TRUE);
+ void nade_heal_boom()
+ {
+       entity healer;
+       healer = spawn();
+       healer.owner = self.owner;
+       healer.realowner = self.realowner;
+       setorigin(healer, self.origin);
+       healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
+       healer.ltime = time + healer.healer_lifetime;
+       healer.team = self.realowner.team;
+       healer.bot_dodge = FALSE;
+       healer.solid = SOLID_TRIGGER;
+       healer.touch = nade_heal_touch;
+       setmodel(healer, "models/ctf/shield.md3");
+       healer.healer_radius = autocvar_g_nades_nade_radius;
+       vector size = '1 1 1' * healer.healer_radius / 2;
+       setsize(healer,-size,size);
+       
+       Net_LinkEntity(healer, TRUE, 0, healer_send);
+       
+       healer.think = nade_heal_think;
+       healer.nextthink = time;
+       healer.SendFlags |= 1;
+ }
  
+ void nade_monster_boom()
+ {
+       entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, FALSE, FALSE, 1);
+       
+       if(autocvar_g_nades_pokenade_monster_lifetime > 0)
+               e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
+       e.monster_skill = MONSTER_SKILL_INSANE;
  }
  
  void nade_boom()
  {
        string expef;
+       float nade_blast = 1;
  
-       switch(self.realowner.team)
+       switch ( self.nade_type )
        {
-               case NUM_TEAM_1: expef = "nade_red_explode"; break;
-               case NUM_TEAM_2: expef = "nade_blue_explode"; break;
-               case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
-               case NUM_TEAM_4: expef = "nade_pink_explode"; break;
-               default:                 expef = "nade_explode"; break;
+               case NADE_TYPE_NAPALM:
+                       nade_blast = autocvar_g_nades_napalm_blast;
+                       expef = "explosion_medium";
+                       break;
+               case NADE_TYPE_ICE:
+                       nade_blast = 0;
+                       expef = "electro_combo"; // hookbomb_explode electro_combo bigplasma_impact
+                       break;
+               case NADE_TYPE_TRANSLOCATE:
+                       nade_blast = 0;
+                       expef = "";
+                       break;
+               case NADE_TYPE_MONSTER:
+               case NADE_TYPE_SPAWN:
+                       nade_blast = 0;
+                       switch(self.realowner.team)
+                       {
+                               case NUM_TEAM_1: expef = "spawn_event_red"; break;
+                               case NUM_TEAM_2: expef = "spawn_event_blue"; break;
+                               case NUM_TEAM_3: expef = "spawn_event_yellow"; break;
+                               case NUM_TEAM_4: expef = "spawn_event_pink"; break;
+                               default: expef = "spawn_event_neutral"; break;
+                       }
+                       break;
+               case NADE_TYPE_HEAL:
+                       nade_blast = 0;
+                       expef = "spawn_event_red";
+                       break;
+               default:
+               case NADE_TYPE_NORMAL:
+                       switch(self.realowner.team)
+                       {
+                               case NUM_TEAM_1: expef = "nade_red_explode"; break;
+                               case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+                               case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+                               case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+                               default:                 expef = "nade_neutral_explode"; break;
+                       }
        }
  
-       sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
-       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
        pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
  
-       Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+       sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
+       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
  
--      self.takedamage = DAMAGE_NO;
-       RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
 -
+       if(nade_blast)
+       {
+               RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
 -                               autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
 +                               autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+               Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+       }
+       switch ( self.nade_type )
+       {
+               case NADE_TYPE_NAPALM: nade_napalm_boom(); break;
+               case NADE_TYPE_ICE: nade_ice_boom(); break;
+               case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break;
+               case NADE_TYPE_SPAWN: nade_spawn_boom(); break;
+               case NADE_TYPE_HEAL: nade_heal_boom(); break;
+               case NADE_TYPE_MONSTER: nade_monster_boom(); break;
+       }
  
        remove(self);
  }
  
  void nade_touch()
  {
+       if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
        PROJECTILE_TOUCH;
        //setsize(self, '-2 -2 -2', '2 2 2');
        //UpdateCSQCProjectile(self);
@@@ -101,26 -572,29 +570,28 @@@ void nade_beep(
  
  void nade_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
 -      if(DEATH_ISWEAPON(deathtype, WEP_LASER))
+       if(self.nade_type == NADE_TYPE_TRANSLOCATE || self.nade_type == NADE_TYPE_SPAWN)
+               return;
 +      if(DEATH_ISWEAPON(deathtype, WEP_BLASTER))
                return;
  
 -      if(DEATH_ISWEAPON(deathtype, WEP_NEX) || DEATH_ISWEAPON(deathtype, WEP_MINSTANEX))
 +      if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
        {
                force *= 6;
                damage = self.max_health * 0.55;
        }
  
 -      if(DEATH_ISWEAPON(deathtype, WEP_UZI))
 +      if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
                damage = self.max_health * 0.1;
  
-       if((DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) && !(deathtype & HITTYPE_SECONDARY)) // WEAPONTODO
-               damage = self.max_health * 1.1;
-       if((DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) && (deathtype & HITTYPE_SECONDARY))
 -      if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN))
 -      if(deathtype & HITTYPE_SECONDARY)
++      if((DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) && (deathtype & HITTYPE_SECONDARY)) // WEAPONTODO
        {
                damage = self.max_health * 0.1;
-               force *= 15;
+               force *= 10;
        }
+       else
+               damage = self.max_health * 1.1;
  
        self.velocity += force;
  
                self.think = nade_beep;
        }
  
-       self.health   -= damage;
-       self.realowner = attacker;
+       self.health       -= damage;
+       
+       if ( self.nade_type != NADE_TYPE_HEAL || IS_PLAYER(attacker) )
+               self.realowner = attacker;
  
        if(self.health <= 0)
                W_PrepareExplosionByDamage(attacker, nade_boom);
  
  void toss_nade(entity e, vector _velocity, float _time)
  {
+       if(e.nade == world)
+               return;
        entity _nade = e.nade;
        e.nade = world;
  
  
        Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES);
  
-       //setorigin(_nade, CENTER_OR_VIEWOFS(e) + (v_right * 10) * -1);
        setorigin(_nade, w_shotorg + (v_right * 25) * -1);
-       setmodel(_nade, "models/weapons/v_ok_grenade.md3");
-       setattachment(_nade, world, "");
+       //setmodel(_nade, "models/weapons/v_ok_grenade.md3");
+       //setattachment(_nade, world, "");
        PROJECTILE_MAKETRIGGER(_nade);
        setsize(_nade, '-16 -16 -16', '16 16 16');
        _nade.movetype = MOVETYPE_BOUNCE;
                _nade.velocity = _velocity;
        else
                _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, TRUE);
-               
        _nade.touch = nade_touch;
        _nade.health = autocvar_g_nades_nade_health;
        _nade.max_health = _nade.health;
        _nade.takedamage = DAMAGE_AIM;
        _nade.event_damage = nade_damage;
+       _nade.customizeentityforclient = func_null;
+       _nade.exteriormodeltoclient = world;
+       _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       _nade.traileffectnum = 0;
        _nade.teleportable = TRUE;
        _nade.pushable = TRUE;
        _nade.gravity = 1;
        _nade.damagedbycontents = TRUE;
        _nade.angles = vectoangles(_nade.velocity);
        _nade.flags = FL_PROJECTILE;
+       _nade.projectiledeathtype = DEATH_NADE;
+       _nade.toss_time = time;
+       _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX);
  
        nade_spawn(_nade);
  
        }
  
        e.nade_refire = time + autocvar_g_nades_nade_refire;
+       e.nade_timer = 0;
+ }
+ void nades_GiveBonus(entity player, float score)
+ {
+       if (autocvar_g_nades)
+       if (autocvar_g_nades_bonus)
+       if (IS_REAL_CLIENT(player))
+       if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max)
+       if (player.frozen == 0)
+       if (player.deadflag == DEAD_NO)
+       {
+               if ( player.bonus_nade_score < 1 )
+                       player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max;
+               if ( player.bonus_nade_score >= 1 )
+               {
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS);
+                       play2(player,"kh/alarm.wav");
+                       player.bonus_nades++;
+                       player.bonus_nade_score -= 1;
+               }
+       }
+ }
+ void nades_RemoveBonus(entity player)
+ {
+       player.bonus_nades = player.bonus_nade_score = 0;
+ }
+ float nade_customize()
+ {
+       //if(IS_SPEC(other)) { return FALSE; }
+       if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner))
+       {
+               // somewhat hide the model, but keep the glow
+               //self.effects = 0;
+               if(self.traileffectnum)
+                       self.traileffectnum = 0;
+               self.alpha = -1;
+       }
+       else
+       {
+               //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+               if(!self.traileffectnum)
+                       self.traileffectnum = particleeffectnum(Nade_TrailEffect(Nade_ProjectileFromID(self.nade_type, FALSE), self.team));
+               self.alpha = 1;
+       }
+       
+       return TRUE;
  }
  
  void nade_prime()
        if(self.fake_nade)
                remove(self.fake_nade);
  
-       self.nade = spawn();
-       setmodel(self.nade, "null");
-       setattachment(self.nade, self, "bip01 l hand");
-       self.nade.classname = "nade";
-       self.nade.realowner = self;
-       self.nade.colormap = self.colormap;
-       self.nade.glowmod = self.glowmod;
-       self.nade.wait = time + autocvar_g_nades_nade_lifetime;
-       self.nade.lifetime = time;
-       self.nade.think = nade_beep;
-       self.nade.nextthink = max(self.nade.wait - 3, time);
-       self.nade.projectiledeathtype = DEATH_NADE;
-       self.fake_nade = spawn();
-       setmodel(self.fake_nade, "models/weapons/h_ok_grenade.iqm");
-       setattachment(self.fake_nade, self.weaponentity, "");
-       self.fake_nade.classname = "fake_nade";
-       //self.fake_nade.viewmodelforclient = self;
-       self.fake_nade.realowner = self.fake_nade.owner = self;
-       self.fake_nade.colormap = self.colormap;
-       self.fake_nade.glowmod = self.glowmod;
-       self.fake_nade.think = SUB_Remove;
-       self.fake_nade.nextthink = self.nade.wait;
+       entity n = spawn(), fn = spawn();
+       n.classname = "nade";
+       fn.classname = "fake_nade";
+       if(self.items & IT_STRENGTH && autocvar_g_nades_bonus_onstrength)
+               n.nade_type = self.nade_type;
+       else if (self.bonus_nades >= 1)
+       {
+               n.nade_type = self.nade_type;
+               n.pokenade_type = self.pokenade_type;
+               self.bonus_nades -= 1;
+       }
+       else
+       {
+               n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type);
+               n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type);
+       }
+       
+       n.nade_type = bound(1, n.nade_type, NADE_TYPE_LAST);
+       setmodel(n, "models/weapons/v_ok_grenade.md3");
+       //setattachment(n, self, "bip01 l hand");
+       n.exteriormodeltoclient = self;
+       n.customizeentityforclient = nade_customize;
+       n.traileffectnum = particleeffectnum(Nade_TrailEffect(Nade_ProjectileFromID(n.nade_type, FALSE), self.team));
+       n.colormod = Nade_Color(n.nade_type);
+       n.realowner = self;
+       n.colormap = self.colormap;
+       n.glowmod = self.glowmod;
+       n.wait = time + autocvar_g_nades_nade_lifetime;
+       n.lifetime = time;
+       n.think = nade_beep;
+       n.nextthink = max(n.wait - 3, time);
+       n.projectiledeathtype = DEATH_NADE;
+       setmodel(fn, "models/weapons/h_ok_grenade.iqm");
+       setattachment(fn, self.weaponentity, "");
+       fn.realowner = fn.owner = self;
+       fn.colormod = Nade_Color(n.nade_type);
+       fn.colormap = self.colormap;
+       fn.glowmod = self.glowmod;
+       fn.think = SUB_Remove;
+       fn.nextthink = n.wait;
+       self.nade = n;
+       self.fake_nade = fn;
  }
  
  float CanThrowNade()
@@@ -285,21 -844,55 +841,55 @@@ void nades_CheckThrow(
        }
  }
  
+ void nades_Clear(entity player)
+ {
+       if(player.nade)
+               remove(player.nade);
+       if(player.fake_nade)
+               remove(player.fake_nade);
+       player.nade = player.fake_nade = world;
+       player.nade_timer = 0;
+ }
+ MUTATOR_HOOKFUNCTION(nades_CheckThrow)
+ {
+       if(MUTATOR_RETURNVALUE) { nades_CheckThrow(); }
+       return FALSE;
+ }
  MUTATOR_HOOKFUNCTION(nades_VehicleEnter)
  {
-       if(other.nade)
-               toss_nade(other, '0 0 100', max(other.nade.wait, time + 0.05));
+       if(vh_player.nade)
+               toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05));
  
        return FALSE;
  }
  
  MUTATOR_HOOKFUNCTION(nades_PlayerPreThink)
  {
-       float key_pressed = ((g_grappling_hook || client_hasweapon(self, WEP_HOOK, FALSE, FALSE) || (weaponsInMap & WEPSET_HOOK)) ? self.button16 : self.BUTTON_HOOK);
+       if(!IS_PLAYER(self)) { return FALSE; }
+       float key_pressed = self.BUTTON_HOOK;
+       float time_score;
  
+       if(g_grappling_hook || client_hasweapon(self, WEP_HOOK, FALSE, FALSE) || (weaponsInMap & WEPSET_HOOK) || g_jetpack || self.items & IT_JETPACK)
+               key_pressed = self.button16; // if hook/jetpack is enabled, use an alternate key
+               
        if(self.nade)
-               if(self.nade.wait - 0.1 <= time)
-                       toss_nade(self, '0 0 0', time + 0.05);
+       {
+               self.nade_timer = bound(0, (time - self.nade.lifetime) / autocvar_g_nades_nade_lifetime, 1);
+               //print(sprintf("%d %d\n", self.nade_timer, time - self.nade.lifetime));
+               makevectors(self.angles);
+               self.nade.velocity = self.velocity;
+               setorigin(self.nade, self.origin + self.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
+               self.nade.angles_y = self.angles_y;
+       }
+       if(self.nade)
+       if(self.nade.wait - 0.1 <= time)
+               toss_nade(self, '0 0 0', time + 0.05);
  
        if(CanThrowNade())
        if(self.nade_refire < time)
                }
        }
  
+       if(IS_PLAYER(self))
+       {
+               if ( autocvar_g_nades_bonus && autocvar_g_nades )
+               {
+                       entity key;
+                       float key_count = 0;
+                       FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; }
+                       if(self.flagcarried || self.ballcarried) // this player is important
+                               time_score = autocvar_g_nades_bonus_score_time_flagcarrier;
+                       else
+                               time_score = autocvar_g_nades_bonus_score_time;
+                               
+                       if(key_count)
+                               time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding
+                       if(autocvar_g_nades_bonus_client_select)
+                       {
+                               self.nade_type = self.cvar_cl_nade_type;
+                               self.pokenade_type = self.cvar_cl_pokenade_type;
+                       }
+                       else
+                       {
+                               self.nade_type = autocvar_g_nades_bonus_type;
+                               self.pokenade_type = autocvar_g_nades_pokenade_monster_type;
+                       }
+                               
+                       self.nade_type = bound(1, self.nade_type, NADE_TYPE_LAST);
+                       if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max)
+                               nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max);
+               }
+               else
+               {
+                       self.bonus_nades = self.bonus_nade_score = 0;
+               }
+       }
+       float n = 0;
+       entity o = world;
+       if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+               n = -1;
+       else
+       {
+               vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+               n = 0;
+               FOR_EACH_PLAYER(other) if(self != other)
+               {
+                       if(other.deadflag == DEAD_NO)
+                       if(other.frozen == 0)
+                       if(SAME_TEAM(other, self))
+                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                       {
+                               if(!o)
+                                       o = other;
+                               if(self.frozen == 1)
+                                       other.reviving = TRUE;
+                               ++n;
+                       }
+               }
+       }
+       if(n && self.frozen == 3) // OK, there is at least one teammate reviving us
+       {
+               self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+               self.health = max(1, self.revive_progress * start_health);
+               if(self.revive_progress >= 1)
+               {
+                       Unfreeze(self);
+                       Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
+                       Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
+               }
+               FOR_EACH_PLAYER(other) if(other.reviving)
+               {
+                       other.revive_progress = self.revive_progress;
+                       other.reviving = FALSE;
+               }
+       }
        return FALSE;
  }
  
@@@ -332,24 -1007,113 +1004,113 @@@ MUTATOR_HOOKFUNCTION(nades_PlayerSpawn
        else
                self.nade_refire  = time + autocvar_g_nades_nade_refire;
  
+       if(autocvar_g_nades_bonus_client_select)
+               self.nade_type = self.cvar_cl_nade_type;
+       self.nade_timer = 0;
+       if(self.nade_spawnloc)
+       {
+               setorigin(self, self.nade_spawnloc.origin);
+               self.nade_spawnloc.cnt -= 1;
+               
+               if(self.nade_spawnloc.cnt <= 0)
+               {
+                       remove(self.nade_spawnloc);
+                       self.nade_spawnloc = world;
+               }
+       }
        return FALSE;
  }
  
  MUTATOR_HOOKFUNCTION(nades_PlayerDies)
  {
-       if(self.nade)
-               toss_nade(self, '0 0 100', max(self.nade.wait, time + 0.05));
+       if(frag_target.nade)
+       if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade)
+               toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05));
+       float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor);
+       if(IS_PLAYER(frag_attacker))
+       {
+               if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target)
+                       nades_RemoveBonus(frag_attacker);
+               else if(frag_target.flagcarried)
+                       nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium);
+               else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1)
+               {
+                       #define SPREE_ITEM(counta,countb,center,normal,gentle) \
+                               case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; }
+                       switch(frag_attacker.killcount)
+                       {
+                               KILL_SPREE_LIST
+                               default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break;
+                       }
+                       #undef SPREE_ITEM
+               }
+               else
+                       nades_GiveBonus(frag_attacker, killcount_bonus);
+       }
+       nades_RemoveBonus(frag_target);
+       return FALSE;
+ }
+ MUTATOR_HOOKFUNCTION(nades_PlayerDamage)
+ {
+       if(frag_target.frozen)
+       if(autocvar_g_freezetag_revive_nade)
+       if(frag_attacker == frag_target)
+       if(frag_deathtype == DEATH_NADE)
+       if(time - frag_inflictor.toss_time <= 0.1)
+       {
+               Unfreeze(frag_target);
+               frag_target.health = autocvar_g_freezetag_revive_nade_health;
+               pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
+               frag_damage = 0;
+               frag_force = '0 0 0';
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname);
+               Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+       }
+       
+       return FALSE;
+ }
+ MUTATOR_HOOKFUNCTION(nades_MonsterDies)
+ {
+       if(IS_PLAYER(frag_attacker))
+       if(DIFF_TEAM(frag_attacker, self))
+       if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
+               nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor);
  
        return FALSE;
  }
  
  MUTATOR_HOOKFUNCTION(nades_RemovePlayer)
  {
-       if(self.nade)
-               remove(self.nade);
+       nades_Clear(self);
+       nades_RemoveBonus(self);
+       return FALSE;
+ }
  
-       if(self.fake_nade)
-               remove(self.fake_nade);
+ MUTATOR_HOOKFUNCTION(nades_SpectateCopy)
+ {
+       self.nade_timer = other.nade_timer;
+       self.nade_type = other.nade_type;
+       self.pokenade_type = other.pokenade_type;
+       self.bonus_nades = other.bonus_nades;
+       self.bonus_nade_score = other.bonus_nade_score;
+       self.stat_healing_orb = other.stat_healing_orb;
+       self.stat_healing_orb_alpha = other.stat_healing_orb_alpha;
+       return FALSE;
+ }
+ MUTATOR_HOOKFUNCTION(nades_GetCvars)
+ {
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type");
+       GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type");
  
        return FALSE;
  }
@@@ -366,31 -1130,50 +1127,50 @@@ MUTATOR_HOOKFUNCTION(nades_BuildMutator
        return FALSE;
  }
  
+ void nades_Initialize()
+ {
+       addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer);
+       addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades);
+       addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type);
+       addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score);
+       addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb);
+       addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha);
+       
+       precache_model("models/ok_nade_counter/ok_nade_counter.md3");
+       precache_model("models/weapons/h_ok_grenade.iqm");
+       precache_model("models/weapons/v_ok_grenade.md3");
+       precache_model("models/ctf/shield.md3");
+       precache_sound("weapons/rocket_impact.wav");
+       precache_sound("weapons/grenade_bounce1.wav");
+       precache_sound("weapons/grenade_bounce2.wav");
+       precache_sound("weapons/grenade_bounce3.wav");
+       precache_sound("weapons/grenade_bounce4.wav");
+       precache_sound("weapons/grenade_bounce5.wav");
+       precache_sound("weapons/grenade_bounce6.wav");
+       precache_sound("overkill/grenadebip.ogg");
+ }
  MUTATOR_DEFINITION(mutator_nades)
  {
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, nades_CheckThrow, CBC_ORDER_LAST);
        MUTATOR_HOOK(VehicleEnter, nades_VehicleEnter, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPreThink, nades_PlayerPreThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, nades_PlayerSpawn, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerDies, nades_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDies, nades_PlayerDies, CBC_ORDER_LAST);
+       MUTATOR_HOOK(PlayerDamage_Calculate, nades_PlayerDamage, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MonsterDies, nades_MonsterDies, CBC_ORDER_ANY);
        MUTATOR_HOOK(MakePlayerObserver, nades_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientDisconnect, nades_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SpectateCopy, nades_SpectateCopy, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GetCvars, nades_GetCvars, CBC_ORDER_ANY);
+       MUTATOR_HOOK(reset_map_global, nades_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsString, nades_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsPrettyString, nades_BuildMutatorsPrettyString, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {
-               precache_model("models/ok_nade_counter/ok_nade_counter.md3");
-               precache_model("models/weapons/h_ok_grenade.iqm");
-               precache_model("models/weapons/v_ok_grenade.md3");
-               precache_sound("weapons/rocket_impact.wav");
-               precache_sound("weapons/grenade_bounce1.wav");
-               precache_sound("weapons/grenade_bounce2.wav");
-               precache_sound("weapons/grenade_bounce3.wav");
-               precache_sound("weapons/grenade_bounce4.wav");
-               precache_sound("weapons/grenade_bounce5.wav");
-               precache_sound("weapons/grenade_bounce6.wav");
-               precache_sound("overkill/grenadebip.ogg");
+               nades_Initialize();
        }
  
        return FALSE;
index 9e3023b7215496e3bce03026982b74b8f58d1df5,30b01b0daa0933351e754df39d45503c17ca2a66..5f02a8aba2f38ffd8fb783291d06a02fa7db70bc
@@@ -12,20 -12,22 +12,22 @@@ void PlayerTouchExplode(entity p1, enti
        entity e;
        e = spawn();
        setorigin(e, org);
 -      RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world);
 +      RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world);
        remove(e);
  }
  
  MUTATOR_HOOKFUNCTION(touchexplode_PlayerThink)
  {
        if(time > self.touchexplode_time)
-       if (!gameover)
+       if(!gameover)
+       if(!self.frozen)
        if(IS_PLAYER(self))
        if(self.deadflag == DEAD_NO)
        if (!IS_INDEPENDENT_PLAYER(self))
        FOR_EACH_PLAYER(other) if(self != other)
        {
                if(time > other.touchexplode_time)
+               if(!other.frozen)
                if(other.deadflag == DEAD_NO)
                if (!IS_INDEPENDENT_PLAYER(other))
                if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
diff --combined qcsrc/server/progs.src
index 9198c2411a73053ab849bcb3a00a2321ae1ea8fc,288b2477597c00114d0a3b0246c296f3e1360b51..9cc8c9954de7f377264353159510ff2ffa9bc99d
@@@ -11,11 -11,17 +11,14 @@@ sys-post.q
  ../warpzonelib/common.qh
  ../warpzonelib/util_server.qh
  ../warpzonelib/server.qh
 -
  ../common/constants.qh
+ ../common/stats.qh
  ../common/teams.qh
  ../common/util.qh
+ ../common/nades.qh
+ ../common/buffs.qh
  ../common/test.qh
  ../common/counting.qh
 -../common/items.qh
 -../common/explosion_equation.qh
  ../common/urllib.qh
  ../common/command/markup.qh
  ../common/command/rpn.qh
  ../common/monsters/sv_monsters.qh
  ../common/monsters/spawn.qh
  
 +../common/weapons/config.qh
 +../common/weapons/weapons.qh // TODO
 +weapons/accuracy.qh
 +weapons/common.qh
 +weapons/csqcprojectile.qh // TODO
 +weapons/hitplot.qh
 +weapons/selection.qh
 +weapons/spawning.qh
 +weapons/throwing.qh
 +weapons/tracing.qh
 +weapons/weaponstats.qh
 +weapons/weaponsystem.qh
 +
 +t_items.qh
 +
  autocvars.qh
  constants.qh
  defs.qh               // Should rename this, it has fields and globals
  ../common/notifications.qh // must be after autocvars
  ../common/deathtypes.qh // must be after notifications
  
- mutators/base.qh
- mutators/mutators.qh
- mutators/gamemode_assault.qh
- mutators/gamemode_ca.qh
- mutators/gamemode_ctf.qh
- mutators/gamemode_domination.qh
- mutators/gamemode_keyhunt.qh // TODO fix this
- mutators/gamemode_keepaway.qh
- mutators/gamemode_nexball.qh 
- mutators/gamemode_lms.qh
- mutators/gamemode_invasion.qh
- mutators/mutator_dodging.qh
- mutators/mutator_nades.qh
+ mutators/mutators_include.qh
  
  //// tZork Turrets ////
  tturrets/include/turrets_early.qh
@@@ -79,7 -58,8 +70,7 @@@ command/getreplies.q
  command/cmd.qh
  command/sv_cmd.qh
  
 -accuracy.qh
 -csqcprojectile.qh
 +
  ../common/csqcmodel_settings.qh
  ../csqcmodellib/common.qh
  ../csqcmodellib/sv_model.qh
@@@ -91,12 -71,16 +82,14 @@@ playerstats.q
  
  portals.qh
  
 -g_hook.qh
 -w_electro.qh
 -w_laser.qh
 +g_hook.qh // TODO
  
  scores.qh
  
  spawnpoints.qh
  
+ mapvoting.qh
  ipban.qh
  
  race.qh
@@@ -115,6 -99,8 +108,8 @@@ scores_rules.q
  
  miscfunctions.qc
  
+ mutators/mutators.qc
  waypointsprites.qc
  
  bot/bot.qc
@@@ -140,6 -126,8 +135,8 @@@ pathlib/pathlib.q
  g_world.qc
  g_casings.qc
  
+ mapvoting.qc
  t_jumppads.qc
  t_teleporters.qc
  
@@@ -152,20 -140,13 +149,20 @@@ g_models.q
  item_key.qc
  secret.qc
  
 -cl_weaponsystem.qc
 -w_common.qc
 -
 -w_all.qc
 +weapons/accuracy.qc
 +weapons/common.qc
 +weapons/csqcprojectile.qc // TODO
 +weapons/hitplot.qc
 +weapons/selection.qc
 +weapons/spawning.qc
 +weapons/throwing.qc
 +weapons/tracing.qc
 +weapons/weaponstats.qc
 +weapons/weaponsystem.qc
 +../common/weapons/config.qc
 +../common/weapons/weapons.qc // TODO
  
  t_items.qc
 -cl_weapons.qc
  cl_impulse.qc
  
  ent_cs.qc
@@@ -228,7 -209,15 +225,10 @@@ target_spawn.q
  func_breakable.qc
  target_music.qc
  
 -../common/items.qc
 -
+ ../common/nades.qc
+ ../common/buffs.qc
 -
 -accuracy.qc
  ../csqcmodellib/sv_model.qc
 -csqcprojectile.qc
  
  playerdemo.qc
  
@@@ -238,43 -227,14 +238,12 @@@ playerstats.q
  
  round_handler.qc
  
 -../common/explosion_equation.qc
 -
  ../common/monsters/sv_monsters.qc
  ../common/monsters/monsters.qc
  
  ../common/monsters/spawn.qc
  
- mutators/base.qc
- mutators/gamemode_assault.qc
- mutators/gamemode_ca.qc
- mutators/gamemode_ctf.qc
- mutators/gamemode_domination.qc
- mutators/gamemode_freezetag.qc
- mutators/gamemode_keyhunt.qc
- mutators/gamemode_keepaway.qc
- mutators/gamemode_nexball.qc
- mutators/gamemode_onslaught.qc
- mutators/gamemode_lms.qc
- mutators/gamemode_invasion.qc
- mutators/mutator_invincibleproj.qc
- mutators/mutator_new_toys.qc
- mutators/mutator_nix.qc
- mutators/mutator_dodging.qc
- mutators/mutator_rocketflying.qc
- mutators/mutator_vampire.qc
- mutators/mutator_spawn_near_teammate.qc
- mutators/mutator_physical_items.qc
- mutators/sandbox.qc
- mutators/mutator_superspec.qc
- mutators/mutator_minstagib.qc
- mutators/mutator_touchexplode.qc
- mutators/mutator_pinata.qc
- mutators/mutator_midair.qc
- mutators/mutator_bloodloss.qc
- mutators/mutator_random_gravity.qc
- mutators/mutator_multijump.qc
- mutators/mutator_melee_only.qc
- mutators/mutator_nades.qc
- mutators/mutator_campcheck.qc
+ mutators/mutators_include.qc
  
  ../warpzonelib/anglestransform.qc
  ../warpzonelib/mathlib.qc
diff --combined qcsrc/server/t_quake3.qc
index a7a2c18748c3685a8daab4d1971c96efa9ef83e4,86a87c043d1fd5b781c13201bdb6d242435286b0..30f1e8f8df5abbcd6d485f3cd2e99d16aa8347ad
@@@ -8,12 -8,13 +8,12 @@@
  void spawnfunc_ammo_shells()         { spawnfunc_item_shells();         }
  
  // MG -> MG
 -void spawnfunc_weapon_machinegun()   { spawnfunc_weapon_uzi();          }
  void spawnfunc_ammo_bullets()        { spawnfunc_item_bullets();        }
  
  // GL -> Mortar
  void spawnfunc_ammo_grenades()       { spawnfunc_item_rockets();        }
  
 -// LG -> Electro
 +// LG -> Lightning
  void spawnfunc_weapon_lightning()    { spawnfunc_weapon_electro();      }
  void spawnfunc_ammo_lightning()      { spawnfunc_item_cells();          }
  
  void spawnfunc_weapon_plasmagun()    { spawnfunc_weapon_hagar();        }
  void spawnfunc_ammo_cells()          { spawnfunc_item_rockets();        }
  
 -// Rail -> Nex
 -void spawnfunc_weapon_railgun()      { spawnfunc_weapon_nex();          }
 -void spawnfunc_ammo_slugs()          { spawnfunc_item_cells();          }
 +// Rail -> Vortex
 +void spawnfunc_weapon_railgun()      { spawnfunc_weapon_vortex();          }
 +void spawnfunc_ammo_slugs()          { spawnfunc_item_plasma();          }
  
  // BFG -> Crylink
  void spawnfunc_weapon_bfg()          { spawnfunc_weapon_crylink();      }
 -void spawnfunc_ammo_bfg()            { spawnfunc_item_cells();          }
 +void spawnfunc_ammo_bfg()            { spawnfunc_item_plasma();          }
  
  // RL -> RL
  void spawnfunc_ammo_rockets()        { spawnfunc_item_rockets();        }
@@@ -71,11 -72,11 +71,11 @@@ void target_give_init(
        entity targ;
        for (targ = world; (targ = find(targ, targetname, self.target)); ) {
                if (targ.classname == "weapon_rocketlauncher") {
 -                      self.ammo_rockets += targ.count * autocvar_g_balance_rocketlauncher_ammo;
 +                      self.ammo_rockets += targ.count * WEP_CVAR(devastator, ammo);
                        self.netname = "rocketlauncher";
                }
                else if (targ.classname == "weapon_plasmagun") {
 -                      self.ammo_rockets += targ.count * autocvar_g_balance_hagar_primary_ammo;
 +                      self.ammo_rockets += targ.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
                        if(self.netname == "")
                                self.netname = "hagar";
                        else
@@@ -89,7 -90,7 +89,7 @@@
                                self.netname = strcat(self.netname, " crylink");
                }
                else if (targ.classname == "weapon_grenadelauncher") {
 -                      self.ammo_rockets += targ.count * autocvar_g_balance_grenadelauncher_primary_ammo;
 +                      self.ammo_rockets += targ.count * autocvar_g_balance_mortar_primary_ammo; // WEAPONTODO
                        if(self.netname == "")
                                self.netname = "grenadelauncher";
                        else
@@@ -113,18 -114,24 +113,24 @@@ void spawnfunc_target_give(
        InitializeEntity(self, target_give_init, INITPRIO_FINDTARGET);
  }
  
- //void spawnfunc_item_flight()       /* not supported */
- //void spawnfunc_item_haste()        /* not supported */
+ //void spawnfunc_item_flight()       /* handled by buffs mutator or jetpack */
+ //void spawnfunc_item_haste()        /* handled by buffs mutator */
  //void spawnfunc_item_health()       /* handled in t_quake.qc */
  //void spawnfunc_item_health_large() /* handled in t_items.qc */
  //void spawnfunc_item_health_small() /* handled in t_items.qc */
  //void spawnfunc_item_health_mega()  /* handled in t_items.qc */
- //void spawnfunc_item_invis()        /* not supported */
- //void spawnfunc_item_regen()        /* not supported */
+ //void spawnfunc_item_invis()        /* handled by buffs mutator */
+ //void spawnfunc_item_regen()        /* handled by buffs mutator */
  
  // CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
  
- void spawnfunc_item_flight()         { spawnfunc_item_jetpack();       }
+ void spawnfunc_item_flight()
+ {
+       if(!cvar("g_buffs") || !cvar("g_buffs_flight"))
+               spawnfunc_item_jetpack();
+       else
+               buff_Init_Compat(self, BUFF_FLIGHT);
+ }
  
  .float notteam;
  .float notsingle;
diff --combined qcsrc/server/teamplay.qc
index 94633dbd86adc5b7597586775983c2acb66d2bc3,06286e8f55c7cfc4190895d5f4220125abf18362..0dd7fd453b9f07ede6af8032e25e26eea54dbb4e
@@@ -94,6 -94,10 +94,10 @@@ void InitGameplayMode(
                fraglimit_override = autocvar_g_domination_point_limit;
                leadlimit_override = autocvar_g_domination_point_leadlimit;
                MUTATOR_ADD(gamemode_domination);
+               if(autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
+                       fraglimit_override = autocvar_g_domination_roundbased_point_limit;
                have_team_spawns = -1; // request team spawns
        }
  
  
        if(g_race)
        {
                if(autocvar_g_race_teams)
                {
                        ActivateTeamplay();
                qualifying_override = autocvar_g_race_qualifying_timelimit_override;
                fraglimit_override = autocvar_g_race_laps_limit;
                leadlimit_override = 0; // currently not supported by race
+               MUTATOR_ADD(gamemode_race);
        }
  
        if(g_cts)
                g_race_qualifying = 1;
                fraglimit_override = 0;
                leadlimit_override = 0;
+               MUTATOR_ADD(gamemode_cts);
        }
  
        if(g_nexball)
        }
  
        if(g_race || g_cts)
-       {
-               if(g_race_qualifying)
-                       independent_players = 1;
-               ScoreRules_race();
-       }
+       if(g_race_qualifying)
+               independent_players = 1;
  
        InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
  }
@@@ -287,7 -289,7 +289,7 @@@ string getwelcomemessage(void
                else
                        modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena");
        }
 -      if(autocvar_g_start_weapon_laser == 0)
 +      if(cvar("g_balance_blaster_weaponstart") == 0)
                modifications = strcat(modifications, ", No start weapons");
        if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
                modifications = strcat(modifications, ", Low gravity");
        if (g_grappling_hook)
                s = strcat(s, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
  
+       if (cvar("g_nades"))
+               s = strcat(s, "\n\n^3nades^8 are enabled, press 'g' to use them\n");
        if(cache_lastmutatormsg != autocvar_g_mutatormsg)
        {
                if(cache_lastmutatormsg)
@@@ -420,10 -425,7 +425,7 @@@ void CheckAllowedTeams (entity for_whom
        else
        {
                // cover anything else by treating it like tdm with no teams spawned
-               if(g_race)
-                       dm = race_teams;
-               else
-                       dm = 2;
+               dm = 2;
  
                ret_float = dm;
                MUTATOR_CALLHOOK(GetTeamCount);
index 11daf4cc22ac8598a413b2d926f2f5b5e520ca2e,0000000000000000000000000000000000000000..3dd93c0581a3433a4a16a37bfb75f5438f7a253b
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,114 @@@
 +.float csqcprojectile_type;
 +
 +float CSQCProjectile_SendEntity(entity to, float sf)
 +{
 +      float ft, fr;
 +
 +      // note: flag 0x08 = no trail please (teleport bit)
 +      sf = sf & 0x0F;
 +
 +      if(self.csqcprojectile_clientanimate)
 +              sf |= 0x80; // client animated, not interpolated
 +
 +      if(self.flags & FL_ONGROUND)
 +              sf |= 0x40;
 +
 +      ft = fr = 0;
 +      if(self.fade_time != 0 || self.fade_rate != 0)
 +      {
 +              ft = (self.fade_time - time) / sys_frametime;
 +              fr = (1 / self.fade_rate) / sys_frametime;
 +              if(ft <= 255 && fr <= 255 && fr >= 1)
 +                      sf |= 0x20;
 +      }
 +
 +      if(self.gravity != 0)
 +              sf |= 0x10;
 +
 +      WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE);
 +      WriteByte(MSG_ENTITY, sf);
 +
 +      if(sf & 1)
 +      {
 +              WriteCoord(MSG_ENTITY, self.origin_x);
 +              WriteCoord(MSG_ENTITY, self.origin_y);
 +              WriteCoord(MSG_ENTITY, self.origin_z);
 +
 +              if(sf & 0x80)
 +              {
 +                      WriteCoord(MSG_ENTITY, self.velocity_x);
 +                      WriteCoord(MSG_ENTITY, self.velocity_y);
 +                      WriteCoord(MSG_ENTITY, self.velocity_z);
 +                      if(sf & 0x10)
 +                              WriteCoord(MSG_ENTITY, self.gravity);
 +              }
 +
 +              if(sf & 0x20)
 +              {
 +                      WriteByte(MSG_ENTITY, ft);
 +                      WriteByte(MSG_ENTITY, fr);
 +              }
++
++              WriteByte(MSG_ENTITY, self.realowner.team);
 +      }
 +
 +      if(sf & 2)
 +              WriteByte(MSG_ENTITY, self.csqcprojectile_type); // TODO maybe put this into sf?
 +
 +      return 1;
 +}
 +
 +.vector csqcprojectile_oldorigin;
 +void CSQCProjectile_Check(entity e)
 +{
 +      if(e.csqcprojectile_clientanimate)
 +      if(e.flags & FL_ONGROUND)
 +      if(e.origin != e.csqcprojectile_oldorigin)
 +              UpdateCSQCProjectile(e);
 +      e.csqcprojectile_oldorigin = e.origin;
 +}
 +
 +void CSQCProjectile(entity e, float clientanimate, float type, float docull)
 +{
 +      Net_LinkEntity(e, docull, 0, CSQCProjectile_SendEntity);
 +
 +      e.csqcprojectile_clientanimate = clientanimate;
 +
 +      if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE)
 +      {
 +              if(e.gravity == 0)
 +                      e.gravity = 1;
 +      }
 +      else
 +              e.gravity = 0;
 +
 +      if(!sound_allowed(MSG_BROADCAST, e))
 +              type |= 0x80;
 +      e.csqcprojectile_type = type;
 +}
 +
 +void UpdateCSQCProjectile(entity e)
 +{
 +      if(e.SendEntity == CSQCProjectile_SendEntity)
 +      {
 +              // send new origin data
 +              e.SendFlags |= 0x01;
 +      }
 +// FIXME HACK
 +      else if(e.SendEntity == ItemSend)
 +      {
 +              ItemUpdate(e);
 +      }
 +// END HACK
 +}
 +
 +void UpdateCSQCProjectileAfterTeleport(entity e)
 +{
 +      if(e.SendEntity == CSQCProjectile_SendEntity)
 +      {
 +              // send new origin data
 +              e.SendFlags |= 0x01;
 +              // mark as teleported
 +              e.SendFlags |= 0x08;
 +      }
 +}
index 7c8ce3511f3d28a7dab2bb8df555834e03ca58f7,0000000000000000000000000000000000000000..4eb6a736494a1a0ec53e5d9a041cda215acb4b9c
mode 100644,000000..100644
--- /dev/null
@@@ -1,938 -1,0 +1,942 @@@
-       if(self.freezetag_frozen)
 +/*
 +===========================================================================
 +
 +  CLIENT WEAPONSYSTEM CODE
 +  Bring back W_Weaponframe
 +
 +===========================================================================
 +*/
 +
 +.float weapon_frametime;
 +
 +float W_WeaponRateFactor()
 +{
 +      float t;
 +      t = 1.0 / g_weaponratefactor;
 +
++      weapon_rate = t;
++      MUTATOR_CALLHOOK(WeaponRateFactor);
++      t = weapon_rate;
++
 +      return t;
 +}
 +
 +// VorteX: static frame globals
 +const float WFRAME_DONTCHANGE = -1;
 +const float WFRAME_FIRE1 = 0;
 +const float WFRAME_FIRE2 = 1;
 +const float WFRAME_IDLE = 2;
 +const float WFRAME_RELOAD = 3;
 +.float wframe;
 +
 +void(float fr, float t, void() func) weapon_thinkf;
 +
 +float CL_Weaponentity_CustomizeEntityForClient()
 +{
 +      self.viewmodelforclient = self.owner;
 +      if(IS_SPEC(other))
 +              if(other.enemy == self.owner)
 +                      self.viewmodelforclient = other;
 +      return TRUE;
 +}
 +
 +/*
 + * supported formats:
 + *
 + * 1. simple animated model, muzzle flash handling on h_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
 + *      tags:
 + *        shot = muzzle end (shot origin, also used for muzzle flashes)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *        weapon = attachment for v_tuba.md3
 + *    v_tuba.md3 - first and third person model
 + *    g_tuba.md3 - pickup model
 + *
 + * 2. simple animated model, muzzle flash handling on v_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
 + *      tags:
 + *        weapon = attachment for v_tuba.md3
 + *    v_tuba.md3 - first and third person model
 + *      tags:
 + *        shot = muzzle end (shot origin, also used for muzzle flashes)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *    g_tuba.md3 - pickup model
 + *
 + * 3. fully animated model, muzzle flash handling on h_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
 + *      tags:
 + *        shot = muzzle end (shot origin, also used for muzzle flashes)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *        handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
 + *    v_tuba.md3 - third person model
 + *    g_tuba.md3 - pickup model
 + *
 + * 4. fully animated model, muzzle flash handling on v_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
 + *      tags:
 + *        shot = muzzle end (shot origin)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *    v_tuba.md3 - third person model
 + *      tags:
 + *        shot = muzzle end (for muzzle flashes)
 + *    g_tuba.md3 - pickup model
 + */
 +
 +// writes:
 +//   self.origin, self.angles
 +//   self.weaponentity
 +//   self.movedir, self.view_ofs
 +//   attachment stuff
 +//   anim stuff
 +// to free:
 +//   call again with ""
 +//   remove the ent
 +void CL_WeaponEntity_SetModel(string name)
 +{
 +      float v_shot_idx;
 +      if (name != "")
 +      {
 +              // if there is a child entity, hide it until we're sure we use it
 +              if (self.weaponentity)
 +                      self.weaponentity.model = "";
 +              setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
 +              v_shot_idx = gettagindex(self, "shot"); // used later
 +              if(!v_shot_idx)
 +                      v_shot_idx = gettagindex(self, "tag_shot");
 +
 +              setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
 +              // preset some defaults that work great for renamed zym files (which don't need an animinfo)
 +              self.anim_fire1  = animfixfps(self, '0 1 0.01', '0 0 0');
 +              self.anim_fire2  = animfixfps(self, '1 1 0.01', '0 0 0');
 +              self.anim_idle   = animfixfps(self, '2 1 0.01', '0 0 0');
 +              self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0');
 +
 +              // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
 +              // if we don't, this is a "real" animated model
 +              if(gettagindex(self, "weapon"))
 +              {
 +                      if (!self.weaponentity)
 +                              self.weaponentity = spawn();
 +                      setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
 +                      setattachment(self.weaponentity, self, "weapon");
 +              }
 +              else if(gettagindex(self, "tag_weapon"))
 +              {
 +                      if (!self.weaponentity)
 +                              self.weaponentity = spawn();
 +                      setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
 +                      setattachment(self.weaponentity, self, "tag_weapon");
 +              }
 +              else
 +              {
 +                      if(self.weaponentity)
 +                              remove(self.weaponentity);
 +                      self.weaponentity = world;
 +              }
 +
 +              setorigin(self,'0 0 0');
 +              self.angles = '0 0 0';
 +              self.frame = 0;
 +              self.viewmodelforclient = world;
 +
 +              float idx;
 +
 +              if(v_shot_idx) // v_ model attached to invisible h_ model
 +              {
 +                      self.movedir = gettaginfo(self.weaponentity, v_shot_idx);
 +              }
 +              else
 +              {
 +                      idx = gettagindex(self, "shot");
 +                      if(!idx)
 +                              idx = gettagindex(self, "tag_shot");
 +                      if(idx)
 +                              self.movedir = gettaginfo(self, idx);
 +                      else
 +                      {
 +                              print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
 +                              self.movedir = '0 0 0';
 +                      }
 +              }
 +
 +              if(self.weaponentity) // v_ model attached to invisible h_ model
 +              {
 +                      idx = gettagindex(self.weaponentity, "shell");
 +                      if(!idx)
 +                              idx = gettagindex(self.weaponentity, "tag_shell");
 +                      if(idx)
 +                              self.spawnorigin = gettaginfo(self.weaponentity, idx);
 +              }
 +              else
 +                      idx = 0;
 +              if(!idx)
 +              {
 +                      idx = gettagindex(self, "shell");
 +                      if(!idx)
 +                              idx = gettagindex(self, "tag_shell");
 +                      if(idx)
 +                              self.spawnorigin = gettaginfo(self, idx);
 +                      else
 +                      {
 +                              print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
 +                              self.spawnorigin = self.movedir;
 +                      }
 +              }
 +
 +              if(v_shot_idx)
 +              {
 +                      self.oldorigin = '0 0 0'; // use regular attachment
 +              }
 +              else
 +              {
 +                      if(self.weaponentity)
 +                      {
 +                              idx = gettagindex(self, "weapon");
 +                              if(!idx)
 +                                      idx = gettagindex(self, "tag_weapon");
 +                      }
 +                      else
 +                      {
 +                              idx = gettagindex(self, "handle");
 +                              if(!idx)
 +                                      idx = gettagindex(self, "tag_handle");
 +                      }
 +                      if(idx)
 +                      {
 +                              self.oldorigin = self.movedir - gettaginfo(self, idx);
 +                      }
 +                      else
 +                      {
 +                              print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
 +                              self.oldorigin = '0 0 0'; // there is no way to recover from this
 +                      }
 +              }
 +
 +              self.viewmodelforclient = self.owner;
 +      }
 +      else
 +      {
 +              self.model = "";
 +              if(self.weaponentity)
 +                      remove(self.weaponentity);
 +              self.weaponentity = world;
 +              self.movedir = '0 0 0';
 +              self.spawnorigin = '0 0 0';
 +              self.oldorigin = '0 0 0';
 +              self.anim_fire1  = '0 1 0.01';
 +              self.anim_fire2  = '0 1 0.01';
 +              self.anim_idle   = '0 1 0.01';
 +              self.anim_reload = '0 1 0.01';
 +      }
 +
 +      self.view_ofs = '0 0 0';
 +
 +      if(self.movedir_x >= 0)
 +      {
 +              vector v0;
 +              v0 = self.movedir;
 +              self.movedir = shotorg_adjust(v0, FALSE, FALSE);
 +              self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
 +      }
 +      self.owner.stat_shotorg = compressShotOrigin(self.movedir);
 +      self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
 +
 +      self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount
 +
 +      // check if an instant weapon switch occurred
 +      setorigin(self, self.view_ofs);
 +      // reset animstate now
 +      self.wframe = WFRAME_IDLE;
 +      setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
 +}
 +
 +vector CL_Weapon_GetShotOrg(float wpn)
 +{
 +      entity wi, oldself;
 +      vector ret;
 +      wi = get_weaponinfo(wpn);
 +      oldself = self;
 +      self = spawn();
 +      CL_WeaponEntity_SetModel(wi.mdl);
 +      ret = self.movedir;
 +      CL_WeaponEntity_SetModel("");
 +      remove(self);
 +      self = oldself;
 +      return ret;
 +}
 +
 +void CL_Weaponentity_Think()
 +{
 +      float tb;
 +      self.nextthink = time;
 +      if (intermission_running)
 +              self.frame = self.anim_idle_x;
 +      if (self.owner.weaponentity != self)
 +      {
 +              if (self.weaponentity)
 +                      remove(self.weaponentity);
 +              remove(self);
 +              return;
 +      }
 +      if (self.owner.deadflag != DEAD_NO)
 +      {
 +              self.model = "";
 +              if (self.weaponentity)
 +                      self.weaponentity.model = "";
 +              return;
 +      }
 +      if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
 +      {
 +              self.weaponname = self.owner.weaponname;
 +              self.dmg = self.owner.modelindex;
 +              self.deadflag = self.owner.deadflag;
 +
 +              CL_WeaponEntity_SetModel(self.owner.weaponname);
 +      }
 +
 +      tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
 +      self.effects = self.owner.effects & EFMASK_CHEAP;
 +      self.effects &= ~EF_LOWPRECISION;
 +      self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
 +      self.effects &= ~EF_TELEPORT_BIT;
 +      self.effects &= ~EF_RESTARTANIM_BIT;
 +      self.effects |= tb;
 +
 +      if(self.owner.alpha == default_player_alpha)
 +              self.alpha = default_weapon_alpha;
 +      else if(self.owner.alpha != 0)
 +              self.alpha = self.owner.alpha;
 +      else
 +              self.alpha = 1;
 +
 +      self.glowmod = self.owner.weaponentity_glowmod;
 +      self.colormap = self.owner.colormap;
 +      if (self.weaponentity)
 +      {
 +              self.weaponentity.effects = self.effects;
 +              self.weaponentity.alpha = self.alpha;
 +              self.weaponentity.colormap = self.colormap;
 +              self.weaponentity.glowmod = self.glowmod;
 +      }
 +
 +      self.angles = '0 0 0';
 +
 +      float f = (self.owner.weapon_nextthink - time);
 +      if (self.state == WS_RAISE && !intermission_running)
 +      {
 +              entity newwep = get_weaponinfo(self.owner.switchweapon);
 +              f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
 +              self.angles_x = -90 * f * f;
 +      }
 +      else if (self.state == WS_DROP && !intermission_running)
 +      {
 +              entity oldwep = get_weaponinfo(self.owner.weapon);
 +              f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop);
 +              self.angles_x = -90 * f * f;
 +      }
 +      else if (self.state == WS_CLEAR)
 +      {
 +              f = 1;
 +              self.angles_x = -90 * f * f;
 +      }
 +}
 +
 +void CL_ExteriorWeaponentity_Think()
 +{
 +      float tag_found;
 +      self.nextthink = time;
 +      if (self.owner.exteriorweaponentity != self)
 +      {
 +              remove(self);
 +              return;
 +      }
 +      if (self.owner.deadflag != DEAD_NO)
 +      {
 +              self.model = "";
 +              return;
 +      }
 +      if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
 +      {
 +              self.weaponname = self.owner.weaponname;
 +              self.dmg = self.owner.modelindex;
 +              self.deadflag = self.owner.deadflag;
 +              if (self.owner.weaponname != "")
 +                      setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
 +              else
 +                      self.model = "";
 +
 +              if((tag_found = gettagindex(self.owner, "tag_weapon")))
 +              {
 +                      self.tag_index = tag_found;
 +                      self.tag_entity = self.owner;
 +              }
 +              else
 +                      setattachment(self, self.owner, "bip01 r hand");
 +      }
 +      self.effects = self.owner.effects;
 +      self.effects |= EF_LOWPRECISION;
 +      self.effects = self.effects & EFMASK_CHEAP; // eat performance
 +      if(self.owner.alpha == default_player_alpha)
 +              self.alpha = default_weapon_alpha;
 +      else if(self.owner.alpha != 0)
 +              self.alpha = self.owner.alpha;
 +      else
 +              self.alpha = 1;
 +
 +      self.glowmod = self.owner.weaponentity_glowmod;
 +      self.colormap = self.owner.colormap;
 +
 +      CSQCMODEL_AUTOUPDATE();
 +}
 +
 +// spawning weaponentity for client
 +void CL_SpawnWeaponentity()
 +{
 +      self.weaponentity = spawn();
 +      self.weaponentity.classname = "weaponentity";
 +      self.weaponentity.solid = SOLID_NOT;
 +      self.weaponentity.owner = self;
 +      setmodel(self.weaponentity, ""); // precision set when changed
 +      setorigin(self.weaponentity, '0 0 0');
 +      self.weaponentity.angles = '0 0 0';
 +      self.weaponentity.viewmodelforclient = self;
 +      self.weaponentity.flags = 0;
 +      self.weaponentity.think = CL_Weaponentity_Think;
 +      self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
 +      self.weaponentity.nextthink = time;
 +
 +      self.exteriorweaponentity = spawn();
 +      self.exteriorweaponentity.classname = "exteriorweaponentity";
 +      self.exteriorweaponentity.solid = SOLID_NOT;
 +      self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
 +      self.exteriorweaponentity.owner = self;
 +      setorigin(self.exteriorweaponentity, '0 0 0');
 +      self.exteriorweaponentity.angles = '0 0 0';
 +      self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
 +      self.exteriorweaponentity.nextthink = time;
 +
 +      {
 +              entity oldself = self;
 +              self = self.exteriorweaponentity;
 +              CSQCMODEL_AUTOINIT();
 +              self = oldself;
 +      }
 +}
 +
 +// Weapon subs
 +void w_clear()
 +{
 +      if (self.weapon != -1)
 +      {
 +              self.weapon = 0;
 +              self.switchingweapon = 0;
 +      }
 +      if (self.weaponentity)
 +      {
 +              self.weaponentity.state = WS_CLEAR;
 +              self.weaponentity.effects = 0;
 +      }
 +}
 +
 +void w_ready()
 +{
 +      if (self.weaponentity)
 +              self.weaponentity.state = WS_READY;
 +      weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
 +}
 +
 +.float prevdryfire;
 +.float prevwarntime;
 +float weapon_prepareattack_checkammo(float secondary)
 +{
 +      if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +      if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary))
 +      {
 +              // always keep the Mine Layer if we placed mines, so that we can detonate them
 +              entity mine;
 +              if(self.weapon == WEP_MINE_LAYER)
 +              for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
 +                      return FALSE;
 +
 +              if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
 +              {
 +                      sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
 +                      self.prevdryfire = time;
 +              }
 +
 +              if(WEP_ACTION(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo
 +              {
 +                      if(time - self.prevwarntime > 1)
 +                      {
 +                              Send_Notification(
 +                                      NOTIF_ONE,
 +                                      self,
 +                                      MSG_MULTI,
 +                                      ITEM_WEAPON_PRIMORSEC,
 +                                      self.weapon,
 +                                      secondary,
 +                                      (1 - secondary)
 +                              );
 +                      }
 +                      self.prevwarntime = time;
 +              }
 +              else // this weapon is totally unable to fire, switch to another one
 +              {
 +                      W_SwitchToOtherWeapon(self);
 +              }
 +
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +.float race_penalty;
 +float weapon_prepareattack_check(float secondary, float attacktime)
 +{
 +      if(!weapon_prepareattack_checkammo(secondary))
 +              return FALSE;
 +
 +      //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
 +      //if all players readied up and the countdown is running
 +      if(time < game_starttime || time < self.race_penalty) {
 +              return FALSE;
 +      }
 +
 +      if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
 +              return FALSE;
 +
 +      // do not even think about shooting if switching
 +      if(self.switchweapon != self.weapon)
 +              return FALSE;
 +
 +      if(attacktime >= 0)
 +      {
 +              // don't fire if previous attack is not finished
 +              if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5)
 +                      return FALSE;
 +              // don't fire while changing weapon
 +              if (self.weaponentity.state != WS_READY)
 +                      return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +float weapon_prepareattack_do(float secondary, float attacktime)
 +{
 +      self.weaponentity.state = WS_INUSE;
 +
 +      self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
 +
 +      // if the weapon hasn't been firing continuously, reset the timer
 +      if(attacktime >= 0)
 +      {
 +              if (ATTACK_FINISHED(self) < time - self.weapon_frametime * 1.5)
 +              {
 +                      ATTACK_FINISHED(self) = time;
 +                      //dprint("resetting attack finished to ", ftos(time), "\n");
 +              }
 +              ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime * W_WeaponRateFactor();
 +      }
 +      self.bulletcounter += 1;
 +      //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
 +      return TRUE;
 +}
 +float weapon_prepareattack(float secondary, float attacktime)
 +{
 +      if(weapon_prepareattack_check(secondary, attacktime))
 +      {
 +              weapon_prepareattack_do(secondary, attacktime);
 +              return TRUE;
 +      }
 +      else
 +              return FALSE;
 +}
 +
 +void weapon_thinkf(float fr, float t, void() func)
 +{
 +      vector a;
 +      vector of, or, ou;
 +      float restartanim;
 +
 +      if(fr == WFRAME_DONTCHANGE)
 +      {
 +              fr = self.weaponentity.wframe;
 +              restartanim = FALSE;
 +      }
 +      else if (fr == WFRAME_IDLE)
 +              restartanim = FALSE;
 +      else
 +              restartanim = TRUE;
 +
 +      of = v_forward;
 +      or = v_right;
 +      ou = v_up;
 +
 +      if (self.weaponentity)
 +      {
 +              self.weaponentity.wframe = fr;
 +              a = '0 0 0';
 +              if (fr == WFRAME_IDLE)
 +                      a = self.weaponentity.anim_idle;
 +              else if (fr == WFRAME_FIRE1)
 +                      a = self.weaponentity.anim_fire1;
 +              else if (fr == WFRAME_FIRE2)
 +                      a = self.weaponentity.anim_fire2;
 +              else // if (fr == WFRAME_RELOAD)
 +                      a = self.weaponentity.anim_reload;
 +              a_z *= g_weaponratefactor;
 +              setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
 +      }
 +
 +      v_forward = of;
 +      v_right = or;
 +      v_up = ou;
 +
 +      if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE)
 +      {
 +              backtrace("Tried to override initial weapon think function - should this really happen?");
 +      }
 +
 +      t *= W_WeaponRateFactor();
 +
 +      // VorteX: haste can be added here
 +      if (self.weapon_think == w_ready)
 +      {
 +              self.weapon_nextthink = time;
 +              //dprint("started firing at ", ftos(time), "\n");
 +      }
 +      if (self.weapon_nextthink < time - self.weapon_frametime * 1.5 || self.weapon_nextthink > time + self.weapon_frametime * 1.5)
 +      {
 +              self.weapon_nextthink = time;
 +              //dprint("reset weapon animation timer at ", ftos(time), "\n");
 +      }
 +      self.weapon_nextthink = self.weapon_nextthink + t;
 +      self.weapon_think = func;
 +      //dprint("next ", ftos(self.weapon_nextthink), "\n");
 +
 +      if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
 +      {
 +              if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2)
 +                      animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
 +              else
 +                      animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
 +      }
 +      else
 +      {
 +              if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE)
 +                      self.anim_upper_action = 0;
 +      }
 +}
 +
 +float forbidWeaponUse()
 +{
 +      if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
 +              return 1;
 +      if(round_handler_IsActive() && !round_handler_IsRoundStarted())
 +              return 1;
 +      if(self.player_blocked)
 +              return 1;
++      if(self.frozen)
 +              return 1;
 +      return 0;
 +}
 +
 +void W_WeaponFrame()
 +{
 +      vector fo, ri, up;
 +
 +      if (frametime)
 +              self.weapon_frametime = frametime;
 +
 +      if (!self.weaponentity || self.health < 1)
 +              return; // Dead player can't use weapons and injure impulse commands
 +
 +      if(forbidWeaponUse())
 +      if(self.weaponentity.state != WS_CLEAR)
 +      {
 +              w_ready();
 +              return;
 +      }
 +
 +      if(!self.switchweapon)
 +      {
 +              self.weapon = 0;
 +              self.switchingweapon = 0;
 +              self.weaponentity.state = WS_CLEAR;
 +              self.weaponname = "";
 +              //self.items &= ~IT_AMMO;
 +              return;
 +      }
 +
 +      makevectors(self.v_angle);
 +      fo = v_forward; // save them in case the weapon think functions change it
 +      ri = v_right;
 +      up = v_up;
 +
 +      // Change weapon
 +      if (self.weapon != self.switchweapon)
 +      {
 +              if (self.weaponentity.state == WS_CLEAR)
 +              {
 +                      // end switching!
 +                      self.switchingweapon = self.switchweapon;
 +                      entity newwep = get_weaponinfo(self.switchweapon);
 +
 +                      // the two weapon entities will notice this has changed and update their models
 +                      self.weapon = self.switchweapon;
 +                      self.weaponname = newwep.mdl;
 +                      self.bulletcounter = 0;
 +                      //self.ammo_field = newwep.ammo_field;
 +                      WEP_ACTION(self.switchweapon, WR_SETUP);
 +                      self.weaponentity.state = WS_RAISE;
 +
 +                      // set our clip load to the load of the weapon we switched to, if it's reloadable
 +                      if(newwep.spawnflags & WEP_FLAG_RELOADABLE && newwep.reloading_ammo) // prevent accessing undefined cvars
 +                      {
 +                              self.clip_load = self.(weapon_load[self.switchweapon]);
 +                              self.clip_size = newwep.reloading_ammo;
 +                      }
 +                      else
 +                              self.clip_load = self.clip_size = 0;
 +
 +                      weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
 +              }
 +              else if (self.weaponentity.state == WS_DROP)
 +              {
 +                      // in dropping phase we can switch at any time
 +                      self.switchingweapon = self.switchweapon;
 +              }
 +              else if (self.weaponentity.state == WS_READY)
 +              {
 +                      // start switching!
 +                      self.switchingweapon = self.switchweapon;
 +                      entity oldwep = get_weaponinfo(self.weapon);
 +
 +                      // set up weapon switch think in the future, and start drop anim
 +                      #ifndef INDEPENDENT_ATTACK_FINISHED
 +                      if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
 +                      {
 +                      #endif
 +                              sound(self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
 +                              self.weaponentity.state = WS_DROP;
 +                              weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
 +                      #ifndef INDEPENDENT_ATTACK_FINISHED
 +                      }
 +                      #endif
 +              }
 +      }
 +
 +      // LordHavoc: network timing test code
 +      //if (self.button0)
 +      //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");
 +
 +      float w;
 +      w = self.weapon;
 +
 +      // call the think code which may fire the weapon
 +      // and do so multiple times to resolve framerate dependency issues if the
 +      // server framerate is very low and the weapon fire rate very high
 +      float c;
 +      c = 0;
 +      while (c < W_TICSPERFRAME)
 +      {
 +              c = c + 1;
 +              if(w && !(self.weapons & WepSet_FromWeapon(w)))
 +              {
 +                      if(self.weapon == self.switchweapon)
 +                              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 +                      w = 0;
 +              }
 +
 +              v_forward = fo;
 +              v_right = ri;
 +              v_up = up;
 +
 +              if(w)
 +                      WEP_ACTION(self.weapon, WR_THINK);
 +              else
 +                      WEP_ACTION(self.weapon, WR_GONETHINK);
 +
 +              if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)
 +              {
 +                      if(self.weapon_think)
 +                      {
 +                              v_forward = fo;
 +                              v_right = ri;
 +                              v_up = up;
 +                              self.weapon_think();
 +                      }
 +                      else
 +                              bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");
 +              }
 +      }
 +}
 +
 +void W_AttachToShotorg(entity flash, vector offset)
 +{
 +      entity xflash;
 +      flash.owner = self;
 +      flash.angles_z = random() * 360;
 +
 +      if(gettagindex(self.weaponentity, "shot"))
 +              setattachment(flash, self.weaponentity, "shot");
 +      else
 +              setattachment(flash, self.weaponentity, "tag_shot");
 +      setorigin(flash, offset);
 +
 +      xflash = spawn();
 +      copyentity(flash, xflash);
 +
 +      flash.viewmodelforclient = self;
 +
 +      if(self.weaponentity.oldorigin_x > 0)
 +      {
 +              setattachment(xflash, self.exteriorweaponentity, "");
 +              setorigin(xflash, self.weaponentity.oldorigin + offset);
 +      }
 +      else
 +      {
 +              if(gettagindex(self.exteriorweaponentity, "shot"))
 +                      setattachment(xflash, self.exteriorweaponentity, "shot");
 +              else
 +                      setattachment(xflash, self.exteriorweaponentity, "tag_shot");
 +              setorigin(xflash, offset);
 +      }
 +}
 +
 +void W_DecreaseAmmo(float ammo_use)
 +{
 +      entity wep = get_weaponinfo(self.weapon);
 +
 +      if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo)
 +              return;
 +
 +      // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
 +      if(wep.reloading_ammo)
 +      {
 +              self.clip_load -= ammo_use;
 +              self.(weapon_load[self.weapon]) = self.clip_load;
 +      }
 +      else if(wep.ammo_field != ammo_none)
 +      {
 +              self.(wep.ammo_field) -= ammo_use;
 +              if(self.(wep.ammo_field) < 0)
 +              {
 +                      backtrace(sprintf(
 +                              "W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... "
 +                              "Please notify Samual immediately with a copy of this backtrace!\n",
 +                              ammo_use,
 +                              wep.netname,
 +                              GetAmmoPicture(wep.ammo_field),
 +                              self.netname,
 +                              self.(wep.ammo_field)
 +                      ));
 +              }
 +      }
 +}
 +
 +// weapon reloading code
 +
 +.float reload_ammo_amount, reload_ammo_min, reload_time;
 +.float reload_complain;
 +.string reload_sound;
 +
 +void W_ReloadedAndReady()
 +{
 +      // finish the reloading process, and do the ammo transfer
 +
 +      self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
 +
 +      // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
 +      if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO || self.ammo_field == ammo_none)
 +              self.clip_load = self.reload_ammo_amount;
 +      else
 +      {
 +              while(self.clip_load < self.reload_ammo_amount && self.(self.ammo_field)) // make sure we don't add more ammo than we have
 +              {
 +                      self.clip_load += 1;
 +                      self.(self.ammo_field) -= 1;
 +              }
 +      }
 +      self.(weapon_load[self.weapon]) = self.clip_load;
 +
 +      // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
 +      // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
 +      // so your weapon is disabled for a few seconds without reason
 +
 +      //ATTACK_FINISHED(self) -= self.reload_time - 1;
 +
 +      w_ready();
 +}
 +
 +void W_Reload(float sent_ammo_min, string sent_sound)
 +{
 +      // set global values to work with
 +      entity e;
 +      e = get_weaponinfo(self.weapon);
 +
 +      self.reload_ammo_min = sent_ammo_min;
 +      self.reload_ammo_amount = e.reloading_ammo;;
 +      self.reload_time = e.reloading_time;
 +      self.reload_sound = sent_sound;
 +
 +      // don't reload weapons that don't have the RELOADABLE flag
 +      if (!(e.spawnflags & WEP_FLAG_RELOADABLE))
 +      {
 +              dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
 +              return;
 +      }
 +
 +      // return if reloading is disabled for this weapon
 +      if(!self.reload_ammo_amount)
 +              return;
 +
 +      // our weapon is fully loaded, no need to reload
 +      if (self.clip_load >= self.reload_ammo_amount)
 +              return;
 +
 +      // no ammo, so nothing to load
 +      if(self.ammo_field != ammo_none)
 +      if(!self.(self.ammo_field) && self.reload_ammo_min)
 +      if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
 +      {
 +              if(IS_REAL_CLIENT(self) && self.reload_complain < time)
 +              {
 +                      play2(self, "weapons/unavailable.wav");
 +                      sprint(self, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(self.weapon), "\n"));
 +                      self.reload_complain = time + 1;
 +              }
 +              // switch away if the amount of ammo is not enough to keep using this weapon
 +              if (!(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2)))
 +              {
 +                      self.clip_load = -1; // reload later
 +                      W_SwitchToOtherWeapon(self);
 +              }
 +              return;
 +      }
 +
 +      if (self.weaponentity)
 +      {
 +              if (self.weaponentity.wframe == WFRAME_RELOAD)
 +                      return;
 +
 +              // allow switching away while reloading, but this will cause a new reload!
 +              self.weaponentity.state = WS_READY;
 +      }
 +
 +      // now begin the reloading process
 +
 +      sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
 +
 +      // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
 +      // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
 +      // so your weapon is disabled for a few seconds without reason
 +
 +      //ATTACK_FINISHED(self) = max(time, ATTACK_FINISHED(self)) + self.reload_time + 1;
 +
 +      weapon_thinkf(WFRAME_RELOAD, self.reload_time, W_ReloadedAndReady);
 +
 +      if(self.clip_load < 0)
 +              self.clip_load = 0;
 +      self.old_clip_load = self.clip_load;
 +      self.clip_load = self.(weapon_load[self.weapon]) = -1;
 +}