]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/showspecs
authorMario <mario@smbclan.net>
Sun, 12 Jun 2016 10:43:18 +0000 (20:43 +1000)
committerMario <mario@smbclan.net>
Sun, 12 Jun 2016 10:43:18 +0000 (20:43 +1000)
# Conflicts:
# qcsrc/client/hud.qc
# qcsrc/client/main.qc
# qcsrc/server/cl_client.qc

1  2 
defaultXonotic.cfg
qcsrc/client/autocvars.qh
qcsrc/client/hud/panel/infomessages.qc
qcsrc/client/main.qc
qcsrc/client/main.qh
qcsrc/server/cl_client.qc

diff --combined defaultXonotic.cfg
index 4d3c1fea781856a841a6ed030eb1789a1abb9b15,dea35012a76a36546bfb922a8247b4ea67c904e4..c26d0c8ca0ad72f39f97c89d930feb9717188bf5
@@@ -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 800 // 0.8.0
+ gameversion 801 // 0.8.1
  gameversion_min 0 // git builds see all versions
  gameversion_max 65535 // git builds see all versions
  
@@@ -72,7 -72,7 +72,7 @@@ seta cl_velocityzoom_time 0.2 "time val
  seta cl_spawnzoom 1 "zoom effect immediately when a player spawns"
  seta cl_spawnzoom_speed 1 "speed at which zooming occurs while spawning"
  seta cl_spawnzoom_factor 2 "factor of zoom while spawning"
- seta cl_zoomfactor 5  "how much +zoom will zoom (1-16)"
+ seta cl_zoomfactor 5  "how much +zoom will zoom (1-30)"
  seta cl_zoomspeed 8   "how fast it will zoom (0.5-16), negative values mean instant zoom"
  seta cl_zoomsensitivity 0     "how zoom changes sensitivity (0 = weakest, 1 = strongest)"
  
@@@ -161,30 -161,23 +161,23 @@@ cl_bobfallcycle 3 "speed of the bobfal
  cl_bobfallspeed 200 "necessary amount of speed for bob-falling to occur"
  cl_bobmodel 1 // whether to have gun model move around on screen when moving (only works if cl_bob is not 0), default is 1
  cl_bobmodel_side 0.2 // amount the gun sways to the sides
- cl_bobmodel_speed 5 // rate at which the gun sways
+ cl_bobmodel_speed 10 // rate at which the gun sways
  cl_bobmodel_up 0.1 // amount the gun sways up and down
- cl_leanmodel 1 // enables weapon leaning effect when looking around
- cl_leanmodel_side_speed 0.7 "gun leaning sideways speed"
- cl_leanmodel_side_limit 35 "gun leaning sideways limit"
- cl_leanmodel_side_highpass1 30 "gun leaning sideways pre-highpass in 1/s"
- cl_leanmodel_side_highpass 3 "gun leaning sideways highpass in 1/s"
- cl_leanmodel_side_lowpass 20 "gun leaning sideways lowpass in 1/s"
- cl_leanmodel_up_speed 0.65 "gun leaning upward speed"
- cl_leanmodel_up_limit 50 "gun leaning upward limit"
- cl_leanmodel_up_highpass1 5 "gun leaning upward pre-highpass in 1/s"
- cl_leanmodel_up_highpass 15 "gun leaning upward highpass in 1/s"
- cl_leanmodel_up_lowpass 20 "gun leaning upward lowpass in 1/s"
  cl_followmodel 1 // enables weapon pushing / pulling effect when walking
- cl_followmodel_side_speed 0.25 "gun following sideways speed"
- cl_followmodel_side_limit 6 "gun following sideways limit"
- cl_followmodel_side_highpass1 30 "gun following sideways pre-highpass in 1/s"
- cl_followmodel_side_highpass 5 "gun following sideways highpass in 1/s"
- cl_followmodel_side_lowpass 10 "gun following sideways lowpass in 1/s"
- cl_followmodel_up_speed 0.5 "gun following upward speed"
- cl_followmodel_up_limit 5 "gun following upward limit"
- cl_followmodel_up_highpass1 60 "gun following upward pre-highpass in 1/s"
- cl_followmodel_up_highpass 2 "gun following upward highpass in 1/s"
- cl_followmodel_up_lowpass 10 "gun following upward lowpass in 1/s"
+ seta cl_followmodel_speed 0.3 "gun following speed"
+ seta cl_followmodel_limit 135 "gun following limit"
+ seta cl_followmodel_velocity_absolute 0 "make the effect ignore velocity direction changes (side effect: it causes a glitch when teleporting / passing through a warpzone)"
+ seta cl_followmodel_velocity_lowpass 0.05 "gun following velocity lowpass averaging time"
+ seta cl_followmodel_highpass 0.05 "gun following highpass averaging time"
+ seta cl_followmodel_lowpass 0.03 "gun following lowpass averaging time"
+ cl_leanmodel 1 // enables weapon leaning effect when looking around
+ seta cl_leanmodel_speed 0.3 "gun leaning speed"
+ seta cl_leanmodel_limit 30 "gun leaning limit"
+ seta cl_leanmodel_highpass1 0.2 "gun leaning pre-highpass averaging time"
+ seta cl_leanmodel_highpass 0.2 "gun leaning highpass averaging time"
+ seta cl_leanmodel_lowpass 0.05 "gun leaning lowpass averaging time"
  
  cl_rollangle 0 // amount of view tilt when strafing, default is 2.0
  v_kicktime 0 // how long damage kicks of the view last, default is 0 seconds
@@@ -234,10 -227,12 +227,12 @@@ seta cl_hitsound_nom_damage 25 "damage 
  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_generator_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)"
@@@ -288,14 -283,9 +283,9 @@@ set cl_deathglow 0.8 "number of second
  
  set sv_gibhealth 100 "Minus health a dead body must have in order to get gibbed"
  
- // fragmessage: This allows extra information to be displayed with the frag centerprints.
- set sv_fraginfo 1 "Enable extra frag message information, 0 = Never display, 1 = Display only in warmup mode; 2 = Always display"
- set sv_fraginfo_ping 1 "Enable ping display information, 0 = Never display, 1 = Always display (If the player is a bot, it will say bot instead of the ping.)"
- set sv_fraginfo_handicap 1 "Enable handicap display information, 0 = Never display, 1 = Only when the player has handicap on, 2 = Always display (Displays Off if disabled)"
- set sv_fraginfo_stats 1 "Enable statistics (health/armor) display information, 0 = Never display, 1 = Always display (Only available for the person who was killed)"
  // use default physics
  set sv_friction_on_land 0
+ set sv_friction_slick 0.5
  
  set sv_player_viewoffset "0 0 35" "view offset of the player model"
  set sv_player_mins "-16 -16 -24" "playermodel mins"
@@@ -308,6 -298,7 +298,7 @@@ set sv_doublejump 0 "allow Quake 2-styl
  set sv_jumpspeedcap_min "" "lower bound on the baseline velocity of a jump; final velocity will be >= (jumpheight * min + jumpheight)"
  set sv_jumpspeedcap_max "" "upper bound on the baseline velocity of a jump; final velocity will be <= (jumpheight * max + jumpheight)"
  set sv_jumpspeedcap_max_disable_on_ramps 0 "disable upper baseline velocity bound on ramps to preserve the old rampjump style"
+ set sv_track_canjump 0 "track if the player released the jump key between 2 jumps to decide if they are able to jump or not"
  
  seta sv_precacheplayermodels 1
  seta sv_precacheweapons 0
@@@ -315,6 -306,7 +306,7 @@@ seta sv_precacheitems 
  set sv_spectator_speed_multiplier 1.5
  seta sv_spectate 1 "if set to 1, new clients are allowed to spectate or observe the game, if set to 0 joining clients spawn as players immediately (no spectating)"
  seta sv_defaultcharacter 0 "master switch, if set to 1 the further configuration for replacing all player models, skins and colors is taken from the sv_defaultplayermodel, sv_defaultplayerskin and sv_defaultplayercolors variables"
+ seta sv_defaultcharacterskin 0 "if set to 1 the further configuration for replacing all skins is taken from the sv_defaultplayerskin variables"
  seta sv_defaultplayermodel "models/player/erebus.iqm" "default model selection, only works if sv_defaultcharacter is set to 1; you may append a :<skinnumber> suffix to model names; you can specify multiple, separated by space, and a random one will be chosen"
  seta sv_defaultplayerskin 0 "each model has 1 or more skins (combination of model and skin = character), set which skin of the model you wish the default character to have, only works if sv_defaultcharacter is set to 1; can be overriden by :<skinnumber> suffix in sv_defaultplayermodel"
  seta sv_defaultplayermodel_red ""     "\"\" means see sv_defaultplayermodel"
@@@ -410,9 -402,9 +402,9 @@@ alias g_waypointeditor_unreachable "imp
  locs_enable 0
  pausable 0
  set g_spawnshieldtime 1 "number of seconds you are invincible after you spawned, this shield is lost after you fire"
+ set g_spawnshield_blockdamage 1 "how much spawn shield protects you from damage (1 = full protection)"
  set g_antilag 2       "AntiLag (0 = no AntiLag, 1 = verified client side hit scan, 2 = server side hit scan in the past, 3 = unverified client side hit scan)"
  set g_antilag_nudge 0 "don't touch"
- set g_shootfromclient 2 "let client decide if it has the gun left or right; if set to 2, center handedness is allowed; see also cl_gunalign"
  set g_shootfromeye 0 "shots are fired from your eye/crosshair; visual gun position can still be influenced by cl_gunalign 1 and 2"
  set g_shootfromcenter 0 "weapon gets moved to the center, shots still come from the barrel of your weapon; visual gun position can still be influenced by cl_gunalign 1 and 2"
  set g_shootfromfixedorigin "" "if set to a string like 0 y z, the gun is moved to the given y and z coordinates. If set to a string like x y z, the whole shot origin is used"
@@@ -442,6 -434,9 +434,9 @@@ seta g_maplist_shuffle 1   "new randomiza
  set g_maplist_check_waypoints 0       "when 1, maps are skipped if there currently are bots, but the map has no waypoints"
  set samelevel 0 "when 1, always play the same level over and over again"
  
+ set g_items_mindist 4000 "starting distance for the fading of items"
+ set g_items_maxdist 4500 "maximum distance at which an item can be viewed, after which it will be invisible"
  set g_grab_range 200 "distance at which dragable objects can be grabbed"
  
  set g_cloaked 0 "display all players mostly invisible"
@@@ -472,14 -467,15 +467,15 @@@ 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.0 "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"
  set g_respawn_delay_small_count 0 "Player count per team for g_respawn_delay_small. <=0 values mean the minimum amount of players to have gameplay (typically 2 in FFA, 1 in teamplay)."
  set g_respawn_delay_large 2 "large game number of seconds you have to wait before you can respawn again"
  set g_respawn_delay_large_count 8 "Player count per team for g_respawn_delay_large. <=0 values mean the minimum amount of players to have gameplay (typically 2 in FFA, 1 in teamplay)."
- set g_respawn_delay_max 0 "number of seconds you can wait before you're forced to respawn (only effective with g_forced_respawn 1)"
+ set g_respawn_delay_max 5 "number of seconds you can wait before you're forced to respawn (only effective with g_forced_respawn 1)"
+ set g_respawn_delay_forced 0 "enforce regular respawn delay (prevent gamemode specific respawn delays)"
  set g_respawn_waves 0 "respawn in waves (every n seconds), intended to decrease overwhelming base attacks"
  
  // overtime
@@@ -494,6 -490,7 +490,7 @@@ set g_tdm_on_dm_maps 0 "when this is se
  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_mirrordamage_onlyweapons 0 "for teamplay 4: only apply mirror damage if the attack was from a weapon"
  seta g_friendlyfire 0.500000  "for teamplay 4: fiendly fire factor"
  seta g_friendlyfire_virtual 1 "for teamplay 4: do not actually apply friendly fire, just show graphics effect for it"
  seta g_friendlyfire_virtual_force 1   "for teamplay 4: apply force even though damage was made virtual only"
@@@ -512,10 -509,6 +509,6 @@@ set g_bloodloss 0   "amount of health b
  
  set g_footsteps 1     "serverside footstep sounds"
  
- 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"
  set g_throughfloor_debug 0 "enable debugging messages for throughfloor calculations"
  set g_throughfloor_damage_max_stddev 2 "Maximum standard deviation for splash damage"
  set g_throughfloor_force_max_stddev 10 "Maximum standard deviation for splash force"
@@@ -544,6 -537,7 +537,7 @@@ gl_picmip_sprites 
  gl_picmip_other 1 // so, picmip -1 is best possible quality
  r_mipsprites 1
  r_mipskins 1
+ gl_max_lightmapsize 4096
  r_shadow_realtime_world_lightmaps 1
  r_shadow_realtime_world_importlightentitiesfrommap 0 // Whether build process uses keepLights is nontransparent and may change, so better make keepLights not matter.
  cl_decals_fadetime 5
@@@ -642,6 -636,7 +636,7 @@@ alias weapon_group_7 "impulse 7
  alias weapon_group_8 "impulse 8"
  alias weapon_group_9 "impulse 9"
  alias weapon_group_0 "impulse 14" // cycles the superweapons
+ // TODO: remove after 0.8.2. Default impulse commands for 0.8.1 servers
  exec weapons.cfg
  
  // score log
@@@ -757,6 -752,7 +752,7 @@@ seta g_waypointsprite_crosshairfadedist
  seta g_waypointsprite_distancefadealpha 1 "alpha multiplier near distance"
  seta g_waypointsprite_distancefadescale 0.7 "scale multiplier near the distance"
  seta g_waypointsprite_distancefadedistancemultiplier 0.5 "distance in map sizes from distance where to stop fading"
+ seta g_waypointsprite_itemstime 2 "show waypoints to indicate that some important items (mega health, large armor) are about to respawn: 1 when spectating, 2 even playing in warmup stage"
  set g_waypointsprite_spam 0 "Debugging feature. Set to 10 and load courtfun in race mode to test."
  alias "g_waypointsprite_personal"     "impulse 30"
  alias "g_waypointsprite_personal_p"   "impulse 31"
@@@ -778,6 -774,19 +774,19 @@@ seta g_waypointsprite_turrets 1 "disabl
  seta g_waypointsprite_turrets_maxdist 5000 "max distace for turret sprites"
  seta g_waypointsprite_tactical 1 "tactical overlay on turrets when in a vehicle"
  
+ seta cl_damagetext "0" "Draw damage dealt where you hit the enemy"
+ seta cl_damagetext_format "-{total}" "How to format the damage text. {health}, {armor}, {total}"
+ seta cl_damagetext_color "1 1 0" "Damage text color"
+ seta cl_damagetext_color_per_weapon "0" "Damage text uses weapon color"
+ seta cl_damagetext_size "8" "Damage text font size"
+ seta cl_damagetext_alpha_start "1" "Damage text initial alpha"
+ seta cl_damagetext_alpha_lifetime "3" "Damage text lifetime in seconds"
+ seta cl_damagetext_velocity "0 0 20" "Damage text move direction"
+ seta cl_damagetext_offset "0 -40 0" "Damage text offset"
+ seta cl_damagetext_accumulate_range "30" "Damage text spawned within this range is accumulated"
+ set sv_itemstime 1 "enable networking of left time until respawn for items such as mega health and large armor"
  // so it can be stuffcmd-ed still
  set cl_gravity 800    "but ignored anyway"
  
@@@ -799,6 -808,8 +808,8 @@@ seta menu_sounds 0 "enables menu sound 
  seta menu_tooltips 1 "menu tooltips: 0 disabled, 1 enabled, 2 also shows cvar or console command (when available) changed or executed by the item"
  set menu_picmip_bypass 0 "bypass texture quality enforcement based on system resources, not recommended and may cause crashes!"
  set menu_showboxes 0 "show item bounding boxes (debug)"
+ set menu_cvarlist_onlymodified 0 "show only modified cvars in the cvar list"
+ set menu_force_on_disconnection 1 "force to show the menu this number of seconds after you get disconnected (0 to disable)"
  
  r_textbrightness 0.2
  r_textcontrast 0.8
@@@ -842,6 -853,7 +853,7 @@@ seta scoreboard_offset_right 0.15 "how 
  seta scoreboard_offset_vertical 0.05 "how far (by percent) the scoreboard is offset from the top and bottom of the screen"
  seta scoreboard_bg_scale 0.25 "scale for the tiled scoreboard background"
  seta scoreboard_respawntime_decimals 1 "decimal places to show for the respawntime countdown display on the scoreboard"
+ seta scoreboard_dynamichud 0 "apply the dynamic hud effects to the scoreboard"
  
  seta accuracy_color_levels "0 20 100" "accuracy values at which a specified color (accuracy_color<X>) will be used. If your accuracy is between 2 of these values then a mix of the Xth and X+1th colors will be used. You can specify up to 10 values, in increasing order"
  seta accuracy_color0 "1 0 0"
@@@ -1006,16 -1018,16 +1018,16 @@@ set g_jump_grunt 0   "Do you make a grunt
  
  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 "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"
- seta cl_weaponpriority9 "" "use impulse 209 for prev gun from this list, 219 for best gun, 229 for next gun"
+ seta cl_weaponpriority0 "devastator mortar hagar seeker fireball"                       "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun.  Default value: explosives"
+ seta cl_weaponpriority1 "vaporizer vortex crylink hlac arc electro blaster shockwave"   "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun.  Default value: energy"
+ seta cl_weaponpriority2 "vaporizer vortex rifle"                                        "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun.  Default value: hitscan exact"
+ seta cl_weaponpriority3 "vaporizer vortex rifle machinegun shotgun"                     "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun.  Default value: hitscan all"
+ seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun"            "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun.  Default value: spam weapons"
+ seta cl_weaponpriority5 "blaster shockwave hook porto"                                  "use weapon_priority_5_prev for prev gun from this list, weapon_priority_5_best for best gun, weapon_priority_5_next for next gun.  Default value: weapons for moving"
+ seta cl_weaponpriority6 ""                                                              "use weapon_priority_6_prev for prev gun from this list, weapon_priority_6_best for best gun, weapon_priority_6_next for next gun"
+ seta cl_weaponpriority7 ""                                                              "use weapon_priority_7_prev for prev gun from this list, weapon_priority_7_best for best gun, weapon_priority_7_next for next gun"
+ seta cl_weaponpriority8 ""                                                              "use weapon_priority_8_prev for prev gun from this list, weapon_priority_8_best for best gun, weapon_priority_8_next for next gun"
+ seta cl_weaponpriority9 ""                                                              "use weapon_priority_9_prev for prev gun from this list, weapon_priority_9_best for best gun, weapon_priority_9_next for next gun"
  seta cl_weaponimpulsemode 0 "0: only cycle between currently usable weapons in weapon priority order; 1: cycle between all possible weapons on a key in weapon priority order"
  
  set g_maplist_allow_hidden 0          "allow hidden maps to be, e.g., voted for and in the maplist"
@@@ -1101,7 -1113,6 +1113,6 @@@ seta cl_gentle_damage 0         "client side g
  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"
  
  set _urllib_nextslot 0 "temp variable"
  set cl_warpzone_usetrace 1 "do not touch"
@@@ -1131,11 -1142,6 +1142,6 @@@ set g_mapinfo_settemp_acl "+*" "ACL fo
  
  seta cl_casings_maxcount 100 "maximum amount of shell casings (must be at least 1)"
  seta cl_gibs_maxcount 100 "maximum amount of gibs (must be at least 1)"
- seta cl_vehicle_spiderbot_cross_alpha 0.6
- seta cl_vehicle_spiderbot_cross_size 1
- seta cl_vehicles_hudscale 0.5
- seta cl_vehicles_hudalpha 0.75
- seta cl_vehicles_hud_tactical 1
  
  //cl_gunalign calculator
  seta menu_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"
@@@ -1182,7 -1188,7 +1188,7 @@@ set waypoint_benchmark 0 "quit after wa
  set g_debug_bot_commands 0 "print scripted bot commands before executing"
  set g_debug_defaultsounds 0 "always use default sounds"
  seta cl_forceplayermodels 0 "make everyone look like your own model (requires server to have sv_defaultcharacter 0)"
- seta cl_forceplayercolors 0 "make everyone look like your own color (requires server to have sv_defaultcharacter 0, and is ignored in teamplay with more than two teams)"
+ seta cl_forceplayercolors 0 "make enemies look like your own color (requires server to have sv_defaultcharacter 0); set it to 2 to enable it even in teamplay (only when there is exactly one enemy team)"
  seta cl_forcemyplayermodel "" "set to the model file name you want to show yourself as (does not affect how enemies look with cl_forceplayermodels)"
  seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (does not affect how enemies look with cl_forceplayermodels)"
  seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)"
@@@ -1343,8 -1349,14 +1349,14 @@@ set g_weapon_charge_colormod_red_full 
  set g_weapon_charge_colormod_green_full -0.5
  set g_weapon_charge_colormod_blue_full -1
  
+ // frozen
+ set g_frozen_revive_falldamage 0 "Enable reviving from this amount of fall damage"
+ set g_frozen_revive_falldamage_health 40 "Amount of health player has if they revived from falling"
+ set g_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn"
+ set g_frozen_force 0.6 "How much to multiply the force on a frozen player with"
  // player statistics server URI
- set g_playerstats_uri "" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory."
+ set g_playerstats_gamereport_uri "http://stats.xonotic.org/stats/submit" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory."
  
  // autoscreenshots
  set g_max_info_autoscreenshot 3 "how many info_autoscreenshot entities are allowed"
@@@ -1393,12 -1405,13 +1405,13 @@@ exec balance-xonotic.cf
  exec effects-normal.cfg
  exec physicsX.cfg
  exec turrets.cfg
- exec vehicles.cfg
  exec crosshairs.cfg
  exec gamemodes.cfg
  exec mutators.cfg
  exec notifications.cfg
  exec monsters.cfg
+ exec minigames.cfg
+ exec physics.cfg
  
  // load console command aliases and settings
  exec commands.cfg
@@@ -1423,21 -1436,16 +1436,21 @@@ alias menu_sync "menu_cmd sync
  set sv_join_notices ""
  set sv_join_notices_time 15
  
- set cl_ghost_items 0.45 "enable ghosted items (when between 0 and 1, overrides the alpha value)"
- set cl_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged"
+ seta cl_ghost_items 0.45 "enable ghosted items (when between 0 and 1, overrides the alpha value)"
+ seta cl_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged"
  set sv_simple_items 1 "allow or forbid client use of simple items"
- set cl_simple_items 0 "enable simple items (if server allows)"
- set cl_simpleitems_postfix "_simple" "posfix to add fo model name when simple items are enabled"
+ seta cl_simple_items 0 "enable simple items (if server allows)"
+ set cl_simpleitems_postfix "_luma" "posfix to add fo model name when simple items are enabled"
  set cl_fullbright_items 0 "enable fullbright items (if server allows, controled by g_fullbrightitems)"
  set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0"
  set cl_weapon_stay_alpha 0.75 "Alpha of picked up weapons when g_weapon_stay > 0"
  
 +<<<<<<< HEAD
 +set sv_showspectators 0
 +set cl_showspectators 1
 +=======
  // Facility for config.cfg use ONLY.
  // Interpreted in post-config.cfg.
  seta menu_forced_saved_cvars "" "These cvars will always be saved, despite engine/Xonotic cvar saving status"
  set menu_reverted_nonsaved_cvars "" "These cvars are currently marked as saved in the flags, but have been reverted and won't stay saved. INTERNAL USE ONLY."
 +>>>>>>> master
index 488115f703eae5be5fab42ba70464a51ac688f53,3e222c00f88666c364c9296be1c1315f5e9cfb4c..18b3b78e6b50cee29c02cc50710b1414693c9348
@@@ -1,5 -1,4 +1,4 @@@
- #ifndef CLIENT_AUTOCVARS_H
- #define CLIENT_AUTOCVARS_H
+ #pragma once
  
  bool autocvar__con_chat_maximized;
  bool autocvar__hud_configure;
@@@ -53,7 -52,6 +52,6 @@@ float autocvar_cl_gibs_velocity_random 
  float autocvar_cl_gibs_velocity_scale = 1;
  float autocvar_cl_gibs_avelocity_scale = 1;
  float autocvar_cl_gibs_velocity_up;
- int autocvar_cl_gunalign;
  bool autocvar_cl_hidewaypoints;
  bool autocvar_cl_lockview;
  bool autocvar_cl_nogibs;
@@@ -73,13 -71,19 +71,19 @@@ bool autocvar_cl_spawn_event_particles
  bool autocvar_cl_spawn_event_sound = 1;
  // float autocvar_cl_spawn_point_model;
  bool autocvar_cl_spawn_point_particles;
+ float autocvar_cl_spawn_point_dist_min = 1200;
+ float autocvar_cl_spawn_point_dist_max = 1600;
  bool autocvar_cl_spawnzoom = 1;
  float autocvar_cl_spawnzoom_speed = 1;
  float autocvar_cl_spawnzoom_factor = 2;
  bool autocvar_cl_stripcolorcodes;
- float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
- float autocvar_cl_vehicle_spiderbot_cross_size = 1;
- bool autocvar_cl_vehicles_hud_tactical = 1;
+ bool autocvar_cl_vehicles_alarm = true;
+ bool autocvar_cl_vehicles_hud_tactical = true;
+ float autocvar_cl_vehicles_hudscale = 0.5;
+ float autocvar_cl_vehicles_notify_time = 15;
+ float autocvar_cl_vehicles_crosshair_size = 0.5;
+ bool autocvar_cl_vehicles_crosshair_colorize = true;
+ bool autocvar__vehicles_shownchasemessage;
  bool autocvar_cl_velocityzoom_enabled;
  float autocvar_cl_velocityzoom_factor;
  int autocvar_cl_velocityzoom_type = 3;
@@@ -94,7 -98,6 +98,6 @@@ bool autocvar_cl_unpress_zoom_on_death 
  bool autocvar_cl_unpress_zoom_on_weapon_switch = 1;
  bool autocvar_cl_unpress_attack_on_weapon_switch = 1;
  bool autocvar_con_chat;
- float autocvar_con_chatpos;
  bool autocvar_con_chatrect;
  float autocvar_con_chatsize;
  float autocvar_con_chattime;
@@@ -151,36 -154,6 +154,6 @@@ float autocvar_crosshair_size
  int autocvar_ekg;
  float autocvar_fov;
  float autocvar_g_balance_damagepush_speedfactor;
- float autocvar_g_balance_tuba_attenuation;
- float autocvar_g_balance_tuba_fadetime;
- float autocvar_g_balance_tuba_volume;
- int autocvar_g_balance_tuba_pitchstep;
- float autocvar_g_warmup_limit;
- bool autocvar_g_waypointsprite_uppercase;
- float autocvar_g_waypointsprite_alpha;
- float autocvar_g_waypointsprite_crosshairfadealpha;
- float autocvar_g_waypointsprite_crosshairfadedistance;
- float autocvar_g_waypointsprite_crosshairfadescale;
- float autocvar_g_waypointsprite_distancealphaexponent;
- float autocvar_g_waypointsprite_distancefadealpha;
- float autocvar_g_waypointsprite_distancefadedistancemultiplier;
- float autocvar_g_waypointsprite_distancefadescale;
- float autocvar_g_waypointsprite_edgefadealpha;
- float autocvar_g_waypointsprite_edgefadedistance;
- float autocvar_g_waypointsprite_edgefadescale;
- float autocvar_g_waypointsprite_edgeoffset_bottom;
- float autocvar_g_waypointsprite_edgeoffset_left;
- float autocvar_g_waypointsprite_edgeoffset_right;
- float autocvar_g_waypointsprite_edgeoffset_top;
- float autocvar_g_waypointsprite_fontsize;
- float autocvar_g_waypointsprite_minalpha;
- float autocvar_g_waypointsprite_minscale;
- float autocvar_g_waypointsprite_normdistance;
- float autocvar_g_waypointsprite_scale;
- int autocvar_g_waypointsprite_spam;
- float autocvar_g_waypointsprite_timealphaexponent;
- bool autocvar_g_waypointsprite_turrets = true;
- float autocvar_g_waypointsprite_turrets_maxdist = 5000;
  bool autocvar_hud_cursormode = true;
  float autocvar_hud_colorflash_alpha;
  bool autocvar_hud_configure_checkcollisions;
