]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/ons_updates
authorMario <zacjardine@y7mail.com>
Tue, 20 Jan 2015 02:40:00 +0000 (13:40 +1100)
committerMario <zacjardine@y7mail.com>
Tue, 20 Jan 2015 02:40:00 +0000 (13:40 +1100)
Conflicts:
qcsrc/client/View.qc
qcsrc/client/scoreboard.qc
qcsrc/common/constants.qh
qcsrc/common/mapinfo.qh
qcsrc/common/notifications.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/g_damage.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/progs.src

26 files changed:
1  2 
defaultXonotic.cfg
gamemodes.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/hud.qh
qcsrc/client/mapvoting.qc
qcsrc/client/progs.src
qcsrc/client/scoreboard.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/mapinfo.qh
qcsrc/common/notifications.qh
qcsrc/common/stats.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/gamemode_onslaught.qh
qcsrc/server/mutators/mutators_include.qh
qcsrc/server/progs.src
qcsrc/server/teamplay.qc

diff --combined defaultXonotic.cfg
index cf6111890827e598f642276a236e708482f993fe,df7044ce562a41c915e9d59d7dc9d7ba5996b683..7444b2e15de629fc233afa2792d8fafc0b635717
@@@ -10,7 -10,7 +10,7 @@@
  // e.g. Xonotic 1.5.1 RC1 will be 15101
  set g_xonoticversion git "Xonotic version (formatted for humans)"
  
- gameversion 700 // 0.7.0
+ gameversion 800 // 0.8.0
  gameversion_min 0 // git builds see all versions
  gameversion_max 65535 // git builds see all versions
  
@@@ -34,6 -34,8 +34,8 @@@ seta g_configversion 0        "Configuration f
  // other aliases
  alias +hook +button6
  alias -hook -button6
+ alias +jetpack +button10
+ alias -jetpack -button10
  alias use "impulse 21"
  
  // for backwards compatibility
@@@ -44,23 -46,22 +46,22 @@@ alias dropweapon "impulse 17
  alias +show_info +button7
  alias -show_info -button7
  
- bind f6 team_auto
  // merge lightmaps up to 2048x2048 textures
  mod_q3bsp_lightmapmergepower 4
  
  // player defaults
  _cl_color "112.211" // same effect as 112, but menuqc can detect this as the default and not intentionally set
- _cl_name Player
+ _cl_name ""
  _cl_playermodel models/player/erebus.iqm
  _cl_playerskin 0
  
  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"
+ seta cl_velocityzoom_enabled 0 "velocity based zooming of fov"
+ seta cl_velocityzoom_factor 0 "factor of fov zooming (negative values zoom out)"
  seta cl_velocityzoom_type 3 "how to factor in speed, 1 = all velocity in all directions, 2 = velocity only in forward direction (can be negative), 3 = velocity only in forward direction (limited to forward only)"
  seta cl_velocityzoom_speed 1000 "target speed for fov factoring"
  seta cl_velocityzoom_time 0.2 "time value for averaging speed values"
@@@ -220,18 -221,19 +221,21 @@@ set sv_ready_restart 0 "if set to 1 all
  set sv_ready_restart_after_countdown 0        "if set to 1 the players and map items are reset after the countdown ended, otherwise they're reset already at the beginning of the countdown"
  set sv_ready_restart_repeatable 0     "allows the players to restart the game as often as needed"
  
- seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy"
+ seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy, 1: same pitch 2: increase pitch with more damage 3: decrease pitch with more damage"
  set cl_hitsound_antispam_time 0.05 "don't play the hitsound more often than this"
+ seta cl_hitsound_min_pitch 0.75 "minimum pitch of hit sound"
+ seta cl_hitsound_max_pitch 1.5 "maximum pitch of hit sound"
+ seta cl_hitsound_nom_damage 25 "damage amount at which hitsound bases pitch off"
  
- seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead"
+ seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead; set to 2 to active the effect only when the corpse doesn't move anymore"
  seta cl_eventchase_nexball 1 "camera goes into 3rd person mode when in nexball game-mode"
  seta cl_eventchase_distance 140 "final camera distance"
 +seta cl_eventchase_distance 400 "final camera distance while viewing generator explosion"
  seta cl_eventchase_speed 1.3 "how fast the camera slides back, 0 is instant"
  seta cl_eventchase_maxs "12 12 8" "max size of eventchase camera bbox"
  seta cl_eventchase_mins "-12 -12 -8" "min size of eventchase camera bbox"
  seta cl_eventchase_viewoffset "0 0 20" "viewoffset of eventchase camera"
 +seta cl_eventchase_generator_viewoffset "0 0 80" "viewoffset of eventchase camera while viewing generator explosion"
  
  //nifreks lockonrestart feature, used in team-based game modes, if set to 1 and all players readied up no other player can then join the game anymore, useful to block spectators from joining
  set teamplay_lockonrestart 0 "it set to 1 in a team-based game, the teams are locked once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)"
@@@ -259,8 -261,8 +263,8 @@@ set sv_timeout_number 2    "how many timeo
  set sv_timeout_leadtime 4     "how long the players will be informed that a timeout was called before it starts, in seconds"
  set sv_timeout_resumetime 3   "how long the remaining timeout-time will be after a player called the timein command"
  
- set g_allow_oldnexbeam 0 "If enabled, clients are allowed to use old v2.3 Nexgun beam"
- seta cl_particles_oldnexbeam 0 "Uses the old v2.3 Nexgun beam instead of the new beam, only works if server allows it (g_allow_oldnexbeam 1)"
+ set g_allow_oldvortexbeam 0 "If enabled, clients are allowed to use old v2.3 Vortex beam"
+ seta cl_particles_oldvortexbeam 0 "Uses the old v2.3 Vortex beam instead of the new beam, only works if server allows it (g_allow_oldvortexbeam 1)"
  
  set g_telefrags 1 "telefragging, i.e. killing someone who stands in the way of someone who is teleporting"
  set g_telefrags_teamplay 1 "never telefrag team mates"
@@@ -362,9 -364,9 +366,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 mortar hagar hlac crylink blaster 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 devastator 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"
@@@ -418,9 -420,7 +422,7 @@@ set g_use_ammunition 1 "if set to 0 al
  set g_pickup_items -1 "if set to 0 all items (health, armor, ammo, weapons...) are removed from the map, if 1 they are forced to spawn"
  set g_weaponarena "0" "put in a list of weapons to enable a weapon arena mode, or try \"all\" or \"most\""
  set g_weaponarena_random "0"  "if set to a number, only that weapon count is given on every spawn (randomly)"
- set g_weaponarena_random_with_laser "1"       "additionally, always provide the laser in random weapon arena games"
- set g_midair 0 "if set to 1 you can only apply damage to your opponent while he is airborne"
- set g_midair_shieldtime 0.3 "number of seconds you are still invincible since you lost contact to the ground"
+ set g_weaponarena_random_with_blaster "1"     "additionally, always provide the blaster in random weapon arena games"
  set g_spawnpoints_auto_move_out_of_solid 0 "if set to 1 you will see a warning if a spawn point was placed inside a solid"
  set g_forced_respawn 0 "if set to 1 and a player died, that player gets automatically respawned once <g_respawn_delay> seconds are over"
  set g_fullbrightplayers 0 "brightens up player models (note that the color, skin or model of the players does not change!)"
@@@ -458,8 -458,6 +460,6 @@@ seta menu_sandbox_edit_physics 
  seta menu_sandbox_edit_force 1
  seta menu_sandbox_edit_material ""
  
- bind f7 menu_showsandboxtools
  seta menu_monsters_edit_spawn ""
  seta menu_monsters_edit_skin 0
  seta menu_monsters_edit_movetarget 1
@@@ -470,7 -468,7 +470,7 @@@ set g_botclip_collisions 1 "0 = disabl
  set g_grappling_hook 0 "let players spawn with the grappling hook which allows them to pull themselves up"
  
  set g_spawn_alloweffects 1 "allow clients to enable spawn point and event effects such as particles and sounds, see cl_spawn_ cvars for more info"
- set g_spawn_furthest 1 "this amount of the spawns shall be far away from any players"
+ set g_spawn_furthest 0.5 "this amount of the spawns shall be far away from any players"
  set g_spawn_useallspawns 0 "use all spawns, e.g. also team spawns in non-teamplay, and all spawns, even enemy spawns, in teamplay"
  // respawn delay
  set g_respawn_delay_small 2 "small game number of seconds you have to wait before you can respawn again"
@@@ -489,7 -487,7 +489,7 @@@ seta timelimit_suddendeath 5 "number o
  set g_tdm 0 "Team Deathmatch: the team who kills their opponents most often wins"
  set g_tdm_on_dm_maps 0 "when this is set, all DM maps automatically support TDM"
  
- seta teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the following four cvars"
+ seta teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the cvars g_mirrordamage*, g_friendlyfire* and g_teamdamage_threshold*"
  seta g_mirrordamage 0.700000  "for teamplay 4: mirror damage factor"
  seta g_mirrordamage_virtual 1 "for teamplay 4: do not actually apply mirror damage, just show graphics effect for it"
  seta g_friendlyfire 0.500000  "for teamplay 4: fiendly fire factor"
@@@ -510,8 -508,6 +510,6 @@@ set g_bloodloss 0   "amount of health b
  
  set g_footsteps 1     "serverside footstep sounds"
  
- set g_deathglow 1.25 "when enabled, players stop glowing after they die (the value specifies glow fading speed)"
  set g_multijump 0     "Number of multiple jumps to allow (jumping again in the air), -1 allows for infinite jumps"
  set g_multijump_add 0 "0 = make the current z velocity equal to jumpvelocity, 1 = add jumpvelocity to the current z velocity"
  set g_multijump_speed -999999 "Minimum vertical speed a player must have in order to jump again"
@@@ -546,12 -542,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"
@@@ -563,7 -559,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
@@@ -630,19 -626,6 +628,6 @@@ alias togglezoom "${_togglezoom}zoom
  
  alias reload "impulse 20"
  
- // movement
- bind w +forward
- bind a +moveleft
- bind s +back
- bind d +moveright
- bind UPARROW +forward
- bind LEFTARROW +moveleft
- bind DOWNARROW +back
- bind RIGHTARROW +moveright
- bind SHIFT +crouch
- bind ENTER +jump
- bind SPACE +jump
  // weapons
  alias weapon_group_1 "impulse 1"
  alias weapon_group_2 "impulse 2"
@@@ -655,102 -638,6 +640,6 @@@ alias weapon_group_8 "impulse 8
  alias weapon_group_9 "impulse 9"
  alias weapon_group_0 "impulse 14" // cycles the superweapons
  exec weapons.cfg
- bind 0 weapon_group_0
- bind 1 weapon_group_1
- bind 2 weapon_group_2
- bind 3 weapon_group_3
- bind 4 weapon_group_4
- bind 5 weapon_group_5
- bind 6 weapon_group_6
- bind 7 weapon_group_7
- bind 8 weapon_group_8
- bind 9 weapon_group_9
- bind q weaplast
- bind MOUSE1 +fire
- bind MOUSE2 +fire2
- bind MOUSE3 togglezoom
- bind MOUSE4 weaplast
- bind MOUSE5 +hook
- bind MWHEELUP weapnext
- bind MWHEELDOWN weapprev
- bind r reload
- bind BACKSPACE dropweapon
- bind g dropweapon
- bind f +use
- bind v +button8 // drag object
- // misc
- bind e +hook
- bind ` toggleconsole
- bind ~ toggleconsole
- bind TAB +showscores
- bind ESCAPE togglemenu
- bind t messagemode
- bind y messagemode2
- bind z messagemode2
- bind u "+con_chat_maximize"
- bind m +hud_panel_radar_maximized
- bind i +show_info
- bind PAUSE pause
- bind F10 menu_showquitdialog
- bind F11 disconnect
- bind F12 screenshot
- bind F4 ready
- bind ALT +showaccuracy
- // Gamepad defaults. Tested with Logitech Rumblepad 2, I hope similar ones works as well.
- bind JOY1 "+crouch"
- bind JOY2 "+jump"
- bind JOY3 "weapprev"
- bind JOY4 "weapnext"
- bind JOY5 "+fire2"
- bind JOY6 "+fire"
- bind JOY7 "+zoom"
- bind JOY8 "dropweapon"
- bind JOY9 "menu_showteamselect"
- bind JOY10 "+show_info"
- bind JOY11 "+showscores"
- bind JOY12 "+con_chat_maximize"
- seta joyadvanced "1"
- seta joyadvaxisr "2"
- seta joyadvaxisx "3"
- seta joyadvaxisy "1"
- seta joyadvaxisz "4"
- seta joysidesensitivity "1.0"
- seta joypitchsensitivity "0.9"
- seta joyyawsensitivity "-1.8"
- // SDL only
- seta joy_deadzoneforward "0.05"
- seta joy_deadzonepitch "0.05"
- seta joy_deadzoneside "0.05"
- seta joy_deadzoneup "0.05"
- seta joy_deadzoneyaw "0.05"
- seta joy_sensitivitypitch "0.9"
- seta joy_sensitivityyaw "-1.8"
- // team say
- bind kp_ins messagemode
- bind kp_del messagemode2
- bind kp_end "+userbind 1"
- bind kp_downarrow "+userbind 2"
- bind kp_pgdn "+userbind 3"
- bind kp_leftarrow "+userbind 4"
- bind kp_5 "+userbind 6"
- bind kp_rightarrow "+userbind 7"
- bind kp_home "+userbind 9"
- bind kp_uparrow "+userbind 10"
- bind kp_pgup "+userbind 11"
- bind kp_multiply "+userbind 12"
- bind kp_slash "+userbind 13"
- bind kp_enter "+userbind 16"
- bind kp_plus "+userbind 17"
- bind kp_minus "+userbind 18"
- bind F1 vyes
- bind F2 vno
- //used for spectate/observer mode
- bind F3 spec
  
  // score log
  set sv_logscores_console 0    "print scores to server console"
@@@ -815,6 -702,12 +704,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"
@@@ -890,43 -783,7 +785,7 @@@ set g_banned_list_idmode "1"      "when set
  
  r_labelsprites_scale 0.40625 // labels sprites get displayed at 0.5x from 640x480 to 1280x1024, and at 1x from 1600x1200 onwards
  
- // usercommands. These can be edited and bound by the menu.
- seta "userbind1_press" "say_team quad soon";  seta "userbind1_release" "";  seta "userbind1_description" "team: quad soon"
- seta "userbind2_press" "say_team free item %x^7 (l:%y^7); g_waypointsprite_team_here_p";  seta "userbind2_release" "";  seta "userbind2_description" "team: free item, icon"
- seta "userbind3_press" "say_team took item (l:%l^7); g_waypointsprite_team_here";  seta "userbind3_release" "";  seta "userbind3_description" "team: took item, icon"
- seta "userbind4_press" "say_team negative";  seta "userbind4_release" "";  seta "userbind4_description" "team: negative"
- seta "userbind5_press" "say_team positive";  seta "userbind5_release" "";  seta "userbind5_description" "team: positive"
- seta "userbind6_press" "say_team need help (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_helpme; cmd voice needhelp";  seta "userbind6_release" "";  seta "userbind6_description" "team: need help, icon"
- seta "userbind7_press" "say_team enemy seen (l:%y^7); g_waypointsprite_team_danger_p; cmd voice incoming";  seta "userbind7_release" "";  seta "userbind7_description" "team: enemy seen, icon"
- seta "userbind8_press" "say_team flag seen (l:%y^7); g_waypointsprite_team_here_p; cmd voice seenflag";  seta "userbind8_release" "";  seta "userbind8_description" "team: flag seen, icon"
- seta "userbind9_press" "say_team defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_here";  seta "userbind9_release" "";  seta "userbind9_description" "team: defending, icon"
- seta "userbind10_press" "say_team roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_here";  seta "userbind10_release" "";  seta "userbind10_description" "team: roaming, icon"
- seta "userbind11_press" "say_team attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7); g_waypointsprite_team_here";  seta "userbind11_release" "";  seta "userbind11_description" "team: attacking, icon"
- seta "userbind12_press" "say_team killed flagcarrier (l:%y^7); g_waypointsprite_team_here_p"; seta "userbind12_release" ""; seta "userbind12_description" "team: killed flag, icon"
- seta "userbind13_press" "say_team dropped flag (l:%d^7); g_waypointsprite_team_here_d"; seta "userbind13_release" ""; seta "userbind13_description" "team: dropped flag, icon"
- seta "userbind14_press" "say_team dropped gun %w^7 (l:%l^7); g_waypointsprite_team_here; wait; dropweapon"; seta "userbind14_release" ""; seta "userbind14_description" "team: drop gun, icon"
- // TODO change this to "use" once we can
- seta "userbind15_press" "say_team dropped flag/key %w^7 (l:%l^7); g_waypointsprite_team_here; wait; +use"; seta "userbind15_release" "-use"; seta "userbind15_description" "team: drop flag/key, icon"
- seta "userbind16_press" "say :-) / nice one"; seta "userbind16_release" ""; seta "userbind16_description" "chat: nice one"
- seta "userbind17_press" "say good game"; seta "userbind17_release" ""; seta "userbind17_description" "chat: good game"
- seta "userbind18_press" "say hi / good luck and have fun"; seta "userbind18_release" ""; seta "userbind18_description" "chat: hi / good luck"
- seta "userbind19_press" "+showscores; +con_chat_maximize"; seta "userbind19_release" "-showscores; -con_chat_maximize"; seta "userbind19_description" "scoreboard / chat history"
- seta "userbind20_press" "toggle cl_capturevideo"; seta "userbind20_release" ""; seta "userbind20_description" "toggle recording .avi"
- seta "userbind21_press" "toggle vid_fullscreen; vid_restart"; seta "userbind21_release" ""; seta "userbind21_description" "toggle fullscreen"
- seta "userbind22_press" ""; seta "userbind22_release" ""; seta "userbind22_description" ""
- seta "userbind23_press" ""; seta "userbind23_release" ""; seta "userbind23_description" ""
- seta "userbind24_press" ""; seta "userbind24_release" ""; seta "userbind24_description" ""
- seta "userbind25_press" ""; seta "userbind25_release" ""; seta "userbind25_description" ""
- seta "userbind26_press" ""; seta "userbind26_release" ""; seta "userbind26_description" ""
- seta "userbind27_press" ""; seta "userbind27_release" ""; seta "userbind27_description" ""
- seta "userbind28_press" ""; seta "userbind28_release" ""; seta "userbind28_description" ""
- seta "userbind29_press" ""; seta "userbind29_release" ""; seta "userbind29_description" ""
- seta "userbind30_press" ""; seta "userbind30_release" ""; seta "userbind30_description" ""
- seta "userbind31_press" ""; seta "userbind31_release" ""; seta "userbind31_description" ""
- seta "userbind32_press" ""; seta "userbind32_release" ""; seta "userbind32_description" ""
- alias _userbind_call "${$1}"
- alias +userbind "_userbind_call userbind${1}_press"
- alias -userbind "_userbind_call userbind${1}_release"
+ exec binds-default.cfg
  
  // we must change its default from 1.0 to 1 to be consistent with menuqc
  set slowmo 1
@@@ -962,19 -819,19 +821,19 @@@ seta scoreboard_accuracy_border_thickne
  seta scoreboard_accuracy_doublerows 0 "use two rows instead of one"
  seta scoreboard_accuracy_nocolors 0 "don't use colors displaying accuracy stats"
  seta scoreboard_accuracy 1 "show weapon accuracy stats panel on scoreboard; colors can be configured with accuracy_color* cvars"
- seta scoreboard_color_bg_r 0 "red color component of the scoreboard background"
- seta scoreboard_color_bg_g 0.4 "green color component of the scoreboard background"
- seta scoreboard_color_bg_b 0.6 "blue color component of the scoreboard background"
- seta scoreboard_color_bg_team 0.5 "team color multiplier of the scoreboard background"
- seta scoreboard_alpha_bg 0.6 "scoreboard background alpha"
+ seta scoreboard_color_bg_r 0.125 "red color component of the scoreboard background"
+ seta scoreboard_color_bg_g 0.55 "green color component of the scoreboard background"
+ seta scoreboard_color_bg_b 0.875 "blue color component of the scoreboard background"
+ seta scoreboard_color_bg_team 0.6 "team color multiplier of the scoreboard background"
+ seta scoreboard_alpha_bg 0.7 "scoreboard background alpha"
  seta scoreboard_alpha_fg 1 "scoreboard foreground alpha"
  seta scoreboard_alpha_name 0.9 "alpha of player text in scoreboard list other than self"
  seta scoreboard_alpha_name_self 1 "alpha of player text in scoreboard list of self"
  seta scoreboard_fadeinspeed 10 "speed at which scoreboard fades in, higher is faster (0 = instant)"
  seta scoreboard_fadeoutspeed 5 "speed at which scoreboard fades out, higher is faster (0 = instant)"
  seta scoreboard_highlight 1 "enable highlighting for rows and columns in the scoreboard"
- seta scoreboard_highlight_alpha 0.10 "highlight alpha value (depends on hud_scoreboard_highlight 1)"
- seta scoreboard_highlight_alpha_self 0.25 "self highlight alpha value"
+ seta scoreboard_highlight_alpha 0.08 "highlight alpha value (depends on hud_scoreboard_highlight 1)"
+ seta scoreboard_highlight_alpha_self 0.3 "self highlight alpha value"
  seta scoreboard_offset_left 0.15 "how far (by percent) the scoreboard is offset from the left screen edge"
  seta scoreboard_offset_right 0.15 "how far (by percent) the scoreboard is offset from the right screen edge"
  seta scoreboard_offset_vertical 0.05 "how far (by percent) the scoreboard is offset from the top and bottom of the screen"
@@@ -1010,7 -867,7 +869,7 @@@ seta menu_slist_categories_CAT_SERVERS_
  seta menu_slist_categories_CAT_XPM_override "CAT_NORMAL"
  seta menu_slist_categories_CAT_MODIFIED_override ""
  seta menu_slist_categories_CAT_OVERKILL_override ""
- seta menu_slist_categories_CAT_MINSTAGIB_override ""
+ seta menu_slist_categories_CAT_INSTAGIB_override ""
  seta menu_slist_categories_CAT_DEFRAG_override ""
  
  seta menu_weaponarena ""
@@@ -1058,7 -915,7 +917,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
@@@ -1076,6 -933,8 +935,8 @@@ makesaved vid_gl1
  makesaved vid_gl20
  makesaved v_idlescale
  makesaved v_kicktime
+ makesaved music_playlist_list0
+ makesaved music_playlist_random0
  
  // ticrate
  //sys_ticrate 0.0166667 // 60fps. This would be ideal, but kills home routers.
@@@ -1125,7 -984,7 +986,7 @@@ cd remap $g_cdtracks_remaplis
  set sv_intermission_cdtrack ""
  
  set g_cdtracks_dontusebydefault "rising-of-the-phoenix"
- set menu_cdtrack "rising-of-the-phoenix"
+ seta menu_cdtrack "rising-of-the-phoenix"
  
  set sv_maxidle 0 "kick players idle for more than this amount of time in seconds"
  set sv_maxidle_spectatorsareidle 0 "when sv_maxidle is not 0, assume spectators are idle too"
@@@ -1140,14 -999,14 +1001,14 @@@ 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 "vaporizer vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun hlac tuba blaster 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_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_weaponpriority0 "devastator mortar 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 "vaporizer vortex crylink hlac arc electro blaster shockwave"             "use impulse 201 for prev gun from this list, 211 for best gun, 221 for next gun.  Default value: energy"
+ seta cl_weaponpriority2 "vaporizer 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 "vaporizer 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 "mortar 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 "blaster shockwave 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"
  seta cl_weaponpriority7 "" "use impulse 207 for prev gun from this list, 217 for best gun, 227 for next gun"
  seta cl_weaponpriority8 "" "use impulse 208 for prev gun from this list, 218 for best gun, 228 for next gun"
@@@ -1171,9 -1030,9 +1032,9 @@@ seta cl_clippedspectating 1 "movement c
  
  seta cl_autoscreenshot 1 "Take a screenshot upon the end of a match... 0 = Disable completely, 1 = Allow sv_autoscreenshot to take a screenshot when requested, 2 = Always take an autoscreenshot anyway."
  
+ seta cl_jetpack_jump 1 "Activate jetpack by pressing jump in the air. 0 = Disable, 1 = Stop when touching ground, 2 = Enable"
  // must be at the bottom of this file:
- // alias for switching the teamselect menu
- bind f5 menu_showteamselect
  
  set g_bugrigs 0
  set g_bugrigs_planar_movement 1       "BROTRR bug emulation"
@@@ -1234,7 -1093,7 +1095,7 @@@ seta cl_gentle_gibs 0           "client side gen
  seta cl_gentle_messages 0     "client side gentle mode (only replaces frag messages/centerprints)"
  seta cl_gentle_damage 0               "client side gentle mode (only replaces damage flash); when set to 1, a white flash replaces the blood image, when set to 2, a randomily colored flash is used instead"
  
- set g_jetpack 0 "Jetpack mutator (uses the hook's button, can't coexist with the offhand hook, but only with the onhand one)"
+ set g_jetpack 0 "Jetpack mutator"
  
  set g_running_guns 0 "... or wonder, till it drives you mad, what would have followed if you had."
  set g_bastet 0 "don't try"
@@@ -1260,7 -1119,7 +1121,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"
@@@ -1308,8 -1167,6 +1169,6 @@@ set sv_accuracy_data_send 1 "1 send wea
  set cl_accuracy_data_share 0 "1 share my weapon accuracy data statistics with other players, 0 keep my weapon accuracy data statistics hidden"
  set cl_accuracy_data_receive 0 "1 receive weapon accuracy data statistics at the end of the match"
  
- set developer_fteqccbugs 0 "check fteqcc bugs on startup"
- set _allow_unacceptable_compiler_bugs 0 "don't bail out if certain bugs are detected (HANDLE WITH CARE)"
  set spawn_debug 0 "use all spawns one by one, then abort, to verify all spawnpoints"
  set loddebug 0 "force this LOD level"
  set spawn_debugview 0 "display spawnpoints and their rating on spawn to debug spawnpoint rating calculation"
@@@ -1373,25 -1230,7 +1232,7 @@@ volume 
  // sucks less than the old one
  cl_decals_newsystem 1
  
- // NOTE: this only replaces weapons on the map
- // use g_start_weapon_* to also replace the on-startup weapons!
- // example: g_weaponreplace_nex "nex minstanex", then Nexes become MinstaNexes 50% of the times
- // 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_grenadelauncher ""
- set g_weaponreplace_electro ""
- set g_weaponreplace_crylink ""
- set g_weaponreplace_nex ""
- set g_weaponreplace_hagar ""
- set g_weaponreplace_rocketlauncher ""
- set g_weaponreplace_porto ""
- set g_weaponreplace_minstanex ""
- 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)"
  
@@@ -1438,6 -1277,7 +1279,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"
@@@ -1544,8 -1384,7 +1386,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 gamemodes.cfg
index 8b6120c368e41996281e4598da0756baa18e120f,b6cbe92cfcba116c79de77e667e30d35d2c6601a..f05ff5238cc61c2a3218dafc9149dac1668107fc
@@@ -18,6 -18,7 +18,7 @@@ alias asay_drop "say_team (%l) dropped 
  // =================
  //  gamestart hooks
  // =================
+ seta cl_matchcount 0 // incremented by cl_hook_gameend and used by playerstats to know when to 
  alias _cl_hook_gamestart "set _cl_hook_gametype $1; _cl_hook_gamestart_stage2"
  alias _cl_hook_gamestart_stage2 "cl_hook_gamestart_all; cl_hook_gamestart_${_cl_hook_gametype}"
  alias cl_hook_gamestart_all
@@@ -37,7 -38,7 +38,7 @@@ alias cl_hook_gamestart_ct
  alias cl_hook_gamestart_ka
  alias cl_hook_gamestart_ft
  alias cl_hook_gamestart_inv
- alias cl_hook_gameend
+ alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends
  alias cl_hook_activeweapon
  
  alias _sv_hook_gamestart "set _sv_hook_gametype $1; _sv_hook_gamestart_stage2"
@@@ -62,6 -63,28 +63,28 @@@ alias sv_hook_gamerestar
  alias sv_hook_gameend
  
  
+ // =====================
+ //  gametype vote hooks
+ // =====================
+ // these are called when the mode is switched via gametype vote screen, earlier than gamestart hooks (useful for enabling per-gamemode mutators)
+ alias sv_vote_gametype_hook_all 
+ alias sv_vote_gametype_hook_as
+ alias sv_vote_gametype_hook_ca
+ alias sv_vote_gametype_hook_ctf
+ alias sv_vote_gametype_hook_cts
+ alias sv_vote_gametype_hook_dm
+ alias sv_vote_gametype_hook_dom
+ alias sv_vote_gametype_hook_ft
+ alias sv_vote_gametype_hook_inv
+ alias sv_vote_gametype_hook_ka
+ alias sv_vote_gametype_hook_kh
+ alias sv_vote_gametype_hook_lms
+ alias sv_vote_gametype_hook_nb
+ alias sv_vote_gametype_hook_ons
+ alias sv_vote_gametype_hook_rc
+ alias sv_vote_gametype_hook_tdm
  // ===========
  //  leadlimit
  // ===========
@@@ -203,10 -226,10 +226,10 @@@ set g_assault 0 "Assault: attack the en
  // ============
  //  clan arena
  // ============
- set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round."
- set g_ca_point_limit 10 "point limit 10 is standard for clan arena"
- set g_ca_point_leadlimit 0
- set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
+ set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round"
+ seta g_ca_point_limit -1 "Clan Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_ca_point_leadlimit -1 "Clan Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games"
  set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
  set g_ca_damage2score_multiplier 0.01
  set g_ca_round_timelimit 180 "round time limit in seconds"
@@@ -300,6 -323,8 +323,8 @@@ set g_dm 1 "Deathmatch: killing any oth
  set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)"
  set g_tdm_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
  seta g_tdm_teams_override 0   "how many teams are in team deathmatch"
+ set g_tdm_point_limit -1 "TDM point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_tdm_point_leadlimit -1 "TDM point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  
  
  // ============
@@@ -314,6 -339,10 +339,10 @@@ set g_domination_point_fullbright        0 "do
  set g_domination_point_rate           0 "override: how often to give those points"
  set g_domination_point_capturetime    0.1 "how long it takes to capture a point (given no interference)"
  set g_domination_point_glow           0 "domination point glow (warning, slow)"
+ set g_domination_roundbased 0 "enable round-based domination (capture all control points to win the round)"
+ set g_domination_roundbased_point_limit 5 "capture limit in round-based domination mode"
+ set g_domination_round_timelimit 120
+ set g_domination_warmup 5
  //set g_domination_balance_team_points        1 "# of points received is based on team sizes"
  
  
@@@ -327,9 -356,12 +356,12 @@@ seta g_freezetag_point_leadlimit -1      "Fr
  set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
  set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
  set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+ set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
+ set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
  set g_freezetag_revive_falldamage 0 "Enable reviving from this amount of fall damage"
  set g_freezetag_revive_falldamage_health 40 "Amount of health player has if they revived from falling"
  set g_freezetag_round_timelimit 180 "round time limit in seconds"
+ set g_freezetag_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn"
  set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
  set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
  seta g_freezetag_teams_override 0
@@@ -447,17 -479,7 +479,17 @@@ seta g_nexball_tackling 1 "Allow ball t
  //  onslaught
  // ===========
  set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it"
 +set g_onslaught_point_limit 1 "Onslaught point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 +set g_onslaught_warmup 5
 +set g_onslaught_round_timelimit 280
 +set g_onslaught_debug 0 "show debug prints in onslaught"
 +set g_onslaught_teleport_radius 200 "Allows teleporting from a control point to another"
 +set g_onslaught_teleport_wait 5 "Time before player can teleport again"
 +set g_onslaught_spawn_choose 1 "Allow players to choose the control point to be spawned at"
 +set g_onslaught_click_radius 500 "When choosing from the map, this level of precision is required"
 +
  set g_onslaught_gen_health 2500
 +set g_onslaught_allow_vehicle_touch 0
  set g_onslaught_cp_health 1000
  set g_onslaught_cp_buildhealth 100
  set g_onslaught_cp_buildtime 5
@@@ -465,13 -487,8 +497,13 @@@ set g_onslaught_cp_regen 2
  set g_onslaught_cp_proxydecap 0 "de-capture controlpoints by standing close to them"
  set g_onslaught_cp_proxydecap_distance 512
  set g_onslaught_cp_proxydecap_dps 100
 +set g_onslaught_shield_force 100
  set g_onslaught_spawn_at_controlpoints 0
 +set g_onslaught_spawn_at_controlpoints_chance 0.5
 +set g_onslaught_spawn_at_controlpoints_random 0
  set g_onslaught_spawn_at_generator 0
 +set g_onslaught_spawn_at_generator_chance 0
 +set g_onslaught_spawn_at_generator_random 0
  
  
  // ======
diff --combined qcsrc/client/Main.qc
index a63d4e46563cf6801fbb5154c54832513f40a13c,d6b00ec9f55182fdee763ce20adb3a7a0639d275..15c08f5acb8818def85bd393907fffb1ddc829c1
@@@ -28,41 -28,19 +28,19 @@@ void menu_sub_null(
  {
  }
  
- #ifdef USE_FTE
- float __engine_check;
- #endif
  string forcefog;
  void WaypointSprite_Load();
  void ConsoleCommand_macro_init();
  void CSQC_Init(void)
  {
        prvm_language = cvar_string("prvm_language");
- #ifdef USE_FTE
- #pragma target ID
-       __engine_check = checkextension("DP_SV_WRITEPICTURE");
-       if(!__engine_check)
-       {
-               print(_("^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n"));
-               localcmd("\ndisconnect\n");
-               return;
-       }
- #pragma target FTE
- #endif
-       check_unacceptable_compiler_bugs();
  
  #ifdef WATERMARK
-       printf(_("^4CSQC Build information: ^1%s\n"), WATERMARK);
+       dprintf("^4CSQC Build information: ^1%s\n", WATERMARK);
  #endif
  
        float i;
  
- #ifdef COMPAT_XON050_ENGINE
-       // old engine lacks implementation of player_localnum
-       player_localnum = player_localentnum - 1;
- #endif
        binddb = db_create();
        tempdb = db_create();
        ClientProgsDB = db_load("client.db");
        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!
        for(i = 0; i < MAX_HUD_FIELDS; ++i)
                hud_title[i] = strzone("(null)");
  
+       Cmd_HUD_SetFields(0);
        postinit = false;
  
        calledhooks = 0;
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
        CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
  
        WaypointSprite_Load();
  
        precache_sound("misc/hit.wav");
        precache_sound("misc/typehit.wav");
  
 +      generator_precache();
        Projectile_Precache();
        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
  // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
  void Shutdown(void)
  {
- #ifdef USE_FTE
- #pragma TARGET id
-       if(!__engine_check)
-               return 0;
- #pragma TARGET fte
- #endif
        WarpZone_Shutdown();
  
        remove(teams);
@@@ -313,8 -288,6 +289,6 @@@ void Porto_Init()
  void TrueAim_Init();
  void PostInit(void)
  {
-       localcmd(strcat("\nscoreboard_columns_set ", autocvar_scoreboard_columns, ";\n"));
        entity playerchecker;
        playerchecker = spawn();
        playerchecker.think = Playerchecker_Think;
@@@ -343,9 -316,6 +317,9 @@@ float CSQC_InputEvent(float bInputType
        if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary))
                return true;
  
 +      if ( HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary) )
 +              return true;
 +
        if (MapVote_InputEvent(bInputType, nPrimary, nSecondary))
                return true;
  
@@@ -548,7 -518,7 +522,7 @@@ void Ent_ClientData(
        }
        spectatee_status = newspectatee_status;
  
-       // non-COMPAT_XON050_ENGINE: we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum
+       // we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum
  }
  
  void Ent_Nagger()
        warmup_stage = (nags & 16);
  }
  
+ void Ent_EliminatedPlayers()
+ {
+       float sf, i, j, b, f;
+       sf = ReadByte();
+       if(sf & 1)
+       {
+               for(j = 0; j < maxclients; ++j)
+                       if(playerslots[j])
+                               playerslots[j].eliminated = 1;
+               for(i = 1; i <= maxclients; i += 8)
+               {
+                       f = ReadByte();
+                       for(j = i-1, b = 1; b < 256; b *= 2, ++j)
+                               if (!(f & b))
+                                       if(playerslots[j])
+                                               playerslots[j].eliminated = 0;
+               }
+       }
+ }
  void Ent_RandomSeed()
  {
        float s;
@@@ -742,7 -733,7 +737,7 @@@ void Ent_ReadSpawnEvent(float is_new
                        button_zoom = FALSE;
                }
        }
 -
 +      HUD_Radar_Hide_Maximized();
        //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum);
  }
  
@@@ -804,6 -795,7 +799,7 @@@ void CSQC_Ent_Update(float bIsNewEntity
                case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break;
                case ENT_CLIENT_LASER: Ent_Laser(); break;
                case ENT_CLIENT_NAGGER: Ent_Nagger(); break;
+               case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break;
                case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break;
                case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break;
                case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break;
                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_GENERATOR: ent_generator(); break;
 +              case ENT_CLIENT_CONTROLPOINT_ICON: ent_cpicon(); break;
                case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break;
                case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break;
                case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); 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));
-                       error(sprintf(_("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n"), self.enttype, num_for_edict(self), self.classname));
+                       error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname));
                        break;
        }
  
@@@ -943,11 -933,15 +939,15 @@@ void Ent_ScoresInfo(
        HUD_ModIcons_SetFunc();
        for(i = 0; i < MAX_SCORE; ++i)
        {
+               if(scores_label[i])
+                       strunzone(scores_label[i]);
                scores_label[i] = strzone(ReadString());
                scores_flags[i] = ReadByte();
        }
        for(i = 0; i < MAX_TEAMSCORE; ++i)
        {
+               if(teamscores_label[i])
+                       strunzone(teamscores_label[i]);
                teamscores_label[i] = strzone(ReadString());
                teamscores_flags[i] = ReadByte();
        }
@@@ -965,14 -959,10 +965,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);
  
        armorblockpercent = ReadByte() / 255.0;
  
-       g_balance_grenadelauncher_bouncefactor = ReadCoord();
-       g_balance_grenadelauncher_bouncestop = ReadCoord();
+       g_balance_mortar_bouncefactor = ReadCoord();
+       g_balance_mortar_bouncestop = ReadCoord();
        g_balance_electro_secondary_bouncefactor = ReadCoord();
        g_balance_electro_secondary_bouncestop = ReadCoord();
  
-       nex_scope = !ReadByte();
+       vortex_scope = !ReadByte();
        rifle_scope = !ReadByte();
  
        serverflags = ReadByte();
@@@ -1182,7 -1172,7 +1178,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();
  
@@@ -1226,16 -1216,16 +1222,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;
@@@ -1303,7 -1297,7 +1303,7 @@@ string getcommandkey(string text, strin
                        return text;
        }
        else if (autocvar_hud_showbinds > 1)
-               return sprintf(_("%s (%s)"), text, keys);
+               return sprintf("%s (%s)", text, keys);
        else
                return keys;
  }
diff --combined qcsrc/client/View.qc
index 375ed903606675f5614a7b3a855c42e5f3429a14,8a725774e144fae6b64122a34330100013073d17..734ef3233e5f38e335499f4870cc9ec85d9f4f49
@@@ -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())
        {
        else
                setsensitivityscale(1);
  
-       makevectors(view_angles);
-       if(autocvar_cl_velocityzoom && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too
+       if(autocvar_cl_velocityzoom_enabled && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too
        {
                if(intermission) { curspeed = 0; }
                else
                {
+                       makevectors(view_angles);
                        v = pmove_vel;
                        if(csqcplayer)
                                v = csqcplayer.velocity;
  
                velocityzoom = bound(0, drawframetime / max(0.000000001, autocvar_cl_velocityzoom_time), 1); // speed at which the zoom adapts to player velocity
                avgspeed = avgspeed * (1 - velocityzoom) + (curspeed / autocvar_cl_velocityzoom_speed) * velocityzoom;
-               velocityzoom = exp(float2range11(avgspeed * -autocvar_cl_velocityzoom / 1) * 1);
+               velocityzoom = exp(float2range11(avgspeed * -autocvar_cl_velocityzoom_factor / 1) * 1);
  
                //print(ftos(avgspeed), " avgspeed, ", ftos(curspeed), " curspeed, ", ftos(velocityzoom), " return\n"); // for debugging
        }
@@@ -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,6 -365,7 +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();
@@@ -374,10 -375,8 +375,8 @@@ entity nightvision_noise, nightvision_n
  
  #define MAX_TIME_DIFF 5
  float pickup_crosshair_time, pickup_crosshair_size;
- float hit_time, typehit_time;
- float nextsound_hit_time, nextsound_typehit_time;
- float hitindication_crosshair_time, hitindication_crosshair_size;
- float use_nex_chargepool;
+ float hitindication_crosshair_size;
+ float use_vortex_chargepool;
  
  float myhealth, myhealth_prev;
  float myhealth_flash;
@@@ -391,25 -390,588 +390,589 @@@ float contentavgalpha, liquidalpha_prev
  vector liquidcolor_prev;
  
  float eventchase_current_distance;
+ float eventchase_running;
+ float WantEventchase()
+ {
+       if(autocvar_cl_orthoview)
+               return FALSE;
+       if(intermission)
+               return TRUE;
+       if(spectatee_status >= 0)
+       {
+               if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WepSet_FromWeapon(WEP_PORTO)))
+                       return TRUE;
+               if(autocvar_cl_eventchase_death && (getstati(STAT_HEALTH) <= 0))
+               {
+                       if(autocvar_cl_eventchase_death == 2)
+                       {
+                               // don't stop eventchase once it's started (even if velocity changes afterwards)
+                               if(self.velocity == '0 0 0' || eventchase_running)
+                                       return TRUE;
+                       }
+                       else return TRUE;
+               }
+       }
+       return FALSE;
+ }
  
  vector damage_blurpostprocess, content_blurpostprocess;
  
  float checkfail[16];
  
- float rainbow_last_flicker;
- vector rainbow_prev_color;
+ float unaccounted_damage = 0;
+ void UpdateDamage()
+ {
+       // accumulate damage with each stat update
+       static float damage_total_prev = 0;
+       float damage_total = getstati(STAT_DAMAGE_DEALT_TOTAL);
+       float unaccounted_damage_new = COMPARE_INCREASING(damage_total, damage_total_prev);
+       damage_total_prev = damage_total;
+       static float damage_dealt_time_prev = 0;
+       float damage_dealt_time = getstatf(STAT_HIT_TIME);
+       if (damage_dealt_time != damage_dealt_time_prev)
+       {
+               unaccounted_damage += unaccounted_damage_new;
+               dprint("dmg total: ", ftos(unaccounted_damage), " (+", ftos(unaccounted_damage_new), ")", "\n");
+       }
+       damage_dealt_time_prev = damage_dealt_time;
+       // prevent hitsound when switching spectatee
+       static float spectatee_status_prev = 0;
+       if (spectatee_status != spectatee_status_prev)
+               unaccounted_damage = 0;
+       spectatee_status_prev = spectatee_status;
+ }
+ void UpdateHitsound()
+ {
+       // varying sound pitch
+       static float hitsound_time_prev = 0;
+       // HACK: the only way to get the arc to sound consistent with pitch shift is to ignore cl_hitsound_antispam_time
+       float arc_hack = activeweapon == WEP_ARC && autocvar_cl_hitsound >= 2;
+       if (arc_hack || COMPARE_INCREASING(time, hitsound_time_prev) > autocvar_cl_hitsound_antispam_time)
+       {
+               if (autocvar_cl_hitsound && unaccounted_damage)
+               {
+                       // customizable gradient function that crosses (0,a), (c,1) and asymptotically approaches b
+                       float a = autocvar_cl_hitsound_max_pitch;
+                       float b = autocvar_cl_hitsound_min_pitch;
+                       float c = autocvar_cl_hitsound_nom_damage;
+                       float x = unaccounted_damage;
+                       float pitch_shift = (b*x*(a-1) + a*c*(1-b)) / (x*(a-1) + c*(1-b));
+                       // if sound variation is disabled, set pitch_shift to 1
+                       if (autocvar_cl_hitsound == 1)
+                               pitch_shift = 1;
+                       // if pitch shift is reversed, mirror in (max-min)/2 + min
+                       if (autocvar_cl_hitsound == 3)
+                       {
+                               float mirror_value = (a-b)/2 + b;
+                               pitch_shift = mirror_value + (mirror_value - pitch_shift);
+                       }
+                       dprint("dmg total (dmg): ", ftos(unaccounted_damage), " , pitch shift: ", ftos(pitch_shift), "\n");
+                       // todo: avoid very long and very short sounds from wave stretching using different sound files? seems unnecessary
+                       // todo: normalize sound pressure levels? seems unnecessary
+                       sound7(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTN_NONE, pitch_shift * 100, 0);
+               }
+               unaccounted_damage = 0;
+               hitsound_time_prev = time;
+       }
+       static float typehit_time_prev = 0;
+       float typehit_time = getstatf(STAT_TYPEHIT_TIME);
+       if (COMPARE_INCREASING(typehit_time, typehit_time_prev) > autocvar_cl_hitsound_antispam_time)
+       {
+               sound(world, CH_INFO, "misc/typehit.wav", VOL_BASE, ATTN_NONE);
+               typehit_time_prev = typehit_time;
+       }
+ }
+ void UpdateCrosshair()
+ {
+       static float rainbow_last_flicker;
+     static vector rainbow_prev_color;
+       entity e = self;
+       float f, i, j;
+       vector v;
+       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
+       {
+               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)
+               if(autocvar_viewsize < 120)
+                       CSQC_common_hud();
+       // crosshair goes VERY LAST
+       if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL)
+       {
+               if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering
+                       return;
+               string wcross_style;
+               float wcross_alpha, wcross_resolution;
+               wcross_style = autocvar_crosshair;
+               if (wcross_style == "0")
+                       return;
+               wcross_resolution = autocvar_crosshair_size;
+               if (wcross_resolution == 0)
+                       return;
+               wcross_alpha = autocvar_crosshair_alpha;
+               if (wcross_alpha == 0)
+                       return;
+               // TrueAim check
+               float shottype;
+               // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
+               wcross_origin = project_3d_to_2d(view_origin + MAX_SHOT_DISTANCE * view_forward);
+               wcross_origin_z = 0;
+               if(autocvar_crosshair_hittest)
+               {
+                       vector wcross_oldorigin;
+                       wcross_oldorigin = wcross_origin;
+                       shottype = TrueAimCheck();
+                       if(shottype == SHOTTYPE_HITWORLD)
+                       {
+                               v = wcross_origin - wcross_oldorigin;
+                               v_x /= vid_conwidth;
+                               v_y /= vid_conheight;
+                               if(vlen(v) > 0.01)
+                                       shottype = SHOTTYPE_HITOBSTRUCTION;
+                       }
+                       if(!autocvar_crosshair_hittest_showimpact)
+                               wcross_origin = wcross_oldorigin;
+               }
+               else
+                       shottype = SHOTTYPE_HITWORLD;
+               vector wcross_color = '0 0 0', wcross_size = '0 0 0';
+               string wcross_name = "";
+               float wcross_scale, wcross_blur;
+               if(autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1))
+               {
+                       e = get_weaponinfo(switchingweapon);
+                       if(e)
+                       {
+                               if(autocvar_crosshair_per_weapon)
+                               {
+                                       // 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;
+                               }
+                       }
+               }
+               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(e)
+                               {
+                                       wcross_color = e.wpcolor;
+                                       break;
+                               }
+                               else { goto normalcolor; }
+                       }
+                       case 2: // crosshair_color_by_health
+                       {
+                               float x = getstati(STAT_HEALTH);
+                               //x = red
+                               //y = green
+                               //z = blue
+                               wcross_color_z = 0;
+                               if(x > 200)
+                               {
+                                       wcross_color_x = 0;
+                                       wcross_color_y = 1;
+                               }
+                               else if(x > 150)
+                               {
+                                       wcross_color_x = 0.4 - (x-150)*0.02 * 0.4;
+                                       wcross_color_y = 0.9 + (x-150)*0.02 * 0.1;
+                               }
+                               else if(x > 100)
+                               {
+                                       wcross_color_x = 1 - (x-100)*0.02 * 0.6;
+                                       wcross_color_y = 1 - (x-100)*0.02 * 0.1;
+                                       wcross_color_z = 1 - (x-100)*0.02;
+                               }
+                               else if(x > 50)
+                               {
+                                       wcross_color_x = 1;
+                                       wcross_color_y = 1;
+                                       wcross_color_z = 0.2 + (x-50)*0.02 * 0.8;
+                               }
+                               else if(x > 20)
+                               {
+                                       wcross_color_x = 1;
+                                       wcross_color_y = (x-20)*90/27/100;
+                                       wcross_color_z = (x-20)*90/27/100 * 0.2;
+                               }
+                               else
+                               {
+                                       wcross_color_x = 1;
+                                       wcross_color_y = 0;
+                               }
+                               break;
+                       }
+                       case 3: // crosshair_color_rainbow
+                       {
+                               if(time >= rainbow_last_flicker)
+                               {
+                                       rainbow_prev_color = randomvec() * autocvar_crosshair_color_special_rainbow_brightness;
+                                       rainbow_last_flicker = time + autocvar_crosshair_color_special_rainbow_delay;
+                               }
+                               wcross_color = rainbow_prev_color;
+                               break;
+                       }
+                       :normalcolor
+                       default: { wcross_color = stov(autocvar_crosshair_color); break; }
+               }
+               if(autocvar_crosshair_effect_scalefade)
+               {
+                       wcross_scale = wcross_resolution;
+                       wcross_resolution = 1;
+               }
+               else
+               {
+                       wcross_scale = 1;
+               }
+               if(autocvar_crosshair_pickup)
+               {
+                       float stat_pickup_time = getstatf(STAT_LAST_PICKUP);
+                       if(pickup_crosshair_time < stat_pickup_time)
+                       {
+                               if(time - stat_pickup_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old
+                                       pickup_crosshair_size = 1;
+                               pickup_crosshair_time = stat_pickup_time;
+                       }
+                       if(pickup_crosshair_size > 0)
+                               pickup_crosshair_size -= autocvar_crosshair_pickup_speed * frametime;
+                       else
+                               pickup_crosshair_size = 0;
+                       wcross_scale += sin(pickup_crosshair_size) * autocvar_crosshair_pickup;
+               }
+               // todo: make crosshair hit indication dependent on damage dealt
+               if(autocvar_crosshair_hitindication)
+               {
+                       vector hitindication_color = ((autocvar_crosshair_color_special == 1) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color));
+                       if(unaccounted_damage)
+                       {
+                               hitindication_crosshair_size = 1;
+                       }
+                       if(hitindication_crosshair_size > 0)
+                               hitindication_crosshair_size -= autocvar_crosshair_hitindication_speed * frametime;
+                       else
+                               hitindication_crosshair_size = 0;
+                       wcross_scale += sin(hitindication_crosshair_size) * autocvar_crosshair_hitindication;
+                       wcross_color_x += sin(hitindication_crosshair_size) * hitindication_color_x;
+                       wcross_color_y += sin(hitindication_crosshair_size) * hitindication_color_y;
+                       wcross_color_z += sin(hitindication_crosshair_size) * hitindication_color_z;
+               }
+               if(shottype == SHOTTYPE_HITENEMY)
+                       wcross_scale *= autocvar_crosshair_hittest; // is not queried if hittest is 0
+               if(shottype == SHOTTYPE_HITTEAM)
+                       wcross_scale /= autocvar_crosshair_hittest; // is not queried if hittest is 0
+               f = fabs(autocvar_crosshair_effect_time);
+               if(wcross_scale != wcross_scale_goal_prev || wcross_alpha != wcross_alpha_goal_prev || wcross_color != wcross_color_goal_prev)
+               {
+                       wcross_changedonetime = time + f;
+               }
+               if(wcross_name != wcross_name_goal_prev || wcross_resolution != wcross_resolution_goal_prev)
+               {
+                       wcross_name_changestarttime = time;
+                       wcross_name_changedonetime = time + f;
+                       if(wcross_name_goal_prev_prev)
+                               strunzone(wcross_name_goal_prev_prev);
+                       wcross_name_goal_prev_prev = wcross_name_goal_prev;
+                       wcross_name_goal_prev = strzone(wcross_name);
+                       wcross_name_alpha_goal_prev_prev = wcross_name_alpha_goal_prev;
+                       wcross_resolution_goal_prev_prev = wcross_resolution_goal_prev;
+                       wcross_resolution_goal_prev = wcross_resolution;
+               }
+               wcross_scale_goal_prev = wcross_scale;
+               wcross_alpha_goal_prev = wcross_alpha;
+               wcross_color_goal_prev = wcross_color;
+               if(shottype == SHOTTYPE_HITTEAM || (shottype == SHOTTYPE_HITOBSTRUCTION && autocvar_crosshair_hittest_blur && !autocvar_chase_active))
+               {
+                       wcross_blur = 1;
+                       wcross_alpha *= 0.75;
+               }
+               else
+                       wcross_blur = 0;
+               // *_prev is at time-frametime
+               // * is at wcross_changedonetime+f
+               // what do we have at time?
+               if(time < wcross_changedonetime)
+               {
+                       f = frametime / (wcross_changedonetime - time + frametime);
+                       wcross_scale = f * wcross_scale + (1 - f) * wcross_scale_prev;
+                       wcross_alpha = f * wcross_alpha + (1 - f) * wcross_alpha_prev;
+                       wcross_color = f * wcross_color + (1 - f) * wcross_color_prev;
+               }
+               wcross_scale_prev = wcross_scale;
+               wcross_alpha_prev = wcross_alpha;
+               wcross_color_prev = wcross_color;
+               wcross_scale *= 1 - autocvar__menu_alpha;
+               wcross_alpha *= 1 - autocvar__menu_alpha;
+               wcross_size = draw_getimagesize(wcross_name) * wcross_scale;
+               if(wcross_scale >= 0.001 && wcross_alpha >= 0.001)
+               {
+                       // crosshair rings for weapon stats
+                       if (autocvar_crosshair_ring || autocvar_crosshair_ring_reload)
+                       {
+                               // declarations and stats
+                               float ring_value = 0, ring_scale = 0, ring_alpha = 0, ring_inner_value = 0, ring_inner_alpha = 0;
+                               string ring_image = string_null, ring_inner_image = string_null;
+                               vector ring_rgb = '0 0 0', ring_inner_rgb = '0 0 0';
+                               ring_scale = autocvar_crosshair_ring_size;
+                               float weapon_clipload, weapon_clipsize;
+                               weapon_clipload = getstati(STAT_WEAPON_CLIPLOAD);
+                               weapon_clipsize = getstati(STAT_WEAPON_CLIPSIZE);
+                               float ok_ammo_charge, ok_ammo_chargepool;
+                               ok_ammo_charge = getstatf(STAT_OK_AMMO_CHARGE);
+                               ok_ammo_chargepool = getstatf(STAT_OK_AMMO_CHARGEPOOl);
+                               float vortex_charge, vortex_chargepool;
+                               vortex_charge = getstatf(STAT_VORTEX_CHARGE);
+                               vortex_chargepool = getstatf(STAT_VORTEX_CHARGEPOOL);
+                               float arc_heat = getstatf(STAT_ARC_HEAT);
+                               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_VORTEX && vortex_charge && autocvar_crosshair_ring_vortex) // ring around crosshair representing velocity-dependent damage for the vortex
+                               {
+                                       if (vortex_chargepool || use_vortex_chargepool) {
+                                               use_vortex_chargepool = 1;
+                                               ring_inner_value = vortex_chargepool;
+                                       } else {
+                                               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_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 = vortex_charge;
+                                       ring_alpha = autocvar_crosshair_ring_vortex_alpha;
+                                       ring_rgb = wcross_color;
+                                       ring_image = "gfx/crosshair_ring_nexgun.tga";
+                               }
+                               else if (autocvar_crosshair_ring && activeweapon == WEP_MINE_LAYER && minelayer_maxmines && autocvar_crosshair_ring_minelayer)
+                               {
+                                       ring_value = bound(0, getstati(STAT_LAYED_MINES) / minelayer_maxmines, 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to.
+                                       ring_alpha = autocvar_crosshair_ring_minelayer_alpha;
+                                       ring_rgb = wcross_color;
+                                       ring_image = "gfx/crosshair_ring.tga";
+                               }
+                               else if (activeweapon == WEP_HAGAR && getstati(STAT_HAGAR_LOAD) && autocvar_crosshair_ring_hagar)
+                               {
+                                       ring_value = bound(0, getstati(STAT_HAGAR_LOAD) / hagar_maxrockets, 1);
+                                       ring_alpha = autocvar_crosshair_ring_hagar_alpha;
+                                       ring_rgb = wcross_color;
+                                       ring_image = "gfx/crosshair_ring.tga";
+                               }
+                               else if (ok_ammo_charge)
+                               {
+                                       ring_value = ok_ammo_chargepool;
+                                       ring_alpha = autocvar_crosshair_ring_reload_alpha;
+                                       ring_rgb = wcross_color;
+                                       ring_image = "gfx/crosshair_ring.tga";
+                               }
+                               else if(autocvar_crosshair_ring_reload && weapon_clipsize) // forces there to be only an ammo ring
+                               {
+                                       ring_value = bound(0, weapon_clipload / weapon_clipsize, 1);
+                                       ring_scale = autocvar_crosshair_ring_reload_size;
+                                       ring_alpha = autocvar_crosshair_ring_reload_alpha;
+                                       ring_rgb = wcross_color;
+                                       // Note: This is to stop Taoki from complaining that the image doesn't match all potential balances.
+                                       // if a new image for another weapon is added, add the code (and its respective file/value) here
+                                       if ((activeweapon == WEP_RIFLE) && (weapon_clipsize == 80))
+                                               ring_image = "gfx/crosshair_ring_rifle.tga";
+                                       else
+                                               ring_image = "gfx/crosshair_ring.tga";
+                               }
+                               else if ( autocvar_crosshair_ring && autocvar_crosshair_ring_arc && arc_heat && activeweapon == WEP_ARC )
+                               {
+                                       ring_value = arc_heat;
+                                       ring_alpha = (1-arc_heat)*autocvar_crosshair_ring_arc_cold_alpha +
+                                               arc_heat*autocvar_crosshair_ring_arc_hot_alpha;
+                                       ring_rgb = (1-arc_heat)*wcross_color + arc_heat*autocvar_crosshair_ring_arc_hot_color;
+                                       ring_image = "gfx/crosshair_ring.tga";
+                               }
+                               // if in weapon switch animation, fade ring out/in
+                               if(autocvar_crosshair_effect_time > 0)
+                               {
+                                       f = (time - wcross_name_changestarttime) / autocvar_crosshair_effect_time;
+                                       if (!(f < 1))
+                                       {
+                                               wcross_ring_prev = ((ring_image) ? TRUE : FALSE);
+                                       }
+                                       if(wcross_ring_prev)
+                                       {
+                                               if(f < 1)
+                                                       ring_alpha *= fabs(1 - bound(0, f, 1));
+                                       }
+                                       else
+                                       {
+                                               if(f < 1)
+                                                       ring_alpha *= bound(0, f, 1);
+                                       }
+                               }
+                               if (autocvar_crosshair_ring_inner && ring_inner_value) // lets draw a ring inside a ring so you can ring while you ring
+                                       DrawCircleClippedPic(wcross_origin, wcross_size_x * ring_scale, ring_inner_image, ring_inner_value, ring_inner_rgb, wcross_alpha * ring_inner_alpha, DRAWFLAG_ADDITIVE);
+                               if (ring_value)
+                                       DrawCircleClippedPic(wcross_origin, wcross_size_x * ring_scale, ring_image, ring_value, ring_rgb, wcross_alpha * ring_alpha, DRAWFLAG_ADDITIVE);
+                       }
+ #define CROSSHAIR_DO_BLUR(M,sz,wcross_name,wcross_alpha) \
+                       do \
+                       { \
+                               if(wcross_blur > 0) \
+                               { \
+                                       for(i = -2; i <= 2; ++i) \
+                                       for(j = -2; j <= 2; ++j) \
+                                       M(i,j,sz,wcross_name,wcross_alpha*0.04); \
+                               } \
+                               else \
+                               { \
+                                       M(0,0,sz,wcross_name,wcross_alpha); \
+                               } \
+                       } \
+                       while(0)
+ #define CROSSHAIR_DRAW_SINGLE(i,j,sz,wcross_name,wcross_alpha) \
+                       drawpic(wcross_origin - ('0.5 0 0' * (sz * wcross_size_x + i * wcross_blur) + '0 0.5 0' * (sz * wcross_size_y + j * wcross_blur)), wcross_name, sz * wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL)
+ #define CROSSHAIR_DRAW(sz,wcross_name,wcross_alpha) \
+                       CROSSHAIR_DO_BLUR(CROSSHAIR_DRAW_SINGLE,sz,wcross_name,wcross_alpha)
+                       if(time < wcross_name_changedonetime && wcross_name != wcross_name_goal_prev_prev && wcross_name_goal_prev_prev)
+                       {
+                               f = (wcross_name_changedonetime - time) / (wcross_name_changedonetime - wcross_name_changestarttime);
+                               wcross_size = draw_getimagesize(wcross_name_goal_prev_prev) * wcross_scale;
+                               CROSSHAIR_DRAW(wcross_resolution_goal_prev_prev, wcross_name_goal_prev_prev, wcross_alpha * f * wcross_name_alpha_goal_prev_prev);
+                               f = 1 - f;
+                       }
+                       else
+                       {
+                               f = 1;
+                       }
+                       wcross_name_alpha_goal_prev = f;
+                       wcross_size = draw_getimagesize(wcross_name) * wcross_scale;
+                       CROSSHAIR_DRAW(wcross_resolution, wcross_name, wcross_alpha * f);
+                       if(autocvar_crosshair_dot)
+                       {
+                               vector wcross_color_old;
+                               wcross_color_old = wcross_color;
+                               if((autocvar_crosshair_dot_color_custom) && (autocvar_crosshair_dot_color != "0"))
+                                       wcross_color = stov(autocvar_crosshair_dot_color);
+                               CROSSHAIR_DRAW(wcross_resolution * autocvar_crosshair_dot_size, "gfx/crosshairdot.tga", f * autocvar_crosshair_dot_alpha);
+                               // FIXME why don't we use wcross_alpha here?cl_notice_run();
+                               wcross_color = wcross_color_old;
+                       }
+               }
+       }
+       else
+       {
+               wcross_scale_prev = 0;
+               wcross_alpha_prev = 0;
+               wcross_scale_goal_prev = 0;
+               wcross_alpha_goal_prev = 0;
+               wcross_changedonetime = 0;
+               if(wcross_name_goal_prev)
+                       strunzone(wcross_name_goal_prev);
+               wcross_name_goal_prev = string_null;
+               if(wcross_name_goal_prev_prev)
+                       strunzone(wcross_name_goal_prev_prev);
+               wcross_name_goal_prev_prev = string_null;
+               wcross_name_changestarttime = 0;
+               wcross_name_changedonetime = 0;
+               wcross_name_alpha_goal_prev = 0;
+               wcross_name_alpha_goal_prev_prev = 0;
+               wcross_resolution_goal_prev = 0;
+               wcross_resolution_goal_prev_prev = 0;
+       }
+ }
  
  #define BUTTON_3 4
  #define BUTTON_4 8
  float cl_notice_run();
  float prev_myteam;
 +.float health;
  void CSQC_UpdateView(float w, float h)
  {
        entity e;
        float fov;
-       float f, i, j;
-       vector v;
+       float f, i;
        vector vf_size, vf_min;
        float a;
  
  
        CSQCPlayer_SetCamera();
  
- #ifdef COMPAT_XON050_ENGINE
-       if(spectatee_status)
-               myteam = GetPlayerColor(spectatee_status - 1);
-       else
- #endif
-               myteam = GetPlayerColor(player_localentnum - 1);
+       myteam = GetPlayerColor(player_localentnum - 1);
  
        if(myteam != prev_myteam)
        {
        // event chase camera
        if(autocvar_chase_active <= 0) // greater than 0 means it's enabled manually, and this code is skipped
        {
-               WepSet weapons_stat = WepSet_GetFromStat();
 -              if(WantEventchase())
 +              float ons_roundlost = (gametype == MAPINFO_TYPE_ONSLAUGHT && getstati(STAT_ROUNDLOST));
 +              entity gen = world;
 +              
 +              if(ons_roundlost)
 +              {
 +                      entity e;
 +                      for(e = world; (e = find(e, classname, "onslaught_generator")); )
 +                      {
 +                              if(e.health <= 0)
 +                              {
 +                                      gen = e;
 +                                      break;
 +                              }
 +                      }
 +                      if(!gen)
 +                              ons_roundlost = FALSE; // don't enforce the 3rd person camera if there is no dead generator to show
 +              }
-               if(((spectatee_status >= 0 && (autocvar_cl_eventchase_death && is_dead)) || intermission || ons_roundlost) && !autocvar_cl_orthoview || (autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(weapons_stat & WepSet_FromWeapon(WEP_PORTO))))
++              if(WantEventchase() || (!autocvar_cl_orthoview && ons_roundlost))
                {
+                       eventchase_running = TRUE;
                        // make special vector since we can't use view_origin (It is one frame old as of this code, it gets set later with the results this code makes.)
                        vector current_view_origin = (csqcplayer ? csqcplayer.origin : pmove_org);
 +                      if(ons_roundlost) { current_view_origin = gen.origin; }
  
                        // detect maximum viewoffset and use it
 -                      if(autocvar_cl_eventchase_viewoffset)
 +                      vector view_offset = autocvar_cl_eventchase_viewoffset;
 +                      if(ons_roundlost) { view_offset = autocvar_cl_eventchase_generator_viewoffset; }
 +
 +                      if(view_offset)
                        {
 -                              WarpZone_TraceLine(current_view_origin, current_view_origin + autocvar_cl_eventchase_viewoffset + ('0 0 1' * autocvar_cl_eventchase_maxs_z), MOVE_WORLDONLY, self);
 -                              if(trace_fraction == 1) { current_view_origin += autocvar_cl_eventchase_viewoffset; }
 +                              WarpZone_TraceLine(current_view_origin, current_view_origin + view_offset + ('0 0 1' * autocvar_cl_eventchase_maxs_z), MOVE_WORLDONLY, self);
 +                              if(trace_fraction == 1) { current_view_origin += view_offset; }
                                else { current_view_origin_z += max(0, (trace_endpos_z - current_view_origin_z) - autocvar_cl_eventchase_maxs_z); }
                        }
  
                        if(!autocvar_chase_active) { cvar_set("chase_active", "-1"); }
  
                        // make the camera smooth back
 -                      if(autocvar_cl_eventchase_speed && eventchase_current_distance < autocvar_cl_eventchase_distance)
 -                              eventchase_current_distance += autocvar_cl_eventchase_speed * (autocvar_cl_eventchase_distance - eventchase_current_distance) * frametime; // slow down the further we get
 -                      else if(eventchase_current_distance != autocvar_cl_eventchase_distance)
 -                              eventchase_current_distance = autocvar_cl_eventchase_distance;
 +                      float chase_distance = autocvar_cl_eventchase_distance;
 +                      if(ons_roundlost) { chase_distance = autocvar_cl_eventchase_generator_distance; }
 +
 +                      if(autocvar_cl_eventchase_speed && eventchase_current_distance < chase_distance)
 +                              eventchase_current_distance += autocvar_cl_eventchase_speed * (chase_distance - eventchase_current_distance) * frametime; // slow down the further we get
 +                      else if(eventchase_current_distance != chase_distance)
 +                              eventchase_current_distance = chase_distance;
  
                        makevectors(view_angles);
  
                }
                else if(autocvar_chase_active < 0) // time to disable chase_active if it was set by this code
                {
+                       eventchase_running = FALSE;
                        cvar_set("chase_active", "0");
                        eventchase_current_distance = 0; // start from 0 next time
                }
  
        // ALWAYS Clear Current Scene First
        clearscene();
- #ifdef WORKAROUND_XON010
-       if(checkextension("DP_CSQC_ROTATEMOVES"))
-       {
- #endif
        setproperty(VF_ORIGIN, view_origin);
        setproperty(VF_ANGLES, view_angles);
- #ifdef WORKAROUND_XON010
-       }
- #endif
  
        // FIXME engine bug? VF_SIZE and VF_MIN are not restored to sensible values by this
        setproperty(VF_SIZE, vf_size);
        {
                // apply night vision effect
                vector tc_00, tc_01, tc_10, tc_11;
-               vector rgb;
-               rgb_x = 0; // fteqcc sucks
-               rgb_y = 0; // fteqcc sucks
-               rgb_z = 0; // fteqcc sucks
+               vector rgb = '0 0 0';
  
                if(!nightvision_noise)
                {
                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
  
        scoreboard_active = HUD_WouldDrawScoreboard();
  
-       hit_time = getstatf(STAT_HIT_TIME);
-       if(hit_time > nextsound_hit_time && autocvar_cl_hitsound)
-       {
-               if(time - hit_time < MAX_TIME_DIFF) // don't play the sound if it's too old.
-                       sound(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTEN_NONE);
-               nextsound_hit_time = time + autocvar_cl_hitsound_antispam_time;
-       }
-       typehit_time = getstatf(STAT_TYPEHIT_TIME);
-       if(typehit_time > nextsound_typehit_time)
-       {
-               if(time - typehit_time < MAX_TIME_DIFF) // don't play the sound if it's too old.
-                       sound(world, CH_INFO, "misc/typehit.wav", VOL_BASE, ATTEN_NONE);
-               nextsound_typehit_time = time + autocvar_cl_hitsound_antispam_time;
-       }
-       //else
-       {
-               if(gametype == MAPINFO_TYPE_FREEZETAG)
-               {
-                       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);
-                       }
-               }
-               if(autocvar_r_letterbox == 0)
-                       if(autocvar_viewsize < 120)
-                               CSQC_common_hud();
-               // crosshair goes VERY LAST
-               if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL)
-               {
-                       if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering
-                               return;
-                       string wcross_style;
-                       float wcross_alpha, wcross_resolution;
-                       wcross_style = autocvar_crosshair;
-                       if (wcross_style == "0")
-                               return;
-                       wcross_resolution = autocvar_crosshair_size;
-                       if (wcross_resolution == 0)
-                               return;
-                       wcross_alpha = autocvar_crosshair_alpha;
-                       if (wcross_alpha == 0)
-                               return;
-                       // TrueAim check
-                       float shottype;
-                       // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
-                       wcross_origin = project_3d_to_2d(view_origin + MAX_SHOT_DISTANCE * view_forward);
-                       wcross_origin_z = 0;
-                       if(autocvar_crosshair_hittest)
-                       {
-                               vector wcross_oldorigin;
-                               wcross_oldorigin = wcross_origin;
-                               shottype = TrueAimCheck();
-                               if(shottype == SHOTTYPE_HITWORLD)
-                               {
-                                       v = wcross_origin - wcross_oldorigin;
-                                       v_x /= vid_conwidth;
-                                       v_y /= vid_conheight;
-                                       if(vlen(v) > 0.01)
-                                               shottype = SHOTTYPE_HITOBSTRUCTION;
-                               }
-                               if(!autocvar_crosshair_hittest_showimpact)
-                                       wcross_origin = wcross_oldorigin;
-                       }
-                       else
-                               shottype = SHOTTYPE_HITWORLD;
-                       vector wcross_color = '0 0 0', wcross_size = '0 0 0';
-                       string wcross_wep = "", wcross_name;
-                       float wcross_scale, wcross_blur;
-                       if (autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1))
-                       {
-                               e = get_weaponinfo(switchingweapon);
-                               if (e && e.netname != "")
-                               {
-                                       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;
-                                       }
-                               }
-                       }
-                       //printf("crosshair style: %s\n", wcross_style);
-                       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 != "")
-                                       {
-                                               wcross_color = stov(cvar_string(sprintf("crosshair_%s_color", wcross_wep)));
-                                               break;
-                                       }
-                                       else { goto normalcolor; }
-                               }
-                               case 2: // crosshair_color_by_health
-                               {
-                                       float x = getstati(STAT_HEALTH);
-                                       //x = red
-                                       //y = green
-                                       //z = blue
-                                       wcross_color_z = 0;
-                                       if(x > 200)
-                                       {
-                                               wcross_color_x = 0;
-                                               wcross_color_y = 1;
-                                       }
-                                       else if(x > 150)
-                                       {
-                                               wcross_color_x = 0.4 - (x-150)*0.02 * 0.4;
-                                               wcross_color_y = 0.9 + (x-150)*0.02 * 0.1;
-                                       }
-                                       else if(x > 100)
-                                       {
-                                               wcross_color_x = 1 - (x-100)*0.02 * 0.6;
-                                               wcross_color_y = 1 - (x-100)*0.02 * 0.1;
-                                               wcross_color_z = 1 - (x-100)*0.02;
-                                       }
-                                       else if(x > 50)
-                                       {
-                                               wcross_color_x = 1;
-                                               wcross_color_y = 1;
-                                               wcross_color_z = 0.2 + (x-50)*0.02 * 0.8;
-                                       }
-                                       else if(x > 20)
-                                       {
-                                               wcross_color_x = 1;
-                                               wcross_color_y = (x-20)*90/27/100;
-                                               wcross_color_z = (x-20)*90/27/100 * 0.2;
-                                       }
-                                       else
-                                       {
-                                               wcross_color_x = 1;
-                                               wcross_color_y = 0;
-                                       }
-                                       break;
-                               }
-                               case 3: // crosshair_color_rainbow
-                               {
-                                       if(time >= rainbow_last_flicker)
-                                       {
-                                               rainbow_prev_color = randomvec() * autocvar_crosshair_color_special_rainbow_brightness;
-                                               rainbow_last_flicker = time + autocvar_crosshair_color_special_rainbow_delay;
-                                       }
-                                       wcross_color = rainbow_prev_color;
-                                       break;
-                               }
-                               :normalcolor
-                               default: { wcross_color = stov(autocvar_crosshair_color); break; }
-                       }
-                       if(autocvar_crosshair_effect_scalefade)
-                       {
-                               wcross_scale = wcross_resolution;
-                               wcross_resolution = 1;
-                       }
-                       else
-                       {
-                               wcross_scale = 1;
-                       }
-                       if(autocvar_crosshair_pickup)
-                       {
-                               float stat_pickup_time = getstatf(STAT_LAST_PICKUP);
-                               if(pickup_crosshair_time < stat_pickup_time)
-                               {
-                                       if(time - stat_pickup_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old
-                                               pickup_crosshair_size = 1;
-                                       pickup_crosshair_time = stat_pickup_time;
-                               }
-                               if(pickup_crosshair_size > 0)
-                                       pickup_crosshair_size -= autocvar_crosshair_pickup_speed * frametime;
-                               else
-                                       pickup_crosshair_size = 0;
-                               wcross_scale += sin(pickup_crosshair_size) * autocvar_crosshair_pickup;
-                       }
-                       if(autocvar_crosshair_hitindication)
-                       {
-                               vector hitindication_color = ((autocvar_crosshair_color_special == 1) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color));
-                               if(hitindication_crosshair_time < hit_time)
-                               {
-                                       if(time - hit_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old
-                                               hitindication_crosshair_size = 1;
-                                       hitindication_crosshair_time = hit_time;
-                               }
-                               if(hitindication_crosshair_size > 0)
-                                       hitindication_crosshair_size -= autocvar_crosshair_hitindication_speed * frametime;
-                               else
-                                       hitindication_crosshair_size = 0;
-                               wcross_scale += sin(hitindication_crosshair_size) * autocvar_crosshair_hitindication;
-                               wcross_color_x += sin(hitindication_crosshair_size) * hitindication_color_x;
-                               wcross_color_y += sin(hitindication_crosshair_size) * hitindication_color_y;
-                               wcross_color_z += sin(hitindication_crosshair_size) * hitindication_color_z;
-                       }
-                       if(shottype == SHOTTYPE_HITENEMY)
-                               wcross_scale *= autocvar_crosshair_hittest; // is not queried if hittest is 0
-                       if(shottype == SHOTTYPE_HITTEAM)
-                               wcross_scale /= autocvar_crosshair_hittest; // is not queried if hittest is 0
-                       f = fabs(autocvar_crosshair_effect_time);
-                       if(wcross_scale != wcross_scale_goal_prev || wcross_alpha != wcross_alpha_goal_prev || wcross_color != wcross_color_goal_prev)
-                       {
-                               wcross_changedonetime = time + f;
-                       }
-                       if(wcross_name != wcross_name_goal_prev || wcross_resolution != wcross_resolution_goal_prev)
-                       {
-                               wcross_name_changestarttime = time;
-                               wcross_name_changedonetime = time + f;
-                               if(wcross_name_goal_prev_prev)
-                                       strunzone(wcross_name_goal_prev_prev);
-                               wcross_name_goal_prev_prev = wcross_name_goal_prev;
-                               wcross_name_goal_prev = strzone(wcross_name);
-                               wcross_name_alpha_goal_prev_prev = wcross_name_alpha_goal_prev;
-                               wcross_resolution_goal_prev_prev = wcross_resolution_goal_prev;
-                               wcross_resolution_goal_prev = wcross_resolution;
-                       }
-                       wcross_scale_goal_prev = wcross_scale;
-                       wcross_alpha_goal_prev = wcross_alpha;
-                       wcross_color_goal_prev = wcross_color;
-                       if(shottype == SHOTTYPE_HITTEAM || (shottype == SHOTTYPE_HITOBSTRUCTION && autocvar_crosshair_hittest_blur && !autocvar_chase_active))
-                       {
-                               wcross_blur = 1;
-                               wcross_alpha *= 0.75;
-                       }
-                       else
-                               wcross_blur = 0;
-                       // *_prev is at time-frametime
-                       // * is at wcross_changedonetime+f
-                       // what do we have at time?
-                       if(time < wcross_changedonetime)
-                       {
-                               f = frametime / (wcross_changedonetime - time + frametime);
-                               wcross_scale = f * wcross_scale + (1 - f) * wcross_scale_prev;
-                               wcross_alpha = f * wcross_alpha + (1 - f) * wcross_alpha_prev;
-                               wcross_color = f * wcross_color + (1 - f) * wcross_color_prev;
-                       }
-                       wcross_scale_prev = wcross_scale;
-                       wcross_alpha_prev = wcross_alpha;
-                       wcross_color_prev = wcross_color;
-                       wcross_scale *= 1 - autocvar__menu_alpha;
-                       wcross_alpha *= 1 - autocvar__menu_alpha;
-                       wcross_size = draw_getimagesize(wcross_name) * wcross_scale;
-                       if(wcross_scale >= 0.001 && wcross_alpha >= 0.001)
-                       {
-                               // crosshair rings for weapon stats
-                               if (autocvar_crosshair_ring || autocvar_crosshair_ring_reload)
-                               {
-                                       // declarations and stats
-                                       float ring_value = 0, ring_scale = 0, ring_alpha = 0, ring_inner_value = 0, ring_inner_alpha = 0;
-                                       string ring_image = string_null, ring_inner_image = string_null;
-                                       vector ring_rgb = '0 0 0', ring_inner_rgb = '0 0 0';
-                                       ring_scale = autocvar_crosshair_ring_size;
-                                       float weapon_clipload, weapon_clipsize;
-                                       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);
-                                       if(nex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
-                                               nex_charge_movingavg = nex_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 (nex_chargepool || use_nex_chargepool) {
-                                                       use_nex_chargepool = 1;
-                                                       ring_inner_value = nex_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);
-                                               }
-                                               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_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_rgb = wcross_color;
-                                               ring_image = "gfx/crosshair_ring_nexgun.tga";
-                                       }
-                                       else if (autocvar_crosshair_ring && activeweapon == WEP_MINE_LAYER && minelayer_maxmines && autocvar_crosshair_ring_minelayer)
-                                       {
-                                               ring_value = bound(0, getstati(STAT_LAYED_MINES) / minelayer_maxmines, 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to.
-                                               ring_alpha = autocvar_crosshair_ring_minelayer_alpha;
-                                               ring_rgb = wcross_color;
-                                               ring_image = "gfx/crosshair_ring.tga";
-                                       }
-                                       else if (activeweapon == WEP_HAGAR && getstati(STAT_HAGAR_LOAD) && autocvar_crosshair_ring_hagar)
-                                       {
-                                               ring_value = bound(0, getstati(STAT_HAGAR_LOAD) / hagar_maxrockets, 1);
-                                               ring_alpha = autocvar_crosshair_ring_hagar_alpha;
-                                               ring_rgb = wcross_color;
-                                               ring_image = "gfx/crosshair_ring.tga";
-                                       }
-                                       if(autocvar_crosshair_ring_reload && weapon_clipsize) // forces there to be only an ammo ring
-                                       {
-                                               ring_value = bound(0, weapon_clipload / weapon_clipsize, 1);
-                                               ring_scale = autocvar_crosshair_ring_reload_size;
-                                               ring_alpha = autocvar_crosshair_ring_reload_alpha;
-                                               ring_rgb = wcross_color;
-                                               // Note: This is to stop Taoki from complaining that the image doesn't match all potential balances.
-                                               // if a new image for another weapon is added, add the code (and its respective file/value) here
-                                               if ((activeweapon == WEP_RIFLE) && (weapon_clipsize == 80))
-                                                       ring_image = "gfx/crosshair_ring_rifle.tga";
-                                               else
-                                                       ring_image = "gfx/crosshair_ring.tga";
-                                       }
-                                       // if in weapon switch animation, fade ring out/in
-                                       if(autocvar_crosshair_effect_time > 0)
-                                       {
-                                               f = (time - wcross_name_changestarttime) / autocvar_crosshair_effect_time;
-                                               if (!(f < 1))
-                                               {
-                                                       wcross_ring_prev = ((ring_image) ? TRUE : FALSE);
-                                               }
-                                               if(wcross_ring_prev)
-                                               {
-                                                       if(f < 1)
-                                                               ring_alpha *= fabs(1 - bound(0, f, 1));
-                                               }
-                                               else
-                                               {
-                                                       if(f < 1)
-                                                               ring_alpha *= bound(0, f, 1);
-                                               }
-                                       }
-                                       if (autocvar_crosshair_ring_inner && ring_inner_value) // lets draw a ring inside a ring so you can ring while you ring
-                                               DrawCircleClippedPic(wcross_origin, wcross_size_x * ring_scale, ring_inner_image, ring_inner_value, ring_inner_rgb, wcross_alpha * ring_inner_alpha, DRAWFLAG_ADDITIVE);
-                                       if (ring_value)
-                                               DrawCircleClippedPic(wcross_origin, wcross_size_x * ring_scale, ring_image, ring_value, ring_rgb, wcross_alpha * ring_alpha, DRAWFLAG_ADDITIVE);
-                               }
- #define CROSSHAIR_DO_BLUR(M,sz,wcross_name,wcross_alpha) \
-                               do \
-                               { \
-                                       if(wcross_blur > 0) \
-                                       { \
-                                               for(i = -2; i <= 2; ++i) \
-                                               for(j = -2; j <= 2; ++j) \
-                                               M(i,j,sz,wcross_name,wcross_alpha*0.04); \
-                                       } \
-                                       else \
-                                       { \
-                                               M(0,0,sz,wcross_name,wcross_alpha); \
-                                       } \
-                               } \
-                               while(0)
- #define CROSSHAIR_DRAW_SINGLE(i,j,sz,wcross_name,wcross_alpha) \
-                               drawpic(wcross_origin - ('0.5 0 0' * (sz * wcross_size_x + i * wcross_blur) + '0 0.5 0' * (sz * wcross_size_y + j * wcross_blur)), wcross_name, sz * wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL)
- #define CROSSHAIR_DRAW(sz,wcross_name,wcross_alpha) \
-                               CROSSHAIR_DO_BLUR(CROSSHAIR_DRAW_SINGLE,sz,wcross_name,wcross_alpha)
-                               if(time < wcross_name_changedonetime && wcross_name != wcross_name_goal_prev_prev && wcross_name_goal_prev_prev)
-                               {
-                                       f = (wcross_name_changedonetime - time) / (wcross_name_changedonetime - wcross_name_changestarttime);
-                                       wcross_size = draw_getimagesize(wcross_name_goal_prev_prev) * wcross_scale;
-                                       CROSSHAIR_DRAW(wcross_resolution_goal_prev_prev, wcross_name_goal_prev_prev, wcross_alpha * f * wcross_name_alpha_goal_prev_prev);
-                                       f = 1 - f;
-                               }
-                               else
-                               {
-                                       f = 1;
-                               }
-                               wcross_name_alpha_goal_prev = f;
-                               wcross_size = draw_getimagesize(wcross_name) * wcross_scale;
-                               CROSSHAIR_DRAW(wcross_resolution, wcross_name, wcross_alpha * f);
-                               if(autocvar_crosshair_dot)
-                               {
-                                       vector wcross_color_old;
-                                       wcross_color_old = wcross_color;
-                                       if((autocvar_crosshair_dot_color_custom) && (autocvar_crosshair_dot_color != "0"))
-                                               wcross_color = stov(autocvar_crosshair_dot_color);
-                                       CROSSHAIR_DRAW(wcross_resolution * autocvar_crosshair_dot_size, "gfx/crosshairdot.tga", f * autocvar_crosshair_dot_alpha);
-                                       // FIXME why don't we use wcross_alpha here?cl_notice_run();
-                                       wcross_color = wcross_color_old;
-                               }
-                       }
-               }
-               else
-               {
-                       wcross_scale_prev = 0;
-                       wcross_alpha_prev = 0;
-                       wcross_scale_goal_prev = 0;
-                       wcross_alpha_goal_prev = 0;
-                       wcross_changedonetime = 0;
-                       if(wcross_name_goal_prev)
-                               strunzone(wcross_name_goal_prev);
-                       wcross_name_goal_prev = string_null;
-                       if(wcross_name_goal_prev_prev)
-                               strunzone(wcross_name_goal_prev_prev);
-                       wcross_name_goal_prev_prev = string_null;
-                       wcross_name_changestarttime = 0;
-                       wcross_name_changedonetime = 0;
-                       wcross_name_alpha_goal_prev = 0;
-                       wcross_name_alpha_goal_prev_prev = 0;
-                       wcross_resolution_goal_prev = 0;
-                       wcross_resolution_goal_prev_prev = 0;
-               }
-       }
+       UpdateDamage();
+       UpdateCrosshair();
+       UpdateHitsound();
  
        if(NextFrameCommand)
        {
  
        if(autocvar__hud_configure)
                HUD_Panel_Mouse();
 +      else 
 +              HUD_Radar_Mouse();
  
      if(hud && !intermission)
      {
index 36601ecc2ee93ea09f822756bda2da361e6cd549,8215b9faf2aeddfe7057eede883513cbc258a4c7..7957bfacc16389e0d73fe212f325b1cfdf41872c
@@@ -57,13 -57,14 +57,14 @@@ float autocvar_cl_nogibs
  float autocvar_cl_orthoview;
  float autocvar_cl_orthoview_nofog;
  float autocvar_cl_particlegibs;
- float autocvar_cl_particles_oldnexbeam;
+ float autocvar_cl_particles_oldvortexbeam;
  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;
@@@ -76,7 -77,8 +77,8 @@@ float autocvar_cl_stripcolorcodes
  var float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
  var float autocvar_cl_vehicle_spiderbot_cross_size = 1;
  var float autocvar_cl_vehicles_hud_tactical = 1;
- float autocvar_cl_velocityzoom;
+ float autocvar_cl_velocityzoom_enabled;
+ float autocvar_cl_velocityzoom_factor;
  var float autocvar_cl_velocityzoom_type = 3;
  float autocvar_cl_velocityzoom_speed;
  float autocvar_cl_velocityzoom_time;
@@@ -115,6 -117,7 +117,7 @@@ string autocvar_crosshair_hitindication
  float autocvar_crosshair_hitindication_speed;
  float autocvar_crosshair_hittest;
  float autocvar_crosshair_hittest_blur;
+ var float autocvar_crosshair_hittest_scale = 1.25;
  float autocvar_crosshair_hittest_showimpact;
  float autocvar_crosshair_per_weapon;
  float autocvar_crosshair_pickup;
@@@ -125,14 -128,18 +128,18 @@@ 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;
+ var float autocvar_crosshair_ring_vortex = 1;
+ var float autocvar_crosshair_ring_vortex_alpha = 0.15;
+ var float autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate = 0.05;
+ var float autocvar_crosshair_ring_vortex_currentcharge_scale = 30;
+ var float autocvar_crosshair_ring_vortex_inner_alpha = 0.15;
+ var float autocvar_crosshair_ring_vortex_inner_color_blue = 0;
+ var float autocvar_crosshair_ring_vortex_inner_color_green = 0;
+ var float autocvar_crosshair_ring_vortex_inner_color_red = 0.8;
+ var float autocvar_crosshair_ring_arc = 1;
+ var vector autocvar_crosshair_ring_arc_hot_color = '1 0 0';
+ var float autocvar_crosshair_ring_arc_cold_alpha = 0.2;
+ var float autocvar_crosshair_ring_arc_hot_alpha = 0.5;
  float autocvar_crosshair_ring_size;
  float autocvar_crosshair_ring_reload;
  float autocvar_crosshair_ring_reload_alpha;
@@@ -214,6 -221,8 +221,8 @@@ float autocvar_hud_panel_ammo
  float autocvar_hud_panel_ammo_iconalign;
  float autocvar_hud_panel_ammo_maxammo;
  float autocvar_hud_panel_ammo_onlycurrent;
+ float autocvar_hud_panel_ammo_noncurrent_alpha = 0.7;
+ float autocvar_hud_panel_ammo_noncurrent_scale = 1;
  float autocvar_hud_panel_ammo_progressbar;
  string autocvar_hud_panel_ammo_progressbar_name;
  float autocvar_hud_panel_ammo_progressbar_xoffset;
@@@ -268,6 -277,7 +277,7 @@@ float autocvar_hud_panel_notify_fadetim
  float autocvar_hud_panel_notify_flip;
  float autocvar_hud_panel_notify_fontsize;
  float autocvar_hud_panel_notify_time;
+ float autocvar_hud_panel_notify_icon_aspect;
  float autocvar_hud_panel_physics;
  float autocvar_hud_panel_physics_acceleration_progressbar_mode;
  float autocvar_hud_panel_physics_acceleration_progressbar_scale;
@@@ -290,6 -300,8 +300,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;
@@@ -321,6 -333,7 +333,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;
@@@ -408,6 -421,9 +421,9 @@@ float autocvar_vid_conwidth
  float autocvar_vid_pixelheight;
  float autocvar_viewsize;
  float autocvar_cl_hitsound;
+ var float autocvar_cl_hitsound_min_pitch = 0.75; // minimal difference in minsta
+ var float autocvar_cl_hitsound_max_pitch = 1.5;
+ var float autocvar_cl_hitsound_nom_damage = 25;
  float autocvar_cl_hitsound_antispam_time;
  var float autocvar_cl_eventchase_death = 1;
  var float autocvar_cl_eventchase_nexball = 1;
@@@ -416,8 -432,6 +432,8 @@@ var float autocvar_cl_eventchase_speed 
  var vector autocvar_cl_eventchase_maxs = '12 12 8';
  var vector autocvar_cl_eventchase_mins = '-12 -12 -8';
  var vector autocvar_cl_eventchase_viewoffset = '0 0 20';
 +var vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80';
 +var float autocvar_cl_eventchase_generator_distance = 400;
  float autocvar_cl_lerpexcess;
  string autocvar__togglezoom;
  float autocvar_cl_damageeffect;
@@@ -442,3 -456,12 +458,12 @@@ string autocvar__cl_playermodel
  float autocvar_cl_deathglow;
  float autocvar_developer_csqcentities;
  float autocvar_g_jetpack_attenuation;
+ var string autocvar_crosshair_hmg = ""; 
+ var vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2';
+ var float autocvar_crosshair_hmg_alpha = 1;
+ var float autocvar_crosshair_hmg_size = 1;
+ var string autocvar_crosshair_rpc = ""; 
+ var vector autocvar_crosshair_rpc_color = '0.2 1.0 0.2';
+ var float autocvar_crosshair_rpc_alpha = 1;
+ var float autocvar_crosshair_rpc_size = 1;
+ float autocvar_cl_nade_timer;
diff --combined qcsrc/client/hud.qc
index 27499cd1afb88c4bafc20f9179a5bb4ae1e972c0,9e6861ebd06e7d05a4a0fabfc4c305dc4433c3c3..a85fca68884ab9b187c40a5014e5fd0073e6f2ac
@@@ -218,9 -218,9 +218,9 @@@ string MakeRaceString(float cp, float m
        if(histime < 0)
                return strcat(col, cpname);
        else if(hisname == "")
-               return strcat(col, sprintf(_("%s (%s)"), cpname, timestr));
+               return strcat(col, sprintf("%s (%s)", cpname, timestr));
        else
-               return strcat(col, sprintf(_("%s (%s %s)"), cpname, timestr, strcat(hisname, col, lapstr)));
+               return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(hisname, col, lapstr)));
  }
  
  // Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
@@@ -408,42 -408,6 +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)
+ void DrawNadeScoreBar(vector myPos, vector mySize, vector color)
  {
-       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;
-       }
+       
+       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);
  }
  
string GetAmmoPicture(float i)
void DrawAmmoNades(vector myPos, vector mySize, float draw_expanding, float expand_time)
  {
-       switch(i)
+       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)
        {
-               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 "";
+               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 isCurrent, float isInfinite)
  {
-       float a;
+       if(ammoType == ammo_none)
+               return;
+       // Initialize variables
+       float ammo;
        if(autocvar__hud_configure)
        {
-               currently_selected = (itemcode == 2); //rockets always selected
-               a = 31 + mod(itemcode*93, 128);
+               isCurrent = (ammoType == ammo_rockets); // Rockets always current
+               ammo = 60;
        }
        else
-               a = getstati(GetAmmoStat(itemcode)); // how much ammo do we have of type itemcode?
-       vector color;
-       if(infinite_ammo)
-               color = '0 0.5 0.75';
-       else if(a < 10)
-               color = '0.7 0 0';
-       else
-               color = '1 1 1';
+               ammo = getstati(GetAmmoStat(ammoType));
  
-       float theAlpha;
-       if(currently_selected)
-               theAlpha = 1;
-       else
-               theAlpha = 0.7;
+       if(!isCurrent)
+       {
+               float scale = bound(0, autocvar_hud_panel_ammo_noncurrent_scale, 1);
+               myPos = myPos + (mySize - mySize * scale) * 0.5;
+               mySize = mySize * scale;
+       }
  
-       vector picpos, numpos;
+       vector iconPos, textPos;
        if(autocvar_hud_panel_ammo_iconalign)
        {
-               numpos = myPos;
-               picpos = myPos + eX * 2 * mySize_y;
+               iconPos = myPos + eX * 2 * mySize_y;
+               textPos = myPos;
        }
        else
        {
-               numpos = myPos + eX * mySize_y;
-               picpos = myPos;
+               iconPos = myPos;
+               textPos = myPos + eX * mySize_y;
        }
  
-       if (currently_selected)
+       float isShadowed = (ammo <= 0 && !isCurrent && !isInfinite);
+       vector iconColor = isShadowed ? '0 0 0' : '1 1 1';
+       vector textColor;
+       if(isInfinite)
+               textColor = '0.2 0.95 0';
+       else if(isShadowed)
+               textColor = '0 0 0';
+       else if(ammo < 10)
+               textColor = '0.8 0.04 0';
+       else
+               textColor = '1 1 1';
+       float alpha;
+       if(isCurrent)
+               alpha = panel_fg_alpha;
+       else if(isShadowed)
+               alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1) * 0.5;
+       else
+               alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1);
+       string text = isInfinite ? "\xE2\x88\x9E" : ftos(ammo); // Use infinity symbol (U+221E)
+       // Draw item
+       if(isCurrent)
                drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
  
-     if(a > 0 && autocvar_hud_panel_ammo_progressbar)
-         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, a/autocvar_hud_panel_ammo_maxammo, 0, 0, color, autocvar_hud_progressbar_alpha * panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+       if(ammo > 0 && autocvar_hud_panel_ammo_progressbar)
+               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, ammo/autocvar_hud_panel_ammo_maxammo, 0, 0, textColor, autocvar_hud_progressbar_alpha * alpha, DRAWFLAG_NORMAL);
  
-     if(autocvar_hud_panel_ammo_text)
-     {
-         if(a > 0 || infinite_ammo)
-             drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize_x + eY * mySize_y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
-         else // "ghost" ammo count
-             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);
-       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);
+       if(autocvar_hud_panel_ammo_text)
+               drawstring_aspect(textPos, text, eX * (2/3) * mySize_x + eY * mySize_y, textColor, alpha, DRAWFLAG_NORMAL);
+       drawpic_aspect_skin(iconPos, GetAmmoPicture(ammoType), '1 1 0' * mySize_y, iconColor, alpha, 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 = 4;
        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;
+               total_ammo_count = 1;
        else
+               total_ammo_count = AMMO_COUNT;
+       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;
-       if (autocvar_hud_panel_ammo_onlycurrent)
+       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_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;
+               .float ammotype;
                row = column = 0;
-               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);
+               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();
  }
  
@@@ -1601,148 -1649,154 +1649,154 @@@ void HUD_HealthArmor(void
  
  void HUD_Notify_Push(string icon, string attacker, string victim)
  {
-       if(icon != "")
-       {
-               --kn_index;
-               if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
-               notify_times[kn_index] = time;
+       if (icon == "")
+               return;
+       ++notify_count;
+       --notify_index;
+       if (notify_index == -1)
+               notify_index = NOTIFY_MAX_ENTRIES-1;
+       // Free old strings
+       if (notify_attackers[notify_index])
+               strunzone(notify_attackers[notify_index]);
  
-               // icon
-               if(notify_icon[kn_index]) { strunzone(notify_icon[kn_index]); }
-               notify_icon[kn_index] = strzone(icon);
+       if (notify_victims[notify_index])
+               strunzone(notify_victims[notify_index]);
  
-               // attacker
-               if(notify_attackers[kn_index]) { strunzone(notify_attackers[kn_index]); }
-               notify_attackers[kn_index] = strzone(attacker);
+       if (notify_icons[notify_index])
+               strunzone(notify_icons[notify_index]);
  
-               // victim
-               if(notify_victims[kn_index]) { strunzone(notify_victims[kn_index]); }
-               notify_victims[kn_index] = strzone(victim);
+       // Allocate new strings
+       if (victim != "")
+       {
+               notify_attackers[notify_index] = strzone(attacker);
+               notify_victims[notify_index] = strzone(victim);
+       }
+       else
+       {
+               // In case of a notification without a victim, the attacker
+               // is displayed on the victim's side. Instead of special
+               // treatment later on, we can simply switch them here.
+               notify_attackers[notify_index] = string_null;
+               notify_victims[notify_index] = strzone(attacker);
        }
+       notify_icons[notify_index] = strzone(icon);
+       notify_times[notify_index] = time;
  }
  
  void HUD_Notify(void)
  {
-       if(!autocvar__hud_configure)
-       {
-               if(!autocvar_hud_panel_notify) return;
-       }
+       if (!autocvar__hud_configure)
+               if (!autocvar_hud_panel_notify)
+                       return;
  
        HUD_Panel_UpdateCvars();
-       vector pos, mySize;
-       pos = panel_pos;
-       mySize = panel_size;
        HUD_Panel_DrawBg(1);
-       if(panel_bg_padding)
+       if (!autocvar__hud_configure)
+               if (notify_count == 0)
+                       return;
+       vector pos, size;
+       pos  = panel_pos;
+       size = panel_size;
+       if (panel_bg_padding)
        {
-               pos += '1 1 0' * panel_bg_padding;
-               mySize -= '2 2 0' * panel_bg_padding;
+               pos  += '1 1 0' * panel_bg_padding;
+               size -= '2 2 0' * panel_bg_padding;
        }
  
-       float entries, height;
-       entries = bound(1, floor(KN_MAX_ENTRIES * mySize_y/mySize_x), KN_MAX_ENTRIES);
-       height = mySize_y/entries;
+       float fade_start = max(0, autocvar_hud_panel_notify_time);
+       float fade_time = max(0, autocvar_hud_panel_notify_fadetime);
+       float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect);
  
-       vector fontsize;
-       float fontheight = height * autocvar_hud_panel_notify_fontsize;
-       fontsize = '0.5 0.5 0' * fontheight;
+       float entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size_y / size_x), NOTIFY_MAX_ENTRIES);
+       float entry_height = size_y / entry_count;
  
-       float a;
-       float when;
-       when = autocvar_hud_panel_notify_time;
-       float fadetime;
-       fadetime = autocvar_hud_panel_notify_fadetime;
+       float panel_width_half = size_x * 0.5;
+       float icon_width_half = entry_height * icon_aspect / 2;
+       float name_maxwidth = panel_width_half - icon_width_half - size_x * NOTIFY_ICON_MARGIN;
  
-       vector pos_attacker, pos_victim, pos_icon;
-       float width_attacker;
+       vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize;
+       vector icon_size = (eX * icon_aspect + eY) * entry_height;
+       vector icon_left = eX * (panel_width_half - icon_width_half);
+       vector attacker_right = eX * name_maxwidth;
+       vector victim_left = eX * (size_x - name_maxwidth);
+       vector attacker_pos, victim_pos, icon_pos;
        string attacker, victim, icon;
+       float i, j, count, step, limit, alpha;
  
-       float i, j, step, limit;
-       if(autocvar_hud_panel_notify_flip) //order items from the top down
+       if (autocvar_hud_panel_notify_flip)
        {
+               // Order items from the top down
                i = 0;
                step = +1;
-               limit = entries;
+               limit = entry_count;
        }
-       else //order items from the bottom up
+       else
        {
-               i = entries - 1;
+               // Order items from the bottom up
+               i = entry_count - 1;
                step = -1;
                limit = -1;
        }
  
-       for(j = kn_index;  i != limit;  i += step, ++j)
+       for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count)
        {
                if(autocvar__hud_configure)
                {
-                       if (step == +1)
-                               a = i;
-                       else // inverse order
-                               a = entries - 1 - i;
-                       attacker = textShortenToWidth(sprintf(_("Player %d"), a+1), 0.48 * mySize_x - height, fontsize, stringwidth_colors);
-                       victim = textShortenToWidth(sprintf(_("Player %d"), a+2), 0.48 * mySize_x - height, fontsize, stringwidth_colors);
-                       icon = strcat("weapon", get_weaponinfo(WEP_FIRST + mod(floor(a*2.4), WEP_LAST)).netname);
-                       a = bound(0, (when - a) / 4, 1);
-                       goto hud_config_notifyprint;
+                       attacker = sprintf(_("Player %d"), count + 1);
+                       victim = sprintf(_("Player %d"), count + 2);
+                       icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2;
+                       alpha = bound(0, 1.2 - count / entry_count, 1);
                }
                else
                {
-                       if (j == KN_MAX_ENTRIES)
+                       if (j == NOTIFY_MAX_ENTRIES)
                                j = 0;
  
-                       if(notify_times[j] + when > time)
-                               a = 1;
-                       else if(fadetime)
+                       if (notify_times[j] + fade_start > time)
+                               alpha = 1;
+                       else if (fade_time != 0)
                        {
-                               a = bound(0, (notify_times[j] + when + fadetime - time) / fadetime, 1);
-                               if(!a)
-                               {
+                               alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1);
+                               if (alpha == 0)
                                        break;
-                               }
                        }
                        else
-                       {
                                break;
-                       }
  
                        attacker = notify_attackers[j];
                        victim = notify_victims[j];
-                       icon = notify_icon[j];
+                       icon = notify_icons[j];
                }
  
-               //type = notify_deathtype[j];
-               //w = DEATH_WEAPONOF(type);
-               if(icon != "")
+               if (icon != "" && victim != "")
                {
-                       if((attacker != "") && (victim == ""))
-                       {
-                               // Y [used by] X
-                               attacker = textShortenToWidth(attacker, 0.73 * mySize_x - height, fontsize, stringwidth_colors);
-                               pos_attacker = pos + eX * (0.27 * mySize_x + height) + eY * ((0.5 * fontsize_y + i * height) + (0.5 * (height - fontheight)));
-                               pos_icon = pos + eX * 0.25 * mySize_x - eX * height + eY * i * height;
+                       vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size_y));
  
-                               drawpic_aspect_skin(pos_icon, icon, '2 1 0' * height, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
-                               drawcolorcodedstring(pos_attacker, attacker, fontsize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
-                       }
-                       else if((attacker != "") && (victim != ""))
+                       icon_pos = pos + icon_left + eY * i * entry_height;
+                       drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+                       victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors);
+                       victim_pos = pos + victim_left + name_top;
+                       drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+                       if (attacker != "")
                        {
-                               // X [did action to] Y
-                               attacker = textShortenToWidth(attacker, 0.48 * mySize_x - height, fontsize, stringwidth_colors);
-                               victim = textShortenToWidth(victim, 0.48 * mySize_x - height, fontsize, stringwidth_colors);
- :hud_config_notifyprint
-                               width_attacker = stringwidth(attacker, TRUE, fontsize);
-                               pos_attacker = pos + eX * (0.48 * mySize_x - height - width_attacker) + eY * ((0.5 * fontsize_y + i * height) + (0.5 * (height - fontheight)));
-                               pos_victim = pos + eX * (0.52 * mySize_x + height) + eY * ((0.5 * fontsize_y + i * height) + (0.5 * (height - fontheight)));
-                               pos_icon = pos + eX * 0.5 * mySize_x - eX * height + eY * i * height;
-                               drawpic_aspect_skin(pos_icon, icon, '2 1 0' * height, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
-                               drawcolorcodedstring(pos_attacker, attacker, fontsize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
-                               drawcolorcodedstring(pos_victim, victim, fontsize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+                               attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors);
+                               attacker_pos = pos + attacker_right - eX * stringwidth(attacker, TRUE, font_size) + name_top;
+                               drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
                        }
                }
        }
+       notify_count = count;
  }
  
  // Timer (#5)
@@@ -1816,167 -1870,6 +1870,167 @@@ void HUD_Timer(void
  
  // Radar (#6)
  //
 +
 +float HUD_Radar_Clickable()
 +{
 +      return hud_panel_radar_mouse && !hud_panel_radar_temp_hidden;
 +}
 +
 +void HUD_Radar_Show_Maximized(float show,float clickable)
 +{
 +      hud_panel_radar_maximized = show;
 +      hud_panel_radar_temp_hidden = 0;
 +      
 +      if ( show )
 +      {
 +              if (clickable)
 +              {
 +                      if(autocvar_hud_cursormode)
 +                              setcursormode(1);
 +                      hud_panel_radar_mouse = 1; 
 +              }
 +      }
 +      else if ( hud_panel_radar_mouse )
 +      {
 +              hud_panel_radar_mouse = 0;
 +              mouseClicked = 0;
 +              if(autocvar_hud_cursormode)
 +              if(!mv_active)
 +                      setcursormode(0);
 +      }
 +}
 +void HUD_Radar_Hide_Maximized()
 +{
 +      HUD_Radar_Show_Maximized(false,false);
 +}
 +
 +
 +float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary)
 +{
 +      if(!hud_panel_radar_maximized || !hud_panel_radar_mouse || 
 +              autocvar__hud_configure || mv_active)
 +              return false;
 +
 +      if(bInputType == 3)
 +      {
 +              mousepos_x = nPrimary;
 +              mousepos_y = nSecondary;
 +              return true;
 +      }
 +
 +      if(nPrimary == K_MOUSE1)
 +      {
 +              if(bInputType == 0) // key pressed
 +                      mouseClicked |= S_MOUSE1;
 +              else if(bInputType == 1) // key released
 +                      mouseClicked -= (mouseClicked & S_MOUSE1);
 +      }
 +      else if(nPrimary == K_MOUSE2)
 +      {
 +              if(bInputType == 0) // key pressed
 +                      mouseClicked |= S_MOUSE2;
 +              else if(bInputType == 1) // key released
 +                      mouseClicked -= (mouseClicked & S_MOUSE2);
 +      }
 +      else if ( nPrimary == K_ESCAPE && bInputType == 0 )
 +      {
 +              HUD_Radar_Hide_Maximized();
 +      }
 +      else
 +      {
 +              // allow console/use binds to work without hiding the map
 +              string con_keys;
 +              float keys;
 +              float i;
 +              con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ;
 +              keys = tokenize(con_keys); // findkeysforcommand returns data for this
 +              for (i = 0; i < keys; ++i)
 +              {
 +                      if(nPrimary == stof(argv(i)))
 +                              return false;
 +              }
 +              
 +              if ( getstati(STAT_HEALTH) <= 0 )
 +              {
 +                      // Show scoreboard
 +                      if ( bInputType < 2 )
 +                      {
 +                              con_keys = findkeysforcommand("+showscores", 0);
 +                              keys = tokenize(con_keys);
 +                              for (i = 0; i < keys; ++i)
 +                              {
 +                                      if ( nPrimary == stof(argv(i)) )
 +                                      {
 +                                              hud_panel_radar_temp_hidden = bInputType == 0;
 +                                              return false;
 +                                      }
 +                              }
 +                      }
 +              }
 +              else if ( bInputType == 0 )
 +                      HUD_Radar_Hide_Maximized();
 +              
 +              return false;
 +      }
 +
 +      return true;
 +}
 +
 +void HUD_Radar_Mouse()
 +{
 +      if ( !hud_panel_radar_mouse ) return;
 +      if(mv_active) return;
 +      
 +      if ( intermission )
 +      {
 +              HUD_Radar_Hide_Maximized();
 +              return;
 +      }
 +      
 +      if(mouseClicked & S_MOUSE2)
 +      {
 +              HUD_Radar_Hide_Maximized();
 +              return;
 +      }
 +      
 +      if(!autocvar_hud_cursormode)
 +      {
 +              mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
 +
 +              mousepos_x = bound(0, mousepos_x, vid_conwidth);
 +              mousepos_y = bound(0, mousepos_y, vid_conheight);
 +      }
 +
 +      HUD_Panel_UpdateCvars();
 +      
 +      
 +      panel_size = autocvar_hud_panel_radar_maximized_size;
 +      panel_size_x = bound(0.2, panel_size_x, 1) * vid_conwidth;
 +      panel_size_y = bound(0.2, panel_size_y, 1) * vid_conheight;
 +      panel_pos_x = (vid_conwidth - panel_size_x) / 2;
 +      panel_pos_y = (vid_conheight - panel_size_y) / 2;
 +              
 +      if(mouseClicked & S_MOUSE1)
 +      {
 +              // click outside
 +              if ( mousepos_x < panel_pos_x || mousepos_x > panel_pos_x + panel_size_x ||
 +                       mousepos_y < panel_pos_y || mousepos_y > panel_pos_y + panel_size_y )
 +              {
 +                      HUD_Radar_Hide_Maximized();
 +                      return;
 +              }
 +              vector pos = teamradar_texcoord_to_3dcoord(teamradar_2dcoord_to_texcoord(mousepos),view_origin_z);
 +              localcmd(sprintf("cmd ons_spawn %f %f %f",pos_x,pos_y,pos_z));
 +              
 +              HUD_Radar_Hide_Maximized();
 +              return;
 +      }
 +      
 +
 +      const vector cursorsize = '32 32 0';
 +      drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', 0.8, DRAWFLAG_NORMAL);
 +}
 +
  void HUD_Radar(void)
  {
        if (!autocvar__hud_configure)
                        }
                }
        }
 +      
 +      if ( hud_panel_radar_temp_hidden )
 +              return;
  
        HUD_Panel_UpdateCvars();
  
  
        for(tm = world; (tm = find(tm, classname, "radarlink")); )
                draw_teamradar_link(tm.origin, tm.velocity, tm.team);
 +      
 +      vector coord;
 +      vector brightcolor;
        for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); )
 +      {
 +              if ( hud_panel_radar_mouse )
 +              if ( tm.health > 0 )
 +              if ( tm.team == myteam+1 )
 +              {
 +                      coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(tm.origin));
 +                      if ( vlen(mousepos-coord) < 8 )
 +                      {
 +                              brightcolor_x = min(1,tm.teamradar_color_x*1.5);
 +                              brightcolor_y = min(1,tm.teamradar_color_y*1.5);
 +                              brightcolor_z = min(1,tm.teamradar_color_z*1.5);
 +                              drawpic(coord - '8 8 0', "gfx/teamradar_icon_glow", '16 16 0', brightcolor, panel_fg_alpha, 0);
 +                      }
 +              }
 +              
                draw_teamradar_icon(tm.origin, tm.teamradar_icon, tm, tm.teamradar_color, panel_fg_alpha);
 +      }
        for(tm = world; (tm = find(tm, classname, "entcs_receiver")); )
        {
                color2 = GetPlayerColor(tm.sv_entnum);
        draw_teamradar_player(view_origin, view_angles, '1 1 1');
  
        drawresetcliparea();
 +      
 +      if ( hud_panel_radar_mouse )
 +      {                       
 +              string message = "Click to select teleport destination";
 +              
 +              if ( getstati(STAT_HEALTH) <= 0 )
 +              {
 +                      message = "Click to select spawn location";
 +              }
 +              
 +              drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, TRUE, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2,
 +                                                       message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL);
 +              
 +              hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y;
 +      }
  }
  
  // Score (#7)
@@@ -2349,11 -2205,7 +2403,7 @@@ void HUD_Score(void
        vector distribution_color;
        entity tm, pl, me;
  
- #ifdef COMPAT_XON050_ENGINE
-       me = (spectatee_status > 0) ? playerslots[spectatee_status - 1] : playerslots[player_localentnum - 1];
- #else
        me = playerslots[player_localentnum - 1];
- #endif
  
        if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
                string timer, distrtimer;
@@@ -3264,7 -3116,7 +3314,7 @@@ void HUD_Mod_NexBall(vector pos, vecto
        //Manage the progress bar if any
        if (nb_pb_starttime > 0)
        {
-               dt = mod(time - nb_pb_starttime, nb_pb_period);
+               dt = (time - nb_pb_starttime) % nb_pb_period;
                // one period of positive triangle
                p = 2 * dt / nb_pb_period;
                if (p > 1)
@@@ -3854,11 -3706,7 +3904,7 @@@ void HUD_InfoMessages(void
                        if(spectatee_status == -1)
                                s = _("^1Observing");
                        else
- #ifdef COMPAT_XON050_ENGINE
-                               s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(spectatee_status - 1));
- #else
                                s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(player_localentnum - 1));
- #endif
                        drawInfoMessage(s)
  
                        if(spectatee_status == -1)
                }
  
                string blinkcolor;
-               if(mod(time, 1) >= 0.5)
+               if(time % 1 >= 0.5)
                        blinkcolor = "^1";
                else
                        blinkcolor = "^3";
@@@ -4371,9 -4219,9 +4417,9 @@@ void HUD_CenterPrint (void
                {
                        float r;
                        r = random();
-                       if (r > 0.9)
+                       if (r > 0.75)
                                centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10);
-                       else if (r > 0.8)
+                       else if (r > 0.5)
                                centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0);
                        else
                                centerprint_hud(sprintf("Message at time %s", seconds_tostring(time)));
                hud_fade_alpha = 1 - autocvar__menu_alpha;
        }
        HUD_Panel_UpdateCvars();
 -
 -      if(scoreboard_fade_alpha)
 +      
 +      if ( HUD_Radar_Clickable() )
 +      {
 +              if (hud_panel_radar_bottom >= 0.96 * vid_conheight)
 +                      return;
 +              
 +              panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x);
 +              panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom);
 +      }
 +      else if(scoreboard_fade_alpha)
        {
                hud_fade_alpha = hud_fade_alpha_save;
  
                else // Expiring soon, so fade it out.
                        a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
  
-               if (a <= 0.5/255.0)  // Guaranteed invisible - don't show.
+               // while counting down show it anyway in order to hold the current message position
+               if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0)  // Guaranteed invisible - don't show.
                        continue;
                if (a > 1)
                        a = 1;
        }
  }
  
+ // 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
@@@ -4626,11 -4527,7 +4733,7 @@@ void HUD_Main (void
                hud_skin_prev = strzone(autocvar_hud_skin);
        }
  
- #ifdef COMPAT_XON050_ENGINE
-     current_player = (spectatee_status > 0) ? spectatee_status : player_localentnum;
- #else
      current_player = player_localentnum;
- #endif
  
        // draw the dock
        if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
                        }
                        else if(hud_dock_color == "pants") {
                                f = stof(getplayerkeyvalue(current_player - 1, "colors"));
-                               color = colormapPaletteColor(mod(f, 16), 1);
+                               color = colormapPaletteColor(f % 16, 1);
                        }
                        else
                                color = stov(hud_dock_color);
                        }
                }
                if (warning)
-                       print(_("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n"));
+                       dprint("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
  
                cvar_set("_hud_panelorder", s);
                if(hud_panelorder_prev)
diff --combined qcsrc/client/hud.qh
index 2747cc10bff13fcfd4ed8214cd585639d27f7d35,d56caf1331e88fa48e9624f20b2cf7982c581f70..f9bd859c528f2fba8d17ce6b2095b5243089ace1
@@@ -9,14 -9,9 +9,14 @@@ string hud_panelorder_prev
  
  float hud_draw_maximized;
  float hud_panel_radar_maximized;
 +float hud_panel_radar_mouse;
 +float hud_panel_radar_bottom;
 +float hud_panel_radar_temp_hidden;
  float chat_panel_modified;
  float radar_panel_modified;
  
 +void HUD_Radar_Hide_Maximized();
 +
  vector mousepos;
  vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click)
  vector panel_click_resizeorigin; // coordinates for opposite point when resizing
@@@ -101,8 -96,6 +101,8 @@@ var string panel_bg_padding_str
  
  float current_player;
  
 +float mv_active;
 +
  
  #define HUD_PANELS \
        HUD_PANEL(WEAPONS      , HUD_Weapons      , weapons) \
        HUD_PANEL(ENGINEINFO   , HUD_EngineInfo   , engineinfo) \
        HUD_PANEL(INFOMESSAGES , HUD_InfoMessages , infomessages) \
        HUD_PANEL(PHYSICS      , HUD_Physics      , physics) \
-       HUD_PANEL(CENTERPRINT  , HUD_CenterPrint  , centerprint)
+       HUD_PANEL(CENTERPRINT  , HUD_CenterPrint  , centerprint) \
+       HUD_PANEL(BUFFS        , HUD_Buffs        , buffs) 
  
  #define HUD_PANEL(NAME,draw_func,name) \
        float HUD_PANEL_##NAME; \
@@@ -195,7 -189,7 +196,7 @@@ if((teamplay) && panel_bg_color_team) {
                if(panel_bg_color_str == "shirt") {\
                        panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player - 1, "colors")) / 16), 0);\
                } else if(panel_bg_color_str == "pants") {\
-                       panel_bg_color = colormapPaletteColor(mod(stof(getplayerkeyvalue(current_player - 1, "colors")), 16), 1);\
+                       panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player - 1, "colors")) % 16, 1);\
                } else {\
                        panel_bg_color = stov(panel_bg_color_str);\
                }\
@@@ -344,14 -338,16 +345,16 @@@ panel_bg_border_str = cvar_string(strca
  HUD_Panel_GetBorder() \
  } ENDS_WITH_CURLY_BRACE
  
+ #define NOTIFY_MAX_ENTRIES 10
+ #define NOTIFY_ICON_MARGIN 0.02
  
- #define KN_MAX_ENTRIES 10
+ float notify_index;
+ float notify_count;
+ float notify_times[NOTIFY_MAX_ENTRIES];
+ string notify_attackers[NOTIFY_MAX_ENTRIES];
+ string notify_victims[NOTIFY_MAX_ENTRIES];
+ string notify_icons[NOTIFY_MAX_ENTRIES];
  
- float kn_index;
- float notify_times[KN_MAX_ENTRIES];
- string notify_icon[KN_MAX_ENTRIES];
- string notify_attackers[KN_MAX_ENTRIES];
- string notify_victims[KN_MAX_ENTRIES];
  void HUD_Notify_Push(string icon, string attacker, string victim);
  
  var void HUD_ModIcons_GameType(vector pos, vector size);
index 75a280fe6e87cf4b882dd724602ca65d784337e4,3f30a6a722f7d3e805e6423facaca0b7ad81ab00..01862f5ebce06e8a8503fcda1d1a49e298257852
@@@ -1,21 -1,31 +1,30 @@@
  float mv_num_maps;
  
 -float mv_active;
  string mv_maps[MAPVOTE_COUNT];
  string mv_pics[MAPVOTE_COUNT];
  string mv_pk3[MAPVOTE_COUNT];
  float mv_preview[MAPVOTE_COUNT];
  float mv_votes[MAPVOTE_COUNT];
+ float mv_avail[MAPVOTE_COUNT];
+ float mv_avail_start[MAPVOTE_COUNT];
  entity mv_pk3list;
  float mv_abstain;
  float mv_ownvote;
  float mv_detail;
  float mv_timeout;
- float mv_maps_mask;
  float mv_top2_time;
  float mv_top2_alpha;
  
  vector mv_mousepos;
  float mv_selection;
+ float mv_columns;
+ float mv_mouse_selection;
+ float mv_selection_keyboard;
+ float gametypevote;
+ string mapvote_chosenmap;
+ vector gtv_text_size;
+ vector gtv_text_size_small;
  
  string MapVote_FormatMapItem(float id, string map, float count, float maxwidth, vector fontsize)
  {
@@@ -25,7 -35,7 +34,7 @@@
        {
                if(count == 1)
                        post = _(" (1 vote)");
-               else if(count >= 0)
+               else if(count >= 0 && mv_avail[id] == GTV_AVAILABLE)
                        post = sprintf(_(" (%d votes)"), count);
                else
                        post = "";
        return strcat(pre, map, post);
  }
  
- vector MapVote_RGB(float id, float count)
+ string GameTypeVote_DescriptionByID(float id)
+ {
+       return MapInfo_Type_Description(MapInfo_Type_FromString(mv_maps[id]));
+ }
+ vector MapVote_RGB(float id)
  {
-       if(count < 0)
+       if(mv_avail[id] != GTV_AVAILABLE)
                return '1 1 1';
        if(id == mv_ownvote)
                return '0 1 0';
                return '1 1 1';
  }
  
+ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, float count, float id)
+ {
+       float alpha;
+       float desc_padding = gtv_text_size_x * 3;
+       float rect_margin = hud_fontsize_y / 2;
+       vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
+       vector rect_size = '1 1 0';
+       rect_size_x = tsize + rect_margin;
+       rect_size_y = maxh + rect_margin;
+       vector rgb = MapVote_RGB(id);
+       vector offset = pos;
+       float nlines = 0;
+       
+       if(mv_avail_start[id] != GTV_AVAILABLE)
+               alpha = 0.2;
+       else if ( mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
+               alpha = mv_top2_alpha;
+       else
+               alpha = 1;
+       
+       if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
+       {
+               drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
+       }
+       if(id == mv_ownvote)
+       {
+               drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL);
+               drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
+       }
+       
+       entity title;
+       title = spawn();
+       title.message = MapVote_FormatMapItem(id, MapInfo_Type_ToText(MapInfo_Type_FromString(gtype)), 
+                                                                                 count, tsize, gtv_text_size);
+       title.origin = pos-offset;
+       
+       pos_y += gtv_text_size_small_y;
+       pos_y += gtv_text_size_y/2;
+       
+       maxh -= gtv_text_size_y;
+       
+       entity picent = spawn();
+       picent.origin = pos-offset;
+       picent.maxs = '1 1 0 ' * min(maxh, desc_padding) * 0.8;
+       
+       pos_x += desc_padding;
+       tsize -= desc_padding;
+       
+       string thelabel = GameTypeVote_DescriptionByID(id), ts;
+       entity last = title;
+       entity next = world;
+       if( thelabel != "") 
+       {
+               float i,n = tokenizebyseparator(thelabel, "\n");
+               for(i = 0; i < n && maxh > (nlines+1)*gtv_text_size_small_y; ++i)
+               {
+                       getWrappedLine_remaining = argv(i);
+                       while(getWrappedLine_remaining && maxh > (nlines+1)*gtv_text_size_small_y)
+                       {
+                               ts = getWrappedLine(tsize, gtv_text_size_small, stringwidth_colors);
+                               if (ts != "")
+                               {
+                                       next = spawn();
+                                       next.message = ts;
+                                       next.origin = pos-offset;
+                                       last.chain = next;
+                                       last = next;
+                                       pos_y += gtv_text_size_small_y;
+                                       nlines++;
+                               }
+                       }
+               }
+       }
+       
+       maxh -= max(nlines*gtv_text_size_small_y,picent.maxs_y);
+       if ( maxh > 0 )
+               offset_y += maxh/2;
+       drawstring(title.origin+offset, title.message, gtv_text_size, rgb, alpha, DRAWFLAG_NORMAL); 
+       
+       if(pic != "")
+               drawpic(picent.origin+offset, pic, picent.maxs, '1 1 1', alpha, DRAWFLAG_NORMAL);
+       
+       for ( last = title.chain; last ; )
+       {
+               drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
+               next = last;
+               last = last.chain;
+               remove(next);
+       }
+       
+       remove(picent);
+       remove(title);
+ }
  void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, float id)
  {
        vector img_size = '0 0 0';
  
        isize -= hud_fontsize_y; // respect the text when calculating the image size
  
-       rgb = MapVote_RGB(id, count);
+       rgb = MapVote_RGB(id);
  
        img_size_y = isize;
        img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
        text_size = stringwidth(label, false, hud_fontsize);
  
        float theAlpha;
-       if (count < 0 && mv_top2_alpha)
+       if (mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
                theAlpha = mv_top2_alpha;
        else
                theAlpha = 1;
        else
                drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, '0 0 0', theAlpha, DRAWFLAG_NORMAL);
  
-       if(id == mv_selection && count >= 0)
+       if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
                drawfill(pos, img_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
  }
  
@@@ -110,7 -219,7 +218,7 @@@ void MapVote_DrawAbstain(vector pos, fl
        float text_size;
        string label;
  
-       rgb = MapVote_RGB(id, count);
+       rgb = MapVote_RGB(id);
  
        pos_y = pos_y + hud_fontsize_y;
  
  vector MapVote_GridVec(vector gridspec, float i, float m)
  {
        float r;
-       r = mod(i, m);
+       r = i % m;
        return
                '1 0 0' * (gridspec_x * r)
                +
  
  float MapVote_Selection(vector topleft, vector cellsize, float rows, float columns)
  {
-       float cell;
        float c, r;
  
-       cell = -1;
+       mv_mouse_selection = -1;
  
        for (r = 0; r < rows; ++r)
                for (c = 0; c < columns; ++c)
                                mv_mousepos_y >= topleft_y + cellsize_y *  r &&
                                mv_mousepos_y <= topleft_y + cellsize_y * (r + 1))
                        {
-                               cell = r * columns + c;
+                               mv_mouse_selection = r * columns + c;
                                break;
                        }
                }
  
-       if (cell >= mv_num_maps)
-               cell = -1;
+       if (mv_mouse_selection >= mv_num_maps)
+               mv_mouse_selection = -1;
  
-       if (mv_abstain && cell < 0)
-               return mv_num_maps;
+       if (mv_abstain && mv_mouse_selection < 0)
+               mv_mouse_selection = mv_num_maps;
  
-       return cell;
+       if ( mv_selection_keyboard )
+               return mv_selection;
+       
+       return mv_mouse_selection;
  }
  
  void MapVote_Draw()
        vector pos;
        float isize;
        float center;
-       float columns, rows;
+       float rows;
        float tsize;
        vector dist = '0 0 0';
  
  
        if (!autocvar_hud_cursormode)
        {
-               mv_mousepos = mv_mousepos + getmousepos();
+               vector mpos = mv_mousepos + getmousepos();
+               mpos_x = bound(0, mpos_x, vid_conwidth);
+               mpos_y = bound(0, mpos_y, vid_conheight);
+               
+               if ( mpos_x != mv_mousepos_x || mpos_y != mv_mousepos_y )
+                       mv_selection_keyboard = 0;
+               mv_mousepos = mpos;
  
-               mv_mousepos_x = bound(0, mv_mousepos_x, vid_conwidth);
-               mv_mousepos_y = bound(0, mv_mousepos_y, vid_conheight);
        }
  
        center = (vid_conwidth - 1)/2;
        pos_z = 0;
  
        draw_beginBoldFont();
-       map = _("Vote for a map");
+       map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map"));
        pos_x = center - stringwidth(map, false, '12 0 0');
        drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
        pos_y += 26;
  
+       if( mapvote_chosenmap != "" )
+       {
+               pos_x = center - stringwidth(mapvote_chosenmap, false, hud_fontsize*1.5/2);
+               drawstring(pos, mapvote_chosenmap, hud_fontsize*1.5, '1 1 1', 1, DRAWFLAG_NORMAL);
+               pos_y += hud_fontsize_y*2;
+       }
        i = ceil(max(0, mv_timeout - time));
        map = sprintf(_("%d seconds left"), i);
        pos_x = center - stringwidth(map, false, '8 0 0');
        if(mv_abstain)
                mv_num_maps -= 1;
  
-       if(mv_num_maps > 3)
-       {
-               columns = 3;
-       } else {
-               columns = mv_num_maps;
-       }
-       rows = ceil(mv_num_maps / columns);
+       rows = ceil(mv_num_maps / mv_columns);
  
-       dist_x = (xmax - xmin) / columns;
+       dist_x = (xmax - xmin) / mv_columns;
        dist_y = (ymax - pos_y) / rows;
-       tsize = dist_x - 10;
-       isize = min(dist_y - 10, 0.75 * tsize);
  
-       mv_selection = MapVote_Selection(pos, dist, rows, columns);
+       if ( gametypevote )
+       {
+               tsize = dist_x - hud_fontsize_y;
+               isize = dist_y;
+               float maxheight = (ymax - pos_y) / 3;
+               if ( isize > maxheight )
+               {
+                       pos_x += (isize - maxheight)/2;
+                       isize = maxheight;
+               }
+               else
+                       dist_y += hud_fontsize_y;
+               pos_x = ( vid_conwidth - dist_x * mv_columns ) / 2;
+       }
+       else
+       {
+               tsize = dist_x - 10;
+               isize = min(dist_y - 10, 0.75 * tsize);
+       }
+       mv_selection = MapVote_Selection(pos, dist, rows, mv_columns);
  
-       pos_x += (xmax - xmin) / (2 * columns);
+       if ( !gametypevote )
+               pos_x += dist_x / 2;
        pos_y += (dist_y - isize) / 2;
        ymax -= isize;
  
        if (mv_top2_time)
                mv_top2_alpha = max(0.2, 1 - (time - mv_top2_time)*(time - mv_top2_time));
  
+       void (vector, float, float, string, string, float, float) DrawItem;
+       if(gametypevote)
+               DrawItem = GameTypeVote_DrawGameTypeItem;
+       else
+               DrawItem = MapVote_DrawMapItem;
        for(i = 0; i < mv_num_maps; ++i)
        {
                tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
                map = mv_maps[i];
                if(mv_preview[i])
-                       MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, mv_pics[i], tmp, i);
+                       DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, mv_pics[i], tmp, i);
                else
-                       MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, "", tmp, i);
+                       DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, "", tmp, i);
        }
  
        if(mv_abstain)
@@@ -328,12 -471,35 +470,35 @@@ void MapVote_CheckPic(string pic, strin
        MapVote_CheckPK3(pic, pk3, id);
  }
  
+ void MapVote_ReadMask()
+ {
+       float i;
+       if ( mv_num_maps < 24 )
+       {
+               float mask, power;
+               if(mv_num_maps < 8)
+                       mask = ReadByte();
+               else if(mv_num_maps < 16)
+                       mask = ReadShort();
+               else
+                       mask = ReadLong();
+               
+               for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+                       mv_avail[i] = (mask & power) ? GTV_AVAILABLE : GTV_FORBIDDEN;
+       }
+       else
+       {
+               for(i = 0; i < mv_num_maps; ++i )
+                       mv_avail[i] = ReadByte();
+       }
+ }
  #define NUM_SSDIRS 4
  string ssdirs[NUM_SSDIRS];
  float n_ssdirs;
  void MapVote_Init()
  {
-       float i, j, power;
+       float i, j;
        string map, pk3, s;
  
        precache_sound ("misc/invshot.wav");
        if(autocvar_hud_cursormode) { setcursormode(1); }
        else { mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; }
        mv_selection = -1;
+       mv_selection_keyboard = 0;
  
        for(n_ssdirs = 0; ; ++n_ssdirs)
        {
        mv_ownvote = -1;
        mv_timeout = ReadCoord();
  
-       if(mv_num_maps <= 8)
-               mv_maps_mask = ReadByte();
-       else
-               mv_maps_mask = ReadShort();
+       gametypevote = ReadByte();
+       
+       float mv_real_num_maps = mv_num_maps - mv_abstain;
+       if(gametypevote)
+       {
+               mapvote_chosenmap = strzone(ReadString());
+               if ( gametypevote == 2 )
+                       gametypevote = 0;
+               gtv_text_size = hud_fontsize*1.4;
+               gtv_text_size_small = hud_fontsize*1.1;
+               
+               if (mv_real_num_maps > 8 )
+                       mv_columns = 3;
+               else
+                       mv_columns = min(2, mv_real_num_maps);
+     }
+     else
+       {
+               if (mv_real_num_maps > 16)
+                       mv_columns = 5;
+               else if (mv_real_num_maps > 9)
+                       mv_columns = 4;
+               else if(mv_real_num_maps > 3)
+                       mv_columns = 3;
+               else
+                       mv_columns = mv_real_num_maps;
+       }
+       MapVote_ReadMask();
+       for(i = 0; i < mv_num_maps; ++i )
+               mv_avail_start[i] = mv_avail[i];
  
        // Assume mv_pk3list is world, there should only be 1 mapvote per round
        mv_pk3list = world; // I'm still paranoid!
  
-       for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+       for(i = 0; i < mv_num_maps; ++i)
        {
                mv_votes[i] = 0;
  
-               if(mv_maps_mask & power)
-               {
-                       map = strzone(ReadString());
-                       pk3 = strzone(ReadString());
-                       j = bound(0, ReadByte(), n_ssdirs - 1);
-                       mv_maps[i] = map;
-                       mv_pk3[i] = pk3;
-                       map = strzone(strcat(ssdirs[j], "/", map));
-                       mv_pics[i] = map;
+               map = strzone(ReadString());
+               pk3 = strzone(ReadString());
+               j = bound(0, ReadByte(), n_ssdirs - 1);
  
-                       mv_preview[i] = false;
+               mv_maps[i] = map;
+               mv_pk3[i] = pk3;
+               mv_avail[i] = ReadByte();
  
-                       MapVote_CheckPic(map, pk3, i);
+               if(gametypevote)
+               {
+                       //map = strzone(strcat("gfx/menu/default/gametype_", map));
+                       //map = strzone(sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map));
+                       string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map);
+                       if(precache_pic(mv_picpath) == "")
+                               mv_picpath = strcat("gfx/menu/default/gametype_", map);
+                       map = strzone(mv_picpath);
+                       mv_pics[i] = map;
+                       mv_preview[i] = PreviewExists(map);
                }
                else
                {
-                       mv_maps[i] = strzone("if-you-see-this-the-code-is-broken");
-                       mv_pk3[i] = strzone("if-you-see-this-the-code-is-broken");
-                       mv_pics[i] = strzone("if-you-see-this-the-code-is-broken");
+                       map = strzone(strcat(ssdirs[j], "/", map));
+                       mv_pics[i] = map;
                        mv_preview[i] = false;
+                       MapVote_CheckPic(map, pk3, i);
                }
        }
  
        n_ssdirs = 0;
  }
  
+ void MapVote_SendChoice(float index)
+ {
+       localcmd(strcat("\nimpulse ", ftos(index+1), "\n"));
+ }
+ float MapVote_MoveLeft(float pos)
+ {
+       float imp;
+       if ( pos < 0 ) 
+               imp = mv_num_maps - 1;
+       else
+               imp = pos < 1 ? mv_num_maps - 1 : pos - 1;
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveLeft(imp);
+       return imp;
+ }
+ float MapVote_MoveRight(float pos)
+ {
+       float imp;
+       if ( pos < 0 ) 
+               imp = 0;
+       else
+               imp = pos >= mv_num_maps - 1 ? 0 : pos + 1;
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveRight(imp);
+       return imp;
+ }
+ float MapVote_MoveUp(float pos)
+ {
+       float imp;
+       if ( pos < 0 ) 
+               imp = mv_num_maps - 1;
+       else
+       {
+               imp = pos - mv_columns;
+               if ( imp < 0 )
+               {
+                       imp = floor(mv_num_maps/mv_columns)*mv_columns + pos % mv_columns;
+                       if ( imp >= mv_num_maps )
+                               imp -= mv_columns;
+               }
+       }
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveUp(imp);
+       return imp;
+ }
+ float MapVote_MoveDown(float pos)
+ {
+       float imp;
+       if ( pos < 0 ) 
+               imp = 0;
+       else
+       {
+               imp = pos + mv_columns;
+               if ( imp >= mv_num_maps )
+                       imp = imp % mv_columns;
+       }
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveDown(imp);
+       return imp;
+ }
  float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
  {
        float imp;
        {
                mv_mousepos_x = nPrimary;
                mv_mousepos_y = nSecondary;
+               mv_selection_keyboard = 0;
                return true;
        }
  
                case K_KP_8: localcmd("\nimpulse 8\n"); return true;
                case K_KP_9: localcmd("\nimpulse 9\n"); return true;
                case K_KP_0: localcmd("\nimpulse 10\n"); return true;
+               case K_RIGHTARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveRight(mv_selection);
+                       return true;
+               case K_LEFTARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveLeft(mv_selection);
+                       return true;
+               case K_DOWNARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveDown(mv_selection);
+                       return true;
+               case K_UPARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveUp(mv_selection);
+                       return true;
+               case K_KP_ENTER:
+               case K_ENTER:
+               case K_SPACE:
+                       if ( mv_selection_keyboard )
+                               MapVote_SendChoice(mv_selection);
+                       return true;
        }
  
        if (nPrimary == K_MOUSE1)
+       {
+               mv_selection_keyboard = 0;
+               mv_selection = mv_mouse_selection;
                if (mv_selection >= 0)
                {
                        imp = min(mv_selection + 1, mv_num_maps);
                        localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
                        return true;
                }
+       }
  
        return false;
  }
  
  void MapVote_UpdateMask()
  {
-       float i, power;
-       float oldmask;
-       oldmask = mv_maps_mask;
-       if(mv_num_maps <= 8)
-               mv_maps_mask = ReadByte();
-       else
-               mv_maps_mask = ReadShort();
-       if((oldmask & mv_maps_mask) != oldmask)
-               if((oldmask & mv_maps_mask) == mv_maps_mask)
-                        sound(world, CH_INFO, "misc_invshot.wav", VOL_BASE, ATTEN_NONE);
-       // remove votes that no longer apply
-       for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
-               if (!(mv_maps_mask & power))
-                       mv_votes[i] = -1;
+       MapVote_ReadMask();
        mv_top2_time = time;
  }
  
  void MapVote_UpdateVotes()
  {
-       float i, power;
-       for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+       float i;
+       for(i = 0; i < mv_num_maps; ++i)
        {
-               if(mv_maps_mask & power)
+               if(mv_avail[i] == GTV_AVAILABLE)
                {
                        if(mv_detail)
                                mv_votes[i] = ReadByte();
diff --combined qcsrc/client/progs.src
index 82af9b202a7cce089e38a95231d5cfeade8d269d,c21726b4dcc655433bbe3d4f80365b0e3dc6e94f..3efcb16f341473cb2e9fe887000caf021135aee8
@@@ -8,18 -8,21 +8,21 @@@ sys-post.q
  Defs.qc
  ../dpdefs/keycodes.qc
  ../common/constants.qh
+ ../common/stats.qh
  
  ../warpzonelib/anglestransform.qh
  ../warpzonelib/mathlib.qh
  ../warpzonelib/common.qh
  ../warpzonelib/client.qh
  
+ ../common/playerstats.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
@@@ -50,19 -53,18 +53,20 @@@ noise.q
  tturrets.qh
  ../server/tturrets/include/turrets_early.qh
  ../server/movelib.qc
 +generator.qh
 +controlpoint.qh
  main.qh
  vehicles/vehicles.qh
  ../common/csqcmodel_settings.qh
  ../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
@@@ -77,7 -79,7 +81,7 @@@ rubble.q
  hook.qc
  particles.qc
  laser.qc
- projectile.qc
+ weapons/projectile.qc // TODO
  gibs.qc
  damage.qc
  casings.qc
@@@ -106,27 -108,25 +110,29 @@@ noise.q
  
  ../common/test.qc
  ../common/util.qc
+ ../common/playerstats.qc
  ../common/notifications.qc
  ../common/command/markup.qc
  ../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
  ../warpzonelib/client.qc
 +
 +generator.qc
 +controlpoint.qc
 +
  tturrets.qc
  
  player_skeleton.qc
index 7ab8576ba99ab04d8037a52bb57d3d1d2df47609,f5b3206ba3bbbb6bc259df0d920675a01a96d7a3..c8609dc321349e8cdb1aea6fa085d6b6d361f842
@@@ -287,19 -287,15 +287,15 @@@ void Cmd_HUD_Help(
                "other gamemodes except DM.\n"));
  }
  
- string HUD_DefaultColumnLayout()
- {
-       return strcat( // fteqcc sucks
-               "ping pl name | ",
-               "-teams,race,lms/kills +freezetag/kills -teams,lms/deaths +freezetag/deaths -teams,lms,race,ka/suicides +freezetag/suicides -race,dm,tdm,ka,freezetag/frags ", // tdm already has this in "score"
-               "+tdm/kills +tdm/deaths +tdm/suicides ",
-               "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes ",
-               "+lms/lives +lms/rank ",
-               "+kh/caps +kh/pushes +kh/destroyed ",
-               "?+race/laps ?+race/time ?+race/fastest ",
-               "+as/objectives +nexball/faults +nexball/goals +ka/pickups +ka/bckills +ka/bctime +freezetag/revivals ",
-               "-lms,race,nexball/score");
- }
+ #define HUD_DefaultColumnLayout() \
+ "ping pl name | " \
+ "-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
 -"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \
++"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
+ "+lms/lives +lms/rank " \
+ "+kh/caps +kh/pushes +kh/destroyed " \
+ "?+race/laps ?+race/time ?+race/fastest " \
+ "+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
+ "-lms,race,nb/score"
  
  void Cmd_HUD_SetFields(float argc)
  {
        float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
        float missing;
  
+       if(!gametype)
+       {
+               // set up a temporary scoreboard layout
+               // no layout can be properly set up until score_info data haven't been received
+               argc = tokenizebyseparator("0 1 ping pl name | score", " ");
+               ps_primary = 0;
+               scores_label[ps_primary] = strzone("score");
+               scores_flags[ps_primary] = SFL_ALLOW_HIDE;
+       }
        // TODO: re enable with gametype dependant cvars?
        if(argc < 3) // no arguments provided
                argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
                        hud_field[hud_num_fields] = SP_PL;
                } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
                        hud_field[hud_num_fields] = SP_KDRATIO;
-               } else if(str == "sum" || str == "diff" || str == "f-d") {
+               } else if(str == "sum" || str == "diff" || str == "k-d") {
                        hud_field[hud_num_fields] = SP_SUM;
                } else if(str == "name" || str == "nick") {
                        hud_field[hud_num_fields] = SP_NAME;
@@@ -513,7 -519,7 +519,7 @@@ string HUD_GetField(entity pl, float fi
        {
                case SP_PING:
                        if (!pl.gotscores)
-                               return "\xEE\x82\x8D\xEE\x82\x8D\xEE\x82\x8D"; // >>> sign
+                               return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
                        //str = getplayerkeyvalue(pl.sv_entnum, "ping");
                        f = pl.ping;
                        if(f == 0)
                                        hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
                                        hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
                                        hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
-                                       hud_field_icon2_rgb = colormapPaletteColor(mod(f, 16), 1);
+                                       hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
                                }
                        }
                        return GetPlayerName(pl.sv_entnum);
@@@ -670,7 -676,7 +676,7 @@@ string HUD_FixScoreboardColumnWidth(flo
        return str;
  }
  
- void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
+ void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, float pl_number)
  {
        vector tmp, rgb;
        rgb = Team_ColorRGB(pl.team);
                rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
                rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
  
-       // Layout:
-       tmp_x = sbwidth;
-       tmp_y = hud_fontsize_y * 1.25;
-       tmp_z = 0;
+       vector h_pos = pos - '1 1 0';
+       vector h_size = item_size + '2 0 0';
        // alternated rows highlighting
        if(is_self)
-               drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
-       else if((scoreboard_highlight) && (!mod(pl_number,2)))
-               drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
+               drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
+       else if((scoreboard_highlight) && (!(pl_number % 2)))
+               drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
  
+       tmp_x = item_size_x;
        tmp_y = 0;
+       tmp_z = 0;
  
        for(i = 0; i < hud_num_fields; ++i)
        {
                        pos_x -= hud_size[i] + hud_fontsize_x;
                }
        }
+       if(pl.eliminated)
+               drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
  }
  
  /*
@@@ -866,7 -874,7 +874,7 @@@ vector HUD_Scoreboard_MakeTable(vector 
                column_dim_x = hud_size[i] + hud_fontsize_x;
                if (scoreboard_highlight)
                {
-                       if (mod(i,2))
+                       if (i % 2)
                                drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
                }
                drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
  
                        if (scoreboard_highlight)
                        {
-                               if (!mod(i,2))
+                               if (!(i % 2))
                                {
                                        if (i == hud_num_fields-1)
                                                column_dim_x = hud_size[i] + hud_fontsize_x / 2 + 1;
        pos_y += 1.25 * hud_fontsize_y; // skip the header
        pos_y += autocvar_scoreboard_border_thickness;
  
+       // item size
+       tmp_x = sbwidth;
+       tmp_y = hud_fontsize_y * 1.25;
        // fill the table and draw the rows
        i = 0;
        if (teamplay)
                {
                        if(pl.team != tm.team)
                                continue;
-                       HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
+                       HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
                        pos_y += 1.25 * hud_fontsize_y;
                        ++i;
                }
                {
                        if(pl.team == NUM_SPECTATOR)
                                continue;
-                       HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
+                       HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
                        pos_y += 1.25 * hud_fontsize_y;
                        ++i;
                }
  float HUD_WouldDrawScoreboard() {
        if (autocvar__hud_configure)
                return 0;
 +      else if (HUD_Radar_Clickable())
 +              return 0;
        else if (scoreboard_showscores)
                return 1;
        else if (intermission == 1)
@@@ -957,7 -967,7 +969,7 @@@ float average_accuracy
  vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
  {
        float i;
-       float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
+       float weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba
        float rows;
        if(autocvar_scoreboard_accuracy_doublerows)
                rows = 2;
        float fontsize = height * 1/3;
        float weapon_height = height * 2/3;
        float weapon_width = sbwidth / weapon_cnt;
-       float g_minstagib = 0;
+       float g_instagib = 0;
  
        drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
        pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
        // column highlighting
        for(i = 0; i < weapon_cnt/rows; ++i)
        {
-               if(!mod(i, 2))
+               if(!(i % 2))
                        drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
        }
  
        if(rows == 2)
                pos_x += weapon_width / 2;
  
-       if(switchweapon == WEP_MINSTANEX)
-               g_minstagib = 1; // TODO: real detection for minstagib?
+       if(switchweapon == WEP_VAPORIZER)
+               g_instagib = 1; // TODO: real detection for instagib?
  
        float weapon_stats;
        if(autocvar_scoreboard_accuracy_nocolors)
                self = get_weaponinfo(i);
                if (!self.weapon)
                        continue;
-               if ((i == WEP_NEX && g_minstagib) || i == WEP_PORTO || (i == WEP_MINSTANEX && !g_minstagib) || i == WEP_TUBA) // skip port-o-launch, nex || minstanex and tuba
+               if ((i == WEP_VORTEX && g_instagib) || i == WEP_PORTO || (i == WEP_VAPORIZER && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba
                        continue;
                weapon_stats = weapon_accuracy[i-WEP_FIRST];
  
                        weapon_alpha = 0.2 * scoreboard_alpha_fg;
  
                // weapon icon
-               drawpic_aspect_skin(pos, strcat("weapon", self.netname), '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
+               drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
                // the accuracy
                if(weapon_stats >= 0) {
                        weapons_with_stats += 1;
                        average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
  
                        string s;
-                       s = sprintf(_("%d%%"), weapon_stats*100);
+                       s = sprintf("%d%%", weapon_stats*100);
  
                        float padding;
                        padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
@@@ -1078,7 -1088,7 +1090,7 @@@ vector HUD_DrawMapStats(vector pos, vec
        float stat_monsters_killed, stat_monsters_total;
        float rows = 0;
        string val;
-       
        // get monster stats
        stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
        stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
@@@ -1175,7 -1185,7 +1187,7 @@@ vector HUD_DrawScoreboardRankings(vecto
                p = count_ordinal(i+1);
                if(grecordholder[i] == GetPlayerName(player_localnum))
                        drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
-               else if(!mod(i, 2) && scoreboard_highlight)
+               else if(!(i % 2) && scoreboard_highlight)
                        drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
                drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
                drawstring(pos + '3 0 0' * hud_fontsize_y, TIME_ENCODED_TOSTRING(t), '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
@@@ -1332,12 -1342,16 +1344,16 @@@ void HUD_DrawScoreboard(
        float specs;
        specs = 0;
        tmp = pos;
+       vector item_size;
+       item_size_x = sbwidth;
+       item_size_y = hud_fontsize_y * 1.25;
+       item_size_z = 0;
        for(pl = players.sort_next; pl; pl = pl.sort_next)
        {
                if(pl.team != NUM_SPECTATOR)
                        continue;
                pos_y += 1.25 * hud_fontsize_y;
-               HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
+               HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
                ++specs;
        }
  
index 46428f3300e61902ac93cc616cd61fc8f6a2e07c,2df3dd411bc6d9f4da76ef4124a81fa51658c8fc..5ba6ad850b64bbb4f7e0de242044e8bc5f4f0c64
@@@ -33,6 -33,7 +33,6 @@@ float waypointsprite_alpha
  .float maxdistance;
  .float hideflags;
  .float spawntime;
 -.float health;
  .float build_started;
  .float build_starthealth;
  .float build_finished;
@@@ -198,8 -199,11 +198,8 @@@ float spritelookupblinkvalue(string s
  {
        switch(s)
        {
 -              case "ons-cp-atck-neut": return 2;
 -              case "ons-cp-atck-red":  return 2;
 -              case "ons-cp-atck-blue": return 2;
 -              case "ons-cp-dfnd-red":  return 0.5;
 -              case "ons-cp-dfnd-blue": return 0.5;
 +              case "ons-cp-atck":      return 2;
 +              case "ons-cp-dfnd":      return 0.5;
                case "item-invis":       return 2;
                case "item-extralife":   return 2;
                case "item-speed":       return 2;
  }
  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, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
        switch(s)
        {
                case "as-push": return _("Push");
                case "keycarrier-yellow": return _("Key carrier");
                case "redbase": return _("Red base");
                case "waypoint": return _("Waypoint");
 -              case "ons-gen-red": return _("Generator");
 -              case "ons-gen-blue": return _("Generator");
 +              case "ons-gen": return _("Generator");
                case "ons-gen-shielded": return _("Generator");
 -              case "ons-cp-neut": return _("Control point");
 -              case "ons-cp-red": return _("Control point");
 -              case "ons-cp-blue": return _("Control point");
 -              case "ons-cp-atck-neut": return _("Control point");
 -              case "ons-cp-atck-red": return _("Control point");
 -              case "ons-cp-atck-blue": return _("Control point");
 -              case "ons-cp-dfnd-red": return _("Control point");
 -              case "ons-cp-dfnd-blue": return _("Control point");
 +              case "ons-cp": return _("Control point");
 +              case "ons-cp-atck": return _("Control point");
 +              case "ons-cp-dfnd": return _("Control point");
                case "race-checkpoint": return _("Checkpoint");
                case "race-finish": return _("Finish");
                case "race-start": return _("Start");
                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;
@@@ -717,7 -701,7 +691,7 @@@ void Ent_WaypointSprite(
                if(f & 0x80)
                {
                        self.(teamradar_times[self.teamradar_time_index]) = time;
-                       self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
+                       self.teamradar_time_index = (self.teamradar_time_index + 1) % MAX_TEAMRADAR_TIMES;
                }
                self.teamradar_color_x = ReadByte() / 255.0;
                self.teamradar_color_y = ReadByte() / 255.0;
index 73662b617cd0f0534f2094c989b503fd4969b7bd,8586cffa9d539ff5f56067ab570df0e132f43ce6..e93f4b40043926802640ee4be6813e8319702e33
@@@ -30,17 -30,18 +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;
@@@ -85,8 -86,7 +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;
@@@ -96,13 -96,13 +96,15 @@@ const float ENT_CLIENT_BUMBLE_RAYGUN = 
  const float ENT_CLIENT_SPAWNPOINT = 36;
  const float ENT_CLIENT_SPAWNEVENT = 37;
  const float ENT_CLIENT_NOTIFICATION = 38;
+ const float ENT_CLIENT_ELIMINATEDPLAYERS = 39;
  const float ENT_CLIENT_TURRET = 40;
  const float ENT_CLIENT_AUXILIARYXHAIR = 50;
  const float ENT_CLIENT_VEHICLE = 60;
 +const float ENT_CLIENT_GENERATOR = 70;
 +const float ENT_CLIENT_CONTROLPOINT_ICON = 71;
  
+ const float ENT_CLIENT_HEALING_ORB = 80;
  const float SPRITERULE_DEFAULT = 0;
  const float SPRITERULE_TEAMPLAY = 1;
  
@@@ -141,80 -141,6 +143,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_NEX_CHARGE = 50;
- const float STAT_LAST_PICKUP = 51;
- const float STAT_HUD = 52;
- const float STAT_NEX_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_ROUNDLOST = 96;
- // 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;
@@@ -234,7 -160,7 +162,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;
@@@ -315,8 -224,8 +226,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;
@@@ -329,7 -238,7 +240,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;
@@@ -338,7 -247,7 +249,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;
@@@ -364,16 -273,7 +275,7 @@@ 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 PROJECTILE_RPC = 60;
  
  const float SPECIES_HUMAN = 0;
  const float SPECIES_ROBOT_SOLID = 1;
@@@ -394,25 -294,6 +296,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
@@@ -459,3 -340,8 +342,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
diff --combined qcsrc/common/mapinfo.qh
index 656b80d5fb582fa4d771b0f94e679aede4bd6fe3,bbcc267b2bdbf5d05005155768419c357516e84c..16d9158834607645822011be5c54f3731d3968f6
@@@ -7,9 -7,11 +7,11 @@@ entity MapInfo_Type_last
  .string netname; // game type name as in cvar (with g_ prefix)
  .string mdl; // game type short name
  .string message; // human readable name
+ .float team; // does this gametype support teamplay?
  .string model2; // game type defaults
+ .string gametype_description; // game type description
  
- #define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults) \
+ #define REGISTER_GAMETYPE(hname,sname,g_name,NAME,gteamplay,defaults,gdescription) \
        var float MAPINFO_TYPE_##NAME; \
        var entity MapInfo_Type##g_name; \
        void RegisterGametypes_##g_name() \
@@@ -21,7 -23,9 +23,9 @@@
                MapInfo_Type##g_name.netname = #g_name; \
                MapInfo_Type##g_name.mdl = #sname; \
                MapInfo_Type##g_name.message = hname; \
+               MapInfo_Type##g_name.team = gteamplay; \
                MapInfo_Type##g_name.model2 = defaults; \
+               MapInfo_Type##g_name.gametype_description = gdescription; \
                if(!MapInfo_Type_first) \
                        MapInfo_Type_first = MapInfo_Type##g_name; \
                if(MapInfo_Type_last) \
  #define IS_GAMETYPE(NAME) \
        (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
  
- REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0");
+ REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,FALSE,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies"));
  #define g_dm IS_GAMETYPE(DEATHMATCH)
  
- REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0");
+ REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,FALSE,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
  #define g_lms IS_GAMETYPE(LMS)
  
- REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0");
+ REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,FALSE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"));
  #define g_race IS_GAMETYPE(RACE)
  
- REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1");
+ REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,FALSE,"timelimit=20 skill=-1",_("Race for fastest time"));
  #define g_cts IS_GAMETYPE(CTS)
  
- REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0");
+ REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,TRUE,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates"));
  #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
  
- REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0");
+ REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,TRUE,"timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it"));
  #define g_ctf IS_GAMETYPE(CTF)
  
- REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0");
+ REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,TRUE,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round"));
  #define g_ca IS_GAMETYPE(CA)
  
- REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0");
+ REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,TRUE,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win"));
  #define g_domination IS_GAMETYPE(DOMINATION)
  
- REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0");
+ REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,TRUE,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"));
  #define g_keyhunt IS_GAMETYPE(KEYHUNT)
  
- REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20");
+ REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,TRUE,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
  #define g_assault IS_GAMETYPE(ASSAULT)
  
- REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"pointlimit=1 timelimit=30");
 -REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,TRUE,"timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
++REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,TRUE,"pointlimit = 1 timelimit=30",_("Capture control points to reach and destroy the enemy generator"));
  #define g_onslaught IS_GAMETYPE(ONSLAUGHT)
  
- REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0");
+ REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,TRUE,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports"));
  #define g_nexball IS_GAMETYPE(NEXBALL)
  
- REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
+ REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,TRUE,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them"));
  #define g_freezetag IS_GAMETYPE(FREEZETAG)
  
- REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
+ REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,TRUE,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
  #define g_keepaway IS_GAMETYPE(KEEPAWAY)
  
- REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0");
+ REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,FALSE,"pointlimit=50 teams=0",_("Survive against waves of monsters"));
  #define g_invasion IS_GAMETYPE(INVASION)
  
- const float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps
+ const float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for instagib-only maps
  const float MAPINFO_FEATURE_VEHICLES      = 2;
  const float MAPINFO_FEATURE_TURRETS       = 4;
  const float MAPINFO_FEATURE_MONSTERS      = 8;
@@@ -134,13 -138,15 +138,15 @@@ float MapInfo_CheckMap(string s); // re
  void MapInfo_LoadMap(string s, float reinit);
  
  // list all maps for the current game type
- string MapInfo_ListAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
+ string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden);
  // list all allowed maps (for any game type)
  string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
  
  // gets a gametype from a string
  string _MapInfo_GetDefaultEx(float t);
+ float _MapInfo_GetTeamPlayBool(float t);
  float MapInfo_Type_FromString(string t);
+ string MapInfo_Type_Description(float t);
  string MapInfo_Type_ToString(float t);
  string MapInfo_Type_ToText(float t);
  void MapInfo_SwitchGameType(float t);
index 3d212d6354138b7777a9d396b49487b5d660d960,0bfd2e5f3075f60746f5160b137ece590c4f43cc..7753288c0c2b582827bdc87c527382060d0eea4f
@@@ -248,9 -248,9 +248,9 @@@ void Send_Notification_WOCOVA
      MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_20,             CH_INFO, "20kills",           VOL_BASEVOICE, ATTEN_NONE) \
      MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_25,             CH_INFO, "25kills",           VOL_BASEVOICE, ATTEN_NONE) \
      MSG_ANNCE_NOTIF(1, ANNCE_KILLSTREAK_30,             CH_INFO, "30kills",           VOL_BASEVOICE, ATTEN_NONE) \
-     MSG_ANNCE_NOTIF(1, ANNCE_MINSTAGIB_LASTSECOND,      CH_INFO, "lastsecond",        VOL_BASEVOICE, ATTEN_NONE) \
-     MSG_ANNCE_NOTIF(1, ANNCE_MINSTAGIB_NARROWLY,        CH_INFO, "narrowly",          VOL_BASEVOICE, ATTEN_NONE) \
-     MSG_ANNCE_NOTIF(1, ANNCE_MINSTAGIB_TERMINATED,      CH_INFO, "terminated",        VOL_BASEVOICE, ATTEN_NONE) \
+     MSG_ANNCE_NOTIF(1, ANNCE_INSTAGIB_LASTSECOND,       CH_INFO, "lastsecond",        VOL_BASEVOICE, ATTEN_NONE) \
+     MSG_ANNCE_NOTIF(1, ANNCE_INSTAGIB_NARROWLY,         CH_INFO, "narrowly",          VOL_BASEVOICE, ATTEN_NONE) \
+     MSG_ANNCE_NOTIF(1, ANNCE_INSTAGIB_TERMINATED,       CH_INFO, "terminated",        VOL_BASEVOICE, ATTEN_NONE) \
      MSG_ANNCE_NOTIF(0, ANNCE_MULTIFRAG,                 CH_INFO, "multifrag",         VOL_BASEVOICE, ATTEN_NONE) \
      MSG_ANNCE_NOTIF(2, ANNCE_NUM_1,                     CH_INFO, "1",                 VOL_BASEVOICE, ATTEN_NONE) \
      MSG_ANNCE_NOTIF(2, ANNCE_NUM_2,                     CH_INFO, "2",                 VOL_BASEVOICE, ATTEN_NONE) \
  
  #define MSG_INFO_NOTIFICATIONS \
      MSG_INFO_NOTIF(2, INFO_CHAT_NOSPECTATORS,              0, 0, "", "",                            "",                     _("^F4NOTE: ^BGSpectator chat is not sent to players during the match"), "") \
+     MSG_INFO_NOTIF(2, INFO_COINTOSS,                       1, 0, "s1", "",                          "",                     _("^F2Throwing coin... Result: %s^F2!"), "") \
+     MSG_INFO_NOTIF(1, INFO_JETPACK_NOFUEL,                 0, 0, "", "",                            "",                     _("^BGYou don't have any fuel for the ^F1Jetpack"), "") \
+     MSG_INFO_NOTIF(2, INFO_SUPERSPEC_MISSING_UID,          0, 0, "", "",                            "",                     _("^F2You lack a UID, superspec options will not be saved/restored"), "") \
+     MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE,                   0, 0, "", "",                            "",                     _("^F1Round already started, you will join the game in the next round"), "") \
+     MSG_INFO_NOTIF(1, INFO_CA_LEAVE,                       0, 0, "", "",                            "",                     _("^F2You will spectate in the next round"), "") \
      MULTITEAM_INFO(1, INFO_CTF_CAPTURE_, 2,                1, 0, "s1", "s1",                        "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag"), "") \
      MULTITEAM_INFO(1, INFO_CTF_CAPTURE_BROKEN_, 2,         2, 2, "s1 f1p2dec s2 f2p2dec", "s1",     "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG%s^BG's previous record of ^F2%s^BG seconds"), "") \
      MULTITEAM_INFO(1, INFO_CTF_CAPTURE_TIME_, 2,           1, 1, "s1 f1p2dec", "s1",                "notify_%s_captured",   _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds"), "") \
      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_CAMP,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_camping",       _("^BG%s^K1 thought they found a nice camping ground%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CHEAT,               2, 1, "s1 s2loc spree_lost", "s1",       "notify_selfkill",      _("^BG%s^K1 unfairly eliminated themself%s%s"), "") \
-     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CUSTOM,              3, 1, "s1 s2 s3loc spree_lost", "s1",    "notify_void",          _("^BG%s^K1 %s^K1%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CUSTOM,              3, 1, "s1 s2 s3loc spree_lost", "s1",    "notify_void",          "^BG%s^K1 %s^K1%s%s", "") \
      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_DROWN,               2, 1, "s1 s2loc spree_lost", "s1",       "notify_water",         _("^BG%s^K1 couldn't catch their breath%s%s"), _("^BG%s^K1 was in the water for too long%s%s")) \
      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FALL,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_fall",          _("^BG%s^K1 hit the ground with a crunch%s%s"), _("^BG%s^K1 hit the ground with a bit too much force%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_LMS_FORFEIT,                    1, 0, "s1", "",                          "",                     _("^BG%s^F3 forfeited"), "") \
      MSG_INFO_NOTIF(1, INFO_LMS_NOLIVES,                    1, 0, "s1", "",                          "",                     _("^BG%s^F3 has no more lives left"), "") \
      MSG_INFO_NOTIF(1, INFO_MONSTERS_DISABLED,              0, 0, "", "",                            "",                     _("^BGMonsters are currently disabled"), "") \
 +    MSG_INFO_NOTIF(1, INFO_ONSLAUGHT_CAPTURE,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^BG captured %s^BG control point"), "") \
 +    MULTITEAM_INFO(1, INFO_ONSLAUGHT_CPDESTROYED_, 4,      2, 0, "s1 s2", "",                       "",                     _("^TC^TT^BG team %s^BG control point has been destroyed by %s"), "") \
 +    MULTITEAM_INFO(1, INFO_ONSLAUGHT_GENDESTROYED_, 4,     0, 0, "", "",                            "",                     _("^TC^TT^BG generator has been destroyed"), "") \
 +    MULTITEAM_INFO(1, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_, 4,  0, 0, "", "",                      "",                     _("^TC^TT^BG generator spontaneously combusted due to overtime!"), "") \
      MSG_INFO_NOTIF(1, INFO_POWERUP_INVISIBILITY,           1, 0, "s1", "s1",                        "strength",             _("^BG%s^K1 picked up Invisibility"), "") \
      MSG_INFO_NOTIF(1, INFO_POWERUP_SHIELD,                 1, 0, "s1", "s1",                        "shield",               _("^BG%s^K1 picked up Shield"), "") \
      MSG_INFO_NOTIF(1, INFO_POWERUP_SPEED,                  1, 0, "s1", "s1",                        "shield",               _("^BG%s^K1 picked up Speed"), "") \
      MSG_INFO_NOTIF(1, INFO_RACE_NEW_SET,                   1, 2, "s1 race_col f1ord race_col f2race_time", "s1 f2race_time",                  "race_newrecordserver",  _("^BG%s^BG set the %s%s^BG place record with %s%s"), "") \
      MULTITEAM_INFO(1, INFO_SCORES_, 4,                     0, 0, "", "",                            "",                     _("^TC^TT ^BGteam scores!"), "") \
      MSG_INFO_NOTIF(1, INFO_SPECTATE_WARNING,               0, 1, "f1secs", "",                      "",                     _("^F2You have to become a player within the next %s, otherwise you will be kicked, because spectating isn't allowed at this time!"), "") \
-     MSG_INFO_NOTIF(1, INFO_SUPERWEAPON_PICKUP,             1, 0, "s1", "s1",                        "strength",             _("^BG%s^K1 picked up a Superweapon"), "") \
+     MSG_INFO_NOTIF(1, INFO_SUPERWEAPON_PICKUP,             1, 0, "s1", "s1",                        "superweapons",         _("^BG%s^K1 picked up a Superweapon"), "") \
+     MSG_INFO_NOTIF(1, INFO_TEAMCHANGE_LARGERTEAM,          0, 0, "", "",                            "",                     _("^BGYou cannot change to a larger team"), "") \
+     MSG_INFO_NOTIF(1, INFO_TEAMCHANGE_NOTALLOWED,          0, 0, "", "",                            "",                     _("^BGYou are not allowed to change teams"), "") \
      MSG_INFO_NOTIF(2, INFO_VERSION_BETA,                   2, 0, "s1 s2", "",                       "",                     _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s"), "") \
      MSG_INFO_NOTIF(2, INFO_VERSION_OLD,                    2, 0, "s1 s2", "",                       "",                     _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s"), "") \
      MSG_INFO_NOTIF(2, INFO_VERSION_OUTDATED,               2, 0, "s1 s2", "",                       "",                     _("^F4NOTE: ^F1Xonotic %s^BG is out, and you still have ^F2Xonotic %s^BG - get the update from ^F3http://www.xonotic.org/^BG!"), "") \
      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",  "weaponarc",              _("^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_HAGAR_SUICIDE,                 2, 1, "s1 s2loc spree_lost", "s1",                 "weaponhagar",            _("^BG%s^K1 played with tiny Hagar rockets%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_HLAC_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhlac",             _("^BG%s%s^K1 was cut down with ^BG%s^K1's HLAC%s%s"), "") \
      MSG_INFO_NOTIF(1, INFO_WEAPON_HLAC_SUICIDE,                  2, 1, "s1 s2loc spree_lost", "s1",                 "weaponhlac",             _("^BG%s^K1 got a little jumpy with their HLAC%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_HMG_MURDER_SNIPE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhmg",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Heavy Machine Gun%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_HMG_MURDER_SPRAY,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponhmg",              _("^BG%s%s^K1 was torn to bits by ^BG%s^K1's Heavy Machine 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_LIMIT,               0, 1, "f1", "",                                    "",                       _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") \
      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_RPC_MURDER_DIRECT,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrpc",              _("^BG%s%s^K1 was sawn in half by ^BG%s^K1's Rocket Propelled Chainsaw%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_RPC_MURDER_SPLASH,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponrpc",              _("^BG%s%s^K1 almost dodged ^BG%s^K1's Rocket Propelled Chainsaw%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_RPC_SUICIDE_DIRECT,            2, 1, "s1 s2loc spree_lost", "s1",                 "weaponrpc",              _("^BG%s^K1 was sawn in half by their own Rocket Propelled Chainsaw%s%s"), "") \
+     MSG_INFO_NOTIF(1, INFO_WEAPON_RPC_SUICIDE_SPLASH,            2, 1, "s1 s2loc spree_lost", "s1",                 "weaponrpc",              _("^BG%s^K1 blew themself up with their Rocket Propelled Chainsaw%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))) \
      MULTITEAM_CENTER##teams(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle)
  
  #define MSG_CENTER_NOTIFICATIONS \
+     MSG_CENTER_NOTIF(1, CENTER_ALONE,                       0, 0, "",             NO_CPID,             "0 0", _("^F4You are now alone!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are attacking!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING,           0, 0, "",             CPID_ASSAULT_ROLE,   "0 0", _("^BGYou are defending!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN,             0, 0, "",             CPID_ROUND,          "2 0", _("^F4Begin!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_ROUND_TIED,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound tied"), "") \
      MSG_CENTER_NOTIF(1, CENTER_ROUND_OVER,                  0, 0, "",             CPID_ROUND,          "0 0", _("^BGRound over, there's no winner"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CAMPCHECK,                   0, 0, "",             CPID_CAMPCHECK,      "0 0", _("^F2Don't camp!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_COINTOSS,                    1, 0, "s1",           NO_CPID,             "0 0", _("^F2Throwing coin... Result: %s^F2!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_FREE,      0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
      MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED,  0, 0, "",             CPID_CTF_CAPSHIELD,  "0 0", _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
      MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 2,             0, 0, "",             CPID_CTF_LOWPRIO,    "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
      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_DEATH_TEAMKILL_FRAG,         1, 0, "s1",           NO_CPID,             "0 0", _("^K1Moron! You fragged ^BG%s^K1, a team mate!"), _("^K1Moron! You went against ^BG%s^K1, a team mate!")) \
      MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAGGED,      1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were fragged by ^BG%s^K1, a team mate"), _("^K1You were scored against by ^BG%s^K1, a team mate")) \
      MSG_CENTER_NOTIF(1, CENTER_DISCONNECT_IDLING,           0, 1, "",             CPID_IDLING,         "1 f1", _("^K1Stop idling!\n^BGDisconnecting in ^COUNT..."), "") \
+     MSG_CENTER_NOTIF(1, CENTER_DOOR_LOCKED_NEED,            0, 0, "",             NO_CPID,             "0 0", _("^BGYou need %s^BG!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_DOOR_LOCKED_ALSONEED,        0, 0, "",             NO_CPID,             "0 0", _("^BGYou also need %s^BG!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_DOOR_UNLOCKED,               0, 0, "",             NO_CPID,             "0 0", _("^BGDoor unlocked!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_EXTRALIVES,                  0, 0, "",             NO_CPID,             "0 0", _("^F2You picked up some extra lives"), "") \
      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)"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_GENERATOR_UNDERATTACK,       0, 0, "",             NO_CPID,             "0 0", _("^BGThe generator is under attack!"), "") \
      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_ROUND_PLAYER_WIN,            1, 0, "s1",           CPID_ROUND,          "0 0", _("^BG%s^BG 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_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART,          0, 1, "",              CPID_KEYHUNT_OTHER,    "1 f1", _("^F4Round will start in ^COUNT"), "") \
      MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN,                0, 1, "",              CPID_KEYHUNT_OTHER,    "f1 0", _("^BGScanning frequency range..."), "") \
      MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4,           0, 0, "",              CPID_KEYHUNT,          "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 4, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 4, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT,                0, 1, "missing_teams", CPID_KEYHUNT_OTHER,    "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_LMS_NOLIVES,                 0, 0, "",              CPID_LMS,              "0 0", _("^BGYou have no lives left, you must wait until the next match"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS,               0, 1, "missing_teams", CPID_MISSING_TEAMS,    "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS,             0, 1, "f1",            CPID_MISSING_PLAYERS,  "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO,             0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO_FIRST,       0, 0, "",              CPID_MINSTA_FINDAMMO,  "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_LIVES_REMAINING,      0, 1, "f1",            NO_CPID,               "0 0", _("^F2Extra lives remaining: ^K1%s"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MINSTA_SECONDARY,            0, 0, "",              NO_CPID,               "0 0", _("^BGSecondary fire inflicts no damage!"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_MOTD,                        1, 0, "s1",            CPID_MOTD,             "-1 0", _("^BG%s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO,           0, 0, "",              CPID_INSTAGIB_FINDAMMO,"1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO_FIRST,     0, 0, "",              CPID_INSTAGIB_FINDAMMO,"1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
+     MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_LIVES_REMAINING,    0, 1, "f1",            NO_CPID,               "0 0", _("^F2Extra lives remaining: ^K1%s"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_MOTD,                        1, 0, "s1",            CPID_MOTD,             "-1 0", "^BG%s", "") \
      MSG_CENTER_NOTIF(1, CENTER_NIX_COUNTDOWN,               0, 2, "item_wepname",  CPID_NIX,              "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_NIX_NEWWEAPON,               0, 1, "item_wepname",  CPID_NIX,              "0 0", _("^F2Active weapon: ^F1%s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_NADE,                        0, 0, "",              NO_CPID,               "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the grenade!"), "") \
 -    MSG_CENTER_NOTIF(1, CENTER_ONS_NOTSHIELDED,             0, 0, "",              CPID_ONSLAUGHT,        "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture controlpoints to shield it!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_CAPTURE,                 1, 0, "s1",            CPID_ONSLAUGHT,        "0 0", _("^BGYou captured %s^BG control point"), "") \
 +    MULTITEAM_CENTER(1, CENTER_ONS_CAPTURE_, 4,             1, 0, "s1",            CPID_ONSLAUGHT,        "0 0", _("^TC^TT^BG team captured %s^BG control point"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_CONTROLPOINT_SHIELDED,   0, 0, "",              CPID_ONS_CAPSHIELD,    "0 0", _("^BGThis control point currently cannot be captured"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_GENERATOR_SHIELDED,      0, 0, "",              CPID_ONS_CAPSHIELD,    "0 0", _("^BGThe enemy generator cannot be destroyed yet\n^F2Capture some control points to unshield it"), "") \
 +    MULTITEAM_CENTER(1, CENTER_ONS_NOTSHIELDED_, 4,         0, 0, "",              CPID_ONSLAUGHT,        "0 0", _("^BGThe ^TCenemy^BG generator is no longer shielded!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_NOTSHIELDED_TEAM,        0, 0, "",              CPID_ONSLAUGHT,        "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture control points to shield it!"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_TELEPORT,                0, 0, "pass_key",      CPID_ONSLAUGHT,        "0 0", _("^BGPress ^F2DROPFLAG%s^BG to teleport"), "") \
 +    MSG_CENTER_NOTIF(1, CENTER_ONS_TELEPORT_ANTISPAM,       0, 1, "f1secs",        CPID_ONSLAUGHT,        "0 0", _("^BGTeleporting disabled for %s"), "") \
      MSG_CENTER_NOTIF(1, CENTER_OVERTIME_FRAG,               0, 0, "",              CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!")) \
      MSG_CENTER_NOTIF(1, CENTER_OVERTIME_CONTROLPOINT,       0, 0, "",              CPID_OVERTIME,         "5 0", _("^F2Now playing ^F4OVERTIME^F2!\n\nGenerators are now decaying.\nThe more control points your team holds,\nthe faster the enemy generator decays"), "") \
      MSG_CENTER_NOTIF(1, CENTER_OVERTIME_TIME,               0, 1, "f1time",        CPID_OVERTIME,         "0 0", _("^F2Now playing ^F4OVERTIME^F2!\n^BGAdded ^F4%s^BG to the game!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_PORTO_CREATED_IN,            0, 0, "",              NO_CPID,               "0 0", _("^K1In^BG-portal created"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_PORTO_CREATED_OUT,           0, 0, "",              NO_CPID,               "0 0", _("^F3Out^BG-portal created"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_PORTO_FAILED,                0, 0, "",              NO_CPID,               "0 0", _("^K1Portal deployment failed.\n\n^F2Catch it to try again!"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_INVISIBILITY,      0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Invisibility has worn off"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SHIELD,            0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Shield has worn off"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERDOWN_SPEED,             0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Speed has worn off"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERUP_SPEED,               0, 0, "",              CPID_POWERUP,          "0 0", _("^F2You are on speed"), "") \
      MSG_CENTER_NOTIF(1, CENTER_POWERUP_STRENGTH,            0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Strength infuses your weapons with devastating power"), "") \
      MSG_CENTER_NOTIF(1, CENTER_RACE_FINISHLAP,              0, 0, "",              CPID_RACE_FINISHLAP,   "0 0", _("^F2The race is over, finish your lap!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_SECONDARY_NODAMAGE,          0, 0, "",              NO_CPID,               "0 0", _("^BGSecondary fire inflicts no damage!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_SEQUENCE_COMPLETED,          0, 0, "",              NO_CPID,               "0 0", _("^BGSequence completed!"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_SEQUENCE_COUNTER,            0, 0, "",              NO_CPID,               "0 0", _("^BGThere are more to go..."), "") \
+     MSG_CENTER_NOTIF(1, CENTER_SEQUENCE_COUNTER_FEWMORE,    0, 1, "f1",            NO_CPID,               "0 0", _("^BGOnly %s^BG more to go..."), "") \
      MSG_CENTER_NOTIF(1, CENTER_SUPERWEAPON_BROKEN,          0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Superweapons have broken down"), "") \
      MSG_CENTER_NOTIF(1, CENTER_SUPERWEAPON_LOST,            0, 0, "",              CPID_POWERUP,          "0 0", _("^F2Superweapons have been lost"), "") \
      MSG_CENTER_NOTIF(1, CENTER_SUPERWEAPON_PICKUP,          0, 0, "",              CPID_POWERUP,          "0 0", _("^F2You now have a superweapon"), "") \
      MSG_CENTER_NOTIF(1, CENTER_TEAMCHANGE_SPECTATE,         0, 1, "",              CPID_TEAMCHANGE,       "1 f1", _("^K1Spectating in ^COUNT"), "") \
      MSG_CENTER_NOTIF(1, CENTER_TEAMCHANGE_SUICIDE,          0, 1, "",              CPID_TEAMCHANGE,       "1 f1", _("^K1Suicide in ^COUNT"), "") \
      MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_BEGINNING,           0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout begins in ^COUNT"), "") \
-     MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING,              0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout ends in ^COUNT"), "")
+     MSG_CENTER_NOTIF(1, CENTER_TIMEOUT_ENDING,              0, 1, "",              CPID_TIMEOUT,          "1 f1", _("^F4Timeout ends in ^COUNT"), "") \
+     MSG_CENTER_NOTIF(1, CENTER_WEAPON_MINELAYER_LIMIT,      0, 1, "f1",            NO_CPID,               "0 0",  _("^BGYou cannot place more than ^F2%s^BG mines at a time"), "") 
  
  #define MULTITEAM_MULTI2(default,prefix,anncepre,infopre,centerpre) \
      MSG_MULTI_NOTIF(default, prefix##RED, anncepre##RED, infopre##RED, centerpre##RED) \
      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, ITEM_WEAPON_NOAMMO,                   NO_MSG,        INFO_ITEM_WEAPON_NOAMMO,                   CENTER_ITEM_WEAPON_NOAMMO) \
      MSG_MULTI_NOTIF(1, ITEM_WEAPON_PRIMORSEC,                NO_MSG,        INFO_ITEM_WEAPON_PRIMORSEC,                CENTER_ITEM_WEAPON_PRIMORSEC) \
      MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE,              NO_MSG,        INFO_ITEM_WEAPON_UNAVAILABLE,              CENTER_ITEM_WEAPON_UNAVAILABLE) \
+     MSG_MULTI_NOTIF(1, MULTI_COINTOSS,                       NO_MSG,        INFO_COINTOSS,                             CENTER_COINTOSS) \
      MSG_MULTI_NOTIF(1, MULTI_COUNTDOWN_BEGIN,                ANNCE_BEGIN,   NO_MSG,                                    CENTER_COUNTDOWN_BEGIN) \
-     MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO,                ANNCE_NUM_10,  NO_MSG,                                    CENTER_MINSTA_FINDAMMO_FIRST) \
+     MSG_MULTI_NOTIF(1, MULTI_INSTAGIB_FINDAMMO,              ANNCE_NUM_10,  NO_MSG,                                    CENTER_INSTAGIB_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_HAGAR_SUICIDE,                 NO_MSG,        INFO_WEAPON_HAGAR_SUICIDE,                 CENTER_DEATH_SELF_GENERIC) \
      MSG_MULTI_NOTIF(1, WEAPON_HLAC_MURDER,                   NO_MSG,        INFO_WEAPON_HLAC_MURDER,                   NO_MSG) \
      MSG_MULTI_NOTIF(1, WEAPON_HLAC_SUICIDE,                  NO_MSG,        INFO_WEAPON_HLAC_SUICIDE,                  CENTER_DEATH_SELF_GENERIC) \
+     MSG_MULTI_NOTIF(1, WEAPON_HMG_MURDER_SNIPE,              NO_MSG,        INFO_WEAPON_HMG_MURDER_SNIPE,              NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_HMG_MURDER_SPRAY,              NO_MSG,        INFO_WEAPON_HMG_MURDER_SPRAY,              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_LIMIT,               NO_MSG,        INFO_WEAPON_MINELAYER_LIMIT,               CENTER_WEAPON_MINELAYER_LIMIT) \
      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_RPC_MURDER_DIRECT,             NO_MSG,        INFO_WEAPON_RPC_MURDER_DIRECT,             NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_RPC_MURDER_SPLASH,             NO_MSG,        INFO_WEAPON_RPC_MURDER_SPLASH,             NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_RPC_SUICIDE_DIRECT,            NO_MSG,        INFO_WEAPON_RPC_SUICIDE_DIRECT,            NO_MSG) \
+     MSG_MULTI_NOTIF(1, WEAPON_RPC_SUICIDE_SPLASH,            NO_MSG,        INFO_WEAPON_RPC_SUICIDE_SPLASH,            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) \
@@@ -948,6 -1009,7 +1021,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
  */
  
@@@ -990,7 -1052,7 +1064,7 @@@ string arg_slot[NOTIF_MAX_ARGS]
      ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   mmssss(f3)) \
      ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
      ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
-     ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
+     ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1)) \
      ARG_CASE(ARG_CS,        "pass_key",      ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
      ARG_CASE(ARG_CS,        "frag_ping",     notif_arg_frag_ping(TRUE, f2)) \
      ARG_CASE(ARG_CS,        "frag_stats",    notif_arg_frag_stats(f2, f3, f4)) \
      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)) \
@@@ -1034,26 -1097,26 +1109,26 @@@ string notif_arg_frag_stats(float fheal
          return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping));
  }
  
- string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
+ string notif_arg_missing_teams(float f1)
  {
      return sprintf("%s%s%s%s",
-         (f1 ?
-             sprintf("%s%s", Team_ColoredFullName(f1 - 1), ((f2 + f3 + f4) ? ", " : ""))
+         ((f1 & 1) ?
+             sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_1), ((f1 & (2 + 4 + 8)) ? ", " : ""))
              :
              ""
          ),
-         (f2 ?
-             sprintf("%s%s", Team_ColoredFullName(f2 - 1), ((f3 + f4) ? ", " : ""))
+         ((f1 & 2) ?
+             sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_2), ((f1 & (4 + 8)) ? ", " : ""))
              :
              ""
          ),
-         (f3 ?
-             sprintf("%s%s", Team_ColoredFullName(f3 - 1), (f4 ? ", " : ""))
+         ((f1 & 4) ?
+             sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_3), ((f1 & 8) ? ", " : ""))
              :
              ""
          ),
-         (f4 ?
-             Team_ColoredFullName(f4 - 1)
+         ((f1 & 8) ?
+             Team_ColoredFullName(NUM_TEAM_4)
              :
              ""
          )
@@@ -1504,6 -1567,7 +1579,7 @@@ void RegisterNotifications_First(
      #endif
  
      printf("Beginning notification initialization on %s%s program...\n", dedi, PROGNAME);
+     #undef dedi
  
      // maybe do another implementation of this with checksums? for now, we don't need versioning
      /*if(autocvar_notification_version != NOTIF_VERSION)
diff --combined qcsrc/common/stats.qh
index 0000000000000000000000000000000000000000,f0570299794f72d698269b62ac7133a0951258a5..3f35a3a6664efba79e4e6e4ff2ac3dab095f934c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,290 +1,290 @@@
 -// 87 empty?
+ // 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?
+ const float STAT_ARC_HEAT               = 41;
+ const float STAT_PRESSED_KEYS           = 42;
+ const float STAT_ALLOW_OLDVORTEXBEAM    = 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_DAMAGE_DEALT_TOTAL     = 55;
+ const float STAT_TYPEHIT_TIME           = 56;
+ const float STAT_LAYED_MINES            = 57;
+ const float STAT_HAGAR_LOAD             = 58;
+ const float STAT_SWITCHINGWEAPON        = 59;
+ const float STAT_SUPERWEAPONS_FINISHED  = 60;
+ const float STAT_VEHICLESTAT_HEALTH     = 61;
+ const float STAT_VEHICLESTAT_SHIELD     = 62;
+ const float STAT_VEHICLESTAT_ENERGY     = 63;
+ const float STAT_VEHICLESTAT_AMMO1      = 64;
+ const float STAT_VEHICLESTAT_RELOAD1    = 65;
+ const float STAT_VEHICLESTAT_AMMO2      = 66;
+ const float STAT_VEHICLESTAT_RELOAD2    = 67;
+ const float STAT_VEHICLESTAT_W2MODE     = 68;
+ 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;
+ const float STAT_OK_AMMO_CHARGE         = 85;
+ const float STAT_OK_AMMO_CHARGEPOOl     = 86;
++const float STAT_ROUNDLOST             = 87;
+ // 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;
index 5d39ccf9a6b35440a6b36d82d958efc520d9097c,168045bd039230257c6a6e13e087b8855e3b2026..460d8354b01c9443c184e61166a14c2b0c0b5af2
@@@ -64,11 -64,10 +64,10 @@@ float autocvar_bot_wander_enable
  float autocvar_captureleadlimit_override;
  #define autocvar_capturelimit_override cvar("capturelimit_override")
  #define autocvar_developer cvar("developer")
- float autocvar_developer_fteqccbugs;
  float autocvar_ekg;
  #define autocvar_fraglimit cvar("fraglimit")
  #define autocvar_fraglimit_override cvar("fraglimit_override")
- float autocvar_g_allow_oldnexbeam;
+ float autocvar_g_allow_oldvortexbeam;
  float autocvar_g_antilag;
  float autocvar_g_antilag_nudge;
  float autocvar_g_balance_armor_blockpercent;
@@@ -80,33 -79,6 +79,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;
@@@ -114,134 -86,11 +86,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;
@@@ -261,75 -110,6 +110,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;
@@@ -337,54 -117,6 +117,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;
@@@ -402,102 -134,6 +134,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;
@@@ -507,11 -143,13 +143,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;
@@@ -529,15 -167,6 +167,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;
@@@ -546,133 -175,10 +175,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;
@@@ -792,6 -298,10 +298,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;
@@@ -801,10 -311,13 +311,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;
@@@ -879,11 -392,11 +392,11 @@@ float autocvar_g_maxplayers_spectator_b
  float autocvar_g_maxpushtime;
  float autocvar_g_maxspeed;
  float autocvar_g_midair_shieldtime;
- #define autocvar_g_minstagib cvar("g_minstagib")
- float autocvar_g_minstagib_ammo_drop;
- float autocvar_g_minstagib_extralives;
- float autocvar_g_minstagib_speed_highspeed;
- float autocvar_g_minstagib_invis_alpha;
+ #define autocvar_g_instagib cvar("g_instagib")
+ float autocvar_g_instagib_ammo_drop;
+ float autocvar_g_instagib_extralives;
+ float autocvar_g_instagib_speed_highspeed;
+ float autocvar_g_instagib_invis_alpha;
  #define autocvar_g_mirrordamage cvar("g_mirrordamage")
  #define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
  
@@@ -913,12 -426,18 +426,13 @@@ float autocvar_g_nick_flood_penalty_red
  float autocvar_g_nick_flood_penalty_yellow;
  //float autocvar_g_nick_flood_timeout;
  float autocvar_g_nix_with_healtharmor;
- float autocvar_g_nix_with_laser;
+ float autocvar_g_nix_with_blaster;
  float autocvar_g_nix_with_powerups;
  float autocvar_g_nodepthtestitems;
  float autocvar_g_nodepthtestplayers;
  float autocvar_g_norecoil;
 -float autocvar_g_onslaught_cp_buildhealth;
 -float autocvar_g_onslaught_cp_buildtime;
 -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;
@@@ -927,7 -446,6 +441,6 @@@ float autocvar_g_pickup_shells_max
  float autocvar_g_player_alpha;
  float autocvar_g_player_brightness;
  float autocvar_g_playerclip_collisions;
- string autocvar_g_playerstats_uri;
  float autocvar_g_powerups;
  float autocvar_g_projectiles_damage;
  float autocvar_g_projectiles_keep_owner;
@@@ -959,8 -477,9 +472,9 @@@ 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_point_limit;
+ float autocvar_g_tdm_point_leadlimit;
  float autocvar_g_tdm_teams;
  float autocvar_g_tdm_teams_override;
  float autocvar_g_teamdamage_resetspeed;
@@@ -1101,6 -620,7 +615,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;
@@@ -1129,7 -649,7 +644,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;
@@@ -1151,6 -671,11 +666,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;
@@@ -1258,6 -783,8 +778,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;
@@@ -1268,37 -795,88 +790,111 @@@ 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;
  float autocvar_g_jump_grunt;
+ float autocvar_g_overkill_powerups_replace;
+ float autocvar_g_overkill_superguns_respawn_time;
+ float autocvar_g_overkill_100h_anyway;
+ float autocvar_g_overkill_100a_anyway;
+ float autocvar_g_overkill_ammo_charge;
+ float autocvar_g_overkill_ammo_charge_notice;
+ float autocvar_g_overkill_ammo_charge_limit;
  float autocvar_g_spawn_near_teammate_distance;
  float autocvar_g_spawn_near_teammate_ignore_spawnpoint;
  float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
  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_onslaught_debug;
 +float autocvar_g_onslaught_teleport_wait;
 +float autocvar_g_onslaught_spawn_at_controlpoints;
 +var float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
 +float autocvar_g_onslaught_spawn_at_controlpoints_random;
 +float autocvar_g_onslaught_spawn_at_generator;
 +float autocvar_g_onslaught_spawn_at_generator_chance;
 +float autocvar_g_onslaught_spawn_at_generator_random;
 +float autocvar_g_onslaught_cp_proxydecap;
 +var float autocvar_g_onslaught_cp_proxydecap_distance = 512;
 +var float autocvar_g_onslaught_cp_proxydecap_dps = 100;
 +float autocvar_g_onslaught_cp_buildhealth;
 +float autocvar_g_onslaught_cp_buildtime;
 +float autocvar_g_onslaught_cp_health;
 +float autocvar_g_onslaught_cp_regen;
 +float autocvar_g_onslaught_gen_health;
 +var float autocvar_g_onslaught_shield_force = 100;
 +float autocvar_g_onslaught_allow_vehicle_touch;
 +float autocvar_g_onslaught_round_timelimit;
 +float autocvar_g_onslaught_point_limit;
 +float autocvar_g_onslaught_warmup;
 +float autocvar_g_onslaught_teleport_radius;
 +float autocvar_g_onslaught_spawn_choose;
 +float autocvar_g_onslaught_click_radius;
+ 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;
index 29f278426bd85592b197ee6106e29775a42a40fb,e58e6709730282677350265c9c55a953c8945850..3127262196cc2820ad3689f06d5346f5d76d1d23
@@@ -1,4 -1,5 +1,4 @@@
  #include "havocbot.qh"
 -#include "role_onslaught.qc"
  #include "role_keyhunt.qc"
  #include "roles.qc"
  
@@@ -91,7 -92,7 +91,7 @@@ void havocbot_ai(
  
                if(self.weapons)
                {
-                       weapon_action(self.weapon, WR_AIM);
+                       WEP_ACTION(self.weapon, WR_AIM);
                        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
                        {
                                self.BUTTON_ATCK = FALSE;
                        for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                        {
                                e = get_weaponinfo(i);
-                               if ((self.weapons & WepSet_FromWeapon(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo"))))
+                               if ((self.weapons & WepSet_FromWeapon(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < e.reloading_ammo))
                                        self.switchweapon = i;
                        }
                }
@@@ -589,10 -590,10 +589,10 @@@ void havocbot_movetogoal(
  
                        return;
                }
-               else if(self.health>autocvar_g_balance_rocketlauncher_damage*0.5)
+               else if(self.health>WEP_CVAR(devastator, damage)*0.5)
                {
                        if(self.velocity_z < 0)
-                       if(client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
+                       if(client_hasweapon(self, WEP_DEVASTATOR, TRUE, FALSE))
                        {
                                self.movement_x = maxspeed;
  
                                        return;
                                }
  
-                               self.switchweapon = WEP_ROCKET_LAUNCHER;
+                               self.switchweapon = WEP_DEVASTATOR;
                                self.v_angle_x = 90;
                                self.BUTTON_ATCK = TRUE;
-                               self.rocketjumptime = time + autocvar_g_balance_rocketlauncher_detonatedelay;
+                               self.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
                                return;
                        }
                }
@@@ -955,7 -956,7 +955,7 @@@ float havocbot_chooseweapon_checkreload
                for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                {
                        // if we are out of ammo for all other weapons, it's an emergency to switch to anything else
-                       if (weapon_action(i, WR_CHECKAMMO1) + weapon_action(i, WR_CHECKAMMO2))
+                       if (WEP_ACTION(i, WR_CHECKAMMO1) + WEP_ACTION(i, WR_CHECKAMMO2))
                                other_weapon_available = TRUE;
                }
                if(other_weapon_available)
@@@ -981,7 -982,7 +981,7 @@@ void havocbot_chooseweapon(
        {
                // If no weapon was chosen get the first available weapon
                if(self.weapon==0)
-               for(i=WEP_LASER + 1; i < WEP_COUNT ; ++i)
+               for(i = WEP_FIRST; i <= WEP_LAST; ++i) if(i != WEP_BLASTER)
                {
                        if(client_hasweapon(self, i, TRUE, FALSE))
                        {
index 58aa893c31e16bf056d0fae164565bff94d572c3,7e3ddbb4340dd820253fccc87b5d2d2449ae2af1..c6f0efa6dd05f82ad23ed6199d82fc4cf9a15552
@@@ -98,7 -98,10 +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;
@@@ -221,50 -224,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
                havocbot_chooserole_dm();
  }
diff --combined qcsrc/server/g_damage.qc
index c6ba6a12c2febb9e4a20dddd907d0903048863ba,4840e15dd25d6916239fb221de8fbd721d47cec0..8887f07efd2c5fee6a2340c09b3881775841e1fe
@@@ -99,7 -99,7 +99,7 @@@ void GiveFrags (entity attacker, entit
                // regular frag
                PlayerScore_Add(attacker, SP_KILLS, 1);
                if(targ.playerid)
-                       PlayerStats_Event(attacker, sprintf("kills-%d", targ.playerid), 1);
+                       PS_GR_P_ADDVAL(attacker, sprintf("kills-%d", targ.playerid), 1);
        }
  
        PlayerScore_Add(targ, SP_DEATHS, 1);
                else if(!(attacker.weapons & WepSet_FromWeapon(culprit)))
                        culprit = attacker.weapon;
  
-               if(g_weaponarena_random_with_laser && culprit == WEP_LASER)
+               if(g_weaponarena_random_with_blaster && 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)
@@@ -419,7 -419,7 +419,7 @@@ void Obituary(entity attacker, entity i
                                case counta: \
                                { \
                                        Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
-                                       PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
+                                       PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
                                        break; \
                                }
                        switch(attacker.killcount)
                        {
                                checkrules_firstblood = TRUE;
                                notif_firstblood = TRUE; // modify the current messages so that they too show firstblood information
-                               PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
-                               PlayerStats_Event(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
+                               PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
+                               PS_GR_P_ADDVAL(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
  
                                // tell spree_inf and spree_cen that this is a first-blood and first-victim event
                                kill_count_to_attacker = -1;
                if(PlayerScore_Add(targ, SP_SCORE, 0) == -5)
                {
                        Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
-                       PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
+                       PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
                }
        }
  
        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;
@@@ -647,7 -727,7 +727,7 @@@ void Damage (entity targ, entity inflic
                                                                attacker.dmg_take += v_x;
                                                                attacker.dmg_save += v_y;
                                                                attacker.dmg_inflictor = inflictor;
-                                                               mirrordamage = v_z; // = 0, to make fteqcc stfu
+                                                               mirrordamage = v_z;
                                                                mirrorforce = 0;
                                                        }
  
                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_instagib)
                {
                        // 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)
                if(!targ.deadflag)
+               if(deathtype != DEATH_BUFF_VENGEANCE)
                if(targ.takedamage == DAMAGE_AIM)
                if(targ != attacker)
                {
                        else
                                victim = targ;
  
-                       if(IS_PLAYER(victim) || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET || victim.flags & FL_MONSTER || victim.classname == "func_assault_destructible" || (victim.classname == "onslaught_generator" && !victim.isshielded) || (victim.classname == "onslaught_controlpoint_icon" && !victim.owner.isshielded))
 -                      if(IS_PLAYER(victim) || (victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (victim.flags & FL_MONSTER))
++                      if(IS_PLAYER(victim) || (victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || (victim.flags & FL_MONSTER) || victim.classname == "func_assault_destructible" || (victim.classname == "onslaught_generator" && !victim.isshielded) || (victim.classname == "onslaught_controlpoint_icon" && !victim.owner.isshielded))
                        {
-                               if(DIFF_TEAM(victim, attacker))
+                               if(DIFF_TEAM(victim, attacker) && !victim.frozen)
                                {
                                        if(damage > 0)
                                        {
                                                        if(victim.BUTTON_CHAT)
                                                                attacker.typehitsound += 1;
                                                        else
-                                                               attacker.hitsound += 1;
+                                                               attacker.damage_dealt += damage;
                                                }
  
                                                damage_goodhits += 1;
        // apply push
        if (self.damageforcescale)
        if (vlen(force))
-       if (!IS_PLAYER(self) || time >= self.spawnshieldtime)
+       if (!IS_PLAYER(self) || time >= self.spawnshieldtime || self == attacker)
        {
                vector farce = damage_explosion_calcpush(self.damageforcescale * force, self.velocity, autocvar_g_balance_damagepush_speedfactor);
                if(self.movetype == MOVETYPE_PHYSICS)
  }
  
  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;
@@@ -1200,24 -1295,24 +1295,24 @@@ 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);
        d = e.fire_damagepersec * t;
  
-       hi = e.fire_owner.hitsound;
+       hi = e.fire_owner.damage_dealt;
        ty = e.fire_owner.typehitsound;
        Damage(e, e, e.fire_owner, d, e.fire_deathtype, e.origin, '0 0 0');
        if(e.fire_hitsound && e.fire_owner)
        {
-               e.fire_owner.hitsound = hi;
+               e.fire_owner.damage_dealt = hi;
                e.fire_owner.typehitsound = ty;
        }
        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 5ab5eae5df0cfd0c3166a832facc8848deca8af7,0fd5d2de3c2eee8f0e2a8076f22ee753b6965992..18bcafef7ad31b28a4949ea57ab11dce16ed8ad9
@@@ -41,7 -41,7 +41,7 @@@ void PingPLReport_Think(
                WriteByte(MSG_BROADCAST, 0);
                WriteByte(MSG_BROADCAST, 0);
        }
-       self.cnt = mod(self.cnt + 1, maxclients);
+       self.cnt = (self.cnt + 1) % maxclients;
  }
  void PingPLReport_Spawn()
  {
@@@ -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();
@@@ -82,32 -81,6 +81,6 @@@ void SetDefaultAlpha(
        }
  }
  
- void fteqcc_testbugs()
- {
-       float a, b;
-       if(!autocvar_developer_fteqccbugs)
-               return;
-       dprint("*** fteqcc test: checking for bugs...\n");
-       a = 1;
-       b = 5;
-       if(sqrt(a) - sqrt(b - a) == 0)
-               dprint("*** fteqcc test: found same-function-twice bug\n");
-       else
-               dprint("*** fteqcc test: same-function-twice bug got FINALLY FIXED! HOORAY!\n");
-       world.cnt = -10;
-       world.enemy = world;
-       world.enemy.cnt += 10;
-       if(world.cnt > 0.2 || world.cnt < -0.2) // don't error out if it's just roundoff errors
-               dprint("*** fteqcc test: found += bug\n");
-       else
-               dprint("*** fteqcc test: += bug got FINALLY FIXED! HOORAY!\n");
-       world.cnt = 0;
- }
  void GotoFirstMap()
  {
        float n;
@@@ -216,7 -189,6 +189,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");
                BADPREFIX("sv_ready_restart_");
  
                // mutators that announce themselves properly to the server browser
-               BADCVAR("g_minstagib");
+               BADCVAR("g_instagib");
                BADCVAR("g_new_toys");
                BADCVAR("g_nix");
                BADCVAR("g_grappling_hook");
@@@ -546,6 -519,7 +519,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);
@@@ -572,8 -546,6 +546,6 @@@ void spawnfunc_worldspawn (void
  
        remove = remove_safely; // during spawning, watch what you remove!
  
-       check_unacceptable_compiler_bugs();
        cvar_changes_init(); // do this very early now so it REALLY matches the server config
  
        compressShortVector_init();
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
  
        ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
  
  
        Map_MarkAsRecent(mapname);
  
-       PlayerStats_Init(); // we need this to be initiated before InitGameplayMode
+       PlayerStats_GameReport_Init(); // we need this to be initiated before InitGameplayMode
  
        precache_model ("null"); // we need this one before InitGameplayMode
        InitGameplayMode();
        readlevelcvars();
        GrappleHookInit();
-       ElectroInit();
-       LaserInit();
  
        player_count = 0;
        bot_waypoints_for_items = autocvar_g_waypoints_for_items;
        if(autocvar_g_campaign)
                CampaignPostInit();
  
-       fteqcc_testbugs();
        Ban_LoadBans();
  
        MapInfo_Enumerate();
        addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
        addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
        addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime);
-       addstat(STAT_ALLOW_OLDNEXBEAM, AS_INT, stat_allow_oldnexbeam);
+       addstat(STAT_ALLOW_OLDVORTEXBEAM, AS_INT, stat_allow_oldvortexbeam);
        Nagger_Init();
  
        addstat(STAT_STRENGTH_FINISHED, AS_FLOAT, strength_finished);
        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_WEAPON_CLIPSIZE, AS_INT, clip_size);
        addstat(STAT_LAST_PICKUP, AS_FLOAT, last_pickup);
        addstat(STAT_HIT_TIME, AS_FLOAT, hit_time);
+       addstat(STAT_DAMAGE_DEALT_TOTAL, AS_INT, damage_dealt_total);
        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);
+       
+       addstat(STAT_ARC_HEAT, AS_FLOAT, arc_heat_percent);
+       // 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);
@@@ -896,7 -873,6 +873,6 @@@ string GetGametype(
        return MapInfo_Type_ToString(MapInfo_LoadedGametype);
  }
  
- string getmapname_stored;
  string GetMapname()
  {
        return mapname;
@@@ -1060,7 -1036,7 +1036,7 @@@ float() MaplistMethod_Iterate = // usua
                for(i = 1; i < Map_Count; ++i)
                {
                        float mapindex;
-                       mapindex = mod(i + Map_Current, Map_Count);
+                       mapindex = (i + Map_Current) % Map_Count;
                        if(Map_Check(mapindex, pass))
                                return mapindex;
                }
@@@ -1088,7 -1064,7 +1064,7 @@@ float() MaplistMethod_Random = // rando
        for(i = 0; i <= imax; ++i)
        {
                float mapindex;
-               mapindex = mod(Map_Current + floor(random() * (Map_Count - 1) + 1), Map_Count); // any OTHER map
+               mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
                if(Map_Check(mapindex, 1))
                        return mapindex;
        }
@@@ -1221,13 -1197,27 +1197,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(autocvar_nextmap);
+                       Map_Goto_SetStr(m);
+                       return FALSE;
+               }
+               
+               if(MapInfo_CheckMap(m))
+               {
+                       Map_Goto_SetStr(m);
                        Map_Goto(reinit);
                        alreadychangedlevel = TRUE;
                        return TRUE;
                }
+       }
        if(!reinit && autocvar_lastlevel)
        {
                cvar_settemp_restore();
@@@ -1264,9 -1254,6 +1254,6 @@@ When the player presses attack or jump
  ============
  */
  .float autoscreenshot;
- void() MapVote_Start;
- void() MapVote_Think;
- float mapvote_initialized;
  void IntermissionThink()
  {
        FixIntermissionClient(self);
@@@ -1513,11 -1500,7 +1500,7 @@@ void NextLevel(
        DumpStats(TRUE);
  
        // send statistics
-       entity e;
-       PlayerStats_EndMatch(1);
-       FOR_EACH_CLIENT(e)
-               PlayerStats_AddGlobalInfo(e);
-       PlayerStats_Shutdown();
+       PlayerStats_GameReport(TRUE);
        WeaponStats_Shutdown();
  
        Kill_Notification(NOTIF_ALL, world, MSG_CENTER, 0); // kill all centerprints now
@@@ -1653,6 -1636,48 +1636,6 @@@ void ClearWinners(void
                head.winning = 0;
  }
  
 -// Onslaught winning condition:
 -// game terminates if only one team has a working generator (or none)
 -float WinningCondition_Onslaught()
 -{
 -      entity head;
 -      float t1, t2, t3, t4;
 -
 -      WinningConditionHelper(); // set worldstatus
 -
 -      if(warmup_stage)
 -              return WINNING_NO;
 -
 -      // first check if the game has ended
 -      t1 = t2 = t3 = t4 = 0;
 -      head = find(world, classname, "onslaught_generator");
 -      while (head)
 -      {
 -              if (head.health > 0)
 -              {
 -                      if (head.team == NUM_TEAM_1) t1 = 1;
 -                      if (head.team == NUM_TEAM_2) t2 = 1;
 -                      if (head.team == NUM_TEAM_3) t3 = 1;
 -                      if (head.team == NUM_TEAM_4) t4 = 1;
 -              }
 -              head = find(head, classname, "onslaught_generator");
 -      }
 -      if (t1 + t2 + t3 + t4 < 2)
 -      {
 -              // game over, only one team remains (or none)
 -              ClearWinners();
 -              if (t1) SetWinners(team, NUM_TEAM_1);
 -              if (t2) SetWinners(team, NUM_TEAM_2);
 -              if (t3) SetWinners(team, NUM_TEAM_3);
 -              if (t4) SetWinners(team, NUM_TEAM_4);
 -              dprint("Have a winner, ending game.\n");
 -              return WINNING_YES;
 -      }
 -
 -      // Two or more teams remain
 -      return WINNING_NO;
 -}
 -
  // Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
  // they win. Otherwise the defending team wins once the timelimit passes.
  void assault_new_round();
@@@ -1989,17 -2014,6 +1972,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
                return;
        }
  
 -      if(g_onslaught)
 -              timelimit = 0; // ONS has its own overtime rule
 -
        float wantovertime;
        wantovertime = 0;
  
        {
                checkrules_status = WinningCondition_LMS();
        }
 -      else if (g_onslaught)
 -      {
 -              checkrules_status = WinningCondition_Onslaught(); // TODO remove this?
 -      }
        else
        {
                checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
        }
  }
  
- 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)
@@@ -2686,19 -2204,13 +2155,13 @@@ void EndFrame(
        float altime;
        FOR_EACH_REALCLIENT(self)
        {
-               if(IS_SPEC(self))
+               entity e = IS_SPEC(self) ? self.enemy : self;
+               if(e.typehitsound)
+                       self.typehit_time = time;
+               else if(e.damage_dealt)
                {
-                       if(self.enemy.typehitsound)
-                               self.typehit_time = time;
-                       else if(self.enemy.hitsound)
-                               self.hit_time = time;
-               }
-               else
-               {
-                       if(self.typehitsound)
-                               self.typehit_time = time;
-                       else if(self.hitsound)
-                               self.hit_time = time;
+                       self.hit_time = time;
+                       self.damage_dealt_total += ceil(e.damage_dealt);
                }
        }
        altime = time + frametime * (1 + autocvar_g_antilag_nudge);
        // needed!
        FOR_EACH_CLIENT(self)
        {
-               self.hitsound = FALSE;
                self.typehitsound = FALSE;
+               self.damage_dealt = 0;
                antilag_record(self, altime);
        }
+       FOR_EACH_MONSTER(self)
+               antilag_record(self, altime);
  }
  
  
@@@ -2784,8 -2298,6 +2249,6 @@@ void RestoreGame(
  
  void Shutdown()
  {
-       entity e;
        gameover = 2;
  
        if(world_initialized > 0)
                print("Saving persistent data...\n");
                Ban_SaveBans();
  
-               PlayerStats_EndMatch(0);
-               FOR_EACH_CLIENT(e)
-                       PlayerStats_AddGlobalInfo(e);
-               PlayerStats_Shutdown();
+               // playerstats with unfinished match
+               PlayerStats_GameReport(FALSE);
  
                if(!cheatcount_total)
                {
index 459dca92801e9d91ae37a470b2010e156bf60a1c,e6fda399e27eb0427e90a241964f623843a54feb..f4069387309875c975f6da4b0f3c6b78931e510c
@@@ -355,12 -355,13 +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;
@@@ -460,7 -461,6 +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;
@@@ -489,6 -489,7 +489,7 @@@ void GetCvars(float f
  
        GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
        GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
+       GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
        GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
        GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
        GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
                if (s == "cl_weaponpriority")
                        self.switchweapon = w_getbestweapon(self);
                if (s == "cl_allow_uidtracking")
-                       PlayerStats_AddPlayer(self);
+                       PlayerStats_GameReport_AddPlayer(self);
        }
  }
  
@@@ -543,6 -544,16 +544,6 @@@ string playername(entity p
          return p.netname;
  }
  
 -vector randompos(vector m1, vector m2)
 -{
 -    vector v;
 -    m2 = m2 - m1;
 -    v_x = m2_x * random() + m1_x;
 -    v_y = m2_y * random() + m1_y;
 -    v_z = m2_z * random() + m1_z;
 -    return  v;
 -}
 -
  //#NO AUTOCVARS START
  
  float g_pickup_shells;
@@@ -553,6 -564,8 +554,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;
@@@ -585,7 -598,7 +588,7 @@@ float g_pickup_weapons_anyway
  float g_weaponarena;
  WepSet g_weaponarena_weapons;
  float g_weaponarena_random;
- float g_weaponarena_random_with_laser;
+ float g_weaponarena_random_with_blaster;
  string g_weaponarena_list;
  float g_weaponspeedfactor;
  float g_weaponratefactor;
@@@ -601,6 -614,7 +604,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;
@@@ -612,14 -626,13 +616,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"));
  
@@@ -677,6 -690,7 +680,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";
                g_weaponarena_random = cvar("g_weaponarena_random");
        else
                g_weaponarena_random = 0;
-       g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
+       g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
  
        if (g_weaponarena)
        {
                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)
  
        if(start_items & IT_UNLIMITED_WEAPON_AMMO)
        {
-               start_ammo_rockets = 999;
                start_ammo_shells = 999;
-               start_ammo_cells = 999;
                start_ammo_nails = 999;
+               start_ammo_rockets = 999;
+               start_ammo_cells = 999;
+               start_ammo_plasma = 999;
                start_ammo_fuel = 999;
        }
        else
                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");
        }
  
                warmup_start_ammo_nails = start_ammo_nails;
                warmup_start_ammo_rockets = start_ammo_rockets;
                warmup_start_ammo_cells = start_ammo_cells;
+               warmup_start_ammo_plasma = start_ammo_plasma;
                warmup_start_ammo_fuel = start_ammo_fuel;
                warmup_start_health = start_health;
                warmup_start_armorvalue = start_armorvalue;
                if (!g_weaponarena && !g_ca)
                {
                        warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
-                       warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
                        warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
                        warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
+                       warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
+                       warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
                        warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
                        warmup_start_health = cvar("g_warmup_start_health");
                        warmup_start_armorvalue = cvar("g_warmup_start_armor");
                        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)
  
        if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
        {
-               g_grappling_hook = 0; // these two can't coexist, as they use the same button
                start_items |= IT_FUEL_REGEN;
                start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
                warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
        {
                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_rockets = max(0, start_ammo_rockets);
+       start_ammo_cells = max(0, start_ammo_cells);
+       start_ammo_plasma = max(0, start_ammo_plasma);
        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_rockets = max(0, warmup_start_ammo_rockets);
+       warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
+       warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
        warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
  }
  
@@@ -894,34 -913,11 +903,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;
      g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
      g_bugrigs_steer = cvar("g_bugrigs_steer");
  
-       g_minstagib = cvar("g_minstagib");
+       g_instagib = cvar("g_instagib");
  
        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");
@@@ -1072,15 -1068,6 +1058,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)
  {
@@@ -1293,6 -1279,7 +1269,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");
@@@ -1614,6 -1589,44 +1579,44 @@@ void Net_LinkEntity(entity e, float doc
      }
  }
  
+ entity eliminatedPlayers;
+ .float(entity) isEliminated;
+ float EliminatedPlayers_SendEntity(entity to, float sendflags)
+ {
+       float i, f, b;
+       entity e;
+       WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
+       WriteByte(MSG_ENTITY, sendflags);
+       if(sendflags & 1)
+       {
+               for(i = 1; i <= maxclients; i += 8)
+               {
+                       for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
+                       {
+                               if(eliminatedPlayers.isEliminated(e))
+                                       f |= b;
+                       }
+                       WriteByte(MSG_ENTITY, f);
+               }
+       }
+       return TRUE;
+ }
+ void EliminatedPlayers_Init(float(entity) isEliminated_func)
+ {
+       if(eliminatedPlayers)
+       {
+               backtrace("Can't spawn eliminatedPlayers again!");
+               return;
+       }
+       Net_LinkEntity(eliminatedPlayers = spawn(), FALSE, 0, EliminatedPlayers_SendEntity);
+       eliminatedPlayers.isEliminated = isEliminated_func;
+ }
  void adaptor_think2touch()
  {
      entity o;
@@@ -1749,7 -1762,9 +1752,9 @@@ float WarpZone_Projectile_Touch_ImpactF
                return TRUE;
        if(SUB_NoImpactCheck())
        {
-               if(self.classname == "grapplinghook")
+               if(self.classname == "nade")
+                       return FALSE; // no checks here
+               else if(self.classname == "grapplinghook")
                        RemoveGrapplingHook(self.realowner);
                else if(self.classname == "spike")
                {
@@@ -1816,82 -1831,6 +1821,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 ecf2194006a2e34dcce37fae1f0782ca91d695eb,74cba2897387862d8893b708cd263c1c8b8c8c69..e47999e7407647d0c800d715e4175d6f52fbc6fe
 -float autocvar_g_onslaught_spawn_at_controlpoints;
 -float autocvar_g_onslaught_spawn_at_generator;
 -float autocvar_g_onslaught_cp_proxydecap;
 -var float autocvar_g_onslaught_cp_proxydecap_distance = 512;
 -var float autocvar_g_onslaught_cp_proxydecap_dps = 100;
 +// =======================
 +// CaptureShield Functions
 +// =======================
  
 -void onslaught_generator_updatesprite(entity e);
 -void onslaught_controlpoint_updatesprite(entity e);
 -void onslaught_link_checkupdate();
 -
 -.entity sprite;
 -.string target2;
 -.float iscaptured;
 -.float islinked;
 -.float isgenneighbor_red;
 -.float isgenneighbor_blue;
 -.float iscpneighbor_red;
 -.float iscpneighbor_blue;
 -.float isshielded;
 -.float lasthealth;
 -.float lastteam;
 -.float lastshielded;
 -.float lastcaptured;
 +float ons_CaptureShield_Customize()
 +{
 +      entity e = WaypointSprite_getviewentity(other);
  
 -entity ons_red_generator;
 -entity ons_blue_generator;
 +      if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return FALSE; }
 +      if(SAME_TEAM(self, e)) { return FALSE; }
  
 -void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
 -{
 -      self.velocity = self.velocity + vforce;
 +      return TRUE;
  }
  
 -.float giblifetime;
 -void ons_throwgib_think()
 +void ons_CaptureShield_Touch()
  {
 -      float d;
 +      if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; }
 +      if(!IS_PLAYER(other)) { return; }
 +      if(SAME_TEAM(other, self)) { return; }
  
 -      self.nextthink = time + 0.05;
 +      vector mymid = (self.absmin + self.absmax) * 0.5;
 +      vector othermid = (other.absmin + other.absmax) * 0.5;
  
 -      d = self.giblifetime - time;
 +      Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ons_captureshield_force);
  
 -      if(d<0)
 +      if(IS_REAL_CLIENT(other))
        {
 -              self.think = SUB_Remove;
 -              return;
 +              play2(other, "onslaught/damageblockedbyshield.wav");
 +
 +              if(self.enemy.classname == "onslaught_generator")
 +                      Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED);
 +              else
 +                      Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED);
        }
 -      if(d<1)
 -              self.alpha = d;
 +}
  
 -      if(d>2)
 -      if(random()<0.6)
 -              pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1);
 +void ons_CaptureShield_Reset()
 +{
 +      self.colormap = self.enemy.colormap;
 +      self.team = self.enemy.team;
  }
  
 -void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn)
 +void ons_CaptureShield_Spawn(entity generator, float is_generator)
  {
 -      entity gib;
 +      entity shield = spawn();
 +
 +      shield.enemy = generator;
 +      shield.team = generator.team;
 +      shield.colormap = generator.colormap;
 +      shield.reset = ons_CaptureShield_Reset;
 +      shield.touch = ons_CaptureShield_Touch;
 +      shield.customizeentityforclient = ons_CaptureShield_Customize;
 +      shield.classname = "ons_captureshield";
 +      shield.effects = EF_ADDITIVE;
 +      shield.movetype = MOVETYPE_NOCLIP;
 +      shield.solid = SOLID_TRIGGER;
 +      shield.avelocity = '7 0 11';
 +      shield.scale = 1;
 +      shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3");
 +      
 +      precache_model(shield.model);
 +      setorigin(shield, generator.origin);
 +      setmodel(shield, shield.model);
 +      setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
 +}
  
 -      gib = spawn();
  
 -      setmodel(gib, smodel);
 -      setorigin(gib, v_from);
 -      gib.solid = SOLID_BBOX;
 -      gib.movetype = MOVETYPE_BOUNCE;
 -      gib.takedamage = DAMAGE_YES;
 -      gib.event_damage = ons_gib_damage;
 -      gib.health = -1;
 -      gib.effects = EF_LOWPRECISION;
 -      gib.flags = FL_NOTARGET;
 -      gib.velocity = v_to;
 -      gib.giblifetime = time + f_lifetime;
 +// ==========
 +// Junk Pile
 +// ==========
  
 -      if (b_burn)
 +void ons_debug(string input)
 +{
 +      switch(autocvar_g_onslaught_debug)
        {
 -              gib.think = ons_throwgib_think;
 -              gib.nextthink = time + 0.05;
 +              case 1: dprint(input); break;
 +              case 2: print(input); break;
        }
 -      else
 -              SUB_SetFade(gib, gib.giblifetime, 2);
 +}
 +
 +void FixSize(entity e)
 +{
 +      e.mins_x = rint(e.mins_x);
 +      e.mins_y = rint(e.mins_y);
 +      e.mins_z = rint(e.mins_z);
 +      
 +      e.maxs_x = rint(e.maxs_x);
 +      e.maxs_y = rint(e.maxs_y);
 +      e.maxs_z = rint(e.maxs_z);
 +}
 +
 +vector randompos(vector m1, vector m2)
 +{
 +      vector v;
 +      m2 = m2 - m1;
 +      v_x = m2_x * random() + m1_x;
 +      v_y = m2_y * random() + m1_y;
 +      v_z = m2_z * random() + m1_z;
 +      return  v;
 +}
 +
 +void setmodel_fixsize(entity e, string m)
 +{
 +      setmodel(e, m);
 +      FixSize(e);
  }
  
  void onslaught_updatelinks()
  {
 -      entity l, links;
 -      float stop, t1, t2, t3, t4;
 +      entity l;
        // first check if the game has ended
 -      dprint("--- updatelinks ---\n");
 -      links = findchain(classname, "onslaught_link");
 +      ons_debug("--- updatelinks ---\n");
        // mark generators as being shielded and networked
 -      l = findchain(classname, "onslaught_generator");
 -      while (l)
 +      for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
        {
                if (l.iscaptured)
 -                      dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n");
 +                      ons_debug(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"));
                else
 -                      dprint(etos(l), " (generator) is destroyed\n");
 +                      ons_debug(strcat(etos(l), " (generator) is destroyed\n"));
                l.islinked = l.iscaptured;
                l.isshielded = l.iscaptured;
 -              l = l.chain;
 +              l.sprite.SendFlags |= 16;
        }
        // mark points as shielded and not networked
 -      l = findchain(classname, "onslaught_controlpoint");
 -      while (l)
 +      for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
        {
                l.islinked = FALSE;
                l.isshielded = TRUE;
 -              l.isgenneighbor_red = FALSE;
 -              l.isgenneighbor_blue = FALSE;
 -              l.iscpneighbor_red = FALSE;
 -              l.iscpneighbor_blue = FALSE;
 -              dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n");
 -              l = l.chain;
 +              float i;
 +              for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = FALSE; l.iscpneighbor[i] = FALSE; }
 +              ons_debug(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n"));
 +              l.sprite.SendFlags |= 16;
        }
        // flow power outward from the generators through the network
 -      l = links;
 -      while (l)
 -      {
 -              dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n");
 -              l = l.chain;
 -      }
 -      stop = FALSE;
 +      float stop = FALSE;
        while (!stop)
        {
                stop = TRUE;
 -              l = links;
 -              while (l)
 +              for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
                {
                        // if both points are captured by the same team, and only one of
                        // them is powered, mark the other one as powered as well
                        if (l.enemy.iscaptured && l.goalentity.iscaptured)
                                if (l.enemy.islinked != l.goalentity.islinked)
 -                                      if (l.enemy.team == l.goalentity.team)
 +                                      if(SAME_TEAM(l.enemy, l.goalentity))
                                        {
                                                if (!l.goalentity.islinked)
                                                {
                                                        stop = FALSE;
                                                        l.goalentity.islinked = TRUE;
 -                                                      dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n");
 +                                                      ons_debug(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"));
                                                }
                                                else if (!l.enemy.islinked)
                                                {
                                                        stop = FALSE;
                                                        l.enemy.islinked = TRUE;
 -                                                      dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n");
 +                                                      ons_debug(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"));
                                                }
                                        }
 -                      l = l.chain;
                }
        }
        // now that we know which points are powered we can mark their neighbors
        // as unshielded if team differs
 -      l = links;
 -      while (l)
 +      for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
        {
                if (l.goalentity.islinked)
                {
 -                      if (l.goalentity.team != l.enemy.team)
 +                      if(DIFF_TEAM(l.goalentity, l.enemy))
                        {
 -                              dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n");
 +                              ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"));
                                l.enemy.isshielded = FALSE;
                        }
                        if(l.goalentity.classname == "onslaught_generator")
 -                      {
 -                              if(l.goalentity.team == NUM_TEAM_1)
 -                                      l.enemy.isgenneighbor_red = TRUE;
 -                              else if(l.goalentity.team == NUM_TEAM_2)
 -                                      l.enemy.isgenneighbor_blue = TRUE;
 -                      }
 +                              l.enemy.isgenneighbor[l.goalentity.team] = TRUE;
                        else
 -                      {
 -                              if(l.goalentity.team == NUM_TEAM_1)
 -                                      l.enemy.iscpneighbor_red = TRUE;
 -                              else if(l.goalentity.team == NUM_TEAM_2)
 -                                      l.enemy.iscpneighbor_blue = TRUE;
 -                      }
 +                              l.enemy.iscpneighbor[l.goalentity.team] = TRUE;
                }
                if (l.enemy.islinked)
                {
 -                      if (l.goalentity.team != l.enemy.team)
 +                      if(DIFF_TEAM(l.goalentity, l.enemy))
                        {
 -                              dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n");
 +                              ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"));
                                l.goalentity.isshielded = FALSE;
                        }
                        if(l.enemy.classname == "onslaught_generator")
 -                      {
 -                              if(l.enemy.team == NUM_TEAM_1)
 -                                      l.goalentity.isgenneighbor_red = TRUE;
 -                              else if(l.enemy.team == NUM_TEAM_2)
 -                                      l.goalentity.isgenneighbor_blue = TRUE;
 -                      }
 +                              l.goalentity.isgenneighbor[l.enemy.team] = TRUE;
                        else
 -                      {
 -                              if(l.enemy.team == NUM_TEAM_1)
 -                                      l.goalentity.iscpneighbor_red = TRUE;
 -                              else if(l.enemy.team == NUM_TEAM_2)
 -                                      l.goalentity.iscpneighbor_blue = TRUE;
 -                      }
 +                              l.goalentity.iscpneighbor[l.enemy.team] = TRUE;
                }
 -              l = l.chain;
        }
 -      // now update the takedamage and alpha variables on generator shields
 -      l = findchain(classname, "onslaught_generator");
 -      while (l)
 +      // now update the generators
 +      for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
        {
                if (l.isshielded)
                {
 -                      dprint(etos(l), " (generator) is shielded\n");
 -                      l.enemy.alpha = 1;
 +                      ons_debug(strcat(etos(l), " (generator) is shielded\n"));
                        l.takedamage = DAMAGE_NO;
                        l.bot_attack = FALSE;
                }
                else
                {
 -                      dprint(etos(l), " (generator) is not shielded\n");
 -                      l.enemy.alpha = -1;
 +                      ons_debug(strcat(etos(l), " (generator) is not shielded\n"));
                        l.takedamage = DAMAGE_AIM;
                        l.bot_attack = TRUE;
                }
 -              l = l.chain;
 +
 +              ons_Generator_UpdateSprite(l);
        }
        // now update the takedamage and alpha variables on control point icons
 -      l = findchain(classname, "onslaught_controlpoint");
 -      while (l)
 +      for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
        {
                if (l.isshielded)
                {
 -                      dprint(etos(l), " (point) is shielded\n");
 -                      l.enemy.alpha = 1;
 +                      ons_debug(strcat(etos(l), " (point) is shielded\n"));
                        if (l.goalentity)
                        {
                                l.goalentity.takedamage = DAMAGE_NO;
                }
                else
                {
 -                      dprint(etos(l), " (point) is not shielded\n");
 -                      l.enemy.alpha = -1;
 +                      ons_debug(strcat(etos(l), " (point) is not shielded\n"));
                        if (l.goalentity)
                        {
                                l.goalentity.takedamage = DAMAGE_AIM;
                                l.goalentity.bot_attack = TRUE;
                        }
                }
 -              onslaught_controlpoint_updatesprite(l);
 -              l = l.chain;
 +              ons_ControlPoint_UpdateSprite(l);
        }
 -      // count generators owned by each team
 -      t1 = t2 = t3 = t4 = 0;
 -      l = findchain(classname, "onslaught_generator");
 -      while (l)
 +      l = findchain(classname, "ons_captureshield");
 +      while(l)
        {
 -              if (l.iscaptured)
 -              {
 -                      if (l.team == NUM_TEAM_1) t1 = 1;
 -                      if (l.team == NUM_TEAM_2) t2 = 1;
 -                      if (l.team == NUM_TEAM_3) t3 = 1;
 -                      if (l.team == NUM_TEAM_4) t4 = 1;
 -              }
 -              onslaught_generator_updatesprite(l);
 +              l.team = l.enemy.team;
 +              l.colormap = l.enemy.colormap;
                l = l.chain;
        }
 -      // see if multiple teams remain (if not, it's game over)
 -      if (t1 + t2 + t3 + t4 < 2)
 -              dprint("--- game over ---\n");
 -      else
 -              dprint("--- done updating links ---\n");
  }
  
 -float onslaught_controlpoint_can_be_linked(entity cp, float t)
 +
 +// ===================
 +// Main Link Functions
 +// ===================
 +
 +float ons_Link_Send(entity to, float sendflags)
  {
 -      if(t == NUM_TEAM_1)
 +      WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
 +      WriteByte(MSG_ENTITY, sendflags);
 +      if(sendflags & 1)
        {
 -              if(cp.isgenneighbor_red)
 -                      return 2;
 -              if(cp.iscpneighbor_red)
 -                      return 1;
 +              WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
 +              WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
 +              WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
        }
 -      else if(t == NUM_TEAM_2)
 +      if(sendflags & 2)
        {
 -              if(cp.isgenneighbor_blue)
 -                      return 2;
 -              if(cp.iscpneighbor_blue)
 -                      return 1;
 +              WriteCoord(MSG_ENTITY, self.enemy.origin_x);
 +              WriteCoord(MSG_ENTITY, self.enemy.origin_y);
 +              WriteCoord(MSG_ENTITY, self.enemy.origin_z);
        }
 -      return 0;
 -      /*
 -         entity e;
 -      // check to see if this player has a legitimate claim to capture this
 -      // control point - more specifically that there is a captured path of
 -      // points leading back to the team generator
 -      e = findchain(classname, "onslaught_link");
 -      while (e)
 -      {
 -      if (e.goalentity == cp)
 -      {
 -      dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)");
 -      if (e.enemy.islinked)
 -      {
 -      dprint(" which is linked");
 -      if (e.enemy.team == t)
 +      if(sendflags & 4)
        {
 -      dprint(" and has the correct team!\n");
 -      return 1;
 -      }
 -      else
 -      dprint(" but has the wrong team\n");
 -      }
 -      else
 -      dprint("\n");
 +              WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
        }
 -      else if (e.enemy == cp)
 -      {
 -      dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)");
 -      if (e.goalentity.islinked)
 -      {
 -      dprint(" which is linked");
 -      if (e.goalentity.team == t)
 +      return TRUE;
 +}
 +
 +void ons_Link_CheckUpdate()
 +{
 +      // TODO check if the two sides have moved (currently they won't move anyway)
 +      float cc = 0, cc1 = 0, cc2 = 0;
 +      
 +      if(self.goalentity.islinked || self.goalentity.iscaptured) { cc1 = (self.goalentity.team - 1) * 0x01; }
 +      if(self.enemy.islinked || self.enemy.iscaptured) { cc2 = (self.enemy.team - 1) * 0x10; }
 +      
 +      cc = cc1 + cc2;
 +
 +      if(cc != self.clientcolors)
        {
 -      dprint(" and has a team!\n");
 -      return 1;
 -      }
 -      else
 -      dprint(" but has the wrong team\n");
 -      }
 -      else
 -      dprint("\n");
 -      }
 -      e = e.chain;
 +              self.clientcolors = cc;
 +              self.SendFlags |= 4;
        }
 +
 +      self.nextthink = time;
 +}
 +
 +void ons_DelayedLinkSetup()
 +{
 +      self.goalentity = find(world, targetname, self.target);
 +      self.enemy = find(world, targetname, self.target2);
 +      if(!self.goalentity) { objerror("can not find target\n"); }
 +      if(!self.enemy) { objerror("can not find target2\n"); }
 +
 +      ons_debug(strcat(etos(self.goalentity), " linked with ", etos(self.enemy), "\n"));
 +      self.SendFlags |= 3;
 +      self.think = ons_Link_CheckUpdate;
 +      self.nextthink = time;
 +}
 +
 +
 +// =============================
 +// Main Control Point Functions
 +// =============================
 +
 +float ons_ControlPoint_CanBeLinked(entity cp, float teamnumber)
 +{
 +      if(cp.isgenneighbor[teamnumber]) { return 2; }
 +      if(cp.iscpneighbor[teamnumber]) { return 1; }
 +
        return 0;
 -       */
  }
  
 -float onslaught_controlpoint_attackable(entity cp, float t)
 +float ons_ControlPoint_Attackable(entity cp, float teamnumber)
        // -2: SAME TEAM, attackable by enemy!
        // -1: SAME TEAM!
        // 0: off limits
        else if(cp.goalentity)
        {
                // if there's already an icon built, nothing happens
 -              if(cp.team == t)
 +              if(cp.team == teamnumber)
                {
 -                      a = onslaught_controlpoint_can_be_linked(cp, NUM_TEAM_1 + NUM_TEAM_2 - t);
 +                      a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
                        if(a) // attackable by enemy?
                                return -2; // EMERGENCY!
                        return -1;
                }
                // we know it can be linked, so no need to check
                // but...
 -              a = onslaught_controlpoint_can_be_linked(cp, t);
 +              a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
                if(a == 2) // near our generator?
                        return 3; // EMERGENCY!
                return 1;
        else
        {
                // free point
 -              if(onslaught_controlpoint_can_be_linked(cp, t))
 +              if(ons_ControlPoint_CanBeLinked(cp, teamnumber))
                {
 -                      a = onslaught_controlpoint_can_be_linked(cp, NUM_TEAM_1 + NUM_TEAM_2 - t);
 +                      a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t
                        if(a == 2)
                                return 4; // GET THIS ONE NOW!
                        else
        return 0;
  }
  
 -float overtime_msg_time;
 -void onslaught_generator_think()
 +void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
 -      float d;
 -      entity e;
 -      self.nextthink = ceil(time + 1);
 -      if (!gameover)
 +      entity oself;
 +      
 +      if(damage <= 0) { return; }
 +
 +      if (self.owner.isshielded)
        {
 -              if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60)
 -              {
 -                      if (!overtime_msg_time)
 -                      {
 -                              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
 -                              overtime_msg_time = time;
 -                      }
 -                      // self.max_health / 300 gives 5 minutes of overtime.
 -                      // control points reduce the overtime duration.
 -                      sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NORM);
 -                      d = 1;
 -                      e = findchain(classname, "onslaught_controlpoint");
 -                      while (e)
 +              // this is protected by a shield, so ignore the damage
 +              if (time > self.pain_finished)
 +                      if (IS_PLAYER(attacker))
                        {
 -                              if (e.team != self.team)
 -                                      if (e.islinked)
 -                                              d = d + 1;
 -                              e = e.chain;
 +                              play2(attacker, "onslaught/damageblockedbyshield.wav");
 +                              self.pain_finished = time + 1;
 +                              attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
                        }
  
 -                      if(autocvar_g_campaign && autocvar__campaign_testrun)
 -                              d = d * self.max_health;
 -                      else
 -                              d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
 -
 -                      Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
 -              }
 -              else if (overtime_msg_time)
 -                      overtime_msg_time = 0;
 -
 -        if(!self.isshielded && self.wait < time)
 -        {
 -            self.wait = time + 5;
 -            FOR_EACH_REALPLAYER(e)
 -            {
 -                              if(SAME_TEAM(e, self))
 -                {
 -                                      Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED);
 -                    soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE);    // FIXME: Uniqe sound?
 -                }
 -            }
 -        }
 +              return;
        }
 -}
 -
 -void onslaught_generator_ring_spawn(vector org)
 -{
 -      modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25);
 -}
  
 -void onslaught_generator_ray_think()
 -{
 -      self.nextthink = time + 0.05;
 -      if(self.count > 10)
 +      if(IS_PLAYER(attacker))
 +      if(time - ons_notification_time[self.team] > 10)
        {
 -              self.think = SUB_Remove;
 -              return;
 +              play2team(self.team, "onslaught/controlpoint_underattack.wav");
 +              ons_notification_time[self.team] = time;
        }
  
 -      if(self.count > 5)
 -              self.alpha -= 0.1;
 +      self.health = self.health - damage;
 +      if(self.owner.iscaptured)
 +              WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 +      else
-               WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / CP_THINKRATE));
++              WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE));
 +      self.pain_finished = time + 1;
 +      // particles on every hit
 +      pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
 +      //sound on every hit
 +      if (random() < 0.5)
 +              sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM);
        else
 -              self.alpha += 0.1;
 +              sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM);
  
 -      self.scale += 0.2;
 -      self.count +=1;
 -}
 +      if (self.health < 0)
 +      {
 +              sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
 +              pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname);
 +              
 +              PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
 +              PlayerScore_Add(attacker, SP_SCORE, 10);
 +              
 +              self.owner.goalentity = world;
 +              self.owner.islinked = FALSE;
 +              self.owner.iscaptured = FALSE;
 +              self.owner.team = 0;
 +              self.owner.colormap = 1024;
  
 -void onslaught_generator_ray_spawn(vector org)
 -{
 -      entity e;
 -      e = spawn();
 -      setmodel(e, "models/onslaught/ons_ray.md3");
 -      setorigin(e, org);
 -      e.angles = randomvec() * 360;
 -      e.alpha = 0;
 -      e.scale = random() * 5 + 8;
 -      e.think = onslaught_generator_ray_think;
 -      e.nextthink = time + 0.05;
 -}
 +              WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
  
 -void onslaught_generator_shockwave_spawn(vector org)
 -{
 -      shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
 -}
 +              onslaught_updatelinks();
  
 -void onslaught_generator_damage_think()
 -{
 -      if(self.owner.health < 0)
 -      {
 -              self.think = SUB_Remove;
 -              return;
 -      }
 -      self.nextthink = time+0.1;
 +              // Use targets now (somebody make sure this is in the right place..)
 +              oself = self;
 +              self = self.owner;
 +              activator = self;
 +              SUB_UseTargets ();
 +              self = oself;
  
 -      // damaged fx (less probable the more damaged is the generator)
 -      if(random() < 0.9 - self.owner.health / self.owner.max_health)
 -              if(random() < 0.01)
 -              {
 -                      pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
 -                      sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM);
 -              }
 -              else
 -                      pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
 -}
 +              self.owner.waslinked = self.owner.islinked;
 +              if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
 +                      setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad.md3");
 +              //setsize(self, '-32 -32 0', '32 32 8');
  
 -void onslaught_generator_damage_spawn(entity gd_owner)
 -{
 -      entity e;
 -      e = spawn();
 -      e.owner = gd_owner;
 -      e.health = self.owner.health;
 -      setorigin(e, gd_owner.origin);
 -      e.think = onslaught_generator_damage_think;
 -      e.nextthink = time+1;
 +              remove(self);
 +      }
 +      
 +      self.SendFlags |= CPSF_STATUS;
  }
  
 -void onslaught_generator_deaththink()
 +void ons_ControlPoint_Icon_Think()
  {
 -      vector org;
 -      float i;
 -
 -      if (!self.count)
 -              self.count = 40;
 +      entity oself;
-       self.nextthink = time + CP_THINKRATE;
++      self.nextthink = time + ONS_CP_THINKRATE;
  
 -      // White shockwave
 -      if(self.count==40||self.count==20)
 +      if(autocvar_g_onslaught_cp_proxydecap)
        {
 -              onslaught_generator_ring_spawn(self.origin);
 -              sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTEN_NORM);
 -      }
 +        float _enemy_count = 0;
 +        float _friendly_count = 0;
 +        float _dist;
 +        entity _player;
  
 -      // Throw some gibs
 -      if(random() < 0.3)
 -      {
 -              i = random();
 -              if(i < 0.3)
 -                      ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE);
 -              else if(i > 0.7)
 -                      ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE);
 -              else
 -                      ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE);
 -      }
 +        FOR_EACH_PLAYER(_player)
 +        {
 +            if(!_player.deadflag)
 +            {
 +                _dist = vlen(_player.origin - self.origin);
 +                if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
 +                {
 +                                      if(SAME_TEAM(_player, self))
 +                        ++_friendly_count;
 +                    else
 +                        ++_enemy_count;
 +                }
 +            }
 +        }
 +
-         _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * CP_THINKRATE);
-         _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * CP_THINKRATE);
++        _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
++        _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
  
 -      // Spawn fire balls
 -      for(i=0;i < 10;++i)
 +        self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
 +              self.SendFlags |= CPSF_STATUS;
 +        if(self.health <= 0)
 +        {
 +            ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0');
 +            return;
 +        }
 +    }
 +
 +      if (time > self.pain_finished + 5)
        {
 -              org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
 -              pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);
 +              if(self.health < self.max_health)
 +              {
 +                      self.health = self.health + self.count;
 +                      if (self.health >= self.max_health)
 +                              self.health = self.max_health;
 +                      WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 +              }
        }
  
 -      // Short explosion sound + small explosion
 -      if(random() < 0.25)
 +      if(self.owner.islinked != self.owner.waslinked)
        {
 -              te_explosion(self.origin);
 -              sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
 -      }
 +              // unteam the spawnpoint if needed
 +              float t;
 +              t = self.owner.team;
 +              if(!self.owner.islinked)
 +                      self.owner.team = 0;
 +
 +              oself = self;
 +              self = self.owner;
 +              activator = self;
 +              SUB_UseTargets ();
 +              self = oself;
  
 -      // Particles
 -      org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
 -      pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);
 +              self.owner.team = t;
  
 -      // rays
 -      if(random() > 0.25 )
 -      {
 -              onslaught_generator_ray_spawn(self.origin);
 +              self.owner.waslinked = self.owner.islinked;
        }
  
 -      // Final explosion
 -      if(self.count==1)
 +      // damaged fx
 +      if(random() < 0.6 - self.health / self.max_health)
        {
 -              org = self.origin;
 -              te_explosion(org);
 -              onslaught_generator_shockwave_spawn(org);
 -              pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);
 -              sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
 -      }
 -      else
 -              self.nextthink = time + 0.05;
 -
 -      self.count = self.count - 1;
 -}
 +              pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
  
 -void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 -{
 -      float i;
 -      if (damage <= 0)
 -              return;
 -      if(warmup_stage)
 -              return;
 -      if (attacker != self)
 -      {
 -              if (self.isshielded)
 -              {
 -                      // this is protected by a shield, so ignore the damage
 -                      if (time > self.pain_finished)
 -                              if (IS_PLAYER(attacker))
 -                              {
 -                                      play2(attacker, "onslaught/damageblockedbyshield.wav");
 -                                      self.pain_finished = time + 1;
 -                              }
 -                      return;
 -              }
 -              if (time > self.pain_finished)
 -              {
 -                      self.pain_finished = time + 10;
 -                      bprint(Team_ColoredFullName(self.team), " generator under attack!\n");
 -                      play2team(self.team, "onslaught/generator_underattack.wav");
 -              }
 -      }
 -      self.health = self.health - damage;
 -      WaypointSprite_UpdateHealth(self.sprite, self.health);
 -      // choose an animation frame based on health
 -      self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
 -      // see if the generator is still functional, or dying
 -      if (self.health > 0)
 -      {
 -#ifdef ONSLAUGHT_SPAM
 -              float h, lh;
 -              lh = ceil(self.lasthealth / 100) * 100;
 -              h = ceil(self.health / 100) * 100;
 -              if(lh != h)
 -                      bprint(Team_ColoredFullName(self.team), " generator has less than ", ftos(h), " health remaining\n");
 -#endif
 -              self.lasthealth = self.health;
 +              if(random() > 0.8)
 +                      sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM);
 +              else if (random() > 0.5)
 +                      sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM);
        }
 -      else if (!warmup_stage)
 -      {
 -              if (attacker == self)
 -                      bprint(Team_ColoredFullName(self.team), " generator spontaneously exploded due to overtime!\n");
 -              else
 -              {
 -                      string t;
 -                      t = Team_ColoredFullName(attacker.team);
 -                      bprint(Team_ColoredFullName(self.team), " generator destroyed by ", t, "!\n");
 -              }
 -              self.iscaptured = FALSE;
 -              self.islinked = FALSE;
 -              self.isshielded = FALSE;
 -              self.takedamage = DAMAGE_NO; // can't be hurt anymore
 -              self.event_damage = func_null; // won't do anything if hurt
 -              self.count = 0; // reset counter
 -              self.think = onslaught_generator_deaththink; // explosion sequence
 -              self.nextthink = time; // start exploding immediately
 -              self.think(); // do the first explosion now
 +}
  
 -              WaypointSprite_UpdateMaxHealth(self.sprite, 0);
 +void ons_ControlPoint_Icon_BuildThink()
 +{
 +      entity oself;
 +      float a;
  
-       self.nextthink = time + CP_THINKRATE;
 -              onslaught_updatelinks();
 -      }
++      self.nextthink = time + ONS_CP_THINKRATE;
  
 -      if(self.health <= 0)
 -              setmodel(self, "models/onslaught/generator_dead.md3");
 -      else if(self.health < self.max_health * 0.10)
 -              setmodel(self, "models/onslaught/generator_dmg9.md3");
 -      else if(self.health < self.max_health * 0.20)
 -              setmodel(self, "models/onslaught/generator_dmg8.md3");
 -      else if(self.health < self.max_health * 0.30)
 -              setmodel(self, "models/onslaught/generator_dmg7.md3");
 -      else if(self.health < self.max_health * 0.40)
 -              setmodel(self, "models/onslaught/generator_dmg6.md3");
 -      else if(self.health < self.max_health * 0.50)
 -              setmodel(self, "models/onslaught/generator_dmg5.md3");
 -      else if(self.health < self.max_health * 0.60)
 -              setmodel(self, "models/onslaught/generator_dmg4.md3");
 -      else if(self.health < self.max_health * 0.70)
 -              setmodel(self, "models/onslaught/generator_dmg3.md3");
 -      else if(self.health < self.max_health * 0.80)
 -              setmodel(self, "models/onslaught/generator_dmg2.md3");
 -      else if(self.health < self.max_health * 0.90)
 -              setmodel(self, "models/onslaught/generator_dmg1.md3");
 -      setsize(self, '-52 -52 -14', '52 52 75');
 +      // only do this if there is power
 +      a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team);
 +      if(!a)
 +              return;
  
 -      // Throw some flaming gibs on damage, more damage = more chance for gib
 -      if(random() < damage/220)
 -      {
 -              sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
 -              i = random();
 -              if(i < 0.3)
 -                      ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);
 -              else if(i > 0.7)
 -                      ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);
 -              else
 -                      ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);
 -      }
 -      else
 +      self.health = self.health + self.count;
 +      
 +      self.SendFlags |= CPSF_STATUS;
 +
 +      if (self.health >= self.max_health)
        {
 -              // particles on every hit
 -              pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
 +              self.health = self.max_health;
-               self.count = autocvar_g_onslaught_cp_regen * CP_THINKRATE; // slow repair rate from now on
++              self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
 +              self.think = ons_ControlPoint_Icon_Think;
 +              sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM);
 +              self.owner.iscaptured = TRUE;
 +              self.solid = SOLID_BBOX;
  
 -              //sound on every hit
 -              if (random() < 0.5)
 -                      sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM);
 -              else
 -                      sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);
 -      }
 +              pointparticles(particleeffectnum(sprintf("%s_cap", Static_Team_ColorName_Lower(self.owner.team))), self.owner.origin, '0 0 0', 1);
  
 -      //throw some gibs on damage
 -      if(random() < damage/200+0.2)
 -              if(random() < 0.5)
 -                      ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);
 -}
 +              WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
 +              WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
  
 -// update links after a delay
 -void onslaught_generator_delayed()
 -{
 -      onslaught_updatelinks();
 -      // now begin normal thinking
 -      self.think = onslaught_generator_think;
 -      self.nextthink = time;
 -}
 +              if(IS_PLAYER(self.owner.ons_toucher))
 +              {
 +                      Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message);
 +                      Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message);
 +                      Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message);
 +                      PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1);
 +                      PlayerTeamScore_AddScore(self.owner.ons_toucher, 10);
 +              }
 +              
 +              self.owner.ons_toucher = world;
  
 -string onslaught_generator_waypointsprite_for_team(entity e, float t)
 -{
 -      if(t == e.team)
 -      {
 -              if(e.team == NUM_TEAM_1)
 -                      return "ons-gen-red";
 -              else if(e.team == NUM_TEAM_2)
 -                      return "ons-gen-blue";
 +              onslaught_updatelinks();
 +
 +              // Use targets now (somebody make sure this is in the right place..)
 +              oself = self;
 +              self = self.owner;
 +              activator = self;
 +              SUB_UseTargets ();
 +              self = oself;
 +              
 +              self.SendFlags |= CPSF_SETUP;
        }
 -      if(e.isshielded)
 -              return "ons-gen-shielded";
 -      if(e.team == NUM_TEAM_1)
 -              return "ons-gen-red";
 -      else if(e.team == NUM_TEAM_2)
 -              return "ons-gen-blue";
 -      return "";
 +      if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
 +              setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad2.md3");
 +              
 +      if(random() < 0.9 - self.health / self.max_health)
 +              pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
  }
  
 -void onslaught_generator_updatesprite(entity e)
 +void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
  {
 -      string s1, s2, s3;
 -      s1 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_1);
 -      s2 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_2);
 -      s3 = onslaught_generator_waypointsprite_for_team(e, -1);
 -      WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
 +      entity e = spawn();
 +      
 +      setsize(e, CPICON_MIN, CPICON_MAX);
 +      setorigin(e, cp.origin + CPICON_OFFSET);
 +      
 +      e.classname = "onslaught_controlpoint_icon";
 +      e.owner = cp;
 +      e.max_health = autocvar_g_onslaught_cp_health;
 +      e.health = autocvar_g_onslaught_cp_buildhealth;
 +      e.solid = SOLID_NOT;
 +      e.takedamage = DAMAGE_AIM;
 +      e.bot_attack = TRUE;
 +      e.event_damage = ons_ControlPoint_Icon_Damage;
 +      e.team = player.team;
 +      e.colormap = 1024 + (e.team - 1) * 17;
-       e.count = (e.max_health - e.health) * CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
++      e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
 +      
 +      sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM);
 +      
 +      cp.goalentity = e;
 +      cp.team = e.team;
 +      cp.colormap = e.colormap;
  
 -      if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
 -      {
 -              e.lastteam = e.team + 2;
 -              e.lastshielded = e.isshielded;
 -              if(e.lastshielded)
 -              {
 -                      if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
 -                      else
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
 -              }
 -              else
 -              {
 -                      if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));
 -                      else
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
 -              }
 -              WaypointSprite_Ping(e.sprite);
 -      }
 +      pointparticles(particleeffectnum(sprintf("%sflag_touch", Static_Team_ColorName_Lower(player.team))), e.origin, '0 0 0', 1);
 +
-       WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / CP_THINKRATE));
++      WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
 +      WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
 +      cp.sprite.SendFlags |= 16;
 +
 +      onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
  }
  
 -string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
 +string ons_ControlPoint_Waypoint(entity e)
  {
        float a;
 -      if(t != -1)
 +      if(e.team)
        {
 -              a = onslaught_controlpoint_attackable(e, t);
 -              if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
 -              {
 -                      if(e.team == NUM_TEAM_1)
 -                              return "ons-cp-atck-red";
 -                      else if(e.team == NUM_TEAM_2)
 -                              return "ons-cp-atck-blue";
 -                      else
 -                              return "ons-cp-atck-neut";
 -              }
 -              else if(a == -2) // DEFEND THIS ONE NOW
 -              {
 -                      if(e.team == NUM_TEAM_1)
 -                              return "ons-cp-dfnd-red";
 -                      else if(e.team == NUM_TEAM_2)
 -                              return "ons-cp-dfnd-blue";
 -              }
 -              else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
 -              {
 -                      if(e.team == NUM_TEAM_1)
 -                              return "ons-cp-red";
 -                      else if(e.team == NUM_TEAM_2)
 -                              return "ons-cp-blue";
 -              }
 -              else if(a == 2) // touch it
 -                      return "ons-cp-neut";
 +              a = ons_ControlPoint_Attackable(e, e.team);
 +              
 +              if(a == -2) { return "ons-cp-dfnd"; } // defend now
 +              if(a == -1 || a == 1 || a == 2) { return "ons-cp"; } // touch
 +              if(a == 3 || a == 4) { return "ons-cp-atck"; } // attack
        }
        else
 -      {
 -              if(e.team == NUM_TEAM_1)
 -                      return "ons-cp-red";
 -              else if(e.team == NUM_TEAM_2)
 -                      return "ons-cp-blue";
 -              else
 -                      return "ons-cp-neut";
 -      }
 +              return "ons-cp";
 +
        return "";
  }
  
 -void onslaught_controlpoint_updatesprite(entity e)
 +void ons_ControlPoint_UpdateSprite(entity e)
  {
 -      string s1, s2, s3;
 -      s1 = onslaught_controlpoint_waypointsprite_for_team(e, NUM_TEAM_1);
 -      s2 = onslaught_controlpoint_waypointsprite_for_team(e, NUM_TEAM_2);
 -      s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);
 -      WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
 +      string s1;
 +      s1 = ons_ControlPoint_Waypoint(e);
 +      WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
  
        float sh;
 -      sh = !(onslaught_controlpoint_can_be_linked(e, NUM_TEAM_1) || onslaught_controlpoint_can_be_linked(e, NUM_TEAM_2));
 +      sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4));
  
        if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
        {
                }
                if(e.lastshielded)
                {
 -                      if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
 +                      if(e.team)
                                WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
                        else
                                WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
                }
                else
                {
 -                      if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
 +                      if(e.team)
                                WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));
                        else
                                WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
        }
  }
  
 -void onslaught_generator_reset()
 +void ons_ControlPoint_Touch()
  {
 -      self.team = self.team_saved;
 -      self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
 -      self.takedamage = DAMAGE_AIM;
 -      self.bot_attack = TRUE;
 -      self.iscaptured = TRUE;
 -      self.islinked = TRUE;
 -      self.isshielded = TRUE;
 -      self.enemy.solid = SOLID_NOT;
 -      self.think = onslaught_generator_delayed;
 -      self.nextthink = time + 0.2;
 -      setmodel(self, "models/onslaught/generator.md3");
 -      setsize(self, '-52 -52 -14', '52 52 75');
 -
 -      if(!self.noalign)
 +      entity toucher = other;
 +      float attackable;
 +      
 +      if((toucher.vehicle_flags & VHF_ISVEHICLE) && toucher.owner)
 +      if(autocvar_g_onslaught_allow_vehicle_touch)
 +              toucher = toucher.owner;
 +      else
 +              return;
 +              
 +      if(!IS_PLAYER(toucher)) { return; }
++      if(toucher.frozen) { return; }
 +      if(toucher.deadflag != DEAD_NO) { return; }
 +      
 +      if ( SAME_TEAM(self,toucher) )
 +      if ( self.iscaptured )
        {
 -              setorigin(self, self.origin + '0 0 20');
 -              droptofloor();
 +              if(time <= toucher.teleport_antispam)
 +                      Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time));
 +              else
 +                      Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
        }
 +              
 +      attackable = ons_ControlPoint_Attackable(self, toucher.team);
 +      if(attackable != 2 && attackable != 4)
 +              return;
 +      // we've verified that this player has a legitimate claim to this point,
 +      // so start building the captured point icon (which only captures this
 +      // point if it successfully builds without being destroyed first)
 +      ons_ControlPoint_Icon_Spawn(self, toucher);
 +      
 +      self.ons_toucher = toucher;
  
 -      WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 -      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +      onslaught_updatelinks();
  }
  
 -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
 -  Base generator.
 -
 -  spawnfunc_onslaught_link entities can target this.
 -
 -keys:
 -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
 -"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
 - */
 -void spawnfunc_onslaught_generator()
 +void ons_ControlPoint_Think()
  {
-       self.nextthink = time + CP_THINKRATE;
 -      if (!g_onslaught)
 -      {
 -              remove(self);
 -              return;
 -      }
 -
 -      //entity e;
 -      precache_model("models/onslaught/generator.md3");
 -      precache_model("models/onslaught/generator_shield.md3");
 -      precache_model("models/onslaught/generator_dmg1.md3");
 -      precache_model("models/onslaught/generator_dmg2.md3");
 -      precache_model("models/onslaught/generator_dmg3.md3");
 -      precache_model("models/onslaught/generator_dmg4.md3");
 -      precache_model("models/onslaught/generator_dmg5.md3");
 -      precache_model("models/onslaught/generator_dmg6.md3");
 -      precache_model("models/onslaught/generator_dmg7.md3");
 -      precache_model("models/onslaught/generator_dmg8.md3");
 -      precache_model("models/onslaught/generator_dmg9.md3");
 -      precache_model("models/onslaught/generator_dead.md3");
 -      precache_model("models/onslaught/shockwave.md3");
 -      precache_model("models/onslaught/shockwavetransring.md3");
 -      precache_model("models/onslaught/gen_gib1.md3");
 -      precache_model("models/onslaught/gen_gib2.md3");
 -      precache_model("models/onslaught/gen_gib3.md3");
 -      precache_model("models/onslaught/ons_ray.md3");
 -      precache_sound("onslaught/generator_decay.wav");
 -      precache_sound("weapons/grenade_impact.wav");
 -      precache_sound("weapons/rocket_impact.wav");
 -      precache_sound("onslaught/generator_underattack.wav");
 -      precache_sound("onslaught/shockwave.wav");
 -      precache_sound("onslaught/ons_hit1.wav");
 -      precache_sound("onslaught/ons_hit2.wav");
 -      precache_sound("onslaught/electricity_explode.wav");
 -      if (!self.team)
 -              objerror("team must be set");
 -
 -      if(self.team == NUM_TEAM_1)
 -        ons_red_generator = self;
++      self.nextthink = time + ONS_CP_THINKRATE;
 +      CSQCMODEL_AUTOUPDATE();
 +}
  
 -      if(self.team == NUM_TEAM_2)
 -        ons_blue_generator = self;
 +void ons_ControlPoint_Reset()
 +{
 +      if(self.goalentity)
 +              remove(self.goalentity);
  
 -      self.team_saved = self.team;
 -      self.colormap = 1024 + (self.team - 1) * 17;
 -      self.solid = SOLID_BBOX;
 -      self.movetype = MOVETYPE_NONE;
 -      self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
 -      setmodel(self, "models/onslaught/generator.md3");
 -      setsize(self, '-52 -52 -14', '52 52 75');
 -      setorigin(self, self.origin);
 -      self.takedamage = DAMAGE_AIM;
 -      self.bot_attack = TRUE;
 -      self.event_damage = onslaught_generator_damage;
 -      self.iscaptured = TRUE;
 -      self.islinked = TRUE;
 +      self.goalentity = world;
 +      self.team = 0;
 +      self.colormap = 1024;
 +      self.iscaptured = FALSE;
 +      self.islinked = FALSE;
        self.isshielded = TRUE;
 -      // helper entity that create fx when generator is damaged
 -      onslaught_generator_damage_spawn(self);
 -      // spawn shield model which indicates whether this can be damaged
 -      self.enemy = spawn();
 -      setattachment(self.enemy , self, "");
 -      self.enemy.classname = "onslaught_generator_shield";
 -      self.enemy.solid = SOLID_NOT;
 -      self.enemy.movetype = MOVETYPE_NONE;
 -      self.enemy.effects = EF_ADDITIVE;
 -      setmodel(self.enemy, "models/onslaught/generator_shield.md3");
 -      //setorigin(e, self.origin);
 -      self.enemy.colormap = self.colormap;
 -      self.enemy.team = self.team;
 -      //self.think = onslaught_generator_delayed;
 -      //self.nextthink = time + 0.2;
 -      InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
 -
 -      WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
 -      WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY);
 -      WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 -      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +      self.think = ons_ControlPoint_Think;
 +      self.ons_toucher = world;
-       self.nextthink = time + CP_THINKRATE;
++      self.nextthink = time + ONS_CP_THINKRATE;
 +      setmodel_fixsize(self, "models/onslaught/controlpoint_pad.md3");
  
 -      waypoint_spawnforitem(self);
 +      WaypointSprite_UpdateMaxHealth(self.sprite, 0);
 +      WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
  
        onslaught_updatelinks();
  
 -      self.reset = onslaught_generator_reset;
 +      activator = self;
 +      SUB_UseTargets(); // to reset the structures, playerspawns etc.
 +
 +      CSQCMODEL_AUTOUPDATE();
  }
  
 -.float waslinked;
 -.float cp_bob_spd;
 -.vector cp_origin, cp_bob_origin, cp_bob_dmg;
 +void ons_DelayedControlPoint_Setup(void)
 +{
 +      onslaught_updatelinks();
 +      
 +      // captureshield setup
 +      ons_CaptureShield_Spawn(self, FALSE);
  
 -float ons_notification_time_team1;
 -float ons_notification_time_team2;
 +      CSQCMODEL_AUTOINIT();
 +}
  
 -void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +void ons_ControlPoint_Setup(entity cp)
  {
 -      entity oself;
 -      float nag;
 +      // declarations
 +      self = cp; // for later usage with droptofloor()
 +      
 +      // main setup
 +      cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist
 +      ons_worldcplist = cp;
 +      
 +      cp.netname = "Control point";
 +      cp.team = 0;
 +      cp.solid = SOLID_BBOX;
 +      cp.movetype = MOVETYPE_NONE;
 +      cp.touch = ons_ControlPoint_Touch;
 +      cp.think = ons_ControlPoint_Think;
-       cp.nextthink = time + CP_THINKRATE;
++      cp.nextthink = time + ONS_CP_THINKRATE;
 +      cp.reset = ons_ControlPoint_Reset;
 +      cp.colormap = 1024;
 +      cp.iscaptured = FALSE;
 +      cp.islinked = FALSE;
 +      cp.isshielded = TRUE;
 +      
 +      if(cp.message == "") { cp.message = "a"; }
  
 -      if (damage <= 0)
 -              return;
 -      if (self.owner.isshielded)
 +      // precache - TODO: clean up!
 +      precache_model("models/onslaught/controlpoint_pad.md3");
 +      precache_model("models/onslaught/controlpoint_pad2.md3");
 +      precache_model("models/onslaught/controlpoint_shield.md3");
 +      precache_model("models/onslaught/controlpoint_icon.md3");
 +      precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
 +      precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
 +      precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
 +      precache_model("models/onslaught/controlpoint_icon_gib1.md3");
 +      precache_model("models/onslaught/controlpoint_icon_gib2.md3");
 +      precache_model("models/onslaught/controlpoint_icon_gib4.md3");
 +      precache_sound("onslaught/controlpoint_build.wav");
 +      precache_sound("onslaught/controlpoint_built.wav");
 +      precache_sound("weapons/grenade_impact.wav");
 +      precache_sound("onslaught/damageblockedbyshield.wav");
 +      precache_sound("onslaught/controlpoint_underattack.wav");
 +      precache_sound("onslaught/ons_spark1.wav");
 +      precache_sound("onslaught/ons_spark2.wav");
 +      
 +      // appearence
 +      setmodel_fixsize(cp, "models/onslaught/controlpoint_pad.md3");
 +      
 +      // control point placement
 +      if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location
        {
 -              // this is protected by a shield, so ignore the damage
 -              if (time > self.pain_finished)
 -                      if (IS_PLAYER(attacker))
 -                      {
 -                              play2(attacker, "onslaught/damageblockedbyshield.wav");
 -                              self.pain_finished = time + 1;
 -                      }
 -              return;
 +              cp.noalign = TRUE;
 +              cp.movetype = MOVETYPE_NONE;
        }
 -
 -      if (IS_PLAYER(attacker))
 +      else // drop to floor, automatically find a platform and set that as spawn origin
        {
 -              nag = FALSE;
 -              if(self.team == NUM_TEAM_1)
 -              {
 -                      if(time - ons_notification_time_team1 > 10)
 -                      {
 -                              nag = TRUE;
 -                              ons_notification_time_team1 = time;
 -                      }
 -              }
 -              else if(self.team == NUM_TEAM_2)
 +              setorigin(cp, cp.origin + '0 0 20');
 +              cp.noalign = FALSE;
 +              self = cp;
 +              droptofloor();
 +              cp.movetype = MOVETYPE_TOSS;
 +      }
 +      
 +      // waypointsprites
 +      WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0');
 +      WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
 +      
 +      InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION);
 +}
 +
 +
 +// =========================
 +// Main Generator Functions
 +// =========================
 +
 +string ons_Generator_Waypoint(entity e)
 +{
 +      if(e.isshielded)
 +              return "ons-gen-shielded";
 +      return "ons-gen";
 +}
 +
 +void ons_Generator_UpdateSprite(entity e)
 +{
 +      string s1;
 +      s1 = ons_Generator_Waypoint(e);
 +      WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
 +
 +      if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
 +      {
 +              e.lastteam = e.team + 2;
 +              e.lastshielded = e.isshielded;
 +              if(e.lastshielded)
                {
 -                      if(time - ons_notification_time_team2 > 10)
 -                      {
 -                              nag = TRUE;
 -                              ons_notification_time_team2 = time;
 -                      }
 +                      if(e.team)
 +                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
 +                      else
 +                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
                }
                else
 -                      nag = TRUE;
 -
 -              if(nag)
 -                      play2team(self.team, "onslaught/controlpoint_underattack.wav");
 +              {
 +                      if(e.team)
 +                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));
 +                      else
 +                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
 +              }
 +              WaypointSprite_Ping(e.sprite);
        }
 +}
 +
 +void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if(damage <= 0) { return; }
 +      if(warmup_stage || gameover) { return; }
 +      if(!round_handler_IsRoundStarted()) { return; }
  
 +      if (attacker != self)
 +      {
 +              if (self.isshielded)
 +              {
 +                      // this is protected by a shield, so ignore the damage
 +                      if (time > self.pain_finished)
 +                              if (IS_PLAYER(attacker))
 +                              {
 +                                      play2(attacker, "onslaught/damageblockedbyshield.wav");
 +                                      attacker.typehitsound += 1;
 +                                      self.pain_finished = time + 1;
 +                              }
 +                      return;
 +              }
 +              if (time > self.pain_finished)
 +              {
 +                      self.pain_finished = time + 10;
 +                      entity head;
 +                      FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); }
 +                      play2team(self.team, "onslaught/generator_underattack.wav");
 +              }
 +      }
        self.health = self.health - damage;
 -      if(self.owner.iscaptured)
 -              WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 -      else
 -              WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
 -      self.pain_finished = time + 1;
 -      self.punchangle = (2 * randomvec() - '1 1 1') * 45;
 -      self.cp_bob_dmg_z = (2 * random() - 1) * 15;
 -      // colormod flash when shot
 -      self.colormod = '2 2 2';
 -      // particles on every hit
 -      pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
 -      //sound on every hit
 -      if (random() < 0.5)
 -              sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM);
 +      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +      // choose an animation frame based on health
 +      self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
 +      // see if the generator is still functional, or dying
 +      if (self.health > 0)
 +      {
 +              self.lasthealth = self.health;
 +      }
        else
 -              sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM);
 -
 -      if (self.health < 0)
        {
 -              sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
 -              pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
 +              if (attacker == self)
 +                      Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_));
 +              else
                {
 -                      string t;
 -                      t = Team_ColoredFullName(attacker.team);
 -                      bprint(Team_ColoredFullName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
 -                      ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
 +                      Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_));
 +                      PlayerScore_Add(attacker, SP_SCORE, 100);
                }
 -              self.owner.goalentity = world;
 -              self.owner.islinked = FALSE;
 -              self.owner.iscaptured = FALSE;
 -              self.owner.team = 0;
 -              self.owner.colormap = 1024;
 +              self.iscaptured = FALSE;
 +              self.islinked = FALSE;
 +              self.isshielded = FALSE;
 +              self.takedamage = DAMAGE_NO; // can't be hurt anymore
 +              self.event_damage = func_null; // won't do anything if hurt
 +              self.count = 0; // reset counter
 +              self.think = func_null;
 +              self.nextthink = 0;
 +              //self.think(); // do the first explosion now
  
 -              WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
 +              WaypointSprite_UpdateMaxHealth(self.sprite, 0);
 +              WaypointSprite_Ping(self.sprite);
 +              //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor
  
                onslaught_updatelinks();
 +      }
  
 -              // Use targets now (somebody make sure this is in the right place..)
 -              oself = self;
 -              self = self.owner;
 -              activator = self;
 -              SUB_UseTargets ();
 -              self = oself;
 -
 -
 -              self.owner.waslinked = self.owner.islinked;
 -              if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
 -                      setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
 -              //setsize(self, '-32 -32 0', '32 32 8');
 +      // Throw some flaming gibs on damage, more damage = more chance for gib
 +      if(random() < damage/220)
 +      {
 +              sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
 +      }
 +      else
 +      {
 +              // particles on every hit
 +              pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
  
 -              remove(self);
 +              //sound on every hit
 +              if (random() < 0.5)
 +                      sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM);
 +              else
 +                      sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);
        }
 +
 +      self.SendFlags |= GSF_STATUS;
  }
  
 -void onslaught_controlpoint_icon_think()
 +void ons_GeneratorThink()
  {
 -      entity oself;
 -      self.nextthink = time + sys_frametime;
 -
 -      if(autocvar_g_onslaught_cp_proxydecap)
 +      entity e;
 +      self.nextthink = time + GEN_THINKRATE;
 +      if (!gameover)
        {
 -        float _enemy_count = 0;
 -        float _friendly_count = 0;
 -        float _dist;
 -        entity _player;
 -
 -        FOR_EACH_PLAYER(_player)
 +        if(!self.isshielded && self.wait < time)
          {
 -            if(!_player.deadflag)
 +            self.wait = time + 5;
 +            FOR_EACH_REALPLAYER(e)
              {
 -                _dist = vlen(_player.origin - self.origin);
 -                if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
 -                {
 -                    if(_player.team == self.team)
 -                        ++_friendly_count;
 -                    else
 -                        ++_enemy_count;
 +                              if(SAME_TEAM(e, self))
 +                              {
 +                                      Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM);
 +                    soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE);    // FIXME: unique sound?
                  }
 +                              else
 +                                      Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_));
              }
          }
 +      }
 +}
 +
 +void ons_GeneratorReset()
 +{
 +      self.team = self.team_saved;
 +      self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
 +      self.takedamage = DAMAGE_AIM;
 +      self.bot_attack = TRUE;
 +      self.iscaptured = TRUE;
 +      self.islinked = TRUE;
 +      self.isshielded = TRUE;
 +      self.event_damage = ons_GeneratorDamage;
 +      self.think = ons_GeneratorThink;
 +      self.nextthink = time + GEN_THINKRATE;
 +      
 +      Net_LinkEntity(self, FALSE, 0, generator_send);
 +      
 +      self.SendFlags = GSF_SETUP; // just incase
 +      self.SendFlags |= GSF_STATUS;
  
 -        _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime);
 -        _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * sys_frametime);
 +      WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 +      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +      WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
 +      
 +      onslaught_updatelinks();
 +}
  
 -        self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
 -        if(self.health <= 0)
 -        {
 -            onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0');
 -            return;
 -        }
 -    }
 +void ons_DelayedGeneratorSetup()
 +{
 +      // bot waypoints
 +      waypoint_spawnforitem_force(self, self.origin);
 +      self.nearestwaypointtimeout = 0; // activate waypointing again
 +      self.bot_basewaypoint = self.nearestwaypoint;
  
 -      if (time > self.pain_finished + 5)
 +      // captureshield setup
 +      ons_CaptureShield_Spawn(self, TRUE);
 +      
 +      onslaught_updatelinks();
 +      
 +      Net_LinkEntity(self, FALSE, 0, generator_send);
 +}
 +
 +
 +void onslaught_generator_touch()
 +{
 +      if ( IS_PLAYER(other) )
 +      if ( SAME_TEAM(self,other) )
 +      if ( self.iscaptured )
        {
 -              if(self.health < self.max_health)
 -              {
 -                      self.health = self.health + self.count;
 -                      if (self.health >= self.max_health)
 -                              self.health = self.max_health;
 -                      WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 -              }
 +              Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT);
        }
 -      if (self.health < self.max_health * 0.25)
 -              setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
 -      else if (self.health < self.max_health * 0.50)
 -              setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
 -      else if (self.health < self.max_health * 0.75)
 -              setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
 -      else if (self.health < self.max_health * 0.90)
 -              setmodel(self, "models/onslaught/controlpoint_icon.md3");
 -      // colormod flash when shot
 -      self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
 +}
  
 -      if(self.owner.islinked != self.owner.waslinked)
 -      {
 -              // unteam the spawnpoint if needed
 -              float t;
 -              t = self.owner.team;
 -              if(!self.owner.islinked)
 -                      self.owner.team = 0;
 +void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc
 +{
 +      // declarations
 +      float teamnumber = gen.team;
 +      self = gen; // for later usage with droptofloor()
 +      
 +      // main setup
 +      gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist
 +      ons_worldgeneratorlist = gen;
 +      
 +      gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber));
 +      gen.classname = "onslaught_generator";
 +      gen.solid = SOLID_BBOX;
 +      gen.team_saved = teamnumber;
 +      gen.movetype = MOVETYPE_NONE;
 +      gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health;
 +      gen.takedamage = DAMAGE_AIM;
 +      gen.bot_attack = TRUE;
 +      gen.event_damage = ons_GeneratorDamage;
 +      gen.reset = ons_GeneratorReset;
 +      gen.think = ons_GeneratorThink;
 +      gen.nextthink = time + GEN_THINKRATE;
 +      gen.iscaptured = TRUE;
 +      gen.islinked = TRUE;
 +      gen.isshielded = TRUE;
 +      gen.touch = onslaught_generator_touch;
 +      
 +      // precache - TODO: clean up!
 +      precache_model("models/onslaught/generator_shield.md3");
 +      precache_model("models/onslaught/gen_gib1.md3");
 +      precache_model("models/onslaught/gen_gib2.md3");
 +      precache_model("models/onslaught/gen_gib3.md3");
 +      precache_sound("onslaught/generator_decay.wav");
 +      precache_sound("weapons/grenade_impact.wav");
 +      precache_sound("weapons/rocket_impact.wav");
 +      precache_sound("onslaught/generator_underattack.wav");
 +      precache_sound("onslaught/shockwave.wav");
 +      precache_sound("onslaught/ons_hit1.wav");
 +      precache_sound("onslaught/ons_hit2.wav");
 +      precache_sound("onslaught/generator_underattack.wav");
 +      
 +      // appearence
 +      // model handled by CSQC
 +      setsize(gen, GENERATOR_MIN, GENERATOR_MAX);
 +      setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET));
 +      gen.colormap = 1024 + (teamnumber - 1) * 17;
 +      
 +      // generator placement
 +      self = gen;
 +      droptofloor();
 +      
 +      // waypointsprites
 +      WaypointSprite_SpawnFixed(string_null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE, '0 0 0');
 +      WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
 +      WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 +      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +      
 +      InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION);
 +}
  
 -              oself = self;
 -              self = self.owner;
 -              activator = self;
 -              SUB_UseTargets ();
 -              self = oself;
  
 -              self.owner.team = t;
 +// ===============
 +//  Round Handler
 +// ===============
  
 -              self.owner.waslinked = self.owner.islinked;
 +float total_generators, redowned, blueowned, yellowowned, pinkowned;
 +void Onslaught_count_generators()
 +{
 +      entity e;
 +      total_generators = redowned = blueowned = yellowowned = pinkowned = 0;
 +      for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
 +      {
 +              ++total_generators;
 +              redowned += (e.team == NUM_TEAM_1 && e.health > 0);
 +              blueowned += (e.team == NUM_TEAM_2 && e.health > 0);
 +              yellowowned += (e.team == NUM_TEAM_3 && e.health > 0);
 +              pinkowned += (e.team == NUM_TEAM_4 && e.health > 0);
        }
 +}
  
 -      if (self.punchangle_x > 0)
 +float Onslaught_GetWinnerTeam()
 +{
 +      float winner_team = 0;
 +      if(redowned > 0)
 +              winner_team = NUM_TEAM_1;
 +      if(blueowned > 0)
        {
 -              self.punchangle_x = self.punchangle_x - 60 * sys_frametime;
 -              if (self.punchangle_x < 0)
 -                      self.punchangle_x = 0;
 +              if(winner_team) return 0;
 +              winner_team = NUM_TEAM_2;
        }
 -      else if (self.punchangle_x < 0)
 +      if(yellowowned > 0)
        {
 -              self.punchangle_x = self.punchangle_x + 60 * sys_frametime;
 -              if (self.punchangle_x > 0)
 -                      self.punchangle_x = 0;
 +              if(winner_team) return 0;
 +              winner_team = NUM_TEAM_3;
        }
 -
 -      if (self.punchangle_y > 0)
 +      if(pinkowned > 0)
        {
 -              self.punchangle_y = self.punchangle_y - 60 * sys_frametime;
 -              if (self.punchangle_y < 0)
 -                      self.punchangle_y = 0;
 +              if(winner_team) return 0;
 +              winner_team = NUM_TEAM_4;
        }
 -      else if (self.punchangle_y < 0)
 +      if(winner_team)
 +              return winner_team;
 +      return -1; // no generators left?
 +}
 +
 +#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
 +#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1)
 +float Onslaught_CheckWinner()
 +{
 +      entity e;
 +      
 +      if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0))
        {
 -              self.punchangle_y = self.punchangle_y + 60 * sys_frametime;
 -              if (self.punchangle_y > 0)
 -                      self.punchangle_y = 0;
 +              ons_stalemate = TRUE;
 +
 +              if (!wpforenemy_announced)
 +              {
 +                      Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
 +                      sound(world, CH_INFO, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NONE);
 +
 +                      wpforenemy_announced = TRUE;
 +              }
 +
 +              entity tmp_entity; // temporary entity
 +              float d;
 +              for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay)
 +              {
 +                      // tmp_entity.max_health / 300 gives 5 minutes of overtime.
 +                      // control points reduce the overtime duration.
 +                      d = 1;
 +                      for(e = ons_worldcplist; e; e = e.ons_worldcpnext)
 +                      {
 +                              if(DIFF_TEAM(e, tmp_entity))
 +                              if(e.islinked)
 +                                      d = d + 1;
 +                      }
 +
 +                      if(autocvar_g_campaign && autocvar__campaign_testrun)
 +                              d = d * tmp_entity.max_health;
 +                      else
 +                              d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
 +
 +                      Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER, tmp_entity.origin, '0 0 0');
 +                      
 +                      tmp_entity.sprite.SendFlags |= 16;
 +
 +                      tmp_entity.ons_overtime_damagedelay = time + 1;
 +              }
        }
 +      else { wpforenemy_announced = FALSE; ons_stalemate = FALSE; }
 +
 +      Onslaught_count_generators();
 +      
 +      if(ONS_OWNED_GENERATORS_OK())
 +              return 0;
 +
 +      float winner_team = Onslaught_GetWinnerTeam();
  
 -      if (self.punchangle_z > 0)
 +      if(winner_team > 0)
        {
 -              self.punchangle_z = self.punchangle_z - 60 * sys_frametime;
 -              if (self.punchangle_z < 0)
 -                      self.punchangle_z = 0;
 +              Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
 +              TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1);
        }
 -      else if (self.punchangle_z < 0)
 +      else if(winner_team == -1)
        {
 -              self.punchangle_z = self.punchangle_z + 60 * sys_frametime;
 -              if (self.punchangle_z > 0)
 -                      self.punchangle_z = 0;
 +              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
        }
 +      
 +      ons_stalemate = FALSE;
  
 -      self.angles_x = self.punchangle_x;
 -      self.angles_y = self.punchangle_y + self.mangle_y;
 -      self.angles_z = self.punchangle_z;
 -      self.mangle_y = self.mangle_y + 45 * sys_frametime;
 +      play2all(sprintf("ctf/%s_capture.wav", Static_Team_ColorName_Lower(winner_team)));
 +      
 +      round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
 +      
 +      FOR_EACH_PLAYER(e)
 +      {
 +              e.ons_roundlost = TRUE;
 +              e.player_blocked = TRUE;
 -      self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd));
 -      self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime;
 -      if(self.cp_bob_dmg_z > 0)
 -              self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * sys_frametime;
 -      else
 -              self.cp_bob_dmg_z = 0;
 -      setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
++              nades_Clear(e);
 +      }
-       
 -      // damaged fx
 -      if(random() < 0.6 - self.health / self.max_health)
 +      return 1;
 +}
 +
 +float Onslaught_CheckPlayers()
 +{
 +      return 1;
 +}
 +
 +void Onslaught_RoundStart()
 +{
 +      entity tmp_entity;
 +      FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = FALSE; }
 +
 +      for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
 +              tmp_entity.sprite.SendFlags |= 16;
 +
 +      for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
 +              tmp_entity.sprite.SendFlags |= 16;
 +}
 +
 +
 +// ================
 +// Bot player logic
 +// ================
 +
 +// NOTE: LEGACY CODE, needs to be re-written!
 +
 +void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
 +{
 +      entity head;
 +      float t, i, c, needarmor = FALSE, needweapons = FALSE;
 +
 +      // Needs armor/health?
 +      if(self.health<100)
 +              needarmor = TRUE;
 +
 +      // Needs weapons?
 +      c = 0;
 +      for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
        {
 -              pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
 +              // Find weapon
 +              if(self.weapons & WepSet_FromWeapon(i))
 +              if(++c>=4)
 +                      break;
 +      }
  
 -              if(random() > 0.8)
 -                      sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM);
 -              else if (random() > 0.5)
 -                      sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM);
 +      if(c<4)
 +              needweapons = TRUE;
 +
 +      if(!needweapons && !needarmor)
 +              return;
 +
 +      ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n"));
 +      ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n"));
 +
 +      // See what is around
 +      head = findchainfloat(bot_pickup, TRUE);
 +      while (head)
 +      {
 +              // gather health and armor only
 +              if (head.solid)
 +              if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
 +              if (vlen(head.origin - org) < sradius)
 +              {
 +                      t = head.bot_pickupevalfunc(self, head);
 +                      if (t > 0)
 +                              navigation_routerating(head, t * ratingscale, 500);
 +              }
 +              head = head.chain;
        }
  }
  
 -void onslaught_controlpoint_icon_buildthink()
 +void havocbot_role_ons_setrole(entity bot, float role)
  {
 -      entity oself;
 -      float a;
 +      ons_debug(strcat(bot.netname," switched to "));
 +      switch(role)
 +      {
 +              case HAVOCBOT_ONS_ROLE_DEFENSE:
 +                      ons_debug("defense");
 +                      bot.havocbot_role = havocbot_role_ons_defense;
 +                      bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
 +                      bot.havocbot_role_timeout = 0;
 +                      break;
 +              case HAVOCBOT_ONS_ROLE_ASSISTANT:
 +                      ons_debug("assistant");
 +                      bot.havocbot_role = havocbot_role_ons_assistant;
 +                      bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
 +                      bot.havocbot_role_timeout = 0;
 +                      break;
 +              case HAVOCBOT_ONS_ROLE_OFFENSE:
 +                      ons_debug("offense");
 +                      bot.havocbot_role = havocbot_role_ons_offense;
 +                      bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
 +                      bot.havocbot_role_timeout = 0;
 +                      break;
 +      }
 +      ons_debug("\n");
 +}
  
 -      self.nextthink = time + sys_frametime;
 +float havocbot_ons_teamcount(entity bot, float role)
 +{
 +      float c = 0;
 +      entity head;
  
 -      // only do this if there is power
 -      a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
 -      if(!a)
 -              return;
 +      FOR_EACH_PLAYER(head)
 +      if(SAME_TEAM(head, self))
 +      if(head.havocbot_role_flags & role)
 +              ++c;
  
 -      self.health = self.health + self.count;
 +      return c;
 +}
  
 -      if (self.health >= self.max_health)
 +void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
 +{
 +      entity cp, cp1, cp2, best, pl, wp;
 +      float radius, found, bestvalue, c;
 +
 +      // Filter control points
 +      for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
        {
 -              self.health = self.max_health;
 -              self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on
 -              self.think = onslaught_controlpoint_icon_think;
 -              sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM);
 -              bprint(Team_ColoredFullName(self.team), " captured ", self.owner.message, " control point\n");
 -              self.owner.iscaptured = TRUE;
 +              cp2.wpcost = c = 0;
 +              cp2.wpconsidered = FALSE;
  
 -              WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
 -              WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 +              if(cp2.isshielded)
 +                      continue;
  
 -              onslaught_updatelinks();
 +              // Ignore owned controlpoints
 +              if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team]))
 +                      continue;
  
 -              // Use targets now (somebody make sure this is in the right place..)
 -              oself = self;
 -              self = self.owner;
 -              activator = self;
 -              SUB_UseTargets ();
 -              self = oself;
 -              self.cp_origin = self.origin;
 -              self.cp_bob_origin = '0 0 0.1';
 -              self.cp_bob_spd = 0;
 +              // Count team mates interested in this control point
 +              // (easier and cleaner than keeping counters per cp and teams)
 +              FOR_EACH_PLAYER(pl)
 +              if(SAME_TEAM(pl, self))
 +              if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
 +              if(pl.havocbot_ons_target==cp2)
 +                      ++c;
 +
 +              // NOTE: probably decrease the cost of attackable control points
 +              cp2.wpcost = c;
 +              cp2.wpconsidered = TRUE;
        }
 -      self.alpha = self.health / self.max_health;
 -      // colormod flash when shot
 -      self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
 -      if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
 -              setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
 -      //setsize(self, '-32 -32 0', '32 32 8');
  
 -      if(random() < 0.9 - self.health / self.max_health)
 -              pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
 +      // We'll consider only the best case
 +      bestvalue = 99999999999;
 +      cp = world;
 +      for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext)
 +      {
 +              if (!cp1.wpconsidered)
 +                      continue;
 +
 +              if(cp1.wpcost<bestvalue)
 +              {
 +                      bestvalue = cp1.wpcost;
 +                      cp = cp1;
 +                      self.havocbot_ons_target = cp1;
 +              }
 +      }
 +
 +      if (!cp)
 +              return;
 +
 +      ons_debug(strcat(self.netname, " chose cp ranked ", ftos(bestvalue), "\n"));
 +
 +      if(cp.goalentity)
 +      {
 +              // Should be attacked
 +              // Rate waypoints near it
 +              found = FALSE;
 +              best = world;
 +              bestvalue = 99999999999;
 +              for(radius=0; radius<1000 && !found; radius+=500)
 +              {
 +                      for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
 +                      {
 +                              if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
 +                              if(wp.classname=="waypoint")
 +                              if(checkpvs(wp.origin,cp))
 +                              {
 +                                      found = TRUE;
 +                                      if(wp.cnt<bestvalue)
 +                                      {
 +                                              best = wp;
 +                                              bestvalue = wp.cnt;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if(best)
 +              {
 +                      navigation_routerating(best, ratingscale, 10000);
 +                      best.cnt += 1;
 +
 +                      self.havocbot_attack_time = 0;
 +                      if(checkpvs(self.view_ofs,cp))
 +                      if(checkpvs(self.view_ofs,best))
 +                              self.havocbot_attack_time = time + 2;
 +              }
 +              else
 +              {
 +                      navigation_routerating(cp, ratingscale, 10000);
 +              }
 +              ons_debug(strcat(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n"));
 +      }
 +      else
 +      {
 +              // Should be touched
 +              ons_debug(strcat(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n"));
 +              found = FALSE;
 +
 +              // Look for auto generated waypoint
 +              if (!bot_waypoints_for_items)
 +              for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
 +              {
 +                      if(wp.classname=="waypoint")
 +                      {
 +                              navigation_routerating(wp, ratingscale, 10000);
 +                              found = TRUE;
 +                      }
 +              }
 +
 +              // Nothing found, rate the controlpoint itself
 +              if (!found)
 +                      navigation_routerating(cp, ratingscale, 10000);
 +      }
  }
  
 +float havocbot_goalrating_ons_generator_attack(float ratingscale)
 +{
 +      entity g, wp, bestwp;
 +      float found, best;
 +
 +      for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
 +      {
 +              if(SAME_TEAM(g, self) || g.isshielded)
 +                      continue;
 +
 +              // Should be attacked
 +              // Rate waypoints near it
 +              found = FALSE;
 +              bestwp = world;
 +              best = 99999999999;
  
 +              for(wp=findradius(g.origin,400); wp; wp=wp.chain)
 +              {
 +                      if(wp.classname=="waypoint")
 +                      if(checkpvs(wp.origin,g))
 +                      {
 +                              found = TRUE;
 +                              if(wp.cnt<best)
 +                              {
 +                                      bestwp = wp;
 +                                      best = wp.cnt;
 +                              }
 +                      }
 +              }
  
 +              if(bestwp)
 +              {
 +                      ons_debug("waypoints found around generator\n");
 +                      navigation_routerating(bestwp, ratingscale, 10000);
 +                      bestwp.cnt += 1;
 +
 +                      self.havocbot_attack_time = 0;
 +                      if(checkpvs(self.view_ofs,g))
 +                      if(checkpvs(self.view_ofs,bestwp))
 +                              self.havocbot_attack_time = time + 5;
 +
 +                      return TRUE;
 +              }
 +              else
 +              {
 +                      ons_debug("generator found without waypoints around\n");
 +                      // if there aren't waypoints near the generator go straight to it
 +                      navigation_routerating(g, ratingscale, 10000);
 +                      self.havocbot_attack_time = 0;
 +                      return TRUE;
 +              }
 +      }
 +      return FALSE;
 +}
  
 -void onslaught_controlpoint_touch()
 +void havocbot_role_ons_offense()
  {
 -      entity e;
 -      float a;
 -      if (!IS_PLAYER(other))
 +      if(self.deadflag != DEAD_NO)
 +      {
 +              self.havocbot_attack_time = 0;
 +              havocbot_ons_reset_role(self);
                return;
 -      a = onslaught_controlpoint_attackable(self, other.team);
 -      if(a != 2 && a != 4)
 +      }
 +
 +      // Set the role timeout if necessary
 +      if (!self.havocbot_role_timeout)
 +              self.havocbot_role_timeout = time + 120;
 +
 +      if (time > self.havocbot_role_timeout)
 +      {
 +              havocbot_ons_reset_role(self);
                return;
 -      // we've verified that this player has a legitimate claim to this point,
 -      // so start building the captured point icon (which only captures this
 -      // point if it successfully builds without being destroyed first)
 -      self.goalentity = e = spawn();
 -      e.classname = "onslaught_controlpoint_icon";
 -      e.owner = self;
 -      e.max_health = autocvar_g_onslaught_cp_health;
 -      e.health = autocvar_g_onslaught_cp_buildhealth;
 -      e.solid = SOLID_BBOX;
 -      e.movetype = MOVETYPE_NONE;
 -      setmodel(e, "models/onslaught/controlpoint_icon.md3");
 -      setsize(e, '-32 -32 -32', '32 32 32');
 -      setorigin(e, self.origin + '0 0 96');
 -      e.takedamage = DAMAGE_AIM;
 -      e.bot_attack = TRUE;
 -      e.event_damage = onslaught_controlpoint_icon_damage;
 -      e.team = other.team;
 -      e.colormap = 1024 + (e.team - 1) * 17;
 -      e.think = onslaught_controlpoint_icon_buildthink;
 -      e.nextthink = time + sys_frametime;
 -      e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
 -      sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM);
 -      self.team = e.team;
 -      self.colormap = e.colormap;
 -      WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
 -      onslaught_updatelinks();
 +      }
 +
 +      if(self.havocbot_attack_time>time)
 +              return;
 +
 +      if (self.bot_strategytime < time)
 +      {
 +              navigation_goalrating_start();
 +              havocbot_goalrating_enemyplayers(20000, self.origin, 650);
 +              if(!havocbot_goalrating_ons_generator_attack(20000))
 +                      havocbot_goalrating_ons_controlpoints_attack(20000);
 +              havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000);
 +              navigation_goalrating_end();
 +
 +              self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
 +      }
  }
  
 -void onslaught_controlpoint_think()
 +void havocbot_role_ons_assistant()
  {
 -      self.nextthink = time;
 -      CSQCMODEL_AUTOUPDATE();
 +      havocbot_ons_reset_role(self);
  }
  
 -void onslaught_controlpoint_reset()
 +void havocbot_role_ons_defense()
  {
 -      if(self.goalentity && self.goalentity != world)
 -              remove(self.goalentity);
 -      self.goalentity = world;
 -      self.team = 0;
 -      self.colormap = 1024;
 -      self.iscaptured = FALSE;
 -      self.islinked = FALSE;
 -      self.isshielded = TRUE;
 -      self.enemy.solid = SOLID_NOT;
 -      self.enemy.colormap = self.colormap;
 -      self.think = onslaught_controlpoint_think;
 -      self.enemy.think = func_null;
 -      self.nextthink = time; // don't like func_null :P
 -      setmodel(self, "models/onslaught/controlpoint_pad.md3");
 -      //setsize(self, '-32 -32 0', '32 32 8');
 -
 -      WaypointSprite_UpdateMaxHealth(self.sprite, 0);
 +      havocbot_ons_reset_role(self);
 +}
  
 -      onslaught_updatelinks();
 +void havocbot_ons_reset_role(entity bot)
 +{
 +      entity head;
 +      float c;
  
 -      activator = self;
 -      SUB_UseTargets(); // to reset the structures, playerspawns etc.
 -      
 -      CSQCMODEL_AUTOUPDATE();
 -}
 +      if(self.deadflag != DEAD_NO)
 +              return;
  
 -/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
 -  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
 +      bot.havocbot_ons_target = world;
  
 -  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
 +      // TODO: Defend control points or generator if necessary
  
 -keys:
 -"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
 -"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
 -"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
 - */
 +      // if there is only me on the team switch to offense
 +      c = 0;
 +      FOR_EACH_PLAYER(head)
 +      if(SAME_TEAM(head, self))
 +              ++c;
  
 -void spawnfunc_onslaught_controlpoint()
 -{
 -      //entity e;
 -      if (!g_onslaught)
 +      if(c==1)
        {
 -              remove(self);
 +              havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
                return;
        }
 -      precache_model("models/onslaught/controlpoint_pad.md3");
 -      precache_model("models/onslaught/controlpoint_pad2.md3");
 -      precache_model("models/onslaught/controlpoint_shield.md3");
 -      precache_model("models/onslaught/controlpoint_icon.md3");
 -      precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
 -      precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
 -      precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
 -      precache_model("models/onslaught/controlpoint_icon_gib1.md3");
 -      precache_model("models/onslaught/controlpoint_icon_gib2.md3");
 -      precache_model("models/onslaught/controlpoint_icon_gib4.md3");
 -      precache_sound("onslaught/controlpoint_build.wav");
 -      precache_sound("onslaught/controlpoint_built.wav");
 -      precache_sound("weapons/grenade_impact.wav");
 -      precache_sound("onslaught/damageblockedbyshield.wav");
 -      precache_sound("onslaught/controlpoint_underattack.wav");
 -      precache_sound("onslaught/ons_spark1.wav");
 -      precache_sound("onslaught/ons_spark2.wav");
  
 -      self.solid = SOLID_BBOX;
 -      self.movetype = MOVETYPE_NONE;
 -      setmodel(self, "models/onslaught/controlpoint_pad.md3");
 -      //setsize(self, '-32 -32 0', '32 32 8');
 -      if(!self.noalign)
 -      {
 -              setorigin(self, self.origin + '0 0 20');
 -              droptofloor();
 -      }
 -      self.touch = onslaught_controlpoint_touch;
 -      self.team = 0;
 -      self.colormap = 1024;
 -      self.iscaptured = FALSE;
 -      self.islinked = FALSE;
 -      self.isshielded = TRUE;
 +      havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
 +}
  
 -      // spawn shield model which indicates whether this can be damaged
 -      self.enemy = spawn();
 -      self.enemy.classname = "onslaught_controlpoint_shield";
 -      self.enemy.solid = SOLID_NOT;
 -      self.enemy.movetype = MOVETYPE_NONE;
 -      self.enemy.effects = EF_ADDITIVE;
 -      setmodel(self.enemy , "models/onslaught/controlpoint_shield.md3");
  
 -      setattachment(self.enemy , self, "");
 -      //setsize(e, '-32 -32 0', '32 32 128');
 +/*
 + * Find control point or generator owned by the same team self which is nearest to pos
 + * if max_dist is positive, only control points within this range will be considered
 + */
 +entity ons_Nearest_ControlPoint(vector pos, float max_dist)
 +{
 +      entity tmp_entity, closest_target = world;
 +      tmp_entity = findchain(classname, "onslaught_controlpoint");
 +      while(tmp_entity)
 +      {
 +              if(SAME_TEAM(tmp_entity, self))
 +              if(tmp_entity.iscaptured)
 +              if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist)
 +              if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
 +                      closest_target = tmp_entity;
 +              tmp_entity = tmp_entity.chain;
 +      }
 +      tmp_entity = findchain(classname, "onslaught_generator");
 +      while(tmp_entity)
 +      {
 +              if(SAME_TEAM(tmp_entity, self))
 +              if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist)
 +              if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
 +                      closest_target = tmp_entity;
 +              tmp_entity = tmp_entity.chain;
 +      }
 +      
 +      return closest_target;
 +}
  
 -      //setorigin(e, self.origin);
 -      self.enemy.colormap = self.colormap;
 +/*
 + * Find control point or generator owned by the same team self which is nearest to pos
 + * if max_dist is positive, only control points within this range will be considered
 + * This function only check distances on the XY plane, disregarding Z
 + */
 +entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist)
 +{
 +      entity tmp_entity, closest_target = world;
 +      vector delta;
 +      float smallest_distance = 0, distance;
 +      
 +      tmp_entity = findchain(classname, "onslaught_controlpoint");
 +      while(tmp_entity)
 +      {
 +              delta = tmp_entity.origin - pos;
 +              delta_z = 0;
 +              distance = vlen(delta);
 +              
 +              if(SAME_TEAM(tmp_entity, self))
 +              if(tmp_entity.iscaptured)
 +              if(max_dist <= 0 || distance <= max_dist)
 +              if(closest_target == world || distance <= smallest_distance )
 +              {
 +                      closest_target = tmp_entity;
 +                      smallest_distance = distance;
 +              }
 +              
 +              tmp_entity = tmp_entity.chain;
 +      }
 +      tmp_entity = findchain(classname, "onslaught_generator");
 +      while(tmp_entity)
 +      {
 +              delta = tmp_entity.origin - pos;
 +              delta_z = 0;
 +              distance = vlen(delta);
 +              
 +              if(SAME_TEAM(tmp_entity, self))
 +              if(max_dist <= 0 || distance <= max_dist)
 +              if(closest_target == world || distance <= smallest_distance )
 +              {
 +                      closest_target = tmp_entity;
 +                      smallest_distance = distance;
 +              }
 +              
 +              tmp_entity = tmp_entity.chain;
 +      }
 +      
 +      return closest_target;
 +}
 +/**
 + * find the number of control points and generators in the same team as self
 + */
 +float ons_Count_SelfControlPoints()
 +{
 +      entity tmp_entity;
 +      tmp_entity = findchain(classname, "onslaught_controlpoint");
 +      float n = 0;
 +      while(tmp_entity)
 +      {
 +              if(SAME_TEAM(tmp_entity, self))
 +              if(tmp_entity.iscaptured)
 +                      n++;
 +              tmp_entity = tmp_entity.chain;
 +      }
 +      tmp_entity = findchain(classname, "onslaught_generator");
 +      while(tmp_entity)
 +      {
 +              if(SAME_TEAM(tmp_entity, self))
 +                      n++;
 +              tmp_entity = tmp_entity.chain;
 +      }
 +      return n;
 +}
  
 -      waypoint_spawnforitem(self);
 +/**
 + * Teleport player to a random position near tele_target
 + * if tele_effects is true, teleport sound+particles are created
 + * return FALSE on failure
 + */
 +float ons_Teleport(entity player, entity tele_target, float range, float tele_effects)
 +{
 +      if ( !tele_target )
 +              return FALSE;
        
 -      self.think = onslaught_controlpoint_think;
 -      self.nextthink = time;
 +      float i;
 +      vector loc;
 +      float theta;
 +      for(i = 0; i < 16; ++i)
 +      {
 +              theta = random() * 2 * M_PI;
 +              loc_y = sin(theta);
 +              loc_x = cos(theta);
 +              loc_z = 0;
 +              loc *= random() * range;
 +              
 +              loc += tele_target.origin + '0 0 128';
 +              
 +              tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player);
 +              if(trace_fraction == 1.0 && !trace_startsolid)
 +              {
 +                      traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world
 +                      if(trace_fraction == 1.0 && !trace_startsolid)
 +                      {
 +                              if ( tele_effects )
 +                              {
 +                                      pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1);
 +                                      sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM);
 +                              }
 +                              setorigin(player, loc);
 +                              player.angles = '0 1 0' * ( theta * RAD2DEG + 180 );
 +                              makevectors(player.angles);
 +                              player.fixangle = TRUE;
 +                              player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait;
 +
 +                              if ( tele_effects )
 +                                      pointparticles(particleeffectnum("teleport"), player.origin + v_forward * 32, '0 0 0', 1);
 +                              return TRUE;
 +                      }
 +              }
 +      }
 +      
 +      return FALSE;
 +}
  
 -      WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
 -      WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY);
 +// ==============
 +// Hook Functions
 +// ==============
  
 -      onslaught_updatelinks();
 +MUTATOR_HOOKFUNCTION(ons_ResetMap)
 +{
 +      FOR_EACH_PLAYER(self)
 +      {
 +              self.ons_roundlost = FALSE;
 +              self.ons_deathloc = '0 0 0';
 +              PutClientInServer();
 +      }
 +      return FALSE;
 +}
  
 -      self.reset = onslaught_controlpoint_reset;
 -      
 -      CSQCMODEL_AUTOINIT();
 +MUTATOR_HOOKFUNCTION(ons_RemovePlayer)
 +{
 +      self.ons_deathloc = '0 0 0';
 +      return FALSE;
  }
  
 -float onslaught_link_send(entity to, float sendflags)
 +MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
  {
 -      WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
 -      WriteByte(MSG_ENTITY, sendflags);
 -      if(sendflags & 1)
 +      if(!round_handler_IsRoundStarted())
        {
 -              WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
 -              WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
 -              WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
 +              self.player_blocked = TRUE;
 +              return FALSE;
        }
 -      if(sendflags & 2)
 +      
 +      entity l;
 +      for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
        {
 -              WriteCoord(MSG_ENTITY, self.enemy.origin_x);
 -              WriteCoord(MSG_ENTITY, self.enemy.origin_y);
 -              WriteCoord(MSG_ENTITY, self.enemy.origin_z);
 +              l.sprite.SendFlags |= 16;
        }
 -      if(sendflags & 4)
 +      for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
        {
 -              WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
 +              l.sprite.SendFlags |= 16;
        }
 -      return TRUE;
 -}
  
 -void onslaught_link_checkupdate()
 -{
 -      // TODO check if the two sides have moved (currently they won't move anyway)
 -      float redpower, bluepower;
 +      if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
  
 -      redpower = bluepower = 0;
 -      if(self.goalentity.islinked)
 +      if ( autocvar_g_onslaught_spawn_choose )
 +      if ( self.ons_spawn_by )
 +      if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,FALSE) )
        {
 -              if(self.goalentity.team == NUM_TEAM_1)
 -                      redpower = 1;
 -              else if(self.goalentity.team == NUM_TEAM_2)
 -                      bluepower = 1;
 +              self.ons_spawn_by = world;
 +              return FALSE;
        }
 -      if(self.enemy.islinked)
 +      
 +      if(autocvar_g_onslaught_spawn_at_controlpoints)
 +      if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
        {
 -              if(self.enemy.team == NUM_TEAM_1)
 -                      redpower = 2;
 -              else if(self.enemy.team == NUM_TEAM_2)
 -                      bluepower = 2;
 -      }
 +              float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random;
 +              entity tmp_entity, closest_target = world;
 +              vector spawn_loc = self.ons_deathloc;
 +              
 +              // new joining player or round reset, don't bother checking
 +              if(spawn_loc == '0 0 0') { return FALSE; }
  
 -      float cc;
 -      if(redpower == 1 && bluepower == 2)
 -              cc = (NUM_TEAM_1 - 1) * 0x01 + (NUM_TEAM_2 - 1) * 0x10;
 -      else if(redpower == 2 && bluepower == 1)
 -              cc = (NUM_TEAM_1 - 1) * 0x10 + (NUM_TEAM_2 - 1) * 0x01;
 -      else if(redpower)
 -              cc = (NUM_TEAM_1 - 1) * 0x11;
 -      else if(bluepower)
 -              cc = (NUM_TEAM_2 - 1) * 0x11;
 -      else
 -              cc = 0;
 -
 -      //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
 -      //print("cc=", ftos(cc), "\n");
 +              if(random_target) { RandomSelection_Init(); }
  
 -      if(cc != self.clientcolors)
 -      {
 -              self.clientcolors = cc;
 -              self.SendFlags |= 4;
 +              for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
 +              {
 +                      if(SAME_TEAM(tmp_entity, self))
 +                      if(random_target)
 +                              RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
 +                      else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
 +                              closest_target = tmp_entity;
 +              }
 +              
 +              if(random_target) { closest_target = RandomSelection_chosen_ent; }
 +              
 +              if(closest_target)
 +              {
 +                      float i;
 +                      vector loc;
 +                      for(i = 0; i < 10; ++i)
 +                      {
 +                              loc = closest_target.origin + '0 0 96';
 +                              loc += ('0 1 0' * random()) * 128;
 +                              tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
 +                              if(trace_fraction == 1.0 && !trace_startsolid)
 +                              {
 +                                      traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
 +                                      if(trace_fraction == 1.0 && !trace_startsolid)
 +                                      {
 +                                              setorigin(self, loc);
 +                                              self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
 +                                              return FALSE;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      
 +      if(autocvar_g_onslaught_spawn_at_generator)
 +      if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
 +      {
 +              float random_target = autocvar_g_onslaught_spawn_at_generator_random;
 +              entity tmp_entity, closest_target = world;
 +              vector spawn_loc = self.ons_deathloc;
 +              
 +              // new joining player or round reset, don't bother checking
 +              if(spawn_loc == '0 0 0') { return FALSE; }
 +              
 +              if(random_target) { RandomSelection_Init(); }
 +
 +              for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
 +              {
 +                      if(random_target)
 +                              RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
 +                      else
 +                      {
 +                              if(SAME_TEAM(tmp_entity, self))
 +                              if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
 +                                      closest_target = tmp_entity;
 +                      }
 +              }
 +              
 +              if(random_target) { closest_target = RandomSelection_chosen_ent; }
 +              
 +              if(closest_target)
 +              {
 +                      float i;
 +                      vector loc;
 +                      for(i = 0; i < 10; ++i)
 +                      {
 +                              loc = closest_target.origin + '0 0 128';
 +                              loc += ('0 1 0' * random()) * 256;
 +                              tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
 +                              if(trace_fraction == 1.0 && !trace_startsolid)
 +                              {
 +                                      traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
 +                                      if(trace_fraction == 1.0 && !trace_startsolid)
 +                                      {
 +                                              setorigin(self, loc);
 +                                              self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
 +                                              return FALSE;
 +                                      }
 +                              }
 +                      }
 +              }
        }
  
 -      self.nextthink = time;
 +    return FALSE;
  }
  
 -void onslaught_link_delayed()
 +MUTATOR_HOOKFUNCTION(ons_PlayerDies)
  {
 -      self.goalentity = find(world, targetname, self.target);
 -      self.enemy = find(world, targetname, self.target2);
 -      if (!self.goalentity)
 -              objerror("can not find target\n");
 -      if (!self.enemy)
 -              objerror("can not find target2\n");
 -      dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
 -      self.SendFlags |= 3;
 -      self.think = onslaught_link_checkupdate;
 -      self.nextthink = time;
 +      frag_target.ons_deathloc = frag_target.origin;
 +      entity l;
 +      for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
 +      {
 +              l.sprite.SendFlags |= 16;
 +      }
 +      for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
 +      {
 +              l.sprite.SendFlags |= 16;
 +      }
 +      
 +      if ( autocvar_g_onslaught_spawn_choose )
 +      if ( ons_Count_SelfControlPoints() > 1 )
 +              stuffcmd(self, "qc_cmd_cl hud clickradar\n");
 +      
 +      return FALSE;
  }
  
 -/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
 -  Link between control points.
 +MUTATOR_HOOKFUNCTION(ons_MonsterThink)
 +{
 +      entity e = find(world, targetname, self.target);
 +      if (e != world)
 +              self.team = e.team;
  
 -  This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
 +      return FALSE;
 +}
  
 -keys:
 -"target" - first control point.
 -"target2" - second control point.
 - */
 -void spawnfunc_onslaught_link()
 +void ons_MonsterSpawn_Delayed()
  {
 -      if (!g_onslaught)
 +      entity e, own = self.owner;
 +      
 +      if(!own) { remove(self); return; }
 +      
 +      if(own.targetname)
        {
 -              remove(self);
 -              return;
 +              e = find(world, target, own.targetname);
 +              if(e != world)
 +              {
 +                      own.team = e.team;
 +                      
 +                      activator = e;
 +                      own.use();
 +              }
        }
 -      if (self.target == "" || self.target2 == "")
 -              objerror("target and target2 must be set\n");
 -      InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
 -      Net_LinkEntity(self, FALSE, 0, onslaught_link_send);
 +      
 +      remove(self);
  }
  
 -MUTATOR_HOOKFUNCTION(ons_BuildMutatorsString)
 +MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
  {
 -      ret_string = strcat(ret_string, ":ONS");
 -      return 0;
 -}
 +      entity e = spawn();
 +      e.owner = self;
 +      InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET);
  
 -MUTATOR_HOOKFUNCTION(ons_BuildMutatorsPrettyString)
 -{
 -      ret_string = strcat(ret_string, ", Onslaught");
 -      return 0;
 +      return FALSE;
  }
  
 -MUTATOR_HOOKFUNCTION(ons_Spawn_Score)
 +void ons_TurretSpawn_Delayed()
  {
 +      entity e, own = self.owner;
  
 -    /*
 -    float _neer_home = (random() > 0.5 ? TRUE : FALSE);
 +      if(!own) { remove(self); return; }
  
 -      RandomSelection_Init();
 +      if(own.targetname)
 +      {
 +              e = find(world, target, own.targetname);
 +              if(e != world)
 +              {
 +                      own.team = e.team;
 +                      own.active = ACTIVE_NOT;
 +                      
 +                      activator = e;
 +                      own.use();
 +              }
 +      }
  
 -      if(self.team == NUM_TEAM_1)
 -        RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1);
 +      remove(self);
 +}
  
 -      if(self.team == NUM_TEAM_2)
 -        RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1);
 +MUTATOR_HOOKFUNCTION(ons_TurretSpawn)
 +{
 +      entity e = spawn();
 +      e.owner = self;
 +      InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
  
 -      entity _cp = findchain(classname, "onslaught_controlpoint"):
 -      while _cp;
 -      {
 -          if(_cp.team == self.team)
 -            RandomSelection_Add(_cp, 0, string_null, 1, 1);
 +      return FALSE;
 +}
  
 -              _cp = _cp.chain;
 -      }
 +MUTATOR_HOOKFUNCTION(ons_BotRoles)
 +{
 +      havocbot_ons_reset_role(self);
 +      return TRUE;
 +}
  
 -      if(RandomSelection_chosen_ent)
 +MUTATOR_HOOKFUNCTION(ons_GetTeamCount)
 +{
 +      // onslaught is special
 +      entity tmp_entity;
 +      for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
        {
 -              self.tur_head = RandomSelection_chosen_ent;
 -              spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
 +              switch(tmp_entity.team)
 +              {
 +                      case NUM_TEAM_1: c1 = 0; break;
 +                      case NUM_TEAM_2: c2 = 0; break;
 +                      case NUM_TEAM_3: c3 = 0; break;
 +                      case NUM_TEAM_4: c4 = 0; break;
 +              }
        }
 -      else if(self.team == spawn_spot.team)
 -              spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
  
 -    */
 +      return TRUE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(ons_SpectateCopy)
 +{
 +      self.ons_roundlost = other.ons_roundlost; // make spectators see it too
 +      return FALSE;
 +}
  
-                       entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
-                       if ( !source_point && self.health > 0 )
-                       {
-                               sprint(self, "\nYou need to be next to a control point\n");
-                               return 1;
-                       }
-                       entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius);
-                       if ( closest_target == world )
-                       {
-                               sprint(self, "\nNo control point found\n");
-                               return 1;
-                       }
-                       if ( self.health <= 0 )
-                       {
-                               self.ons_spawn_by = closest_target;
-                               self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
-                       }
-                       else
 +MUTATOR_HOOKFUNCTION(ons_SV_ParseClientCommand)
 +{
 +      if(MUTATOR_RETURNVALUE) // command was already handled?
 +              return FALSE;
 +
 +      if ( cmd_name == "ons_spawn" )
 +      {
 +              vector pos = self.origin;
 +              if(cmd_argc > 1)
 +                      pos_x = stof(argv(1));
 +              if(cmd_argc > 2)
 +                      pos_y = stof(argv(2));
 +              if(cmd_argc > 3)
 +                      pos_z = stof(argv(3));
 +              
 +              if ( IS_PLAYER(self) )
 +              {
-                               if ( source_point == closest_target )
++                      if ( !self.frozen )
 +                      {
-                                       sprint(self, "\nTeleporting to the same point\n");
++                              entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
++                              
++                              if ( !source_point && self.health > 0 )
 +                              {
-                               if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,TRUE) )
-                                       sprint(self, "\nUnable to teleport there\n");
++                                      sprint(self, "\nYou need to be next to a control point\n");
 +                                      return 1;
 +                              }
-                       //sprint(self, "\nNo teleportation for you\n");
-                       return 1;
++                              
++                              
++                              entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius);
++                      
++                              if ( closest_target == world )
++                              {
++                                      sprint(self, "\nNo control point found\n");
++                                      return 1;
++                              }
++                              
++                              if ( self.health <= 0 )
++                              {
++                                      self.ons_spawn_by = closest_target;
++                                      self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
++                              }
++                              else
++                              {
++                                      if ( source_point == closest_target )
++                                      {
++                                              sprint(self, "\nTeleporting to the same point\n");
++                                              return 1;
++                                      }
++                                      
++                                      if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,TRUE) )
++                                              sprint(self, "\nUnable to teleport there\n");
++                              }
++                              
++                              return 1;
 +                      }
 +                      
++                      sprint(self, "\nNo teleportation for you\n");
 +              }
 +              
 +              return 1;
 +      }
        return 0;
  }
  
 -MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
 +MUTATOR_HOOKFUNCTION(ons_PlayerUseKey)
  {
 -    if(!autocvar_g_onslaught_spawn_at_controlpoints)
 -        return 0;
 +      if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
 +      
 +      if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle)
 +      {
 +              entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
 +              if ( source_point )
 +              {
 +                      stuffcmd(self, "qc_cmd_cl hud clickradar\n");
 +                      return TRUE;
 +              }
 +      }
 +      
 +      return FALSE;
 +}
  
 -    if(random() < 0.5)  // 50/50 chane to use default spawnsystem.
 -        return 0;
 +// ==========
 +// Spawnfuncs
 +// ==========
  
 -    float _close_to_home = ((random() > 0.5) ? TRUE : FALSE);
 -    entity _best = world, _trg_gen = world;
 -    float _score, _best_score = MAX_SHOT_DISTANCE;
 +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
 +  Link between control points.
  
 -      RandomSelection_Init();
 +  This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
  
 -      if(self.team == NUM_TEAM_1)
 -      {
 -          if(!_close_to_home)
 -            _trg_gen = ons_blue_generator;
 -        else
 -            _trg_gen  = ons_red_generator;
 -      }
 +keys:
 +"target" - first control point.
 +"target2" - second control point.
 + */
 +void spawnfunc_onslaught_link()
 +{
 +      if(!g_onslaught) { remove(self); return; }
  
 -      if(self.team == NUM_TEAM_2)
 -      {
 -          if(_close_to_home)
 -            _trg_gen = ons_blue_generator;
 -        else
 -            _trg_gen  = ons_red_generator;
 -      }
 +      if (self.target == "" || self.target2 == "")
 +              objerror("target and target2 must be set\n");
  
 -      entity _cp = findchain(classname, "onslaught_controlpoint");
 -      while(_cp)
 -      {
 -          if(_cp.team == self.team)
 -        {
 -            _score = vlen(_trg_gen.origin - _cp.origin);
 -            if(_score < _best_score)
 -            {
 -                _best = _cp;
 -                _best_score = _score;
 -            }
 -        }
 -              _cp = _cp.chain;
 -      }
 +      self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
 +      ons_worldlinklist = self;
  
 -    vector _loc;
 -    float i;
 -    if(_best)
 -    {
 -        for(i = 0; i < 10; ++i)
 -        {
 -            _loc = _best.origin + '0 0 96';
 -            _loc += ('0 1 0' * random()) * 128;
 -            tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
 -            if(trace_fraction == 1.0 && !trace_startsolid)
 -            {
 -                setorigin(self, _loc);
 -                self.angles = normalize(_loc - _best.origin) * RAD2DEG;
 -                return 0;
 -            }
 -        }
 -    }
 -    else
 -    {
 -        if(!autocvar_g_onslaught_spawn_at_generator)
 -            return 0;
 +      InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
 +      Net_LinkEntity(self, FALSE, 0, ons_Link_Send);
 +}
  
 -        _trg_gen = ((self.team == NUM_TEAM_1) ? ons_red_generator : ons_blue_generator);
 +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
 +  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
  
 -        for(i = 0; i < 10; ++i)
 -        {
 -            _loc = _trg_gen.origin + '0 0 96';
 -            _loc += ('0 1 0' * random()) * 128;
 -            tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
 -            if(trace_fraction == 1.0 && !trace_startsolid)
 -            {
 -                setorigin(self, _loc);
 -                self.angles = normalize(_loc - _trg_gen.origin) * RAD2DEG;
 -                return 0;
 -            }
 -        }
 -    }
 +  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
  
 -    return 0;
 +keys:
 +"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
 +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
 +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
 + */
 +
 +void spawnfunc_onslaught_controlpoint()
 +{
 +      if(!g_onslaught) { remove(self); return; }
 +      
 +      ons_ControlPoint_Setup(self);
  }
  
 -MUTATOR_HOOKFUNCTION(ons_MonsterThink)
 +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
 +  Base generator.
 +
 +  spawnfunc_onslaught_link entities can target this.
 +
 +keys:
 +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
 +"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
 + */
 +void spawnfunc_onslaught_generator()
  {
 -      entity e = find(world, targetname, self.target);
 -      if (e != world)
 -              self.team = e.team;
 +      if(!g_onslaught) { remove(self); return; }
 +      if(!self.team) { objerror("team must be set"); }
  
 -      return FALSE;
 +      ons_GeneratorSetup(self);
  }
  
 -MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
 +
 +// scoreboard setup
 +void ons_ScoreRules()
  {
 -      entity e, ee = world;
 -      
 -      if(self.targetname)
 -      {
 -              e = find(world,target,self.targetname);
 -              if(e != world)
 -              {
 -                      self.team = e.team;
 -                      ee = e;
 -              }
 -      }
 +      CheckAllowedTeams(world);
 +      ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, TRUE);
 +      ScoreInfo_SetLabel_TeamScore  (ST_ONS_CAPS,     "destroyed", SFL_SORT_PRIO_PRIMARY);
 +      ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
 +      ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES,    "takes",     0);
 +      ScoreRules_basics_end();
 +}
 +
 +void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up
 +{
 +      ons_ScoreRules();
        
 -      if(ee)
 -      {
 -        activator = ee;
 -        self.use();
 -    }
 +      round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart);
 +      round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
 +}
  
 -      return FALSE;
 +void ons_Initialize()
 +{
 +      precache_sound("ctf/red_capture.wav");
 +      precache_sound("ctf/blue_capture.wav");
 +      precache_sound("ctf/yellow_capture.wav");
 +      precache_sound("ctf/pink_capture.wav");
 +
 +      ons_captureshield_force = autocvar_g_onslaught_shield_force;
 +
 +      addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost);
 +
 +      InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE);
  }
  
  MUTATOR_DEFINITION(gamemode_onslaught)
  {
 -      MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(reset_map_global, ons_ResetMap, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MakePlayerObserver, ons_RemovePlayer, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(ClientDisconnect, ons_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerDies, ons_PlayerDies, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterMove, ons_MonsterThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterSpawn, ons_MonsterSpawn, CBC_ORDER_ANY);
 -      //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(TurretSpawn, ons_TurretSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(HavocBot_ChooseRole, ons_BotRoles, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(GetTeamCount, ons_GetTeamCount, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SpectateCopy, ons_SpectateCopy, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SV_ParseClientCommand, ons_SV_ParseClientCommand, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerUseKey, ons_PlayerUseKey, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {
                if(time > 1) // game loads at time 1
                        error("This is a game type and it cannot be added at runtime.");
 +              ons_Initialize();
 +      }
 +
 +      MUTATOR_ONROLLBACK_OR_REMOVE
 +      {
 +              // we actually cannot roll back ons_Initialize here
 +              // BUT: we don't need to! If this gets called, adding always
 +              // succeeds.
        }
  
        MUTATOR_ONREMOVE
                return -1;
        }
  
 -      return 0;
 +      return FALSE;
  }
index d78dfe5d795b228e3c8e55230b1a6d176610e3ee,0000000000000000000000000000000000000000..cb4aeb4cf4ff02b2748fcaa83322322c1e7f80a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,94 @@@
- #define CP_THINKRATE 0.2
 +// these are needed since mutators are compiled last
 +
 +#ifdef SVQC
 +
 +.entity ons_toucher; // player who touched the control point
 +
 +// control point / generator constants
++#define ONS_CP_THINKRATE 0.2
 +#define GEN_THINKRATE 1
 +#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13))
 +#define CPGEN_WAYPOINT_OFFSET ('0 0 128')
 +#define CPICON_OFFSET ('0 0 96')
 +
 +// list of generators on the map
 +entity ons_worldgeneratorlist;
 +.entity ons_worldgeneratornext;
 +.entity ons_stalegeneratornext;
 +
 +// list of control points on the map
 +entity ons_worldcplist;
 +.entity ons_worldcpnext;
 +.entity ons_stalecpnext;
 +
 +// list of links on the map
 +entity ons_worldlinklist;
 +.entity ons_worldlinknext;
 +.entity ons_stalelinknext;
 +
 +// definitions
 +.entity sprite;
 +.string target2;
 +.float iscaptured;
 +.float islinked;
 +.float isshielded;
 +.float lasthealth;
 +.float lastteam;
 +.float lastshielded;
 +.float lastcaptured;
 +
 +.float waslinked;
 +
 +float ons_stalemate;
 +
 +.float teleport_antispam;
 +
 +.float ons_roundlost;
 +
 +// waypoint sprites
 +.entity bot_basewaypoint; // generator waypointsprite
 +float wpforenemy_announced;
 +
 +.float isgenneighbor[17];
 +.float iscpneighbor[17];
 +float ons_notification_time[17];
 +
 +.float ons_overtime_damagedelay;
 +
 +.vector ons_deathloc;
 +
 +.entity ons_spawn_by;
 +
 +// declarations for functions used outside gamemode_onslaught.qc
 +void ons_Generator_UpdateSprite(entity e);
 +void ons_ControlPoint_UpdateSprite(entity e);
 +float ons_ControlPoint_Attackable(entity cp, float teamnumber);
 +
 +// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet
 +float ons_captureshield_force; // push force of the shield
 +
 +// bot player logic
 +#define HAVOCBOT_ONS_ROLE_NONE                0
 +#define HAVOCBOT_ONS_ROLE_DEFENSE     2
 +#define HAVOCBOT_ONS_ROLE_ASSISTANT   4
 +#define HAVOCBOT_ONS_ROLE_OFFENSE     8
 +
 +.entity havocbot_ons_target;
 +
 +.float havocbot_role_flags;
 +.float havocbot_attack_time;
 +
 +void havocbot_role_ons_defense();
 +void havocbot_role_ons_offense();
 +void havocbot_role_ons_assistant();
 +
 +void havocbot_ons_reset_role(entity bot);
 +void havocbot_goalrating_items(float ratingscale, vector org, float sradius);
 +void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius);
 +
 +// score rule declarations
 +#define ST_ONS_CAPS 1
 +#define SP_ONS_CAPS 4
 +#define SP_ONS_TAKES 6
 +
 +#endif
index 0000000000000000000000000000000000000000,c869ab69669a6eab1da1abc924c94da93c5bd70e..ef2809ae28f691750ee61341d445be5f047568af
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,18 +1,19 @@@
+ #include "base.qh"
+ #include "mutators.qh"
+ #include "gamemode_assault.qh"
+ #include "gamemode_ca.qh"
+ #include "gamemode_ctf.qh"
+ #include "gamemode_domination.qh"
+ #include "gamemode_keyhunt.qh"
+ #include "gamemode_keepaway.qh"
+ #include "gamemode_nexball.qh"
+ #include "gamemode_lms.qh"
+ #include "gamemode_invasion.qh"
+ #include "gamemode_race.qh"
+ #include "gamemode_cts.qh"
++#include "gamemode_onslaught.qh"
+ #include "mutator_dodging.qh"
+ #include "mutator_overkill.qh"
+ #include "mutator_nades.qh"
+ #include "mutator_buffs.qh"
diff --combined qcsrc/server/progs.src
index 62a35767d8152d9a814c0b4a6f4d1e4d624bb545,78c0b091eb7ecf70205b4086adf96146084a73e9..d2df9dacc88ca21e209ab20427da45fbc7645996
@@@ -11,14 -11,14 +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/gamemode_onslaught.qh
- mutators/mutator_dodging.qh
- mutators/mutator_nades.qh
+ mutators/mutators_include.qh
  
  //// tZork Turrets ////
  tturrets/include/turrets_early.qh
  vehicles/vehicles_def.qh
  
 +generator.qh
 +controlpoint.qh
 +
  campaign.qh
  ../common/campaign_common.qh
  ../common/mapinfo.qh
@@@ -71,8 -70,7 +73,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
@@@ -80,18 -78,18 +81,18 @@@ csqceffects.q
  
  anticheat.qh
  cheats.qh
- playerstats.qh
../common/playerstats.qh
  
  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
@@@ -110,6 -108,8 +111,8 @@@ scores_rules.q
  
  miscfunctions.qc
  
+ mutators/mutators.qc
  waypointsprites.qc
  
  bot/bot.qc
@@@ -135,6 -135,8 +138,8 @@@ pathlib/pathlib.q
  g_world.qc
  g_casings.qc
  
+ mapvoting.qc
  t_jumppads.qc
  t_teleporters.qc
  
@@@ -147,13 -149,20 +152,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
@@@ -212,67 -221,29 +224,32 @@@ spawnpoints.q
  
  portals.qc
  
 +generator.qc
 +controlpoint.qc
 +
  target_spawn.qc
  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
  
  anticheat.qc
  cheats.qc
- playerstats.qc
../common/playerstats.qc
  
  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/teamplay.qc
index 19ec610fdcc198648908fecbcb217b7877f3823a,2f6963bf71b9f0d85c1a86cae7fcafffebadc602..cecc624b861f2ac0c08fbb9a779c794da7743cec
@@@ -13,7 -13,6 +13,6 @@@ void TeamchangeFrags(entity e
        PlayerScore_Clear(e);
  }
  
- void tdm_init();
  void entcs_init();
  
  void LogTeamchange(float player_id, float team_number, float type)
@@@ -83,9 -82,11 +82,11 @@@ void InitGameplayMode(
        if(g_tdm)
        {
                ActivateTeamplay();
-               tdm_init();
+               fraglimit_override = autocvar_g_tdm_point_limit;
+               leadlimit_override = autocvar_g_tdm_point_leadlimit;
                if(autocvar_g_tdm_team_spawns)
                        have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_tdm);
        }
  
        if(g_domination)
                ActivateTeamplay();
                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
+               MUTATOR_ADD(gamemode_domination);
        }
  
        if(g_ctf)
                ActivateTeamplay();
                fraglimit_override = autocvar_capturelimit_override;
                leadlimit_override = autocvar_captureleadlimit_override;
-               MUTATOR_ADD(gamemode_ctf);
                have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_ctf);
        }
  
        if(g_lms)
                ActivateTeamplay();
                fraglimit_override = autocvar_g_ca_point_limit;
                leadlimit_override = autocvar_g_ca_point_leadlimit;
-               MUTATOR_ADD(gamemode_ca);
                if(autocvar_g_ca_team_spawns)
                        have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_ca);
        }
  
        if(g_keyhunt)
                ActivateTeamplay();
                fraglimit_override = autocvar_g_freezetag_point_limit;
                leadlimit_override = autocvar_g_freezetag_point_leadlimit;
-               MUTATOR_ADD(gamemode_freezetag);
                if(autocvar_g_freezetag_team_spawns)
                        have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_freezetag);
        }
  
        if(g_assault)
        {
                ActivateTeamplay();
-               MUTATOR_ADD(gamemode_assault);
                have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_assault);
        }
  
        if(g_onslaught)
        {
                ActivateTeamplay();
 +              fraglimit_override = autocvar_g_onslaught_point_limit;
                have_team_spawns = -1; // request team spawns
                MUTATOR_ADD(gamemode_onslaught);
        }
  
        if(g_race)
        {
                if(autocvar_g_race_teams)
                {
                        ActivateTeamplay();
                }
                else
                        race_teams = 0;
                qualifying_override = autocvar_g_race_qualifying_timelimit_override;
                fraglimit_override = autocvar_g_race_laps_limit;
                leadlimit_override = 0; // currently not supported by race
+               // we need to find out the correct value for g_race_qualifying
+               float want_qualifying = ((qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit) > 0;
+               if(autocvar_g_campaign)
+               {
+                       g_race_qualifying = 1;
+                       independent_players = 1;
+               }
+               else if(!autocvar_g_campaign && want_qualifying)
+               {
+                       g_race_qualifying = 2;
+                       independent_players = 1;
+                       race_fraglimit = (race_fraglimit >= 0) ? fraglimit_override : autocvar_fraglimit;
+                       race_leadlimit = (race_leadlimit >= 0) ? leadlimit_override : autocvar_leadlimit;
+                       race_timelimit = (race_timelimit >= 0) ? timelimit_override : autocvar_timelimit;
+                       fraglimit_override = 0;
+                       leadlimit_override = 0;
+                       timelimit_override = autocvar_g_race_qualifying_timelimit;
+               }
+               else
+               {
+                       g_race_qualifying = 0;
+               }
+               MUTATOR_ADD(gamemode_race);
        }
  
        if(g_cts)
                g_race_qualifying = 1;
                fraglimit_override = 0;
                leadlimit_override = 0;
+               independent_players = 1;
+               MUTATOR_ADD(gamemode_cts);
        }
  
        if(g_nexball)
        {
-         fraglimit_override = autocvar_g_nexball_goallimit;
-         leadlimit_override = autocvar_g_nexball_goalleadlimit;
-         ActivateTeamplay();
-         have_team_spawns = -1; // request team spawns
-           MUTATOR_ADD(gamemode_nexball);
+               fraglimit_override = autocvar_g_nexball_goallimit;
+               leadlimit_override = autocvar_g_nexball_goalleadlimit;
+               ActivateTeamplay();
+               have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_nexball);
        }
  
        if(g_keepaway)
                        cvar_set("g_race_qualifying_timelimit", ftos(qualifying_override));
        }
  
-       if(g_race)
-       {
-               // we need to find out the correct value for g_race_qualifying
-               if(autocvar_g_campaign)
-               {
-                       g_race_qualifying = 1;
-               }
-               else if(!autocvar_g_campaign && autocvar_g_race_qualifying_timelimit > 0)
-               {
-                       g_race_qualifying = 2;
-                       race_fraglimit = autocvar_fraglimit;
-                       race_leadlimit = autocvar_leadlimit;
-                       race_timelimit = autocvar_timelimit;
-                       cvar_set("fraglimit", "0");
-                       cvar_set("leadlimit", "0");
-                       cvar_set("timelimit", ftos(autocvar_g_race_qualifying_timelimit));
-               }
-               else
-                       g_race_qualifying = 0;
-       }
-       if(g_race || g_cts)
-       {
-               if(g_race_qualifying)
-                       independent_players = 1;
-               ScoreRules_race();
-       }
        InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
  }
  
@@@ -288,7 -287,7 +288,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)
@@@ -421,10 -423,7 +424,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);
@@@ -512,7 -511,7 +512,7 @@@ void GetTeamCounts(entity ignore
        FOR_EACH_CLIENT(head)
        {
                float t;
-               if(IS_PLAYER(head))
+               if(IS_PLAYER(head) || head.caplayer)
                        t = head.team;
                else if(head.team_forced > 0)
                        t = head.team_forced; // reserve the spot
@@@ -662,16 -661,8 +662,8 @@@ float FindSmallestTeam(entity pl, floa
        {
                if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl))
                        return 1; // special case for campaign and player joining
-               else if(g_domination)
-                       error("Too few teams available for domination\n");
-               else if(g_ctf)
-                       error("Too few teams available for ctf\n");
-               else if(g_keyhunt)
-                       error("Too few teams available for key hunt\n");
-               else if(g_freezetag)
-                       error("Too few teams available for freeze tag\n");
                else
-                       error("Too few teams available for team deathmatch\n");
+                       error(sprintf("Too few teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
        }
  
        // count how many players are in each team
@@@ -826,7 -817,7 +818,7 @@@ void SV_ChangeTeam(float _color
        }
  
        if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && self.wasplayer)) {
-               sprint(self, "Team changes not allowed\n");
+               Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TEAMCHANGE_NOTALLOWED);
                return; // changing teams is not allowed
        }
  
                GetTeamCounts(self);
                if(!TeamSmallerEqThanTeam(dteam, steam, self))
                {
-                       sprint(self, "Cannot change to a larger/better/shinier team\n");
+                       Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
                        return;
                }
        }
@@@ -997,46 -988,5 +989,5 @@@ void ShufflePlayerOutOfTeam (float sour
  
        if(selected.deadflag == DEAD_NO)
                Damage(selected, selected, selected, 100000, DEATH_AUTOTEAMCHANGE, selected.origin, '0 0 0');
-       centerprint(selected, strcat("You have been moved into a different team to improve team balance\nYou are now on: ", Team_ColoredFullName(selected.team)));
- }
- // code from here on is just to support maps that don't have team entities
- void tdm_spawnteam (string teamname, float teamcolor)
- {
-       entity e;
-       e = spawn();
-       e.classname = "tdm_team";
-       e.netname = teamname;
-       e.cnt = teamcolor;
-       e.team = e.cnt + 1;
- }
- // spawn some default teams if the map is not set up for tdm
- void tdm_spawnteams()
- {
-       float numteams;
-       numteams = autocvar_g_tdm_teams_override;
-       if(numteams < 2)
-               numteams = autocvar_g_tdm_teams;
-       numteams = bound(2, numteams, 4);
-       tdm_spawnteam("Red", NUM_TEAM_1-1);
-       tdm_spawnteam("Blue", NUM_TEAM_2-1);
-       if(numteams >= 3)
-               tdm_spawnteam("Yellow", NUM_TEAM_3-1);
-       if(numteams >= 4)
-               tdm_spawnteam("Pink", NUM_TEAM_4-1);
- }
- void tdm_delayedinit()
- {
-       // if no teams are found, spawn defaults
-       if (find(world, classname, "tdm_team") == world)
-               tdm_spawnteams();
- }
- void tdm_init()
- {
-       InitializeEntity(world, tdm_delayedinit, INITPRIO_GAMETYPE);
+       Send_Notification(NOTIF_ONE, selected, MSG_CENTER, CENTER_DEATH_SELF_AUTOTEAMCHANGE, selected.team);
  }