@@@ -220,8 -193,25 +193,25 @@@ string autocvar_hud_dock
  float autocvar_hud_dock_alpha;
  string autocvar_hud_dock_color;
  bool autocvar_hud_dock_color_team;
+ bool autocvar_hud_panel_weapons_dynamichud      = true;
+ bool autocvar_hud_panel_ammo_dynamichud         = true;
+ bool autocvar_hud_panel_powerups_dynamichud     = true;
+ bool autocvar_hud_panel_healtharmor_dynamichud  = true;
+ bool autocvar_hud_panel_notify_dynamichud       = true;
+ bool autocvar_hud_panel_timer_dynamichud        = true;
+ bool autocvar_hud_panel_radar_dynamichud        = true;
+ bool autocvar_hud_panel_score_dynamichud        = true;
+ bool autocvar_hud_panel_racetimer_dynamichud    = true;
+ bool autocvar_hud_panel_vote_dynamichud         = true;
+ bool autocvar_hud_panel_modicons_dynamichud     = true;
+ bool autocvar_hud_panel_pressedkeys_dynamichud  = true;
+ bool autocvar_hud_panel_engineinfo_dynamichud   = true;
+ bool autocvar_hud_panel_infomessages_dynamichud = false;
+ bool autocvar_hud_panel_physics_dynamichud      = true;
+ bool autocvar_hud_panel_centerprint_dynamichud  = true;
+ bool autocvar_hud_panel_itemstime_dynamichud    = true;
  bool autocvar_hud_panel_ammo;
- bool autocvar_hud_panel_ammo_iconalign; // TODO: check if this should be turned into an int
+ bool autocvar_hud_panel_ammo_iconalign;
  int autocvar_hud_panel_ammo_maxammo;
  bool autocvar_hud_panel_ammo_onlycurrent;
  float autocvar_hud_panel_ammo_noncurrent_alpha = 0.7;
@@@ -233,8 -223,8 +223,8 @@@ bool autocvar_hud_panel_ammo_text
  string autocvar_hud_panel_bg;
  float autocvar_hud_panel_bg_alpha;
  float autocvar_hud_panel_bg_border;
- vector autocvar_hud_panel_bg_color; // TODO: int?
- float autocvar_hud_panel_bg_color_team; // ^
+ vector autocvar_hud_panel_bg_color;
+ float autocvar_hud_panel_bg_color_team;
  float autocvar_hud_panel_bg_padding;
  bool autocvar_hud_panel_centerprint;
  float autocvar_hud_panel_centerprint_align;
@@@ -257,6 -247,7 +247,7 @@@ float autocvar_hud_panel_engineinfo_fra
  float autocvar_hud_panel_fg_alpha;
  bool autocvar_hud_panel_healtharmor;
  int autocvar_hud_panel_healtharmor_baralign;
+ bool autocvar_hud_panel_healtharmor_combined;
  bool autocvar_hud_panel_healtharmor_flip;
  int autocvar_hud_panel_healtharmor_iconalign;
  int autocvar_hud_panel_healtharmor_maxarmor;
@@@ -282,10 -273,12 +273,12 @@@ float autocvar_hud_panel_notify_fontsiz
  float autocvar_hud_panel_notify_time;
  float autocvar_hud_panel_notify_icon_aspect;
  bool autocvar_hud_panel_physics;
+ float autocvar_hud_panel_physics_acceleration_movingaverage = 1;
  float autocvar_hud_panel_physics_acceleration_progressbar_mode;
  float autocvar_hud_panel_physics_acceleration_progressbar_scale;
  float autocvar_hud_panel_physics_acceleration_progressbar_nonlinear;
  float autocvar_hud_panel_physics_acceleration_max;
+ float autocvar_hud_panel_physics_update_interval;
  int autocvar_hud_panel_physics_progressbar;
  bool autocvar_hud_panel_physics_acceleration_vertical;
  int autocvar_hud_panel_physics_baralign;
@@@ -300,18 -293,15 +293,15 @@@ bool autocvar_hud_panel_physics_topspee
  float autocvar_hud_panel_physics_topspeed_time;
  bool autocvar_hud_panel_powerups;
  int autocvar_hud_panel_powerups_baralign;
- bool autocvar_hud_panel_powerups_flip;
  int autocvar_hud_panel_powerups_iconalign;
  bool autocvar_hud_panel_powerups_progressbar;
- bool 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;
  bool autocvar_hud_panel_powerups_text;
  int autocvar_hud_panel_pressedkeys;
  float autocvar_hud_panel_pressedkeys_aspect;
  bool autocvar_hud_panel_pressedkeys_attack;
+ float autocvar_hud_panel_quickmenu_translatecommands;
+ string autocvar_hud_panel_quickmenu_file;
+ float autocvar_hud_panel_quickmenu_time;
  bool autocvar_hud_panel_racetimer;
  int autocvar_hud_panel_radar;
  float autocvar_hud_panel_radar_foreground_alpha;
@@@ -352,12 -342,18 +342,18 @@@ float autocvar_hud_panel_weapons_compla
  int autocvar_hud_panel_weapons_label;
  float autocvar_hud_panel_weapons_label_scale = 0.5;
  bool autocvar_hud_panel_weapons_onlyowned;
+ float autocvar_hud_panel_weapons_noncurrent_alpha = 1;
+ float autocvar_hud_panel_weapons_noncurrent_scale = 1;
+ float autocvar_hud_panel_weapons_selection_radius = 0;
+ float autocvar_hud_panel_weapons_selection_speed = 10;
  float autocvar_hud_panel_weapons_timeout;
  int autocvar_hud_panel_weapons_timeout_effect;
  float autocvar_hud_panel_weapons_timeout_fadebgmin;
  float autocvar_hud_panel_weapons_timeout_fadefgmin;
  float autocvar_hud_panel_weapons_timeout_speed_in = 0.25;
  float autocvar_hud_panel_weapons_timeout_speed_out = 0.75;
+ //float autocvar_hud_panel_quickmenu;
+ float autocvar_hud_panel_quickmenu_align;
  vector autocvar_hud_progressbar_acceleration_color;
  vector autocvar_hud_progressbar_acceleration_neg_color;
  float autocvar_hud_progressbar_alpha;
@@@ -369,6 -365,8 +365,8 @@@ vector autocvar_hud_progressbar_shield_
  vector autocvar_hud_progressbar_speed_color;
  vector autocvar_hud_progressbar_strength_color;
  vector autocvar_hud_progressbar_superweapons_color;
+ vector autocvar_hud_progressbar_vehicles_ammo1_color;
+ vector autocvar_hud_progressbar_vehicles_ammo2_color;
  bool autocvar_hud_showbinds;
  bool autocvar_hud_showbinds_limit;
  bool autocvar__hud_showbinds_reload;
@@@ -393,6 -391,7 +391,7 @@@ float autocvar_hud_shownames_offset
  string autocvar_hud_skin;
  float autocvar_menu_mouse_speed;
  string autocvar_menu_skin;
+ float autocvar_r_drawviewmodel;
  int autocvar_r_fakelight;
  int autocvar_r_fullbright;
  float autocvar_r_letterbox;
@@@ -419,18 -418,23 +418,23 @@@ float autocvar_scoreboard_offset_left
  float autocvar_scoreboard_offset_right;
  float autocvar_scoreboard_offset_vertical;
  float autocvar_scoreboard_respawntime_decimals;
+ float autocvar_scoreboard_dynamichud = 1;
  bool autocvar_v_flipped;
  float autocvar_vid_conheight;
  float autocvar_vid_conwidth;
  float autocvar_vid_pixelheight;
  float autocvar_viewsize;
+ bool autocvar_cl_eventchase_vehicle = 1;
+ vector autocvar_cl_eventchase_vehicle_viewoffset = '0 0 80';
+ float autocvar_cl_eventchase_vehicle_distance = 250;
  int autocvar_cl_hitsound;
  float autocvar_cl_hitsound_min_pitch = 0.75;
  float autocvar_cl_hitsound_max_pitch = 1.5;
  float autocvar_cl_hitsound_nom_damage = 25;
  float autocvar_cl_hitsound_antispam_time;
  int autocvar_cl_eventchase_death = 1;
- int autocvar_cl_eventchase_nexball = 1;
+ vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80';
+ float autocvar_cl_eventchase_generator_distance = 400;
  float autocvar_cl_eventchase_distance = 140;
  float autocvar_cl_eventchase_speed = 1.3;
  vector autocvar_cl_eventchase_maxs = '12 12 8';
@@@ -460,7 -464,6 +464,7 @@@ string autocvar__cl_playermodel
  float autocvar_cl_deathglow;
  bool autocvar_developer_csqcentities;
  float autocvar_g_jetpack_attenuation;
 +bool autocvar_cl_showspectators;
  string autocvar_crosshair_hmg = "";
  vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2';
  float autocvar_crosshair_hmg_alpha = 1;
@@@ -470,4 -473,5 +474,5 @@@ vector autocvar_crosshair_rpc_color = '
  float autocvar_crosshair_rpc_alpha = 1;
  float autocvar_crosshair_rpc_size = 1;
  int autocvar_cl_nade_timer;
- #endif
+ bool autocvar_cl_items_nofade;
+ float autocvar_slowmo;
index 0000000000000000000000000000000000000000,a197963e75492145545a315b9de468585059cdce..fe16ef14798e772e886f6a3bfde00a16457a2b53
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,191 +1,209 @@@
+ #include "infomessages.qh"
+ #include <common/ent_cs.qh>
+ #include <common/mapinfo.qh>
+ // Info messages panel (#14)
+ #define drawInfoMessage(s) MACRO_BEGIN {                                                                                                                                                      \
+       if(autocvar_hud_panel_infomessages_flip)                                                                                                                                                \
+               o.x = pos.x + mySize.x - stringwidth(s, true, fontsize);                                                                                                        \
+       drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL);                                                                                                               \
+       o.y += fontsize.y;                                                                                                                                                                                              \
+ } MACRO_END
+ void HUD_InfoMessages()
+ {
+       if(!autocvar__hud_configure)
+       {
+               if(!autocvar_hud_panel_infomessages) return;
+       }
+       HUD_Panel_UpdateCvars();
+       vector pos, mySize;
+       pos = panel_pos;
+       mySize = panel_size;
+       if (autocvar_hud_panel_infomessages_dynamichud)
+               HUD_Scale_Enable();
+       else
+               HUD_Scale_Disable();
+       HUD_Panel_DrawBg(1);
+       if(panel_bg_padding)
+       {
+               pos += '1 1 0' * panel_bg_padding;
+               mySize -= '2 2 0' * panel_bg_padding;
+       }
+       // always force 5:1 aspect
+       vector newSize = '0 0 0';
+       if(mySize.x/mySize.y > 5)
+       {
+               newSize.x = 5 * mySize.y;
+               newSize.y = mySize.y;
+               pos.x = pos.x + (mySize.x - newSize.x) / 2;
+       }
+       else
+       {
+               newSize.y = 1/5 * mySize.x;
+               newSize.x = mySize.x;
+               pos.y = pos.y + (mySize.y - newSize.y) / 2;
+       }
+       mySize = newSize;
+       entity tm;
+       vector o;
+       o = pos;
+       vector fontsize;
+       fontsize = '0.20 0.20 0' * mySize.y;
+       float a;
+       a = panel_fg_alpha;
+       string s;
+       if(!autocvar__hud_configure)
+       {
+               if(spectatee_status)
+               {
+                       a = 1;
+                       if(spectatee_status == -1)
+                               s = _("^1Observing");
+                       else
+                               s = sprintf(_("^1Spectating: ^7%s"), entcs_GetName(current_player));
+                       drawInfoMessage(s);
+                       if(spectatee_status == -1)
+                               s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
+                       else
+                               s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
+                       drawInfoMessage(s);
+                       if(spectatee_status == -1)
+                               s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
+                       else
+                               s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2"));
+                       drawInfoMessage(s);
+                       s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
+                       drawInfoMessage(s);
+                       if(gametype == MAPINFO_TYPE_LMS)
+                       {
+                               entity sk;
+                               sk = playerslots[player_localnum];
+                               if(sk.(scores[ps_primary]) >= 666)
+                                       s = _("^1Match has already begun");
+                               else if(sk.(scores[ps_primary]) > 0)
+                                       s = _("^1You have no more lives left");
+                               else
+                                       s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+                       }
+                       else
+                               s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+                       drawInfoMessage(s);
+               }
+               if (time < STAT(GAMESTARTTIME))
+               {
+                       //we need to ceil, otherwise the countdown would be off by .5 when using round()
+                       float countdown = ceil(STAT(GAMESTARTTIME) - time);
+                       s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
+                       drawInfoMessage(s);
+               }
+               if(warmup_stage)
+               {
+                       s = _("^2Currently in ^1warmup^2 stage!");
+                       drawInfoMessage(s);
+               }
++              if(autocvar_cl_showspectators)
++              if(num_spectators)
++              //if(spectatee_status != -1)
++              {
++                      s = ((spectatee_status) ? _("^1Spectating this player:") : _("^1Spectating you:"));
++                      //drawInfoMessage(s)
++                      int limit = min(num_spectators, MAX_SPECTATORS);
++                      for(int i = 0; i < limit; ++i)
++                      {
++                              float slot = spectatorlist[i];
++                              if(i == 0)
++                                      s = strcat(s, " ^3", entcs_GetName(slot));
++                              else
++                                      s = strcat("^3", entcs_GetName(slot));
++                              drawInfoMessage(s);
++                      }
++              }
++
+               string blinkcolor;
+               if(time % 1 >= 0.5)
+                       blinkcolor = "^1";
+               else
+                       blinkcolor = "^3";
+               if(ready_waiting && !spectatee_status)
+               {
+                       if(ready_waiting_for_me)
+                       {
+                               if(warmup_stage)
+                                       s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+                               else
+                                       s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+                       }
+                       else
+                       {
+                               if(warmup_stage)
+                                       s = _("^2Waiting for others to ready up to end warmup...");
+                               else
+                                       s = _("^2Waiting for others to ready up...");
+                       }
+                       drawInfoMessage(s);
+               }
+               else if(warmup_stage && !spectatee_status)
+               {
+                       s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
+                       drawInfoMessage(s);
+               }
+               if(teamplay && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
+               {
+                       float ts_min = 0, ts_max = 0;
+                       tm = teams.sort_next;
+                       if (tm)
+                       {
+                               for (; tm.sort_next; tm = tm.sort_next)
+                               {
+                                       if(!tm.team_size || tm.team == NUM_SPECTATOR)
+                                               continue;
+                                       if(!ts_min) ts_min = tm.team_size;
+                                       else ts_min = min(ts_min, tm.team_size);
+                                       if(!ts_max) ts_max = tm.team_size;
+                                       else ts_max = max(ts_max, tm.team_size);
+                               }
+                               if ((ts_max - ts_min) > 1)
+                               {
+                                       s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
+                                       tm = GetTeam(myteam, false);
+                                       if (tm)
+                                       if (tm.team != NUM_SPECTATOR)
+                                       if (tm.team_size == ts_max)
+                                               s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
+                                       drawInfoMessage(s);
+                               }
+                       }
+               }
+       }
+       else
+       {
+               s = _("^7Press ^3ESC ^7to show HUD options.");
+               drawInfoMessage(s);
+               s = _("^3Doubleclick ^7a panel for panel-specific options.");
+               drawInfoMessage(s);
+               s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
+               drawInfoMessage(s);
+               s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
+               drawInfoMessage(s);
+       }
+ }
diff --combined qcsrc/client/main.qc
index 71198ebc3e23e5223f3cadf4c7506504725b9892,b6cca28b141a80db7f0cdd34839ac9857e7c33eb..7025b5df159c2ae0293a6c0d4a2e9cdd8d31d310
+ #include "main.qh"
+ #include <common/effects/qc/all.qh>
+ #include "hud/all.qh"
  #include "mapvoting.qh"
- #include "modeleffects.qh"
- #include "particles.qh"
+ #include "mutators/events.qh"
+ #include "hud/panel/quickmenu.qh"
  #include "scoreboard.qh"
  #include "shownames.qh"
- #include "target_music.qh"
- #include "tturrets.qh"
- #include "tuba.qh"
+ #include <common/t_items.qh>
  #include "wall.qh"
- #include "waypointsprites.qh"
- #include "vehicles/vehicles.qh"
- #include "../server/vehicles/bumblebee.qh"
- #include "../common/net_notice.qh"
- #include "../common/monsters/monsters.qh"
- #include "../warpzonelib/client.qh"
+ #include "weapons/projectile.qh"
+ #include <common/deathtypes/all.qh>
+ #include <common/items/all.qh>
+ #include <common/mapinfo.qh>
+ #include <common/minigames/cl_minigames.qh>
+ #include <common/minigames/cl_minigames_hud.qh>
+ #include <common/net_notice.qh>
+ #include <common/triggers/include.qh>
+ #include <common/vehicles/all.qh>
+ #include <lib/csqcmodel/cl_model.qh>
+ #include <lib/csqcmodel/interpolate.qh>
+ #include <lib/warpzone/client.qh>
  
  // --------------------------------------------------------------------------
  // BEGIN REQUIRED CSQC FUNCTIONS
  //include "main.qh"
  
- entity clearentity_ent;
- void clearentity(entity e)
+ #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED
+ void draw_cursor(vector pos, vector ofs, string img, vector col, float a)
  {
-       if (!clearentity_ent)
-       {
-               clearentity_ent = spawn();
-               clearentity_ent.classname = "clearentity";
-       }
-       int n = e.entnum;
-       copyentity(clearentity_ent, e);
-       e.entnum = n;
+       ofs = eX * (ofs.x * SIZE_CURSOR.x) + eY * (ofs.y * SIZE_CURSOR.y);
+       drawpic(pos - ofs, strcat(draw_currentSkin, img), SIZE_CURSOR, col, a, DRAWFLAG_NORMAL);
  }
  
- #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED
- void menu_show_error()
+ void draw_cursor_normal(vector pos, vector col, float a)
  {
-       drawstring('0 200 0', _("ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!"), '8 8 0', '1 0 0', 1, 0);
+       draw_cursor(pos, OFFSET_CURSOR, "/cursor", col, a);
  }
  
- // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)
- // Useful for precaching things
- void menu_sub_null()
+ void LoadMenuSkinValues()
  {
+       int fh = -1;
+       if(cvar_string("menu_skin") != "")
+       {
+               draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
+               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
+       }
+       if(fh < 0 && cvar_defstring("menu_skin") != "")
+       {
+               cvar_set("menu_skin", cvar_defstring("menu_skin"));
+               draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
+               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
+       }
+       if(fh < 0)
+       {
+               draw_currentSkin = "gfx/menu/default";
+               fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
+       }
+       draw_currentSkin = strzone(draw_currentSkin);
+       if(fh >= 0)
+       {
+               string s;
+               while((s = fgets(fh)))
+               {
+                       int n = tokenize_console(s);
+                       if (n < 2)
+                               continue;
+                       if(substring(argv(0), 0, 2) == "//")
+                               continue;
+                       if(argv(0) == "SIZE_CURSOR")
+                               SIZE_CURSOR = stov(substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
+                       else if(argv(0) == "OFFSET_CURSOR")
+                               OFFSET_CURSOR = stov(substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
+               }
+               fclose(fh);
+       }
  }
  
- string forcefog;
- void WaypointSprite_Load();
+ // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)
+ // Useful for precaching things
  void ConsoleCommand_macro_init();
- void CSQC_Init(void)
+ void CSQC_Init()
  {
-       prvm_language = cvar_string("prvm_language");
+       prvm_language = strzone(cvar_string("prvm_language"));
  
  #ifdef WATERMARK
-       dprintf("^4CSQC Build information: ^1%s\n", WATERMARK);
+       LOG_INFOF("^4CSQC Build information: ^1%s\n", WATERMARK);
  #endif
  
-       int i;
+       {
+               int i = 0;
+               for ( ; i < 255; ++i)
+                       if (getplayerkeyvalue(i, "viewentity") == "")
+                               break;
+               maxclients = i;
+       }
+       // needs to be done so early because of the constants they create
+       static_init();
+       static_init_late();
+       static_init_precache();
  
        binddb = db_create();
        tempdb = db_create();
        compressShortVector_init();
  
        draw_endBoldFont();
-       menu_visible = false;
-       menu_show = menu_show_error;
-       menu_action = func_null;
-       for(i = 0; i < 255; ++i)
-               if(getplayerkeyvalue(i, "viewentity") == "")
-                       break;
-       maxclients = i;
  
        //registercommand("hud_configure");
        //registercommand("hud_save");
        registercvar("cl_nade_type", "3");
        registercvar("cl_pokenade_type", "zombie");
  
+       registercvar("cl_jumpspeedcap_min", "");
+       registercvar("cl_jumpspeedcap_max", "");
+       registercvar("cl_multijump", "1");
+       registercvar("cl_spawn_near_teammate", "1");
        gametype = 0;
  
        // hud_fields uses strunzone on the titles!
-       for(i = 0; i < MAX_HUD_FIELDS; ++i)
+       for(int i = 0; i < MAX_HUD_FIELDS; ++i)
                hud_title[i] = strzone("(null)");
  
        Cmd_HUD_SetFields(0);
  
        GetTeam(NUM_SPECTATOR, true); // add specs first
  
-       // needs to be done so early because of the constants they create
-       CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
-       CALL_ACCUMULATED_FUNCTION(RegisterMonsters);
-       CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
-       CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
-       CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
-       CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
-       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
-       WaypointSprite_Load();
        // precaches
-       precache_model("null");
-       precache_sound("misc/hit.wav");
-       precache_sound("misc/typehit.wav");
-       Projectile_Precache();
-       Hook_Precache();
-       GibSplash_Precache();
-       Casings_Precache();
-       Vehicles_Precache();
-       turrets_precache();
-       Tuba_Precache();
-       CSQCPlayer_Precache();
  
        if(autocvar_cl_reticle)
        {
                // weapon reticles are precached in weapon files
        }
  
-       get_mi_min_max_texcoords(1); // try the CLEVER way first
-       minimapname = strcat("gfx/", mi_shortname, "_radar.tga");
-       shortmapname = mi_shortname;
-       if(precache_pic(minimapname) == "")
        {
-               // but maybe we have a non-clever minimap
-               minimapname = strcat("gfx/", mi_shortname, "_mini.tga");
-               if(precache_pic(minimapname) == "")
-                       minimapname = ""; // FAIL
-               else
-                       get_mi_min_max_texcoords(0); // load new texcoords
-       }
+               get_mi_min_max_texcoords(1); // try the CLEVER way first
+               minimapname = strcat("gfx/", mi_shortname, "_radar.tga");
+               shortmapname = mi_shortname;
  
-       mi_center = (mi_min + mi_max) * 0.5;
-       mi_scale = mi_max - mi_min;
-       minimapname = strzone(minimapname);
+               if (precache_pic(minimapname) == "")
+               {
+                       // but maybe we have a non-clever minimap
+                       minimapname = strcat("gfx/", mi_shortname, "_mini.tga");
+                       if (precache_pic(minimapname) == "")
+                               minimapname = ""; // FAIL
+                       else
+                               get_mi_min_max_texcoords(0); // load new texcoords
+               }
  
-       WarpZone_Init();
+               mi_center = (mi_min + mi_max) * 0.5;
+               mi_scale = mi_max - mi_min;
+               minimapname = strzone(minimapname);
+       }
  
        hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
-       draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin")));
+       LoadMenuSkinValues();
  }
  
  // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
- void Shutdown(void)
+ void Shutdown()
  {
        WarpZone_Shutdown();
  
        if(autocvar_chase_active < 0)
                cvar_set("chase_active", "0");
  
+       cvar_set("slowmo", cvar_defstring("slowmo")); // reset it back to 'default'
        if (!isdemo())
        {
                if (!(calledhooks & HOOK_START))
                if (!(calledhooks & HOOK_END))
                        localcmd("\ncl_hook_gameend\n");
        }
+       deactivate_minigame();
+       HUD_MinigameMenu_Close(NULL, NULL, NULL);
  }
  
  .float has_team;
  float SetTeam(entity o, int Team)
  {
+     TC(int, Team);
+       devassert_once(Team);
        entity tm;
        if(teamplay)
        {
                        default:
                                if(GetTeam(Team, false) == world)
                                {
-                                       dprintf("trying to switch to unsupported team %d\n", Team);
+                                       LOG_TRACEF("trying to switch to unsupported team %d\n", Team);
                                        Team = NUM_SPECTATOR;
                                }
                                break;
                        default:
                                if(GetTeam(Team, false) == world)
                                {
-                                       dprintf("trying to switch to unsupported team %d\n", Team);
+                                       LOG_TRACEF("trying to switch to unsupported team %d\n", Team);
                                        Team = NUM_SPECTATOR;
                                }
                                break;
        return false;
  }
  
- void Playerchecker_Think()
+ void Playerchecker_Think(entity this)
  {
      int i;
        entity e;
        for(i = 0; i < maxclients; ++i)
        {
                e = playerslots[i];
-               if(GetPlayerName(i) == "")
+               if(entcs_GetName(i) == "")
                {
                        if(e.sort_prev)
                        {
                        {
                                // player connected
                                if (!e)
-                                       playerslots[i] = e = spawn();
+                               {
+                                       playerslots[i] = e = new_pure(playerslot);
+                               }
                                e.sv_entnum = i;
                                e.ping = 0;
                                e.ping_packetloss = 0;
                                e.ping_movementloss = 0;
                                //e.gotscores = 0; // we might already have the scores...
-                               SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with HUD_UpdatePlayerTeams
+                               int t = entcs_GetScoreTeam(i);
+                               if (t) SetTeam(e, t); // will not hurt; later updates come with HUD_UpdatePlayerTeams
                                RegisterPlayer(e);
                                HUD_UpdatePlayerPos(e);
                        }
                }
        }
-       self.nextthink = time + 0.2;
+       this.nextthink = time + 0.2;
  }
  
- void Porto_Init();
  void TrueAim_Init();
- void PostInit(void)
+ void PostInit()
  {
-       entity playerchecker;
-       playerchecker = spawn();
-       playerchecker.think = Playerchecker_Think;
+       entity playerchecker = new_pure(playerchecker);
+       setthink(playerchecker, Playerchecker_Think);
        playerchecker.nextthink = time + 0.2;
  
-       Porto_Init();
        TrueAim_Init();
  
        postinit = true;
  // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0.
  // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta.
  // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos.
- float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)
+ float CSQC_InputEvent(int bInputType, float nPrimary, float nSecondary)
  {
-       float bSkipKey;
-       bSkipKey = false;
+     TC(int, bInputType);
        if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary))
                return true;
  
+       if (QuickMenu_InputEvent(bInputType, nPrimary, nSecondary))
+               return true;
+       if (HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary))
+               return true;
        if (MapVote_InputEvent(bInputType, nPrimary, nSecondary))
                return true;
  
-       if(menu_visible && menu_action)
-               if(menu_action(bInputType, nPrimary, nSecondary))
-                       return true;
+       if (HUD_Minigame_InputEvent(bInputType, nPrimary, nSecondary))
+               return true;
  
-       return bSkipKey;
+       return false;
  }
  
  // END REQUIRED CSQC FUNCTIONS
  
  // --------------------------------------------------------------------------
  // BEGIN OPTIONAL CSQC FUNCTIONS
- void Ent_RemoveEntCS()
- {
-       entcs_receiver[self.sv_entnum] = world;
- }
- void Ent_ReadEntCS()
- {
-     int sf;
-       InterpolateOrigin_Undo();
  
-       self.classname = "entcs_receiver";
-       sf = ReadByte();
+ void Ent_Remove(entity this);
  
-       if(sf & 1)
-               self.sv_entnum = ReadByte();
-       if(sf & 2)
-       {
-               self.origin_x = ReadShort();
-               self.origin_y = ReadShort();
-               self.origin_z = ReadShort();
-               setorigin(self, self.origin);
-       }
-       if(sf & 4)
-       {
-               self.angles_y = ReadByte() * 360.0 / 256;
-               self.angles_x = self.angles_z = 0;
-       }
-       if(sf & 8)
-               self.healthvalue = ReadByte() * 10;
-       if(sf & 16)
-               self.armorvalue = ReadByte() * 10;
-       entcs_receiver[self.sv_entnum] = self;
-       self.entremove = Ent_RemoveEntCS;
-       self.iflags |= IFLAG_ORIGIN;
-       InterpolateOrigin_Note();
- }
- void Ent_Remove();
- void Ent_RemovePlayerScore()
+ void Ent_RemovePlayerScore(entity this)
  {
-       if(self.owner) {
-               SetTeam(self.owner, -1);
-               self.owner.gotscores = 0;
+       if(this.owner) {
+               SetTeam(this.owner, -1);
+               this.owner.gotscores = 0;
                for(int i = 0; i < MAX_SCORE; ++i) {
-                       self.owner.(scores[i]) = 0; // clear all scores
+                       this.owner.(scores[i]) = 0; // clear all scores
                }
        }
  }
  
void Ent_ReadPlayerScore()
NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
  {
+       make_pure(this);
        int i, n;
        bool isNew;
        entity o;
  
        // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN
        // (no I've never heard of M-x replace-string, sed, or anything like that)
-       isNew = !self.owner; // workaround for DP bug
+       isNew = !this.owner; // workaround for DP bug
        n = ReadByte()-1;
  
  #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
-       if(!isNew && n != self.sv_entnum)
+       if(!isNew && n != this.sv_entnum)
        {
                //print("A CSQC entity changed its owner!\n");
-               printf("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(self), self.classname);
+               LOG_INFOF("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", etof(this), this.classname);
                isNew = true;
-               Ent_Remove();
-               self.enttype = ENT_CLIENT_SCORES;
+               Ent_Remove(this);
        }
  #endif
  
-       self.sv_entnum = n;
+       this.sv_entnum = n;
  
-       if (!(playerslots[self.sv_entnum]))
-               playerslots[self.sv_entnum] = spawn();
-       o = self.owner = playerslots[self.sv_entnum];
-       o.sv_entnum = self.sv_entnum;
+       o = playerslots[this.sv_entnum];
+       if (!o)
+       {
+               o = playerslots[this.sv_entnum] = new_pure(playerslot);
+       }
+       this.owner = o;
+       o.sv_entnum = this.sv_entnum;
        o.gotscores = 1;
  
        //if (!o.sort_prev)
                                o.(scores[i]) = ReadChar();
                }
  
+       return = true;
        if(o.sort_prev)
                HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet!
  
-       self.entremove = Ent_RemovePlayerScore;
+       this.entremove = Ent_RemovePlayerScore;
  }
  
void Ent_ReadTeamScore()
NET_HANDLE(ENT_CLIENT_TEAMSCORES, bool isnew)
  {
+       make_pure(this);
        int i;
        entity o;
  
-       self.team = ReadByte();
-       o = self.owner = GetTeam(self.team, true); // these team numbers can always be trusted
+       this.team = ReadByte();
+       o = this.owner = GetTeam(this.team, true); // these team numbers can always be trusted
  
      int sf, lf;
  #if MAX_TEAMSCORE <= 8
                                o.(teamscores[i]) = ReadChar();
                }
  
+       return = true;
        HUD_UpdateTeamPos(o);
  }
  
void Ent_ClientData()
NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
  {
+       make_pure(this);
        float newspectatee_status;
  
      int f = ReadByte();
        else
                angles_held_status = 0;
  
 +      if(f & 16)
 +      {
 +              num_spectators = ReadByte();
 +
 +              float i, slot;
 +
 +              for(i = 0; i < MAX_SPECTATORS; ++i)
 +                      spectatorlist[i] = 0; // reset list first
 +
 +              for(i = 0; i < num_spectators; ++i)
 +              {
 +                      slot = ReadByte();
 +                      spectatorlist[i] = slot - 1;
 +              }
 +      }
 +
+       return = true;
        if(newspectatee_status != spectatee_status)
        {
                // clear race stuff
                race_laptime = 0;
                race_checkpointtime = 0;
+               hud_dynamic_shake_factor = -1;
        }
        if (autocvar_hud_panel_healtharmor_progressbar_gfx)
        {
        // we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum
  }
  
void Ent_Nagger()
NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew)
  {
+       make_pure(this);
      int i, j, b, f;
  
      int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS
  
-       if(!(nags & 4))
+       if(!(nags & BIT(2)))
        {
                if(vote_called_vote)
                        strunzone(vote_called_vote);
                vote_active = 1;
        }
  
-       if(nags & 64)
+       if(nags & BIT(6))
        {
                vote_yescount = ReadByte();
                vote_nocount = ReadByte();
                vote_highlighted = ReadChar();
        }
  
-       if(nags & 128)
+       if(nags & BIT(7))
        {
                if(vote_called_vote)
                        strunzone(vote_called_vote);
                }
        }
  
-       ready_waiting = (nags & 1);
-       ready_waiting_for_me = (nags & 2);
-       vote_waiting = (nags & 4);
-       vote_waiting_for_me = (nags & 8);
-       warmup_stage = (nags & 16);
+       return = true;
+       ready_waiting = (nags & BIT(0));
+       ready_waiting_for_me = (nags & BIT(1));
+       vote_waiting = (nags & BIT(2));
+       vote_waiting_for_me = (nags & BIT(3));
+       warmup_stage = (nags & BIT(4));
  }
  
void Ent_EliminatedPlayers()
NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew)
  {
+       make_pure(this);
      int i, j, b, f;
  
      int sf = ReadByte();
                                                playerslots[j].eliminated = 0;
                }
        }
+       return true;
  }
  
void Ent_RandomSeed()
NET_HANDLE(ENT_CLIENT_RANDOMSEED, bool isnew)
  {
-       float s;
+       make_pure(this);
        prandom_debug();
-       s = ReadShort();
+       float s = ReadShort();
        psrandom(s);
+       return true;
  }
  
void Ent_ReadAccuracy(void)
NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew)
  {
-     int f, w;
+       make_pure(this);
      int sf = ReadInt24_t();
-       if(sf == 0)
-       {
-               for(w = 0; w <= WEP_LAST - WEP_FIRST; ++w)
+       if (sf == 0) {
+               for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w)
                        weapon_accuracy[w] = -1;
-               return;
+               return true;
        }
  
-       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w)
-       {
-               if(sf & f)
-               {
+       int f = 1;
+       for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
+               if (sf & f) {
              int b = ReadByte();
-                       if(b == 0)
+                       if (b == 0)
                                weapon_accuracy[w] = -1;
-                       else if(b == 255)
+                       else if (b == 255)
                                weapon_accuracy[w] = 1.0; // no better error handling yet, sorry
                        else
                                weapon_accuracy[w] = (b - 1.0) / 100.0;
                }
-               if(f == 0x800000)
-                       f = 1;
-               else
-                       f *= 2;
+               f = (f == 0x800000) ? 1 : f * 2;
        }
+       return true;
  }
  
- void Spawn_Draw(void)
+ void Spawn_Draw(entity this)
  {
-       pointparticles(self.cnt, self.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1));
+       __pointparticles(this.cnt, this.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1));
  }
  
- void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint
+ void Spawn_PreDraw(entity this)
+ {
+       float alph;
+       vector org = getpropertyvec(VF_ORIGIN);
+       if(this.fade_start)
+               alph = bound(0, (this.fade_end - vlen(org - this.origin - 0.5 * (this.mins + this.maxs))) / (this.fade_end - this.fade_start), 1);
+       else
+               alph = 1;
+       //printf("%v <-> %v\n", view_origin, this.origin + 0.5 * (this.mins + this.maxs));
+       this.alpha = alph;
+       if(alph <= 0)
+               this.drawmask = 0;
+       else
+               this.drawmask = MASK_NORMAL;
+ }
+ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
  {
        float teamnum = (ReadByte() - 1);
        vector spn_origin;
-       spn_origin.x = ReadShort();
-       spn_origin.y = ReadShort();
-       spn_origin.z = ReadShort();
+       spn_origin.x = ReadCoord();
+       spn_origin.y = ReadCoord();
+       spn_origin.z = ReadCoord();
  
-       if(is_new)
-       {
-               self.origin = spn_origin;
-               setsize(self, PL_MIN, PL_MAX);
-               droptofloor();
+       //if(is_new)
+       //{
+               this.origin = spn_origin;
+               setsize(this, PL_MIN_CONST, PL_MAX_CONST);
+               //droptofloor();
  
                /*if(autocvar_cl_spawn_point_model) // needs a model first
                {
-                       self.mdl = "models/spawnpoint.md3";
-                       self.colormod = Team_ColorRGB(teamnum);
-                       precache_model(self.mdl);
-                       setmodel(self, self.mdl);
-                       self.drawmask = MASK_NORMAL;
-                       //self.movetype = MOVETYPE_NOCLIP;
-                       //self.draw = Spawn_Draw;
+                       this.mdl = "models/spawnpoint.md3";
+                       this.colormod = Team_ColorRGB(teamnum);
+                       precache_model(this.mdl);
+                       setmodel(this, this.mdl);
+                       this.drawmask = MASK_NORMAL;
+                       //this.movetype = MOVETYPE_NOCLIP;
+                       //this.draw = Spawn_Draw;
                }*/
                if(autocvar_cl_spawn_point_particles)
                {
                        {
                                switch(teamnum)
                                {
-                                       case NUM_TEAM_1: self.cnt = particleeffectnum("spawn_point_red"); break;
-                                       case NUM_TEAM_2: self.cnt = particleeffectnum("spawn_point_blue"); break;
-                                       case NUM_TEAM_3: self.cnt = particleeffectnum("spawn_point_yellow"); break;
-                                       case NUM_TEAM_4: self.cnt = particleeffectnum("spawn_point_pink"); break;
-                                       default: self.cnt = particleeffectnum("spawn_point_neutral"); break;
+                                       case NUM_TEAM_1: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_RED); break;
+                                       case NUM_TEAM_2: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_BLUE); break;
+                                       case NUM_TEAM_3: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_YELLOW); break;
+                                       case NUM_TEAM_4: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_PINK); break;
+                                       default: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); break;
                                }
                        }
-                       else { self.cnt = particleeffectnum("spawn_point_neutral"); }
+                       else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
  
-                       self.draw = Spawn_Draw;
+                       this.draw = Spawn_Draw;
+                       setpredraw(this, Spawn_PreDraw);
+                       this.fade_start = autocvar_cl_spawn_point_dist_min;
+                       this.fade_end = autocvar_cl_spawn_point_dist_max;
                }
-       }
+       //}
  
-       //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(self.origin), teamnum, self.cnt);
+       //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(this.origin), teamnum, this.cnt);
+       return true;
  }
  
void Ent_ReadSpawnEvent(float is_new)
NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new)
  {
        // If entnum is 0, ONLY do the local spawn actions
        // this way the server can disable the sending of
  
        if(entnum)
        {
-               self.origin_x = ReadShort();
-               self.origin_y = ReadShort();
-               self.origin_z = ReadShort();
+               this.origin_x = ReadCoord();
+               this.origin_y = ReadCoord();
+               this.origin_z = ReadCoord();
  
                if(is_new)
                {
-                       float teamnum = GetPlayerColor(entnum - 1);
+                       float teamnum = entcs_GetTeam(entnum - 1);
  
                        if(autocvar_cl_spawn_event_particles)
                        {
                                switch(teamnum)
                                {
-                                       case NUM_TEAM_1: pointparticles(particleeffectnum("spawn_event_red"), self.origin, '0 0 0', 1); break;
-                                       case NUM_TEAM_2: pointparticles(particleeffectnum("spawn_event_blue"), self.origin, '0 0 0', 1); break;
-                                       case NUM_TEAM_3: pointparticles(particleeffectnum("spawn_event_yellow"), self.origin, '0 0 0', 1); break;
-                                       case NUM_TEAM_4: pointparticles(particleeffectnum("spawn_event_pink"), self.origin, '0 0 0', 1); break;
-                                       default: pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); break;
+                                       case NUM_TEAM_1: pointparticles(EFFECT_SPAWN_RED, this.origin, '0 0 0', 1); break;
+                                       case NUM_TEAM_2: pointparticles(EFFECT_SPAWN_BLUE, this.origin, '0 0 0', 1); break;
+                                       case NUM_TEAM_3: pointparticles(EFFECT_SPAWN_YELLOW, this.origin, '0 0 0', 1); break;
+                                       case NUM_TEAM_4: pointparticles(EFFECT_SPAWN_PINK, this.origin, '0 0 0', 1); break;
+                                       default: pointparticles(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); break;
                                }
                        }
                        if(autocvar_cl_spawn_event_sound)
                        {
-                               sound(self, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTEN_NORM);
+                               sound(this, CH_TRIGGER, SND_SPAWN, VOL_BASE, ATTEN_NORM);
                        }
                }
        }
+       return = true;
  
        // local spawn actions
        if(is_new && (!entnum || (entnum == player_localentnum)))
                        button_zoom = false;
                }
        }
-       //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum);
+       HUD_Radar_Hide_Maximized();
+       //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(this.origin), entnum, player_localentnum);
  }
  
  // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.
  // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.
- void Ent_RadarLink();
- void Ent_Init();
- void Ent_ScoresInfo();
- void CSQC_Ent_Update(float bIsNewEntity)
- {
-       float t;
-       float savetime;
-       t = ReadByte();
-       if(autocvar_developer_csqcentities)
-               printf("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t);
+ void CSQC_Ent_Update(bool isnew)
+ {ENGINE_EVENT();
+       this.sourceLoc = __FILE__ ":" STR(__LINE__);
+       int t = ReadByte();
  
        // set up the "time" global for received entities to be correct for interpolation purposes
-       savetime = time;
+       float savetime = time;
        if(servertime)
        {
                time = servertime;
        else
        {
                serverprevtime = time;
-               serverdeltatime = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE);
+               serverdeltatime = STAT(MOVEVARS_TICRATE) * STAT(MOVEVARS_TIMESCALE);
                time = serverprevtime + serverdeltatime;
        }
  
  #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
-       if(self.enttype)
+       if (this.enttype)
        {
-               if(t != self.enttype || bIsNewEntity)
+               if (t != this.enttype || isnew)
                {
-                       //print("A CSQC entity changed its type!\n");
-                       printf("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t);
-                       Ent_Remove();
-                       clearentity(self);
-                       bIsNewEntity = 1;
+                       LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", etof(this), this.entnum, this.enttype, t);
+                       Ent_Remove(this);
+                       clearentity(this);
+                       isnew = true;
                }
        }
        else
        {
-               if(!bIsNewEntity)
+               if (!isnew)
                {
-                       printf("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(self), self.entnum, t);
-                       bIsNewEntity = 1;
+                       LOG_INFOF("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", etof(this), this.entnum, t);
+                       isnew = true;
                }
        }
  #endif
-       self.enttype = t;
-       switch(t)
+       this.enttype = t;
+       bool done = false;
+       FOREACH(LinkedEntities, it.m_id == t, {
+               if (isnew) this.classname = it.netname;
+               if (autocvar_developer_csqcentities)
+             LOG_INFOF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
+               done = it.m_read(this, NULL, isnew);
+               break;
+       });
+       time = savetime;
+       if (!done)
        {
-               case ENT_CLIENT_ENTCS: Ent_ReadEntCS(); break;
-               case ENT_CLIENT_SCORES: Ent_ReadPlayerScore(); break;
-               case ENT_CLIENT_TEAMSCORES: Ent_ReadTeamScore(); break;
-               case ENT_CLIENT_POINTPARTICLES: Ent_PointParticles(); break;
-               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_GIBSPLASH: Ent_GibSplash(bIsNewEntity); break;
-               case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(bIsNewEntity); break;
-               case ENT_CLIENT_CASING: Ent_Casing(bIsNewEntity); break;
-               case ENT_CLIENT_INIT: Ent_Init(); break;
-               case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break;
-               case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break;
-               case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break;
-               case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break;
-               case ENT_CLIENT_WALL: Ent_Wall(); break;
-               case ENT_CLIENT_MODELEFFECT: Ent_ModelEffect(bIsNewEntity); break;
-               case ENT_CLIENT_TUBANOTE: Ent_TubaNote(bIsNewEntity); break;
-               case ENT_CLIENT_WARPZONE: WarpZone_Read(bIsNewEntity); break;
-               case ENT_CLIENT_WARPZONE_CAMERA: WarpZone_Camera_Read(bIsNewEntity); 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_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_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));
-                       break;
+               LOG_FATALF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t);
        }
-       time = savetime;
  }
  // Destructor, but does NOT deallocate the entity by calling remove(). Also
  // used when an entity changes its type. For an entity that someone interacts
  // with others, make sure it can no longer do so.
- void Ent_Remove()
+ void Ent_Remove(entity this)
  {
-       if(self.entremove)
-               self.entremove();
+       if(this.entremove) this.entremove(this);
  
-       if(self.skeletonindex)
+       if(this.skeletonindex)
        {
-               skel_delete(self.skeletonindex);
-               self.skeletonindex = 0;
+               skel_delete(this.skeletonindex);
+               this.skeletonindex = 0;
        }
  
-       if(self.snd_looping > 0)
+       if(this.snd_looping > 0)
        {
-               sound(self, self.snd_looping, "misc/null.wav", VOL_BASE, autocvar_g_jetpack_attenuation);
-               self.snd_looping = 0;
+               sound(this, this.snd_looping, SND_Null, VOL_BASE, autocvar_g_jetpack_attenuation);
+               this.snd_looping = 0;
        }
  
-       self.enttype = 0;
-       self.classname = "";
-       self.draw = menu_sub_null;
-       self.entremove = menu_sub_null;
+       this.enttype = 0;
+       this.classname = "";
+       this.draw = func_null;
+       this.entremove = func_null;
        // TODO possibly set more stuff to defaults
  }
- // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(self) as well.
+ // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(this) as well.
  void CSQC_Ent_Remove()
- {
-       if(autocvar_developer_csqcentities)
-               printf("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype);
-       if(wasfreed(self))
+ {ENGINE_EVENT();
+       if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype);
+       if (wasfreed(this))
        {
-               print("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
+               LOG_WARNING("CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
                return;
        }
-       if(self.enttype)
-               Ent_Remove();
-       remove(self);
+       if (this.enttype) Ent_Remove(this);
+       remove(this);
  }
  
  void Gamemode_Init()
  // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided.  To execute standard behavior, simply execute localcmd with the string.
  void CSQC_Parse_StuffCmd(string strMessage)
  {
-       if(autocvar_developer_csqcentities)
-               printf("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage);
+       if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage);
        localcmd(strMessage);
  }
  // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided.  To execute standard behavior, simply execute print with the string.
  void CSQC_Parse_Print(string strMessage)
  {
-       if(autocvar_developer_csqcentities)
-               printf("CSQC_Parse_Print(\"%s\")\n", strMessage);
+       if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_Print(\"%s\")\n", strMessage);
        print(ColorTranslateRGB(strMessage));
  }
  
  // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided.
  void CSQC_Parse_CenterPrint(string strMessage)
  {
-       if(autocvar_developer_csqcentities)
-               printf("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage);
+       if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage);
        centerprint_hud(strMessage);
  }
  
- string notranslate_fogcmd1 = "\nfog ";
- string notranslate_fogcmd2 = "\nr_fog_exp2 0\nr_drawfog 1\n";
- void Fog_Force()
+ // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.
+ // You must ALWAYS first acquire the temporary ID, which is sent as a byte.
+ // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
+ bool CSQC_Parse_TempEntity()
  {
-       // TODO somehow thwart prvm_globalset client ...
+       // Acquire TE ID
+       int nTEID = ReadByte();
+       FOREACH(TempEntities, it.m_id == nTEID, {
+               if (autocvar_developer_csqcentities)
+                       LOG_INFOF("CSQC_Parse_TempEntity() nTEID=%s (%d)\n", it.netname, nTEID);
+               return it.m_read(NULL, NULL, true);
+       });
+       if (autocvar_developer_csqcentities)
+               LOG_INFOF("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID);
+       // No special logic for this temporary entity; return 0 so the engine can handle it
+       return false;
+ }
  
-       if(autocvar_cl_orthoview && autocvar_cl_orthoview_nofog)
-               { localcmd("\nr_drawfog 0\n"); }
-       else if(forcefog != "")
-               { localcmd(strcat(notranslate_fogcmd1, forcefog, notranslate_fogcmd2)); }
+ string forcefog;
+ void Fog_Force()
+ {
+       if (autocvar_cl_orthoview && autocvar_cl_orthoview_nofog)
+               localcmd("\nr_drawfog 0\n");
+       else if (forcefog != "")
+               localcmd(sprintf("\nfog %s\nr_fog_exp2 0\nr_drawfog 1\n", forcefog));
  }
  
  void Gamemode_Init();
void Ent_ScoresInfo()
NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew)
  {
-     int i;
-       self.classname = "ent_client_scores_info";
+       make_pure(this);
        gametype = ReadInt24_t();
        HUD_ModIcons_SetFunc();
-       for(i = 0; i < MAX_SCORE; ++i)
+       for (int i = 0; i < MAX_SCORE; ++i)
        {
-               if(scores_label[i])
-                       strunzone(scores_label[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)
+       for (int i = 0; i < MAX_TEAMSCORE; ++i)
        {
-               if(teamscores_label[i])
-                       strunzone(teamscores_label[i]);
+               if (teamscores_label[i]) strunzone(teamscores_label[i]);
                teamscores_label[i] = strzone(ReadString());
                teamscores_flags[i] = ReadByte();
        }
+       return = true;
        HUD_InitScores();
        Gamemode_Init();
  }
  
void Ent_Init()
NET_HANDLE(ENT_CLIENT_INIT, bool isnew)
  {
-       self.classname = "ent_client_init";
        nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th
  
        hook_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
        arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
        arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
  
-       if(forcefog)
-               strunzone(forcefog);
+       if (forcefog) strunzone(forcefog);
        forcefog = strzone(ReadString());
  
        armorblockpercent = ReadByte() / 255.0;
  
-       g_balance_mortar_bouncefactor = ReadCoord();
-       g_balance_mortar_bouncestop = ReadCoord();
-       g_balance_electro_secondary_bouncefactor = ReadCoord();
-       g_balance_electro_secondary_bouncestop = ReadCoord();
-       vortex_scope = !ReadByte();
-       rifle_scope = !ReadByte();
        serverflags = ReadByte();
  
-       minelayer_maxmines = ReadByte();
+       g_trueaim_minrange = ReadCoord();
  
-       hagar_maxrockets = ReadByte();
+       return = true;
  
-       g_trueaim_minrange = ReadCoord();
-       g_balance_porto_secondary = ReadByte();
+       MUTATOR_CALLHOOK(Ent_Init);
  
-       if(!postinit)
-               PostInit();
+       if (!postinit) PostInit();
  }
  
void Net_ReadRace()
NET_HANDLE(TE_CSQC_RACE, bool isNew)
  {
-       float b;
+       int b = ReadByte();
  
-       b = ReadByte();
-       switch(b)
+       switch (b)
        {
                case RACE_NET_CHECKPOINT_HIT_QUALIFYING:
                        race_checkpoint = ReadByte();
                                strunzone(grecordholder[pos-1]);
                        grecordholder[pos-1] = strzone(ReadString());
                        grecordtime[pos-1] = ReadInt24_t();
-                       if(grecordholder[pos-1] == GetPlayerName(player_localnum))
+                       if(grecordholder[pos-1] == entcs_GetName(player_localnum))
                                race_myrank = pos;
                        break;
                case RACE_NET_SERVER_STATUS:
                                strunzone(race_status_name);
                        race_status_name = strzone(ReadString());
        }
+       return true;
  }
  
void Net_TeamNagger()
NET_HANDLE(TE_CSQC_TEAMNAGGER, bool isNew)
  {
        teamnagger = 1;
+       return true;
  }
  
void Net_ReadPingPLReport()
NET_HANDLE(TE_CSQC_PINGPLREPORT, bool isNew)
  {
-       int e, pi, pl, ml;
-       e = ReadByte();
-       pi = ReadShort();
-       pl = ReadByte();
-       ml = ReadByte();
-       if (!(playerslots[e]))
-               return;
-       playerslots[e].ping = pi;
-       playerslots[e].ping_packetloss = pl / 255.0;
-       playerslots[e].ping_movementloss = ml / 255.0;
+       int i = ReadByte();
+       int pi = ReadShort();
+       int pl = ReadByte();
+       int ml = ReadByte();
+       return = true;
+       entity e = playerslots[i];
+       if (!e) return;
+       e.ping = pi;
+       e.ping_packetloss = pl / 255.0;
+       e.ping_movementloss = ml / 255.0;
  }
  
void Net_WeaponComplain()
NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew)
  {
        complain_weapon = ReadByte();
-       if(complain_weapon_name)
-               strunzone(complain_weapon_name);
-       complain_weapon_name = strzone(WEP_NAME(complain_weapon));
        complain_weapon_type = ReadByte();
+       return = true;
  
        complain_weapon_time = time;
        weapontime = time; // ping the weapon panel
        }
  }
  
- // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.
- // You must ALWAYS first acquire the temporary ID, which is sent as a byte.
- // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
- float CSQC_Parse_TempEntity()
- {
-       float bHandled;
-               bHandled  = true;
-       // Acquire TE ID
-       float nTEID;
-               nTEID = ReadByte();
-       if(autocvar_developer_csqcentities)
-               printf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID);
-               // NOTE: Could just do return instead of break...
-       switch(nTEID)
-       {
-               case TE_CSQC_TARGET_MUSIC:
-                       Net_TargetMusic();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_PICTURE:
-                       Net_MapVote_Picture();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_RACE:
-                       Net_ReadRace();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_VORTEXBEAMPARTICLE:
-                       Net_ReadVortexBeamParticle();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_TEAMNAGGER:
-                       Net_TeamNagger();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_ARC:
-                       Net_ReadArc();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_PINGPLREPORT:
-                       Net_ReadPingPLReport();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_WEAPONCOMPLAIN:
-                       Net_WeaponComplain();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_VEHICLESETUP:
-                       Net_VehicleSetup();
-                       bHandled = true;
-                       break;
-               case TE_CSQC_SVNOTICE:
-                       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;
-                       break;
-       }
-       return bHandled;
- }
  string getcommandkey(string text, string command)
  {
        string keys;
        keys = db_get(binddb, command);
        if (keys == "")
        {
+               bool joy_detected = cvar("joy_detected");
                n = tokenize(findkeysforcommand(command, 0)); // uses '...' strings
                for(j = 0; j < n; ++j)
                {
                        k = stof(argv(j));
                        if(k != -1)
                        {
-                               if ("" == keys)
-                                       keys = keynumtostring(k);
+                               string key = keynumtostring(k);
+                               if(!joy_detected && substring(key, 0, 3) == "JOY")
+                                       continue;
+                               if (keys == "")
+                                       keys = key;
                                else
-                                       keys = strcat(keys, ", ", keynumtostring(k));
+                                       keys = strcat(keys, ", ", key);
  
                                ++l;
                                if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l)
diff --combined qcsrc/client/main.qh
index bf0bb74b0c0ab2fffd46441ddaa6ce1a1decb6b1,8601d26b589338b2dc8b18a342e0a68abc230396..65aad3e9f64357a290a707eb8c4f32b629c38765
@@@ -1,23 -1,11 +1,11 @@@
- #ifndef MAIN_H
- #define MAIN_H
+ #pragma once
+ #include <common/constants.qh>
+ #include <common/weapons/all.qh>
  
  // --------------------------------------------------------------------------
  // MENU Functionality
  
- const float DATABUF_PING = 0;
- #define DATABUF_CAPTURES (1*maxclients)
- #define DATABUF_DEATHS (2*maxclients)
- #define DATABUF_RETURNS (3*maxclients)
- #define DATABUF_NEXT (5*maxclients)
- void() menu_show_error;
- void() menu_sub_null;
- float menu_visible;
- var void() menu_show;
- var float(float bInputType, float nPrimary, float nSecondary) menu_action;
  // --------------------------------------------------------------------------
  // Onslaught
  
@@@ -42,6 -30,13 +30,13 @@@ float gametype
  
  float FONT_USER = 8;
  
+ vector OFFSET_CURSOR = '0 0 0';
+ vector SIZE_CURSOR = '32 32 0';
+ void draw_cursor(vector pos, vector ofs, string img, vector col, float a);
+ void draw_cursor_normal(vector pos, vector col, float a);
+ void LoadMenuSkinValues();
  // --------------------------------------------------------------------------
  // Scoreboard stuff
  
@@@ -89,23 -84,25 +84,25 @@@ entity teamslots[17];    // 17 teams (i
  .float ready;
  .float eliminated;
  
- .void(void) draw;
- .void(void) draw2d;
- .void(void) entremove;
+ .void(entity) draw;
+ .void(entity) draw2d;
+ .void(entity) entremove;
  float drawframetime;
  vector view_origin, view_forward, view_right, view_up;
  
float button_zoom;
float spectatorbutton_zoom;
float button_attack2;
bool button_zoom;
bool spectatorbutton_zoom;
bool button_attack2;
  
int activeweapon;
int switchingweapon;
int switchweapon;
Weapon activeweapon;
Weapon switchingweapon;
Weapon switchweapon;
  float current_viewzoom;
  float zoomin_effect;
  float warmup_stage;
  
+ void Fog_Force();
  string getcommandkey(string text, string command);
  
  string vote_called_vote;
@@@ -125,6 -122,7 +122,7 @@@ float camera_roll
  vector camera_direction;
  
  void centerprint_hud(string strMessage);
+ void centerprint_kill(float id);
  void centerprint_generic(float new_id, string strMessage, float duration, float countdown_num);
  
  const float ALPHA_MIN_VISIBLE = 0.003;
@@@ -138,20 -136,9 +136,14 @@@ const int HOOK_END =      2
  
  .float ping, ping_packetloss, ping_movementloss;
  
- float g_balance_mortar_bouncefactor;
- float g_balance_mortar_bouncestop;
- float g_balance_electro_secondary_bouncefactor;
- float g_balance_electro_secondary_bouncestop;
  float g_trueaim_minrange;
  
- entity entcs_receiver[255]; // 255 is the engine limit on maxclients
  float hud;
  float view_quality;
 +
 +int num_spectators;
 +const int MAX_SPECTATORS = 7;
 +int spectatorlist[MAX_SPECTATORS];
 +
  int framecount;
- #endif
+ .float health;
index fe12ffc0393134d27b7311ec0b612e562bc98a0f,968a9899f20262d29f6c3c665de738b00b9732e2..1f92d24442c3bf10cc5944b346d49a85ae37713f
- #include "waypointsprites.qh"
+ #include "cl_client.qh"
  
+ #include "anticheat.qh"
  #include "cl_impulse.qh"
  #include "cl_player.qh"
- #include "ent_cs.qh"
- #include "g_subs.qh"
  #include "ipban.qh"
  #include "miscfunctions.qh"
  #include "portals.qh"
  #include "teamplay.qh"
  #include "playerdemo.qh"
- #include "secret.qh"
+ #include "spawnpoints.qh"
+ #include "g_damage.qh"
+ #include "g_hook.qh"
+ #include "command/common.qh"
+ #include "cheats.qh"
+ #include "g_world.qh"
+ #include "race.qh"
+ #include "antilag.qh"
+ #include "campaign.qh"
+ #include "command/common.qh"
  
  #include "bot/bot.qh"
  #include "bot/navigation.qh"
  
+ #include "../common/ent_cs.qh"
+ #include <common/state.qh>
+ #include <common/effects/qc/globalsound.qh>
+ #include "../common/triggers/teleporters.qh"
+ #include "../common/vehicles/all.qh"
  #include "weapons/hitplot.qh"
  #include "weapons/weaponsystem.qh"
  
  #include "../common/net_notice.qh"
+ #include "../common/physics/player.qh"
+ #include "../common/items/all.qc"
+ #include "../common/mutators/mutator/waypoints/all.qh"
+ #include "../common/triggers/subs.qh"
+ #include "../common/triggers/triggers.qh"
+ #include "../common/triggers/trigger/secret.qh"
+ #include "../common/minigames/sv_minigames.qh"
+ #include "../common/items/inventory.qh"
  
  #include "../common/monsters/sv_monsters.qh"
  
- #include "../warpzonelib/server.qh"
+ #include "../lib/warpzone/server.qh"
+ STATIC_METHOD(Client, Add, void(Client this, int _team))
+ {
+     WITHSELF(this, ClientConnect());
+     TRANSMUTE(Player, this);
+     this.frame = 12; // 7
+     this.team = _team;
+     WITHSELF(this, PutClientInServer());
+ }
+ void PutObserverInServer(entity this);
+ void ClientDisconnect();
  
- float c1, c2, c3, c4;
+ STATIC_METHOD(Client, Remove, void(Client this))
+ {
+     TRANSMUTE(Observer, this);
+     WITHSELF(this, PutClientInServer());
+     WITHSELF(this, ClientDisconnect());
+ }
  
  void send_CSQC_teamnagger() {
-       WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
-       WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
+       WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
  }
  
-       entity head;
-       float spec_count = 0;
-       FOR_EACH_REALCLIENT(head)
 +int CountSpectators(entity player, entity to)
 +{
 +      if(!player) { return 0; } // not sure how, but best to be safe
 +
-               if(IS_SPEC(head))
-               if(head != to)
-               if(head.enemy == player)
-                       spec_count += 1;
-       }
++      int spec_count = 0;
++
++      FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
 +      {
-       entity head;
-       FOR_EACH_REALCLIENT(head)
++              spec_count++;
++      });
 +
 +      return spec_count;
 +}
 +
 +void WriteSpectators(entity player, entity to)
 +{
 +      if(!player) { return; } // not sure how, but best to be safe
 +
-               if(IS_SPEC(head))
-               if(head != to)
-               if(head.enemy == player)
-                       WriteByte(MSG_ENTITY, num_for_edict(head));
-       }
++      FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player,
 +      {
- bool ClientData_Send(entity to, int sf)
++              WriteByte(MSG_ENTITY, num_for_edict(it));
++      });
 +}
 +
+ bool ClientData_Send(entity this, entity to, int sf)
  {
-       if(to != self.owner)
-       {
-               error("wtf");
-               return false;
-       }
+       assert(to == this.owner, return false);
  
-       entity e;
-       e = to;
-       if(IS_SPEC(to))
-               e = to.enemy;
+       entity e = to;
+       if (IS_SPEC(e)) e = e.enemy;
  
        sf = 0;
+       if (e.race_completed)       sf |= 1; // forced scoreboard
+       if (to.spectatee_status)    sf |= 2; // spectator ent number follows
+       if (e.zoomstate)            sf |= 4; // zoomed
+       if (e.porto_v_angle_held)   sf |= 8; // angles held
++      sf |= 16; // always check spectators
  
-       if(e.race_completed)
-               sf |= 1; // forced scoreboard
-       if(to.spectatee_status)
-               sf |= 2; // spectator ent number follows
-       if(e.zoomstate)
-               sf |= 4; // zoomed
-       if(e.porto_v_angle_held)
-               sf |= 8; // angles held
-       // always check spectators
-       sf |= 16; // spectator handling?
-       WriteByte(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
        WriteByte(MSG_ENTITY, sf);
  
-       if(sf & 2)
+       if (sf & 2)
+       {
                WriteByte(MSG_ENTITY, to.spectatee_status);
-       if(sf & 8)
+       }
+       if (sf & 8)
        {
                WriteAngle(MSG_ENTITY, e.v_angle.x);
                WriteAngle(MSG_ENTITY, e.v_angle.y);
        }
 +
 +      if(sf & 16)
 +      {
 +              float specs = CountSpectators(e, to);
 +              WriteByte(MSG_ENTITY, specs);
 +              WriteSpectators(e, to);
 +      }
 +
        return true;
  }
  
- void ClientData_Attach()
+ void ClientData_Attach(entity this)
  {
-       Net_LinkEntity(self.clientdata = spawn(), false, 0, ClientData_Send);
-       self.clientdata.drawonlytoclient = self;
-       self.clientdata.owner = self;
+       Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send);
+       this.clientdata.drawonlytoclient = this;
+       this.clientdata.owner = this;
  }
  
- void ClientData_Detach()
+ void ClientData_Detach(entity this)
  {
-       remove(self.clientdata);
-       self.clientdata = world;
+       remove(this.clientdata);
+       this.clientdata = NULL;
  }
  
  void ClientData_Touch(entity e)
        e.clientdata.SendFlags = 1;
  
        // make it spectatable
-       entity e2;
-       FOR_EACH_REALCLIENT(e2)
-       {
-               if(e2 != e)
-                       if(IS_SPEC(e2))
-                               if(e2.enemy == e)
-                                       e2.clientdata.SendFlags = 1;
-       }
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1));
  }
  
  .string netname_previous;
  
- void SetSpectator(entity player, entity spectatee);
+ void SetSpectatee(entity player, entity spectatee);
  
  
  /*
@@@ -189,168 -176,171 +209,172 @@@ string CheckPlayerModel(string plyermod
  void setplayermodel(entity e, string modelname)
  {
        precache_model(modelname);
-       setmodel(e, modelname);
-       player_setupanimsformodel();
-       UpdatePlayerSounds();
+       _setmodel(e, modelname);
+       player_setupanimsformodel(e);
+       if(!autocvar_g_debug_globalsounds)
+               UpdatePlayerSounds(e);
  }
  
- /*
- =============
- PutObserverInServer
- putting a client as observer in the server
- =============
- */
- void FixPlayermodel();
- void PutObserverInServer (void)
+ void FixPlayermodel(entity player);
+ /** putting a client as observer in the server */
+ void PutObserverInServer(entity this)
  {
-       entity spot;
-       SetSpectator(self, world);
-     self.hud = HUD_NORMAL;
-       if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
+     bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
+       PlayerState_detach(this);
  
-       spot = SelectSpawnPoint (true);
-       if(!spot)
-               error("No spawnpoints for observers?!?\n");
-       RemoveGrapplingHook(self); // Wazat's Grappling Hook
-       if(IS_REAL_CLIENT(self))
-       {
-               msg_entity = self;
-               WriteByte(MSG_ONE, SVC_SETVIEW);
-               WriteEntity(MSG_ONE, self);
-       }
-       self.frags = FRAGS_SPECTATOR;
-       MUTATOR_CALLHOOK(MakePlayerObserver);
+       if (IS_PLAYER(this) && this.health >= 1) {
+         // despawn effect
+               Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+     }
  
-       Portal_ClearAll(self);
+     {
+         entity spot = SelectSpawnPoint(this, true);
+         if (!spot) LOG_FATAL("No spawnpoints for observers?!?");
+         this.angles = spot.angles;
+         this.angles_z = 0;
+         this.fixangle = true;
+         // offset it so that the spectator spawns higher off the ground, looks better this way
+         setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL));
+         this.prevorigin = this.origin;
+         if (IS_REAL_CLIENT(this))
+         {
+             msg_entity = this;
+             WriteByte(MSG_ONE, SVC_SETVIEW);
+             WriteEntity(MSG_ONE, this);
+         }
+         // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
+         // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
+         if(!autocvar_g_debug_globalsounds)
+         {
+               // needed for player sounds
+               this.model = "";
+               FixPlayermodel(this);
+         } 
+         setmodel(this, MDL_Null);
+         setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL));
+         this.view_ofs = '0 0 0';
+     }
  
-       Unfreeze(self);
+     RemoveGrapplingHook(this);
+       Portal_ClearAll(this);
+       Unfreeze(this);
++      SetSpectatee(this, world);
  
-       if(self.alivetime)
+       if (this.alivetime)
        {
-               if(!warmup_stage)
-                       PS_GR_P_ADDVAL(self, PLAYERSTATS_ALIVETIME, time - self.alivetime);
-               self.alivetime = 0;
+               if (!warmup_stage)
+                       PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+               this.alivetime = 0;
        }
  
-       if(self.vehicle)
-               vehicles_exit(VHEF_RELESE);
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
  
-       WaypointSprite_PlayerDead();
+       WaypointSprite_PlayerDead(this);
  
-       if (!g_ca)  // don't reset teams when moving a ca player to the spectators
-               self.team = -1;  // move this as it is needed to log the player spectating in eventlog
+       if (mutator_returnvalue) {
+           // mutator prevents resetting teams+score
+       } else {
+               this.team = -1;  // move this as it is needed to log the player spectating in eventlog
+         this.frags = FRAGS_SPECTATOR;
+         PlayerScore_Clear(this);  // clear scores when needed
+     }
  
-       if(self.killcount != -666)
+       if (this.killcount != FRAGS_SPECTATOR)
        {
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname);
-               if(autocvar_g_chat_nospectators == 1 || (cvar("g_warmup") && !(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
-                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CHAT_NOSPECTATORS);
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, this.netname);
+               if(!intermission_running)
+               if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2))
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS);
  
-               if(self.just_joined == false) {
-                       LogTeamchange(self.playerid, -1, 4);
+               if(this.just_joined == false) {
+                       LogTeamchange(this.playerid, -1, 4);
                } else
-                       self.just_joined = false;
-       }
-       PlayerScore_Clear(self); // clear scores when needed
-       accuracy_resend(self);
-       self.spectatortime = time;
-       self.classname = "observer";
-       self.iscreature = false;
-       self.teleportable = TELEPORT_SIMPLE;
-       self.damagedbycontents = false;
-       self.health = -666;
-       self.takedamage = DAMAGE_NO;
-       self.solid = SOLID_NOT;
-       self.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
-       self.flags = FL_CLIENT | FL_NOTARGET;
-       self.armorvalue = 666;
-       self.effects = 0;
-       self.armorvalue = autocvar_g_balance_armor_start;
-       self.pauserotarmor_finished = 0;
-       self.pauserothealth_finished = 0;
-       self.pauseregen_finished = 0;
-       self.damageforcescale = 0;
-       self.death_time = 0;
-       self.respawn_flags = 0;
-       self.respawn_time = 0;
-       self.stat_respawn_time = 0;
-       self.alpha = 0;
-       self.scale = 0;
-       self.fade_time = 0;
-       self.pain_frame = 0;
-       self.pain_finished = 0;
-       self.strength_finished = 0;
-       self.invincible_finished = 0;
-       self.superweapons_finished = 0;
-       self.pushltime = 0;
-       self.istypefrag = 0;
-       self.think = func_null;
-       self.nextthink = 0;
-       self.hook_time = 0;
-       self.deadflag = DEAD_NO;
-       self.angles = spot.angles;
-       self.angles_z = 0;
-       self.fixangle = true;
-       self.crouch = false;
-       self.revival_time = 0;
-       setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
-       self.prevorigin = self.origin;
-       self.items = 0;
-       self.weapons = '0 0 0';
-       self.model = "";
-       FixPlayermodel();
-       setmodel(self, "null");
-       self.drawonlytoclient = self;
-       setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX); // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY
-       self.view_ofs = '0 0 0'; // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS"
-       self.weapon = 0;
-       self.weaponname = "";
-       self.switchingweapon = 0;
-       self.weaponmodel = "";
-       self.weaponentity = world;
-       self.exteriorweaponentity = world;
-       self.killcount = -666;
-       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.fire_endtime = -1;
-       self.event_damage = func_null;
+                       this.just_joined = false;
+       }
+       accuracy_resend(this);
+       this.spectatortime = time;
+       this.bot_attack = false;
+     this.hud = HUD_NORMAL;
+       TRANSMUTE(Observer, this);
+       this.iscreature = false;
+       this.teleportable = TELEPORT_SIMPLE;
+       this.damagedbycontents = false;
+       this.health = FRAGS_SPECTATOR;
+       this.takedamage = DAMAGE_NO;
+       this.solid = SOLID_NOT;
+       this.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink
+       this.flags = FL_CLIENT | FL_NOTARGET;
+       this.armorvalue = 666;
+       this.effects = 0;
+       this.armorvalue = autocvar_g_balance_armor_start;
+       this.pauserotarmor_finished = 0;
+       this.pauserothealth_finished = 0;
+       this.pauseregen_finished = 0;
+       this.damageforcescale = 0;
+       this.death_time = 0;
+       this.respawn_flags = 0;
+       this.respawn_time = 0;
+       this.stat_respawn_time = 0;
+       this.alpha = 0;
+       this.scale = 0;
+       this.fade_time = 0;
+       this.pain_frame = 0;
+       this.pain_finished = 0;
+       this.strength_finished = 0;
+       this.invincible_finished = 0;
+       this.superweapons_finished = 0;
+       this.pushltime = 0;
+       this.istypefrag = 0;
+       setthink(this, func_null);
+       this.nextthink = 0;
+       this.hook_time = 0;
+       this.deadflag = DEAD_NO;
+       this.crouch = false;
+       this.revival_time = 0;
+       this.items = 0;
+       this.weapons = '0 0 0';
+       this.drawonlytoclient = this;
+       this.weaponname = "";
+       this.weaponmodel = "";
+       for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+       {
+               this.weaponentities[slot] = NULL;
+       }
+       this.exteriorweaponentity = NULL;
+       this.killcount = FRAGS_SPECTATOR;
+       this.velocity = '0 0 0';
+       this.avelocity = '0 0 0';
+       this.punchangle = '0 0 0';
+       this.punchvector = '0 0 0';
+       this.oldvelocity = this.velocity;
+       this.fire_endtime = -1;
+       this.event_damage = func_null;
  }
  
- .float model_randomizer;
- void FixPlayermodel()
+ int player_getspecies(entity this)
  {
-       string defaultmodel;
-       float defaultskin, chmdl, oldskin, n, i;
-       vector m1, m2;
-       defaultmodel = "";
-       defaultskin = 0;
-       chmdl = false;
+       get_model_parameters(this.model, this.skin);
+       int s = get_model_parameters_species;
+       get_model_parameters(string_null, 0);
+       if (s < 0) return SPECIES_HUMAN;
+       return s;
+ }
  
-       if(autocvar_sv_defaultcharacter == 1)
+ .float model_randomizer;
+ void FixPlayermodel(entity player)
+ {
+       string defaultmodel = "";
+       int defaultskin = 0;
+       if(autocvar_sv_defaultcharacter)
        {
                if(teamplay)
                {
-                       string s;
-                       s = Static_Team_ColorName_Lower(self.team);
-                       if(s != "neutral")
+                       string s = Static_Team_ColorName_Lower(player.team);
+                       if (s != "neutral")
                        {
                                defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s));
                                defaultskin = cvar(strcat("sv_defaultplayerskin_", s));
                        defaultskin = autocvar_sv_defaultplayerskin;
                }
  
-               n = tokenize_console(defaultmodel);
+               int n = tokenize_console(defaultmodel);
                if(n > 0)
                {
-                       defaultmodel = argv(floor(n * self.model_randomizer));
+                       defaultmodel = argv(floor(n * player.model_randomizer));
                        // However, do NOT randomize if the player-selected model is in the list.
-                       for (i = 0; i < n; ++i)
-                               if ((argv(i) == self.playermodel && defaultskin == stof(self.playerskin)) || argv(i) == strcat(self.playermodel, ":", self.playerskin))
+                       for (int i = 0; i < n; ++i)
+                               if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin))
                                        defaultmodel = argv(i);
                }
  
-               i = strstrofs(defaultmodel, ":", 0);
+               int i = strstrofs(defaultmodel, ":", 0);
                if(i >= 0)
                {
                        defaultskin = stof(substring(defaultmodel, i+1, -1));
                        defaultmodel = substring(defaultmodel, 0, i);
                }
        }
+       if(autocvar_sv_defaultcharacterskin && !defaultskin)
+       {
+               if(teamplay)
+               {
+                       string s = Static_Team_ColorName_Lower(player.team);
+                       if (s != "neutral")
+                               defaultskin = cvar(strcat("sv_defaultplayerskin_", s));
+               }
+               if(!defaultskin)
+                       defaultskin = autocvar_sv_defaultplayerskin;
+       }
  
+       MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player);
+       defaultmodel = M_ARGV(0, string);
+       defaultskin = M_ARGV(1, int);
+       bool chmdl = false;
+       int oldskin;
        if(defaultmodel != "")
        {
-               if (defaultmodel != self.model)
+               if (defaultmodel != player.model)
                {
-                       m1 = self.mins;
-                       m2 = self.maxs;
-                       setplayermodel (self, defaultmodel);
-                       setsize (self, m1, m2);
+                       vector m1 = player.mins;
+                       vector m2 = player.maxs;
+                       setplayermodel (player, defaultmodel);
+                       setsize (player, m1, m2);
                        chmdl = true;
                }
  
-               oldskin = self.skin;
-               self.skin = defaultskin;
+               oldskin = player.skin;
+               player.skin = defaultskin;
        } else {
-               if (self.playermodel != self.model || self.playermodel == "")
+               if (player.playermodel != player.model || player.playermodel == "")
                {
-                       self.playermodel = CheckPlayerModel(self.playermodel); // this is never "", so no endless loop
-                       m1 = self.mins;
-                       m2 = self.maxs;
-                       setplayermodel (self, self.playermodel);
-                       setsize (self, m1, m2);
+                       player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop
+                       vector m1 = player.mins;
+                       vector m2 = player.maxs;
+                       setplayermodel (player, player.playermodel);
+                       setsize (player, m1, m2);
                        chmdl = true;
                }
  
-               oldskin = self.skin;
-               self.skin = stof(self.playerskin);
+               if(!autocvar_sv_defaultcharacterskin)
+               {
+                       oldskin = player.skin;
+                       player.skin = stof(player.playerskin);
+               }
+               else
+               {
+                       oldskin = player.skin;
+                       player.skin = defaultskin;
+               }
        }
  
-       if(chmdl || oldskin != self.skin) // model or skin has changed
+       if(chmdl || oldskin != player.skin) // model or skin has changed
        {
-               self.species = player_getspecies(); // update species
-               UpdatePlayerSounds(); // update skin sounds
+               player.species = player_getspecies(player); // update species
+               if(!autocvar_g_debug_globalsounds)
+                       UpdatePlayerSounds(player); // update skin sounds
        }
  
        if(!teamplay)
                if(strlen(autocvar_sv_defaultplayercolors))
-                       if(self.clientcolors != stof(autocvar_sv_defaultplayercolors))
-                               setcolor(self, stof(autocvar_sv_defaultplayercolors));
+                       if(player.clientcolors != stof(autocvar_sv_defaultplayercolors))
+                               setcolor(player, stof(autocvar_sv_defaultplayercolors));
  }
  
- /*
- =============
- PutClientInServer
  
- Called when a client spawns in the server
- =============
- */
- void PutClientInServer (void)
- {
-       if(IS_BOT_CLIENT(self))
-               self.classname = "player";
-       else if(IS_REAL_CLIENT(self))
-       {
-               msg_entity = self;
+ /** Called when a client spawns in the server */
+ void PutClientInServer()
+ {ENGINE_EVENT();
+       if (IS_BOT_CLIENT(this)) {
+               TRANSMUTE(Player, this);
+       } else if (IS_REAL_CLIENT(this)) {
+               msg_entity = this;
                WriteByte(MSG_ONE, SVC_SETVIEW);
-               WriteEntity(MSG_ONE, self);
+               WriteEntity(MSG_ONE, this);
+       }
+       if (gameover) {
+               TRANSMUTE(Observer, this);
        }
  
-       SetSpectator(self, world);
+       SetSpectatee(this, NULL);
  
        // reset player keys
-       self.itemkeys = 0;
-       MUTATOR_CALLHOOK(PutClientInServer);
-       if(gameover)
-               self.classname = "observer";
+       this.itemkeys = 0;
  
-       if(IS_PLAYER(self))
-       {
-               entity spot, oldself;
-               float j;
+       MUTATOR_CALLHOOK(PutClientInServer, this);
  
-               accuracy_resend(self);
+       if (IS_OBSERVER(this)) {
+               PutObserverInServer(this);
+       } else if (IS_PLAYER(this)) {
+               PlayerState_attach(this);
+               accuracy_resend(this);
  
-               if(self.team < 0)
-                       JoinBestTeam(self, false, true);
+               if (this.team < 0)
+                       JoinBestTeam(this, false, true);
  
-               spot = SelectSpawnPoint (false);
-               if(!spot)
-               {
-                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
+               entity spot = SelectSpawnPoint(this, false);
+               if (!spot) {
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS);
                        return; // spawn failed
                }
  
-               RemoveGrapplingHook(self); // Wazat's Grappling Hook
-               if(self.vehicle)
-                       vehicles_exit(VHEF_RELESE);
-               self.classname = "player";
-               self.wasplayer = true;
-               self.iscreature = true;
-               self.teleportable = TELEPORT_NORMAL;
-               self.damagedbycontents = true;
-               self.movetype = MOVETYPE_WALK;
-               self.solid = SOLID_SLIDEBOX;
-               self.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
-               if(autocvar_g_playerclip_collisions)
-                       self.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
-               if(IS_BOT_CLIENT(self) && autocvar_g_botclip_collisions)
-                       self.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
-               self.frags = FRAGS_PLAYER;
-               if(INDEPENDENT_PLAYERS)
-                       MAKE_INDEPENDENT_PLAYER(self);
-               self.flags = FL_CLIENT;
-               if(autocvar__notarget)
-                       self.flags |= FL_NOTARGET;
-               self.takedamage = DAMAGE_AIM;
-               self.effects = 0;
-               self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
-               self.air_finished = time + 12;
-               self.dmg = 2;
-               if(WEP_CVAR(vortex, charge))
-               {
-                       if(WEP_CVAR_SEC(vortex, chargepool))
-                               self.vortex_chargepool_ammo = 1;
-                       self.vortex_charge = WEP_CVAR(vortex, charge_start);
-               }
-               if(warmup_stage)
-               {
-                       self.ammo_shells = warmup_start_ammo_shells;
-                       self.ammo_nails = warmup_start_ammo_nails;
-                       self.ammo_rockets = warmup_start_ammo_rockets;
-                       self.ammo_cells = warmup_start_ammo_cells;
-                       self.ammo_plasma = warmup_start_ammo_plasma;
-                       self.ammo_fuel = warmup_start_ammo_fuel;
-                       self.health = warmup_start_health;
-                       self.armorvalue = warmup_start_armorvalue;
-                       self.weapons = WARMUP_START_WEAPONS;
-               }
-               else
-               {
-                       self.ammo_shells = start_ammo_shells;
-                       self.ammo_nails = start_ammo_nails;
-                       self.ammo_rockets = start_ammo_rockets;
-                       self.ammo_cells = start_ammo_cells;
-                       self.ammo_plasma = start_ammo_plasma;
-                       self.ammo_fuel = start_ammo_fuel;
-                       self.health = start_health;
-                       self.armorvalue = start_armorvalue;
-                       self.weapons = start_weapons;
-               }
-               if(self.weapons & WEPSET_SUPERWEAPONS)
-                       self.superweapons_finished = time + autocvar_g_balance_superweapons_time;
-               else
-                       self.superweapons_finished = 0;
-               if(g_weaponarena_random) // WEAPONTODO: more stuff that should be in a mutator. also: rename those cvars
-               {
-                       if(g_weaponarena_random_with_blaster)
-                               self.weapons &= ~WEPSET_BLASTER;
-                       W_RandomWeapons(self, g_weaponarena_random);
-                       if(g_weaponarena_random_with_blaster)
-                               self.weapons |= WEPSET_BLASTER;
+               TRANSMUTE(Player, this);
+               this.wasplayer = true;
+               this.iscreature = true;
+               this.teleportable = TELEPORT_NORMAL;
+               this.damagedbycontents = true;
+               this.movetype = MOVETYPE_WALK;
+               this.solid = SOLID_SLIDEBOX;
+               this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID;
+               if (autocvar_g_playerclip_collisions)
+                       this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
+               if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions)
+                       this.dphitcontentsmask |= DPCONTENTS_BOTCLIP;
+               this.frags = FRAGS_PLAYER;
+               if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this);
+               this.flags = FL_CLIENT | FL_PICKUPITEMS;
+               if (autocvar__notarget)
+                       this.flags |= FL_NOTARGET;
+               this.takedamage = DAMAGE_AIM;
+               this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
+               this.dmg = 2; // WTF
+               if (warmup_stage) {
+                       this.ammo_shells = warmup_start_ammo_shells;
+                       this.ammo_nails = warmup_start_ammo_nails;
+                       this.ammo_rockets = warmup_start_ammo_rockets;
+                       this.ammo_cells = warmup_start_ammo_cells;
+                       this.ammo_plasma = warmup_start_ammo_plasma;
+                       this.ammo_fuel = warmup_start_ammo_fuel;
+                       this.health = warmup_start_health;
+                       this.armorvalue = warmup_start_armorvalue;
+                       this.weapons = WARMUP_START_WEAPONS;
+               } else {
+                       this.ammo_shells = start_ammo_shells;
+                       this.ammo_nails = start_ammo_nails;
+                       this.ammo_rockets = start_ammo_rockets;
+                       this.ammo_cells = start_ammo_cells;
+                       this.ammo_plasma = start_ammo_plasma;
+                       this.ammo_fuel = start_ammo_fuel;
+                       this.health = start_health;
+                       this.armorvalue = start_armorvalue;
+                       this.weapons = start_weapons;
                }
  
-               self.items = start_items;
-               self.spawnshieldtime = time + autocvar_g_spawnshieldtime;
-               self.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
-               self.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
-               self.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
-               self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
-               //extend the pause of rotting if client was reset at the beginning of the countdown
-               if(!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted?
-                       self.spawnshieldtime += game_starttime - time;
-                       self.pauserotarmor_finished += game_starttime - time;
-                       self.pauserothealth_finished += game_starttime - time;
-                       self.pauseregen_finished += game_starttime - time;
+               this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0;
+               this.items = start_items;
+               this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
+               this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
+               this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
+               this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
+               this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
+               // extend the pause of rotting if client was reset at the beginning of the countdown
+               if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted?
+                       float f = game_starttime - time;
+                       this.spawnshieldtime += f;
+                       this.pauserotarmor_finished += f;
+                       this.pauserothealth_finished += f;
+                       this.pauseregen_finished += f;
                }
-               self.damageforcescale = 2;
-               self.death_time = 0;
-               self.respawn_flags = 0;
-               self.respawn_time = 0;
-               self.stat_respawn_time = 0;
-               self.scale = 0;
-               self.fade_time = 0;
-               self.pain_frame = 0;
-               self.pain_finished = 0;
-               self.strength_finished = 0;
-               self.invincible_finished = 0;
-               self.pushltime = 0;
-               // players have no think function
-               self.think = func_null;
-               self.nextthink = 0;
-               self.hook_time = 0;
-               self.dmg_team = 0;
-               self.ballistics_density = autocvar_g_ballistics_density_player;
-               self.metertime = 0;
-               self.deadflag = DEAD_NO;
-               self.angles = spot.angles;
-               self.angles_z = 0; // never spawn tilted even if the spot says to
-               if(IS_BOT_CLIENT(self))
-                       self.v_angle = self.angles;
-               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.fire_endtime = -1;
-               self.revival_time = 0;
-               entity spawnevent = spawn();
-               spawnevent.owner = self;
+               this.damageforcescale = 2;
+               this.death_time = 0;
+               this.respawn_flags = 0;
+               this.respawn_time = 0;
+               this.stat_respawn_time = 0;
+               this.scale = autocvar_sv_player_scale;
+               this.fade_time = 0;
+               this.pain_frame = 0;
+               this.pain_finished = 0;
+               this.pushltime = 0;
+               setthink(this, func_null); // players have no think function
+               this.nextthink = 0;
+               this.dmg_team = 0;
+               this.ballistics_density = autocvar_g_ballistics_density_player;
+               this.deadflag = DEAD_NO;
+               this.angles = spot.angles;
+               this.angles_z = 0; // never spawn tilted even if the spot says to
+               if (IS_BOT_CLIENT(this))
+                       this.v_angle = this.angles;
+               this.fixangle = true; // turn this way immediately
+               this.oldvelocity = this.velocity = '0 0 0';
+               this.avelocity = '0 0 0';
+               this.punchangle = '0 0 0';
+               this.punchvector = '0 0 0';
+               this.strength_finished = 0;
+               this.invincible_finished = 0;
+               this.fire_endtime = -1;
+               this.revival_time = 0;
+               this.air_finished = time + 12;
+               entity spawnevent = new_pure(spawnevent);
+               spawnevent.owner = this;
                Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
  
                // Cut off any still running player sounds.
-               stopsound(self, CH_PLAYER_SINGLE);
+               stopsound(this, CH_PLAYER_SINGLE);
  
-               self.model = "";
-               FixPlayermodel();
-               self.drawonlytoclient = world;
+               this.model = "";
+               FixPlayermodel(this);
+               this.drawonlytoclient = NULL;
  
-               self.crouch = false;
-               self.view_ofs = PL_VIEW_OFS;
-               setsize (self, PL_MIN, PL_MAX);
-               self.spawnorigin = spot.origin;
-               setorigin (self, spot.origin + '0 0 1' * (1 - self.mins.z - 24));
+               this.crouch = false;
+               this.view_ofs = STAT(PL_VIEW_OFS, NULL);
+               setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+               this.spawnorigin = spot.origin;
+               setorigin(this, spot.origin + '0 0 1' * (1 - this.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;
-               self.lastrocket = world; // stop rocket guiding, no revenge from the grave!
-               self.lastteleporttime = time; // prevent insane speeds due to changing origin
-         self.hud = HUD_NORMAL;
+               this.oldorigin = this.origin;
+               this.prevorigin = this.origin;
+               this.lastteleporttime = time; // prevent insane speeds due to changing origin
+               this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
+               this.hud = HUD_NORMAL;
  
-               self.event_damage = PlayerDamage;
+               this.event_damage = PlayerDamage;
  
-               self.bot_attack = true;
-               self.monster_attack = true;
+               this.bot_attack = true;
+               this.monster_attack = true;
  
-               self.spider_slowness = 0;
+               PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
  
-               self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = 0;
-               if(self.killcount == -666) {
-                       PlayerScore_Clear(self);
-                       self.killcount = 0;
+               if (this.killcount == FRAGS_SPECTATOR) {
+                       PlayerScore_Clear(this);
+                       this.killcount = 0;
                }
  
-               CL_SpawnWeaponentity();
-               self.alpha = default_player_alpha;
-               self.colormod = '1 1 1' * autocvar_g_player_brightness;
-               self.exteriorweaponentity.alpha = default_weapon_alpha;
-               self.speedrunning = false;
+               for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       CL_SpawnWeaponentity(this, weaponentities[slot]);
+               }
+               this.alpha = default_player_alpha;
+               this.colormod = '1 1 1' * autocvar_g_player_brightness;
+               this.exteriorweaponentity.alpha = default_weapon_alpha;
  
-               //stuffcmd(self, "chase_active 0");
-               //stuffcmd(self, "set viewsize $tmpviewsize \n");
+               this.speedrunning = false;
  
-               target_voicescript_clear(self);
+               target_voicescript_clear(this);
  
                // reset fields the weapons may use
-               for (j = WEP_FIRST; j <= WEP_LAST; ++j)
-               {
-                       WEP_ACTION(j, WR_RESETPLAYER);
+               FOREACH(Weapons, true, LAMBDA(
+                       it.wr_resetplayer(it, this);
+                       // reload all reloadable weapons
+                       if (it.spawnflags & WEP_FLAG_RELOADABLE) {
+                               this.weapon_load[it.m_id] = it.reloading_ammo;
+                       }
+               ));
  
-                       // all weapons must be fully loaded when we spawn
-                       entity e = get_weaponinfo(j);
-                       if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
-                               self.(weapon_load[j]) = e.reloading_ammo;
+               {
+                       string s = spot.target;
+                       spot.target = string_null;
+                       SUB_UseTargets(spot, this, NULL);
+                       spot.target = s;
                }
  
-               oldself = self;
-               self = spot;
-                       activator = oldself;
-                               string s;
-                               s = self.target;
-                               self.target = string_null;
-                               SUB_UseTargets();
-                               self.target = s;
-                       activator = world;
-               self = oldself;
-               Unfreeze(self);
+               Unfreeze(this);
  
-               spawn_spot = spot;
-               MUTATOR_CALLHOOK(PlayerSpawn);
+               MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
  
-               if(autocvar_spawn_debug)
+               if (autocvar_spawn_debug)
                {
-                       sprint(self, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
-                       remove(spot);   // usefull for checking if there are spawnpoints, that let drop through the floor
+                       sprint(this, strcat("spawnpoint origin:  ", vtos(spot.origin), "\n"));
+                       remove(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
                }
  
-               self.switchweapon = w_getbestweapon(self);
-               self.cnt = -1; // W_LastWeapon will not complain
-               self.weapon = 0;
-               self.weaponname = "";
-               self.switchingweapon = 0;
+               PS(this).m_switchweapon = w_getbestweapon(this);
+               this.cnt = -1; // W_LastWeapon will not complain
+               PS(this).m_weapon = WEP_Null;
+               this.weaponname = "";
+               PS(this).m_switchingweapon = WEP_Null;
  
-               if(!warmup_stage)
-                       if(!self.alivetime)
-                               self.alivetime = time;
+               if (!warmup_stage && !this.alivetime)
+                       this.alivetime = time;
  
-               antilag_clear(self);
-       }
-       else if(IS_OBSERVER(self))
-       {
-               PutObserverInServer ();
+               antilag_clear(this, CS(this));
        }
  }
  
+ void ClientInit_misc(entity this);
  .float ebouncefactor, ebouncestop; // electro's values
  // TODO do we need all these fields, or should we stop autodetecting runtime
  // changes and just have a console command to update this?
float ClientInit_SendEntity(entity to, float sf)
bool ClientInit_SendEntity(entity this, entity to, int sf)
  {
-       WriteByte(MSG_ENTITY, ENT_CLIENT_INIT);
-       WriteByte(MSG_ENTITY, g_nexball_meter_period * 32);
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[0]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[1]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[2]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[3]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3]));
+       WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
+       return = true;
+       msg_entity = to;
+       // MSG_INIT replacement
+       // TODO: make easier to use
+       Registry_send_all();
+       W_PROP_reload(MSG_ONE, to);
+       ClientInit_misc(this);
+       MUTATOR_CALLHOOK(Ent_Init);
+ }
+ void ClientInit_misc(entity this)
+ {
+       int channel = MSG_ONE;
+       WriteHeader(channel, ENT_CLIENT_INIT);
+       WriteByte(channel, g_nexball_meter_period * 32);
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
+       WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
+       WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
  
        if(sv_foginterval && world.fog != "")
-               WriteString(MSG_ENTITY, world.fog);
+               WriteString(channel, world.fog);
        else
-               WriteString(MSG_ENTITY, "");
-       WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent
-       WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO
-       WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop
-       WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor
-       WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop
-       WriteByte(MSG_ENTITY, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO
-       WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO
-       WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
-       WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO
-       WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
-       WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
-       WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO
-       return true;
+               WriteString(channel, "");
+       WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent
+       WriteByte(channel, serverflags); // client has to know if it should zoom or not
+       WriteCoord(channel, autocvar_g_trueaim_minrange);
  }
  
- void ClientInit_CheckUpdate()
+ void ClientInit_CheckUpdate(entity this)
  {
-       self.nextthink = time;
-       if(self.count != autocvar_g_balance_armor_blockpercent)
-       {
-               self.count = autocvar_g_balance_armor_blockpercent;
-               self.SendFlags |= 1;
-       }
-       if(self.bouncefactor != autocvar_g_balance_mortar_bouncefactor) // WEAPONTODO
-       {
-               self.bouncefactor = autocvar_g_balance_mortar_bouncefactor;
-               self.SendFlags |= 1;
-       }
-       if(self.bouncestop != autocvar_g_balance_mortar_bouncestop)
-       {
-               self.bouncestop = autocvar_g_balance_mortar_bouncestop;
-               self.SendFlags |= 1;
-       }
-       if(self.ebouncefactor != autocvar_g_balance_electro_secondary_bouncefactor)
+       this.nextthink = time;
+       if(this.count != autocvar_g_balance_armor_blockpercent)
        {
-               self.ebouncefactor = autocvar_g_balance_electro_secondary_bouncefactor;
-               self.SendFlags |= 1;
-       }
-       if(self.ebouncestop != autocvar_g_balance_electro_secondary_bouncestop)
-       {
-               self.ebouncestop = autocvar_g_balance_electro_secondary_bouncestop;
-               self.SendFlags |= 1;
+               this.count = autocvar_g_balance_armor_blockpercent;
+               this.SendFlags |= 1;
        }
  }
  
  void ClientInit_Spawn()
  {
-       entity o;
-       entity e;
-       e = spawn();
-       e.classname = "clientinit";
-       e.think = ClientInit_CheckUpdate;
+       entity e = new_pure(clientinit);
+       setthink(e, ClientInit_CheckUpdate);
        Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
  
-       o = self;
-       self = e;
-       ClientInit_CheckUpdate();
-       self = o;
+       ClientInit_CheckUpdate(e);
  }
  
  /*
  SetNewParms
  =============
  */
- void SetNewParms (void)
+ void SetNewParms ()
  {
        // initialize parms for a new player
        parm1 = -(86400 * 366);
+       MUTATOR_CALLHOOK(SetNewParms);
  }
  
  /*
  SetChangeParms
  =============
  */
- void SetChangeParms (void)
- {
+ void SetChangeParms ()
+ {ENGINE_EVENT();
        // save parms for level change
-       parm1 = self.parm_idlesince - time;
+       parm1 = this.parm_idlesince - time;
+       MUTATOR_CALLHOOK(SetChangeParms);
  }
  
  /*
  DecodeLevelParms
  =============
  */
- void DecodeLevelParms (void)
+ void DecodeLevelParms(entity this)
  {
        // load parms
-       self.parm_idlesince = parm1;
-       if(self.parm_idlesince == -(86400 * 366))
-               self.parm_idlesince = time;
+       this.parm_idlesince = parm1;
+       if (this.parm_idlesince == -(86400 * 366))
+               this.parm_idlesince = time;
  
        // whatever happens, allow 60 seconds of idling directly after connect for map loading
-       self.parm_idlesince = max(self.parm_idlesince, time - sv_maxidle + 60);
+       this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60);
+       MUTATOR_CALLHOOK(DecodeLevelParms);
  }
  
  /*
@@@ -818,91 -766,90 +800,90 @@@ Called when a client types 'kill' in th
  */
  
  .float clientkill_nexttime;
- void ClientKill_Now_TeamChange()
+ void ClientKill_Now_TeamChange(entity this)
  {
-       if(self.killindicator_teamchange == -1)
+       if(this.killindicator_teamchange == -1)
        {
-               JoinBestTeam( self, false, true );
+               JoinBestTeam( this, false, true );
        }
-       else if(self.killindicator_teamchange == -2)
+       else if(this.killindicator_teamchange == -2)
        {
                if(blockSpectators)
-                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
-               PutObserverInServer();
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
+               PutObserverInServer(this);
        }
        else
-               SV_ChangeTeam(self.killindicator_teamchange - 1);
-       self.killindicator_teamchange = 0;
+               WITHSELF(this, SV_ChangeTeam(this.killindicator_teamchange - 1));
+       this.killindicator_teamchange = 0;
  }
  
- void ClientKill_Now()
+ void ClientKill_Now(entity this)
  {
-       if(self.vehicle)
+       if(this.vehicle)
        {
-           vehicles_exit(VHEF_RELESE);
-           if(!self.killindicator_teamchange)
+           vehicles_exit(this.vehicle, VHEF_RELEASE);
+           if(!this.killindicator_teamchange)
            {
-             self.vehicle_health = -1;
-             Damage(self, self, self, 1 , DEATH_KILL, self.origin, '0 0 0');
+             this.vehicle_health = -1;
+             Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0');
            }
        }
  
-       if(self.killindicator && !wasfreed(self.killindicator))
-               remove(self.killindicator);
+       if(this.killindicator && !wasfreed(this.killindicator))
+               remove(this.killindicator);
  
-       self.killindicator = world;
+       this.killindicator = world;
  
-       if(self.killindicator_teamchange)
-               ClientKill_Now_TeamChange();
+       if(this.killindicator_teamchange)
+               ClientKill_Now_TeamChange(this);
  
-       if(IS_PLAYER(self))
-               Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+       if(IS_PLAYER(this))
+               Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0');
  
        // now I am sure the player IS dead
  }
- void KillIndicator_Think()
+ void KillIndicator_Think(entity this)
  {
        if (gameover)
        {
-               self.owner.killindicator = world;
-               remove(self);
+               this.owner.killindicator = world;
+               remove(this);
                return;
        }
  
-       if (self.owner.alpha < 0 && !self.owner.vehicle)
+       if (this.owner.alpha < 0 && !this.owner.vehicle)
        {
-               self.owner.killindicator = world;
-               remove(self);
+               this.owner.killindicator = world;
+               remove(this);
                return;
        }
  
-       if(self.cnt <= 0)
+       if(this.cnt <= 0)
        {
-               self = self.owner;
-               ClientKill_Now(); // no oldself needed
+               ClientKill_Now(this.owner);
                return;
        }
-     else if(g_cts && self.health == 1) // health == 1 means that it's silent
+     else if(g_cts && this.health == 1) // health == 1 means that it's silent
      {
-         self.nextthink = time + 1;
-         self.cnt -= 1;
+         this.nextthink = time + 1;
+         this.cnt -= 1;
      }
        else
        {
-               if(self.cnt <= 10)
-                       setmodel(self, strcat("models/sprites/", ftos(self.cnt), ".spr32"));
-               if(IS_REAL_CLIENT(self.owner))
+               if(this.cnt <= 10)
+                       setmodel(this, MDL_NUM(this.cnt));
+               if(IS_REAL_CLIENT(this.owner))
                {
-                       if(self.cnt <= 10)
-                               { Send_Notification(NOTIF_ONE, self.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, self.cnt)); }
+                       if(this.cnt <= 10)
+                               { Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); }
                }
-               self.nextthink = time + 1;
-               self.cnt -= 1;
+               this.nextthink = time + 1;
+               this.cnt -= 1;
        }
  }
  
  float clientkilltime;
- void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2 = spec
+ void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec
  {
        float killtime;
        float starttime;
        if(g_race_qualifying || g_cts)
                killtime = 0;
  
-     if(g_cts && self.killindicator && self.killindicator.health == 1) // self.killindicator.health == 1 means that the kill indicator was spawned by CTS_ClientKill
-     {
-               remove(self.killindicator);
-               self.killindicator = world;
-         ClientKill_Now(); // allow instant kill in this case
-         return;
-     }
+     if(MUTATOR_CALLHOOK(ClientKill, this, killtime))
+       return;
  
-       self.killindicator_teamchange = targetteam;
+       this.killindicator_teamchange = targetteam;
  
-     if(!self.killindicator)
+     if(!this.killindicator)
        {
-               if(self.deadflag == DEAD_NO)
+               if(!IS_DEAD(this))
                {
-                       killtime = max(killtime, self.clientkill_nexttime - time);
-                       self.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam;
+                       killtime = max(killtime, this.clientkill_nexttime - time);
+                       this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam;
                }
  
-               if(killtime <= 0 || !IS_PLAYER(self) || self.deadflag != DEAD_NO)
+               if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this))
                {
-                       ClientKill_Now();
+                       ClientKill_Now(this);
                }
                else
                {
                        starttime = max(time, clientkilltime);
  
-                       self.killindicator = spawn();
-                       self.killindicator.owner = self;
-                       self.killindicator.scale = 0.5;
-                       setattachment(self.killindicator, self, "");
-                       setorigin(self.killindicator, '0 0 52');
-                       self.killindicator.think = KillIndicator_Think;
-                       self.killindicator.nextthink = starttime + (self.lip) * 0.05;
-                       clientkilltime = max(clientkilltime, self.killindicator.nextthink + 0.05);
-                       self.killindicator.cnt = ceil(killtime);
-                       self.killindicator.count = bound(0, ceil(killtime), 10);
-                       //sprint(self, strcat("^1You'll be dead in ", ftos(self.killindicator.cnt), " seconds\n"));
+                       this.killindicator = spawn();
+                       this.killindicator.owner = this;
+                       this.killindicator.scale = 0.5;
+                       setattachment(this.killindicator, this, "");
+                       setorigin(this.killindicator, '0 0 52');
+                       setthink(this.killindicator, KillIndicator_Think);
+                       this.killindicator.nextthink = starttime + (this.lip) * 0.05;
+                       clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05);
+                       this.killindicator.cnt = ceil(killtime);
+                       this.killindicator.count = bound(0, ceil(killtime), 10);
+                       //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n"));
  
                        for(e = world; (e = find(e, classname, "body")) != world; )
                        {
-                               if(e.enemy != self)
+                               if(e.enemy != this)
                                        continue;
                                e.killindicator = spawn();
                                e.killindicator.owner = e;
                                e.killindicator.scale = 0.5;
                                setattachment(e.killindicator, e, "");
                                setorigin(e.killindicator, '0 0 52');
-                               e.killindicator.think = KillIndicator_Think;
+                               setthink(e.killindicator, KillIndicator_Think);
                                e.killindicator.nextthink = starttime + (e.lip) * 0.05;
                                clientkilltime = max(clientkilltime, e.killindicator.nextthink + 0.05);
                                e.killindicator.cnt = ceil(killtime);
                        }
-                       self.lip = 0;
+                       this.lip = 0;
                }
        }
-       if(self.killindicator)
+       if(this.killindicator)
        {
                if(targetteam == 0) // just die
                {
-                       self.killindicator.colormod = '0 0 0';
-                       if(IS_REAL_CLIENT(self))
-                       if(self.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, self.killindicator.cnt);
+                       this.killindicator.colormod = '0 0 0';
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, this.killindicator.cnt);
                }
                else if(targetteam == -1) // auto
                {
-                       self.killindicator.colormod = '0 1 0';
-                       if(IS_REAL_CLIENT(self))
-                       if(self.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, self.killindicator.cnt);
+                       this.killindicator.colormod = '0 1 0';
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, this.killindicator.cnt);
                }
                else if(targetteam == -2) // spectate
                {
-                       self.killindicator.colormod = '0.5 0.5 0.5';
-                       if(IS_REAL_CLIENT(self))
-                       if(self.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, self.killindicator.cnt);
+                       this.killindicator.colormod = '0.5 0.5 0.5';
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, this.killindicator.cnt);
                }
                else
                {
-                       self.killindicator.colormod = Team_ColorRGB(targetteam);
-                       if(IS_REAL_CLIENT(self))
-                       if(self.killindicator.cnt > 0)
-                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, APP_TEAM_NUM_4(targetteam, CENTER_TEAMCHANGE_), self.killindicator.cnt);
+                       this.killindicator.colormod = Team_ColorRGB(targetteam);
+                       if(IS_REAL_CLIENT(this))
+                       if(this.killindicator.cnt > 0)
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE), this.killindicator.cnt);
                }
        }
  
  }
  
- void ClientKill (void)
- {
+ void ClientKill ()
+ {ENGINE_EVENT();
        if(gameover) return;
-       if(self.player_blocked) return;
-       if(self.frozen) return;
-       ClientKill_TeamChange(0);
- }
+       if(this.player_blocked) return;
+       if(STAT(FROZEN, this)) return;
  
- void CTS_ClientKill (entity e) // silent version of ClientKill, used when player finishes a CTS run. Useful to prevent cheating by running back to the start line and starting out with more speed
- {
-     e.killindicator = spawn();
-     e.killindicator.owner = e;
-     e.killindicator.think = KillIndicator_Think;
-     e.killindicator.nextthink = time + (e.lip) * 0.05;
-     e.killindicator.cnt = ceil(autocvar_g_cts_finish_kill_delay);
-     e.killindicator.health = 1; // this is used to indicate that it should be silent
-     e.lip = 0;
+       ClientKill_TeamChange(this, 0);
  }
  
  void FixClientCvars(entity e)
  {
        // send prediction settings to the client
        stuffcmd(e, "\nin_bindmap 0 0\n");
-       if(g_race || g_cts)
-               stuffcmd(e, "cl_cmd settemp cl_movecliptokeyboard 2\n");
        if(autocvar_g_antilag == 3) // client side hitscan
                stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
        if(autocvar_sv_gentle)
                stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
+       MUTATOR_CALLHOOK(FixClientCvars, e);
  }
  
  float PlayerInIDList(entity p, string idlist)
@@@ -1067,239 -997,172 +1031,172 @@@ ClientPreConnec
  Called once (not at each match start) when a client begins a connection to the server
  =============
  */
- void ClientPreConnect (void)
- {
+ void ClientPreConnect ()
+ {ENGINE_EVENT();
        if(autocvar_sv_eventlog)
        {
                GameLogEcho(sprintf(":connect:%d:%d:%s",
-                       self.playerid,
-                       num_for_edict(self),
-                       ((IS_REAL_CLIENT(self)) ? self.netaddress : "bot")
+                       this.playerid,
+                       etof(this),
+                       ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot")
                ));
        }
  }
  #endif
  
- /*
+ /**
  =============
  ClientConnect
  
  Called when a client connects to the server
  =============
  */
- void DecodeLevelParms (void);
- //void dom_player_join_team(entity pl);
- void set_dom_state(entity e);
- void ClientConnect (void)
- {
-       float t;
-       if(IS_CLIENT(self))
-       {
-               print("Warning: ClientConnect, but already connected!\n");
-               return;
-       }
-       if(Ban_MaybeEnforceBanOnce(self))
-               return;
-       DecodeLevelParms();
+ void ClientConnect()
+ {ENGINE_EVENT();
+       if (Ban_MaybeEnforceBanOnce(this)) return;
+       assert(!IS_CLIENT(this), return);
+       this.flags |= FL_CLIENT;
+       assert(player_count >= 0, player_count = 0);
  
  #ifdef WATERMARK
-       Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_WATERMARK, WATERMARK);
+       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK);
  #endif
-       self.classname = "player_joining";
-       self.flags = FL_CLIENT;
-       self.version_nagtime = time + 10 + random() * 10;
-       if(player_count<0)
-       {
-               dprint("BUG player count is lower than zero, this cannot happen!\n");
-               player_count = 0;
-       }
-       if(IS_REAL_CLIENT(self)) { PlayerStats_PlayerBasic_CheckUpdate(self); }
-       PlayerScore_Attach(self);
-       ClientData_Attach();
-       accuracy_init(self);
-       bot_clientconnect();
-       playerdemo_init();
-       anticheat_init();
+       this.version_nagtime = time + 10 + random() * 10;
+       TRANSMUTE(Client, this);
  
        // identify the right forced team
-       if(autocvar_g_campaign)
+       if (autocvar_g_campaign)
        {
-               if(IS_REAL_CLIENT(self)) // only players, not bots
+               if (IS_REAL_CLIENT(this)) // only players, not bots
                {
-                       switch(autocvar_g_campaign_forceteam)
+                       switch (autocvar_g_campaign_forceteam)
                        {
-                               case 1: self.team_forced = NUM_TEAM_1; break;
-                               case 2: self.team_forced = NUM_TEAM_2; break;
-                               case 3: self.team_forced = NUM_TEAM_3; break;
-                               case 4: self.team_forced = NUM_TEAM_4; break;
-                               default: self.team_forced = 0;
+                               case 1: this.team_forced = NUM_TEAM_1; break;
+                               case 2: this.team_forced = NUM_TEAM_2; break;
+                               case 3: this.team_forced = NUM_TEAM_3; break;
+                               case 4: this.team_forced = NUM_TEAM_4; break;
+                               default: this.team_forced = 0;
                        }
                }
        }
-       else if(PlayerInIDList(self, autocvar_g_forced_team_red))
-               self.team_forced = NUM_TEAM_1;
-       else if(PlayerInIDList(self, autocvar_g_forced_team_blue))
-               self.team_forced = NUM_TEAM_2;
-       else if(PlayerInIDList(self, autocvar_g_forced_team_yellow))
-               self.team_forced = NUM_TEAM_3;
-       else if(PlayerInIDList(self, autocvar_g_forced_team_pink))
-               self.team_forced = NUM_TEAM_4;
-       else if(autocvar_g_forced_team_otherwise == "red")
-               self.team_forced = NUM_TEAM_1;
-       else if(autocvar_g_forced_team_otherwise == "blue")
-               self.team_forced = NUM_TEAM_2;
-       else if(autocvar_g_forced_team_otherwise == "yellow")
-               self.team_forced = NUM_TEAM_3;
-       else if(autocvar_g_forced_team_otherwise == "pink")
-               self.team_forced = NUM_TEAM_4;
-       else if(autocvar_g_forced_team_otherwise == "spectate")
-               self.team_forced = -1;
-       else if(autocvar_g_forced_team_otherwise == "spectator")
-               self.team_forced = -1;
-       else
-               self.team_forced = 0;
-       if(!teamplay)
-               if(self.team_forced > 0)
-                       self.team_forced = 0;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_red))    this.team_forced = NUM_TEAM_1;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_blue))   this.team_forced = NUM_TEAM_2;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3;
+       else if (PlayerInIDList(this, autocvar_g_forced_team_pink))   this.team_forced = NUM_TEAM_4;
+       else switch (autocvar_g_forced_team_otherwise)
+       {
+               default: this.team_forced = 0; break;
+               case "red": this.team_forced = NUM_TEAM_1; break;
+               case "blue": this.team_forced = NUM_TEAM_2; break;
+               case "yellow": this.team_forced = NUM_TEAM_3; break;
+               case "pink": this.team_forced = NUM_TEAM_4; break;
+               case "spectate":
+               case "spectator":
+                       this.team_forced = -1;
+                       break;
+       }
+       if (!teamplay && this.team_forced > 0) this.team_forced = 0;
  
-       JoinBestTeam(self, false, false); // if the team number is valid, keep it
+     {
+         int id = this.playerid;
+         this.playerid = 0; // silent
+           JoinBestTeam(this, false, false); // if the team number is valid, keep it
+           this.playerid = id;
+     }
  
-       if((autocvar_sv_spectate == 1) || autocvar_g_campaign || self.team_forced < 0) {
-               self.classname = "observer";
+       if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) {
+               TRANSMUTE(Observer, this);
        } else {
-               if(teamplay)
-               {
-                       if(autocvar_g_balance_teams)
-                       {
-                               self.classname = "player";
-                               campaign_bots_may_start = 1;
-                       }
-                       else
-                       {
-                               self.classname = "observer"; // do it anyway
-                       }
-               }
-               else
-               {
-                       self.classname = "player";
-                       campaign_bots_may_start = 1;
+               if (!teamplay || autocvar_g_balance_teams) {
+                       TRANSMUTE(Player, this);
+                       campaign_bots_may_start = true;
+               } else {
+                       TRANSMUTE(Observer, this); // do it anyway
                }
        }
  
-       self.playerid = (playerid_last = playerid_last + 1);
-       PlayerStats_GameReport_AddEvent(sprintf("kills-%d", self.playerid));
-     if(IS_BOT_CLIENT(self))
-         PlayerStats_GameReport_AddPlayer(self);
-       if(autocvar_sv_eventlog)
-               GameLogEcho(strcat(":join:", ftos(self.playerid), ":", ftos(num_for_edict(self)), ":", ((IS_REAL_CLIENT(self)) ? self.netaddress : "bot"), ":", self.netname));
-       LogTeamchange(self.playerid, self.team, 1);
+       PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid));
  
-       self.just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
+       // always track bots, don't ask for cl_allow_uidtracking
+     if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this);
  
-       self.netname_previous = strzone(self.netname);
+       if (autocvar_sv_eventlog)
+               GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname));
  
-       if(IS_PLAYER(self) && teamplay)
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(self, INFO_JOIN_CONNECT_TEAM_), self.netname);
-       else
-               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_CONNECT, self.netname);
+       LogTeamchange(this.playerid, this.team, 1);
  
-       stuffcmd(self, strcat(clientstuff, "\n"));
-       stuffcmd(self, "cl_particles_reloadeffects\n"); // TODO do we still need this?
+       this.just_joined = true;  // stop spamming the eventlog with additional lines when the client connects
  
-       FixClientCvars(self);
+       this.netname_previous = strzone(this.netname);
  
-       // spawnfunc_waypoint sprites
-       WaypointSprite_InitClient(self);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname);
  
-       // Wazat's grappling hook
-       SetGrappleHookBindings();
+       stuffcmd(this, clientstuff, "\n");
+       stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
  
-       // Jetpack binds
-       stuffcmd(self, "alias +jetpack +button10\n");
-       stuffcmd(self, "alias -jetpack -button10\n");
+       FixClientCvars(this);
  
        // get version info from player
-       stuffcmd(self, "cmd clientversion $gameversion\n");
-       // get other cvars from player
-       GetCvars(0);
+       stuffcmd(this, "cmd clientversion $gameversion\n");
  
        // notify about available teams
-       if(teamplay)
+       if (teamplay)
        {
-               CheckAllowedTeams(self);
-               t = 0; if(c1 >= 0) t |= 1; if(c2 >= 0) t |= 2; if(c3 >= 0) t |= 4; if(c4 >= 0) t |= 8;
-               stuffcmd(self, strcat("set _teams_available ", ftos(t), "\n"));
+               CheckAllowedTeams(this);
+               int t = 0;
+               if (c1 >= 0) t |= BIT(0);
+               if (c2 >= 0) t |= BIT(1);
+               if (c3 >= 0) t |= BIT(2);
+               if (c4 >= 0) t |= BIT(3);
+               stuffcmd(this, sprintf("set _teams_available %d\n", t));
        }
        else
-               stuffcmd(self, "set _teams_available 0\n");
-       attach_entcs();
+       {
+               stuffcmd(this, "set _teams_available 0\n");
+       }
  
        bot_relinkplayerlist();
  
-       self.spectatortime = time;
-       if(blockSpectators)
+       this.spectatortime = time;
+       if (blockSpectators)
        {
-               Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
        }
  
-       self.jointime = time;
-       self.allowed_timeouts = autocvar_sv_timeout_number;
+       this.jointime = time;
+       this.allowed_timeouts = autocvar_sv_timeout_number;
  
-       if(IS_REAL_CLIENT(self))
+       if (IS_REAL_CLIENT(this))
        {
-               if(!autocvar_g_campaign)
+               if (!autocvar_g_campaign)
                {
-                       self.motd_actived_time = -1;
-                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage());
+                       this.motd_actived_time = -1;
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
                }
  
-               if(autocvar_g_bugrigs || (g_weaponarena_weapons == WEPSET_TUBA))
-                       stuffcmd(self, "cl_cmd settemp chase_active 1\n");
+               if (g_weaponarena_weapons == WEPSET(TUBA))
+                       stuffcmd(this, "cl_cmd settemp chase_active 1\n");
        }
  
-       if(!sv_foginterval && world.fog != "")
-               stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
-       W_HitPlotOpen(self);
-       if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
-               send_CSQC_teamnagger();
+       if (!sv_foginterval && world.fog != "")
+               stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
  
-       CheatInitClient();
+       if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)))
+               if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
+                       send_CSQC_teamnagger();
  
-       CSQCMODEL_AUTOINIT();
+       CSQCMODEL_AUTOINIT(this);
  
-       self.model_randomizer = random();
+       this.model_randomizer = random();
  
-       if(IS_REAL_CLIENT(self))
-               sv_notice_join();
+       if (IS_REAL_CLIENT(this))
+               sv_notice_join(this);
  
-       for (entity e = world; (e = findfloat(e, init_for_player_needed, 1)); ) {
-               entity oldself = self;
-               self = e;
-               e.init_for_player(oldself);
-               self = oldself;
-       }
+       FOREACH_ENTITY_FLOAT(init_for_player_needed, true, {
+               it.init_for_player(it, this);
+       });
  
-       MUTATOR_CALLHOOK(ClientConnect);
+       MUTATOR_CALLHOOK(ClientConnect, this);
  }
  /*
  =============
@@@ -1310,127 -1173,96 +1207,98 @@@ Called when a client disconnects from t
  */
  .entity chatbubbleentity;
  void ReadyCount();
- void ClientDisconnect (void)
- {
-       if(self.vehicle)
-           vehicles_exit(VHEF_RELESE);
-       if (!IS_CLIENT(self))
-       {
-               print("Warning: ClientDisconnect without ClientConnect\n");
-               return;
-       }
-       PlayerStats_GameReport_FinalizePlayer(self);
-       SetSpectator(self, world);
-       if(IS_PLAYER(self)) { pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); }
+ void ClientDisconnect()
+ {ENGINE_EVENT();
+       assert(IS_CLIENT(this), return);
  
-       CheatShutdownClient();
+       PlayerStats_GameReport_FinalizePlayer(this);
+       if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE);
+       if (this.active_minigame) part_minigame(this);
+       if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
  
-       W_HitPlotClose(self);
+       if (autocvar_sv_eventlog)
+               GameLogEcho(strcat(":part:", ftos(this.playerid)));
  
-       anticheat_report();
-       anticheat_shutdown();
+       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname);
  
-       playerdemo_shutdown();
++      SetSpectatee(this, NULL);
 +
-       bot_clientdisconnect();
+     MUTATOR_CALLHOOK(ClientDisconnect, this);
  
-       if(self.entcs)
-               detach_entcs();
+       ClientState_detach(this);
  
-       if(autocvar_sv_eventlog)
-               GameLogEcho(strcat(":part:", ftos(self.playerid)));
-       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_DISCONNECT, self.netname);
-       MUTATOR_CALLHOOK(ClientDisconnect);
+       Portal_ClearAll(this);
  
-       Portal_ClearAll(self);
+       Unfreeze(this);
  
-       Unfreeze(self);
-       RemoveGrapplingHook(self);
+       RemoveGrapplingHook(this);
  
        // Here, everything has been done that requires this player to be a client.
  
-       self.flags &= ~FL_CLIENT;
-       if (self.chatbubbleentity)
-               remove (self.chatbubbleentity);
+       this.flags &= ~FL_CLIENT;
  
-       if (self.killindicator)
-               remove (self.killindicator);
+       if (this.chatbubbleentity) remove(this.chatbubbleentity);
+       if (this.killindicator) remove(this.killindicator);
  
-       WaypointSprite_PlayerGone();
+       WaypointSprite_PlayerGone(this);
  
        bot_relinkplayerlist();
  
-       accuracy_free(self);
-       ClientData_Detach();
-       PlayerScore_Detach(self);
-       if(self.netname_previous)
-               strunzone(self.netname_previous);
-       if(self.clientstatus)
-               strunzone(self.clientstatus);
-       if(self.weaponorder_byimpulse)
-               strunzone(self.weaponorder_byimpulse);
+       if (this.netname_previous) strunzone(this.netname_previous);
+       if (this.clientstatus) strunzone(this.clientstatus);
+       if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse);
+       if (this.personal) remove(this.personal);
  
-       ClearPlayerSounds();
-       if(self.personal)
-               remove(self.personal);
-       self.playerid = 0;
+       this.playerid = 0;
        ReadyCount();
-       // free cvars
-       GetCvars(-1);
+       if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false);
  }
  
- .float BUTTON_CHAT;
- void ChatBubbleThink()
+ void ChatBubbleThink(entity this)
  {
-       self.nextthink = time;
-       if ((self.owner.alpha < 0) || self.owner.chatbubbleentity != self)
+       this.nextthink = time;
+       if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this)
        {
-               if(self.owner) // but why can that ever be world?
-                       self.owner.chatbubbleentity = world;
-               remove(self);
+               if(this.owner) // but why can that ever be world?
+                       this.owner.chatbubbleentity = world;
+               remove(this);
                return;
        }
-       if ((self.owner.BUTTON_CHAT && !self.owner.deadflag)
- #ifdef TETRIS
-               || self.owner.tetris_on
- #endif
-       )
-               self.model = self.mdl;
-       else
-               self.model = "";
+       this.mdl = "";
+       if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) )
+       {
+               if ( this.owner.active_minigame )
+                       this.mdl = "models/sprites/minigame_busy.iqm";
+               else if (PHYS_INPUT_BUTTON_CHAT(this.owner))
+                       this.mdl = "models/misc/chatbubble.spr";
+       }
+       if ( this.model != this.mdl )
+               _setmodel(this, this.mdl);
  }
  
- void UpdateChatBubble()
+ void UpdateChatBubble(entity this)
  {
-       if (self.alpha < 0)
+       if (this.alpha < 0)
                return;
        // spawn a chatbubble entity if needed
-       if (!self.chatbubbleentity)
-       {
-               self.chatbubbleentity = spawn();
-               self.chatbubbleentity.owner = self;
-               self.chatbubbleentity.exteriormodeltoclient = self;
-               self.chatbubbleentity.think = ChatBubbleThink;
-               self.chatbubbleentity.nextthink = time;
-               setmodel(self.chatbubbleentity, "models/misc/chatbubble.spr"); // precision set below
-               //setorigin(self.chatbubbleentity, self.origin + '0 0 15' + self.maxs_z * '0 0 1');
-               setorigin(self.chatbubbleentity, '0 0 15' + self.maxs.z * '0 0 1');
-               setattachment(self.chatbubbleentity, self, "");  // sticks to moving player better, also conserves bandwidth
-               self.chatbubbleentity.mdl = self.chatbubbleentity.model;
-               self.chatbubbleentity.model = "";
-               self.chatbubbleentity.effects = EF_LOWPRECISION;
+       if (!this.chatbubbleentity)
+       {
+               this.chatbubbleentity = new(chatbubbleentity);
+               this.chatbubbleentity.owner = this;
+               this.chatbubbleentity.exteriormodeltoclient = this;
+               setthink(this.chatbubbleentity, ChatBubbleThink);
+               this.chatbubbleentity.nextthink = time;
+               setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below
+               //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1');
+               setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1');
+               setattachment(this.chatbubbleentity, this, "");  // sticks to moving player better, also conserves bandwidth
+               this.chatbubbleentity.mdl = this.chatbubbleentity.model;
+               //this.chatbubbleentity.model = "";
+               this.chatbubbleentity.effects = EF_LOWPRECISION;
        }
  }
  
  /*void UpdateColorModHack()
  {
        float c;
-       c = self.clientcolors & 15;
+       c = this.clientcolors & 15;
        // LordHavoc: only bothering to support white, green, red, yellow, blue
-            if (!teamplay) self.colormod = '0 0 0';
-       else if (c ==  0) self.colormod = '1.00 1.00 1.00';
-       else if (c ==  3) self.colormod = '0.10 1.73 0.10';
-       else if (c ==  4) self.colormod = '1.73 0.10 0.10';
-       else if (c == 12) self.colormod = '1.22 1.22 0.10';
-       else if (c == 13) self.colormod = '0.10 0.10 1.73';
-       else self.colormod = '1 1 1';
+            if (!teamplay) this.colormod = '0 0 0';
+       else if (c ==  0) this.colormod = '1.00 1.00 1.00';
+       else if (c ==  3) this.colormod = '0.10 1.73 0.10';
+       else if (c ==  4) this.colormod = '1.73 0.10 0.10';
+       else if (c == 12) this.colormod = '1.22 1.22 0.10';
+       else if (c == 13) this.colormod = '0.10 0.10 1.73';
+       else this.colormod = '1 1 1';
  }*/
  
- void respawn(void)
+ void respawn(entity this)
  {
-       if(self.alpha >= 0 && autocvar_g_respawn_ghosts)
-       {
-               self.solid = SOLID_NOT;
-               self.takedamage = DAMAGE_NO;
-               self.movetype = MOVETYPE_FLY;
-               self.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
-               self.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
-               self.effects |= CSQCMODEL_EF_RESPAWNGHOST;
-               pointparticles(particleeffectnum("respawn_ghost"), self.origin, '0 0 0', 1);
+       if(this.alpha >= 0 && autocvar_g_respawn_ghosts)
+       {
+               this.solid = SOLID_NOT;
+               this.takedamage = DAMAGE_NO;
+               this.movetype = MOVETYPE_FLY;
+               this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed;
+               this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3;
+               this.effects |= CSQCMODEL_EF_RESPAWNGHOST;
+               Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1);
                if(autocvar_g_respawn_ghosts_maxtime)
-                       SUB_SetFade (self, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
+                       SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5);
        }
  
-       CopyBody(1);
+       CopyBody(this, 1);
  
-       self.effects |= EF_NODRAW; // prevent another CopyBody
-       PutClientInServer();
+       this.effects |= EF_NODRAW; // prevent another CopyBody
+       WITHSELF(this, PutClientInServer());
  }
  
- void play_countdown(float finished, string samp)
+ void play_countdown(entity this, float finished, Sound samp)
  {
-       if(IS_REAL_CLIENT(self))
+     TC(Sound, samp);
+       if(IS_REAL_CLIENT(this))
                if(floor(finished - time - frametime) != floor(finished - time))
                        if(finished - time < 6)
-                               sound (self, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
+                               sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
  }
  
- void player_powerups (void)
+ void player_powerups(entity this)
  {
        // add a way to see what the items were BEFORE all of these checks for the mutator hook
-       olditems = self.items;
+       int items_prev = this.items;
  
-       if((self.items & IT_USING_JETPACK) && !self.deadflag && !gameover)
-               self.modelflags |= MF_ROCKET;
+       if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover)
+               this.modelflags |= MF_ROCKET;
        else
-               self.modelflags &= ~MF_ROCKET;
+               this.modelflags &= ~MF_ROCKET;
  
-       self.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
+       this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST);
  
-       if((self.alpha < 0 || self.deadflag) && !self.vehicle) // don't apply the flags if the player is gibbed
+       if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed
                return;
  
-       Fire_ApplyDamage(self);
-       Fire_ApplyEffect(self);
+       Fire_ApplyDamage(this);
+       Fire_ApplyEffect(this);
  
        if (!g_instagib)
        {
-               if (self.items & IT_STRENGTH)
+               if (this.items & ITEM_Strength.m_itemid)
                {
-                       play_countdown(self.strength_finished, "misc/poweroff.wav");
-                       self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > self.strength_finished)
+                       play_countdown(this, this.strength_finished, SND_POWEROFF);
+                       this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > this.strength_finished)
                        {
-                               self.items = self.items - (self.items & IT_STRENGTH);
-                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_STRENGTH, self.netname);
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
+                               this.items = this.items - (this.items & ITEM_Strength.m_itemid);
+                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
                        }
                }
                else
                {
-                       if (time < self.strength_finished)
+                       if (time < this.strength_finished)
                        {
-                               self.items = self.items | IT_STRENGTH;
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_STRENGTH, self.netname);
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_STRENGTH);
+                               this.items = this.items | ITEM_Strength.m_itemid;
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH);
                        }
                }
-               if (self.items & IT_INVINCIBLE)
+               if (this.items & ITEM_Shield.m_itemid)
                {
-                       play_countdown(self.invincible_finished, "misc/poweroff.wav");
-                       self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > self.invincible_finished)
+                       play_countdown(this, this.invincible_finished, SND_POWEROFF);
+                       this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > this.invincible_finished)
                        {
-                               self.items = self.items - (self.items & IT_INVINCIBLE);
-                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_SHIELD, self.netname);
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
+                               this.items = this.items - (this.items & ITEM_Shield.m_itemid);
+                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
                        }
                }
                else
                {
-                       if (time < self.invincible_finished)
+                       if (time < this.invincible_finished)
                        {
-                               self.items = self.items | IT_INVINCIBLE;
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SHIELD, self.netname);
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_POWERUP_SHIELD);
+                               this.items = this.items | ITEM_Shield.m_itemid;
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD);
                        }
                }
-               if (self.items & IT_SUPERWEAPON)
+               if (this.items & IT_SUPERWEAPON)
                {
-                       if (!(self.weapons & WEPSET_SUPERWEAPONS))
+                       if (!(this.weapons & WEPSET_SUPERWEAPONS))
                        {
-                               self.superweapons_finished = 0;
-                               self.items = self.items - (self.items & IT_SUPERWEAPON);
-                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_LOST, self.netname);
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
+                               this.superweapons_finished = 0;
+                               this.items = this.items - (this.items & IT_SUPERWEAPON);
+                               //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST);
                        }
-                       else if (self.items & IT_UNLIMITED_SUPERWEAPONS)
+                       else if (this.items & IT_UNLIMITED_SUPERWEAPONS)
                        {
                                // don't let them run out
                        }
                        else
                        {
-                               play_countdown(self.superweapons_finished, "misc/poweroff.wav");
-                               if (time > self.superweapons_finished)
+                               play_countdown(this, this.superweapons_finished, SND_POWEROFF);
+                               if (time > this.superweapons_finished)
                                {
-                                       self.items = self.items - (self.items & IT_SUPERWEAPON);
-                                       self.weapons &= ~WEPSET_SUPERWEAPONS;
-                                       //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_BROKEN, self.netname);
-                                       Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
+                                       this.items = this.items - (this.items & IT_SUPERWEAPON);
+                                       this.weapons &= ~WEPSET_SUPERWEAPONS;
+                                       //Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname);
+                                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN);
                                }
                        }
                }
-               else if(self.weapons & WEPSET_SUPERWEAPONS)
+               else if(this.weapons & WEPSET_SUPERWEAPONS)
                {
-                       if (time < self.superweapons_finished || (self.items & IT_UNLIMITED_SUPERWEAPONS))
+                       if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS))
                        {
-                               self.items = self.items | IT_SUPERWEAPON;
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_PICKUP, self.netname);
-                               Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
+                               this.items = this.items | IT_SUPERWEAPON;
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname);
+                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP);
                        }
                        else
                        {
-                               self.superweapons_finished = 0;
-                               self.weapons &= ~WEPSET_SUPERWEAPONS;
+                               this.superweapons_finished = 0;
+                               this.weapons &= ~WEPSET_SUPERWEAPONS;
                        }
                }
                else
                {
-                       self.superweapons_finished = 0;
+                       this.superweapons_finished = 0;
                }
        }
  
        if(autocvar_g_nodepthtestplayers)
-               self.effects = self.effects | EF_NODEPTHTEST;
+               this.effects = this.effects | EF_NODEPTHTEST;
  
        if(autocvar_g_fullbrightplayers)
-               self.effects = self.effects | EF_FULLBRIGHT;
+               this.effects = this.effects | EF_FULLBRIGHT;
  
        if (time >= game_starttime)
-       if (time < self.spawnshieldtime)
-               self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
+       if (time < this.spawnshieldtime)
+               this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
  
-       MUTATOR_CALLHOOK(PlayerPowerups);
+       MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
  }
  
  float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
@@@ -1643,45 -1476,60 +1512,60 @@@ float CalcRotRegen(float current, floa
        return current;
  }
  
- void player_regen (void)
+ void player_regen(entity this)
  {
        float max_mod, regen_mod, rot_mod, limit_mod;
        max_mod = regen_mod = rot_mod = limit_mod = 1;
-       regen_mod_max = max_mod;
-       regen_mod_regen = regen_mod;
-       regen_mod_rot = rot_mod;
-       regen_mod_limit = limit_mod;
-       if(!MUTATOR_CALLHOOK(PlayerRegen))
-       if(!self.frozen)
-       {
-               float minh, mina, maxh, maxa, limith, limita;
-               maxh = autocvar_g_balance_health_rotstable;
+       float regen_health = autocvar_g_balance_health_regen;
+       float regen_health_linear = autocvar_g_balance_health_regenlinear;
+       float regen_health_rot = autocvar_g_balance_health_rot;
+       float regen_health_rotlinear = autocvar_g_balance_health_rotlinear;
+       float regen_health_stable = autocvar_g_balance_health_regenstable;
+       float regen_health_rotstable = autocvar_g_balance_health_rotstable;
+       bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot,
+               regen_health_rotlinear, regen_health_stable, regen_health_rotstable);
+       max_mod = M_ARGV(1, float);
+       regen_mod = M_ARGV(2, float);
+       rot_mod = M_ARGV(3, float);
+       limit_mod = M_ARGV(4, float);
+       regen_health = M_ARGV(5, float);
+       regen_health_linear = M_ARGV(6, float);
+       regen_health_rot = M_ARGV(7, float);
+       regen_health_rotlinear = M_ARGV(8, float);
+       regen_health_stable = M_ARGV(9, float);
+       regen_health_rotstable = M_ARGV(10, float);
+       if(!mutator_returnvalue)
+       if(!STAT(FROZEN, this))
+       {
+               float mina, maxa, limith, limita;
                maxa = autocvar_g_balance_armor_rotstable;
-               minh = autocvar_g_balance_health_regenstable;
                mina = autocvar_g_balance_armor_regenstable;
                limith = autocvar_g_balance_health_limit;
                limita = autocvar_g_balance_armor_limit;
  
-               max_mod = regen_mod_max;
-               regen_mod = regen_mod_regen;
-               rot_mod = regen_mod_rot;
-               limit_mod = regen_mod_limit;
-               maxh = maxh * max_mod;
-               minh = minh * max_mod;
+               regen_health_rotstable = regen_health_rotstable * max_mod;
+               regen_health_stable = regen_health_stable * max_mod;
                limith = limith * limit_mod;
                limita = limita * limit_mod;
  
-               self.armorvalue = CalcRotRegen(self.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > self.pauserotarmor_finished), limita);
-               self.health = CalcRotRegen(self.health, minh, autocvar_g_balance_health_regen, autocvar_g_balance_health_regenlinear, regen_mod * frametime * (time > self.pauseregen_finished), maxh, autocvar_g_balance_health_rot, autocvar_g_balance_health_rotlinear, rot_mod * frametime * (time > self.pauserothealth_finished), limith);
+               this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita);
+               this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith);
        }
  
        // if player rotted to death...  die!
        // check this outside above checks, as player may still be able to rot to death
-       if(self.health < 1)
-               self.event_damage(self, self, 1, DEATH_ROT, self.origin, '0 0 0');
+       if(this.health < 1)
+       {
+               if(this.vehicle)
+                       vehicles_exit(this.vehicle, VHEF_RELEASE);
+               if(this.event_damage)
+                       this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0');
+       }
  
-       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
+       if (!(this.items & IT_UNLIMITED_WEAPON_AMMO))
        {
                float minf, maxf, limitf;
  
                minf = autocvar_g_balance_fuel_regenstable;
                limitf = autocvar_g_balance_fuel_limit;
  
-               self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > self.pauseregen_finished) * ((self.items & IT_FUEL_REGEN) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > self.pauserotfuel_finished), limitf);
+               this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf);
        }
  }
  
float zoomstate_set;
- void SetZoomState(float z)
bool zoomstate_set;
+ void SetZoomState(entity this, float z)
  {
-       if(z != self.zoomstate)
+       if(z != this.zoomstate)
        {
-               self.zoomstate = z;
-               ClientData_Touch(self);
+               this.zoomstate = z;
+               ClientData_Touch(this);
        }
-       zoomstate_set = 1;
+       zoomstate_set = true;
  }
  
- void GetPressedKeys(void) {
-       MUTATOR_CALLHOOK(GetPressedKeys);
-       if (self.movement.x > 0) // get if movement keys are pressed
-       {       // forward key pressed
-               self.pressedkeys |= KEY_FORWARD;
-               self.pressedkeys &= ~KEY_BACKWARD;
-       }
-       else if (self.movement.x < 0)
-       {       // backward key pressed
-               self.pressedkeys |= KEY_BACKWARD;
-               self.pressedkeys &= ~KEY_FORWARD;
-       }
-       else
-       {       // no x input
-               self.pressedkeys &= ~KEY_FORWARD;
-               self.pressedkeys &= ~KEY_BACKWARD;
-       }
-       if (self.movement.y > 0)
-       {       // right key pressed
-               self.pressedkeys |= KEY_RIGHT;
-               self.pressedkeys &= ~KEY_LEFT;
-       }
-       else if (self.movement.y < 0)
-       {       // left key pressed
-               self.pressedkeys |= KEY_LEFT;
-               self.pressedkeys &= ~KEY_RIGHT;
-       }
-       else
-       {       // no y input
-               self.pressedkeys &= ~KEY_RIGHT;
-               self.pressedkeys &= ~KEY_LEFT;
-       }
-       if (self.BUTTON_JUMP) // get if jump and crouch keys are pressed
-               self.pressedkeys |= KEY_JUMP;
-       else
-               self.pressedkeys &= ~KEY_JUMP;
-       if (self.BUTTON_CROUCH)
-               self.pressedkeys |= KEY_CROUCH;
-       else
-               self.pressedkeys &= ~KEY_CROUCH;
-       if (self.BUTTON_ATCK)
-               self.pressedkeys |= KEY_ATCK;
-       else
-               self.pressedkeys &= ~KEY_ATCK;
-       if (self.BUTTON_ATCK2)
-               self.pressedkeys |= KEY_ATCK2;
-       else
-               self.pressedkeys &= ~KEY_ATCK2;
+ void GetPressedKeys(entity this)
+ {
+       MUTATOR_CALLHOOK(GetPressedKeys, this);
+       int keys = this.pressedkeys;
+       keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
+       keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
+       keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
+       keys = BITSET(keys, KEY_LEFT,           this.movement.y < 0);
+       keys = BITSET(keys, KEY_JUMP,           PHYS_INPUT_BUTTON_JUMP(this));
+       keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
+       keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
+       keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
+       this.pressedkeys = keys;
  }
  
  /*
@@@ -1763,69 -1574,68 +1610,68 @@@ spectate mode routine
  ======================
  */
  
- void SpectateCopy(entity spectatee) {
-       other = spectatee;
-       MUTATOR_CALLHOOK(SpectateCopy);
-       self.armortype = spectatee.armortype;
-       self.armorvalue = spectatee.armorvalue;
-       self.ammo_cells = spectatee.ammo_cells;
-       self.ammo_plasma = spectatee.ammo_plasma;
-       self.ammo_shells = spectatee.ammo_shells;
-       self.ammo_nails = spectatee.ammo_nails;
-       self.ammo_rockets = spectatee.ammo_rockets;
-       self.ammo_fuel = spectatee.ammo_fuel;
-       self.clip_load = spectatee.clip_load;
-       self.clip_size = spectatee.clip_size;
-       self.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
-       self.health = spectatee.health;
-       self.impulse = 0;
-       self.items = spectatee.items;
-       self.last_pickup = spectatee.last_pickup;
-       self.hit_time = spectatee.hit_time;
-       self.metertime = spectatee.metertime;
-       self.strength_finished = spectatee.strength_finished;
-       self.invincible_finished = spectatee.invincible_finished;
-       self.pressedkeys = spectatee.pressedkeys;
-       self.weapons = spectatee.weapons;
-       self.switchweapon = spectatee.switchweapon;
-       self.switchingweapon = spectatee.switchingweapon;
-       self.weapon = spectatee.weapon;
-       self.vortex_charge = spectatee.vortex_charge;
-       self.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
-       self.hagar_load = spectatee.hagar_load;
-       self.arc_heat_percent = spectatee.arc_heat_percent;
-       self.minelayer_mines = spectatee.minelayer_mines;
-       self.punchangle = spectatee.punchangle;
-       self.view_ofs = spectatee.view_ofs;
-       self.velocity = spectatee.velocity;
-       self.dmg_take = spectatee.dmg_take;
-       self.dmg_save = spectatee.dmg_save;
-       self.dmg_inflictor = spectatee.dmg_inflictor;
-       self.v_angle = spectatee.v_angle;
-       self.angles = spectatee.v_angle;
-       self.frozen = spectatee.frozen;
-       self.revive_progress = spectatee.revive_progress;
-       if(!self.BUTTON_USE)
-               self.fixangle = true;
-       setorigin(self, spectatee.origin);
-       setsize(self, spectatee.mins, spectatee.maxs);
-       SetZoomState(spectatee.zoomstate);
-     anticheat_spectatecopy(spectatee);
-       self.hud = spectatee.hud;
+ void SpectateCopy(entity this, entity spectatee)
+ {
+     TC(Client, this); TC(Client, spectatee);
+       MUTATOR_CALLHOOK(SpectateCopy, spectatee, this);
+       PS(this) = PS(spectatee);
+       this.armortype = spectatee.armortype;
+       this.armorvalue = spectatee.armorvalue;
+       this.ammo_cells = spectatee.ammo_cells;
+       this.ammo_plasma = spectatee.ammo_plasma;
+       this.ammo_shells = spectatee.ammo_shells;
+       this.ammo_nails = spectatee.ammo_nails;
+       this.ammo_rockets = spectatee.ammo_rockets;
+       this.ammo_fuel = spectatee.ammo_fuel;
+       this.clip_load = spectatee.clip_load;
+       this.clip_size = spectatee.clip_size;
+       this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance
+       this.health = spectatee.health;
+       this.impulse = 0;
+       this.items = spectatee.items;
+       this.last_pickup = spectatee.last_pickup;
+       this.hit_time = spectatee.hit_time;
+       this.strength_finished = spectatee.strength_finished;
+       this.invincible_finished = spectatee.invincible_finished;
+       this.pressedkeys = spectatee.pressedkeys;
+       this.weapons = spectatee.weapons;
+       this.vortex_charge = spectatee.vortex_charge;
+       this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
+       this.hagar_load = spectatee.hagar_load;
+       this.arc_heat_percent = spectatee.arc_heat_percent;
+       this.minelayer_mines = spectatee.minelayer_mines;
+       this.punchangle = spectatee.punchangle;
+       this.view_ofs = spectatee.view_ofs;
+       this.velocity = spectatee.velocity;
+       this.dmg_take = spectatee.dmg_take;
+       this.dmg_save = spectatee.dmg_save;
+       this.dmg_inflictor = spectatee.dmg_inflictor;
+       this.v_angle = spectatee.v_angle;
+       this.angles = spectatee.v_angle;
+       STAT(FROZEN, this) = STAT(FROZEN, spectatee);
+       this.revive_progress = spectatee.revive_progress;
+       if(!PHYS_INPUT_BUTTON_USE(this))
+               this.fixangle = true;
+       setorigin(this, spectatee.origin);
+       setsize(this, spectatee.mins, spectatee.maxs);
+       SetZoomState(this, spectatee.zoomstate);
+     anticheat_spectatecopy(this, spectatee);
+       this.hud = spectatee.hud;
        if(spectatee.vehicle)
      {
-         self.fixangle = false;
-         //self.velocity = spectatee.vehicle.velocity;
-         self.vehicle_health = spectatee.vehicle_health;
-         self.vehicle_shield = spectatee.vehicle_shield;
-         self.vehicle_energy = spectatee.vehicle_energy;
-         self.vehicle_ammo1 = spectatee.vehicle_ammo1;
-         self.vehicle_ammo2 = spectatee.vehicle_ammo2;
-         self.vehicle_reload1 = spectatee.vehicle_reload1;
-         self.vehicle_reload2 = spectatee.vehicle_reload2;
-         msg_entity = self;
+         this.fixangle = false;
+         //this.velocity = spectatee.vehicle.velocity;
+         this.vehicle_health = spectatee.vehicle_health;
+         this.vehicle_shield = spectatee.vehicle_shield;
+         this.vehicle_energy = spectatee.vehicle_energy;
+         this.vehicle_ammo1 = spectatee.vehicle_ammo1;
+         this.vehicle_ammo2 = spectatee.vehicle_ammo2;
+         this.vehicle_reload1 = spectatee.vehicle_reload1;
+         this.vehicle_reload2 = spectatee.vehicle_reload2;
+         msg_entity = this;
  
          WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
              WriteAngle(MSG_ONE,  spectatee.v_angle.x);
              WriteAngle(MSG_ONE,  spectatee.v_angle.z);
  
          //WriteByte (MSG_ONE, SVC_SETVIEW);
-         //    WriteEntity(MSG_ONE, self);
+         //    WriteEntity(MSG_ONE, this);
          //makevectors(spectatee.v_angle);
-         //setorigin(self, spectatee.origin - v_forward * 400 + v_up * 300);*/
+         //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/
      }
  }
  
float SpectateUpdate()
bool SpectateUpdate(entity this)
  {
-       if(!self.enemy)
-           return 0;
+       if(!this.enemy)
+           return false;
  
-       if(!IS_PLAYER(self.enemy) || self == self.enemy)
+       if(!IS_PLAYER(this.enemy) || this == this.enemy)
        {
-               SetSpectator(self, world);
-               return 0;
+               SetSpectatee(this, NULL);
+               return false;
        }
  
-       SpectateCopy(self.enemy);
+       SpectateCopy(this, this.enemy);
  
-       return 1;
+       return true;
  }
  
float SpectateSet()
bool SpectateSet(entity this)
  {
-       if(!IS_PLAYER(self.enemy))
+       if(!IS_PLAYER(this.enemy))
                return false;
  
-       ClientData_Touch(self.enemy);
++      ClientData_Touch(this.enemy);
 +
-       msg_entity = self;
+       msg_entity = this;
        WriteByte(MSG_ONE, SVC_SETVIEW);
-       WriteEntity(MSG_ONE, self.enemy);
-       //stuffcmd(self, "set viewsize $tmpviewsize \n");
-       self.movetype = MOVETYPE_NONE;
-       accuracy_resend(self);
+       WriteEntity(MSG_ONE, this.enemy);
+       this.movetype = MOVETYPE_NONE;
+       accuracy_resend(this);
  
-       if(!SpectateUpdate())
-               PutObserverInServer();
+       if(!SpectateUpdate(this))
+               PutObserverInServer(this);
  
        return true;
  }
  
- void SetSpectator(entity player, entity spectatee)
+ void SetSpectatee(entity this, entity spectatee)
  {
-       entity old_spectatee = player.enemy;
+       entity old_spectatee = this.enemy;
  
-       player.enemy = spectatee;
+       this.enemy = spectatee;
  
        // WEAPONTODO
        // these are required to fix the spectator bug with arc
        if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
-       if(player.enemy && player.enemy.arc_beam) { player.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
+       if(this.enemy && this.enemy.arc_beam) { this.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; }
 +
 +      // needed to update spectator list
 +      if(old_spectatee) { ClientData_Touch(old_spectatee); }
  }
  
- float Spectate(entity pl)
- {
-       if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
-       if(DIFF_TEAM(pl, self))
-               return 0;
-       SetSpectator(self, pl);
-       return SpectateSet();
- }
- // Returns next available player to spectate if g_ca_spectate_enemies == 0
- entity CA_SpectateNext(entity start)
+ bool Spectate(entity this, entity pl)
  {
-       if(SAME_TEAM(start, self)) { return start; }
-       other = start;
-       // continue from current player
-       while(other && DIFF_TEAM(other, self))
-               other = find(other, classname, "player");
-       if (!other)
-       {
-               // restart from begining
-               other = find(other, classname, "player");
-               while(other && DIFF_TEAM(other, self))
-                       other = find(other, classname, "player");
-       }
+       if(MUTATOR_CALLHOOK(SpectateSet, this, pl))
+               return false;
+       pl = M_ARGV(1, entity);
  
-       return other;
+       SetSpectatee(this, pl);
+       return SpectateSet(this);
  }
  
float SpectateNext()
bool SpectateNext(entity this)
  {
-       other = find(self.enemy, classname, "player");
+       other = find(this.enemy, classname, STR_PLAYER);
  
-       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
-       {
-               // CA and ca players when spectating enemies is forbidden
-               other = CA_SpectateNext(other);
-       }
-       else
-       {
-               // other modes and ca spectators or spectating enemies is allowed
-               if (!other)
-                       other = find(other, classname, "player");
-       }
+       if (MUTATOR_CALLHOOK(SpectateNext, this, other))
+               other = M_ARGV(1, entity);
+       else if (!other)
+               other = find(other, classname, STR_PLAYER);
  
-       if(other) { SetSpectator(self, other); }
+       if(other) { SetSpectatee(this, other); }
  
-       return SpectateSet();
+       return SpectateSet(this);
  }
  
float SpectatePrev()
bool SpectatePrev(entity this)
  {
        // NOTE: chain order is from the highest to the lower entnum (unlike find)
-       other = findchain(classname, "player");
+       other = findchain(classname, STR_PLAYER);
        if (!other) // no player
                return false;
  
        entity first = other;
        // skip players until current spectated player
-       if(self.enemy)
-       while(other && other != self.enemy)
+       if(this.enemy)
+       while(other && other != this.enemy)
                other = other.chain;
  
-       if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer)
+       switch (MUTATOR_CALLHOOK(SpectatePrev, this, other, first))
        {
-               do { other = other.chain; }
-               while(other && other.team != self.team);
-               if (!other)
+               case MUT_SPECPREV_FOUND:
+                   other = M_ARGV(1, entity);
+                   break;
+               case MUT_SPECPREV_RETURN:
+                   other = M_ARGV(1, entity);
+                   return true;
+               case MUT_SPECPREV_CONTINUE:
+               default:
                {
-                       other = first;
-                       while(other.team != self.team)
+                       if(other.chain)
                                other = other.chain;
-                       if(other == self.enemy)
-                               return true;
+                       else
+                               other = first;
+                       break;
                }
        }
-       else
-       {
-               if(other.chain)
-                       other = other.chain;
-               else
-                       other = first;
-       }
-       SetSpectator(self, other);
-       return SpectateSet();
+       SetSpectatee(this, other);
+       return SpectateSet(this);
  }
  
  /*
@@@ -1987,57 -1761,54 +1802,56 @@@ ShowRespawnCountdown(
  Update a respawn countdown display.
  =============
  */
- void ShowRespawnCountdown()
+ void ShowRespawnCountdown(entity this)
  {
        float number;
-       if(self.deadflag == DEAD_NO) // just respawned?
+       if(!IS_DEAD(this)) // just respawned?
                return;
        else
        {
-               number = ceil(self.respawn_time - time);
+               number = ceil(this.respawn_time - time);
                if(number <= 0)
                        return;
-               if(number <= self.respawn_countdown)
+               if(number <= this.respawn_countdown)
                {
-                       self.respawn_countdown = number - 1;
-                       if(ceil(self.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
-                               { Send_Notification(NOTIF_ONE, self, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); }
+                       this.respawn_countdown = number - 1;
+                       if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds
+                               { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); }
                }
        }
  }
  
- void LeaveSpectatorMode()
+ void LeaveSpectatorMode(entity this)
  {
-       if(self.caplayer)
+       if(this.caplayer)
                return;
-       if(nJoinAllowed(self))
+       if(nJoinAllowed(this, this))
        {
-               if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
+               if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
                {
-                       self.classname = "player";
-                       nades_RemoveBonus(self);
+                       TRANSMUTE(Player, this);
  
-                       SetSpectator(self, world);
++                      SetSpectatee(self, world);
 +
                        if(autocvar_g_campaign || autocvar_g_balance_teams)
-                               { JoinBestTeam(self, false, true); }
+                               { JoinBestTeam(this, false, true); }
  
                        if(autocvar_g_campaign)
-                               { campaign_bots_may_start = 1; }
+                               { campaign_bots_may_start = true; }
  
-                       Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN);
+                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
  
-                       PutClientInServer();
+                       WITHSELF(this, PutClientInServer());
  
-                       if(IS_PLAYER(self)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname); }
+                       if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
                }
                else
-                       stuffcmd(self, "menu_showteamselect\n");
+                       stuffcmd(this, "menu_showteamselect\n");
        }
        else
        {
                // Player may not join because g_maxplayers is set
-               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_PREVENT);
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
        }
  }
  
   * it checks whether the number of currently playing players exceeds g_maxplayers.
   * @return int number of free slots for players, 0 if none
   */
- float nJoinAllowed(entity ignore) {
+ bool nJoinAllowed(entity this, entity ignore)
+ {
        if(!ignore)
        // this is called that way when checking if anyone may be able to join (to build qcstatus)
        // so report 0 free slots if restricted
        {
                if(autocvar_g_forced_team_otherwise == "spectate")
-                       return 0;
+                       return false;
                if(autocvar_g_forced_team_otherwise == "spectator")
-                       return 0;
+                       return false;
        }
  
-       if(self.team_forced < 0)
-               return 0; // forced spectators can never join
+       if(this.team_forced < 0)
+               return false; // forced spectators can never join
  
        // TODO simplify this
-       entity e;
-       float totalClients = 0;
-       FOR_EACH_CLIENT(e)
-               if(e != ignore)
-                       totalClients += 1;
+       int totalClients = 0;
+       int currentlyPlaying = 0;
+       FOREACH_CLIENT(true, LAMBDA(
+               if(it != ignore)
+                       ++totalClients;
+               if(IS_REAL_CLIENT(it))
+               if(IS_PLAYER(it) || it.caplayer)
+                       ++currentlyPlaying;
+       ));
  
        if (!autocvar_g_maxplayers)
                return maxclients - totalClients;
  
-       float currentlyPlaying = 0;
-       FOR_EACH_REALCLIENT(e)
-               if(IS_PLAYER(e) || e.caplayer)
-                       currentlyPlaying += 1;
        if(currentlyPlaying < autocvar_g_maxplayers)
                return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
  
-       return 0;
+       return false;
  }
  
  /**
   * Checks whether the client is an observer or spectator, if so, he will get kicked after
   * g_maxplayers_spectator_blocktime seconds
   */
- void checkSpectatorBlock() {
-       if(IS_SPEC(self) || IS_OBSERVER(self))
-       if(!self.caplayer)
-       if(IS_REAL_CLIENT(self))
-       {
-               if( time > (self.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
-                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
-                       dropclient(self);
+ void checkSpectatorBlock(entity this)
+ {
+       if(IS_SPEC(this) || IS_OBSERVER(this))
+       if(!this.caplayer)
+       if(IS_REAL_CLIENT(this))
+       {
+               if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
+                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
+                       dropclient(this);
                }
        }
  }
  
- void PrintWelcomeMessage()
+ void PrintWelcomeMessage(entity this)
  {
-       if(self.motd_actived_time == 0)
+       if(this.motd_actived_time == 0)
        {
                if (autocvar_g_campaign) {
-                       if ((IS_PLAYER(self) && self.BUTTON_INFO) || (!IS_PLAYER(self))) {
-                               self.motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, campaign_message);
+                       if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) {
+                               this.motd_actived_time = time;
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message);
                        }
                } else {
-                       if (self.BUTTON_INFO) {
-                               self.motd_actived_time = time;
-                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_MOTD, getwelcomemessage());
+                       if (PHYS_INPUT_BUTTON_INFO(this)) {
+                               this.motd_actived_time = time;
+                               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this));
                        }
                }
        }
-       else if(self.motd_actived_time > 0) // showing MOTD or campaign message
+       else if(this.motd_actived_time > 0) // showing MOTD or campaign message
        {
                if (autocvar_g_campaign) {
-                       if (self.BUTTON_INFO)
-                               self.motd_actived_time = time;
-                       else if ((time - self.motd_actived_time > 2) && IS_PLAYER(self)) { // hide it some seconds after BUTTON_INFO has been released
-                               self.motd_actived_time = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD);
+                       if (PHYS_INPUT_BUTTON_INFO(this))
+                               this.motd_actived_time = time;
+                       else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released
+                               this.motd_actived_time = 0;
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                        }
                } else {
-                       if (self.BUTTON_INFO)
-                               self.motd_actived_time = time;
-                       else if (time - self.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
-                               self.motd_actived_time = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD);
+                       if (PHYS_INPUT_BUTTON_INFO(this))
+                               this.motd_actived_time = time;
+                       else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released
+                               this.motd_actived_time = 0;
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                        }
                }
        }
-       else //if(self.motd_actived_time < 0) // just connected, motd is active
+       else //if(this.motd_actived_time < 0) // just connected, motd is active
        {
-               if(self.BUTTON_INFO) // BUTTON_INFO hides initial MOTD
-                       self.motd_actived_time = -2; // wait until BUTTON_INFO gets released
-               else if(self.motd_actived_time == -2 || IS_PLAYER(self))
+               if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD
+                       this.motd_actived_time = -2; // wait until BUTTON_INFO gets released
+               else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this))
                {
                        // instanctly hide MOTD
-                       self.motd_actived_time = 0;
-                       Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_MOTD);
+                       this.motd_actived_time = 0;
+                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD);
                }
        }
  }
  
- void ObserverThink()
+ void ObserverThink(entity this)
  {
+       if ( this.impulse )
+       {
+               MinigameImpulse(this, this.impulse);
+               this.impulse = 0;
+       }
        float prefered_movetype;
-       if (self.flags & FL_JUMPRELEASED) {
-               if (self.BUTTON_JUMP && !self.version_mismatch) {
-                       self.flags &= ~FL_JUMPRELEASED;
-                       self.flags |= FL_SPAWNING;
-               } else if(self.BUTTON_ATCK && !self.version_mismatch) {
-                       self.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext()) {
-                               self.classname = "spectator";
+       if (this.flags & FL_JUMPRELEASED) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       this.flags |= FL_SPAWNING;
+               } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       if(SpectateNext(this)) {
+                               TRANSMUTE(Spectator, this);
                        }
                } else {
-                       prefered_movetype = ((!self.BUTTON_USE ? self.cvar_cl_clippedspectating : !self.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
-                       if (self.movetype != prefered_movetype)
-                               self.movetype = prefered_movetype;
+                       prefered_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP);
+                       if (this.movetype != prefered_movetype)
+                               this.movetype = prefered_movetype;
                }
        } else {
-               if (!(self.BUTTON_ATCK || self.BUTTON_JUMP)) {
-                       self.flags |= FL_JUMPRELEASED;
-                       if(self.flags & FL_SPAWNING)
+               if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) {
+                       this.flags |= FL_JUMPRELEASED;
+                       if(this.flags & FL_SPAWNING)
                        {
-                               self.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode();
+                               this.flags &= ~FL_SPAWNING;
+                               LeaveSpectatorMode(this);
                                return;
                        }
                }
        }
  }
  
- void SpectatorThink()
+ void SpectatorThink(entity this)
  {
-       if (self.flags & FL_JUMPRELEASED) {
-               if (self.BUTTON_JUMP && !self.version_mismatch) {
-                       self.flags &= ~FL_JUMPRELEASED;
-                       self.flags |= FL_SPAWNING;
-               } else if(self.BUTTON_ATCK || self.impulse == 10 || self.impulse == 15 || self.impulse == 18 || (self.impulse >= 200 && self.impulse <= 209)) {
-                       self.flags &= ~FL_JUMPRELEASED;
-                       if(SpectateNext()) {
-                               self.classname = "spectator";
+       if ( this.impulse )
+       {
+               if(MinigameImpulse(this, this.impulse))
+                       this.impulse = 0;
+       }
+       if (this.flags & FL_JUMPRELEASED) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       this.flags |= FL_SPAWNING;
+               } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       if(SpectateNext(this)) {
+                               TRANSMUTE(Spectator, this);
                        } else {
-                               self.classname = "observer";
-                               PutClientInServer();
+                               TRANSMUTE(Observer, this);
+                               WITHSELF(this, PutClientInServer());
                        }
-                       self.impulse = 0;
-               } else if(self.impulse == 12 || self.impulse == 16  || self.impulse == 19 || (self.impulse >= 220 && self.impulse <= 229)) {
-                       self.flags &= ~FL_JUMPRELEASED;
-                       if(SpectatePrev()) {
-                               self.classname = "spectator";
+                       this.impulse = 0;
+               } else if(this.impulse == 12 || this.impulse == 16  || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       if(SpectatePrev(this)) {
+                               TRANSMUTE(Spectator, this);
                        } else {
-                               self.classname = "observer";
-                               PutClientInServer();
+                               TRANSMUTE(Observer, this);
+                               WITHSELF(this, PutClientInServer());
                        }
-                       self.impulse = 0;
-               } else if (self.BUTTON_ATCK2) {
-                       self.flags &= ~FL_JUMPRELEASED;
-                       self.classname = "observer";
-                       PutClientInServer();
+                       this.impulse = 0;
+               } else if (PHYS_INPUT_BUTTON_ATCK2(this)) {
+                       this.flags &= ~FL_JUMPRELEASED;
+                       TRANSMUTE(Observer, this);
+                       WITHSELF(this, PutClientInServer());
                } else {
-                       if(!SpectateUpdate())
-                               PutObserverInServer();
+                       if(!SpectateUpdate(this))
+                               PutObserverInServer(this);
                }
        } else {
-               if (!(self.BUTTON_ATCK || self.BUTTON_ATCK2)) {
-                       self.flags |= FL_JUMPRELEASED;
-                       if(self.flags & FL_SPAWNING)
+               if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) {
+                       this.flags |= FL_JUMPRELEASED;
+                       if(this.flags & FL_SPAWNING)
                        {
-                               self.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode();
+                               this.flags &= ~FL_SPAWNING;
+                               LeaveSpectatorMode(this);
                                return;
                        }
                }
-               if(!SpectateUpdate())
-                       PutObserverInServer();
+               if(!SpectateUpdate(this))
+                       PutObserverInServer(this);
        }
  
-       self.flags |= FL_CLIENT | FL_NOTARGET;
+       this.flags |= FL_CLIENT | FL_NOTARGET;
  }
  
- void PlayerUseKey()
+ void vehicles_enter (entity pl, entity veh);
+ void PlayerUseKey(entity this)
  {
-       if (!IS_PLAYER(self))
+       if (!IS_PLAYER(this))
                return;
  
-       if(self.vehicle)
+       if(this.vehicle)
        {
-         vehicles_exit(VHEF_NORMAL);
-         return;
+               if(!gameover)
+               {
+                       vehicles_exit(this.vehicle, VHEF_NORMAL);
+                       return;
+               }
        }
-       // a use key was pressed; call handlers
-       MUTATOR_CALLHOOK(PlayerUseKey);
- }
- float isInvisibleString(string s)
- {
-       float i, n, c;
-       s = strdecolorize(s);
-       for((i = 0), (n = strlen(s)); i < n; ++i)
+       else if(autocvar_g_vehicles_enter)
        {
-               c = str2chr(s, i);
-               switch(c)
+               if(!STAT(FROZEN, this))
+               if(!IS_DEAD(this))
+               if(!gameover)
                {
-                       case 0:
-                       case 32: // space
-                               break;
-                       case 192: // charmap space
-                               if (!autocvar_utf8_enable)
-                                       break;
-                               return false;
-                       case 160: // space in unicode fonts
-                       case 0xE000 + 192: // utf8 charmap space
-                               if (autocvar_utf8_enable)
-                                       break;
-                       default:
-                               return false;
+                       entity head, closest_target = world;
+                       head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true);
+                       while(head) // find the closest acceptable target to enter
+                       {
+                               if(head.vehicle_flags & VHF_ISVEHICLE)
+                               if(!IS_DEAD(head))
+                               if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this)))
+                               if(head.takedamage != DAMAGE_NO)
+                               {
+                                       if(closest_target)
+                                       {
+                                               if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin))
+                                               { closest_target = head; }
+                                       }
+                                       else { closest_target = head; }
+                               }
+                               head = head.chain;
+                       }
+                       if(closest_target) { vehicles_enter(this, closest_target); return; }
                }
        }
-       return true;
+       // a use key was pressed; call handlers
+       MUTATOR_CALLHOOK(PlayerUseKey, this);
  }
  
  /*
  =============
  PlayerPreThink
@@@ -2274,368 -2064,347 +2107,347 @@@ Called every frame for each client befo
  =============
  */
  .float usekeypressed;
void() nexball_setstatus;
.float last_vehiclecheck;
  .int items_added;
- void PlayerPreThink (void)
- {
-       WarpZone_PlayerPhysics_FixVAngle();
+ void PlayerPreThink ()
+ {ENGINE_EVENT();
+       WarpZone_PlayerPhysics_FixVAngle(this);
  
-       self.stat_game_starttime = game_starttime;
-       self.stat_round_starttime = round_starttime;
-       self.stat_allow_oldvortexbeam = autocvar_g_allow_oldvortexbeam;
-       self.stat_leadlimit = autocvar_leadlimit;
+     STAT(GAMESTARTTIME, this) = game_starttime;
+       STAT(ROUNDSTARTTIME, this) = round_starttime;
+       STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam;
+       STAT(LEADLIMIT, this) = autocvar_leadlimit;
  
-       if(frametime)
-       {
+       STAT(WEAPONSINMAP, this) = weaponsInMap;
+       if (frametime) {
                // physics frames: update anticheat stuff
-               anticheat_prethink();
+               anticheat_prethink(this);
        }
  
-       if(blockSpectators && frametime)
-               // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
-               checkSpectatorBlock();
+       if (blockSpectators && frametime) {
+               // WORKAROUND: only use dropclient in server frames (frametime set).
+               // Never use it in cl_movement frames (frametime zero).
+               checkSpectatorBlock(this);
+     }
  
-       zoomstate_set = 0;
+       zoomstate_set = false;
  
-       // Savage: Check for nameless players
-       if (isInvisibleString(self.netname)) {
-               string new_name = strzone(strcat("Player@", ftos(self.playerid)));
-               if(autocvar_sv_eventlog)
-                       GameLogEcho(strcat(":name:", ftos(self.playerid), ":", new_name));
-               if(self.netname_previous)
-                       strunzone(self.netname_previous);
-               self.netname_previous = strzone(new_name);
-               self.netname = self.netname_previous;
-               // stuffcmd(self, strcat("name ", self.netname, "\n"));
-       } else if(self.netname_previous != self.netname) {
-               if(autocvar_sv_eventlog)
-                       GameLogEcho(strcat(":name:", ftos(self.playerid), ":", self.netname));
-               if(self.netname_previous)
-                       strunzone(self.netname_previous);
-               self.netname_previous = strzone(self.netname);
+       // Check for nameless players
+       if (isInvisibleString(this.netname)) {
+               this.netname = strzone(sprintf("Player#%d", this.playerid));
+               // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
+       }
+       if (this.netname != this.netname_previous) {
+               if (autocvar_sv_eventlog) {
+                       GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname));
+         }
+               if (this.netname_previous) strunzone(this.netname_previous);
+               this.netname_previous = strzone(this.netname);
        }
  
        // version nagging
-       if(self.version_nagtime)
-               if(self.cvar_g_xonoticversion)
-                       if(time > self.version_nagtime)
-                       {
-                               // don't notify git users
-                               if(strstr(self.cvar_g_xonoticversion, "git", 0) < 0 && strstr(self.cvar_g_xonoticversion, "autobuild", 0) < 0)
-                               {
-                                       if(strstr(autocvar_g_xonoticversion, "git", 0) >= 0 || strstr(autocvar_g_xonoticversion, "autobuild", 0) >= 0)
-                                       {
-                                               // notify release users if connecting to git
-                                               dprint("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, " (beta)^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
-                                               Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, self.cvar_g_xonoticversion);
-                                       }
-                                       else
-                                       {
-                                               float r;
-                                               r = vercmp(self.cvar_g_xonoticversion, autocvar_g_xonoticversion);
-                                               if(r < 0)
-                                               {
-                                                       // give users new version
-                                                       dprint("^1NOTE^7 to ", self.netname, "^7 - ^3Xonotic ", autocvar_g_xonoticversion, "^7 is out, and you still have ^3Xonotic ", self.cvar_g_xonoticversion, "^1 - get the update from ^4http://www.xonotic.org/^1!\n");
-                                                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, self.cvar_g_xonoticversion);
-                                               }
-                                               else if(r > 0)
-                                               {
-                                                       // notify users about old server version
-                                                       print("^1NOTE^7 to ", self.netname, "^7 - the server is running ^3Xonotic ", autocvar_g_xonoticversion, "^7, you have ^3Xonotic ", self.cvar_g_xonoticversion, "^1\n");
-                                                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, self.cvar_g_xonoticversion);
-                                               }
-                                       }
-                               }
-                               self.version_nagtime = 0;
-                       }
+       if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) {
+         this.version_nagtime = 0;
+         if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) {
+             // git client
+         } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) {
+             // git server
+             Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
+         } else {
+             int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion);
+             if (r < 0) { // old client
+                 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
+             } else if (r > 0) { // old server
+                 Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion);
+             }
+         }
+     }
  
        // GOD MODE info
-       if(!(self.flags & FL_GODMODE)) if(self.max_armorvalue)
+       if (!(this.flags & FL_GODMODE) && this.max_armorvalue)
        {
-               Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_GODMODE_OFF, self.max_armorvalue);
-               self.max_armorvalue = 0;
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue);
+               this.max_armorvalue = 0;
        }
  
- #ifdef TETRIS
-       if (TetrisPreFrame())
-               return;
- #endif
-       if(self.frozen == 2)
+       if (STAT(FROZEN, this) == 2)
        {
-               self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
-               self.health = max(1, self.revive_progress * start_health);
-               self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+               this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1);
+               this.health = max(1, this.revive_progress * start_health);
+               this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1);
  
-               if(self.revive_progress >= 1)
-                       Unfreeze(self);
+               if (this.revive_progress >= 1)
+                       Unfreeze(this);
        }
-       else if(self.frozen == 3)
+       else if (STAT(FROZEN, this) == 3)
        {
-               self.revive_progress = bound(0, self.revive_progress - frametime * self.revive_speed, 1);
-               self.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * self.revive_progress );
+               this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1);
+               this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress );
  
-               if(self.health < 1)
+               if (this.health < 1)
                {
-                       if(self.vehicle)
-                               vehicles_exit(VHEF_RELESE);
-                       self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+                       if (this.vehicle)
+                               vehicles_exit(this.vehicle, VHEF_RELEASE);
+                       if(this.event_damage)
+                               this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
                }
-               else if ( self.revive_progress <= 0 )
-                       Unfreeze(self);
+               else if (this.revive_progress <= 0)
+                       Unfreeze(this);
        }
  
-       MUTATOR_CALLHOOK(PlayerPreThink);
+       MUTATOR_CALLHOOK(PlayerPreThink, this);
  
-       if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
+       if(autocvar_g_vehicles_enter)
+       if(time > this.last_vehiclecheck)
+       if(IS_PLAYER(this))
+       if(!gameover)
+       if(!STAT(FROZEN, this))
+       if(!this.vehicle)
+       if(!IS_DEAD(this))
        {
-               if(self.BUTTON_USE && !self.usekeypressed)
-                       PlayerUseKey();
-               self.usekeypressed = self.BUTTON_USE;
-       }
+               entity veh;
+               for(veh = world; (veh = findflags(veh, vehicle_flags, VHF_ISVEHICLE)); )
+               if(vdist(veh.origin - this.origin, <, autocvar_g_vehicles_enter_radius))
+               if(!IS_DEAD(veh))
+               if(veh.takedamage != DAMAGE_NO)
+               if((veh.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(veh.owner, this))
+                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER);
+               else if(!veh.owner)
+               if(!veh.team || SAME_TEAM(this, veh))
+                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER);
+               else if(autocvar_g_vehicles_steal)
+                       Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL);
  
-       if(IS_REAL_CLIENT(self))
-               PrintWelcomeMessage();
+               this.last_vehiclecheck = time + 1;
+       }
  
-       if(IS_PLAYER(self))
+       if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
        {
+               if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed)
+                       PlayerUseKey(this);
+               this.usekeypressed = PHYS_INPUT_BUTTON_USE(this);
+       }
  
-               CheckRules_Player();
+       if (IS_REAL_CLIENT(this))
+               PrintWelcomeMessage(this);
  
-               if (intermission_running)
-               {
-                       IntermissionThink ();   // otherwise a button could be missed between
-                       return;                                 // the think tics
-               }
+       if (IS_PLAYER(this)) {
+               CheckRules_Player(this);
  
-               //don't allow the player to turn around while game is paused!
-               if(timeout_status == TIMEOUT_ACTIVE) {
-                       // FIXME turn this into CSQC stuff
-                       self.v_angle = self.lastV_angle;
-                       self.angles = self.lastV_angle;
-                       self.fixangle = true;
+               if (intermission_running) {
+                       IntermissionThink(this);
+                       return;
                }
  
-               if(frametime)
-               {
-                       if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge))
-                       {
-                               self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
-                               self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
-                               self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit));
-                               if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit))
-                               {
-                                       self.weaponentity_glowmod_x = self.weaponentity_glowmod.x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
-                                       self.weaponentity_glowmod_y = self.weaponentity_glowmod.y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
-                                       self.weaponentity_glowmod_z = self.weaponentity_glowmod.z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit));
-                               }
-                       }
-                       else
-                               self.weaponentity_glowmod = colormapPaletteColor(self.clientcolors & 0x0F, true) * 2;
-                       player_powerups();
+               if (timeout_status == TIMEOUT_ACTIVE) {
+             // don't allow the player to turn around while game is paused
+                       // FIXME turn this into CSQC stuff
+                       this.v_angle = this.lastV_angle;
+                       this.angles = this.lastV_angle;
+                       this.fixangle = true;
                }
  
-               if (self.deadflag != DEAD_NO)
-               {
-                       if(self.personal && g_race_qualifying)
-                       {
-                               if(time > self.respawn_time)
-                               {
-                                       self.respawn_time = time + 1; // only retry once a second
-                                       self.stat_respawn_time = self.respawn_time;
-                                       respawn();
-                                       self.impulse = 141;
-                               }
-                       }
-                       else
-                       {
-                               float button_pressed;
-                               if(frametime)
-                                       player_anim();
-                               button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE);
+               if (frametime) player_powerups(this);
  
-                               if (self.deadflag == DEAD_DYING)
-                               {
-                                       if((self.respawn_flags & RESPAWN_FORCE) && !autocvar_g_respawn_delay_max)
-                                               self.deadflag = DEAD_RESPAWNING;
-                                       else if(!button_pressed)
-                                               self.deadflag = DEAD_DEAD;
-                               }
-                               else if (self.deadflag == DEAD_DEAD)
-                               {
-                                       if(button_pressed)
-                                               self.deadflag = DEAD_RESPAWNABLE;
-                                       else if(time >= self.respawn_time_max && (self.respawn_flags & RESPAWN_FORCE))
-                                               self.deadflag = DEAD_RESPAWNING;
-                               }
-                               else if (self.deadflag == DEAD_RESPAWNABLE)
-                               {
-                                       if(!button_pressed)
-                                               self.deadflag = DEAD_RESPAWNING;
+               if (IS_DEAD(this)) {
+                       if (this.personal && g_race_qualifying) {
+                               if (time > this.respawn_time) {
+                                       STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second
+                                       respawn(this);
+                                       this.impulse = CHIMPULSE_SPEEDRUN.impulse;
                                }
-                               else if (self.deadflag == DEAD_RESPAWNING)
-                               {
-                                       if(time > self.respawn_time)
-                                       {
-                                               self.respawn_time = time + 1; // only retry once a second
-                                               self.respawn_time_max = self.respawn_time;
-                                               respawn();
+                       } else {
+                               if (frametime) player_anim(this);
+                               bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this));
+                               if (this.deadflag == DEAD_DYING) {
+                                       if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) {
+                                               this.deadflag = DEAD_RESPAWNING;
+                                       } else if (!button_pressed) {
+                                               this.deadflag = DEAD_DEAD;
+                     }
+                               } else if (this.deadflag == DEAD_DEAD) {
+                                       if (button_pressed) {
+                                               this.deadflag = DEAD_RESPAWNABLE;
+                                       } else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) {
+                                               this.deadflag = DEAD_RESPAWNING;
+                     }
+                               } else if (this.deadflag == DEAD_RESPAWNABLE) {
+                                       if (!button_pressed) {
+                                               this.deadflag = DEAD_RESPAWNING;
+                     }
+                               } else if (this.deadflag == DEAD_RESPAWNING) {
+                                       if (time > this.respawn_time) {
+                                               this.respawn_time = time + 1; // only retry once a second
+                                               this.respawn_time_max = this.respawn_time;
+                                               respawn(this);
                                        }
                                }
  
-                               ShowRespawnCountdown();
+                               ShowRespawnCountdown(this);
  
-                               if(self.respawn_flags & RESPAWN_SILENT)
-                                       self.stat_respawn_time = 0;
-                               else if((self.respawn_flags & RESPAWN_FORCE) && autocvar_g_respawn_delay_max)
-                                       self.stat_respawn_time = self.respawn_time_max;
+                               if (this.respawn_flags & RESPAWN_SILENT)
+                                       STAT(RESPAWN_TIME, this) = 0;
+                               else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max)
+                               {
+                                       if (time < this.respawn_time)
+                                               STAT(RESPAWN_TIME, this) = this.respawn_time;
+                                       else if (this.deadflag != DEAD_RESPAWNING)
+                                               STAT(RESPAWN_TIME, this) = -this.respawn_time_max;
+                               }
                                else
-                                       self.stat_respawn_time = self.respawn_time;
+                                       STAT(RESPAWN_TIME, this) = this.respawn_time;
                        }
  
                        // if respawning, invert stat_respawn_time to indicate this, the client translates it
-                       if(self.deadflag == DEAD_RESPAWNING && self.stat_respawn_time > 0)
-                               self.stat_respawn_time *= -1;
+                       if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0)
+                               STAT(RESPAWN_TIME, this) *= -1;
  
                        return;
                }
  
-               self.prevorigin = self.origin;
-               float do_crouch = self.BUTTON_CROUCH;
-               if(self.hook.state)
-                       do_crouch = 0;
-               if(self.vehicle)
-                       do_crouch = 0;
-               if(self.frozen)
-                       do_crouch = 0;
-               // WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY
-               // It cannot be predicted by the engine!
-               if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
-                       do_crouch = 0;
-               if (do_crouch)
-               {
-                       if (!self.crouch)
-                       {
-                               self.crouch = true;
-                               self.view_ofs = PL_CROUCH_VIEW_OFS;
-                               setsize (self, PL_CROUCH_MIN, PL_CROUCH_MAX);
-                               // setanim(self, self.anim_duck, false, true, true); // this anim is BROKEN anyway
-                       }
-               }
-               else
-               {
-                       if (self.crouch)
-                       {
-                               tracebox(self.origin, PL_MIN, PL_MAX, self.origin, false, self);
-                               if (!trace_startsolid)
-                               {
-                                       self.crouch = false;
-                                       self.view_ofs = PL_VIEW_OFS;
-                                       setsize (self, PL_MIN, PL_MAX);
-                               }
+               this.prevorigin = this.origin;
+               bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this);
+         .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+               if (this.hook.state) {
+                       do_crouch = false;
+               } else if (this.vehicle) {
+                       do_crouch = false;
+               } else if (STAT(FROZEN, this)) {
+                       do_crouch = false;
+         } else if ((PS(this).m_weapon == WEP_SHOTGUN || PS(this).m_weapon == WEP_SHOCKWAVE) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) {
+                   // WEAPONTODO: predict
+                       do_crouch = false;
+         }
+               if (do_crouch) {
+                       if (!this.crouch) {
+                               this.crouch = true;
+                               this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this);
+                               setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this));
+                               // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway
                        }
+               } else if (this.crouch) {
+             tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this);
+             if (!trace_startsolid) {
+                 this.crouch = false;
+                 this.view_ofs = STAT(PL_VIEW_OFS, this);
+                 setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
+             }
                }
  
-               FixPlayermodel();
-               GrapplingHookFrame();
+               FixPlayermodel(this);
  
                // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers
                //if(frametime)
                {
-                       self.items &= ~self.items_added;
+                       this.items &= ~this.items_added;
  
-                       W_WeaponFrame();
+                       W_WeaponFrame(this);
  
-                       self.items_added = 0;
-                       if(self.items & IT_JETPACK)
-                               if(self.items & IT_FUEL_REGEN || self.ammo_fuel >= 0.01)
-                                       self.items_added |= IT_FUEL;
+                       this.items_added = 0;
+                       if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01))
+                 this.items_added |= IT_FUEL;
  
-                       self.items |= self.items_added;
+                       this.items |= this.items_added;
                }
  
-               player_regen();
+               player_regen(this);
  
                // WEAPONTODO: Add a weapon request for this
                // rot vortex charge to the charge limit
-               if(WEP_CVAR(vortex, charge_rot_rate) && self.vortex_charge > WEP_CVAR(vortex, charge_limit) && self.vortex_charge_rottime < time)
-                       self.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), self.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
+               if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time)
+                       this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
  
-               if(frametime)
-                       player_anim();
+               if (frametime) player_anim(this);
  
                // secret status
-               secrets_setstatus();
+               secrets_setstatus(this);
  
                // monsters status
-               monsters_setstatus();
+               monsters_setstatus(this);
  
-               self.dmg_team = max(0, self.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
-               //self.angles_y=self.v_angle_y + 90;   // temp
-       } else if(gameover) {
-               if (intermission_running)
-                       IntermissionThink ();   // otherwise a button could be missed between
+               this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
+       }
+       else if (gameover) {
+               if (intermission_running) IntermissionThink(this);
                return;
-       } else if(IS_OBSERVER(self)) {
-               ObserverThink();
-       } else if(IS_SPEC(self)) {
-               SpectatorThink();
+       }
+       else if (IS_OBSERVER(this)) {
+               ObserverThink(this);
+       }
+       else if (IS_SPEC(this)) {
+               SpectatorThink(this);
        }
  
        // WEAPONTODO: Add weapon request for this
-       if(!zoomstate_set)
-               SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_VORTEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)); // WEAPONTODO
-       float oldspectatee_status;
-       oldspectatee_status = self.spectatee_status;
-       if(IS_SPEC(self))
-               self.spectatee_status = num_for_edict(self.enemy);
-       else if(IS_OBSERVER(self))
-               self.spectatee_status = num_for_edict(self);
-       else
-               self.spectatee_status = 0;
-       if(self.spectatee_status != oldspectatee_status)
-       {
-               ClientData_Touch(self);
+       if (!zoomstate_set) {
+               SetZoomState(this,
+                       PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this)
+                       || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX)
+                       || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)
+               );
+     }
+       int oldspectatee_status = this.spectatee_status;
+       if (IS_SPEC(this)) {
+               this.spectatee_status = etof(this.enemy);
+       } else if (IS_OBSERVER(this)) {
+               this.spectatee_status = etof(this);
+       } else {
+               this.spectatee_status = 0;
+     }
+       if (this.spectatee_status != oldspectatee_status) {
+               ClientData_Touch(this);
+               if (g_race || g_cts) race_InitSpectator();
        }
  
-       if(self.teamkill_soundtime)
-       if(time > self.teamkill_soundtime)
+       if (this.teamkill_soundtime && time > this.teamkill_soundtime)
        {
-               self.teamkill_soundtime = 0;
-               entity oldpusher, oldself;
-               oldself = self; self = self.teamkill_soundsource;
-               oldpusher = self.pusher; self.pusher = oldself;
+               this.teamkill_soundtime = 0;
  
-               PlayerSound(playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY);
-               self.pusher = oldpusher;
-               self = oldself;
+               entity e = this.teamkill_soundsource;
+               entity oldpusher = e.pusher;
+               e.pusher = this;
+               PlayerSound(e, playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY);
+               e.pusher = oldpusher;
        }
  
-       if(self.taunt_soundtime)
-       if(time > self.taunt_soundtime)
-       {
-               self.taunt_soundtime = 0;
-               PlayerSound(playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT);
+       if (this.taunt_soundtime && time > this.taunt_soundtime) {
+               this.taunt_soundtime = 0;
+               PlayerSound(this, playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT);
        }
  
-       target_voicescript_next(self);
+       target_voicescript_next(this);
  
        // WEAPONTODO: Move into weaponsystem somehow
        // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring
-       if(!self.weapon)
-               self.clip_load = self.clip_size = 0;
+       if (PS(this).m_weapon == WEP_Null)
+               this.clip_load = this.clip_size = 0;
+ }
+ void DrownPlayer(entity this)
+ {
+       if(IS_DEAD(this))
+               return;
+       if (this.waterlevel != WATERLEVEL_SUBMERGED)
+       {
+               if(this.air_finished < time)
+                       PlayerSound(this, playersound_gasp, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+               this.air_finished = time + autocvar_g_balance_contents_drowndelay;
+               this.dmg = 2;
+       }
+       else if (this.air_finished < time)
+       {       // drown!
+               if (this.pain_finished < time)
+               {
+                       Damage (this, world, world, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0');
+                       this.pain_finished = time + 0.5;
+               }
+       }
  }
  
  /*
@@@ -2646,88 -2415,61 +2458,61 @@@ Called every frame for each client afte
  =============
  */
  .float idlekick_lasttimeleft;
- void PlayerPostThink (void)
- {
-       if(sv_maxidle > 0 && frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
-       if(IS_PLAYER(self) || sv_maxidle_spectatorsareidle)
-       {
-               if (time - self.parm_idlesince < 1) // instead of (time == self.parm_idlesince) to support sv_maxidle <= 10
+ void PlayerPostThink ()
+ {ENGINE_EVENT();
+       if (sv_maxidle > 0)
+       if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
+       if (IS_REAL_CLIENT(this))
+       if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle)
+       {
+               if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
                {
-                       if(self.idlekick_lasttimeleft)
+                       if (this.idlekick_lasttimeleft)
                        {
-                               self.idlekick_lasttimeleft = 0;
-                               Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_IDLING);
+                               this.idlekick_lasttimeleft = 0;
+                               Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING);
                        }
                }
                else
                {
-                       float timeleft;
-                       timeleft = ceil(sv_maxidle - (time - self.parm_idlesince));
-                       if(timeleft == min(10, sv_maxidle - 1)) // - 1 to support sv_maxidle <= 10
-                       {
-                               if(!self.idlekick_lasttimeleft)
-                                       Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
+                       float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince));
+                       if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10
+                               if (!this.idlekick_lasttimeleft)
+                                       Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft);
                        }
-                       if(timeleft <= 0)
-                       {
-                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_KICK_IDLING, self.netname);
-                               dropclient(self);
+                       if (timeleft <= 0) {
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname);
+                               dropclient(this);
                                return;
                        }
-                       else if(timeleft <= 10)
-                       {
-                               if(timeleft != self.idlekick_lasttimeleft)
-                                       { Send_Notification(NOTIF_ONE, self, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft)); }
-                               self.idlekick_lasttimeleft = timeleft;
+                       else if (timeleft <= 10) {
+                               if (timeleft != this.idlekick_lasttimeleft) {
+                                   Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft));
+                 }
+                               this.idlekick_lasttimeleft = timeleft;
                        }
                }
        }
  
- #ifdef TETRIS
-       if(self.impulse == 100)
-               ImpulseCommands();
-       if (!TetrisPostFrame())
-       {
- #endif
-       CheatFrame();
+       CheatFrame(this);
  
        //CheckPlayerJump();
  
-       if(IS_PLAYER(self)) {
-               CheckRules_Player();
-               UpdateChatBubble();
-               if (self.impulse)
-                       ImpulseCommands();
-               if (intermission_running)
-                       return;         // intermission or finale
-               GetPressedKeys();
-       }
- #ifdef TETRIS
-       }
- #endif
-       /*
-       float i;
-       for(i = 0; i < 1000; ++i)
-       {
-               vector end;
-               end = self.origin + '0 0 1024' + 512 * randomvec();
-               tracebox(self.origin, self.mins, self.maxs, end, MOVE_NORMAL, self);
-               if(trace_fraction < 1)
-               if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT))
-               {
-                       print("I HIT SOLID: ", vtos(self.origin), " -> ", vtos(end), "\n");
-                       break;
-               }
+       if (IS_PLAYER(this)) {
+               DrownPlayer(this);
+               CheckRules_Player(this);
+               UpdateChatBubble(this);
+               if (this.impulse) ImpulseCommands(this);
+               if (intermission_running) return; // intermission or finale
+               GetPressedKeys(this);
        }
-       */
  
-       if(self.waypointsprite_attachedforcarrier)
-               WaypointSprite_UpdateHealth(self.waypointsprite_attachedforcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
+       if (this.waypointsprite_attachedforcarrier) {
+           vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id);
+               WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v);
+     }
  
-       playerdemo_write();
+       playerdemo_write(this);
  
-       CSQCMODEL_AUTOUPDATE();
+       CSQCMODEL_AUTOUPDATE(this);
  }