]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/monsters
authorMario <mario.mario@y7mail.com>
Tue, 3 Dec 2013 13:12:20 +0000 (00:12 +1100)
committerMario <mario.mario@y7mail.com>
Tue, 3 Dec 2013 13:12:20 +0000 (00:12 +1100)
55 files changed:
1  2 
defaultXonotic.cfg
gamemodes.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/projectile.qc
qcsrc/client/scoreboard.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/csqcmodel_settings.qh
qcsrc/common/mapinfo.qh
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/monster/wyvern.qc
qcsrc/common/monsters/monster/zombie.qc
qcsrc/common/monsters/monsters.qc
qcsrc/common/monsters/monsters.qh
qcsrc/common/monsters/spawn.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/monsters/sv_monsters.qh
qcsrc/common/notifications.qh
qcsrc/common/util.qh
qcsrc/menu/xonotic/mainwindow.c
qcsrc/server/autocvars.qh
qcsrc/server/bot/aim.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/cl_weaponsystem.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/getreplies.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/movelib.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_freezetag.qc
qcsrc/server/mutators/gamemode_invasion.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/mutator_dodging.qc
qcsrc/server/mutators/mutator_minstagib.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/sv_main.qc
qcsrc/server/t_items.qc
qcsrc/server/target_spawn.qc
qcsrc/server/teamplay.qc
qcsrc/server/tturrets/system/system_main.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/w_electro.qc
qcsrc/server/w_shotgun.qc

diff --combined defaultXonotic.cfg
index c2658e7448034729843c72857f8bc4b537f7ca92,dd16de9bc6ee4224b900bf576cfc86ff8e5fce20..91b9ffeef92107d54d7ac0cd6f715baac978b0d0
@@@ -400,7 -400,6 +400,6 @@@ pausable 
  set g_spawnshieldtime 1 "number of seconds you are invincible after you spawned, this shield is lost after you fire"
  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_antilag_bullets 1 "Bullets AntiLag (0 = no AntiLag, 1 = server side hit scan in the past) - DO NOT TOUCH (severely changes weapon balance)"
  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"
@@@ -455,10 -454,6 +454,10 @@@ seta menu_sandbox_edit_material "
  
  bind f7 menu_showsandboxtools
  
 +seta menu_monsters_edit_spawn ""
 +seta menu_monsters_edit_skin 0
 +seta menu_monsters_edit_movetarget 1
 +
  set g_playerclip_collisions 1 "0 = disable collision testing against playerclips, might be useful on some defrag maps"
  set g_botclip_collisions 1 "0 = disable collision testing against botclips, might be useful on some defrag maps"
  
@@@ -469,6 -464,7 +468,7 @@@ set g_spawn_furthest 1 "this amount of 
  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 2 "number of seconds you have to wait before you can respawn again"
+ 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_waves 0 "respawn in waves (every n seconds), intended to decrease overwhelming base attacks"
  
  // overtime
@@@ -982,6 -978,28 +982,28 @@@ seta menu_slist_showfull 1 "show server
  seta menu_slist_showempty 1 "show servers even if they are no empty and have no opponents to play against"
  seta menu_slist_modfilter "" // set to either: !modname or modname. modname of = means "same as we are running now".
  
+ // other serverlist cvars
+ seta menu_slist_categories 1
+ seta menu_slist_categories_onlyifmultiple 1
+ seta menu_slist_purethreshold 0
+ seta menu_slist_modimpurity 0
+ seta menu_slist_recommendations 3
+ seta menu_slist_recommendations_maxping 150
+ seta menu_slist_recommendations_minfreeslots 1
+ seta menu_slist_recommendations_minhumans 0
+ seta menu_slist_recommendations_purethreshold -1
+ // serverlist category override cvars
+ seta menu_slist_categories_CAT_FAVORITED_override ""
+ seta menu_slist_categories_CAT_RECOMMENDED_override ""
+ seta menu_slist_categories_CAT_NORMAL_override ""
+ seta menu_slist_categories_CAT_SERVERS_override "CAT_NORMAL"
+ seta menu_slist_categories_CAT_XPM_override "CAT_NORMAL"
+ seta menu_slist_categories_CAT_MODIFIED_override ""
+ seta menu_slist_categories_CAT_OVERKILL_override ""
+ seta menu_slist_categories_CAT_MINSTAGIB_override ""
+ seta menu_slist_categories_CAT_DEFRAG_override ""
  seta menu_weaponarena ""
  
  seta menu_maxplayers 16 "maxplayers value when the menu starts a game"
@@@ -1544,7 -1562,6 +1566,7 @@@ exec crosshairs.cf
  exec gamemodes.cfg
  exec mutators.cfg
  exec notifications.cfg
 +exec monsters.cfg
  
  // load console command aliases and settings
  exec commands.cfg
diff --combined gamemodes.cfg
index e333cf0b61ff734f21b25f1412c4480204ef0d59,feed7488b5c2681d4e78565c600d6cf69a2350e0..d523c88eeaf81a6940efa6fb5e85293d5d21128c
@@@ -27,7 -27,6 +27,6 @@@ alias cl_hook_gamestart_td
  alias cl_hook_gamestart_dom
  alias cl_hook_gamestart_ctf
  alias cl_hook_gamestart_lms
- alias cl_hook_gamestart_arena
  alias cl_hook_gamestart_ca
  alias cl_hook_gamestart_kh
  alias cl_hook_gamestart_ons
@@@ -37,7 -36,6 +36,7 @@@ alias cl_hook_gamestart_n
  alias cl_hook_gamestart_cts
  alias cl_hook_gamestart_ka
  alias cl_hook_gamestart_ft
 +alias cl_hook_gamestart_invasion
  alias cl_hook_gameend
  alias cl_hook_activeweapon
  
@@@ -49,7 -47,6 +48,6 @@@ alias sv_hook_gamestart_td
  alias sv_hook_gamestart_dom
  alias sv_hook_gamestart_ctf
  alias sv_hook_gamestart_lms
- alias sv_hook_gamestart_arena
  alias sv_hook_gamestart_ca
  alias sv_hook_gamestart_kh
  alias sv_hook_gamestart_ons
@@@ -59,7 -56,6 +57,7 @@@ alias sv_hook_gamestart_n
  alias sv_hook_gamestart_cts
  alias sv_hook_gamestart_ka
  alias sv_hook_gamestart_ft
 +alias sv_hook_gamestart_invasion
  alias sv_hook_gamerestart
  alias sv_hook_gameend
  
@@@ -75,8 -71,6 +73,6 @@@ seta fraglimit_override -1    "Frag limit 
  seta leadlimit_override -1    "Lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta capturelimit_override -1 "Capture limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta captureleadlimit_override -1     "Capture llead imit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
- seta g_arena_point_limit -1   "Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
- seta g_arena_point_leadlimit -1       "Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_domination_point_limit -1      "Domination point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_domination_point_leadlimit -1  "Domination point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_keyhunt_point_limit -1 "Keyhunt point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
@@@ -84,7 -78,6 +80,7 @@@ seta g_keyhunt_point_leadlimit -1     "Keyh
  seta g_race_laps_limit -1     "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
 +seta g_invasion_round_limit -1 "Invasion round limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
  
  
  // =================================
@@@ -113,9 -106,6 +109,6 @@@ set g_ka_weapon_stay 
  set g_kh_respawn_delay 0
  set g_kh_respawn_waves 0
  set g_kh_weapon_stay 0
- set g_arena_respawn_delay 0
- set g_arena_respawn_waves 0
- set g_arena_weapon_stay 0
  set g_ca_respawn_delay 0
  set g_ca_respawn_waves 0
  set g_ca_weapon_stay 0
@@@ -137,21 -127,8 +130,11 @@@ set g_cts_weapon_stay 
  set g_ft_respawn_waves 0
  set g_ft_respawn_delay 0
  set g_ft_weapon_stay 0
 +set g_invasion_respawn_waves 0
 +set g_invasion_respawn_delay 0
 +set g_invasion_weapon_stay 0
  
  
- // =======
- //  arena
- // =======
- set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
- set g_arena_maxspawned 2      "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
- set g_arena_roundbased 1      "if disabled, the next player will spawn as soon as someone dies"
- set g_arena_round_timelimit 180
- set g_arena_warmup 5  "time, newly spawned players have to prepare themselves in round based matches"
  // =========
  //  assault
  // =========
@@@ -172,7 -149,6 +155,6 @@@ seta g_ca_teams_override 
  set g_ca_teams 0
  
  
  // ==================
  //  capture the flag
  // ==================
@@@ -224,7 -200,6 +206,7 @@@ set g_ctf_pass_timelimit 2 "how long a 
  set g_ctf_pass_velocity 750 "how fast or far a player can pass the flag"
  set g_ctf_allow_vehicle_touch 0 "allow flags to be picked up/captured/returned without even leaving the vehicle"
  set g_ctf_allow_vehicle_carry 1 "allow players to hold flags inside a vehicle"
 +set g_ctf_allow_monster_touch 0 "allow flags to be returned by monsters"
  
  set g_ctf_shield_max_ratio 0  "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)"
  set g_ctf_shield_min_negscore 20      "shield the player from the flag if he's got this negative amount of points or less"
@@@ -425,12 -400,3 +407,12 @@@ set g_race 0 "Race: be faster than you
  set g_race_qualifying_timelimit 0
  set g_race_qualifying_timelimit_override -1
  set g_race_teams 0    "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
 +
 +// ==========
 +//  invasion
 +// ==========
 +set g_invasion 0 "Invasion: survive against waves of monsters"
 +set g_invasion_round_timelimit 120 "maximum time to kill all monsters"
 +set g_invasion_warmup 10 "time between waves to prepare for battle"
 +set g_invasion_monster_count 10 "number of monsters on first wave (increments)"
 +set g_invasion_zombies_only 0 "only spawn zombies"
diff --combined qcsrc/client/Main.qc
index c8d0e8cd173b8db322bc5c89cf0a138c90cf1aa7,a06eb5e6584e3ac96f48c489b762ee748ce9a127..3937a022b55d2cf1b4791649e5592ff9dc4ace22
@@@ -81,7 -81,7 +81,7 @@@ void CSQC_Init(void
        //registercommand("hud_configure");
        //registercommand("hud_save");
        //registercommand("menu_action");
-       
        ConsoleCommand_macro_init();
  
        registercvar("hud_usecsqc", "1");
  
        // 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);
        turrets_precache();
        Tuba_Precache();
        CSQCPlayer_Precache();
-       
        if(autocvar_cl_reticle)
        {
                if(autocvar_cl_reticle_item_normal) { precache_pic("gfx/reticle_normal"); }
                if(autocvar_cl_reticle_item_nex) { precache_pic("gfx/reticle_nex"); }
        }
-       
        get_mi_min_max_texcoords(1); // try the CLEVER way first
        minimapname = strcat("gfx/", mi_shortname, "_radar.tga");
        shortmapname = mi_shortname;
@@@ -652,7 -651,7 +652,7 @@@ void Ent_ReadSpawnPoint(float is_new) /
        spn_origin_x = ReadShort();
        spn_origin_y = ReadShort();
        spn_origin_z = ReadShort();
-       
        if(is_new)
        {
                self.origin = spn_origin;
                                }
                        }
                        else { self.cnt = particleeffectnum("spawn_point_neutral"); }
-                       
                        self.draw = Spawn_Draw;
                }
        }
@@@ -697,7 -696,7 +697,7 @@@ void Ent_ReadSpawnEvent(float is_new
        // this way the server can disable the sending of
        // spawn origin or such to clients if wanted.
        float entnum = ReadByte();
-       
        if(entnum)
        {
                self.origin_x = ReadShort();
                        }
                }
        }
-       
        // local spawn actions
        if(is_new && (!entnum || (entnum == player_localentnum)))
        {
                        button_zoom = FALSE;
                }
        }
-       
        //print(sprintf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum));
  }
  
@@@ -823,9 -822,9 +823,9 @@@ void CSQC_Ent_Update(float bIsNewEntity
                case ENT_CLIENT_GAUNTLET: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_GAUNTLET); 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_TURRET: ent_turret(); break;
                case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break;
-               case ENT_CLIENT_ITEM: ItemRead(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;
diff --combined qcsrc/client/View.qc
index c02efbef8f5dd2888d12d72d86adb2f1adb516c6,8c9d59dd91f9a77e421d746a0b8193a8c449fb0f..9acee0173954f67ea5f288042ce10d6ca2632786
@@@ -127,8 -127,8 +127,8 @@@ vector GetCurrentFov(float fov
        else if(autocvar_cl_spawnzoom && zoomin_effect)
        {
                float spawnzoomfactor = bound(1, autocvar_cl_spawnzoom_factor, 16);
-               
-               current_viewzoom += (autocvar_cl_spawnzoom_speed * (spawnzoomfactor - current_viewzoom) * drawframetime); 
+               current_viewzoom += (autocvar_cl_spawnzoom_speed * (spawnzoomfactor - current_viewzoom) * drawframetime);
                current_viewzoom = bound(1 / spawnzoomfactor, current_viewzoom, 1);
                if(current_viewzoom == 1) { zoomin_effect = 0; }
        }
                setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity));
        else
                setsensitivityscale(1);
-               
        makevectors(view_angles);
  
        if(autocvar_cl_velocityzoom && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too
                                case 1: default: curspeed = vlen(v); break;
                        }
                }
-               
                velocityzoom = bound(0, drawframetime / max(0.000000001, autocvar_cl_velocityzoom_time), 1); // speed at which the zoom adapts to player velocity
                avgspeed = avgspeed * (1 - velocityzoom) + (curspeed / autocvar_cl_velocityzoom_speed) * velocityzoom;
                velocityzoom = exp(float2range11(avgspeed * -autocvar_cl_velocityzoom / 1) * 1);
-               
                //print(ftos(avgspeed), " avgspeed, ", ftos(curspeed), " curspeed, ", ftos(velocityzoom), " return\n"); // for debugging
        }
        else
@@@ -797,7 -797,7 +797,7 @@@ void CSQC_UpdateView(float w, float h
                R_PolygonVertex(autocvar_vid_conheight * '0 1 0', tc_01, rgb, a);
                R_EndPolygon();
        }
-         
        // Draw the aiming reticle for weapons that use it
        // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use
        // It must be a persisted float for fading out to work properly (you let go of the zoom button for
                reticle_type = 1; // normal zoom
        else if((activeweapon == WEP_NEX) && button_attack2)
                reticle_type = 2; // nex zoom
-     
        if(reticle_type && autocvar_cl_reticle)
        {
                if(autocvar_cl_reticle_stretch)
                        }
                }
        }
 -
 +      
        float e1 = (autocvar_hud_postprocessing_maxbluralpha != 0);
        float e2 = (autocvar_hud_powerup != 0);
        if(autocvar_hud_postprocessing && (e1 || e2)) // TODO: Remove this code and re-do the postprocess handling in the engine, where it properly belongs.
                        old_bluralpha = 0;
                }
  
-               // edge detection postprocess handling done second (used by hud_powerup) 
+               // edge detection postprocess handling done second (used by hud_powerup)
                float sharpen_intensity = 0, strength_finished = getstatf(STAT_STRENGTH_FINISHED), invincible_finished = getstatf(STAT_INVINCIBLE_FINISHED);
                if (strength_finished - time > 0) { sharpen_intensity += (strength_finished - time); }
                if (invincible_finished - time > 0) { sharpen_intensity += (invincible_finished - time); }
-               
                sharpen_intensity = bound(0, ((getstati(STAT_HEALTH) > 0) ? sharpen_intensity : 0), 5); // Check to see if player is alive (if not, set 0) - also bound to fade out starting at 5 seconds.
-               
                if(autocvar_hud_powerup && sharpen_intensity > 0)
                {
                        if(sharpen_intensity != old_sharpen_intensity) // reduce cvar_set spam as much as possible
        {
                if(time - hit_time < MAX_TIME_DIFF) // don't play the sound if it's too old.
                        sound(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTEN_NONE);
-                       
                nextsound_hit_time = time + autocvar_cl_hitsound_antispam_time;
        }
        typehit_time = getstatf(STAT_TYPEHIT_TIME);
-       if(typehit_time > nextsound_typehit_time) 
+       if(typehit_time > nextsound_typehit_time)
        {
                if(time - typehit_time < MAX_TIME_DIFF) // don't play the sound if it's too old.
                        sound(world, CH_INFO, "misc/typehit.wav", VOL_BASE, ATTEN_NONE);
-                       
                nextsound_typehit_time = time + autocvar_cl_hitsound_antispam_time;
        }
  
        //else
        {
 -              if(gametype == MAPINFO_TYPE_FREEZETAG)
 +              if(getstati(STAT_FROZEN))
 +                      drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 +              if(getstatf(STAT_REVIVE_PROGRESS))
                {
 -                      if(getstati(STAT_FROZEN))
 -                              drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 -                      if(getstatf(STAT_REVIVE_PROGRESS))
 -                      {
 -                              DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 -                              drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
 -                      }
 +                      DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
 +                      drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
                }
  
                if(autocvar_r_letterbox == 0)
                                CSQC_common_hud();
  
                // crosshair goes VERY LAST
-               if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL) 
+               if(!scoreboard_active && !camera_active && intermission != 2 && spectatee_status != -1 && hud == HUD_NORMAL)
                {
                        if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering
                                return;
-                               
                        string wcross_style;
                        float wcross_alpha, wcross_resolution;
                        wcross_style = autocvar_crosshair;
                        if(autocvar_crosshair_pickup)
                        {
                                float stat_pickup_time = getstatf(STAT_LAST_PICKUP);
-                               
                                if(pickup_crosshair_time < stat_pickup_time)
                                {
                                        if(time - stat_pickup_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old
                                                pickup_crosshair_size = 1;
-                                               
                                        pickup_crosshair_time = stat_pickup_time;
                                }
  
                        if(autocvar_crosshair_hitindication)
                        {
                                vector hitindication_color = ((autocvar_crosshair_color_special == 1) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color));
-                               
                                if(hitindication_crosshair_time < hit_time)
                                {
                                        if(time - hit_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old
                                                hitindication_crosshair_size = 1;
-                                               
                                        hitindication_crosshair_time = hit_time;
                                }
  
                                        // handle the values
                                        if (autocvar_crosshair_ring && activeweapon == WEP_NEX && nex_charge && autocvar_crosshair_ring_nex) // ring around crosshair representing velocity-dependent damage for the nex
                                        {
-                                               if (nex_chargepool || use_nex_chargepool) { 
-                                                       use_nex_chargepool = 1; 
+                                               if (nex_chargepool || use_nex_chargepool) {
+                                                       use_nex_chargepool = 1;
                                                        ring_inner_value = nex_chargepool;
-                                               } else { 
+                                               } else {
                                                        nex_charge_movingavg = (1 - autocvar_crosshair_ring_nex_currentcharge_movingavg_rate) * nex_charge_movingavg + autocvar_crosshair_ring_nex_currentcharge_movingavg_rate * nex_charge;
-                                                       ring_inner_value = bound(0, autocvar_crosshair_ring_nex_currentcharge_scale * (nex_charge - nex_charge_movingavg), 1); 
+                                                       ring_inner_value = bound(0, autocvar_crosshair_ring_nex_currentcharge_scale * (nex_charge - nex_charge_movingavg), 1);
                                                }
  
                                                ring_inner_alpha = autocvar_crosshair_ring_nex_inner_alpha;
                                                ring_rgb = wcross_color;
                                                ring_image = "gfx/crosshair_ring_nexgun.tga";
                                        }
-                                       else if (autocvar_crosshair_ring && activeweapon == WEP_MINE_LAYER && minelayer_maxmines && autocvar_crosshair_ring_minelayer) 
+                                       else if (autocvar_crosshair_ring && activeweapon == WEP_MINE_LAYER && minelayer_maxmines && autocvar_crosshair_ring_minelayer)
                                        {
                                                ring_value = bound(0, getstati(STAT_LAYED_MINES) / minelayer_maxmines, 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to.
                                                ring_alpha = autocvar_crosshair_ring_minelayer_alpha;
                                                ring_image = "gfx/crosshair_ring.tga";
                                        }
  
-                                       if(autocvar_crosshair_ring_reload && weapon_clipsize) // forces there to be only an ammo ring 
+                                       if(autocvar_crosshair_ring_reload && weapon_clipsize) // forces there to be only an ammo ring
                                        {
                                                ring_value = bound(0, weapon_clipload / weapon_clipsize, 1);
                                                ring_scale = autocvar_crosshair_ring_reload_size;
                                                {
                                                        wcross_ring_prev = ((ring_image) ? TRUE : FALSE);
                                                }
-                                               
                                                if(wcross_ring_prev)
                                                {
                                                        if(f < 1)
                                {
                                        vector wcross_color_old;
                                        wcross_color_old = wcross_color;
-                                       
                                        if((autocvar_crosshair_dot_color_custom) && (autocvar_crosshair_dot_color != "0"))
                                                wcross_color = stov(autocvar_crosshair_dot_color);
-                                               
                                        CROSSHAIR_DRAW(wcross_resolution * autocvar_crosshair_dot_size, "gfx/crosshairdot.tga", f * autocvar_crosshair_dot_alpha);
                                        // FIXME why don't we use wcross_alpha here?cl_notice_run();
                                        wcross_color = wcross_color_old;
  
        if(autocvar__hud_configure)
                HUD_Panel_Mouse();
-     
      if(hud && !intermission)
-     {        
+     {
          if(hud == HUD_SPIDERBOT)
              CSQC_SPIDER_HUD();
          else if(hud == HUD_WAKIZASHI)
          else if(hud == HUD_BUMBLEBEE_GUN)
              CSQC_BUMBLE_GUN_HUD();
      }
-       
        cl_notice_run();
-       
        // let's reset the view back to normal for the end
        setproperty(VF_MIN, '0 0 0');
        setproperty(VF_SIZE, '1 0 0' * w + '0 1 0' * h);
index 658f7b5b0dc41c342b1e3428c3cd9275f33a540d,52f6e2324b5ecd578a15b1556e9ce90279fefc45..518f0310294b0a2a326e1e0c6941189e864cd749
@@@ -111,7 -111,7 +111,7 @@@ void Projectile_Draw(
                        case PROJECTILE_NADE_PINK:
                        case PROJECTILE_NADE_BURN:
                        case PROJECTILE_NADE:
-                               rot = self.avelocity; 
+                               rot = self.avelocity;
                                break;
                        case PROJECTILE_HOOKBOMB:
                                rot = '1000 0 0'; // forward
  
        switch(self.cnt)
        {
-               case PROJECTILE_BULLET_GLOWING:
-               case PROJECTILE_BULLET_GLOWING_TRACER:
-                       adddynamiclight(self.origin, 50 * a, '1 1 0');
-                       break;
+               // Possibly add dlights here.
                default:
                        break;
        }
@@@ -292,9 -289,6 +289,6 @@@ void Ent_Projectile(
                {
                        case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
                        case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
-                       case PROJECTILE_BULLET: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_bullet"); break;
-                       case PROJECTILE_BULLET_GLOWING: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle_weak"); break;
-                       case PROJECTILE_BULLET_GLOWING_TRACER: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle"); break;
                        case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
                        case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
                        case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
                        case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break;
                        case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break;
                        case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break;
 +                      
 +                      case PROJECTILE_MAGE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break;
 +                      case PROJECTILE_SHAMBLER_LIGHTNING: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
  
                        case PROJECTILE_RAPTORBOMB:    setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
                        case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3");     self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
  
                        case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
                        case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-                       
                        case PROJECTILE_NADE_RED: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red"); break;
                        case PROJECTILE_NADE_RED_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red_burn"); break;
                        case PROJECTILE_NADE_BLUE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue"); break;
                                self.scale = 1.5;
                                self.avelocity = randomvec() * 720;
                                break;
 +                      case PROJECTILE_SHAMBLER_LIGHTNING:
 +                              self.mins = '-8 -8 -8';
 +                              self.maxs = '8 8 8';
 +                              self.scale = 2.5;
 +                              self.avelocity = randomvec() * 720;
 +                              break;
                        case PROJECTILE_MINE:
                                self.mins = '-4 -4 -4';
                                self.maxs = '4 4 4';
                                break;
              case PROJECTILE_WAKIROCKET:
                  loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
-                               break;            
+                               break;
              /*
              case PROJECTILE_WAKICANNON:
                                break;
@@@ -532,7 -517,7 +526,7 @@@ void Projectile_Precache(
        precache_model("models/rocket.md3");
        precache_model("models/tagrocket.md3");
        precache_model("models/tracer.mdl");
-       
        precache_model("models/weapons/v_ok_grenade.md3");
  
        precache_sound("weapons/electro_fly.wav");
index 5a6283f70121deb401cbd5cc06dc55e8deab1f7e,b037d03d20cb35a4431315f87bc6ade863153b8b..6c204f8beb2e57b424c8d065177451a715fef719
@@@ -586,7 -586,7 +586,7 @@@ string HUD_GetField(entity pl, float fi
                        } else
                                str = sprintf("%.1f", num/denom);
                        return str;
-                       
                case SP_SUM:
                        f = pl.(scores[SP_KILLS]);
                        f -= pl.(scores[SP_DEATHS]);
@@@ -1081,29 -1081,21 +1081,29 @@@ vector HUD_DrawKeyValue(vector pos, str
        drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
        pos_x = px;
        pos_y+= hud_fontsize_y;
-       
        return pos;
  }
  
  vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
        float stat_secrets_found, stat_secrets_total;
 -      float rows;
 +      float stat_monsters_killed, stat_monsters_total;
 +      float rows = 0;
        string val;
 +      
 +      // get monster stats
 +      stat_monsters_killed = getstatf(STAT_MONSTERS_KILLED);
 +      stat_monsters_total = getstatf(STAT_MONSTERS_TOTAL);
  
        // get secrets stats
        stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
        stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
  
        // get number of rows
 -      rows = (stat_secrets_total ? 1 : 0);
 +      if(stat_secrets_total)
 +              rows += 1;
 +      if(stat_monsters_total)
 +              rows += 1;
  
        // if no rows, return
        if (!rows)
        //  draw table header
        drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
        pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
-       
-       // draw table   
+       // draw table
        vector tmp = '0 0 0';
        tmp_x = sbwidth;
        tmp_y = hud_fontsize_y * rows;
        else
                drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
        drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
-       
 +      // draw monsters
 +      if(stat_monsters_total)
 +      {
 +              val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
 +              pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
 +      }
 +
        // draw secrets
 -      val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
 -      pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
 +      if(stat_secrets_total)
 +      {
 +              val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
 +              pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
 +      }
-       
        // update position
        pos_y += 1.25 * hud_fontsize_y;
        return pos;
@@@ -1334,7 -1316,7 +1334,7 @@@ void HUD_DrawScoreboard(
                        pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
        }
  
-               
        if(teamplay)
                pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
        else
                                str = strcat(str, _(" or"));
                        if(teamplay)
                        {
-                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl), 
+                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
                                        (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
                                        (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
                                        TranslateScoresLabel(teamscores_label[ts_primary])));
                        }
                        else
                        {
-                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl), 
+                               str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
                                        (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
                                        (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
                                        TranslateScoresLabel(scores_label[ps_primary])));
                                str = strcat(str, _(" or"));
                        if(teamplay)
                        {
-                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll), 
+                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
                                        (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
                                        (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
                                        TranslateScoresLabel(teamscores_label[ts_primary])));
                        }
                        else
                        {
-                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll), 
+                               str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
                                        (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
                                        (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
                                        TranslateScoresLabel(scores_label[ps_primary])));
                        // a negative number means we are awaiting respawn, time value is still the same
                        respawn_time *= -1; // remove mark now that we checked it
                        respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
-                       
                        str = sprintf(_("^1Respawning in ^3%s^1..."),
                                (autocvar_scoreboard_respawntime_decimals ?
                                        count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
index 2ab1a5875e4246f8256dcd63297575e6506037e5,a3e856d1438197df091be171493946c75273a1c5..3a54f7239ebc17633efddb4344d98b7494dab4d7
@@@ -96,7 -96,7 +96,7 @@@ void drawhealthbar(vector org, float ro
        o = hotspot;
        ri = '1 0 0';
        up = '0 1 0';
-       
        rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
        o = rotate(o, rot) + org;
        ri = rotate(ri, rot);
@@@ -316,7 -316,7 +316,7 @@@ string spritelookuptext(string s
                case "item-shield": return _("Shield");
                case "item-fuelregen": return _("Fuel regen");
                case "item-jetpack": return _("Jet Pack");
 -              case "freezetag_frozen": return _("Frozen!");
 +              case "frozen": return _("Frozen!");
                case "tagged-target": return _("Tagged");
                case "vehicle": return _("Vehicle");
                default: return s;
@@@ -439,10 -439,10 +439,10 @@@ void Draw_WaypointSprite(
                return;
  
        ++waypointsprite_newcount;
-       
        float dist;
        dist = vlen(self.origin - view_origin);
-       
        float a;
        a = self.alpha * autocvar_hud_panel_fg_alpha;
  
        if(rgb == '0 0 0')
        {
                self.teamradar_color = '1 0 1';
-               print(sprintf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage)); 
+               print(sprintf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage));
        }
  
        if(time - floor(time) > 0.5)
        float ang;
  
        o = project_3d_to_2d(self.origin);
-       if(o_z < 0 
-       || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) 
-       || o_y < (vid_conheight * waypointsprite_edgeoffset_top) 
-       || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))  
+       if(o_z < 0
+       || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
+       || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
+       || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
        || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
        {
                // scale it to be just in view
        o_z = 0;
  
        float edgedistance_min, crosshairdistance;
-               edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), 
+               edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
        (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
-       (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, 
+       (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
        (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
  
        float vidscale;
        }
  
        o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t);
-       
        string txt;
        if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
                txt = _("Spam");
index e3c461fa12fa753f10c35c9bd4b2bc82446c220d,55d16953f6a04d4a5e4f9bc8c289b5981654704e..eb4504c3109efaa32d0682d74ef88b0609c32d3b
@@@ -183,9 -183,6 +183,9 @@@ const float STAT_ROUNDSTARTTIME = 73
  const float STAT_WEAPONS2 = 74;
  const float STAT_WEAPONS3 = 75;
  
 +const float STAT_MONSTERS_TOTAL = 76;
 +const float STAT_MONSTERS_KILLED = 77;
 +
  // mod stats (1xx)
  const float STAT_REDALIVE = 100;
  const float STAT_BLUEALIVE = 101;
@@@ -330,7 -327,6 +330,6 @@@ const float ATTEN_MAX = 3.984375
  const float PROJECTILE_ELECTRO = 1;
  const float PROJECTILE_ROCKET = 2;
  const float PROJECTILE_TAG = 3;
- const float PROJECTILE_BULLET = 4;
  const float PROJECTILE_CRYLINK = 5;
  const float PROJECTILE_ELECTRO_BEAM = 6;
  const float PROJECTILE_GRENADE = 7;
@@@ -345,11 -341,9 +344,9 @@@ const float PROJECTILE_PORTO_BLUE = 15
  const float PROJECTILE_HOOKBOMB = 16;
  const float PROJECTILE_HAGAR = 17;
  const float PROJECTILE_HAGAR_BOUNCING = 18;
- const float PROJECTILE_BULLET_GLOWING = 19;
  const float PROJECTILE_CRYLINK_BOUNCING = 20;
  const float PROJECTILE_FIREBALL = 21;
  const float PROJECTILE_FIREMINE = 22;
- const float PROJECTILE_BULLET_GLOWING_TRACER = 23;
  
  const float PROJECTILE_RAPTORCANNON = 24;
  const float PROJECTILE_RAPTORBOMB = 25;
@@@ -361,9 -355,6 +358,9 @@@ const float PROJECTILE_WAKICANNON = 29
  const float PROJECTILE_BUMBLE_GUN = 30;
  const float PROJECTILE_BUMBLE_BEAM = 31;
  
 +float PROJECTILE_MAGE_SPIKE = 32;
 +float PROJECTILE_SHAMBLER_LIGHTNING = 33;
 +
  const float PROJECTILE_NADE_RED = 50;
  const float PROJECTILE_NADE_RED_BURN = 51;
  const float PROJECTILE_NADE_BLUE = 52;
index 74482b925db0c5faf9b4c9650965dcf9106a6be5,9f202d2b384ab3ca421df06cae81579319edbf90..8e794cc76a39612198e1a32c09020dbc5d4cd03a
@@@ -34,6 -34,9 +34,9 @@@
                CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_x, 255, 0, 255) \
                CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_y, 255, 0, 255) \
                CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, glowmod_z, 255, 0, 255) \
+               CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_x, 255, 0, 255) \
+               CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_y, 255, 0, 255) \
+               CSQCMODEL_PROPERTY_SCALED(256, float, ReadByte, WriteByte, colormod_z, 255, 0, 255) \
        CSQCMODEL_ENDIF \
        CSQCMODEL_IF(isplayer) \
                CSQCMODEL_PROPERTY(128, float, ReadByte, WriteByte, anim_state) \
                CSQCMODEL_PROPERTY(512, float, ReadApproxPastTime, WriteApproxPastTime, anim_upper_time) \
                CSQCMODEL_PROPERTY(1024, float, ReadAngle, WriteAngle, v_angle_x) \
        CSQCMODEL_ENDIF \
-       CSQCMODEL_PROPERTY_SCALED(4096, float, ReadShort, WriteShort, scale, 256, 0, 16384)
 +      CSQCMODEL_IF(!isplayer) \
 +              CSQCMODEL_PROPERTY(2048, float, ReadByte, WriteByte, monsterid) \
 +      CSQCMODEL_ENDIF \
+       CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255)
  // TODO get rid of colormod/glowmod here, find good solution for nex charge glowmod hack; also get rid of some useless properties on non-players that only exist for CopyBody
  
  // add hook function calls here
diff --combined qcsrc/common/mapinfo.qh
index ab273374fade5e4c33762029305ba5d49fc0fc35,eb93b8de817df90c268d3d4fa51b589bb68a6f18..7746dfe3eb1d3de6912f129527e216af9dff873f
@@@ -39,9 -39,6 +39,6 @@@ REGISTER_GAMETYPE(_("Deathmatch"),dm,g_
  REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0");
  #define g_lms IS_GAMETYPE(LMS)
  
- REGISTER_GAMETYPE(_("Arena"),arena,g_arena,ARENA,"timelimit=20 pointlimit=10 leadlimit=0");
- #define g_arena IS_GAMETYPE(ARENA)
  REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0");
  #define g_race IS_GAMETYPE(RACE)
  
@@@ -78,9 -75,6 +75,9 @@@ REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_
  REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
  #define g_keepaway IS_GAMETYPE(KEEPAWAY)
  
 +REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=5");
 +#define g_invasion IS_GAMETYPE(INVASION)
 +
  const float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps
  const float MAPINFO_FEATURE_VEHICLES      = 2;
  const float MAPINFO_FEATURE_TURRETS       = 4;
index c6e1b18981f17a02eb47b2ead84d3c5a930571cc,0000000000000000000000000000000000000000..83924ab7d613e5dc426671be2b1f4d336c4a801b
mode 100644,000000..100644
--- /dev/null
@@@ -1,427 -1,0 +1,427 @@@
- /* MON_##id   */ MAGE,
- /* function   */ m_mage,
 +#ifdef REGISTER_MONSTER
 +REGISTER_MONSTER(
- /* model      */ "mage.dpm",
- /* netname    */ "mage",
- /* fullname   */ _("Mage")
++/* MON_##id     */ MAGE,
++/* function     */ m_mage,
 +/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED,
 +/* mins,maxs  */ '-36 -36 -24', '36 36 50',
- const float mage_anim_walk            = 1;
- const float mage_anim_attack  = 2;
- const float mage_anim_pain            = 3;
- const float mage_anim_death   = 4;
++/* model        */ "mage.dpm",
++/* netname      */ "mage",
++/* fullname     */ _("Mage")
 +);
 +
 +#else
 +#ifdef SVQC
 +float autocvar_g_monster_mage_health;
 +float autocvar_g_monster_mage_attack_spike_damage;
 +float autocvar_g_monster_mage_attack_spike_radius;
 +float autocvar_g_monster_mage_attack_spike_delay;
 +float autocvar_g_monster_mage_attack_spike_accel;
 +float autocvar_g_monster_mage_attack_spike_decel;
 +float autocvar_g_monster_mage_attack_spike_turnrate;
 +float autocvar_g_monster_mage_attack_spike_speed_max;
 +float autocvar_g_monster_mage_attack_spike_smart;
 +float autocvar_g_monster_mage_attack_spike_smart_trace_min;
 +float autocvar_g_monster_mage_attack_spike_smart_trace_max;
 +float autocvar_g_monster_mage_attack_spike_smart_mindist;
 +float autocvar_g_monster_mage_attack_push_damage;
 +float autocvar_g_monster_mage_attack_push_radius;
 +float autocvar_g_monster_mage_attack_push_delay;
 +float autocvar_g_monster_mage_attack_push_force;
 +float autocvar_g_monster_mage_heal_self;
 +float autocvar_g_monster_mage_heal_allies;
 +float autocvar_g_monster_mage_heal_minhealth;
 +float autocvar_g_monster_mage_heal_range;
 +float autocvar_g_monster_mage_heal_delay;
 +float autocvar_g_monster_mage_shield_time;
 +float autocvar_g_monster_mage_shield_delay;
 +float autocvar_g_monster_mage_shield_blockpercent;
 +float autocvar_g_monster_mage_speed_stop;
 +float autocvar_g_monster_mage_speed_run;
 +float autocvar_g_monster_mage_speed_walk;
 +
 +const float mage_anim_idle            = 0;
-       
++const float mage_anim_walk            = 1;
++const float mage_anim_attack  = 2;
++const float mage_anim_pain            = 3;
++const float mage_anim_death           = 4;
 +const float mage_anim_run             = 5;
 +
 +void() mage_heal;
 +void() mage_shield;
 +
 +.entity mage_spike;
 +.float shield_ltime;
 +
 +float friend_needshelp(entity e)
 +{
 +      if(e == world)
 +              return FALSE;
 +      if(e.health <= 0)
 +              return FALSE;
 +      if(DIFF_TEAM(e, self) && e != self.monster_owner)
 +              return FALSE;
 +      if(e.frozen)
 +              return FALSE;
 +      if(!IS_PLAYER(e))
 +              return (e.flags & FL_MONSTER && e.health < e.max_health);
 +      if(e.items & IT_INVINCIBLE)
 +              return FALSE;
 +
 +      switch(self.skin)
 +      {
 +              case 0: return (e.health < autocvar_g_balance_health_regenstable);
 +              case 1: return ((e.ammo_cells && e.ammo_cells < g_pickup_cells_max) || (e.ammo_rockets && e.ammo_rockets < g_pickup_rockets_max) || (e.ammo_nails && e.ammo_nails < g_pickup_nails_max) || (e.ammo_shells && e.ammo_shells < g_pickup_shells_max));
 +              case 2: return (e.armorvalue < autocvar_g_balance_armor_regenstable);
 +              case 3: return (e.health > 0);
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +void mage_spike_explode()
 +{
 +      self.event_damage = func_null;
-       
++
 +      sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
-       
++
 +      self.realowner.mage_spike = world;
-               e               = self.enemy;
-               eorg            = 0.5 * (e.absmin + e.absmax);
-               turnrate        = (autocvar_g_monster_mage_attack_spike_turnrate); // how fast to turn
-               desireddir      = normalize(eorg - self.origin);
-               olddir          = normalize(self.velocity); // get my current direction
-               dist            = vlen(eorg - self.origin);
++
 +      pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1);
 +      RadiusDamage (self, self.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), world, 0, DEATH_MONSTER_MAGE, other);
 +
 +      remove (self);
 +}
 +
 +void mage_spike_touch()
 +{
 +      PROJECTILE_TOUCH;
 +
 +      mage_spike_explode();
 +}
 +
 +// copied from W_Seeker_Think
 +void mage_spike_think()
 +{
 +      entity e;
 +      vector desireddir, olddir, newdir, eorg;
 +      float turnrate;
 +      float dist;
 +      float spd;
 +
 +      if (time > self.ltime || self.enemy.health <= 0 || self.owner.health <= 0)
 +      {
 +              self.projectiledeathtype |= HITTYPE_SPLASH;
 +              mage_spike_explode();
 +      }
 +
 +      spd = vlen(self.velocity);
 +      spd = bound(
 +              spd - (autocvar_g_monster_mage_attack_spike_decel) * frametime,
 +              (autocvar_g_monster_mage_attack_spike_speed_max),
 +              spd + (autocvar_g_monster_mage_attack_spike_accel) * frametime
 +      );
 +
 +      if (self.enemy != world)
 +              if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)
 +                      self.enemy = world;
 +
 +      if (self.enemy != world)
 +      {
-                       desireddir  = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
++              e                               = self.enemy;
++              eorg                    = 0.5 * (e.absmin + e.absmax);
++              turnrate                = (autocvar_g_monster_mage_attack_spike_turnrate); // how fast to turn
++              desireddir              = normalize(eorg - self.origin);
++              olddir                  = normalize(self.velocity); // get my current direction
++              dist                    = vlen(eorg - self.origin);
 +
 +              // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
 +              if ((autocvar_g_monster_mage_attack_spike_smart) && (dist > (autocvar_g_monster_mage_attack_spike_smart_mindist)))
 +              {
 +                      // Is it a better idea (shorter distance) to trace to the target itself?
 +                      if ( vlen(self.origin + olddir * self.wait) < dist)
 +                              traceline(self.origin, self.origin + olddir * self.wait, FALSE, self);
 +                      else
 +                              traceline(self.origin, eorg, FALSE, self);
 +
 +                      // Setup adaptive tracelength
 +                      self.wait = bound((autocvar_g_monster_mage_attack_spike_smart_trace_min), vlen(self.origin - trace_endpos), self.wait = (autocvar_g_monster_mage_attack_spike_smart_trace_max));
 +
 +                      // Calc how important it is that we turn and add this to the desierd (enemy) dir.
-               
++                      desireddir      = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
 +              }
-               
++
 +              newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
 +              self.velocity = newdir * spd; // make me fly in the new direction at my flight speed
 +      }
 +      else
 +              dist = 0;
-       setsize (missile, '0 0 0', '0 0 0');    
++
 +      ///////////////
 +
 +      //self.angles = vectoangles(self.velocity);                     // turn model in the new flight direction
 +      self.nextthink = time;// + 0.05; // csqc projectiles
 +      UpdateCSQCProjectile(self);
 +}
 +
 +void mage_attack_spike()
 +{
 +      entity missile;
 +      vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
 +
 +      makevectors(self.angles);
 +
 +      missile = spawn ();
 +      missile.owner = missile.realowner = self;
 +      missile.think = mage_spike_think;
 +      missile.ltime = time + 7;
 +      missile.nextthink = time;
 +      missile.solid = SOLID_BBOX;
 +      missile.movetype = MOVETYPE_FLYMISSILE;
 +      missile.flags = FL_PROJECTILE;
 +      setorigin(missile, self.origin + v_forward * 14 + '0 0 30' + v_right * -14);
-       
++      setsize (missile, '0 0 0', '0 0 0');
 +      missile.velocity = dir * 400;
 +      missile.avelocity = '300 300 300';
 +      missile.enemy = self.enemy;
 +      missile.touch = mage_spike_touch;
-       
++
 +      self.mage_spike = missile;
-       
++
 +      CSQCProjectile(missile, TRUE, PROJECTILE_MAGE_SPIKE, TRUE);
 +}
 +
 +void mage_heal()
 +{
 +      entity head;
 +      float washealed = FALSE;
-                       
++
 +      for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(friend_needshelp(head))
 +      {
 +              washealed = TRUE;
 +              string fx = "";
 +              if(IS_PLAYER(head))
 +              {
 +                      switch(self.skin)
 +                      {
 +                              case 0:
 +                                      if(head.health < autocvar_g_balance_health_regenstable) head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable);
 +                                      fx = "healing_fx";
 +                                      break;
 +                              case 1:
 +                                      if(head.ammo_cells) head.ammo_cells = bound(head.ammo_cells, head.ammo_cells + 1, g_pickup_cells_max);
 +                                      if(head.ammo_rockets) head.ammo_rockets = bound(head.ammo_rockets, head.ammo_rockets + 1, g_pickup_rockets_max);
 +                                      if(head.ammo_shells) head.ammo_shells = bound(head.ammo_shells, head.ammo_shells + 2, g_pickup_shells_max);
 +                                      if(head.ammo_nails) head.ammo_nails = bound(head.ammo_nails, head.ammo_nails + 5, g_pickup_nails_max);
 +                                      fx = "ammoregen_fx";
 +                                      break;
 +                              case 2:
 +                                      if(head.armorvalue < autocvar_g_balance_armor_regenstable)
 +                                      {
 +                                              head.armorvalue = bound(0, head.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable);
 +                                              fx = "armorrepair_fx";
 +                                      }
 +                                      break;
 +                              case 3:
 +                                      head.health = bound(0, head.health - ((head == self)  ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable);
 +                                      fx = "rage";
 +                                      break;
 +                      }
-       
++
 +                      pointparticles(particleeffectnum(fx), head.origin, '0 0 0', 1);
 +              }
 +              else
 +              {
 +                      pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1);
 +                      head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health);
 +                      WaypointSprite_UpdateHealth(head.sprite, head.health);
 +              }
 +      }
-       
++
 +      if(washealed)
 +      {
 +              self.frame = mage_anim_attack;
 +              self.attack_finished_single = time + (autocvar_g_monster_mage_heal_delay);
 +      }
 +}
 +
 +void mage_push()
 +{
 +      sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM);
 +      RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy);
 +      pointparticles(particleeffectnum("TE_EXPLOSION"), self.origin, '0 0 0', 1);
-       
++
 +      self.frame = mage_anim_attack;
 +      self.attack_finished_single = time + (autocvar_g_monster_mage_attack_push_delay);
 +}
 +
 +void mage_teleport()
 +{
 +      if(vlen(self.enemy.origin - self.origin) >= 500)
 +              return;
 +
 +      makevectors(self.enemy.angles);
 +      tracebox(self.enemy.origin + ((v_forward * -1) * 200), self.mins, self.maxs, self.origin, MOVE_NOMONSTERS, self);
-               
++
 +      if(trace_fraction < 1)
 +              return;
-       
++
 +      pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1);
 +      setorigin(self, self.enemy.origin + ((v_forward * -1) * 200));
-                               
++
 +      self.attack_finished_single = time + 0.2;
 +}
 +
 +void mage_shield_remove()
 +{
 +      self.effects &= ~(EF_ADDITIVE | EF_BLUE);
 +      self.armorvalue = 0;
 +      self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent;
 +}
 +
 +void mage_shield()
 +{
 +      self.effects |= (EF_ADDITIVE | EF_BLUE);
 +      self.lastshielded = time + (autocvar_g_monster_mage_shield_delay);
 +      self.m_armor_blockpercent = (autocvar_g_monster_mage_shield_blockpercent);
 +      self.armorvalue = self.health;
 +      self.shield_ltime = time + (autocvar_g_monster_mage_shield_time);
 +      self.frame = mage_anim_attack;
 +      self.attack_finished_single = time + 1;
 +}
 +
 +float mage_attack(float attack_type)
 +{
 +      switch(attack_type)
 +      {
 +              case MONSTER_ATTACK_MELEE:
 +              {
 +                      if(random() <= 0.7)
 +                      {
 +                              mage_push();
 +                              return TRUE;
 +                      }
-                       
++
 +                      return FALSE;
 +              }
 +              case MONSTER_ATTACK_RANGED:
 +              {
 +                      if(!self.mage_spike)
 +                      {
 +                              if(random() <= 0.4)
 +                              {
 +                                      mage_teleport();
 +                                      return TRUE;
 +                              }
 +                              else
 +                              {
 +                                      self.frame = mage_anim_attack;
 +                                      self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay);
 +                                      defer(0.2, mage_attack_spike);
 +                                      return TRUE;
 +                              }
 +                      }
-       
++
 +                      if(self.mage_spike)
 +                              return TRUE;
 +                      else
 +                              return FALSE;
 +              }
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +void spawnfunc_monster_mage()
 +{
 +      self.classname = "monster_mage";
-       
++
 +      self.monster_spawnfunc = spawnfunc_monster_mage;
-       
++
 +      if(Monster_CheckAppearFlags(self))
 +              return;
-                       
++
 +      if(!monster_initialize(MON_MAGE, FALSE)) { remove(self); return; }
 +}
 +
 +// compatibility with old spawns
 +void spawnfunc_monster_shalrath() { spawnfunc_monster_mage(); }
 +
 +float m_mage(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_THINK:
 +              {
 +                      entity head;
 +                      float need_help = FALSE;
-                               
++
 +                      for(head = findradius(self.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain)
 +                      if(head != self)
 +                      if(friend_needshelp(head))
 +                      {
 +                              need_help = TRUE;
 +                              break;
 +                      }
-                               
++
 +                      if(self.health < (autocvar_g_monster_mage_heal_minhealth) || need_help)
 +                      if(time >= self.attack_finished_single)
 +                      if(random() < 0.5)
 +                              mage_heal();
-                               
++
 +                      if(time >= self.shield_ltime && self.armorvalue)
 +                              mage_shield_remove();
-                       
++
 +                      if(self.enemy)
 +                      if(self.health < self.max_health)
 +                      if(time >= self.lastshielded)
 +                      if(random() < 0.5)
 +                              mage_shield();
-                       
++
 +                      monster_move((autocvar_g_monster_mage_speed_run), (autocvar_g_monster_mage_speed_walk), (autocvar_g_monster_mage_speed_stop), mage_anim_walk, mage_anim_run, mage_anim_idle);
 +                      return TRUE;
 +              }
 +              case MR_DEATH:
 +              {
 +                      self.frame = mage_anim_death;
 +                      return TRUE;
 +              }
 +              case MR_SETUP:
 +              {
 +                      if(!self.health) self.health = (autocvar_g_monster_mage_health);
-                       
++
 +                      self.monster_loot = spawnfunc_item_health_large;
 +                      self.monster_attackfunc = mage_attack;
 +                      self.frame = mage_anim_walk;
-       
++
 +                      return TRUE;
 +              }
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/mage.dpm");
 +                      precache_sound ("weapons/grenade_impact.wav");
 +                      precache_sound ("weapons/tagexp1.wav");
 +                      return TRUE;
 +              }
 +      }
-       
++
 +      return TRUE;
 +}
 +
 +#endif // SVQC
 +#ifdef CSQC
 +float m_mage(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/mage.dpm");
 +                      return TRUE;
 +              }
 +      }
++
 +      return TRUE;
 +}
 +
 +#endif // CSQC
 +#endif // REGISTER_MONSTER
index 499a9ff7300425795620d19b915c8961b5a05a27,0000000000000000000000000000000000000000..5da537c2723e46583bc7a08710a329c80d8a6348
mode 100644,000000..100644
--- /dev/null
@@@ -1,260 -1,0 +1,260 @@@
- /* MON_##id   */ SHAMBLER,
- /* function   */ m_shambler,
 +#ifdef REGISTER_MONSTER
 +REGISTER_MONSTER(
- /* model      */ "shambler.mdl",
- /* netname    */ "shambler",
- /* fullname   */ _("Shambler")
++/* MON_##id     */ SHAMBLER,
++/* function     */ m_shambler,
 +/* spawnflags */ MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED,
 +/* mins,maxs  */ '-41 -41 -31', '41 41 65',
- const float shambler_anim_stand       = 0;
- const float shambler_anim_walk                = 1;
- const float shambler_anim_run                 = 2;
- const float shambler_anim_smash       = 3;
- const float shambler_anim_swingr      = 4;
- const float shambler_anim_swingl      = 5;
- const float shambler_anim_magic       = 6;
- const float shambler_anim_pain                = 7;
- const float shambler_anim_death       = 8;
++/* model        */ "shambler.mdl",
++/* netname      */ "shambler",
++/* fullname     */ _("Shambler")
 +);
 +
 +#else
 +#ifdef SVQC
 +float autocvar_g_monster_shambler_health;
 +float autocvar_g_monster_shambler_attack_smash_damage;
 +float autocvar_g_monster_shambler_attack_claw_damage;
 +float autocvar_g_monster_shambler_attack_lightning_damage;
 +float autocvar_g_monster_shambler_attack_lightning_force;
 +float autocvar_g_monster_shambler_attack_lightning_radius;
 +float autocvar_g_monster_shambler_attack_lightning_radius_zap;
 +float autocvar_g_monster_shambler_attack_lightning_speed;
 +float autocvar_g_monster_shambler_attack_lightning_speed_up;
 +float autocvar_g_monster_shambler_speed_stop;
 +float autocvar_g_monster_shambler_speed_run;
 +float autocvar_g_monster_shambler_speed_walk;
 +
-       
++const float shambler_anim_stand               = 0;
++const float shambler_anim_walk                = 1;
++const float shambler_anim_run         = 2;
++const float shambler_anim_smash               = 3;
++const float shambler_anim_swingr      = 4;
++const float shambler_anim_swingl      = 5;
++const float shambler_anim_magic               = 6;
++const float shambler_anim_pain                = 7;
++const float shambler_anim_death               = 8;
 +
 +.float shambler_lastattack; // delay attacks separately
 +
 +void shambler_smash()
 +{
 +      makevectors(self.angles);
 +      pointparticles(particleeffectnum("explosion_medium"), (self.origin + (v_forward * 150)) - ('0 0 1' * self.maxs_z), '0 0 0', 1);
 +      sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
-       
++
 +      tracebox(self.origin + v_forward * 50, self.mins * 0.5, self.maxs * 0.5, self.origin + v_forward * 500, MOVE_NORMAL, self);
-       
++
 +      if(trace_ent.takedamage)
 +              Damage(trace_ent, self, self, (autocvar_g_monster_shambler_attack_smash_damage) * Monster_SkillModifier(), DEATH_MONSTER_SHAMBLER_SMASH, trace_ent.origin, normalize(trace_ent.origin - self.origin));
 +}
 +
 +void shambler_swing()
 +{
 +      float r = (random() < 0.5);
 +      monster_melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? shambler_anim_swingr : shambler_anim_swingl), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW, TRUE);
 +      if(r)
 +      {
 +              defer(0.5, shambler_swing);
 +              self.attack_finished_single += 0.5;
 +      }
 +}
 +
 +void shambler_lightning_explode()
 +{
 +      entity head;
-       
++
 +      sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
 +      pointparticles(particleeffectnum("electro_impact"), '0 0 0', '0 0 0', 1);
 +
 +      self.event_damage = func_null;
 +      self.takedamage = DAMAGE_NO;
 +      self.movetype = MOVETYPE_NONE;
 +      self.velocity = '0 0 0';
 +
 +      if(self.movetype == MOVETYPE_NONE)
 +              self.velocity = self.oldvelocity;
 +
 +      RadiusDamage (self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), world, (autocvar_g_monster_shambler_attack_lightning_force), self.projectiledeathtype, other);
-               
++
 +      for(head = findradius(self.origin, (autocvar_g_monster_shambler_attack_lightning_radius_zap)); head; head = head.chain) if(head != self.realowner) if(head.takedamage)
 +      {
 +              te_csqc_lightningarc(self.origin, head.origin);
 +              Damage(head, self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage) * Monster_SkillModifier(), DEATH_MONSTER_SHAMBLER_ZAP, head.origin, '0 0 0');
 +      }
 +
 +      self.think = SUB_Remove;
 +      self.nextthink = time + 0.2;
 +}
 +
 +void shambler_lightning_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if (self.health <= 0)
 +              return;
-               
++
 +      if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
 +              return; // g_projectiles_damage says to halt
-       
++
 +      self.health = self.health - damage;
-       
++
 +      if (self.health <= 0)
 +              W_PrepareExplosionByDamage(attacker, self.use);
 +}
 +
 +void shambler_lightning_touch()
 +{
 +      PROJECTILE_TOUCH;
-       
++
 +      self.use ();
 +}
 +
 +void shambler_lightning_think()
 +{
 +      self.nextthink = time;
 +      if (time > self.cnt)
 +      {
 +              other = world;
 +              shambler_lightning_explode();
 +              return;
 +      }
 +}
 +
 +void shambler_lightning()
 +{
 +      entity gren;
-                       
++
 +      monster_makevectors(self.enemy);
 +
 +      gren = spawn ();
 +      gren.owner = gren.realowner = self;
 +      gren.classname = "grenade";
 +      gren.bot_dodge = TRUE;
 +      gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage);
 +      gren.movetype = MOVETYPE_BOUNCE;
 +      PROJECTILE_MAKETRIGGER(gren);
 +      gren.projectiledeathtype = DEATH_MONSTER_SHAMBLER_ZAP;
 +      setorigin(gren, CENTER_OR_VIEWOFS(self));
 +      setsize(gren, '-8 -8 -8', '8 8 8');
 +      gren.scale = 2.5;
 +
 +      gren.cnt = time + 5;
 +      gren.nextthink = time;
 +      gren.think = shambler_lightning_think;
 +      gren.use = shambler_lightning_explode;
 +      gren.touch = shambler_lightning_touch;
 +
 +      gren.takedamage = DAMAGE_YES;
 +      gren.health = 50;
 +      gren.damageforcescale = 0;
 +      gren.event_damage = shambler_lightning_damage;
 +      gren.damagedbycontents = TRUE;
 +      gren.missile_flags = MIF_SPLASH | MIF_ARC;
 +      W_SetupProjectileVelocityEx(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, FALSE);
 +
 +      gren.angles = vectoangles (gren.velocity);
 +      gren.flags = FL_PROJECTILE;
 +
 +      CSQCProjectile(gren, TRUE, PROJECTILE_SHAMBLER_LIGHTNING, TRUE);
 +}
 +
 +float shambler_attack(float attack_type)
 +{
 +      switch(attack_type)
 +      {
 +              case MONSTER_ATTACK_MELEE:
 +              {
 +                      shambler_swing();
 +                      return TRUE;
 +              }
 +              case MONSTER_ATTACK_RANGED:
 +              {
 +                      if(time >= self.shambler_lastattack) // shambler doesn't attack much
 +                      if(random() <= 0.5 && vlen(self.enemy.origin - self.origin) <= 500)
 +                      {
 +                              self.frame = shambler_anim_smash;
 +                              defer(0.7, shambler_smash);
 +                              self.attack_finished_single = time + 1.1;
 +                              self.shambler_lastattack = time + 3;
 +                              return TRUE;
 +                      }
 +                      else if(random() <= 0.1) // small chance, don't want this spammed
 +                      {
 +                              self.frame = shambler_anim_magic;
 +                              self.attack_finished_single = time + 1.1;
 +                              self.shambler_lastattack = time + 3;
 +                              defer(0.6, shambler_lightning);
 +                              return TRUE;
 +                      }
-       
++
 +                      return FALSE;
 +              }
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +void spawnfunc_monster_shambler()
 +{
 +      self.classname = "monster_shambler";
-       
++
 +      self.monster_spawnfunc = spawnfunc_monster_shambler;
-       
++
 +      if(Monster_CheckAppearFlags(self))
 +              return;
-                       
++
 +      if(!monster_initialize(MON_SHAMBLER, FALSE)) { remove(self); return; }
 +}
 +
 +float m_shambler(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_THINK:
 +              {
 +                      monster_move((autocvar_g_monster_shambler_speed_run), (autocvar_g_monster_shambler_speed_walk), (autocvar_g_monster_shambler_speed_stop), shambler_anim_run, shambler_anim_walk, shambler_anim_stand);
 +                      return TRUE;
 +              }
 +              case MR_DEATH:
 +              {
 +                      self.frame = shambler_anim_death;
 +                      return TRUE;
 +              }
 +              case MR_SETUP:
 +              {
 +                      if(!self.health) self.health = (autocvar_g_monster_shambler_health);
 +                      if(!self.attack_range) self.attack_range = 150;
-                       
++
 +                      self.monster_loot = spawnfunc_item_health_mega;
 +                      self.monster_attackfunc = shambler_attack;
 +                      self.frame = shambler_anim_stand;
 +                      self.weapon = WEP_NEX;
-       
++
 +                      return TRUE;
 +              }
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/shambler.mdl");
 +                      return TRUE;
 +              }
 +      }
-       
++
 +      return TRUE;
 +}
 +
 +#endif // SVQC
 +#ifdef CSQC
 +float m_shambler(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/shambler.mdl");
 +                      return TRUE;
 +              }
 +      }
++
 +      return TRUE;
 +}
 +
 +#endif // CSQC
 +#endif // REGISTER_MONSTER
index 7e3c9f0c3e3684cc0c5d261efd7f115c1a182b70,0000000000000000000000000000000000000000..2de82422a37af55c9a510618d41a027638d1f448
mode 100644,000000..100644
--- /dev/null
@@@ -1,183 -1,0 +1,183 @@@
- /* MON_##id   */ SPIDER,
- /* function   */ m_spider,
 +#ifdef REGISTER_MONSTER
 +REGISTER_MONSTER(
- /* model      */ "spider.dpm",
- /* netname    */ "spider",
- /* fullname   */ _("Spider")
++/* MON_##id     */ SPIDER,
++/* function     */ m_spider,
 +/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_RANGED,
 +/* mins,maxs  */ '-18 -18 -25', '18 18 30',
-               
++/* model        */ "spider.dpm",
++/* netname      */ "spider",
++/* fullname     */ _("Spider")
 +);
 +
 +#else
 +#ifdef SVQC
 +float autocvar_g_monster_spider_health;
 +float autocvar_g_monster_spider_attack_bite_damage;
 +float autocvar_g_monster_spider_attack_bite_delay;
 +float autocvar_g_monster_spider_attack_web_damagetime;
 +float autocvar_g_monster_spider_attack_web_speed;
 +float autocvar_g_monster_spider_attack_web_speed_up;
 +float autocvar_g_monster_spider_attack_web_delay;
 +float autocvar_g_monster_spider_speed_stop;
 +float autocvar_g_monster_spider_speed_run;
 +float autocvar_g_monster_spider_speed_walk;
 +
 +const float spider_anim_idle          = 0;
 +const float spider_anim_walk          = 1;
 +const float spider_anim_attack                = 2;
 +const float spider_anim_attack2               = 3;
 +
 +.float spider_web_delay;
 +
 +void spider_web_explode()
 +{
 +      entity e;
 +      if(self)
 +      {
 +              pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
 +              RadiusDamage(self, self.realowner, 0, 0, 25, world, 25, self.projectiledeathtype, world);
-               
++
 +              for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0)
 +                      e.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime);
-       
++
 +              remove(self);
 +      }
 +}
 +
 +void spider_web_touch()
 +{
 +      PROJECTILE_TOUCH;
-       
++
 +      spider_web_explode();
 +}
 +
 +void spider_shootweb()
 +{
 +      monster_makevectors(self.enemy);
-               {       
++
 +      sound(self, CH_SHOTS, "weapons/electro_fire2.wav", VOL_BASE, ATTEN_NORM);
 +
 +      entity proj = spawn ();
 +      proj.classname = "plasma";
 +      proj.owner = proj.realowner = self;
 +      proj.use = spider_web_touch;
 +      proj.think = adaptor_think2use_hittype_splash;
 +      proj.bot_dodge = TRUE;
 +      proj.bot_dodgerating = 0;
 +      proj.nextthink = time + 5;
 +      PROJECTILE_MAKETRIGGER(proj);
 +      proj.projectiledeathtype = DEATH_MONSTER_SPIDER;
 +      setorigin(proj, CENTER_OR_VIEWOFS(self));
 +
 +      //proj.glow_size = 50;
 +      //proj.glow_color = 45;
 +      proj.movetype = MOVETYPE_BOUNCE;
 +      W_SetupProjectileVelocityEx(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, FALSE);
 +      proj.touch = spider_web_touch;
 +      setsize(proj, '-4 -4 -4', '4 4 4');
 +      proj.takedamage = DAMAGE_NO;
 +      proj.damageforcescale = 0;
 +      proj.health = 500;
 +      proj.event_damage = func_null;
 +      proj.flags = FL_PROJECTILE;
 +      proj.damagedbycontents = TRUE;
 +
 +      proj.bouncefactor = 0.3;
 +      proj.bouncestop = 0.05;
 +      proj.missile_flags = MIF_SPLASH | MIF_ARC;
 +
 +      CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, TRUE);
 +}
 +
 +float spider_attack(float attack_type)
 +{
 +      switch(attack_type)
 +      {
 +              case MONSTER_ATTACK_MELEE:
-                       
++              {
 +                      return monster_melee(self.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? spider_anim_attack : spider_anim_attack2), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER, TRUE);
 +              }
 +              case MONSTER_ATTACK_RANGED:
 +              {
 +                      if(time >= self.spider_web_delay)
 +                      {
 +                              self.frame = spider_anim_attack2;
 +                              self.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
 +                              spider_shootweb();
 +                              self.spider_web_delay = time + 3;
 +                              return TRUE;
 +                      }
-       
++
 +                      return FALSE;
 +              }
 +      }
- void spawnfunc_monster_spider() 
++
 +      return FALSE;
 +}
 +
-       
++void spawnfunc_monster_spider()
 +{
 +      self.classname = "monster_spider";
-       
++
 +      self.monster_spawnfunc = spawnfunc_monster_spider;
-       
++
 +      if(Monster_CheckAppearFlags(self))
 +              return;
-                       
++
 +      if(!monster_initialize(MON_SPIDER, FALSE)) { remove(self); return; }
 +}
 +
 +float m_spider(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_THINK:
 +              {
 +                      monster_move((autocvar_g_monster_spider_speed_run), (autocvar_g_monster_spider_speed_walk), (autocvar_g_monster_spider_speed_stop), spider_anim_walk, spider_anim_walk, spider_anim_idle);
 +                      return TRUE;
 +              }
 +              case MR_DEATH:
 +              {
 +                      self.frame = spider_anim_attack;
 +                      self.angles_x = 180;
 +                      return TRUE;
 +              }
 +              case MR_SETUP:
 +              {
 +                      if(!self.health) self.health = (autocvar_g_monster_spider_health);
-                       
++
 +                      self.monster_loot = spawnfunc_item_health_medium;
 +                      self.monster_attackfunc = spider_attack;
 +                      self.frame = spider_anim_idle;
-       
++
 +                      return TRUE;
 +              }
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/spider.dpm");
 +                      precache_sound ("weapons/electro_fire2.wav");
 +                      return TRUE;
 +              }
 +      }
-       
++
 +      return TRUE;
 +}
 +
 +#endif // SVQC
 +#ifdef CSQC
 +float m_spider(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/spider.dpm");
 +                      return TRUE;
 +              }
 +      }
++
 +      return TRUE;
 +}
 +
 +#endif // CSQC
 +#endif // REGISTER_MONSTER
index eadfab950c1301dea4a239cfc031b3dc31e8ecc0,0000000000000000000000000000000000000000..3774d9bdbd114b7a107301741698eb39b2f68c1f
mode 100644,000000..100644
--- /dev/null
@@@ -1,164 -1,0 +1,164 @@@
- /* MON_##id   */ WYVERN,
- /* function   */ m_wyvern,
 +#ifdef REGISTER_MONSTER
 +REGISTER_MONSTER(
- /* model      */ "wizard.mdl",
- /* netname    */ "wyvern",
- /* fullname   */ _("Wyvern")
++/* MON_##id     */ WYVERN,
++/* function     */ m_wyvern,
 +/* spawnflags */ MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED,
 +/* mins,maxs  */ '-20 -20 -58', '20 20 20',
- const float wyvern_anim_hover         = 0;
- const float wyvern_anim_fly   = 1;
- const float wyvern_anim_magic         = 2;
- const float wyvern_anim_pain  = 3;
- const float wyvern_anim_death         = 4;
++/* model        */ "wizard.mdl",
++/* netname      */ "wyvern",
++/* fullname     */ _("Wyvern")
 +);
 +
 +#else
 +#ifdef SVQC
 +float autocvar_g_monster_wyvern_health;
 +float autocvar_g_monster_wyvern_attack_fireball_damage;
 +float autocvar_g_monster_wyvern_attack_fireball_edgedamage;
 +float autocvar_g_monster_wyvern_attack_fireball_damagetime;
 +float autocvar_g_monster_wyvern_attack_fireball_force;
 +float autocvar_g_monster_wyvern_attack_fireball_radius;
 +float autocvar_g_monster_wyvern_attack_fireball_speed;
 +float autocvar_g_monster_wyvern_speed_stop;
 +float autocvar_g_monster_wyvern_speed_run;
 +float autocvar_g_monster_wyvern_speed_walk;
 +
-               
++const float wyvern_anim_hover = 0;
++const float wyvern_anim_fly           = 1;
++const float wyvern_anim_magic = 2;
++const float wyvern_anim_pain  = 3;
++const float wyvern_anim_death = 4;
 +
 +void wyvern_fireball_explode()
 +{
 +      entity e;
 +      if(self)
 +      {
 +              pointparticles(particleeffectnum("fireball_explode"), self.origin, '0 0 0', 1);
-               
++
 +              RadiusDamage(self, self.realowner, (autocvar_g_monster_wyvern_attack_fireball_damage), (autocvar_g_monster_wyvern_attack_fireball_edgedamage), (autocvar_g_monster_wyvern_attack_fireball_force), world, (autocvar_g_monster_wyvern_attack_fireball_radius), self.projectiledeathtype, world);
-               
++
 +              for(e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); ) if(vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius))
 +                      Fire_AddDamage(e, self, 5 * Monster_SkillModifier(), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype);
-       
++
 +              remove(self);
 +      }
 +}
 +
 +void wyvern_fireball_touch()
 +{
 +      PROJECTILE_TOUCH;
-       
++
 +      wyvern_fireball_explode();
 +}
 +
 +void wyvern_fireball()
 +{
 +      entity missile = spawn();
 +      vector dir = normalize((self.enemy.origin + '0 0 10') - self.origin);
-       setsize(missile, '-6 -6 -6', '6 6 6');          
++
 +      monster_makevectors(self.enemy);
 +
 +      missile.owner = missile.realowner = self;
 +      missile.solid = SOLID_TRIGGER;
 +      missile.movetype = MOVETYPE_FLYMISSILE;
 +      missile.projectiledeathtype = DEATH_MONSTER_WYVERN;
-                       
++      setsize(missile, '-6 -6 -6', '6 6 6');
 +      setorigin(missile, self.origin + self.view_ofs + v_forward * 14);
 +      missile.flags = FL_PROJECTILE;
 +      missile.velocity = dir * (autocvar_g_monster_wyvern_attack_fireball_speed);
 +      missile.avelocity = '300 300 300';
 +      missile.nextthink = time + 5;
 +      missile.think = wyvern_fireball_explode;
 +      missile.enemy = self.enemy;
 +      missile.touch = wyvern_fireball_touch;
 +      CSQCProjectile(missile, TRUE, PROJECTILE_FIREMINE, TRUE);
 +}
 +
 +float wyvern_attack(float attack_type)
 +{
 +      switch(attack_type)
 +      {
 +              case MONSTER_ATTACK_MELEE:
 +              case MONSTER_ATTACK_RANGED:
 +              {
 +                      self.attack_finished_single = time + 1.2;
-                       
++
 +                      wyvern_fireball();
-       
++
 +                      return TRUE;
 +              }
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +void spawnfunc_monster_wyvern()
 +{
 +      self.classname = "monster_wyvern";
-       
++
 +      self.monster_spawnfunc = spawnfunc_monster_wyvern;
-       
++
 +      if(Monster_CheckAppearFlags(self))
 +              return;
-                       
++
 +      if(!monster_initialize(MON_WYVERN, TRUE)) { remove(self); return; }
 +}
 +
 +// compatibility with old spawns
 +void spawnfunc_monster_wizard() { spawnfunc_monster_wyvern(); }
 +
 +float m_wyvern(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_THINK:
 +              {
 +                      monster_move((autocvar_g_monster_wyvern_speed_run), (autocvar_g_monster_wyvern_speed_walk), (autocvar_g_monster_wyvern_speed_stop), wyvern_anim_fly, wyvern_anim_hover, wyvern_anim_hover);
 +                      return TRUE;
 +              }
 +              case MR_DEATH:
 +              {
 +                      self.frame = wyvern_anim_death;
 +                      self.velocity_x = -200 + 400 * random();
 +                      self.velocity_y = -200 + 400 * random();
 +                      self.velocity_z = 100 + 100 * random();
 +                      return TRUE;
 +              }
 +              case MR_SETUP:
 +              {
 +                      if(!self.health) self.health = (autocvar_g_monster_wyvern_health);
-                       
++
 +                      self.monster_loot = spawnfunc_item_cells;
 +                      self.monster_attackfunc = wyvern_attack;
 +                      self.frame = wyvern_anim_hover;
-       
++
 +                      return TRUE;
 +              }
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/wizard.mdl");
 +                      return TRUE;
 +              }
 +      }
-       
++
 +      return TRUE;
 +}
 +
 +#endif // SVQC
 +#ifdef CSQC
 +float m_wyvern(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/wizard.mdl");
 +                      return TRUE;
 +              }
 +      }
++
 +      return TRUE;
 +}
 +
 +#endif // CSQC
 +#endif // REGISTER_MONSTER
index 26e88b2f273fd4c7209683434365fcced69a9e56,0000000000000000000000000000000000000000..5d16169148d460713146ec775eb578613159e915
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,202 @@@
- /* MON_##id   */ ZOMBIE,
- /* function   */ m_zombie,
 +#ifdef REGISTER_MONSTER
 +REGISTER_MONSTER(
- /* model      */ "zombie.dpm",
- /* netname    */ "zombie",
- /* fullname   */ _("Zombie")
++/* MON_##id     */ ZOMBIE,
++/* function     */ m_zombie,
 +/* spawnflags */ MON_FLAG_MELEE,
 +/* mins,maxs  */ '-18 -18 -25', '18 18 47',
- const float zombie_anim_blockend                      = 7;
++/* model        */ "zombie.dpm",
++/* netname      */ "zombie",
++/* fullname     */ _("Zombie")
 +);
 +
 +#else
 +#ifdef SVQC
 +float autocvar_g_monster_zombie_health;
 +float autocvar_g_monster_zombie_attack_melee_damage;
 +float autocvar_g_monster_zombie_attack_melee_delay;
 +float autocvar_g_monster_zombie_attack_leap_damage;
 +float autocvar_g_monster_zombie_attack_leap_force;
 +float autocvar_g_monster_zombie_attack_leap_speed;
 +float autocvar_g_monster_zombie_attack_leap_delay;
 +float autocvar_g_monster_zombie_speed_stop;
 +float autocvar_g_monster_zombie_speed_run;
 +float autocvar_g_monster_zombie_speed_walk;
 +
 +const float zombie_anim_attackleap                    = 0;
 +const float zombie_anim_attackrun1                    = 1;
 +const float zombie_anim_attackrun2                    = 2;
 +const float zombie_anim_attackrun3                    = 3;
 +const float zombie_anim_attackstanding1               = 4;
 +const float zombie_anim_attackstanding2               = 5;
 +const float zombie_anim_attackstanding3               = 6;
- const float zombie_anim_idle                          = 19;
- const float zombie_anim_painback1                     = 20;
- const float zombie_anim_painback2                     = 21;
++const float zombie_anim_blockend                      = 7;
 +const float zombie_anim_blockstart                    = 8;
 +const float zombie_anim_deathback1                    = 9;
 +const float zombie_anim_deathback2                    = 10;
 +const float zombie_anim_deathback3                    = 11;
 +const float zombie_anim_deathfront1                   = 12;
 +const float zombie_anim_deathfront2                   = 13;
 +const float zombie_anim_deathfront3                   = 14;
 +const float zombie_anim_deathleft1                    = 15;
 +const float zombie_anim_deathleft2                    = 16;
 +const float zombie_anim_deathright1                   = 17;
 +const float zombie_anim_deathright2                   = 18;
- const float zombie_anim_runbackwards          = 24;
- const float zombie_anim_runbackwardsleft      = 25;
- const float zombie_anim_runbackwardsright     = 26;
++const float zombie_anim_idle                          = 19;
++const float zombie_anim_painback1                     = 20;
++const float zombie_anim_painback2                     = 21;
 +const float zombie_anim_painfront1                    = 22;
 +const float zombie_anim_painfront2                    = 23;
- const float zombie_anim_runforwardleft                = 28;
++const float zombie_anim_runbackwards          = 24;
++const float zombie_anim_runbackwardsleft      = 25;
++const float zombie_anim_runbackwardsright     = 26;
 +const float zombie_anim_runforward                    = 27;
- const float zombie_anim_spawn                         = 30;
++const float zombie_anim_runforwardleft                = 28;
 +const float zombie_anim_runforwardright               = 29;
-               
++const float zombie_anim_spawn                         = 30;
 +
 +void zombie_attack_leap_touch()
 +{
 +      if (self.health <= 0)
 +              return;
-       
++
 +      vector angles_face;
 +
 +      if(other.takedamage)
 +      {
 +              angles_face = vectoangles(self.moveto - self.origin);
 +              angles_face = normalize(angles_face) * (autocvar_g_monster_zombie_attack_leap_force);
 +              Damage(other, self, self, (autocvar_g_monster_zombie_attack_leap_damage) * Monster_SkillModifier(), DEATH_MONSTER_ZOMBIE_JUMP, other.origin, angles_face);
 +              self.touch = MonsterTouch; // instantly turn it off to stop damage spam
 +      }
 +
 +      if (trace_dphitcontents)
 +              self.touch = MonsterTouch;
 +}
 +
 +void zombie_blockend()
 +{
 +      if(self.health <= 0)
 +              return;
 +
 +      self.frame = zombie_anim_blockend;
 +      self.armorvalue = 0;
 +      self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent;
 +}
 +
 +float zombie_block()
 +{
 +      self.frame = zombie_anim_blockstart;
 +      self.armorvalue = 100;
 +      self.m_armor_blockpercent = 0.9;
 +      self.state = MONSTER_STATE_ATTACK_MELEE; // freeze monster
 +      self.attack_finished_single = time + 2.1;
-       
++
 +      defer(2, zombie_blockend);
-               
++
 +      return TRUE;
 +}
 +
 +float zombie_attack(float attack_type)
 +{
 +      switch(attack_type)
 +      {
 +              case MONSTER_ATTACK_MELEE:
 +              {
 +                      float rand = random(), chosen_anim;
-                       
++
 +                      if(rand < 0.33)
 +                              chosen_anim = zombie_anim_attackstanding1;
 +                      else if(rand < 0.66)
 +                              chosen_anim = zombie_anim_attackstanding2;
 +                      else
 +                              chosen_anim = zombie_anim_attackstanding3;
-                       
++
 +                      if(random() < 0.3 && self.health < 75 && self.enemy.health > 10)
 +                              return zombie_block();
-       
++
 +                      return monster_melee(self.enemy, (autocvar_g_monster_zombie_attack_melee_damage), chosen_anim, self.attack_range, (autocvar_g_monster_zombie_attack_melee_delay), DEATH_MONSTER_ZOMBIE_MELEE, TRUE);
 +              }
 +              case MONSTER_ATTACK_RANGED:
 +              {
 +                      makevectors(self.angles);
 +                      return monster_leap(zombie_anim_attackleap, zombie_attack_leap_touch, v_forward * (autocvar_g_monster_zombie_attack_leap_speed) + '0 0 200', (autocvar_g_monster_zombie_attack_leap_delay));
 +              }
 +      }
- void spawnfunc_monster_zombie() 
++
 +      return FALSE;
 +}
 +
-       
++void spawnfunc_monster_zombie()
 +{
 +      self.classname = "monster_zombie";
-       
++
 +      self.monster_spawnfunc = spawnfunc_monster_zombie;
-       
++
 +      self.spawnflags |= MONSTER_RESPAWN_DEATHPOINT;
-       
++
 +      if(Monster_CheckAppearFlags(self))
 +              return;
-                       
++
 +      if(!monster_initialize(MON_ZOMBIE, FALSE)) { remove(self); return; }
 +}
 +
 +float m_zombie(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_THINK:
 +              {
 +                      monster_move((autocvar_g_monster_zombie_speed_run), (autocvar_g_monster_zombie_speed_walk), (autocvar_g_monster_zombie_speed_stop), zombie_anim_runforward, zombie_anim_runforward, zombie_anim_idle);
 +                      return TRUE;
 +              }
 +              case MR_DEATH:
 +              {
 +                      self.armorvalue = 0;
 +                      self.m_armor_blockpercent = autocvar_g_monsters_armor_blockpercent;
 +                      self.frame = ((random() > 0.5) ? zombie_anim_deathback1 : zombie_anim_deathfront1);
 +                      return TRUE;
 +              }
 +              case MR_SETUP:
 +              {
 +                      if(!self.health) self.health = (autocvar_g_monster_zombie_health);
-                       
++
 +                      if(self.spawnflags & MONSTERFLAG_NORESPAWN)
 +                              self.spawnflags &= ~MONSTERFLAG_NORESPAWN; // zombies always respawn
-                       
++
 +                      self.monster_loot = spawnfunc_item_health_medium;
 +                      self.monster_attackfunc = zombie_attack;
 +                      self.frame = zombie_anim_spawn;
 +                      self.spawn_time = time + 2.1;
 +                      self.spawnshieldtime = self.spawn_time;
 +                      self.respawntime = 0.2;
-       
++
 +                      return TRUE;
 +              }
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/zombie.dpm");
 +                      return TRUE;
 +              }
 +      }
-       
++
 +      return TRUE;
 +}
 +
 +#endif // SVQC
 +#ifdef CSQC
 +float m_zombie(float req)
 +{
 +      switch(req)
 +      {
 +              case MR_PRECACHE:
 +              {
 +                      precache_model ("models/monsters/zombie.dpm");
 +                      return TRUE;
 +              }
 +      }
++
 +      return TRUE;
 +}
 +
 +#endif // CSQC
 +#endif // REGISTER_MONSTER
index f40d30d2921522009cd66909a27a3d6279a02f5d,0000000000000000000000000000000000000000..70802dbbde3fafe4c90fe41002b255a206ca9be2
mode 100644,000000..100644
--- /dev/null
@@@ -1,49 -1,0 +1,49 @@@
-       
 +#include "all.qh"
 +
 +// MONSTER PLUGIN SYSTEM
 +entity monster_info[MON_MAXCOUNT];
 +entity dummy_monster_info;
 +
 +void register_monster(float id, float(float) func, float monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname)
 +{
 +      entity e;
 +      monster_info[id - 1] = e = spawn();
 +      e.classname = "monster_info";
 +      e.monsterid = id;
 +      e.netname = shortname;
 +      e.monster_name = mname;
 +      e.monster_func = func;
 +      e.mdl = modelname;
 +      e.spawnflags = monsterflags;
 +      e.mins = min_s;
 +      e.maxs = max_s;
 +      e.model = strzone(strcat("models/monsters/", modelname));
++
 +      #ifndef MENUQC
 +      func(MR_PRECACHE);
 +      #endif
 +}
 +float m_null(float dummy) { return 0; }
 +void register_monsters_done()
 +{
 +      dummy_monster_info = spawn();
 +      dummy_monster_info.classname = "monster_info";
 +      dummy_monster_info.monsterid = 0; // you can recognize dummies by this
 +      dummy_monster_info.netname = "";
 +      dummy_monster_info.monster_name = "Monster";
 +      dummy_monster_info.monster_func = m_null;
 +      dummy_monster_info.mdl = "";
 +      dummy_monster_info.mins = '-0 -0 -0';
 +      dummy_monster_info.maxs = '0 0 0';
 +      dummy_monster_info.model = "";
 +}
 +entity get_monsterinfo(float id)
 +{
 +      entity m;
 +      if(id < MON_FIRST || id > MON_LAST)
 +              return dummy_monster_info;
 +      m = monster_info[id - 1];
 +      if(m)
 +              return m;
 +      return dummy_monster_info;
 +}
index f6db09c89c85f5633f5638f311428b6bcb4bed76,0000000000000000000000000000000000000000..c355e12a75a31b197fadd75476db33290f270564
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- #define MR_SETUP          1 // (SERVER) setup monster data
 +// monster requests
- #define MR_DEATH          3 // (SERVER) called when monster dies
- #define MR_PRECACHE       4 // (BOTH) precaches models/sounds used by this monster
++#define MR_SETUP                1 // (SERVER) setup monster data
 +#define MR_THINK                2 // (SERVER) logic to run every frame
- //  Monster Registration
++#define MR_DEATH                3 // (SERVER) called when monster dies
++#define MR_PRECACHE             4 // (BOTH) precaches models/sounds used by this monster
 +
 +// functions:
 +entity get_monsterinfo(float id);
 +
 +// special spawn flags
 +const float MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died
 +const float MONSTER_TYPE_FLY = 32;
 +const float MONSTER_TYPE_SWIM = 64;
 +const float MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced
 +const float MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster
 +const float MON_FLAG_RANGED = 512; // monster shoots projectiles
 +const float MON_FLAG_MELEE = 1024;
 +
 +// entity properties of monsterinfo:
 +.float monsterid; // MON_...
 +.string netname; // short name
 +.string monster_name; // human readable name
 +.float(float) monster_func; // m_...
 +.string mdl; // currently a copy of the model
 +.string model; // full name of model
 +.float spawnflags;
 +.vector mins, maxs; // monster hitbox size
 +
 +// other useful macros
 +#define MON_ACTION(monstertype,mrequest) (get_monsterinfo(monstertype)).monster_func(mrequest)
 +#define M_NAME(monstertype) (get_monsterinfo(monstertype)).monster_name
 +
 +// =====================
++//    Monster Registration
 +// =====================
 +
 +float m_null(float dummy);
 +void register_monster(float id, float(float) func, float monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname);
 +void register_monsters_done();
 +
 +const float MON_MAXCOUNT = 24;
 +#define MON_FIRST 1
 +float MON_COUNT;
 +float MON_LAST;
 +
 +#define REGISTER_MONSTER_2(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
 +      float id; \
 +      float func(float); \
 +      void RegisterMonsters_##id() \
 +      { \
 +              MON_LAST = (id = MON_FIRST + MON_COUNT); \
 +              ++MON_COUNT; \
 +              register_monster(id,func,monsterflags,min_s,max_s,modelname,shortname,mname); \
 +      } \
 +      ACCUMULATE_FUNCTION(RegisterMonsters, RegisterMonsters_##id)
 +#ifdef MENUQC
 +#define REGISTER_MONSTER(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
 +      REGISTER_MONSTER_2(MON_##id,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
 +#else
 +#define REGISTER_MONSTER(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
 +      REGISTER_MONSTER_2(MON_##id,func,monsterflags,min_s,max_s,modelname,shortname,mname)
 +#endif
 +
 +#include "all.qh"
 +
 +#undef REGISTER_MONSTER
 +ACCUMULATE_FUNCTION(RegisterMonsters, register_monsters_done);
index 61bf56e320ed7bf9a06ec501203004ed615af44d,0000000000000000000000000000000000000000..3fe8567aee36c8f178616d76e8dad81fd234fae7
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,57 @@@
-       
 +entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float moveflag)
 +{
 +      // ensure spawnfunc database is initialized
 +      initialize_field_db();
-       
++
 +      entity e = spawn();
-       
++
 +      e.spawnflags = MONSTERFLAG_SPAWNED;
-       
++
 +      if(!respwn)
 +              e.spawnflags |= MONSTERFLAG_NORESPAWN;
-       
++
 +      setorigin(e, orig);
-               
++
 +      if(monster != "")
 +      {
 +              float i, found = 0;
 +              entity mon;
 +              for(i = MON_FIRST; i <= MON_LAST; ++i)
 +              {
 +                      mon = get_monsterinfo(i);
 +                      if(mon.netname == monster)
 +                      {
 +                              found = TRUE;
 +                              break;
 +                      }
 +              }
 +              if(!found)
 +                      monster = (get_monsterinfo(MON_FIRST)).netname;
 +      }
-       
++
 +      if(monster == "")
 +      if(monster_id)
 +              monster = (get_monsterinfo(monster_id)).netname;
-       
++
 +      e.realowner = spawnedby;
-       
++
 +      if(moveflag)
 +              e.monster_moveflags = moveflag;
-                       
++
 +      if(IS_PLAYER(spawnedby))
 +      {
 +              if(teamplay && autocvar_g_monsters_teams)
 +                      e.team = spawnedby.team; // colors handled in spawn code
-                       
++
 +              if(autocvar_g_monsters_owners)
 +                      e.monster_owner = own; // using .owner makes the monster non-solid for its master
-               
++
 +              e.angles = spawnedby.angles;
 +      }
-               
++
 +      monster = strcat("$ spawnfunc_monster_", monster);
-               
++
 +      target_spawn_edit_entity(e, monster, world, world, world, world, world);
++
 +      return e;
 +}
index 1acc47626a35475cf1ab02f0810ff25c2792c17b,0000000000000000000000000000000000000000..8176dee3df1f59ab75052c1b7981786159e10895
mode 100644,000000..100644
--- /dev/null
@@@ -1,1124 -1,0 +1,1124 @@@
- //  SVQC Monster Properties
 +// =========================
-       
++//    SVQC Monster Properties
 +// =========================
 +
 +
 +void monster_item_spawn()
 +{
 +      if(self.monster_loot)
 +              self.monster_loot();
 +
 +      self.gravity = 1;
 +      self.reset = SUB_Remove;
 +      self.noalign = TRUE;
 +      self.velocity = randomvec() * 175 + '0 0 325';
 +      self.classname = "droppedweapon"; // hax
 +      self.item_spawnshieldtime = time + 0.7;
 +
 +      SUB_SetFade(self, time + autocvar_g_monsters_drop_time, 1);
 +}
 +
 +void monster_dropitem()
 +{
 +      if(!self.candrop || !self.monster_loot)
 +              return;
 +
 +      vector org = self.origin + ((self.mins + self.maxs) * 0.5);
 +      entity e = spawn();
 +
 +      setorigin(e, org);
 +
 +      e.monster_loot = self.monster_loot;
 +
 +      other = e;
 +      MUTATOR_CALLHOOK(MonsterDropItem);
 +      e = other;
 +
 +      if(e)
 +      {
 +              e.think = monster_item_spawn;
 +              e.nextthink = time + 0.3;
 +      }
 +}
 +
 +float Monster_SkillModifier()
 +{
 +      float t = 0.5+self.monster_skill*((1.2-0.3)/10);
-               
++
 +      return t;
 +}
 +
 +float monster_isvalidtarget (entity targ, entity ent)
 +{
 +      if(!targ || !ent)
 +              return FALSE; // someone doesn't exist
 +
 +      if(targ == ent)
 +              return FALSE; // don't attack ourselves
 +
 +      traceline(ent.origin, targ.origin, MOVE_NORMAL, ent);
 +
 +      if(trace_ent != targ)
 +              return FALSE;
 +
 +      if(targ.vehicle_flags & VHF_ISVEHICLE)
 +      if(!((get_monsterinfo(ent.monsterid)).spawnflags & MON_FLAG_RANGED))
 +              return FALSE; // melee attacks are useless against vehicles
 +
 +      if(time < game_starttime)
 +              return FALSE; // monsters do nothing before the match has started
 +
 +      if(vlen(targ.origin - ent.origin) >= ent.target_range)
 +              return FALSE; // enemy is too far away
 +
 +      if(targ.takedamage == DAMAGE_NO)
 +              return FALSE; // enemy can't be damaged
 +
 +      if(targ.items & IT_INVISIBILITY)
 +              return FALSE; // enemy is invisible
 +
 +      if(substring(targ.classname, 0, 10) == "onslaught_")
 +              return FALSE; // don't attack onslaught targets
 +
 +      if(IS_SPEC(targ) || IS_OBSERVER(targ))
 +              return FALSE; // enemy is a spectator
 +
 +      if(!(targ.vehicle_flags & VHF_ISVEHICLE))
 +      if(targ.deadflag != DEAD_NO || ent.deadflag != DEAD_NO || targ.health <= 0 || ent.health <= 0)
 +              return FALSE; // enemy/self is dead
 +
 +      if(ent.monster_owner == targ)
 +              return FALSE; // don't attack our master
 +
 +      if(targ.monster_owner == ent)
 +              return FALSE; // don't attack our pet
 +
 +      if(!(targ.vehicle_flags & VHF_ISVEHICLE))
 +      if(targ.flags & FL_NOTARGET)
 +              return FALSE; // enemy can't be targeted
 +
 +      if(!autocvar_g_monsters_typefrag)
 +      if(targ.BUTTON_CHAT)
 +              return FALSE; // no typefragging!
 +
 +      if(SAME_TEAM(targ, ent))
 +              return FALSE; // enemy is on our team
-               
++
 +      if (targ.frozen)
 +              return FALSE; // ignore frozen
 +
 +      if(autocvar_g_monsters_target_infront || ent.spawnflags & MONSTERFLAG_INFRONT)
 +      if(ent.enemy != targ)
 +      {
 +              float dot;
 +
 +              makevectors (ent.angles);
 +              dot = normalize (targ.origin - ent.origin) * v_forward;
 +
 +              if(dot <= 0.3)
 +                      return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +entity FindTarget (entity ent)
 +{
 +      if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return ent.enemy; } // Handled by a mutator
 +
 +      entity head, closest_target = world;
 +      head = findradius(ent.origin, ent.target_range);
 +
 +      while(head) // find the closest acceptable target to pass to
 +      {
 +              if(head.monster_attack)
 +              if(monster_isvalidtarget(head, ent))
 +              {
 +                      // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
 +                      vector head_center = CENTER_OR_VIEWOFS(head);
 +                      vector ent_center = CENTER_OR_VIEWOFS(ent);
 +
 +                      //if(ctf_CheckPassDirection(head_center, ent_center, ent.v_angle, head.WarpZone_findradius_nearest))
 +                      if(closest_target)
 +                      {
 +                              vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
 +                              if(vlen(ent_center - head_center) < vlen(ent_center - closest_target_center))
 +                                      { closest_target = head; }
 +                      }
 +                      else { closest_target = head; }
 +              }
 +
 +              head = head.chain;
 +      }
 +
 +      return closest_target;
 +}
 +
 +void MonsterTouch ()
 +{
 +      if(other == world)
 +              return;
 +
 +      if(self.enemy != other)
 +      if(!(other.flags & FL_MONSTER))
 +      if(monster_isvalidtarget(other, self))
 +              self.enemy = other;
 +}
 +
 +string get_monster_model_datafilename(string m, float sk, string fil)
 +{
 +      if(m)
 +              m = strcat(m, "_");
 +      else
 +              m = "models/monsters/*_";
 +      if(sk >= 0)
 +              m = strcat(m, ftos(sk));
 +      else
 +              m = strcat(m, "*");
 +      return strcat(m, ".", fil);
 +}
 +
 +void PrecacheMonsterSounds(string f)
 +{
 +      float fh;
 +      string s;
 +      fh = fopen(f, FILE_READ);
 +      if(fh < 0)
 +              return;
 +      while((s = fgets(fh)))
 +      {
 +              if(tokenize_console(s) != 3)
 +              {
 +                      dprint("Invalid sound info line: ", s, "\n");
 +                      continue;
 +              }
 +              PrecacheGlobalSound(strcat(argv(1), " ", argv(2)));
 +      }
 +      fclose(fh);
 +}
 +
 +void precache_monstersounds()
 +{
 +      string m = (get_monsterinfo(self.monsterid)).model;
 +      float globhandle, n, i;
 +      string f;
 +
 +      globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
 +      if (globhandle < 0)
 +              return;
 +      n = search_getsize(globhandle);
 +      for (i = 0; i < n; ++i)
 +      {
 +              //print(search_getfilename(globhandle, i), "\n");
 +              f = search_getfilename(globhandle, i);
 +              PrecacheMonsterSounds(f);
 +      }
 +      search_end(globhandle);
 +}
 +
 +void ClearMonsterSounds()
 +{
 +#define _MSOUND(m) if(self.monstersound_##m) { strunzone(self.monstersound_##m); self.monstersound_##m = string_null; }
 +      ALLMONSTERSOUNDS
 +#undef _MSOUND
 +}
 +
 +.string GetMonsterSoundSampleField(string type)
 +{
 +      GetMonsterSoundSampleField_notFound = 0;
 +      switch(type)
 +      {
 +#define _MSOUND(m) case #m: return monstersound_##m;
 +              ALLMONSTERSOUNDS
 +#undef _MSOUND
 +      }
 +      GetMonsterSoundSampleField_notFound = 1;
 +      return string_null;
 +}
 +
 +float LoadMonsterSounds(string f, float first)
 +{
 +      float fh;
 +      string s;
 +      var .string field;
 +      fh = fopen(f, FILE_READ);
 +      if(fh < 0)
 +      {
 +              dprint("Monster sound file not found: ", f, "\n");
 +              return 0;
 +      }
 +      while((s = fgets(fh)))
 +      {
 +              if(tokenize_console(s) != 3)
 +                      continue;
 +              field = GetMonsterSoundSampleField(argv(0));
 +              if(GetMonsterSoundSampleField_notFound)
 +                      continue;
 +              if(self.field)
 +                      strunzone(self.field);
 +              self.field = strzone(strcat(argv(1), " ", argv(2)));
 +      }
 +      fclose(fh);
 +      return 1;
 +}
 +
 +.float skin_for_monstersound;
 +void UpdateMonsterSounds()
 +{
 +      entity mon = get_monsterinfo(self.monsterid);
 +
 +      if(self.skin == self.skin_for_monstersound)
 +              return;
 +      self.skin_for_monstersound = self.skin;
 +      ClearMonsterSounds();
 +      //LoadMonsterSounds("sound/monsters/default.sounds", 1);
 +      if(!autocvar_g_debug_defaultsounds)
 +      if(!LoadMonsterSounds(get_monster_model_datafilename(mon.model, self.skin, "sounds"), 0))
 +              LoadMonsterSounds(get_monster_model_datafilename(mon.model, 0, "sounds"), 0);
 +}
 +
 +void MonsterSound(.string samplefield, float sound_delay, float delaytoo, float chan)
 +{
 +      if(delaytoo && time < self.msound_delay)
 +              return; // too early
 +      GlobalSound(self.samplefield, chan, VOICETYPE_PLAYERSOUND);
 +
 +      self.msound_delay = time + sound_delay;
 +}
 +
 +void monster_makevectors(entity e)
 +{
 +      vector v;
 +
 +      v = e.origin + (e.mins + e.maxs) * 0.5;
 +      self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
 +      self.v_angle_x = -self.v_angle_x;
 +
 +      makevectors(self.v_angle);
 +}
 +
 +float monster_melee(entity targ, float damg, float anim, float er, float anim_finished, float deathtype, float dostop)
 +{
 +      if (self.health <= 0)
 +              return FALSE; // attacking while dead?!
 +
 +      if(dostop)
 +      {
 +              self.velocity_x = 0;
 +              self.velocity_y = 0;
 +              self.state = MONSTER_STATE_ATTACK_MELEE;
 +      }
 +
 +      self.frame = anim;
 +
 +      if(anim_finished != 0)
 +              self.attack_finished_single = time + anim_finished;
 +
 +      monster_makevectors(targ);
 +
 +      traceline(self.origin + self.view_ofs, self.origin + v_forward * er, 0, self);
 +
 +      if(trace_ent.takedamage)
 +              Damage(trace_ent, self, self, damg * Monster_SkillModifier(), deathtype, trace_ent.origin, normalize(trace_ent.origin - self.origin));
 +
 +      return TRUE;
 +}
 +
 +void Monster_CheckMinibossFlag ()
 +{
 +      if(MUTATOR_CALLHOOK(MonsterCheckBossFlag))
 +              return;
 +
 +      float chance = random() * 100;
 +
 +      // g_monsters_miniboss_chance cvar or spawnflags 64 causes a monster to be a miniboss
 +      if ((self.spawnflags & MONSTERFLAG_MINIBOSS) || (chance < autocvar_g_monsters_miniboss_chance))
 +      {
 +              self.health += autocvar_g_monsters_miniboss_healthboost;
 +              self.effects |= EF_RED;
 +              if(!self.weapon)
 +                      self.weapon = WEP_NEX;
 +      }
 +}
 +
 +float Monster_CanRespawn(entity ent)
 +{
 +      other = ent;
 +      if(ent.deadflag == DEAD_DEAD) // don't call when monster isn't dead
 +      if(MUTATOR_CALLHOOK(MonsterRespawn))
 +              return TRUE; // enabled by a mutator
 +
 +      if(ent.spawnflags & MONSTERFLAG_NORESPAWN)
 +              return FALSE;
 +
 +      if(!autocvar_g_monsters_respawn)
 +              return FALSE;
 +
 +      return TRUE;
 +}
 +
 +void Monster_Fade ()
 +{
 +      if(Monster_CanRespawn(self))
 +      {
 +              self.monster_respawned = TRUE;
 +              self.think = self.monster_spawnfunc;
 +              self.nextthink = time + self.respawntime;
 +              self.ltime = 0;
 +              self.deadflag = DEAD_RESPAWNING;
 +              if(self.spawnflags & MONSTER_RESPAWN_DEATHPOINT)
 +              {
 +                      self.pos1 = self.origin;
 +                      self.pos2 = self.angles;
 +              }
 +              self.event_damage = func_null;
 +              self.takedamage = DAMAGE_NO;
 +              setorigin(self, self.pos1);
 +              self.angles = self.pos2;
 +              self.health = self.max_health;
 +              setmodel(self, "null");
 +      }
 +      else
 +              SUB_SetFade(self, time + 3, 1);
 +}
 +
 +float Monster_CanJump (vector vel)
 +{
 +      if(self.state)
 +              return FALSE; // already attacking
 +      if(!(self.flags & FL_ONGROUND))
 +              return FALSE; // not on the ground
 +      if(self.health <= 0)
 +              return FALSE; // called when dead?
 +      if(time < self.attack_finished_single)
 +              return FALSE; // still attacking
 +
 +      vector old = self.velocity;
 +
 +      self.velocity = vel;
 +      tracetoss(self, self);
 +      self.velocity = old;
 +      if (trace_ent != self.enemy)
 +              return FALSE;
 +
 +      return TRUE;
 +}
 +
 +float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished)
 +{
 +      if(!Monster_CanJump(vel))
 +              return FALSE;
 +
 +      self.frame = anm;
 +      self.state = MONSTER_STATE_ATTACK_LEAP;
 +      self.touch = touchfunc;
 +      self.origin_z += 1;
 +      self.velocity = vel;
 +      self.flags &= ~FL_ONGROUND;
 +
 +      self.attack_finished_single = time + anim_finished;
 +
 +      return TRUE;
 +}
 +
 +void monster_checkattack(entity e, entity targ)
 +{
 +      if(e == world)
 +              return;
 +      if(targ == world)
 +              return;
 +
 +      if(!e.monster_attackfunc)
 +              return;
 +
 +      if(time < e.attack_finished_single)
 +              return;
 +
 +      if(vlen(targ.origin - e.origin) <= e.attack_range)
 +      if(e.monster_attackfunc(MONSTER_ATTACK_MELEE))
 +      {
 +              MonsterSound(monstersound_melee, 0, FALSE, CH_VOICE);
 +              return;
 +      }
 +
 +      if(vlen(targ.origin - e.origin) > e.attack_range)
 +      if(e.monster_attackfunc(MONSTER_ATTACK_RANGED))
 +      {
 +              MonsterSound(monstersound_ranged, 0, FALSE, CH_VOICE);
 +              return;
 +      }
 +}
 +
 +void monster_use ()
 +{
 +      if(!self.enemy)
 +      if(self.health > 0)
 +      if(monster_isvalidtarget(activator, self))
 +              self.enemy = activator;
 +}
 +
 +.float last_trace;
 +.float last_enemycheck; // for checking enemy
 +vector monster_pickmovetarget(entity targ)
 +{
 +      // enemy is always preferred target
 +      if(self.enemy)
 +      {
 +              makevectors(self.angles);
 +              self.monster_movestate = MONSTER_MOVE_ENEMY;
 +              self.last_trace = time + 1.2;
 +              return self.enemy.origin;
 +      }
 +
 +      switch(self.monster_moveflags)
 +      {
 +              case MONSTER_MOVE_OWNER:
 +              {
 +                      self.monster_movestate = MONSTER_MOVE_OWNER;
 +                      self.last_trace = time + 0.3;
 +                      return (self.monster_owner) ? self.monster_owner.origin : self.origin;
 +              }
 +              case MONSTER_MOVE_SPAWNLOC:
 +              {
 +                      self.monster_movestate = MONSTER_MOVE_SPAWNLOC;
 +                      self.last_trace = time + 2;
 +                      return self.pos1;
 +              }
 +              case MONSTER_MOVE_NOMOVE:
 +              {
 +                      self.monster_movestate = MONSTER_MOVE_NOMOVE;
 +                      self.last_trace = time + 2;
 +                      return self.origin;
 +              }
 +              default:
 +              case MONSTER_MOVE_WANDER:
 +              {
 +                      vector pos;
 +                      self.monster_movestate = MONSTER_MOVE_WANDER;
 +                      self.last_trace = time + 2;
 +
 +                      self.angles_y = rint(random() * 500);
 +                      makevectors(self.angles);
 +                      pos = self.origin + v_forward * 600;
 +
 +                      if(self.flags & FL_FLY || self.flags & FL_SWIM)
 +                      if(self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
 +                      {
 +                              pos_z = random() * 200;
 +                              if(random() >= 0.5)
 +                                      pos_z *= -1;
 +                      }
 +
 +                      if(targ)
 +                      {
 +                              self.last_trace = time + 0.5;
 +                              pos = targ.origin;
 +                      }
 +
 +                      return pos;
 +              }
 +      }
 +}
 +
 +void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle)
 +{
 +      fixedmakevectors(self.angles);
 +
 +      if(self.target2)
 +              self.goalentity = find(world, targetname, self.target2);
 +
 +      entity targ;
 +
 +      if(self.frozen)
 +      {
 +              self.revive_progress = bound(0, self.revive_progress + self.ticrate * self.revive_speed, 1);
 +              self.health = max(1, self.max_health * self.revive_progress);
-               
++
 +              WaypointSprite_UpdateHealth(self.sprite, self.health);
-               
++
 +              movelib_beak_simple(stopspeed);
-               
++
 +              self.frame = manim_idle;
-       
++
 +              self.enemy = world;
 +              self.nextthink = time + self.ticrate;
 +
 +              if(self.revive_progress >= 1)
 +                      Unfreeze(self); // wait for next think before attacking
 +
 +              return; // no moving while frozen
 +      }
 +
 +      if(self.flags & FL_SWIM)
 +      {
 +              if(self.waterlevel < WATERLEVEL_WETFEET)
 +              {
 +                      if(time >= self.last_trace)
 +                      {
 +                              self.fish_wasdrowning = TRUE;
 +                              self.last_trace = time + 0.4;
 +
 +                              Damage (self, world, world, 2, DEATH_DROWN, self.origin, '0 0 0');
 +                              self.angles = '90 90 0';
 +                              if(random() < 0.5)
 +                              {
 +                                      self.velocity_y += random() * 50;
 +                                      self.velocity_x -= random() * 50;
 +                              }
 +                              else
 +                              {
 +                                      self.velocity_y -= random() * 50;
 +                                      self.velocity_x += random() * 50;
 +                              }
 +                              self.velocity_z += random() * 150;
 +                      }
 +
 +
 +                      self.movetype = MOVETYPE_BOUNCE;
 +                      //self.velocity_z = -200;
 +
 +                      return;
 +              }
 +              else if(self.fish_wasdrowning)
 +              {
 +                      self.fish_wasdrowning = FALSE;
 +                      self.angles_x = 0;
 +                      self.movetype = MOVETYPE_WALK;
 +              }
 +      }
 +
 +      targ = self.goalentity;
 +
 +      monster_target = targ;
 +      monster_speed_run = runspeed;
 +      monster_speed_walk = walkspeed;
 +
 +      if(MUTATOR_CALLHOOK(MonsterMove) || gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
 +      {
 +              runspeed = walkspeed = 0;
 +              if(time >= self.spawn_time)
 +                      self.frame = manim_idle;
 +              movelib_beak_simple(stopspeed);
 +              return;
 +      }
 +
 +      targ = monster_target;
 +      runspeed = bound(0, monster_speed_run * Monster_SkillModifier(), runspeed * 2); // limit maxspeed to prevent craziness
 +      walkspeed = bound(0, monster_speed_walk * Monster_SkillModifier(), walkspeed * 2); // limit maxspeed to prevent craziness
-               
++
 +      if(time < self.spider_slowness)
 +      {
 +              runspeed *= 0.5;
 +              walkspeed *= 0.5;
 +      }
 +
 +      if(teamplay)
 +      if(autocvar_g_monsters_teams)
 +      if(DIFF_TEAM(self.monster_owner, self))
 +              self.monster_owner = world;
 +
 +      if(self.enemy && self.enemy.health < 1)
 +              self.enemy = world; // enough!
 +
 +      if(time >= self.last_enemycheck)
 +      {
 +              if(!monster_isvalidtarget(self.enemy, self))
 +                      self.enemy = world;
 +
 +              if(!self.enemy)
 +              {
 +                      self.enemy = FindTarget(self);
 +                      if(self.enemy)
 +                              MonsterSound(monstersound_sight, 0, FALSE, CH_VOICE);
 +              }
 +
 +              self.last_enemycheck = time + 0.5;
 +      }
 +
 +      if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single)
 +              self.state = 0;
 +
 +      if(self.state != MONSTER_STATE_ATTACK_MELEE) // don't move if set
 +      if(time >= self.last_trace || self.enemy) // update enemy instantly
 +              self.moveto = monster_pickmovetarget(targ);
 +
 +      if(!self.enemy)
 +              MonsterSound(monstersound_idle, 7, TRUE, CH_VOICE);
 +
 +      if(self.state != MONSTER_STATE_ATTACK_LEAP && self.state != MONSTER_STATE_ATTACK_MELEE)
 +              self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +
 +      if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND))
 +      {
 +              self.state = 0;
 +              self.touch = MonsterTouch;
 +      }
 +
 +      //self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
 +
 +      float turny = 0;
 +      vector real_angle = vectoangles(self.steerto) - self.angles;
 +
 +      if(self.state != MONSTER_STATE_ATTACK_LEAP && self.state != MONSTER_STATE_ATTACK_MELEE)
 +              turny = 20;
 +
 +      if(self.flags & FL_SWIM)
 +              turny = vlen(self.angles - self.moveto);
 +
 +      if(turny)
 +      {
 +              turny = bound(turny * -1, shortangle_f(real_angle_y, self.angles_y), turny);
 +              self.angles_y += turny;
 +      }
 +
 +      if(self.state == MONSTER_STATE_ATTACK_MELEE)
 +              self.moveto = self.origin;
 +
 +      if(self.enemy && self.enemy.vehicle)
 +              runspeed = 0;
 +
 +      if(((self.flags & FL_FLY) || (self.flags & FL_SWIM)) && self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
 +              v_forward = normalize(self.moveto - self.origin);
 +      else
 +              self.moveto_z = self.origin_z;
 +
 +      if(vlen(self.origin - self.moveto) > 64)
 +      {
 +              if(self.flags & FL_FLY || self.flags & FL_SWIM)
 +                      movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
 +              else
 +                      movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
 +
 +              if(time > self.pain_finished)
 +              if(time > self.attack_finished_single)
 +              if(vlen(self.velocity) > 10)
 +                      self.frame = ((self.enemy) ? manim_run : manim_walk);
 +              else
 +                      self.frame = manim_idle;
 +      }
 +      else
 +      {
 +              entity e = find(world, targetname, self.target2);
 +              if(e.target2)
 +                      self.target2 = e.target2;
 +              else if(e.target)
 +                      self.target2 = e.target;
 +
 +              movelib_beak_simple(stopspeed);
 +              if(time > self.attack_finished_single)
 +              if(time > self.pain_finished)
 +              if (vlen(self.velocity) <= 30)
 +                      self.frame = manim_idle;
 +      }
 +
 +      monster_checkattack(self, self.enemy);
 +}
 +
 +void monster_remove(entity mon)
 +{
 +      if(!mon)
 +              return; // nothing to remove
-               
++
 +      pointparticles(particleeffectnum("item_pickup"), mon.origin, '0 0 0', 1);
-                                       
++
 +      if(mon.weaponentity)
 +              remove(mon.weaponentity);
-                                       
++
 +      if(mon.iceblock)
 +              remove(mon.iceblock);
-                                       
++
 +      WaypointSprite_Kill(mon.sprite);
-       
++
 +      remove(mon);
 +}
 +
 +void monster_dead_think()
 +{
 +      self.nextthink = time + self.ticrate;
-       
++
 +      CSQCMODEL_AUTOUPDATE();
 +
 +      if(self.ltime != 0)
 +      if(time >= self.ltime)
 +      {
 +              Monster_Fade();
 +              return;
 +      }
 +}
 +
 +void monsters_setstatus()
 +{
 +      self.stat_monsters_total = monsters_total;
 +      self.stat_monsters_killed = monsters_killed;
 +}
 +
 +void Monster_Appear()
 +{
 +      self.enemy = activator;
 +      self.spawnflags &= ~MONSTERFLAG_APPEAR;
 +      self.monster_spawnfunc();
 +}
 +
 +float Monster_CheckAppearFlags(entity ent)
 +{
 +      if(!(ent.spawnflags & MONSTERFLAG_APPEAR))
 +              return FALSE;
 +
 +      ent.think = func_null;
 +      ent.nextthink = 0;
 +      ent.use = Monster_Appear;
 +      ent.flags = FL_MONSTER; // set so this monster can get butchered
 +
 +      return TRUE;
 +}
 +
 +void monsters_reset()
 +{
 +      setorigin(self, self.pos1);
 +      self.angles = self.pos2;
-               
++
 +      Unfreeze(self); // remove any icy remains
 +
 +      self.health = self.max_health;
 +      self.velocity = '0 0 0';
 +      self.enemy = world;
 +      self.goalentity = world;
 +      self.attack_finished_single = 0;
 +      self.moveto = self.origin;
 +}
 +
 +void monsters_corpse_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      self.health -= damage;
 +
 +      Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
 +
 +      if(self.health <= -100) // 100 health until gone?
 +      {
 +              Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
-       
++
 +              if(IS_CLIENT(self.realowner))
 +              if(!self.monster_respawned)
 +                      self.realowner.monstercount -= 1;
 +
 +              self.think = SUB_Remove;
 +              self.nextthink = time + 0.1;
 +      }
 +}
 +
 +void monster_die(entity attacker, float gibbed)
 +{
 +      self.think = monster_dead_think;
 +      self.nextthink = time;
 +      self.ltime = time + 5;
-       
++
 +      if ( self.frozen )
 +      {
 +              Unfreeze(self); // remove any icy remains
 +              self.health = 0; // reset by Unfreeze
 +      }
-               
++
 +      monster_dropitem();
 +
 +      MonsterSound(monstersound_death, 0, FALSE, CH_VOICE);
 +
 +      if(!(self.spawnflags & MONSTERFLAG_SPAWNED) && !self.monster_respawned)
 +              monsters_killed += 1;
-       if( autocvar_g_monsters_score_spawned || 
++
 +      if(IS_PLAYER(attacker))
-               
++      if( autocvar_g_monsters_score_spawned ||
 +                      ( !(self.spawnflags & MONSTERFLAG_SPAWNED) && !self.monster_respawned) )
 +              PlayerScore_Add(attacker, SP_SCORE, +autocvar_g_monsters_score_kill);
 +
 +      if(!Monster_CanRespawn(self) || gibbed)
 +      {
 +              // number of monsters spawned with mobspawn command
 +              totalspawned -= 1;
-       
++
 +              if(IS_CLIENT(self.realowner))
 +              if(!self.monster_respawned)
 +                      self.realowner.monstercount -= 1;
 +      }
 +
 +      if(self.candrop && self.weapon)
 +              W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
 +
 +      self.event_damage       = monsters_corpse_damage;
 +      self.solid                      = SOLID_CORPSE;
 +      self.takedamage         = DAMAGE_AIM;
 +      self.deadflag           = DEAD_DEAD;
 +      self.enemy                      = world;
 +      self.movetype           = MOVETYPE_TOSS;
 +      self.moveto                     = self.origin;
 +      self.touch                      = MonsterTouch; // reset incase monster was pouncing
 +      self.reset                      = func_null;
 +      self.state                      = 0;
 +      self.attack_finished_single = 0;
 +
 +      if(!(self.flags & FL_FLY))
 +              self.velocity = '0 0 0';
 +
 +      MON_ACTION(self.monsterid, MR_DEATH);
 +}
 +
 +void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
-       
++
 +      if(self.frozen && deathtype != DEATH_KILL)
 +              return;
 +
 +      if(time < self.pain_finished && deathtype != DEATH_KILL)
 +              return;
 +
 +      if(time < self.spawnshieldtime && deathtype != DEATH_KILL)
 +              return;
 +
 +      vector v;
 +      float take, save;
 +
 +      v = healtharmor_applydamage(self.armorvalue, self.m_armor_blockpercent, deathtype, damage);
 +      take = v_x;
 +      save = v_y;
 +
 +      self.health -= take;
-               
++
 +      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +
 +      self.dmg_time = time;
 +
 +      if(sound_allowed(MSG_BROADCAST, attacker) && deathtype != DEATH_DROWN)
 +              spamsound (self, CH_PAIN, "misc/bodyimpact1.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
 +
 +      self.velocity += force * self.damageforcescale;
 +
 +      if(deathtype != DEATH_DROWN)
 +      {
 +              Violence_GibSplash_At(hitloc, force, 2, bound(0, take, 200) / 16, self, attacker);
 +              if (take > 50)
 +                      Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, self, attacker);
 +              if (take > 100)
 +                      Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker);
 +      }
 +
 +      if(self.health <= 0)
 +      {
 +              if(deathtype == DEATH_KILL)
 +                      self.candrop = FALSE; // killed by mobkill command
 +
 +              // TODO: fix this?
 +              activator = attacker;
 +              other = self.enemy;
 +              SUB_UseTargets();
 +              self.target2 = self.oldtarget2; // reset to original target on death, incase we respawn
 +
 +              monster_die(attacker, (self.health <= -100 || deathtype == DEATH_KILL));
-               
++
 +              WaypointSprite_Kill(self.sprite);
-       
++
 +              frag_attacker = attacker;
 +              frag_target = self;
 +              MUTATOR_CALLHOOK(MonsterDies);
 +
 +              if(self.health <= -100 || deathtype == DEATH_KILL) // check if we're already gibbed
 +              {
 +                      Violence_GibSplash(self, 1, 0.5, attacker);
 +
 +                      self.think = SUB_Remove;
 +                      self.nextthink = time + 0.1;
 +              }
 +      }
 +}
 +
 +void monster_setupcolors()
 +{
 +      if(IS_PLAYER(self.monster_owner))
 +              self.colormap = self.monster_owner.colormap;
 +      else if(teamplay && self.team)
 +              self.colormap = 1024 + (self.team - 1) * 17;
 +      else
 +      {
 +              if(self.monster_skill <= MONSTER_SKILL_EASY)
 +                      self.colormap = 1029;
 +              else if(self.monster_skill <= MONSTER_SKILL_MEDIUM)
 +                      self.colormap = 1027;
 +              else if(self.monster_skill <= MONSTER_SKILL_HARD)
 +                      self.colormap = 1038;
 +              else if(self.monster_skill <= MONSTER_SKILL_INSANE)
 +                      self.colormap = 1028;
 +              else if(self.monster_skill <= MONSTER_SKILL_NIGHTMARE)
 +                      self.colormap = 1032;
 +              else
 +                      self.colormap = 1024;
 +      }
 +}
 +
 +void monster_think()
 +{
 +      self.think = monster_think;
 +      self.nextthink = self.ticrate;
-       
++
 +      if(self.ltime)
 +      if(time >= self.ltime)
 +      {
 +              Damage(self, self, self, self.health + self.max_health, DEATH_KILL, self.origin, self.origin);
 +              return;
 +      }
 +
 +      MON_ACTION(self.monsterid, MR_THINK);
-       
++
 +      CSQCMODEL_AUTOUPDATE();
 +}
 +
 +float monster_spawn()
 +{
 +      MON_ACTION(self.monsterid, MR_SETUP);
 +
 +      if(!self.monster_respawned)
 +      {
 +              Monster_CheckMinibossFlag();
 +              self.health *= Monster_SkillModifier();
 +      }
 +
 +      self.max_health = self.health;
 +      self.pain_finished = self.nextthink;
-       
++
 +      if(IS_PLAYER(self.monster_owner))
 +              self.effects |= EF_DIMLIGHT;
 +
 +      if(!self.monster_respawned)
 +      if(!self.skin)
 +              self.skin = rint(random() * 4);
 +
 +      if(!self.attack_range)
 +              self.attack_range = autocvar_g_monsters_attack_range;
-               
++
 +      precache_monstersounds();
 +      UpdateMonsterSounds();
 +
 +      if(teamplay)
 +              self.monster_attack = TRUE; // we can have monster enemies in team games
-       
++
 +      MonsterSound(monstersound_spawn, 0, FALSE, CH_VOICE);
-       
++
 +      WaypointSprite_Spawn(M_NAME(self.monsterid), 0, 1024, self, '0 0 1' * (self.maxs_z + 15), world, self.team, self, sprite, TRUE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0'));
 +      WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
 +      WaypointSprite_UpdateHealth(self.sprite, self.health);
 +
 +      self.think = monster_think;
 +      self.nextthink = time + self.ticrate;
-       
++
 +      if(MUTATOR_CALLHOOK(MonsterSpawn))
 +              return FALSE;
-       
++
 +      return TRUE;
 +}
 +
 +float monster_initialize(float mon_id, float nodrop)
 +{
 +      if(!autocvar_g_monsters)
 +              return FALSE;
 +
 +      entity mon = get_monsterinfo(mon_id);
-       
++
 +      if(!self.monster_skill)
 +              self.monster_skill = cvar("g_monsters_skill");
 +
 +      // support for quake style removing monsters based on skill
 +      if(self.monster_skill == MONSTER_SKILL_EASY) if(self.spawnflags & MONSTERSKILL_NOTEASY) { return FALSE; }
 +      if(self.monster_skill == MONSTER_SKILL_MEDIUM) if(self.spawnflags & MONSTERSKILL_NOTMEDIUM) { return FALSE; }
 +      if(self.monster_skill == MONSTER_SKILL_HARD) if(self.spawnflags & MONSTERSKILL_NOTHARD) { return FALSE; }
 +
 +      if(self.team && !teamplay)
 +              self.team = 0;
 +
 +      if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) // naturally spawned monster
 +      if(!self.monster_respawned)
 +              monsters_total += 1;
 +
 +      setmodel(self, mon.model);
 +      setsize(self, mon.mins, mon.maxs);
 +      self.flags                              = FL_MONSTER;
 +      self.takedamage                 = DAMAGE_AIM;
 +      self.bot_attack                 = TRUE;
 +      self.iscreature                 = TRUE;
 +      self.teleportable               = TRUE;
 +      self.damagedbycontents  = TRUE;
 +      self.monsterid                  = mon_id;
 +      self.damageforcescale   = 0;
 +      self.event_damage               = monsters_damage;
 +      self.touch                              = MonsterTouch;
 +      self.use                                = monster_use;
 +      self.solid                              = SOLID_BBOX;
 +      self.movetype                   = MOVETYPE_WALK;
 +      self.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
 +      self.enemy                              = world;
 +      self.velocity                   = '0 0 0';
 +      self.moveto                             = self.origin;
 +      self.pos1                               = self.origin;
 +      self.pos2                               = self.angles;
 +      self.reset                              = monsters_reset;
 +      self.netname                    = mon.netname;
 +      self.monster_name               = M_NAME(mon_id);
 +      self.candrop                    = TRUE;
 +      self.view_ofs                   = '0 0 1' * (self.maxs_z * 0.5);
 +      self.oldtarget2                 = self.target2;
 +      self.deadflag                   = DEAD_NO;
 +      self.scale                              = 1;
 +      self.noalign                    = nodrop;
 +      self.spawn_time                 = time;
 +      self.spider_slowness    = 0;
 +      self.gravity                    = 1;
 +      self.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
-               
++
 +      if(autocvar_g_fullbrightplayers)
 +              self.effects |= EF_FULLBRIGHT;
-               
++
 +      if(autocvar_g_nodepthtestplayers)
 +              self.effects |= EF_NODEPTHTEST;
 +
 +      if(mon.spawnflags & MONSTER_TYPE_SWIM)
 +              self.flags |= FL_SWIM;
 +
 +      if(mon.spawnflags & MONSTER_TYPE_FLY)
 +      {
 +              self.flags |= FL_FLY;
 +              self.movetype = MOVETYPE_FLY;
 +      }
 +
 +      if(mon.spawnflags & MONSTER_SIZE_BROKEN)
 +              self.scale = 1.3;
 +
 +      if(!self.ticrate)
 +              self.ticrate = autocvar_g_monsters_think_delay;
 +
 +      self.ticrate = bound(sys_frametime, self.ticrate, 60);
 +
 +      if(!self.m_armor_blockpercent)
 +              self.m_armor_blockpercent = 0.5;
 +
 +      if(!self.target_range)
 +              self.target_range = autocvar_g_monsters_target_range;
 +
 +      if(!self.respawntime)
 +              self.respawntime = autocvar_g_monsters_respawn_delay;
 +
 +      if(!self.monster_moveflags)
 +              self.monster_moveflags = MONSTER_MOVE_WANDER;
-               
++
 +      if(!self.noalign)
 +      {
 +              setorigin(self, self.origin + '0 0 20');
 +              tracebox(self.origin + '0 0 64', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
 +              setorigin(self, trace_endpos);
 +      }
-               
++
 +      if(!monster_spawn())
 +              return FALSE;
-       
++
 +      if(!self.monster_respawned)
 +              monster_setupcolors();
++
 +      CSQCMODEL_AUTOINIT();
 +
 +      return TRUE;
 +}
index 5bdb23b5a41e0ea117b11da52428b7b24d7df3d2,0000000000000000000000000000000000000000..a35d888050a2a035e98c05eb574da3a0a196e2cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,86 @@@
-               _MSOUND(idle) 
 +.string spawnmob;
 +.float monster_attack;
 +
 +.entity monster_owner; // new monster owner entity, fixes non-solid monsters
 +.float monstercount; // per player monster count
 +
 +.float stat_monsters_killed; // stats
 +.float stat_monsters_total;
 +float monsters_total;
 +float monsters_killed;
 +void monsters_setstatus(); // monsters.qc
 +.float monster_moveflags; // checks where to move when not attacking
 +
 +.float spider_slowness; // special spider timer
 +
 +void monster_remove(entity mon); // removes a monster
 +
 +.float(float attack_type) monster_attackfunc;
 +const float MONSTER_ATTACK_MELEE = 1;
 +const float MONSTER_ATTACK_RANGED = 2;
 +
 +.float monster_skill;
 +const float MONSTER_SKILL_EASY = 1;
 +const float MONSTER_SKILL_MEDIUM = 3;
 +const float MONSTER_SKILL_HARD = 5;
 +const float MONSTER_SKILL_INSANE = 7;
 +const float MONSTER_SKILL_NIGHTMARE = 10;
 +
 +.float fish_wasdrowning; // used to reset a drowning fish's angles if it reaches water again
 +
 +.float candrop;
 +
 +.float attack_range;
 +
 +.float spawn_time; // stop monster from moving around right after spawning
 +
 +.string oldtarget2;
 +.float lastshielded;
 +
 +.vector oldangles;
 +
 +.float m_armor_blockpercent;
 +
 +// monster sounds
 +// copied from player sounds
 +.float msound_delay; // temporary antilag system
 +#define ALLMONSTERSOUNDS \
 +              _MSOUND(death) \
 +              _MSOUND(sight) \
 +              _MSOUND(ranged) \
 +              _MSOUND(melee) \
 +              _MSOUND(pain) \
 +              _MSOUND(spawn) \
- const float MONSTERFLAG_MINIBOSS = 64;  // monster spawns as mini-boss (also has a chance of naturally becoming one)
++              _MSOUND(idle)
 +
 +#define _MSOUND(m) .string monstersound_##m;
 +ALLMONSTERSOUNDS
 +#undef _MSOUND
 +
 +float GetMonsterSoundSampleField_notFound;
 +
 +.float monster_respawned; // used to make sure we're not recounting respawned monster stats
 +
 +const float MONSTERSKILL_NOTEASY = 256; // monster will not spawn on skill <= 1
 +const float MONSTERSKILL_NOTMEDIUM = 512; // monster will not spawn on skill 2
 +const float MONSTERSKILL_NOTHARD = 1024; // monster will not spawn on skill >= 3
 +
 +// new flags
 +const float MONSTERFLAG_APPEAR = 2; // delay spawn until triggered
 +const float MONSTERFLAG_NORESPAWN = 4;
 +const float MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically
 +const float MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us
++const float MONSTERFLAG_MINIBOSS = 64;        // monster spawns as mini-boss (also has a chance of naturally becoming one)
 +const float MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters
 +
 +.void() monster_spawnfunc;
 +
 +.float monster_movestate; // used to tell what the monster is currently doing
 +const float MONSTER_MOVE_OWNER = 1; // monster will move to owner if in range, or stand still
 +const float MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around
 +const float MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn location when not attacking
 +const float MONSTER_MOVE_NOMOVE = 4; // monster simply stands still
 +const float MONSTER_MOVE_ENEMY = 5; // used only as a movestate
 +
 +const float MONSTER_STATE_ATTACK_LEAP = 1;
 +const float MONSTER_STATE_ATTACK_MELEE = 2;
index cc56b531c80bd1063795e60567d5121e50373b03,6c602219ba77bd3cc362780df15a4c0c5ac9f382..707eb2edce271cc66469a7ae52dadfd2ee581841
@@@ -5,7 -5,7 +5,7 @@@
  
  // main types/groups of notifications
  #define MSG_ANNCE 1 // "Global" AND "personal" announcer messages
- #define MSG_INFO 2 // "Global" information messages 
+ #define MSG_INFO 2 // "Global" information messages
  #define MSG_CENTER 3 // "Personal" centerprint messages
  #define MSG_CENTER_CPID 4 // Kill centerprint message
  #define MSG_MULTI 5 // Subcall MSG_INFO and/or MSG_CENTER notifications
@@@ -51,7 -51,7 +51,7 @@@ void Create_Notification_Entity
        float strnum,
        float flnum,
        /* MSG_ANNCE */
-       float channel, 
+       float channel,
        string snd,
        float vol,
        float position,
@@@ -188,7 -188,7 +188,7 @@@ void Send_Notification_WOCOVA
  
   Check out the definitions in util.qc/util.qh/teams.qh for string CCR(...) and
   string TCR(...) to better understand how these code replacements work.
-  
   Additionally, you can find all the definitions and explanations for
   the argument values and what they return down below in this file.
  
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FALL,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_fall",          _("^BG%s%s^K1 was grounded by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_LAVA,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_lava",          _("^BG%s%s^K1 was cooked by ^BG%s^K1%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_MONSTER,           3, 2, "spree_inf s1 s2 s3loc spree_end", "",       "",                             _("^BG%s%s^K1 was pushed infront of a monster by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SHOOTING_STAR,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_shootingstar",  _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SLIME,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC,             2, 1, "s1 s2loc spree_lost", "s1",       "notify_selfkill",      _("^BG%s^K1 died%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_lava",          _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE,                2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW,   2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH,  2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_ZAP,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was zapped to death by a Shambler%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SPIDER,              2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was bitten by a Spider%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN,          2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP,     2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 joins the Zombies%s%s"), "") \
 +      MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NOAMMO,              2, 1, "s1 s2loc spree_lost", "s1",       "notify_outofammo",     _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_ROT,                 2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 rotted away%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_SHOOTING_STAR,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_shootingstar",  _("^BG%s^K1 became a shooting star%s%s"), "") \
        MULTITEAM_INFO(1, INFO_KEYHUNT_PICKUP_, 4,             1, 0, "s1", "",                          "",                     _("^BG%s^BG picked up the ^TC^TT Key"), "") \
        MSG_INFO_NOTIF(1, INFO_LMS_FORFEIT,                    1, 0, "s1", "",                          "",                     _("^BG%s^F3 forfeited"), "") \
        MSG_INFO_NOTIF(1, INFO_LMS_NOLIVES,                    1, 0, "s1", "",                          "",                     _("^BG%s^F3 has no more lives left"), "") \
 +      MSG_INFO_NOTIF(1, INFO_MONSTERS_DISABLED,              0, 0, "", "",                            "",                     _("^BGMonsters are currently disabled"), "") \
        MSG_INFO_NOTIF(1, INFO_POWERUP_INVISIBILITY,           1, 0, "s1", "s1",                        "strength",             _("^BG%s^K1 picked up Invisibility"), "") \
        MSG_INFO_NOTIF(1, INFO_POWERUP_SHIELD,                 1, 0, "s1", "s1",                        "shield",               _("^BG%s^K1 picked up Shield"), "") \
        MSG_INFO_NOTIF(1, INFO_POWERUP_SPEED,                  1, 0, "s1", "s1",                        "shield",               _("^BG%s^K1 picked up Speed"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_MURDER,                   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weapontuba",             _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_SUICIDE,                  2, 1, "s1 s2loc spree_lost", "s1",                 "weapontuba",             _("^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SNIPE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \
-       MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SPRAY,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") 
+       MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SPRAY,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "weaponuzi",              _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "")
  
  #define MULTITEAM_CENTER2(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \
        MSG_CENTER_NOTIF(default, prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_FIRE,             0, 0, "",             NO_CPID,             "0 0", _("^K1You got a little bit too crispy!"), _("^K1You felt a little too hot!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_GENERIC,          0, 0, "",             NO_CPID,             "0 0", _("^K1You killed your own dumb self!"), _("^K1You need to be more careful!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_LAVA,             0, 0, "",             NO_CPID,             "0 0", _("^K1You couldn't stand the heat!"), "") \
 +      MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_MONSTER,          0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed by a monster!"), _("^K1You need to watch out for monsters!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE,                             0, 0, "",                         NO_CPID,                         "0 0", _("^K1You forgot to put the pin back in!"), _("^K1Tastes like chicken!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NOAMMO,           0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_ROT,              0, 0, "",             NO_CPID,             "0 0", _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
        MSG_CENTER_NOTIF(1, CENTER_ROUND_PLAYER_WIN,            1, 0, "s1",           CPID_ROUND,          "0 0", _("^BG%s^BG wins the round"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF,              0, 0, "",             NO_CPID,             "0 0", _("^K1You froze yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1Round already started, you spawn as frozen"), "") \
 +      MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER,       1, 0, "s1",           NO_CPID,             "0 0", _("^K1A %s has arrived!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE,        0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP,            1, 1, "item_wepname item_wepammo",         CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT,             0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_FALL,                    NO_MSG,        INFO_DEATH_MURDER_FALL,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_FIRE,                    NO_MSG,        INFO_DEATH_MURDER_FIRE,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA,                    NO_MSG,        INFO_DEATH_MURDER_LAVA,                    NO_MSG) \
 +      MSG_MULTI_NOTIF(1, DEATH_MURDER_MONSTER,                 NO_MSG,        INFO_DEATH_MURDER_MONSTER,                 CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE,                    NO_MSG,        INFO_DEATH_MURDER_NADE,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR,           NO_MSG,        INFO_DEATH_MURDER_SHOOTING_STAR,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME,                   NO_MSG,        INFO_DEATH_MURDER_SLIME,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_FIRE,                      NO_MSG,        INFO_DEATH_SELF_FIRE,                      CENTER_DEATH_SELF_FIRE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_GENERIC,                   NO_MSG,        INFO_DEATH_SELF_GENERIC,                   CENTER_DEATH_SELF_GENERIC) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_LAVA,                      NO_MSG,        INFO_DEATH_SELF_LAVA,                      CENTER_DEATH_SELF_LAVA) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_MAGE,                                  NO_MSG,        INFO_DEATH_SELF_MON_MAGE,                                  CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_CLAW,                 NO_MSG,        INFO_DEATH_SELF_MON_SHAMBLER_CLAW,                 CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_SMASH,                NO_MSG,        INFO_DEATH_SELF_MON_SHAMBLER_SMASH,                CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SHAMBLER_ZAP,                  NO_MSG,        INFO_DEATH_SELF_MON_SHAMBLER_ZAP,                  CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_SPIDER,                                NO_MSG,        INFO_DEATH_SELF_MON_SPIDER,                                CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_WYVERN,                                NO_MSG,        INFO_DEATH_SELF_MON_WYVERN,                                CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP,                   NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_JUMP,                   CENTER_DEATH_SELF_MONSTER) \
 +      MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE,                  NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_MELEE,                  CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NADE,                                              NO_MSG,                INFO_DEATH_SELF_NADE,                                      CENTER_DEATH_SELF_NADE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO,                    NO_MSG,        INFO_DEATH_SELF_NOAMMO,                    CENTER_DEATH_SELF_NOAMMO) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_ROT,                       NO_MSG,        INFO_DEATH_SELF_ROT,                       CENTER_DEATH_SELF_ROT) \
@@@ -878,7 -857,7 +878,7 @@@ var float autocvar_notification_item_ce
  
  // 0 = no, 1 = yes, 2 = forced on for all MSG_INFO notifs
  // DISABLED IN CODE, BUT ENABLED IN CONFIG FOR COMPATIBILITY WITH OLD CLIENTS
- var float autocvar_notification_allow_chatboxprint = 0; 
+ var float autocvar_notification_allow_chatboxprint = 0;
  
  var float autocvar_notification_show_sprees_center = TRUE;
  var float autocvar_notification_show_sprees_center_specialonly = TRUE;
   allows for more dynamic data to be inferred by the local
   notification parser, so that the server does not have to network
   anything too crazy on a per-client/per-situation basis.
-  
   Pay attention to the CSQC/SVQC relations, some of these are redefined
   in slightly different ways for different programs, this is because the
   server does a more conservative approach to the notifs than the client.
-  
   All arguments are swapped into strings, so be sure that your
   sprintf usage matches with proper %s placement.
-  
   Argument descriptions:
      s1-s4: string arguments to be literally swapped into sprintf
      s2loc: s2 string of locations of deaths or other events
@@@ -1046,13 -1025,13 +1046,13 @@@ string notif_arg_missing_teams(float f1
  string notif_arg_spree_cen(float spree)
  {
        // 0 = off, 1 = target (but only for first victim) and attacker
-       if(autocvar_notification_show_sprees_center) 
+       if(autocvar_notification_show_sprees_center)
        {
                if(spree > 1)
                {
                        #define SPREE_ITEM(counta,countb,center,normal,gentle) \
                                case counta: { return normal_or_gentle(center, sprintf(_("%d score spree! "), spree)); }
-                       
                        switch(spree)
                        {
                                KILL_SPREE_LIST
                                                                ),
                                                                spree);
                                        }
-                                       else { return ""; } // don't show spree information if it isn't an achievement 
+                                       else { return ""; } // don't show spree information if it isn't an achievement
                                }
                        }
  
@@@ -1095,7 -1074,7 +1095,7 @@@ string notif_arg_spree_inf(float type, 
                {
                        // 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker
                        // this conditional (& 2) is true for 2 and 3
-                       if(autocvar_notification_show_sprees_info & 2) 
+                       if(autocvar_notification_show_sprees_info & 2)
                        {
                                #ifdef CSQC
                                string spree_newline =
                                string spree_newline =
                                        (autocvar_notification_show_sprees_info_newline ? "\n" : "");
                                #endif
-                               
                                if(spree > 1)
                                {
                                        #define SPREE_ITEM(counta,countb,center,normal,gentle) \
                                                case counta: { return sprintf(CCR(normal_or_gentle(normal, gentle)), player, spree_newline); }
-                                       
                                        switch(spree)
                                        {
                                                KILL_SPREE_LIST
                                                                                spree_newline
                                                                        );
                                                        }
-                                                       else { return ""; } // don't show spree information if it isn't an achievement 
+                                                       else { return ""; } // don't show spree information if it isn't an achievement
                                                }
                                        }
  
@@@ -1485,9 -1464,9 +1485,9 @@@ void RegisterNotifications_First(
        #else
        #define dedi ""
        #endif
-       
        print(sprintf("Beginning notification initialization on %s%s program...\n", dedi, PROGNAME));
-       
        // maybe do another implementation of this with checksums? for now, we don't need versioning
        /*if(autocvar_notification_version != NOTIF_VERSION)
        {
diff --combined qcsrc/common/util.qh
index a5ef7b3afc06026707fa60845cafc688a6f1fe6e,a9f9095c7a40492e6615258364cc0ca059c08620..9ebf8614b6643ee894f98699c851342d7225500b
@@@ -52,7 -52,7 +52,7 @@@ void ACCUMULATE_call(string func
  // used for simplifying ACCUMULATE_FUNCTIONs
  #define SET_FIRST_OR_LAST(input,first,count) if(!input) { input = (first + count); }
  #define SET_FIELD_COUNT(field,first,count) if(!field) { field = (first + count); ++count; }
- #define CHECK_MAX_COUNT(name,max,count,type) if(count == max) { error(strcat("Maximum ", type, " hit: ", #name, ": ", ftos(count), ".\n")); }
+ #define CHECK_MAX_COUNT(name,max,count,type) if(count > max) { error(strcat("Maximum ", type, " hit: ", #name, ": ", ftos(count), ".\n")); }
  
  // this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline
  // NOTE: s IS allowed to be a tempstring
@@@ -101,7 -101,7 +101,7 @@@ void buf_save(float buf, string filenam
  
  // modulo function
  #ifndef MENUQC
- float mod(float a, float b) { return a - (floor(a / b) * b); }   
+ float mod(float a, float b) { return a - (floor(a / b) * b); }
  #endif
  
  #define TIME_TO_NTHS(t,n) floor((t) * (n) + 0.4)
@@@ -215,7 -215,7 +215,7 @@@ float compressShotOrigin(vector v)
  vector decompressShotOrigin(float f);
  
  #ifdef SVQC
 -string rankings_reply, ladder_reply, lsmaps_reply, maplist_reply; // cached replies
 +string rankings_reply, ladder_reply, lsmaps_reply, maplist_reply, monsterlist_reply; // cached replies
  string records_reply[10];
  #endif
  
index 120d80213c3d7facb79ff1e90d468162b523263d,0af90bbe270f8b43356e33c19da14b30ff773e52..72cecea19cfce5ce4e9627e2adcad594eb79fa93
@@@ -50,61 -50,61 +50,61 @@@ void MainWindow_configureMainWindow(ent
        me.firstRunDialog = i = spawnXonoticFirstRunDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
-       
        // hud_configure dialogs
        i = spawnXonoticHUDExitDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDNotificationDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDAmmoDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDHealthArmorDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDChatDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDModIconsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDPowerupsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDPressedKeysDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDRaceTimerDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDRadarDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDScoreDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDTimerDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDVoteDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        i = spawnXonoticHUDWeaponsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
        i = spawnXonoticHUDCenterprintDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
-       
        // dialogs used by settings
        me.userbindEditDialog = i = spawnXonoticUserbindEditDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        me.cvarsDialog = i = spawnXonoticCvarsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
-       
        // dialog used by singleplayer
        me.winnerDialog = i = spawnXonoticWinnerDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
-       
        // dialog used by multiplayer/join
        me.serverInfoDialog = i = spawnXonoticServerInfoDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
-       
        // dialogs used by multiplayer/create
        me.mapInfoDialog = i = spawnXonoticMapInfoDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        me.advancedDialog = i = spawnXonoticAdvancedDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
        me.mutatorsDialog = i = spawnXonoticMutatorsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
  
        // dialogs used by multiplayer/player setup
        me.crosshairDialog = i = spawnXonoticCrosshairDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        me.hudDialog = i = spawnXonoticHUDDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
        me.hudconfirmDialog = i = spawnXonoticHUDConfirmDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        me.modelDialog = i = spawnXonoticModelDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        me.viewDialog = i = spawnXonoticViewDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
        me.weaponsDialog = i = spawnXonoticWeaponsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
  
        // mutator dialogs
        i = spawnXonoticSandboxToolsDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS);
-       
-       
        // miscellaneous dialogs
        i = spawnXonoticTeamSelectDialog();
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
-       
 +      i = spawnXonoticMonsterToolsDialog();
 +      i.configureDialog(i);
 +      me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS);
-       
-       
++
        // main dialogs/windows
        me.mainNexposee = n = spawnXonoticNexposee();
        /*
                i.configureDialog(i);
                n.addItemCentered(n, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
                n.setNexposee(n, i, SKINPOSITION_DIALOG_SINGLEPLAYER, SKINALPHAS_MAINMENU_x, SKINALPHAS_MAINMENU_y);
-               
                i = spawnXonoticMultiplayerDialog();
                i.configureDialog(i);
                n.addItemCentered(n, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
                n.addItemCentered(n, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
                n.setNexposee(n, i, SKINPOSITION_DIALOG_QUIT, SKINALPHAS_MAINMENU_x, SKINALPHAS_MAINMENU_y);
                n.pullNexposee(n, i, eY * (SKINHEIGHT_TITLE * SKINFONTSIZE_TITLE / conheight));
-               
        me.addItem(me, n, '0 0 0', '1 1 0', SKINALPHAS_MAINMENU_z);
        me.moveItemAfter(me, n, NULL);
  
index 8d6da04d34b566a23ffd43bde3d68d715387ff25,19187a39deeab1c16cab1a8b05834d997dcb35d0..e528bdfdb31b7b6e78f2503380d68c1b84f5285d
@@@ -70,14 -70,7 +70,7 @@@ float autocvar_ekg
  #define autocvar_fraglimit_override cvar("fraglimit_override")
  float autocvar_g_allow_oldnexbeam;
  float autocvar_g_antilag;
- float autocvar_g_antilag_bullets;
  float autocvar_g_antilag_nudge;
- float autocvar_g_arena_maxspawned;
- float autocvar_g_arena_point_leadlimit;
- float autocvar_g_arena_point_limit;
- float autocvar_g_arena_roundbased;
- float autocvar_g_arena_round_timelimit;
- float autocvar_g_arena_warmup;
  float autocvar_g_balance_armor_blockpercent;
  float autocvar_g_balance_armor_limit;
  float autocvar_g_balance_armor_regen;
@@@ -730,7 -723,6 +723,7 @@@ float autocvar_g_chat_teamcolors
  float autocvar_g_chat_tellprivacy;
  float autocvar_g_ctf_allow_vehicle_carry;
  float autocvar_g_ctf_allow_vehicle_touch;
 +float autocvar_g_ctf_allow_monster_touch;
  float autocvar_g_ctf_throw;
  float autocvar_g_ctf_throw_angle_max;
  float autocvar_g_ctf_throw_angle_min;
@@@ -809,6 -801,7 +802,7 @@@ float autocvar_g_domination_point_leadl
  float autocvar_g_domination_point_rate;
  float autocvar_g_domination_teams_override;
  float autocvar_g_forced_respawn;
+ float autocvar_g_respawn_delay_max;
  string autocvar_g_forced_team_blue;
  string autocvar_g_forced_team_otherwise;
  string autocvar_g_forced_team_pink;
@@@ -1228,34 -1221,10 +1222,34 @@@ float autocvar_physics_ode
  float autocvar_g_physical_items;
  float autocvar_g_physical_items_damageforcescale;
  float autocvar_g_physical_items_reset;
 +float autocvar_g_monsters;
 +float autocvar_g_monsters_think_delay;
 +float autocvar_g_monsters_max;
 +float autocvar_g_monsters_max_perplayer;
 +float autocvar_g_monsters_target_range;
 +float autocvar_g_monsters_target_infront;
 +float autocvar_g_monsters_attack_range;
 +float autocvar_g_monsters_score_kill;
 +float autocvar_g_monsters_score_spawned;
 +float autocvar_g_monsters_typefrag;
 +float autocvar_g_monsters_owners;
 +float autocvar_g_monsters_miniboss_chance;
 +float autocvar_g_monsters_miniboss_healthboost;
 +float autocvar_g_monsters_drop_time;
 +float autocvar_g_monsters_spawnshieldtime;
 +float autocvar_g_monsters_teams;
 +float autocvar_g_monsters_respawn_delay;
 +float autocvar_g_monsters_respawn;
 +float autocvar_g_monsters_armor_blockpercent;
  float autocvar_g_touchexplode_radius;
  float autocvar_g_touchexplode_damage;
  float autocvar_g_touchexplode_edgedamage;
  float autocvar_g_touchexplode_force;
 +float autocvar_g_invasion_round_timelimit;
 +#define autocvar_g_invasion_round_limit cvar("g_invasion_round_limit")
 +float autocvar_g_invasion_warmup;
 +float autocvar_g_invasion_monster_count;
 +float autocvar_g_invasion_zombies_only;
  #define autocvar_g_bloodloss cvar("g_bloodloss")
  float autocvar_g_random_gravity_negative_chance;
  float autocvar_g_random_gravity_min;
diff --combined qcsrc/server/bot/aim.qc
index 3dd0b84543b6c83f0cdad70b90ca0a35762a6ce1,3467e2b395d61c7c1f28357fe0460b0a97011394..61f4ab5e8f307b8bc421b4f250090c5169652c0e
@@@ -111,7 -111,7 +111,7 @@@ float bot_shouldattack(entity e
                        return FALSE;
        }
  
 -      if(e.freezetag_frozen)
 +      if(e.frozen)
                return FALSE;
  
        // If neither player has ball then don't attack unless the ball is on the
                return FALSE;
        if(e.flags & FL_NOTARGET)
                return FALSE;
-               
        checkentity = e;
        if(MUTATOR_CALLHOOK(BotShouldAttack))
                return FALSE;
-               
        return TRUE;
  }
  
index 5dba8fc43e2109b2122f892ddd5cb488f0b78113,d803602ad6ccd6c229e5b61ffa20f4dfc976b6ae..ebb9ec5285820f5e7ee5f396a36c01c33cf96b00
@@@ -167,9 -167,7 +167,9 @@@ void PutObserverInServer (void
        MUTATOR_CALLHOOK(MakePlayerObserver);
  
        Portal_ClearAll(self);
-       
 +      Unfreeze(self);
-       
++
        if(self.alivetime)
        {
                if(!warmup_stage)
        }
  
        if(self.vehicle)
-               vehicles_exit(VHEF_RELESE);         
+               vehicles_exit(VHEF_RELESE);
  
        WaypointSprite_PlayerDead();
  
        accuracy_resend(self);
  
        self.spectatortime = time;
-       
        self.classname = "observer";
        self.iscreature = FALSE;
        self.teleportable = TELEPORT_SIMPLE;
@@@ -540,9 -538,6 +540,9 @@@ void PutClientInServer (void
                self.event_damage = PlayerDamage;
  
                self.bot_attack = TRUE;
 +              self.monster_attack = TRUE;
 +              
 +              self.spider_slowness = 0;
  
                self.statdraintime = time + 5;
                self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = 0;
  
                //stuffcmd(self, "chase_active 0");
                //stuffcmd(self, "set viewsize $tmpviewsize \n");
-               
                target_voicescript_clear(self);
  
                // reset fields the weapons may use
                                self.target = s;
                        activator = world;
                self = oldself;
 +              
 +              Unfreeze(self);
  
                spawn_spot = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
@@@ -773,7 -766,7 +773,7 @@@ void ClientKill_Now(
            if(!self.killindicator_teamchange)
            {
              self.vehicle_health = -1;
-             Damage(self, self, self, 1 , DEATH_KILL, self.origin, '0 0 0');           
+             Damage(self, self, self, 1 , DEATH_KILL, self.origin, '0 0 0');
            }
        }
  
@@@ -940,8 -933,8 +940,8 @@@ void ClientKill (void
  {
        if(gameover) return;
        if(self.player_blocked) return;
 -      if(self.freezetag_frozen) return;
 +      if(self.frozen) return;
-       
        ClientKill_TeamChange(0);
  }
  
@@@ -1284,14 -1277,12 +1284,14 @@@ void ClientDisconnect (void
  
        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(self);
 +      
 +      Unfreeze(self);
  
        RemoveGrapplingHook(self);
  
@@@ -1525,7 -1516,7 +1525,7 @@@ void player_powerups (void
                        self.superweapons_finished = 0;
                }
        }
-       
        if(autocvar_g_nodepthtestplayers)
                self.effects = self.effects | EF_NODEPTHTEST;
  
@@@ -1609,10 -1600,10 +1609,10 @@@ void player_regen (void
        limita = limita * limit_mod;
        //limitf = limitf * limit_mod;
  
 -      if(g_ca)
 +      if(g_ca || g_invasion)
                rot_mod = 0;
  
 -      if (!g_minstagib && !g_ca && (!g_lms || autocvar_g_lms_regenerate))
 +      if (!g_minstagib && !g_ca && !g_invasion && (!g_lms || autocvar_g_lms_regenerate))
        {
                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);
@@@ -1734,14 -1725,12 +1734,14 @@@ void SpectateCopy(entity spectatee) 
        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;
        if(spectatee.vehicle)
          self.vehicle_reload2 = spectatee.vehicle_reload2;
  
          msg_entity = self;
-         
          WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
              WriteAngle(MSG_ONE,  spectatee.v_angle_x);
              WriteAngle(MSG_ONE,  spectatee.v_angle_y);
              WriteAngle(MSG_ONE,  spectatee.v_angle_z);
  
          //WriteByte (MSG_ONE, SVC_SETVIEW);
-         //    WriteEntity(MSG_ONE, self);            
+         //    WriteEntity(MSG_ONE, self);
          //makevectors(spectatee.v_angle);
-         //setorigin(self, spectatee.origin - v_forward * 400 + v_up * 300);*/    
+         //setorigin(self, spectatee.origin - v_forward * 400 + v_up * 300);*/
      }
  }
  
  float SpectateUpdate() {
        if(!self.enemy)
-           return 0;           
+           return 0;
  
        if (self == self.enemy)
                return 0;
@@@ -1831,13 -1820,13 +1831,13 @@@ entity CA_SpectateNext(entity start) 
        if (start.team == self.team) {
                return start;
        }
-       
        other = start;
        // continue from current player
        while(other && other.team != self.team) {
                other = find(other, classname, "player");
        }
-       
        if (!other) {
                // restart from begining
                other = find(other, classname, "player");
                        other = find(other, classname, "player");
                }
        }
-       
        return other;
  }
  
@@@ -2154,7 -2143,7 +2154,7 @@@ void PlayerUseKey(
          vehicles_exit(VHEF_NORMAL);
          return;
        }
-       
        // a use key was pressed; call handlers
        MUTATOR_CALLHOOK(PlayerUseKey);
  }
@@@ -2246,16 -2235,6 +2246,16 @@@ void PlayerPreThink (void
                return;
  #endif
  
 +      if(self.frozen == 2)
 +      {
 +              self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
 +              self.health = max(1, self.revive_progress * autocvar_g_balance_health_start);
 +              self.iceblock.alpha = 1 - self.revive_progress;
 +
 +              if(self.revive_progress >= 1)
 +                      Unfreeze(self);
 +      }
 +
        MUTATOR_CALLHOOK(PlayerPreThink);
  
        if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
                                if(frametime)
                                        player_anim();
                                button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE);
-                               
                                if (self.deadflag == DEAD_DYING)
                                {
-                                       if(self.respawn_flags & RESPAWN_FORCE)
+                                       if((self.respawn_flags & RESPAWN_FORCE) && !autocvar_g_respawn_delay_max)
                                                self.deadflag = DEAD_RESPAWNING;
                                        else if(!button_pressed)
                                                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(time > self.respawn_time)
                                        {
                                                self.respawn_time = time + 1; // only retry once a second
+                                               self.respawn_time_max = self.respawn_time;
                                                respawn();
                                        }
                                }
  
                                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;
                                else
                                        self.stat_respawn_time = self.respawn_time;
                        }
                        do_crouch = 0;
                if(self.vehicle)
                        do_crouch = 0;
 -              if(self.freezetag_frozen)
 +              if(self.frozen)
                        do_crouch = 0;
                if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
                        do_crouch = 0;
  
                if(frametime)
                        player_anim();
-               
                // secret status
                secrets_setstatus();
-               
 +              // monsters status
 +              monsters_setstatus();
-               
++
                self.dmg_team = max(0, self.dmg_team - autocvar_g_teamdamage_resetspeed * frametime);
  
                //self.angles_y=self.v_angle_y + 90;   // temp
@@@ -2597,7 -2578,7 +2602,7 @@@ void PlayerPostThink (void
                        return;         // intermission or finale
                GetPressedKeys();
        }
-       
  #ifdef TETRIS
        }
  #endif
index 95c41d792f530c1ee83c646b3dcd87c3ac47b42a,edad6d63f845881338547c06bc59d2a4285a0ce7..8c7bd126e96c87ec7801ced880dd8dd293b9afb6
@@@ -19,19 -19,16 +19,19 @@@ When you press the jump ke
  */
  void PlayerJump (void)
  {
 +      if(self.frozen)
 +              return; // no jumping in freezetag when frozen
 +              
        float doublejump = FALSE;
  
        player_multijump = doublejump;
        if(MUTATOR_CALLHOOK(PlayerJump))
                return;
-               
        doublejump = player_multijump;
  
        float mjumpheight;
-       
        if (autocvar_sv_doublejump)
        {
                tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self);
        self.flags &= ~FL_JUMPRELEASED;
  
        animdecide_setaction(self, ANIMACTION_JUMP, TRUE);
-       
        if(autocvar_g_jump_grunt)
                PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
  
@@@ -520,9 -517,9 +520,9 @@@ void PM_Accelerate(vector wishdir, floa
        }
        else
                vel_perpend = vel_perpend * max(0, 1 - frametime * wishspeed * sidefric);
-       
        vel_xy = vel_straight * wishdir + vel_perpend;
-       
        if(speedclamp >= 0)
        {
                float vel_xy_preclamp;
@@@ -636,7 -633,7 +636,7 @@@ void SV_PlayerPhysics(
        string c;
  
        WarpZone_PlayerPhysics_FixVAngle();
-       
        maxspd_mod = 1;
        if(self.ballcarried)
                if(g_nexball)
                        return;
                bot_think();
        }
-       
        self.items &= ~IT_USING_JETPACK;
  
        if(IS_PLAYER(self))
        self.disableclientprediction = 0;
        if(time < self.ladder_time)
                self.disableclientprediction = 1;
 +              
 +      if(time < self.spider_slowness)
 +      {
 +              self.stat_sv_maxspeed *= 0.5; // half speed while slow from spider
 +              self.stat_sv_airspeedlimit_nonqw *= 0.5;
 +      }
 +              
 +      if(self.frozen)
 +      {
 +              if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
 +              {
 +                      self.movement_x = bound(-5, self.movement_x, 5);
 +                      self.movement_y = bound(-5, self.movement_y, 5);
 +                      self.movement_z = bound(-5, self.movement_z, 5);
 +              }
 +              else
 +                      self.movement = '0 0 0';
 +              self.disableclientprediction = 1;
 +      }
  
        MUTATOR_CALLHOOK(PlayerPhysics);
  
                        PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
                }
        }
 -      else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.freezetag_frozen)
 +      else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.frozen)
        {
                //makevectors(self.v_angle_y * '0 1 0');
                makevectors(self.v_angle);
index 61036277d38ab8aac218cd9b7cd517f04e7c92e9,eb3e81fbb280a7d226e65f2554b22df8c5eab99d..6f16491cf7bcb3aec857fa38e3a95fdea9a7aeea
@@@ -245,7 -245,7 +245,7 @@@ void player_anim (void
                else
                        deadbits = ANIMSTATE_DEAD2;
        float animbits = deadbits;
 -      if(self.freezetag_frozen)
 +      if(self.frozen)
                animbits |= ANIMSTATE_FROZEN;
        if(self.crouch)
                animbits |= ANIMSTATE_DUCK;
@@@ -637,6 -637,10 +637,10 @@@ void PlayerDamage (entity inflictor, en
                        self.respawn_time = ceil((time + sdelay) / waves) * waves;
                else
                        self.respawn_time = time + sdelay;
+               if(autocvar_g_respawn_delay_max > sdelay)
+                       self.respawn_time_max = time + autocvar_g_respawn_delay_max;
+               else
+                       self.respawn_time_max = self.respawn_time;
                if((sdelay + waves >= 5.0) && (self.respawn_time - time > 1.75))
                        self.respawn_countdown = 10; // first number to count down from is 10
                else
index 87b670b909d781385ea8e96722a9441f3cdbedcb,88397a88021feff495ada18a2d140c0412849c6e..ed4fa901f1708c406133a388ecdba8d791c81638
@@@ -307,7 -307,7 +307,7 @@@ float W_IsWeaponThrowable(float w
                return 0;
      if(w == 0)
          return 0;
-       
        wa = W_AmmoItemCode(w);
        if(start_weapons & WepSet_FromWeapon(w))
        {
@@@ -330,8 -330,6 +330,8 @@@ void W_ThrowWeapon(vector velo, vector 
        w = self.weapon;
        if (w == 0)
                return; // just in case
 +      if(self.frozen)
 +              return;
        if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
                return;
        if(!autocvar_g_weapon_throwable)
  
        W_SwitchWeapon_Force(self, w_getbestweapon(self));
        a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);
-       
        if (!a) return;
        Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
  }
@@@ -360,7 -358,7 +360,7 @@@ float forbidWeaponUse(
                return 1;
        if(self.player_blocked)
                return 1;
 -      if(self.freezetag_frozen)
 +      if(self.frozen)
                return 1;
        return 0;
  }
@@@ -437,7 -435,7 +437,7 @@@ void W_WeaponFrame(
                        self.switchingweapon = self.switchweapon;
  
                        entity oldwep = get_weaponinfo(self.weapon);
-                       
  #ifndef INDEPENDENT_ATTACK_FINISHED
                        if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
                        {
index 46082b0d67576cb9b052c39a9b6afac5462bc507,8d72a798bab35d21c183711337a95095bcced570..c0fd3dfbd689ef4becf0290ff00509b5f6b4f016
@@@ -163,7 -163,7 +163,7 @@@ void W_SetupShot_Dir_ProjectileSize_Ran
        // track max damage
        if(accuracy_canbegooddamage(ent))
                accuracy_add(ent, ent.weapon, maxdamage, 0);
 -
 +              
        W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
  
        if(ent.weaponentity.movedir_x > 0)
@@@ -541,7 -541,7 +541,7 @@@ void CL_Weaponentity_Think(
        }
  
        self.angles = '0 0 0';
-       
        float f = (self.owner.weapon_nextthink - time);
        if (self.state == WS_RAISE && !intermission_running)
        {
@@@ -667,7 -667,7 +667,7 @@@ float client_hasweapon(entity cl, floa
                complain = 0;
        if(complain)
                self.hasweapon_complain_spam = time + 0.2;
-               
        if(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
                complain = 0;
  
@@@ -851,7 -851,7 +851,7 @@@ float weapon_prepareattack_checkammo(fl
                {
                        W_SwitchToOtherWeapon(self);
                }
-               
                return FALSE;
        }
        return TRUE;
@@@ -1129,7 -1129,7 +1129,7 @@@ vector W_CalculateProjectileSpread(vect
        if(spread <= 0)
                return forward;
        sstyle = autocvar_g_projectiles_spread_style;
-       
        if(sstyle == 0)
        {
                // this is the baseline for the spread value!
index 011a12716cdab435f89d594a72b98043394ab0e0,363bb7c4c33bad339e121ae9e8be4456f5b546c8..404e514e46ad9af6e3c4b25fe4d53cba71528f68
@@@ -39,13 -39,13 +39,13 @@@ void ClientCommand_autoswitch(float req
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2autoswitch^7\n");
                case CMD_REQUEST_USAGE:
                {
                        sprint(self, "\nUsage:^3 cmd autoswitch selection\n");
-                       sprint(self, "  Where 'selection' controls if autoswitch is on or off.\n"); 
+                       sprint(self, "  Where 'selection' controls if autoswitch is on or off.\n");
                        return;
                }
        }
@@@ -61,7 -61,7 +61,7 @@@ void ClientCommand_checkfail(float requ
                        self.checkfail = 1;
                        return; // never fall through to usage
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2checkfail^7\n");
                case CMD_REQUEST_USAGE:
@@@ -84,27 -84,27 +84,27 @@@ void ClientCommand_clientversion(float 
                                if(IS_CLIENT(self))
                                {
                                        self.version = ((argv(1) == "$gameversion") ? 1 : stof(argv(1)));
-                                       
                                        if(self.version < autocvar_gameversion_min || self.version > autocvar_gameversion_max)
                                        {
                                                self.version_mismatch = 1;
                                                ClientKill_TeamChange(-2); // observe
-                                       } 
-                                       else if(autocvar_g_campaign || autocvar_g_balance_teams) 
+                                       }
+                                       else if(autocvar_g_campaign || autocvar_g_balance_teams)
                                        {
                                                //JoinBestTeam(self, FALSE, TRUE);
-                                       } 
-                                       else if(teamplay && !autocvar_sv_spectate && !(self.team_forced > 0)) 
+                                       }
+                                       else if(teamplay && !autocvar_sv_spectate && !(self.team_forced > 0))
                                        {
                                                self.classname = "observer"; // really?
                                                stuffcmd(self, "menu_showteamselect\n");
                                        }
                                }
-                               
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2clientversion^7\n");
                case CMD_REQUEST_USAGE:
@@@ -124,13 -124,13 +124,13 @@@ void ClientCommand_mv_getpicture(float 
                {
                        if(argv(1) != "")
                        {
-                               if(intermission_running)                                
+                               if(intermission_running)
                                        MapVote_SendPicture(stof(argv(1)));
  
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2mv_getpicture^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void ClientCommand_join(float request) 
+ void ClientCommand_join(float request)
  {
        switch(request)
        {
                {
                        if(IS_CLIENT(self))
                        {
-                               if(!IS_PLAYER(self) && !lockteams && !g_arena)
+                               if(!IS_PLAYER(self) && !lockteams)
                                {
-                                       if(nJoinAllowed(self)) 
+                                       if(nJoinAllowed(self))
                                        {
                                                if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
  
                                                Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname);
                                                PutClientInServer();
                                        }
-                                       else 
+                                       else
                                        {
                                                //player may not join because of g_maxplayers is set
                                                Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_PREVENT);
                        }
                        return; // never fall through to usage
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
        }
  }
  
 +void ClientCommand_mobedit(float request, float argc)
 +{
 +      switch(request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      makevectors(self.v_angle);
 +                      WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self);
 +                      
 +                      if(!(trace_ent.flags & FL_MONSTER)) { sprint(self, "You need to aim at your monster to edit its properties.\n"); return; }
 +                      if(trace_ent.realowner != self) { sprint(self, "That monster does not belong to you.\n"); return; }
 +                      
 +                      switch(argv(1))
 +                      {
 +                              case "skin": if(trace_ent.monsterid != MON_MAGE) { trace_ent.skin = stof(argv(2)); } return;
 +                              case "movetarget": trace_ent.monster_moveflags = stof(argv(2)); return;
 +                      }
 +              }
 +              default:
 +                      sprint(self, "Incorrect parameters for ^2mobedit^7\n");
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      sprint(self, "\nUsage:^3 cmd mobedit [argument]\n");
 +                      sprint(self, "  Where 'argument' can be skin or movetarget.\n");
 +                      return;
 +              }
 +      }
 +}
 +      
 +void ClientCommand_mobkill(float request)
 +{
 +      switch(request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      makevectors(self.v_angle);
 +                      WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self);
 +                      
 +                      if(trace_ent.flags & FL_MONSTER)
 +                      {
 +                              if(trace_ent.realowner != self)
 +                              {
 +                                      sprint(self, "That monster does not belong to you.\n");
 +                                      return;
 +                              }
 +                              sprint(self, strcat("Your pet '", trace_ent.monster_name, "' has been brutally mutilated.\n"));
 +                              Damage (trace_ent, world, world, trace_ent.health + trace_ent.max_health + 200, DEATH_KILL, trace_ent.origin, '0 0 0');
 +                              return;
 +                      }
 +                      else
 +                              sprint(self, "You need to aim at your monster to kill it.\n");
 +                      
 +                      return;
 +              }
 +      
 +              default:
 +                      sprint(self, "Incorrect parameters for ^2mobkill^7\n");
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      sprint(self, "\nUsage:^3 cmd mobkill\n");
 +                      sprint(self, "  Aim at your monster to kill it.\n");
 +                      return;
 +              }
 +      }
 +}
 +
 +void ClientCommand_mobspawn(float request, float argc)
 +{
 +      switch(request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      entity e;
 +                      string tospawn;
 +                      float moveflag, i;
 +                      
 +                      moveflag = (argv(2) ? stof(argv(2)) : 1); // follow owner if not defined
 +                      tospawn = strtolower(argv(1));
 +                      
 +                      if(tospawn == "list")
 +                      {
 +                              sprint(self, monsterlist_reply);
 +                              return;
 +                      }
 +                      
 +                      if(tospawn == "random")
 +                      {
 +                              RandomSelection_Init();
 +                              for(i = MON_FIRST; i <= MON_LAST; ++i)
 +                                      RandomSelection_Add(world, 0, (get_monsterinfo(i)).netname, 1, 1);
 +                              
 +                              tospawn = RandomSelection_chosen_string;
 +                      }
 +                      
 +                      if(autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { sprint(self, "Monster spawning is disabled.\n"); }
 +                      else if(!IS_PLAYER(self)) { sprint(self, "You can't spawn monsters while spectating.\n"); }
 +                      else if(g_invasion) { sprint(self, "You can't spawn monsters during an invasion!\n"); }
 +                      else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); }
 +                      else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); }
 +                      else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); }
 +                      else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); }
 +                      else if(self.monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); }
 +                      else if(totalspawned >= autocvar_g_monsters_max) { sprint(self, "The global maximum monster count has been reached, kill some before trying to spawn any more.\n"); }
 +                      else // all worked out, so continue
 +                      {
 +                              self.monstercount += 1;
 +                              totalspawned += 1;
 +                      
 +                              makevectors(self.v_angle);
 +                              WarpZone_TraceBox (CENTER_OR_VIEWOFS(self), PL_MIN, PL_MAX, CENTER_OR_VIEWOFS(self) + v_forward * 150, TRUE, self);
 +                              //WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 150, MOVE_NORMAL, self);
 +                      
 +                              e = spawnmonster(tospawn, 0, self, self, trace_endpos, FALSE, moveflag);
 +                              
 +                              sprint(self, strcat("Spawned ", e.monster_name, "\n"));
 +                      }
 +                      
 +                      return;
 +              }
 +      
 +              default:
 +                      sprint(self, "Incorrect parameters for ^2mobspawn^7\n");
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      sprint(self, "\nUsage:^3 cmd mobspawn monster\n");
 +                      sprint(self, "  See 'cmd mobspawn list' for available arguments.\n");
 +                      sprint(self, "  Argument 'random' spawns a randomly selected monster.\n");
 +                      return;
 +              }
 +      }
 +}
 +
  void ClientCommand_ready(float request) // todo: anti-spam for toggling readyness
  {
        switch(request)
                        }
                        return; // never fall through to usage
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -369,7 -237,7 +369,7 @@@ void ClientCommand_say(float request, f
                        if(argc >= 2) { Say(self, FALSE, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); }
                        return; // never fall through to usage
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -389,7 -257,7 +389,7 @@@ void ClientCommand_say_team(float reque
                        if(argc >= 2) { Say(self, TRUE, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); }
                        return; // never fall through to usage
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -412,10 -280,10 +412,10 @@@ void ClientCommand_selectteam(float req
                                {
                                        if(teamplay)
                                                if(self.team_forced <= 0)
-                                                       if (!lockteams) 
+                                                       if (!lockteams)
                                                        {
                                                                float selection;
-                                                               
                                                                switch(argv(1))
                                                                {
                                                                        case "red": selection = NUM_TEAM_1; break;
                                                                        case "yellow": selection = NUM_TEAM_3; break;
                                                                        case "pink": selection = NUM_TEAM_4; break;
                                                                        case "auto": selection = (-1); break;
-                                                                       
                                                                        default: selection = 0; break;
                                                                }
-                                                               
                                                                if(selection)
                                                                {
                                                                        if(self.team == selection && self.deadflag == DEAD_NO)
                                        else
                                                sprint(self, "^7selectteam can only be used in teamgames\n");
                                }
-                               return; 
+                               return;
                        }
                }
  
@@@ -472,7 -340,7 +472,7 @@@ void ClientCommand_selfstuff(float requ
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2selectteam^7\n");
                case CMD_REQUEST_USAGE:
@@@ -494,19 -362,19 +494,19 @@@ void ClientCommand_sentcvar(float reque
                        {
                                //float tokens;
                                string s;
-                               
                                if(argc == 2) // undefined cvar: use the default value on the server then
                                {
                                        s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
                                        tokenize_console(s);
                                }
-                               
                                GetCvars(1);
-                               
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2sentcvar^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void ClientCommand_spectate(float request) 
+ void ClientCommand_spectate(float request)
  {
        switch(request)
        {
                {
                        if(IS_CLIENT(self))
                        {
-                               if(g_arena) { return; } 
                                if(g_lms)
                                {
                                        if(self.lms_spectate_warning)
                                                return;
                                        }
                                }
-                               
-                               if(IS_PLAYER(self) && autocvar_sv_spectate == 1) 
+                               if(IS_PLAYER(self) && autocvar_sv_spectate == 1)
                                        ClientKill_TeamChange(-2); // observe
  
                                // in CA, allow a dead player to move to spectators (without that, caplayer!=0 will be moved back to the player list)
                        }
                        return; // never fall through to usage
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -580,7 -447,7 +579,7 @@@ void ClientCommand_suggestmap(float req
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2suggestmap^7\n");
                case CMD_REQUEST_USAGE:
@@@ -602,7 -469,7 +601,7 @@@ void ClientCommand_tell(float request, 
                        {
                                entity tell_to = GetIndexedEntity(argc, 1);
                                float tell_accepted = VerifyClientEntity(tell_to, TRUE, FALSE);
-                               
                                if(tell_accepted > 0) // the target is a real client
                                {
                                        if(tell_to != self) // and we're allowed to send to them :D
                                        }
                                        else { print_to(self, "You can't ^2tell^7 a message to yourself."); return; }
                                }
-                               else if(argv(1) == "#0") 
-                               { 
+                               else if(argv(1) == "#0")
+                               {
                                        trigger_magicear_processmessage_forallears(self, -1, world, substring(command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token)));
                                        return;
                                }
                                else { print_to(self, strcat("tell: ", GetClientErrorString(tell_accepted, argv(1)), ".")); return; }
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2tell^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void ClientCommand_voice(float request, float argc, string command) 
+ void ClientCommand_voice(float request, float argc, string command)
  {
        switch(request)
        {
                                        VoiceMessage(argv(1), substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
                                else
                                        VoiceMessage(argv(1), "");
-                                       
                                return;
                        }
                }
-                       
                default:
                        sprint(self, "Incorrect parameters for ^2voice^7\n");
                case CMD_REQUEST_USAGE:
@@@ -669,10 -536,10 +668,10 @@@ void ClientCommand_(float request
        {
                case CMD_REQUEST_COMMAND:
                {
-                       
                        return; // never fall through to usage
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
        CLIENT_COMMAND("clientversion", ClientCommand_clientversion(request, arguments), "Release version of the game") \
        CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(request, arguments), "Retrieve mapshot picture from the server") \
        CLIENT_COMMAND("join", ClientCommand_join(request), "Become a player in the game") \
 +      CLIENT_COMMAND("mobedit", ClientCommand_mobedit(request, arguments), "Edit your monster's properties") \
 +      CLIENT_COMMAND("mobkill", ClientCommand_mobkill(request), "Kills your monster") \
 +      CLIENT_COMMAND("mobspawn", ClientCommand_mobspawn(request, arguments), "Spawn monsters infront of yourself") \
        CLIENT_COMMAND("ready", ClientCommand_ready(request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
        CLIENT_COMMAND("say", ClientCommand_say(request, arguments, command), "Print a message to chat to all players") \
        CLIENT_COMMAND("say_team", ClientCommand_say_team(request, arguments, command), "Print a message to chat to all team mates") \
        CLIENT_COMMAND("tell", ClientCommand_tell(request, arguments, command), "Send a message directly to a player") \
        CLIENT_COMMAND("voice", ClientCommand_voice(request, arguments, command), "Send voice message via sound") \
        /* nothing */
-       
  void ClientCommand_macro_help()
  {
        #define CLIENT_COMMAND(name,function,description) \
                { sprint(self, "  ^2", name, "^7: ", description, "\n"); }
-               
        CLIENT_COMMANDS(0, 0, "")
        #undef CLIENT_COMMAND
-       
        return;
  }
  
@@@ -726,10 -590,10 +725,10 @@@ float ClientCommand_macro_command(floa
  {
        #define CLIENT_COMMAND(name,function,description) \
                { if(name == strtolower(argv(0))) { function; return TRUE; } }
-               
        CLIENT_COMMANDS(CMD_REQUEST_COMMAND, argc, command)
        #undef CLIENT_COMMAND
-       
        return FALSE;
  }
  
@@@ -737,21 -601,21 +736,21 @@@ float ClientCommand_macro_usage(float a
  {
        #define CLIENT_COMMAND(name,function,description) \
                { if(name == strtolower(argv(1))) { function; return TRUE; } }
-               
        CLIENT_COMMANDS(CMD_REQUEST_USAGE, argc, "")
        #undef CLIENT_COMMAND
-       
        return FALSE;
  }
  
  void ClientCommand_macro_write_aliases(float fh)
  {
        #define CLIENT_COMMAND(name,function,description) \
-               { CMD_Write_Alias("qc_cmd_cmd", name, description); } 
-               
+               { CMD_Write_Alias("qc_cmd_cmd", name, description); }
        CLIENT_COMMANDS(0, 0, "")
        #undef CLIENT_COMMAND
-       
        return;
  }
  
@@@ -767,17 -631,17 +766,17 @@@ void SV_ParseClientCommand(string comma
                return;
  
        float argc = tokenize_console(command);
-       
        // for the mutator hook system
        cmd_name = strtolower(argv(0));
        cmd_argc = argc;
        cmd_string = command;
-       
        // Guide for working with argc arguments by example:
        // argc:   1    - 2      - 3     - 4
-       // argv:   0    - 1      - 2     - 3 
+       // argv:   0    - 1      - 2     - 3
        // cmd     vote - master - login - password
-       
        // for floodcheck
        switch(strtolower(argv(0)))
        {
                case "prespawn": break; // handled by engine in host_cmd.c
                case "sentcvar": break; // handled by server in this file
                case "spawn": break; // handled by engine in host_cmd.c
-               
-               default: 
+               default:
                        if(SV_ParseClientCommand_floodcheck())
                                break; // "TRUE": continue, as we're not flooding yet
                        else
                                return; // "FALSE": not allowed to continue, halt // print("^1ERROR: ^7ANTISPAM CAUGHT: ", command, ".\n");
        }
-       
        /* NOTE: should this be disabled? It can be spammy perhaps, but hopefully it's okay for now */
-       if(argv(0) == "help") 
+       if(argv(0) == "help")
        {
-               if(argc == 1) 
+               if(argc == 1)
                {
                        sprint(self, "\nClient networked commands:\n");
                        ClientCommand_macro_help();
-                       
                        sprint(self, "\nCommon networked commands:\n");
                        CommonCommand_macro_help(self);
-                       
                        sprint(self, "\nUsage:^3 cmd COMMAND...^7, where possible commands are listed above.\n");
                        sprint(self, "For help about a specific command, type cmd help COMMAND\n");
                        return;
-               } 
+               }
                else if(CommonCommand_macro_usage(argc, self)) // Instead of trying to call a command, we're going to see detailed information about it
                {
                        return;
                {
                        return;
                }
-       } 
+       }
        else if(MUTATOR_CALLHOOK(SV_ParseClientCommand))
        {
                return; // handled by a mutator
        }
-       else if(CheatCommand(argc)) 
+       else if(CheatCommand(argc))
        {
                return; // handled by server/cheats.qc
        }
index 2f4c11f7d6af3b36519d3632af2e6e3c5053824c,a55e0e0cd28706f7199c06db12aae058a02c1dbc..5e92523f7caf9454a7f64568620fe17aa9982880
@@@ -9,12 -9,12 +9,12 @@@
  // without using any extra processing time.
  
  // See common.qc for their proper commands
-       
  string getrecords(float page) // 50 records per page
- {     
+ {
        float rec = 0, r, i;
        string h, s;
-       
        s = "";
  
        if (g_ctf)
                        if (MapInfo_Get_ByID(i))
                        {
                                r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
-                               
                                if (!r)
                                        continue;
-                                       
                                // TODO: uid2name
                                h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
                                s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
                        if (MapInfo_Get_ByID(i))
                        {
                                r = race_readTime(MapInfo_Map_bspname, 1);
-                               
                                if (!r)
                                        continue;
-                                       
                                h = race_readName(MapInfo_Map_bspname, 1);
                                s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
                                ++rec;
                        if (MapInfo_Get_ByID(i))
                        {
                                r = race_readTime(MapInfo_Map_bspname, 1);
-                               
                                if (!r)
                                        continue;
-                                       
                                h = race_readName(MapInfo_Map_bspname, 1);
                                s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
                                ++rec;
@@@ -91,10 -91,10 +91,10 @@@ string getrankings(
        for (i = 1; i <= RANKINGS_CNT; ++i)
        {
                t = race_readTime(map, i);
-               
                if (t == 0)
                        continue;
-                       
                n = race_readName(map, i);
                p = count_ordinal(i);
                s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
@@@ -112,7 -112,7 +112,7 @@@ string getladder(
  {
        float i, j, k, uidcnt = 0, thiscnt;
        string s, temp_s, rr, myuid, thisuid;
-       
        if(g_cts)
                rr = CTS_RECORD;
        else
                                // LADDER_CNT+1 = total points
  
                                temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
-                               
                                if(temp_s == "")
                                {
                                        db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
                                        ++uidcnt;
-                                       
                                        for(j = 0; j <= LADDER_CNT + 1; ++j)
                                        {
                                                if(j != LADDER_CNT + 1)
                                                top_uids[k] = top_uids[k-1];
                                                top_scores[k] = top_scores[k-1];
                                        }
-                                       
                                        top_uids[j] = thisuid;
                                        top_scores[j] = thiscnt;
                                        break;
                        }
                }
        }
-       
        s = "^3-----------------------\n\n";
-       
        s = strcat(s, "Pos ^3|");
        s = strcat(s, " ^7Total  ^3|");
-       
        for(i = 1; i <= LADDER_CNT; ++i)
                { s = strcat(s, " ^7", count_ordinal(i), " ^3|"); }
-       
        s = strcat(s, " ^7Speed awards ^3| ^7Name");
        s = strcat(s, "\n^3----+--------");
-       
        for(i = 1; i <= min(9, LADDER_CNT); ++i)
                { s = strcat(s, "+-----"); }
-               
        #if LADDER_CNT > 9
        for(i = 1; i <= LADDER_CNT - 9; ++i)
                { s = strcat(s, "+------"); }
        {
                temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
                tokenize_console(temp_s);
-               
                if(argv(LADDER_CNT+1) == "") // total is 0, skip
                        continue;
-                       
                s = strcat(s, strpad(4, count_ordinal(i+1)), "^3| ^7"); // pos
                s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
-               
                for(j = 1; j <= min(9, LADDER_CNT); ++j)
                        { s = strcat(s, strpad(4, argv(j)), "^3| ^7"); } // 1st, 2nd, 3rd etc cnt
-                       
                #if LADDER_CNT > 9
                for(j = 10; j <= LADDER_CNT; ++j)
                        { s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); } // 1st, 2nd, 3rd etc cnt
@@@ -290,7 -290,7 +290,7 @@@ string getmaplist(
  {
        string maplist = "", col;
        float i, argc;
-       
        argc = tokenize_console(autocvar_g_maplist);
        for(i = 0; i < argc; ++i)
        {
        return sprintf("^7Maps in list: %s\n", maplist);
  }
  
-       
  string getlsmaps()
  {
        string lsmaps = "", col;
        float i, newmaps = 0;
-       
        for(i = 0; i < MapInfo_count; ++i)
        {
                if((MapInfo_Get_ByID(i)) && !(MapInfo_Map_flags & MapInfo_ForbiddenFlags()))
        MapInfo_ClearTemps();
        return sprintf("^7Maps available%s: %s\n", (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps);
  }
 +
 +string getmonsterlist()
 +{
 +      string monsterlist = "", col;
 +      float i;
 +      
 +      for(i = MON_FIRST; i <= MON_LAST; ++i)
 +      {
 +              if(mod(i, 2)) { col = "^2"; }
 +              else { col = "^3"; }
 +              monsterlist = sprintf("%s%s%s ", monsterlist, col, (get_monsterinfo(i)).netname);
 +      }
 +      
 +      return sprintf("^7Monsters available: %s\n", monsterlist);
 +}
index 1f5ffd30585863d487749a735c1345918c271f54,ff07f2458a0608ed5f7f3696f129531da76d9f8d..c98ed3cfed0c94cc1700f8790475f8eb1b04863c
@@@ -74,31 -74,31 +74,31 @@@ void GameCommand_adminmsg(float request
                {
                        entity client;
                        float accepted;
-                       
                        string targets = strreplace(",", " ", argv(1));
                        string original_targets = strreplace(" ", ", ", targets);
                        string admin_message = argv(2);
                        float infobartime = stof(argv(3));
-                       
                        string successful, t;
                        successful = string_null;
-                       
                        if((targets) && (admin_message))
                        {
                                for(;targets;)
                                {
                                        t = car(targets); targets = cdr(targets);
-                                       
                                        // Check to see if the player is a valid target
                                        client = GetFilteredEntity(t);
                                        accepted = VerifyClientEntity(client, TRUE, FALSE);
-                                       
-                                       if(accepted <= 0) 
+                                       if(accepted <= 0)
                                        {
-                                               print("adminmsg: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : ".\n")); 
+                                               print("adminmsg: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : ".\n"));
                                                continue;
                                        }
-                                       
                                        // send the centerprint/console print or infomessage
                                        if(infobartime)
                                        {
                                                centerprint(client, strcat("^3", admin_name(), ":\n^7", admin_message));
                                                sprint(client, strcat("\{1}\{13}^3", admin_name(), "^7: ", admin_message, "\n"));
                                        }
-                                       
                                        successful = strcat(successful, (successful ? ", " : ""), client.netname);
                                        dprint("Message sent to ", client.netname, "\n");
                                        continue;
                                }
-                               
                                if(successful)
                                        bprint("Successfully sent message '", admin_message, "' to ", successful, ".\n");
                                else
                                        print("No players given (", original_targets, ") could receive the message.\n");
-                                       
                                return;
                        }
                }
-               
                default:
                        print("Incorrect parameters for ^2adminmsg^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
 +void GameCommand_butcher(float request)
 +{
 +      switch(request)
 +      {
 +              case CMD_REQUEST_COMMAND:
 +              {
 +                      if(autocvar_g_campaign) { print("This command doesn't work in campaign mode.\n"); return; }
 +                      if(g_invasion) { print("This command doesn't work during an invasion.\n"); return; }
 +              
 +            float removed_count = 0;
 +                      entity head;
 +                      
 +                      FOR_EACH_MONSTER(head)
 +                      {
 +                              monster_remove(head);
 +                              ++removed_count;
 +                      }
 +                      
 +                      FOR_EACH_PLAYER(head)
 +                              head.monstercount = 0;
 +                              
 +                      monsters_total = 0; // reset stats?
 +                      monsters_killed = 0;
 +                              
 +                      totalspawned = 0;
 +                      
 +                      if(removed_count <= 0)
 +                              print("No monsters to kill\n");
 +                      else
 +                              print(sprintf("Killed %d monster%s\n", removed_count, ((removed_count == 1) ? "" : "s")));
 +                              
 +                      return; // never fall through to usage
 +              }
 +                      
 +              default:
 +              case CMD_REQUEST_USAGE:
 +              {
 +                      print("\nUsage:^3 sv_cmd butcher\n");
 +                      print("  No arguments required.\n");
 +                      return;
 +              }
 +      }
 +}
 +
  void GameCommand_allready(float request)
  {
        switch(request)
                        ReadyRestart();
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
  }
  
  void GameCommand_allspec(float request, float argc)
- {     
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                        entity client;
                        string reason = argv(1);
                        float i = 0;
-                       
                        FOR_EACH_REALPLAYER(client)
                        {
                                self = client;
                        else { print("No players found to spectate.\n"); }
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
        }
  }
  
- void GameCommand_anticheat(float request, float argc) 
+ void GameCommand_anticheat(float request, float argc)
  {
        switch(request)
        {
                {
                        entity client = GetIndexedEntity(argc, 1);
                        float accepted = VerifyClientEntity(client, FALSE, FALSE);
-                       
-                       if(accepted > 0) 
+                       if(accepted > 0)
                        {
                                self = client;
                                anticheat_report();
                        }
                        else
                        {
-                               print("anticheat: ", GetClientErrorString(accepted, argv(1)), ".\n"); 
+                               print("anticheat: ", GetClientErrorString(accepted, argv(1)), ".\n");
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2anticheat^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_bbox(float request) 
+ void GameCommand_bbox(float request)
  {
        switch(request)
        {
                                print(" ", ftos(world.absmax_z));
                        else
                                print(" ", ftos(trace_endpos_z));
-                               
                        print("\n");
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -367,7 -323,7 +367,7 @@@ void GameCommand_bot_cmd(float request
                case CMD_REQUEST_COMMAND:
                {
                        entity bot;
-                       
                        if(argv(1) == "reset")
                        {
                                bot_resetqueues();
                                        print(strcat("Error: Can't find bot with the name or id '", argv(1),"' - Did you mistype the command?\n")); // don't return so that usage is shown
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2bot_cmd^7\n");
                case CMD_REQUEST_USAGE:
@@@ -483,13 -439,13 +483,13 @@@ void GameCommand_cointoss(float request
                        string result1 = (argv(2) ? strcat("^7", argv(1), "^3!\n") : "^1HEADS^3!\n");
                        string result2 = (argv(2) ? strcat("^7", argv(2), "^3!\n") : "^4TAILS^3!\n");
                        string choice = ((random() > 0.5) ? result1 : result2);
-                       
                        FOR_EACH_CLIENT(client)
                                centerprint(client, strcat("^3Throwing coin... Result: ", choice));
                        bprint(strcat("^3Throwing coin... Result: ", choice));
                        return;
                }
-               
                default:
                case CMD_REQUEST_USAGE:
                {
        }
  }
  
- void GameCommand_database(float request, float argc) 
+ void GameCommand_database(float request, float argc)
  {
        switch(request)
        {
                                }
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2database^7\n");
                case CMD_REQUEST_USAGE:
  }
  
  void GameCommand_defer_clear(float request, float argc)
- {     
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                {
                        entity client;
                        float accepted;
-                       
                        if(argc >= 2)
                        {
                                client = GetIndexedEntity(argc, 1);
                                accepted = VerifyClientEntity(client, TRUE, FALSE);
-                               
                                if(accepted > 0)
                                {
                                        stuffcmd(client, "defer clear\n");
                                        print("defer clear stuffed to ", client.netname, "\n");
                                }
                                else { print("defer_clear: ", GetClientErrorString(accepted, argv(1)), ".\n"); }
-                               
                                return;
                        }
                }
-               
                default:
                        print("Incorrect parameters for ^2defer_clear^7\n");
                case CMD_REQUEST_USAGE:
  }
  
  void GameCommand_defer_clear_all(float request)
- {     
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                        entity client;
                        float i = 0;
                        float argc;
-                       
                        FOR_EACH_CLIENT(client)
                        {
                                argc = tokenize_console(strcat("defer_clear ", ftos(num_for_edict(client))));
-                               GameCommand_defer_clear(CMD_REQUEST_COMMAND, argc);     
+                               GameCommand_defer_clear(CMD_REQUEST_COMMAND, argc);
                                ++i;
                        }
-                       if(i) { print(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); } // should a message be added if no players were found? 
+                       if(i) { print(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); } // should a message be added if no players were found?
                        return;
                }
-               
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -625,8 -581,8 +625,8 @@@ void GameCommand_delrec(float request, 
                                        race_deleteTime(GetMapname(), stof(argv(1)));
                                return;
                        }
-               }       
-               
+               }
                default:
                        print("Incorrect parameters for ^2delrec^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_effectindexdump(float request) 
+ void GameCommand_effectindexdump(float request)
  {
        switch(request)
        {
                {
                        float fh, d;
                        string s;
-                       
                        d = db_create();
                        print("begin of effects list\n");
                        db_put(d, "TE_GUNSHOT", "1"); print("effect TE_GUNSHOT is ", ftos(particleeffectnum("TE_GUNSHOT")), "\n");
                        db_close(d);
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
        }
  }
  
- void GameCommand_extendmatchtime(float request) 
+ void GameCommand_extendmatchtime(float request)
  {
        switch(request)
        {
                        changematchtime(autocvar_timelimit_increment * 60, autocvar_timelimit_min * 60, autocvar_timelimit_max * 60);
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
  }
  
  void GameCommand_find(float request, float argc)  // is this even needed? We have prvm_edicts command and such ANYWAY
- {     
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                {
                        entity client;
-                       
                        for(client = world; (client = find(client, classname, argv(1))); )
                                print(etos(client), "\n");
-                               
                        return;
                }
-                       
                default:
                        print("Incorrect parameters for ^2find^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_gametype(float request, float argc) 
- {     
+ void GameCommand_gametype(float request, float argc)
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                        {
                                string s = argv(1);
                                float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
-                               
                                if(t)
                                {
                                        MapInfo_SwitchGameType(t);
                                }
                                else
                                        bprint("Game type switch to ", s, " failed: this type does not exist!\n");
-                                       
                                return;
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2gametype^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_gettaginfo(float request, float argc) 
- {     
+ void GameCommand_gettaginfo(float request, float argc)
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                        entity tmp_entity;
                        float i;
                        vector v;
-                       
                        if(argc >= 4)
                        {
                                tmp_entity = spawn();
                                }
                                else
                                        print("bone not found\n");
-                                       
                                remove(tmp_entity);
                                return;
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2gettaginfo^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_animbench(float request, float argc) 
+ void GameCommand_animbench(float request, float argc)
  {
        switch(request)
        {
@@@ -940,7 -896,7 +940,7 @@@ void GameCommand_gotomap(float request
                                return;
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2gotomap^7\n");
                case CMD_REQUEST_USAGE:
@@@ -970,7 -926,7 +970,7 @@@ void GameCommand_lockteams(float reques
                        }
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
        }
  }
  
- void GameCommand_make_mapinfo(float request) 
+ void GameCommand_make_mapinfo(float request)
  {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
-               { 
+               {
                        entity tmp_entity;
-                       
                        tmp_entity = spawn();
                        tmp_entity.classname = "make_mapinfo";
                        tmp_entity.think = make_mapinfo_Think;
                        MapInfo_Enumerate();
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -1017,17 -973,17 +1017,17 @@@ void GameCommand_moveplayer(float reque
                {
                        float accepted;
                        entity client;
-       
                        string targets = strreplace(",", " ", argv(1));
                        string original_targets = strreplace(" ", ", ", targets);
                        string destination = argv(2);
-                       
                        string successful, t;
                        successful = string_null;
-                       
                        // lets see if the target(s) even actually exist.
                        if((targets) && (destination))
-                       { 
+                       {
                                for(;targets;)
                                {
                                        t = car(targets); targets = cdr(targets);
                                        // Check to see if the player is a valid target
                                        client = GetFilteredEntity(t);
                                        accepted = VerifyClientEntity(client, FALSE, FALSE);
-                                       
-                                       if(accepted <= 0) 
+                                       if(accepted <= 0)
                                        {
-                                               print("moveplayer: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : ".\n")); 
+                                               print("moveplayer: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : ".\n"));
                                                continue;
                                        }
-                                       
                                        // Where are we putting this player?
-                                       if(destination == "spec" || destination == "spectator") 
+                                       if(destination == "spec" || destination == "spectator")
                                        {
                                                if(!IS_SPEC(client) && !IS_OBSERVER(client))
                                                {
                                                        self = client;
                                                        PutObserverInServer();
-                                                       
                                                        successful = strcat(successful, (successful ? ", " : ""), client.netname);
                                                }
                                                else
                                                                        // keep the forcing undone
                                                                        print("Player ", ftos(GetFilteredNumber(t)), " (", client.netname, ") is already on the ", Team_ColoredFullName(client.team), (targets ? "^7, skipping to next player.\n" : "^7.\n"));
                                                                        continue;
-                                                               } 
+                                                               }
                                                                else if(team_id == 0)  // auto team
                                                                {
                                                                        team_id = Team_NumberToTeam(FindSmallestTeam(client, FALSE));
                                                                        CheckAllowedTeams(client);
                                                                }
                                                                client.team_forced = save;
-                                                               
                                                                // Check to see if the destination team is even available
-                                                               switch(team_id) 
+                                                               switch(team_id)
                                                                {
                                                                        case NUM_TEAM_1: if(c1 == -1) { print("Sorry, can't move player to red team if it doesn't exist.\n"); return; } break;
                                                                        case NUM_TEAM_2: if(c2 == -1) { print("Sorry, can't move player to blue team if it doesn't exist.\n"); return; } break;
                                                                        case NUM_TEAM_3: if(c3 == -1) { print("Sorry, can't move player to yellow team if it doesn't exist.\n"); return; } break;
                                                                        case NUM_TEAM_4: if(c4 == -1) { print("Sorry, can't move player to pink team if it doesn't exist.\n"); return; } break;
-                                                                       
                                                                        default: print("Sorry, can't move player here if team ", destination, " doesn't exist.\n"); return;
                                                                }
-                                                               
                                                                // If so, lets continue and finally move the player
                                                                client.team_forced = 0;
                                                                MoveToTeam(client, team_id, 6);
                                                }
                                                else
                                                {
-                                                       print("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P 
+                                                       print("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P
                                                        return;
                                                }
                                        }
                                }
-                               
                                if(successful)
                                        bprint("Successfully moved players ", successful, " to destination ", destination, ".\n");
                                else
                                        print("No players given (", original_targets, ") are able to move.\n");
-                                       
                                return; // still correct parameters so return to avoid usage print
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2moveplayer^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_nospectators(float request) 
+ void GameCommand_nospectators(float request)
  {
        switch(request)
        {
                        bprint(strcat("^7All spectators will be automatically kicked when not joining the game after ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds!\n"));
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
  }
  
  void GameCommand_playerdemo(float request, float argc)
- {     
+ {
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
                        {
                                entity client;
                                float i, n, accepted;
-                               
                                switch(argv(1))
                                {
                                        case "read":
                                        {
                                                client = GetIndexedEntity(argc, 2);
                                                accepted = VerifyClientEntity(client, FALSE, TRUE);
-                                               
-                                               if(accepted <= 0) 
+                                               if(accepted <= 0)
                                                {
-                                                       print("playerdemo: read: ", GetClientErrorString(accepted, argv(2)), ".\n"); 
+                                                       print("playerdemo: read: ", GetClientErrorString(accepted, argv(2)), ".\n");
                                                        return;
                                                }
-                                               
                                                self = client;
                                                playerdemo_open_read(argv(next_token));
                                                return;
                                        }
-                                       
                                        case "write":
                                        {
                                                client = GetIndexedEntity(argc, 2);
                                                accepted = VerifyClientEntity(client, FALSE, FALSE);
-                                               
-                                               if(accepted <= 0) 
+                                               if(accepted <= 0)
                                                {
-                                                       print("playerdemo: write: ", GetClientErrorString(accepted, argv(2)), ".\n"); 
+                                                       print("playerdemo: write: ", GetClientErrorString(accepted, argv(2)), ".\n");
                                                        return;
                                                }
-                                               
                                                self = client;
                                                playerdemo_open_write(argv(next_token));
                                                return;
                                        }
-                                       
                                        case "auto_read_and_write":
                                        {
                                                n = GetFilteredNumber(argv(3));
                                                cvar_set("bot_number", ftos(n));
-                                               
                                                localcmd("wait; wait; wait\n");
                                                for(i = 0; i < n; ++i) { localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", argv(2), ftos(i+1), "\n"); }
-                                               
                                                localcmd("sv_cmd playerdemo write 1 ", ftos(n+1), "\n");
                                                return;
                                        }
-                                       
                                        case "auto_read":
                                        {
                                                n = GetFilteredNumber(argv(3));
                                                cvar_set("bot_number", ftos(n));
-                                               
                                                localcmd("wait; wait; wait\n");
                                                for(i = 0; i < n; ++i) { localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", argv(2), ftos(i+1), "\n"); }
                                                return;
                                }
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2playerdemo^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_printstats(float request) 
+ void GameCommand_printstats(float request)
  {
        switch(request)
        {
                        print("stats dumped.\n");
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -1285,7 -1241,7 +1285,7 @@@ void GameCommand_radarmap(float request
                        if(RadarMap_Make(argc))
                                return;
                }
-                       
                default:
                        print("Incorrect parameters for ^2radarmap^7\n");
                case CMD_REQUEST_USAGE:
        }
  }
  
- void GameCommand_reducematchtime(float request) 
+ void GameCommand_reducematchtime(float request)
  {
        switch(request)
        {
                        changematchtime(autocvar_timelimit_decrement *-60, autocvar_timelimit_min * 60, autocvar_timelimit_max * 60);
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -1334,7 -1290,7 +1334,7 @@@ void GameCommand_setbots(float request
                                return;
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2setbots^7\n");
                case CMD_REQUEST_USAGE:
@@@ -1364,22 -1320,22 +1364,22 @@@ void GameCommand_shuffleteams(float req
                                FOR_EACH_PLAYER(tmp_player)
                                {
                                        CheckAllowedTeams(tmp_player);
-                                       
                                        if(c1 >= 0) t_teams = max(1, t_teams);
                                        if(c2 >= 0) t_teams = max(2, t_teams);
                                        if(c3 >= 0) t_teams = max(3, t_teams);
                                        if(c4 >= 0) t_teams = max(4, t_teams);
-                                       
                                        ++t_players;
                                }
-                               
                                // build a list of the players in a random order
                                FOR_EACH_PLAYER(tmp_player)
                                {
                                        for(;;)
                                        {
                                                i = bound(1, floor(random() * maxclients) + 1, maxclients);
-                                               
                                                if(shuffleteams_players[i])
                                                {
                                                        continue; // a player is already assigned to this slot
                                        }
                                }
  
-                               // finally, from the list made earlier, re-join the players in different order. 
+                               // finally, from the list made earlier, re-join the players in different order.
                                for(i = 1; i <= t_teams; ++i)
                                {
                                        // find out how many players to assign to this team
                                        x = (t_players / t_teams);
                                        x = ((i == 1) ? ceil(x) : floor(x));
-                                       
                                        team_color = Team_NumberToTeam(i);
-                                       
-                                       // sort through the random list of players made earlier 
+                                       // sort through the random list of players made earlier
                                        for(z = 1; z <= maxclients; ++z)
-                                       {                                                       
+                                       {
                                                if (!(shuffleteams_teams[i] >= x))
                                                {
                                                        if (!(shuffleteams_players[z]))
                                                                continue; // not a player, move on to next random slot
-                                                               
                                                        if(VerifyClientNumber(shuffleteams_players[z]))
                                                                self = edict_num(shuffleteams_players[z]);
  
-                                                       if(self.team != team_color) 
+                                                       if(self.team != team_color)
                                                                MoveToTeam(self, team_color, 6);
  
                                                        shuffleteams_players[z] = 0;
                                                }
                                        }
                                }
-                               
                                bprint("Successfully shuffled the players around randomly.\n");
-                               
                                // clear the buffers now
                                for (i=0; i<SHUFFLETEAMS_MAX_PLAYERS; ++i)
                                        shuffleteams_players[i] = 0;
-                               
                                for (i=0; i<SHUFFLETEAMS_MAX_TEAMS; ++i)
                                        shuffleteams_teams[i] = 0;
                        }
                        {
                                print("Can't shuffle teams when currently not playing a team game.\n");
                        }
-                       
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -1469,19 -1425,19 +1469,19 @@@ void GameCommand_stuffto(float request
                        {
                                entity client = GetIndexedEntity(argc, 1);
                                float accepted = VerifyClientEntity(client, TRUE, FALSE);
-                               
                                if(accepted > 0)
                                {
                                        stuffcmd(client, strcat("\n", argv(next_token), "\n"));
                                        print(strcat("Command: \"", argv(next_token), "\" sent to ", GetCallerName(client), " (", argv(1) ,").\n"));
                                }
                                else
-                                       print("stuffto: ", GetClientErrorString(accepted, argv(1)), ".\n"); 
-                               
+                                       print("stuffto: ", GetClientErrorString(accepted, argv(1)), ".\n");
                                return;
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2stuffto^7\n");
                case CMD_REQUEST_USAGE:
@@@ -1510,7 -1466,7 +1510,7 @@@ void GameCommand_trace(float request, f
                        entity e;
                        vector org, delta, start, end, p, q, q0, pos, vv, dv;
                        float i, f, safe, unsafe, dq, dqf;
-       
                        switch(argv(1))
                        {
                                case "debug":
                                        }
                                        return;
                                }
-                                       
                                case "debug2":
                                {
                                        e = nextent(world);
                                        print("highest possible dist: ", ftos(f), "\n");
                                        return;
                                }
-                               
                                case "walk":
                                {
                                        if(argc == 4)
                                                return;
                                        }
                                }
-                               
                                case "showline":
                                {
                                        if(argc == 4)
                                                return;
                                        }
                                }
-                               
                                // no default case, just go straight to invalid
                        }
                }
-                       
                default:
                        print("Incorrect parameters for ^2trace^7\n");
                case CMD_REQUEST_USAGE:
@@@ -1694,7 -1650,7 +1694,7 @@@ void GameCommand_unlockteams(float requ
                        }
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -1718,7 -1674,7 +1718,7 @@@ void GameCommand_warp(float request, fl
                                {
                                        CampaignLevelWarp(stof(argv(1)));
                                        print("Successfully warped to campaign level ", stof(argv(1)), ".\n");
-                               }       
+                               }
                                else
                                {
                                        CampaignLevelWarp(-1);
                                print("Not in campaign, can't level warp\n");
                        return;
                }
-               
                default:
                case CMD_REQUEST_USAGE:
                {
@@@ -1749,10 -1705,10 +1749,10 @@@ void GameCommand_(float request
        {
                case CMD_REQUEST_COMMAND:
                {
-                       
                        return;
                }
-                       
                default:
                case CMD_REQUEST_USAGE:
                {
  // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
  #define SERVER_COMMANDS(request,arguments,command) \
        SERVER_COMMAND("adminmsg", GameCommand_adminmsg(request, arguments), "Send an admin message to a client directly") \
 +      SERVER_COMMAND("butcher", GameCommand_butcher(request), "Instantly removes all monsters on the map") \
        SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \
        SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \
        SERVER_COMMAND("anticheat", GameCommand_anticheat(request, arguments), "Create an anticheat report for a client") \
@@@ -1810,10 -1765,10 +1810,10 @@@ void GameCommand_macro_help(
  {
        #define SERVER_COMMAND(name,function,description) \
                { print("  ^2", name, "^7: ", description, "\n"); }
-               
        SERVER_COMMANDS(0, 0, "")
        #undef SERVER_COMMAND
-       
        return;
  }
  
@@@ -1821,10 -1776,10 +1821,10 @@@ float GameCommand_macro_command(float a
  {
        #define SERVER_COMMAND(name,function,description) \
                { if(name == strtolower(argv(0))) { function; return TRUE; } }
-               
        SERVER_COMMANDS(CMD_REQUEST_COMMAND, argc, command)
        #undef SERVER_COMMAND
-       
        return FALSE;
  }
  
@@@ -1832,10 -1787,10 +1832,10 @@@ float GameCommand_macro_usage(float arg
  {
        #define SERVER_COMMAND(name,function,description) \
                { if(name == strtolower(argv(1))) { function; return TRUE; } }
-               
        SERVER_COMMANDS(CMD_REQUEST_USAGE, argc, "")
        #undef SERVER_COMMAND
-       
        return FALSE;
  }
  
@@@ -1843,13 -1798,13 +1843,13 @@@ void GameCommand_macro_write_aliases(fl
  {
        #define SERVER_COMMAND(name,function,description) \
                { CMD_Write_Alias("qc_cmd_sv", name, description); }
-               
        SERVER_COMMANDS(0, 0, "")
        #undef SERVER_COMMAND
-       
        return;
  }
-       
  
  // =========================================
  //  Main Function Called By Engine (sv_cmd)
  void GameCommand(string command)
  {
        float argc = tokenize_console(command);
-       
        // Guide for working with argc arguments by example:
        // argc:   1    - 2      - 3     - 4
-       // argv:   0    - 1      - 2     - 3 
+       // argv:   0    - 1      - 2     - 3
        // cmd     vote - master - login - password
  
-       if(strtolower(argv(0)) == "help") 
+       if(strtolower(argv(0)) == "help")
        {
-               if(argc == 1) 
+               if(argc == 1)
                {
                        print("\nServer console commands:\n");
                        GameCommand_macro_help();
-                       
                        print("\nBanning commands:\n");
                        BanCommand_macro_help();
-                       
                        print("\nCommon networked commands:\n");
                        CommonCommand_macro_help(world);
-                       
                        print("\nGeneric commands shared by all programs:\n");
                        GenericCommand_macro_help();
-                       
                        print("\nUsage:^3 sv_cmd COMMAND...^7, where possible commands are listed above.\n");
                        print("For help about a specific command, type sv_cmd help COMMAND\n");
-                       
                        return;
-               } 
+               }
                else if(BanCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it
                {
                        return;
                {
                        return;
                }
-       } 
-       else if(BanCommand(command)) 
+       }
+       else if(BanCommand(command))
        {
                return; // handled by server/command/ipban.qc
        }
        {
                return; // handled by server/command/common.qc
        }
-       else if(GenericCommand(command)) 
+       else if(GenericCommand(command))
        {
                return; // handled by common/command/generic.qc
        }
        {
                return; // handled by one of the above GameCommand_* functions
        }
-       
        // nothing above caught the command, must be invalid
        print(((command != "") ? strcat("Unknown server command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try sv_cmd help.\n");
-       
        return;
  }
diff --combined qcsrc/server/defs.qh
index 2c631cbbbb78d70d80dc48faaf3d3812bb9b9fe4,72f4457d61d9fe99dff84bc1c2e728b2dcacab62..3823fa0a5cd161316912afa451ef8148d89f6fc5
@@@ -55,7 -55,7 +55,7 @@@ float team1_score, team2_score, team3_s
  float maxclients;
  
  // flag set on worldspawn so that the code knows if it is dedicated or not
- float server_is_dedicated; 
+ float server_is_dedicated;
  
  // Fields
  
  .float play_time;
  .float respawn_flags;
  .float respawn_time;
+ .float respawn_time_max;
  .float death_time;
  .float fade_time;
  .float fade_rate;
@@@ -243,7 -244,6 +244,7 @@@ float game_completion_ratio; // 0 at st
  float nJoinAllowed(entity ignore);
  
  .float spawnshieldtime;
 +.float item_spawnshieldtime;
  
  .entity flagcarried;
  
@@@ -476,7 -476,7 +477,7 @@@ void target_voicescript_clear(entity pl
  .float target_random;
  .float trigger_reverse;
  
- // Nexball 
+ // Nexball
  .entity ballcarried; // Also used for keepaway
  .float metertime;
  float g_nexball_meter_period;
@@@ -517,8 -517,8 +518,8 @@@ string matchid
  
  .float last_pickup;
  
- .float hit_time; 
- .float typehit_time; 
+ .float hit_time;
+ .float typehit_time;
  
  .float stat_leadlimit;
  
@@@ -584,10 -584,7 +585,10 @@@ float serverflags
  
  .float player_blocked;
  
 -.float freezetag_frozen;
 +.float frozen; // for freeze attacks
 +.float revive_progress;
 +.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
 +.entity iceblock;
  
  .entity muzzle_flash;
  .float misc_bulletcounter;    // replaces uzi & hlac bullet counter.
diff --combined qcsrc/server/g_damage.qc
index 633f9b1666a86d4e36ed3e0e5258fdee54429e3d,195829b40d00c701f9e95d494b19178caeb1a815..4b7c297314c84531ed240eaea1609b9346c3d990
@@@ -349,7 -349,7 +349,7 @@@ void Obituary(entity attacker, entity i
                )
        );
        #endif
-       
        // =======
        // SUICIDE
        // =======
                                                Obituary_SpecialDeath(targ, FALSE, deathtype, targ.netname, deathlocation, "", targ.killcount, 0, 0);
                                                break;
                                        }
-                                       
                                        default:
                                        {
                                                Obituary_SpecialDeath(targ, FALSE, deathtype, targ.netname, deathlocation, "", targ.killcount, 0, 0);
                        GiveFrags(attacker, targ, -1, deathtype);
  
                        attacker.killcount = 0;
-                       
                        Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname);
                        Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker.netname);
                        Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(targ.team, INFO_DEATH_TEAMKILL_), targ.netname, attacker.netname, deathlocation, targ.killcount);
                                        0);
                                break;
                        }
-                       
                        default:
                        {
                                Obituary_SpecialDeath(targ, FALSE, deathtype, targ.netname, deathlocation, "", targ.killcount, 0, 0);
        if(targ.killcount) { targ.killcount = 0; }
  }
  
 +void Ice_Think()
 +{
 +      setorigin(self, self.owner.origin - '0 0 16');
 +      self.nextthink = time;
 +}
 +
 +void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint)
 +{
 +      if(!IS_PLAYER(targ) && !(targ.flags & FL_MONSTER)) // only specified entities can be freezed
 +              return;
 +              
 +      if(targ.frozen)
 +              return;
 +              
 +      targ.frozen = frozen_type;
 +      targ.revive_progress = 0;
 +      targ.health = 1;
 +      targ.revive_speed = freeze_time;
 +
 +      entity ice, head;
 +      ice = spawn();
 +      ice.owner = targ;
 +      ice.classname = "ice";
 +      ice.scale = targ.scale;
 +      ice.think = Ice_Think;
 +      ice.nextthink = time;
 +      ice.frame = floor(random() * 21); // ice model has 20 different looking frames
 +      setmodel(ice, "models/ice/ice.md3");
 +      ice.alpha = 1;
 +      ice.colormod = Team_ColorRGB(targ.team);
 +      ice.glowmod = ice.colormod;
 +      targ.iceblock = ice;
 +
 +      entity oldself;
 +      oldself = self;
 +      self = ice;
 +      Ice_Think();
 +      self = oldself;
 +
 +      RemoveGrapplingHook(targ);
 +      
 +      FOR_EACH_PLAYER(head)
 +      if(head.hook.aiment == targ)
 +              RemoveGrapplingHook(head);
 +      
 +      // add waypoint
 +      if(show_waypoint)       
 +              WaypointSprite_Spawn("frozen", 0, 0, targ, '0 0 64', world, targ.team, targ, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
 +}
 +
 +void Unfreeze (entity targ)
 +{
 +      if(targ.frozen) // only reset health if target was frozen
 +              targ.health = ((IS_PLAYER(targ)) ? autocvar_g_balance_health_start : targ.max_health);
 +
 +      entity head;
 +      targ.frozen = 0;
 +      targ.revive_progress = 0;
 +      
 +      WaypointSprite_Kill(targ.waypointsprite_attached);
 +      
 +      FOR_EACH_PLAYER(head)
 +      if(head.hook.aiment == targ)
 +              RemoveGrapplingHook(head);
 +
 +      // remove the ice block
 +      if(targ.iceblock)
 +              remove(targ.iceblock);
 +      targ.iceblock = world;
 +}
 +
  // these are updated by each Damage call for use in button triggering and such
  entity damage_targ;
  entity damage_inflictor;
@@@ -629,7 -558,7 +629,7 @@@ void Damage (entity targ, entity inflic
  {
        float mirrordamage;
        float mirrorforce;
-       float complainteamdamage = 0; 
+       float complainteamdamage = 0;
        entity attacker_save;
        mirrordamage = 0;
        mirrorforce = 0;
                // exit the vehicle before killing (fixes a crash)
                if(IS_PLAYER(targ) && targ.vehicle)
                        vehicles_exit(VHEF_RELESE);
-       
                // These are ALWAYS lethal
                // No damage modification here
                // Instead, prepare the victim for his death...
                        force = force * g_weaponforcefactor;
                        mirrorforce *= g_weaponforcefactor;
                }
-               
 +              if(targ.frozen && deathtype != DEATH_HURTTRIGGER)
 +              {
 +                      damage = 0;
 +                      force *= 0.2;
 +              }
-               
++
                // should this be changed at all? If so, in what way?
                frag_attacker = attacker;
                frag_target = targ;
                frag_damage = damage;
                frag_force = force;
 -        frag_deathtype = deathtype;
 +              frag_deathtype = deathtype;
                frag_mirrordamage = mirrordamage;
                MUTATOR_CALLHOOK(PlayerDamage_Calculate);
                damage = frag_damage;
                mirrordamage = frag_mirrordamage;
                force = frag_force;
-               
                if (!g_minstagib)
                {
                        // apply strength multiplier
                        else
                                victim = targ;
  
 -                      if(IS_PLAYER(victim) || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
 +                      if(IS_PLAYER(victim) || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET || victim.flags & FL_MONSTER)
                        {
                                if(DIFF_TEAM(victim, attacker))
                                {
@@@ -1175,7 -1098,7 +1175,7 @@@ float Fire_AddDamage(entity e, entity o
                if(maxtime > mintime || maxdps > mindps)
                {
                        // Constraints:
-                       
                        // damage we have right now
                        mindamage = mindps * mintime;
  
@@@ -1277,7 -1200,7 +1277,7 @@@ void Fire_ApplyDamage(entity e
                e.fire_endtime = 0;
  
        // ice stops fire
 -      if(e.freezetag_frozen)
 +      if(e.frozen)
                e.fire_endtime = 0;
  
        t = min(frametime, e.fire_endtime - time);
diff --combined qcsrc/server/g_world.qc
index 700c2c513bb2f3b1d585c64c827e74ebf628d123,42c2d7a8e4e74cad37e0a1cacc88f0b74361fd6e..85bd64ac4c0e88745edfa5ed677e123b455204d5
@@@ -253,7 -253,6 +253,6 @@@ void cvar_changes_init(
  
                // mapinfo
                BADCVAR("fraglimit");
-               BADCVAR("g_arena");
                BADCVAR("g_assault");
                BADCVAR("g_ca");
                BADCVAR("g_ca_teams");
  
                // does nothing visible
                BADCVAR("captureleadlimit_override");
-               BADCVAR("g_arena_point_leadlimit");
                BADCVAR("g_balance_kill_delay");
                BADCVAR("g_ca_point_leadlimit");
                BADCVAR("g_ctf_captimerecord_always");
                BADCVAR("g_nix");
                BADCVAR("g_grappling_hook");
                BADCVAR("g_jetpack");
-               
  #undef BADPREFIX
  #undef BADCVAR
  
@@@ -544,7 -542,6 +542,7 @@@ void spawnfunc___init_dedicated_server(
  
        // 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);
@@@ -593,7 -590,6 +591,7 @@@ void spawnfunc_worldspawn (void
  
        // 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);
        addstat(STAT_NEX_CHARGEPOOL, AS_FLOAT, nex_chargepool_ammo);
  
        addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
 +      
 +      // freeze attacks
 +      addstat(STAT_FROZEN, AS_INT, frozen);
 +      addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
  
        // g_movementspeed hack
        addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
        // secrets
        addstat(STAT_SECRETS_TOTAL, AS_FLOAT, stat_secrets_total);
        addstat(STAT_SECRETS_FOUND, AS_FLOAT, stat_secrets_found);
 +      
 +      // monsters
 +      addstat(STAT_MONSTERS_TOTAL, AS_FLOAT, stat_monsters_total);
 +      addstat(STAT_MONSTERS_KILLED, AS_FLOAT, stat_monsters_killed);
  
        // misc
        addstat(STAT_RESPAWN_TIME, AS_FLOAT, stat_respawn_time);
        // set up information replies for clients and server to use
        maplist_reply = strzone(getmaplist());
        lsmaps_reply = strzone(getlsmaps());
 +      monsterlist_reply = strzone(getmonsterlist());
        for(i = 0; i < 10; ++i)
        {
                s = getrecords(i);
@@@ -1412,7 -1399,7 +1410,7 @@@ void DumpStats(float final
                {
                        s = strcat(":player:see-labels:", GetPlayerScoreString(other, 0), ":");
                        s = strcat(s, ftos(rint(time - other.jointime)), ":");
-                       if(IS_PLAYER(other) || g_arena || other.caplayer == 1 || g_lms)
+                       if(IS_PLAYER(other) || other.caplayer == 1 || g_lms)
                                s = strcat(s, ftos(other.team), ":");
                        else
                                s = strcat(s, "spectator:");
@@@ -1525,7 -1512,7 +1523,7 @@@ void NextLevel(
                PlayerStats_AddGlobalInfo(e);
        PlayerStats_Shutdown();
        WeaponStats_Shutdown();
-       
        Kill_Notification(NOTIF_ALL, world, MSG_CENTER, 0); // kill all centerprints now
  
        if(autocvar_sv_eventlog)
@@@ -1866,7 -1853,7 +1864,7 @@@ float WinningCondition_Scores(float lim
        if(WinningConditionHelper_zeroisworst)
                leadlimit = 0; // not supported in this mode
  
-       if(g_dm || g_tdm || g_arena || g_ca || (g_race && !g_race_qualifying) || g_nexball)
+       if(g_dm || g_tdm || g_ca || (g_race && !g_race_qualifying) || g_nexball)
        // these modes always score in increments of 1, thus this makes sense
        {
                if(leaderfrags != WinningConditionHelper_topscore)
index 6eb281ba78ddc0a0e6cfbdfa67303fccaa66bc58,e7e771dc6ed03a26ff89f050aa1499ff2fc835ad..5dc3a81347314ec15d7e0ab48692bbdd84d1ae97
@@@ -99,8 -99,6 +99,8 @@@ const string STR_OBSERVER = "observer"
  #define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too
  #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v))
  
 +#define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; )
 +
  #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
  
  // copies a string to a tempstring (so one can strunzone it)
@@@ -650,16 -648,16 +650,16 @@@ float want_weapon(string cvarprefix, en
                d = 0; // weapon is set a few lines later
        else
                d = (i == WEP_LASER || i == WEP_SHOTGUN);
-               
        if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
                d |= (i == WEP_HOOK);
        if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
                d = 0;
  
        var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
-       
        //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
-       
        // bit order in t:
        // 1: want or not
        // 2: is default?
@@@ -791,7 -789,7 +791,7 @@@ void readplayerstartcvars(
  
        if(!cvar("g_use_ammunition"))
                start_items |= IT_UNLIMITED_AMMO;
-       
        if(start_items & IT_UNLIMITED_WEAPON_AMMO)
        {
                start_ammo_rockets = 999;
@@@ -909,7 -907,7 +909,7 @@@ void readlevelcvars(void
        // load mutators
        #define CHECK_MUTATOR_ADD(mut_cvar,mut_name,dependence) \
                { if(cvar(mut_cvar) && dependence) { MUTATOR_ADD(mut_name); } }
-               
        CHECK_MUTATOR_ADD("g_dodging", mutator_dodging, 1);
        CHECK_MUTATOR_ADD("g_spawn_near_teammate", mutator_spawn_near_teammate, 1);
        CHECK_MUTATOR_ADD("g_physical_items", mutator_physical_items, 1);
        CHECK_MUTATOR_ADD("g_nades", mutator_nades, 1);
        CHECK_MUTATOR_ADD("g_sandbox", sandbox, 1);
        CHECK_MUTATOR_ADD("g_campcheck", mutator_campcheck, 1);
-       
        #undef CHECK_MUTATOR_ADD
-       
        if(cvar("sv_allow_fullbright"))
                serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
  
      g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
      g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
      g_bugrigs_steer = cvar("g_bugrigs_steer");
-       
        g_minstagib = cvar("g_minstagib");
  
        sv_clones = cvar("sv_clones");
        g_warmup_allguns = cvar("g_warmup_allguns");
        g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
  
-       if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
+       if ((g_race && g_race_qualifying == 2) || g_assault || cvar("g_campaign"))
                warmup_stage = 0; // these modes cannot work together, sorry
  
        g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
@@@ -1820,7 -1818,7 +1820,7 @@@ string uid2name(string myuid) 
                        db_put(ServerProgsDB, strcat("uid2name", myuid), "");
                }
        }
-       
        if(s == "")
                s = "^1Unregistered Player";
        return s;
diff --combined qcsrc/server/movelib.qc
index b6ee83b80901ce1a2f329b0afbf6616dd05b71ac,de82b16a8ab1f793623514e24cb38639430deb4f..c262d0152e23bb9c1ad88b39dad2c51ddce3681e
@@@ -1,4 -1,4 +1,4 @@@
- #ifdef SVQC 
+ #ifdef SVQC
  .vector moveto;
  
  /**
@@@ -170,9 -170,6 +170,9 @@@ void movelib_move_simple(vector newdir,
  */
  #define movelib_move_simple(newdir,velo,blendrate) \
      self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo
 +      
 +#define movelib_move_simple_gravity(newdir,velo,blendrate) \
 +    if(self.flags & FL_ONGROUND) movelib_move_simple(newdir,velo,blendrate)
  
  void movelib_beak_simple(float force)
  {
index bd70e7ccea62d26179da3d4b7a9efd7aa5e79d1f,893ebb301ff439ffbed18757f6da29cc95cf92b8..1aa3128ca99c1a3cd0fe65cb333655fe5e51b30a
@@@ -72,7 -72,7 +72,7 @@@ MUTATOR_HOOKABLE(PlayerDies)
                entity frag_attacker;
                entity frag_target; // same as self
                float frag_deathtype;
-               
  MUTATOR_HOOKABLE(PlayerJump);
        // called when a player presses the jump key
        // INPUT, OUTPUT:
@@@ -114,7 -114,7 +114,7 @@@ MUTATOR_HOOKABLE(BuildMutatorsPrettyStr
        // appends ", Mutator name" to ret_string for display
        // INPUT, OUTPUT:
                string ret_string;
-               
  MUTATOR_HOOKABLE(CustomizeWaypoint);
        // called every frame
        // customizes the waypoint for spectators
@@@ -127,7 -127,7 +127,7 @@@ MUTATOR_HOOKABLE(FilterItem)
  MUTATOR_HOOKABLE(TurretSpawn);
        // return error to request removal
        // INPUT: self - turret
-       
  MUTATOR_HOOKABLE(OnEntityPreSpawn);
        // return error to prevent entity spawn, or modify the entity
  
@@@ -152,39 -152,6 +152,39 @@@ MUTATOR_HOOKABLE(EditProjectile)
        // INPUT:
                entity self;
                entity other;
 +        
 +MUTATOR_HOOKABLE(MonsterSpawn);
 +      // called when a monster spawns
 +    
 +MUTATOR_HOOKABLE(MonsterDies);
 +      // called when a monster dies
 +      // INPUT:
 +              entity frag_attacker;
 +              
 +MUTATOR_HOOKABLE(MonsterRespawn);
 +      // called when a monster wants to respawn
 +      // INPUT:
 +              entity other;
 +              
 +MUTATOR_HOOKABLE(MonsterDropItem);
 +      // called when a monster is dropping loot
 +      // INPUT, OUTPUT:
 +              .void() monster_loot;
 +              entity other;
 +      
 +MUTATOR_HOOKABLE(MonsterMove);
 +      // called when a monster moves
 +      // returning TRUE makes the monster stop
 +      // INPUT:
 +              float monster_speed_run;
 +              float monster_speed_walk;
 +              entity monster_target;
 +    
 +MUTATOR_HOOKABLE(MonsterFindTarget);
 +      // called when a monster looks for another target
 +    
 +MUTATOR_HOOKABLE(MonsterCheckBossFlag);
 +    // called to change a random monster to a miniboss
  
  MUTATOR_HOOKABLE(PlayerDamage_SplitHealthArmor);
        // called when a player gets damaged to e.g. remove stuff he was carrying.
        // INPUT, OUTPUT:
                float damage_take;
                float damage_save;
-               
  MUTATOR_HOOKABLE(PlayerDamage_Calculate);
        // called to adjust damage and force values which are applied to the player, used for e.g. strength damage/force multiplier
        // i'm not sure if I should change this around slightly (Naming of the entities, and also how they're done in g_damage).
@@@ -213,7 -180,7 +213,7 @@@ MUTATOR_HOOKABLE(PlayerPowerups)
        // called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items.
        // INPUT
        entity self;
-       float olditems; // also technically output, but since it is at the end of the function it's useless for that :P 
+       float olditems; // also technically output, but since it is at the end of the function it's useless for that :P
  
  MUTATOR_HOOKABLE(PlayerUseKey);
        // called when the use key is pressed
@@@ -264,7 -231,7 +264,7 @@@ MUTATOR_HOOKABLE(SV_StartFrame)
  MUTATOR_HOOKABLE(SetModname);
        // OUT
        string modname; // name of the mutator/mod if it warrants showing as such in the server browser
-       
  MUTATOR_HOOKABLE(Item_Spawn);
        // called for each item being spawned on a map, including dropped weapons
        // return 1 to remove an item
@@@ -277,13 -244,13 +277,13 @@@ MUTATOR_HOOKABLE(SetWeaponreplace)
                entity other; // weapon info
        // IN+OUT
                string ret_string;
-               
  MUTATOR_HOOKABLE(Item_RespawnCountdown);
        // called when an item is about to respawn
        // INPUT+OUTPUT:
        string item_name;
        vector item_color;
-               
  MUTATOR_HOOKABLE(BotShouldAttack);
        // called when a bot checks a target to attack
        // INPUT
@@@ -294,7 -261,7 +294,7 @@@ MUTATOR_HOOKABLE(PortalTeleport)
        // allows you to strip a player of an item if they go through the teleporter to help prevent cheating
        // INPUT
        entity self;
-       
  MUTATOR_HOOKABLE(HelpMePing);
        // called whenever a player uses impulse 33 (help me) in cl_impulse.qc
        // normally help me ping uses self.waypointsprite_attachedforcarrier,
        // in a special manner using this hook
        // INPUT
        entity self; // the player who pressed impulse 33
-       
  MUTATOR_HOOKABLE(VehicleSpawn);
        // called when a vehicle initializes
        // return TRUE to remove the vehicle
-       
  MUTATOR_HOOKABLE(VehicleEnter);
        // called when a player enters a vehicle
        // allows mutators to set special settings in this event
        // INPUT
        entity vh_player; // player
        entity vh_vehicle; // vehicle
-       
  MUTATOR_HOOKABLE(VehicleTouch);
        // called when a player touches a vehicle
        // return TRUE to stop player from entering the vehicle
        // INPUT
        entity self; // vehicle
        entity other; // player
-       
  MUTATOR_HOOKABLE(VehicleExit);
        // called when a player exits a vehicle
        // allows mutators to set special settings in this event
        // INPUT
        entity vh_player; // player
        entity vh_vehicle; // vehicle
-       
  MUTATOR_HOOKABLE(AbortSpeedrun);
        // called when a speedrun is aborted and the player is teleported back to start position
        // INPUT
index 8db0df90d441766ecfab05f2edde1ba2c69550c9,19cc563f80358df8760b3e176adf7f4963ee0cfe..862504d72c826b9bb04dd65a2b01174a84cadb13
@@@ -25,20 -25,20 +25,20 @@@ void ctf_CaptureRecord(entity flag, ent
        float cap_record = ctf_captimerecord;
        float cap_time = (time - flag.ctf_pickuptime);
        string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
-       
        // notify about shit
        if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_TIME_), player.netname, (cap_time * 100)); }
        else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_BROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
        else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
-       
        // write that shit in the database
        if((!ctf_captimerecord) || (cap_time < cap_record))
        {
                ctf_captimerecord = cap_time;
                db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
                db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
-               write_recordmarker(player, (time - cap_time), cap_time); 
-       } 
+               write_recordmarker(player, (time - cap_time), cap_time);
+       }
  }
  
  void ctf_FlagcarrierWaypoints(entity player)
@@@ -92,7 -92,7 +92,7 @@@ float ctf_CheckPassDirection(vector hea
                float ang; // angle between shotdir and h
                float h; // hypotenuse, which is the distance between attacker to head
                float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin
-               
                h = vlen(head_center - passer_center);
                ang = acos(dotproduct(normalize(head_center - passer_center), v_forward));
                a = h * cos(ang);
  
  
  // =======================
- // CaptureShield Functions 
+ // CaptureShield Functions
  // =======================
  
- float ctf_CaptureShield_CheckStatus(entity p) 
+ float ctf_CaptureShield_CheckStatus(entity p)
  {
        float s, se;
        entity e;
  
        // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
        // use this rule here
-       
        if(players_worseeq >= players_total * ctf_captureshield_max_ratio)
                return FALSE;
  
@@@ -163,7 -163,7 +163,7 @@@ float ctf_CaptureShield_Customize(
  {
        if(!other.ctf_captureshielded) { return FALSE; }
        if(SAME_TEAM(self, other)) { return FALSE; }
-       
        return TRUE;
  }
  
@@@ -171,7 -171,7 +171,7 @@@ void ctf_CaptureShield_Touch(
  {
        if(!other.ctf_captureshielded) { return; }
        if(SAME_TEAM(self, other)) { return; }
-       
        vector mymid = (self.absmin + self.absmax) * 0.5;
        vector othermid = (other.absmin + other.absmax) * 0.5;
  
  void ctf_CaptureShield_Spawn(entity flag)
  {
        entity shield = spawn();
-       
        shield.enemy = self;
        shield.team = self.team;
        shield.touch = ctf_CaptureShield_Touch;
        shield.solid = SOLID_TRIGGER;
        shield.avelocity = '7 0 11';
        shield.scale = 0.5;
-       
        setorigin(shield, self.origin);
        setmodel(shield, "models/ctf/shield.md3");
        setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
@@@ -217,16 -217,16 +217,16 @@@ void ctf_Handle_Drop(entity flag, entit
        flag.ctf_droptime = time;
        flag.ctf_dropper = player;
        flag.ctf_status = FLAG_DROPPED;
-       
        // messages and sounds
        Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_LOST_), player.netname);
        sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("dropped", player.team, player);
  
        // scoring
-       PlayerTeamScore_AddScore(player, -autocvar_g_ctf_score_penalty_drop);   
+       PlayerTeamScore_AddScore(player, -autocvar_g_ctf_score_penalty_drop);
        PlayerScore_Add(player, SP_CTF_DROPS, 1);
-       
        // waypoints
        if(autocvar_g_ctf_flag_dropped_waypoint)
                WaypointSprite_Spawn("flagdropped", 0, 0, flag, FLAG_WAYPOINT_OFFSET, world, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, TRUE, RADARICON_FLAG, WPCOLOR_DROPPEDFLAG(flag.team));
                WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health);
                WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health);
        }
-       
        player.throw_antispam = time + autocvar_g_ctf_pass_wait;
-       
        if(droptype == DROP_PASS)
        {
                flag.pass_distance = 0;
@@@ -251,11 -251,11 +251,11 @@@ void ctf_Handle_Retrieve(entity flag, e
  {
        entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
        entity sender = flag.pass_sender;
-       
        // transfer flag to player
        flag.owner = player;
        flag.owner.flagcarried = flag;
-       
        // reset flag
        setattachment(flag, player, "");
        setorigin(flag, FLAG_CARRY_OFFSET);
        // messages and sounds
        sound(player, CH_TRIGGER, flag.snd_flag_pass, VOL_BASE, ATTEN_NORM);
        ctf_EventLog("receive", flag.team, player);
-       
        FOR_EACH_REALPLAYER(tmp_player)
        {
                if(tmp_player == sender)
                else if(SAME_TEAM(tmp_player, sender))
                        Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname);
        }
-       
        // create new waypoint
        ctf_FlagcarrierWaypoints(player);
-       
        sender.throw_antispam = time + autocvar_g_ctf_pass_wait;
        player.throw_antispam = sender.throw_antispam;
  
@@@ -294,12 -294,12 +294,12 @@@ void ctf_Handle_Throw(entity player, en
  {
        entity flag = player.flagcarried;
        vector targ_origin, flag_velocity;
-       
        if(!flag) { return; }
        if((droptype == DROP_PASS) && !receiver) { return; }
-       
        if(flag.speedrunning) { ctf_RespawnFlag(flag); return; }
-       
        // reset the flag
        setattachment(flag, world, "");
        setorigin(flag, player.origin + FLAG_DROP_OFFSET);
        flag.solid = SOLID_TRIGGER;
        flag.ctf_dropper = player;
        flag.ctf_droptime = time;
-       
        flag.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS
-       
        switch(droptype)
        {
                case DROP_PASS:
                        flag.pass_sender = player;
                        flag.pass_target = receiver;
                        flag.ctf_status = FLAG_PASSING;
-                       
                        // other
                        sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM);
                        WarpZone_TrailParticles(world, particleeffectnum(flag.passeffect), player.origin, targ_origin);
                        ctf_EventLog("pass", flag.team, player);
                        break;
                }
-               
                case DROP_THROW:
                {
                        makevectors((player.v_angle_y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle_x, autocvar_g_ctf_throw_angle_max) * '1 0 0'));
-                               
                        flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & IT_STRENGTH) ? autocvar_g_ctf_throw_strengthmultiplier : 1)));
                        flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, FALSE);
                        ctf_Handle_Drop(flag, player, droptype);
                        break;
                }
-               
                case DROP_RESET:
                {
                        flag.velocity = '0 0 0'; // do nothing
                        break;
                }
-               
                default:
                case DROP_NORMAL:
                {
        // kill old waypointsprite
        WaypointSprite_Ping(player.wps_flagcarrier);
        WaypointSprite_Kill(player.wps_flagcarrier);
-       
        if(player.wps_enemyflagcarrier)
                WaypointSprite_Kill(player.wps_enemyflagcarrier);
-       
        // captureshield
        ctf_CaptureShield_Update(player, 0); // shield player from picking up flag
  }
@@@ -384,22 -384,22 +384,22 @@@ void ctf_Handle_Capture(entity flag, en
  {
        entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher);
        entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper);
-       float old_time, new_time; 
-       
+       float old_time, new_time;
        if (!player) { return; } // without someone to give the reward to, we can't possibly cap
-       
        // messages and sounds
        Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(enemy_flag, CENTER_CTF_CAPTURE_));
        ctf_CaptureRecord(enemy_flag, player);
        sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTEN_NONE);
-       
        switch(capturetype)
        {
                case CAPTURE_NORMAL: ctf_EventLog("capture", enemy_flag.team, player); break;
                case CAPTURE_DROPPED: ctf_EventLog("droppedcapture", enemy_flag.team, player); break;
                default: break;
        }
-       
        // scoring
        PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_capture);
        PlayerTeamScore_Add(player, SP_CTF_CAPS, ST_CTF_CAPS, 1);
        {
                WaypointSprite_Kill(player.wps_flagcarrier);
                if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); }
-               
                if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper))
                        { PlayerTeamScore_AddScore(enemy_flag.ctf_dropper, autocvar_g_ctf_score_capture_assist); }
        }
-       
        // reset the flag
        player.next_take_time = time + autocvar_g_ctf_flag_collect_delay;
        ctf_RespawnFlag(enemy_flag);
  void ctf_Handle_Return(entity flag, entity player)
  {
        // messages and sounds
 -      Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_));
 -      Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_), player.netname);
 +      if(IS_PLAYER(player))
 +              Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_));
 +              
 +      Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_), (player.flags & FL_MONSTER) ? player.monster_name : player.netname);
        sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("return", flag.team, player);
  
        // scoring
 -      PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
 -      PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
 +      if(IS_PLAYER(player))
 +      {
 +              PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
 +              PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
 +      }
  
        TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it
-       
-       if(flag.ctf_dropper) 
+       if(flag.ctf_dropper)
        {
                PlayerScore_Add(flag.ctf_dropper, SP_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag
-               ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag 
+               ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag
                flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time
        }
-       
        // reset the flag
        ctf_RespawnFlag(flag);
  }
@@@ -462,20 -457,20 +462,20 @@@ void ctf_Handle_Pickup(entity flag, ent
  {
        // declarations
        float pickup_dropped_score; // used to calculate dropped pickup score
-       
        // attach the flag to the player
        flag.owner = player;
        player.flagcarried = flag;
        setattachment(flag, player, "");
        setorigin(flag, FLAG_CARRY_OFFSET);
-       
        // flag setup
        flag.movetype = MOVETYPE_NONE;
        flag.takedamage = DAMAGE_NO;
        flag.solid = SOLID_NOT;
        flag.angles = '0 0 0';
        flag.ctf_status = FLAG_CARRY;
-       
        switch(pickuptype)
        {
                case PICKUP_BASE: flag.ctf_pickuptime = time; break; // used for timing runs
        }
  
        // messages and sounds
-       Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_PICKUP_), player.netname);  
+       Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_PICKUP_), player.netname);
        Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PICKUP_));
        if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); }
-       
        Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, CHOICE_CTF_PICKUP_TEAM, Team_ColorCode(player.team), player.netname);
        Send_Notification(NOTIF_TEAM, flag, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY, Team_ColorCode(player.team), player.netname);
-       
        sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE);
-       
        // scoring
        PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
        switch(pickuptype)
-       {               
+       {
                case PICKUP_BASE:
                {
                        PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_pickup_base);
                        ctf_EventLog("steal", flag.team, player);
                        break;
                }
-               
                case PICKUP_DROPPED:
                {
                        pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1);
                        ctf_EventLog("pickup", flag.team, player);
                        break;
                }
-               
                default: break;
        }
-       
        // speedrunning
        if(pickuptype == PICKUP_BASE)
        {
                if((player.speedrunning) && (ctf_captimerecord))
                        ctf_FakeTimeLimit(player, time + ctf_captimerecord);
        }
-               
        // effects
        pointparticles(particleeffectnum(flag.toucheffect), player.origin, '0 0 0', 1);
-       
-       // waypoints 
+       // waypoints
        if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); }
        ctf_FlagcarrierWaypoints(player);
        WaypointSprite_Ping(player.wps_flagcarrier);
@@@ -544,7 -539,7 +544,7 @@@ void ctf_CheckFlagReturn(entity flag, f
        if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING))
        {
                if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); }
-               
                if((flag.health <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time))
                {
                        switch(returntype)
                                case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DAMAGED_)); break;
                                case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), ctf_captimerecord); break;
                                case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_NEEDKILL_)); break;
-                               
                                default:
                                case RETURN_TIMEOUT:
                                        { Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_TIMEOUT_)); break; }
@@@ -582,7 -577,7 +582,7 @@@ void ctf_CheckStalemate(void
                {
                        tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist
                        ctf_staleflaglist = tmp_entity;
-                       
                        switch(tmp_entity.team)
                        {
                                case NUM_TEAM_1: ++stale_red_flags; break;
                { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; }
        else if((!stale_red_flags || !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 1)
                { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; }
-               
        // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary
        if(ctf_stalemate)
        {
                        if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier))
                                WaypointSprite_Spawn("enemyflagcarrier", 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, tmp_entity.team, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team));
                }
-               
                if (!wpforenemy_announced)
                {
                        FOR_EACH_REALPLAYER(tmp_entity)
                                Send_Notification(NOTIF_ONE, tmp_entity, MSG_CENTER, ((tmp_entity.flagcarried) ? CENTER_CTF_STALEMATE_CARRIER : CENTER_CTF_STALEMATE_OTHER));
-                       
                        wpforenemy_announced = TRUE;
                }
        }
@@@ -626,7 -621,7 +626,7 @@@ void ctf_FlagDamage(entity inflictor, e
                ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
                return;
        }
-       if(autocvar_g_ctf_flag_return_damage) 
+       if(autocvar_g_ctf_flag_return_damage)
        {
                // reduce health and check if it should be returned
                self.health = self.health - damage;
@@@ -653,7 -648,7 +653,7 @@@ void ctf_FlagThink(
                tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
                if(!trace_startsolid) // can we resize it without getting stuck?
                        setsize(self, FLAG_MIN, FLAG_MAX); }
-                       
        switch(self.ctf_status) // reset flag angles in case warpzones adjust it
        {
                case FLAG_DROPPED:
                        self.angles = '0 0 0';
                        break;
                }
-               
                default: break;
        }
  
        // main think method
        switch(self.ctf_status)
-       {       
+       {
                case FLAG_BASE:
                {
                        if(autocvar_g_ctf_dropped_capture_radius)
                        }
                        return;
                }
-               
                case FLAG_DROPPED:
                {
                        if(autocvar_g_ctf_flag_dropped_floatinwater)
                                if(pointcontents(midpoint) == CONTENT_WATER)
                                {
                                        self.velocity = self.velocity * 0.5;
-                                       
                                        if(pointcontents(midpoint + FLAG_FLOAT_OFFSET) == CONTENT_WATER)
                                                { self.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; }
                                        else
                                self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE);
                                ctf_CheckFlagReturn(self, RETURN_TIMEOUT);
                                return;
-                       } 
+                       }
                        return;
                }
-                       
                case FLAG_CARRY:
                {
-                       if(self.speedrunning && ctf_captimerecord && (time >= self.ctf_pickuptime + ctf_captimerecord)) 
+                       if(self.speedrunning && ctf_captimerecord && (time >= self.ctf_pickuptime + ctf_captimerecord))
                        {
                                self.health = 0;
                                ctf_CheckFlagReturn(self, RETURN_SPEEDRUN);
                        }
                        return;
                }
-               
                case FLAG_PASSING:
                {
                        vector targ_origin = ((self.pass_target.absmin + self.pass_target.absmax) * 0.5);
                        targ_origin = WarpZone_RefSys_TransformOrigin(self.pass_target, self, targ_origin); // origin of target as seen by the flag (us)
                        WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
-                       
                        if((self.pass_target == world)
                                || (self.pass_target.deadflag != DEAD_NO)
                                || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius)
  void ctf_FlagTouch()
  {
        if(gameover) { return; }
-       
        entity toucher = other;
-       
        // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
        if(ITEM_TOUCH_NEEDKILL())
        {
                ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
                return;
        }
-       
        // special touch behaviors
 -      if(toucher.vehicle_flags & VHF_ISVEHICLE)
 +      if(toucher.frozen) { return; }
 +      else if(toucher.vehicle_flags & VHF_ISVEHICLE)
        {
                if(autocvar_g_ctf_allow_vehicle_touch)
                        toucher = toucher.owner; // the player is actually the vehicle owner, not other
                else
                        return; // do nothing
        }
 +      else if(toucher.flags & FL_MONSTER)
 +      {
 +              if(!autocvar_g_ctf_allow_monster_touch)
 +                      return; // do nothing
 +      }
        else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world
        {
                if(time > self.wait) // if we haven't in a while, play a sound/effect
        }
        else if(toucher.deadflag != DEAD_NO) { return; }
  
-       switch(self.ctf_status) 
-       {       
+       switch(self.ctf_status)
+       {
                case FLAG_BASE:
                {
 -                      if(SAME_TEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self))
 +                      if(SAME_TEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && !(toucher.flags & FL_MONSTER))
                                ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base
 -                      else if(DIFF_TEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time))
 +                      else if(DIFF_TEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && !(toucher.flags & FL_MONSTER))
                                ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag
                        break;
                }
-               
                case FLAG_DROPPED:
                {
                        if(SAME_TEAM(toucher, self))
                                ctf_Handle_Return(self, toucher); // toucher just returned his own flag
 -                      else if((!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
 +                      else if(!(toucher.flags & FL_MONSTER) && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
                                ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
                        break;
                }
-                       
                case FLAG_CARRY:
                {
                        dprint("Someone touched a flag even though it was being carried?\n");
                        break;
                }
-               
                case FLAG_PASSING:
                {
                        if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender))
@@@ -858,15 -847,15 +858,15 @@@ void ctf_RespawnFlag(entity flag
                { backtrace("flag respawn called twice quickly! please notify Samual about this..."); }
  
        flag.last_respawn = time;
-       
        // reset the player (if there is one)
        if((flag.owner) && (flag.owner.flagcarried == flag))
        {
                if(flag.owner.wps_enemyflagcarrier)
                        WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
-                       
                WaypointSprite_Kill(flag.wps_flagcarrier);
-               
                flag.owner.flagcarried = world;
  
                if(flag.speedrunning)
        // reset the flag
        setattachment(flag, world, "");
        setorigin(flag, flag.ctf_spawnorigin);
-       
        flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS);
        flag.takedamage = DAMAGE_NO;
        flag.health = flag.max_flag_health;
        flag.velocity = '0 0 0';
        flag.angles = flag.mangle;
        flag.flags = FL_ITEM | FL_NOTARGET;
-       
        flag.ctf_status = FLAG_BASE;
        flag.owner = world;
        flag.pass_distance = 0;
@@@ -903,7 -892,7 +903,7 @@@ void ctf_Reset(
        if(self.owner)
                if(IS_PLAYER(self.owner))
                        ctf_Handle_Throw(self.owner, world, DROP_RESET);
-                       
        ctf_RespawnFlag(self);
  }
  
@@@ -922,12 -911,12 +922,12 @@@ void ctf_DelayedFlagSetup(void) // call
        ctf_CaptureShield_Spawn(self);
  }
  
- void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc 
- {     
+ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
+ {
        // declarations
-       teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1. 
+       teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1.
        self = flag; // for later usage with droptofloor()
-       
        // main setup
        flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist
        ctf_worldflaglist = flag;
        flag.flags = FL_ITEM | FL_NOTARGET;
        flag.solid = SOLID_TRIGGER;
        flag.takedamage = DAMAGE_NO;
-       flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale;   
+       flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale;
        flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100);
        flag.health = flag.max_flag_health;
        flag.event_damage = ctf_FlagDamage;
        if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber) ? "redflag_touch" : "blueflag_touch"); }
        if(flag.passeffect == "")  { flag.passeffect = ((teamnumber) ? "red_pass" : "blue_pass"); }
        if(flag.capeffect == "")   { flag.capeffect = ((teamnumber) ? "red_cap" : "blue_cap"); }
-       
-       // sound 
+       // sound
        if(flag.snd_flag_taken == "")    { flag.snd_flag_taken  = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
        if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
        if(flag.snd_flag_capture == "")  { flag.snd_flag_capture = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
        if(flag.snd_flag_dropped == "")  { flag.snd_flag_dropped = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
        if(flag.snd_flag_touch == "")    { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
        if(flag.snd_flag_pass == "")     { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
-       
        // precache
        precache_sound(flag.snd_flag_taken);
        precache_sound(flag.snd_flag_returned);
        setmodel(flag, flag.model); // precision set below
        setsize(flag, FLAG_MIN, FLAG_MAX);
        setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET));
-       
        if(autocvar_g_ctf_flag_glowtrails)
        {
                flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
                flag.glow_size = 25;
                flag.glow_trail = 1;
        }
-       
        flag.effects |= EF_LOWPRECISION;
        if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
        if(autocvar_g_ctf_dynamiclights)   { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
-       
        // flag placement
        if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location
-       {       
-               flag.dropped_origin = flag.origin; 
+       {
+               flag.dropped_origin = flag.origin;
                flag.noalign = TRUE;
                flag.movetype = MOVETYPE_NONE;
        }
        else // drop to floor, automatically find a platform and set that as spawn origin
-       { 
+       {
                flag.noalign = FALSE;
                self = flag;
                droptofloor();
-               flag.movetype = MOVETYPE_TOSS; 
-       }       
-       
+               flag.movetype = MOVETYPE_TOSS;
+       }
        InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION);
  }
  
@@@ -1686,12 -1675,12 +1686,12 @@@ void havocbot_role_ctf_setrole(entity b
  MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
  {
        entity flag;
-       
        // initially clear items so they can be set as necessary later.
        self.items &= ~(IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST
                | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
  
-       // scan through all the flags and notify the client about them 
+       // scan through all the flags and notify the client about them
        for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
        {
                switch(flag.ctf_status)
                        {
                                if((flag.owner == self) || (flag.pass_sender == self))
                                        self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag
-                               else 
+                               else
                                        self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag
                                break;
                        }
                        }
                }
        }
-       
        // item for stopping players from capturing the flag too often
        if(self.ctf_captureshielded)
                self.items |= IT_CTF_SHIELDED;
-       
        // update the health of the flag carrier waypointsprite
-       if(self.wps_flagcarrier) 
+       if(self.wps_flagcarrier)
                WaypointSprite_UpdateHealth(self.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
-       
        return FALSE;
  }
  
@@@ -1759,10 -1748,10 +1759,10 @@@ MUTATOR_HOOKFUNCTION(ctf_PlayerDies
                PlayerTeamScore_AddScore(frag_attacker, autocvar_g_ctf_score_kill);
                PlayerScore_Add(frag_attacker, SP_CTF_FCKILLS, 1);
        }
-                               
        if(frag_target.flagcarried)
                { ctf_Handle_Throw(frag_target, world, DROP_NORMAL); }
-               
        return FALSE;
  }
  
@@@ -1775,23 -1764,23 +1775,23 @@@ MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKi
  MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
  {
        entity flag; // temporary entity for the search method
-       
        if(self.flagcarried)
                { ctf_Handle_Throw(self, world, DROP_NORMAL); }
-       
        for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
        {
                if(flag.pass_sender == self) { flag.pass_sender = world; }
                if(flag.pass_target == self) { flag.pass_target = world; }
                if(flag.ctf_dropper == self) { flag.ctf_dropper = world; }
        }
-               
        return FALSE;
  }
  
  MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
  {
-       if(self.flagcarried) 
+       if(self.flagcarried)
        if(!autocvar_g_ctf_portalteleport)
                { ctf_Handle_Throw(self, world, DROP_NORMAL); }
  
  MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
  {
        if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
-       
        entity player = self;
  
        if((time > player.throw_antispam) && (player.deadflag == DEAD_NO) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch))
                {
                        entity head, closest_target = world;
                        head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, TRUE);
-                       
                        while(head) // find the closest acceptable target to pass to
                        {
                                if(IS_PLAYER(head) && head.deadflag == DEAD_NO)
                                if(head != player && SAME_TEAM(head, player))
                                if(!head.speedrunning && !head.vehicle)
                                {
-                                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) 
+                                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
                                        vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
                                        vector passer_center = CENTER_OR_VIEWOFS(player);
-                                       
                                        if(ctf_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest))
                                        {
-                                               if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) 
-                                               { 
+                                               if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried)
+                                               {
                                                        if(IS_BOT_CLIENT(head))
                                                        {
                                                                Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname);
                                                                Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_CTF_PASS_REQUESTED, player.netname);
                                                                Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname);
                                                        }
-                                                       player.throw_antispam = time + autocvar_g_ctf_pass_wait; 
-                                                       return TRUE; 
+                                                       player.throw_antispam = time + autocvar_g_ctf_pass_wait;
+                                                       return TRUE;
                                                }
                                                else if(player.flagcarried)
                                                {
                                }
                                head = head.chain;
                        }
-                       
                        if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return TRUE; }
                }
-               
                // throw the flag in front of you
                if(autocvar_g_ctf_throw && player.flagcarried)
                {
                                if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_time) { player.throw_count = 1; }
                                else { player.throw_count += 1; }
                                if(player.throw_count >= autocvar_g_ctf_throw_punish_count) { player.throw_count = -1; }
-                                       
                                player.throw_prevtime = time;
                                ctf_Handle_Throw(player, world, DROP_THROW);
                                return TRUE;
                        }
                }
        }
-               
        return FALSE;
  }
  
@@@ -1897,7 -1886,7 +1897,7 @@@ MUTATOR_HOOKFUNCTION(ctf_HelpMePing
        {
                self.wps_helpme_time = time;
                WaypointSprite_HelpMePing(self.wps_flagcarrier);
-       } 
+       }
        else // create a normal help me waypointsprite
        {
                WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, FALSE, RADARICON_HELPME, '1 0.5 0');
@@@ -1916,15 -1905,15 +1916,15 @@@ MUTATOR_HOOKFUNCTION(ctf_VehicleEnter
                        ctf_Handle_Throw(vh_player, world, DROP_NORMAL);
                }
                else
-               {            
-                       setattachment(vh_player.flagcarried, vh_vehicle, ""); 
+               {
+                       setattachment(vh_player.flagcarried, vh_vehicle, "");
                        setorigin(vh_player.flagcarried, VEHICLE_FLAG_OFFSET);
                        vh_player.flagcarried.scale = VEHICLE_FLAG_SCALE;
-                       //vh_player.flagcarried.angles = '0 0 0';       
+                       //vh_player.flagcarried.angles = '0 0 0';
                }
                return TRUE;
        }
-               
        return FALSE;
  }
  
@@@ -1932,7 -1921,7 +1932,7 @@@ MUTATOR_HOOKFUNCTION(ctf_VehicleExit
  {
        if(vh_player.flagcarried)
        {
-               setattachment(vh_player.flagcarried, vh_player, ""); 
+               setattachment(vh_player.flagcarried, vh_player, "");
                setorigin(vh_player.flagcarried, FLAG_CARRY_OFFSET);
                vh_player.flagcarried.scale = FLAG_SCALE;
                vh_player.flagcarried.angles = '0 0 0';
@@@ -1950,14 -1939,14 +1950,14 @@@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun
                ctf_RespawnFlag(self.flagcarried);
                return TRUE;
        }
-       
        return FALSE;
  }
  
  MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
  {
        entity flag; // temporary entity for the search method
-       
        for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
        {
                switch(flag.ctf_status)
                                flag.takedamage = DAMAGE_NO;
                                flag.solid = SOLID_NOT;
                                flag.nextthink = FALSE; // stop thinking
-                               
                                //dprint("stopping the ", flag.netname, " from moving.\n");
                                break;
                        }
-                       
                        default:
                        case FLAG_BASE:
                        case FLAG_CARRY:
                        }
                }
        }
-       
        return FALSE;
  }
  
@@@ -2005,7 -1994,7 +2005,7 @@@ Keys: "angle" viewing angle when spawni
  void spawnfunc_info_player_team1()
  {
        if(g_assault) { remove(self); return; }
-       
        self.team = NUM_TEAM_1; // red
        spawnfunc_info_player_deathmatch();
  }
@@@ -2017,7 -2006,7 +2017,7 @@@ Keys: "angle" viewing angle when spawni
  void spawnfunc_info_player_team2()
  {
        if(g_assault) { remove(self); return; }
-       
        self.team = NUM_TEAM_2; // blue
        spawnfunc_info_player_deathmatch();
  }
@@@ -2028,7 -2017,7 +2028,7 @@@ Keys: "angle" viewing angle when spawni
  void spawnfunc_info_player_team3()
  {
        if(g_assault) { remove(self); return; }
-       
        self.team = NUM_TEAM_3; // yellow
        spawnfunc_info_player_deathmatch();
  }
@@@ -2040,20 -2029,20 +2040,20 @@@ Keys: "angle" viewing angle when spawni
  void spawnfunc_info_player_team4()
  {
        if(g_assault) { remove(self); return; }
-       
        self.team = NUM_TEAM_4; // purple
        spawnfunc_info_player_deathmatch();
  }
  
  /*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
  CTF flag for team one (Red).
- Keys: 
- "angle" Angle the flag will point (minus 90 degrees)... 
+ Keys:
+ "angle" Angle the flag will point (minus 90 degrees)...
  "model" model to use, note this needs red and blue as skins 0 and 1...
  "noise" sound played when flag is picked up...
  "noise1" sound played when flag is returned by a teammate...
  "noise2" sound played when flag is captured...
- "noise3" sound played when flag is lost in the field and respawns itself... 
+ "noise3" sound played when flag is lost in the field and respawns itself...
  "noise4" sound played when flag is dropped by a player...
  "noise5" sound played when flag touches the ground... */
  void spawnfunc_item_flag_team1()
  
  /*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
  CTF flag for team two (Blue).
- Keys: 
- "angle" Angle the flag will point (minus 90 degrees)... 
+ Keys:
+ "angle" Angle the flag will point (minus 90 degrees)...
  "model" model to use, note this needs red and blue as skins 0 and 1...
  "noise" sound played when flag is picked up...
  "noise1" sound played when flag is returned by a teammate...
  "noise2" sound played when flag is captured...
- "noise3" sound played when flag is lost in the field and respawns itself... 
+ "noise3" sound played when flag is lost in the field and respawns itself...
  "noise4" sound played when flag is dropped by a player...
  "noise5" sound played when flag touches the ground... */
  void spawnfunc_item_flag_team2()
@@@ -2090,7 -2079,7 +2090,7 @@@ Keys
  void spawnfunc_ctf_team()
  {
        if(!g_ctf) { remove(self); return; }
-       
        self.classname = "ctf_team";
        self.team = self.cnt + 1;
  }
@@@ -2146,7 -2135,7 +2146,7 @@@ void ctf_DelayedInit() // Do this chec
                ctf_SpawnTeam("Red", NUM_TEAM_1 - 1);
                ctf_SpawnTeam("Blue", NUM_TEAM_2 - 1);
        }
-       
        ctf_ScoreRules();
  }
  
@@@ -2157,7 -2146,7 +2157,7 @@@ void ctf_Initialize(
        ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
        ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
        ctf_captureshield_force = autocvar_g_ctf_shield_force;
-       
        InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
  }
  
@@@ -2178,7 -2167,7 +2178,7 @@@ MUTATOR_DEFINITION(gamemode_ctf
        MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
        MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY);
-       
        MUTATOR_ONADD
        {
                if(time > 1) // game loads at time 1
index 5a8a6a5655b1aeb93d52df77a9312e40878838cd,9bc06522374aaae4e22b810d3c1a73afbaf3b46c..029a9b58fe35064deec989b3a2ee22c5d56d2d4a
@@@ -1,6 -1,7 +1,6 @@@
  .float freezetag_frozen_time;
  .float freezetag_frozen_timeout;
  .float freezetag_revive_progress;
 -.entity freezetag_ice;
  #define ICE_MAX_ALPHA 1
  #define ICE_MIN_ALPHA 0.1
  float freezetag_teams;
@@@ -13,22 -14,22 +13,22 @@@ void freezetag_count_alive_players(
                if(e.team == NUM_TEAM_1 && e.health >= 1)
                {
                        ++total_players;
 -                      if (!e.freezetag_frozen) ++redalive;
 +                      if (e.frozen != 1) ++redalive;
                }
                else if(e.team == NUM_TEAM_2 && e.health >= 1)
                {
                        ++total_players;
 -                      if (!e.freezetag_frozen) ++bluealive;
 +                      if (e.frozen != 1) ++bluealive;
                }
                else if(e.team == NUM_TEAM_3 && e.health >= 1)
                {
                        ++total_players;
 -                      if (!e.freezetag_frozen) ++yellowalive;
 +                      if (e.frozen != 1) ++yellowalive;
                }
                else if(e.team == NUM_TEAM_4 && e.health >= 1)
                {
                        ++total_players;
 -                      if (!e.freezetag_frozen) ++pinkalive;
 +                      if (e.frozen != 1) ++pinkalive;
                }
        }
        FOR_EACH_REALCLIENT(e) {
@@@ -127,6 -128,15 +127,6 @@@ float freezetag_CheckWinner(
        return 1;
  }
  
 -// this is needed to allow the player to turn his view around (fixangle can't
 -// be used to freeze his view, as that also changes the angles), while not
 -// turning that ice object with the player
 -void freezetag_Ice_Think()
 -{
 -      setorigin(self, self.owner.origin - '0 0 16');
 -      self.nextthink = time;
 -}
 -
  void freezetag_Add_Score(entity attacker)
  {
        if(attacker == self)
  
  void freezetag_Freeze(entity attacker)
  {
 -      if(self.freezetag_frozen)
 +      if(self.frozen)
                return;
 -      self.freezetag_frozen = 1;
 -      self.freezetag_frozen_time = time;
 -      self.freezetag_revive_progress = 0;
 -      self.health = 1;
 -      if(autocvar_g_freezetag_frozen_maxtime > 0)
 -              self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
 -
 +      
 +      Freeze(self, 0, 1, TRUE);
 +      
        freezetag_count_alive_players();
  
 -      entity ice;
 -      ice = spawn();
 -      ice.owner = self;
 -      ice.classname = "freezetag_ice";
 -      ice.think = freezetag_Ice_Think;
 -      ice.nextthink = time;
 -      ice.frame = floor(random() * 21); // ice model has 20 different looking frames
 -      ice.alpha = ICE_MAX_ALPHA;
 -      ice.colormod = Team_ColorRGB(self.team);
 -      ice.glowmod = ice.colormod;
 -      setmodel(ice, "models/ice/ice.md3");
 -
 -      self.freezetag_ice = ice;
 -
 -      RemoveGrapplingHook(self);
 -
 -      // add waypoint
 -      WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
 -
        freezetag_Add_Score(attacker);
  }
  
  void freezetag_Unfreeze(entity attacker)
  {
 -      self.freezetag_frozen = 0;
        self.freezetag_frozen_time = 0;
        self.freezetag_frozen_timeout = 0;
 -      self.freezetag_revive_progress = 0;
 -
 -      remove(self.freezetag_ice);
 -      self.freezetag_ice = world;
 -
 -      if(self.waypointsprite_attached)
 -              WaypointSprite_Kill(self.waypointsprite_attached);
 +      
 +      Unfreeze(self);
  }
  
 -
  // ================
  // Bot player logic
  // ================
@@@ -181,7 -221,7 +181,7 @@@ void havocbot_goalrating_freeplayers(fl
        {
                if ((head != self) && (head.team == self.team))
                {
 -                      if (head.freezetag_frozen)
 +                      if (head.frozen == 1)
                        {
                                distance = vlen(head.origin - org);
                                if (distance > sradius)
@@@ -213,12 -253,12 +213,12 @@@ void havocbot_role_ft_offense(
        unfrozen = 0;
        FOR_EACH_PLAYER(head)
        {
 -              if ((head.team == self.team) && (!head.freezetag_frozen))
 +              if ((head.team == self.team) && (head.frozen != 1))
                        unfrozen++;
        }
  
        // If only one left on team or if role has timed out then start trying to free players.
 -      if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
 +      if (((unfrozen == 0) && (!self.frozen)) || (time > self.havocbot_role_timeout))
        {
                dprint("changing role to freeing\n");
                self.havocbot_role = havocbot_role_ft_freeing;
@@@ -286,7 -326,7 +286,7 @@@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDi
        if(round_handler_IsActive())
        if(round_handler_CountdownRunning())
        {
 -              if(self.freezetag_frozen)
 +              if(self.frozen)
                        freezetag_Unfreeze(world);
                freezetag_count_alive_players();
                return 1; // let the player die so that he can respawn whenever he wants
                || frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
        {
                // let the player die, he will be automatically frozen when he respawns
 -              if(!self.freezetag_frozen)
 +              if(self.frozen != 1)
                {
                        freezetag_Add_Score(frag_attacker);
                        freezetag_count_alive_players();
                return 1;
        }
  
 -      if(self.freezetag_frozen)
 +      if(self.frozen)
                return 1;
  
        freezetag_Freeze(frag_attacker);
                Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
        }
  
 -      frag_target.health = 1; // "respawn" the player :P
 -
        return 1;
  }
  
@@@ -359,7 -401,7 +359,7 @@@ MUTATOR_HOOKFUNCTION(freezetag_reset_ma
  {
        FOR_EACH_PLAYER(self)
        {
 -              if (self.freezetag_frozen)
 +              if (self.frozen)
                        freezetag_Unfreeze(world);
                self.freezetag_frozen_timeout = -1;
                PutClientInServer();
@@@ -383,7 -425,7 +383,7 @@@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPr
        if(gameover)
                return 1;
  
 -      if(self.freezetag_frozen)
 +      if(self.frozen == 1)
        {
                // keep health = 1
                self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
        entity o;
        o = world;
        if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
 -              self.freezetag_ice.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
 +              self.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
  
        if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
                n = -1;
                n = 0;
                FOR_EACH_PLAYER(other) if(self != other)
                {
 -                      if(other.freezetag_frozen == 0)
 +                      if(other.frozen == 0)
                        {
 -                              if(other.team == self.team)
 +                              if(SAME_TEAM(other, self))
                                {
                                        if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
                                        {
                                                if(!o)
                                                        o = other;
 -                                              if(self.freezetag_frozen)
 +                                              if(self.frozen == 1)
                                                        other.reviving = TRUE;
                                                ++n;
                                        }
                }
        }
  
 -      if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
 +      if(n && self.frozen == 1) // OK, there is at least one teammate reviving us
        {
 -              self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
 -              self.health = max(1, self.freezetag_revive_progress * autocvar_g_balance_health_start);
 +              self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
 +              self.health = max(1, self.revive_progress * autocvar_g_balance_health_start);
  
 -              if(self.freezetag_revive_progress >= 1)
 +              if(self.revive_progress >= 1)
                {
                        freezetag_Unfreeze(self);
                        freezetag_count_alive_players();
                {
                        if(other.reviving)
                        {
 -                              other.freezetag_revive_progress = self.freezetag_revive_progress;
 +                              other.revive_progress = self.revive_progress;
                                other.reviving = FALSE;
                        }
                }
        }
 -      else if(!n && self.freezetag_frozen) // only if no teammate is nearby will we reset
 +      else if(!n && self.frozen == 1) // only if no teammate is nearby will we reset
        {
 -              self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
 -              self.health = max(1, self.freezetag_revive_progress * autocvar_g_balance_health_start);
 +              self.revive_progress = bound(0, self.revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
 +              self.health = max(1, self.revive_progress * autocvar_g_balance_health_start);
        }
 -      else if(!n)
 +      else if(!n && !self.frozen)
        {
 -              self.freezetag_revive_progress = 0; // thawing nobody
 +              self.revive_progress = 0; // thawing nobody
        }
  
        return 1;
  }
  
 -MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
 -{
 -      if(self.freezetag_frozen)
 -      {
 -              if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
 -              {
 -                      self.movement_x = bound(-5, self.movement_x, 5);
 -                      self.movement_y = bound(-5, self.movement_y, 5);
 -                      self.movement_z = bound(-5, self.movement_z, 5);
 -              }
 -              else
 -                      self.movement = '0 0 0';
 -
 -              self.disableclientprediction = 1;
 -      }
 -      return 1;
 -}
 -
  MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
  {
 -      if(frag_target.freezetag_frozen && frag_deathtype != DEATH_HURTTRIGGER)
 +      if(frag_target.frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
        {
                if(autocvar_g_freezetag_revive_falldamage > 0)
                if(frag_deathtype == DEATH_FALL)
                        Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, frag_target.netname);
                        Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_FALL);
                }
-       
                frag_damage = 0;
                frag_force = frag_force * autocvar_g_freezetag_frozen_force;
        }
        return 1;
  }
  
 -MUTATOR_HOOKFUNCTION(freezetag_PlayerJump)
 -{
 -      if(self.freezetag_frozen)
 -              return TRUE; // no jumping in freezetag when frozen
 -
 -      return FALSE;
 -}
 -
 -MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
 -{
 -      if (self.freezetag_frozen)
 -              return 1;
 -      return 0;
 -}
 -
 -MUTATOR_HOOKFUNCTION(freezetag_ItemTouch)
 -{
 -      if (other.freezetag_frozen)
 -              return MUT_ITEMTOUCH_RETURN;
 -      return MUT_ITEMTOUCH_CONTINUE;
 -}
 -
  MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
  {
        if (!self.deadflag)
        return TRUE;
  }
  
 -MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
 -{
 -      self.freezetag_frozen = other.freezetag_frozen;
 -      self.freezetag_revive_progress = other.freezetag_revive_progress;
 -      return 0;
 -}
 -
  MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
  {
        freezetag_teams = autocvar_g_freezetag_teams_override;
        return 0;
  }
  
 -MUTATOR_HOOKFUNCTION(freezetag_VehicleTouch)
 -{
 -      if(other.freezetag_frozen)
 -              return TRUE;
 -
 -      return FALSE;
 -}
 -
  void freezetag_Initialize()
  {
        precache_model("models/ice/ice.md3");
        addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
        addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
        addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
 -
 -      addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
 -      addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
  }
  
  MUTATOR_DEFINITION(gamemode_freezetag)
        MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
        MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
 -      MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(PlayerJump, freezetag_PlayerJump, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
        MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
 -      MUTATOR_HOOK(VehicleTouch, freezetag_VehicleTouch, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {
index de8b13e44b3ebbf4ad23caf7214a11b9f1da0d5e,0000000000000000000000000000000000000000..f7442c1ca03e53a6462863f17e9fdc01a2402ab9
mode 100644,000000..100644
--- /dev/null
@@@ -1,335 -1,0 +1,335 @@@
-       
 +void invasion_spawnpoint()
 +{
 +      if(!g_invasion) { remove(self); return; }
-       
++
 +      self.classname = "invasion_spawnpoint";
 +}
 +
 +float invasion_PickMonster(float supermonster_count)
 +{
 +      if(autocvar_g_invasion_zombies_only)
 +              return MON_ZOMBIE;
 +
 +      float i;
 +      entity mon;
-       
++
 +      RandomSelection_Init();
-               
++
 +      for(i = MON_FIRST; i <= MON_LAST; ++i)
 +      {
 +              mon = get_monsterinfo(i);
 +              if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM) || (mon.spawnflags & MON_FLAG_SUPERMONSTER && supermonster_count >= 1))
 +                      continue; // flying/swimming monsters not yet supported
-       
++
 +              RandomSelection_Add(world, i, "", 1, 1);
 +      }
-       
++
 +      return RandomSelection_chosen_float;
 +}
 +
 +entity invasion_PickSpawn()
 +{
 +      entity e;
-       
++
 +      RandomSelection_Init();
-               
++
 +      for(e = world;(e = find(e, classname, "invasion_spawnpoint")); )
 +              RandomSelection_Add(e, 0, string_null, 1, 1);
-       
++
 +      return RandomSelection_chosen_ent;
 +}
 +
 +void invasion_SpawnChosenMonster(float mon)
 +{
 +      entity spawn_point, monster;
-       
++
 +      spawn_point = invasion_PickSpawn();
-       
++
 +      if(spawn_point == world)
 +      {
 +              dprint("Warning: couldn't find any invasion_spawnpoint spawnpoints, no monsters will spawn!\n");
 +              return;
 +      }
-       
++
 +      monster = spawnmonster("", mon, spawn_point, spawn_point, spawn_point.origin, FALSE, 2);
-       
++
 +      if(roundcnt >= maxrounds)
 +              monster.spawnflags |= MONSTERFLAG_MINIBOSS;
 +}
 +
 +void invasion_SpawnMonsters(float supermonster_count)
 +{
 +      float chosen_monster = invasion_PickMonster(supermonster_count);
-               
++
 +      invasion_SpawnChosenMonster(chosen_monster);
 +}
 +
 +float Invasion_CheckWinner()
 +{
 +      entity head;
 +      if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
 +      {
 +              FOR_EACH_MONSTER(head)
 +                      monster_remove(head);
-       
++
 +              if(roundcnt >= maxrounds)
 +              {
 +                      NextLevel();
 +                      return 1;
 +              }
-       
++
 +              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
 +              round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
 +              return 1;
 +      }
-               
++
 +      // boss round
 +      if(roundcnt >= maxrounds)
 +      {
 +              if(numspawned < 1)
 +              {
 +                      maxspawned = 1;
 +                      invasion_SpawnMonsters(0);
 +              }
 +      }
 +      else
 +      {
 +              float total_alive_monsters = 0, supermonster_count = 0;
-                       
++
 +              FOR_EACH_MONSTER(head) if(head.health > 0)
 +              {
 +                      if((get_monsterinfo(head.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
 +                              ++supermonster_count;
 +                      ++total_alive_monsters;
 +              }
 +
 +              if((total_alive_monsters + numkilled) < maxspawned && maxcurrent < 10) // 10 at a time should be plenty
 +              {
 +                      if(time >= last_check)
 +                      {
 +                              invasion_SpawnMonsters(supermonster_count);
 +                              last_check = time + 2;
 +                      }
-       
++
 +                      return 0;
 +              }
 +      }
-       
++
 +      if(numspawned < 1 || numkilled < maxspawned)
 +              return 0; // nothing has spawned yet, or there are still alive monsters
-       
++
 +      if(roundcnt >= maxrounds)
 +      {
 +              NextLevel();
 +              return 1;
 +      }
-       
++
 +      entity winner = world;
 +      float winning_score = 0;
-       
++
 +      FOR_EACH_PLAYER(head)
 +      {
 +              float cs = PlayerScore_Add(head, SP_KILLS, 0);
 +              if(cs > winning_score)
 +              {
 +                      winning_score = cs;
 +                      winner = head;
 +              }
 +      }
-       
++
 +      FOR_EACH_MONSTER(head)
 +              monster_remove(head);
 +
 +      if(winner)
 +      {
 +              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, winner.netname);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, winner.netname);
 +      }
-               
++
 +      round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
 +
 +      return 1;
 +}
 +
 +float Invasion_CheckPlayers()
 +{
 +      return TRUE;
 +}
 +
 +void Invasion_RoundStart()
 +{
 +      entity e;
 +      float numplayers = 0;
 +      FOR_EACH_PLAYER(e)
 +      {
 +              e.player_blocked = 0;
 +              ++numplayers;
 +      }
-       
++
 +      roundcnt += 1;
-       
++
 +      invasion_monsterskill = roundcnt + (numplayers * 0.3);
-               
++
 +      maxcurrent = 0;
 +      numspawned = 0;
 +      numkilled = 0;
-       
++
 +      if(roundcnt > 1)
 +              maxspawned = rint(autocvar_g_invasion_monster_count * (roundcnt * 0.5));
 +      else
 +              maxspawned = autocvar_g_invasion_monster_count;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_MonsterDies)
 +{
 +      if(!self.monster_respawned)
 +      {
 +              numkilled += 1;
 +              maxcurrent -= 1;
-       
++
 +              if(IS_PLAYER(frag_attacker))
 +                      PlayerScore_Add(frag_attacker, SP_KILLS, +1);
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_MonsterSpawn)
 +{
 +      if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
 +      {
 +              monster_remove(self);
 +              return FALSE;
 +      }
-       
++
 +      if(roundcnt < maxrounds && self.spawnflags & MONSTERFLAG_MINIBOSS)
 +              self.spawnflags &= ~MONSTERFLAG_MINIBOSS;
-       
++
 +      if(!self.monster_respawned)
 +      {
 +              numspawned += 1;
 +              maxcurrent += 1;
 +      }
-       
++
 +      self.monster_skill = invasion_monsterskill;
-       
++
 +      if((get_monsterinfo(self.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
 +              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, M_NAME(self.monsterid));
-       
++
 +      self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
-       
++
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_PlayerThink)
 +{
 +      monsters_total = maxspawned; // TODO: make sure numspawned never exceeds maxspawned
 +      monsters_killed = numkilled;
-       
++
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_PlayerSpawn)
 +{
 +      self.bot_attack = FALSE;
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_PlayerDamage)
 +{
 +      if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target) && frag_attacker != frag_target)
 +      {
 +              frag_damage = 0;
 +              frag_force = '0 0 0';
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_PlayerCommand)
 +{
 +      if(MUTATOR_RETURNVALUE) // command was already handled?
 +              return FALSE;
-               
++
 +      if(cmd_name == "debuginvasion")
 +      {
 +              sprint(self, strcat("maxspawned = ", ftos(maxspawned), "\n"));
 +              sprint(self, strcat("numspawned = ", ftos(numspawned), "\n"));
 +              sprint(self, strcat("numkilled = ", ftos(numkilled), "\n"));
 +              sprint(self, strcat("roundcnt = ", ftos(roundcnt), "\n"));
 +              sprint(self, strcat("monsters_total = ", ftos(monsters_total), "\n"));
 +              sprint(self, strcat("monsters_killed = ", ftos(monsters_killed), "\n"));
 +              sprint(self, strcat("invasion_monsterskill = ", ftos(invasion_monsterskill), "\n"));
-       
++
 +              return TRUE;
 +      }
-       
++
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(invasion_SetStartItems)
 +{
 +      start_health = 200;
 +      start_armorvalue = 200;
-       
++
 +      return FALSE;
 +}
 +
 +void invasion_ScoreRules()
 +{
 +      ScoreRules_basics(0, 0, 0, FALSE);
 +      ScoreInfo_SetLabel_PlayerScore(SP_KILLS, "frags", SFL_SORT_PRIO_PRIMARY);
 +      ScoreRules_basics_end();
 +}
 +
 +void invasion_Initialize()
 +{
 +      independent_players = 1; // to disable extra useless scores
 +
 +      invasion_ScoreRules();
-       
++
 +      independent_players = 0;
 +
 +      round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
 +      round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
-       
++
 +      allowed_to_spawn = TRUE;
-       
++
 +      roundcnt = 0;
 +}
 +
 +MUTATOR_DEFINITION(gamemode_invasion)
 +{
 +      MUTATOR_HOOK(MonsterDies, invasion_MonsterDies, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MonsterSpawn, invasion_MonsterSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerPreThink, invasion_PlayerThink, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerSpawn, invasion_PlayerSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerDamage_Calculate, invasion_PlayerDamage, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SV_ParseClientCommand, invasion_PlayerCommand, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SetStartItems, invasion_SetStartItems, CBC_ORDER_ANY);
-               
++
 +      MUTATOR_ONADD
 +      {
 +              if(time > 1) // game loads at time 1
 +                      error("This is a game type and it cannot be added at runtime.");
 +              invasion_Initialize();
++
 +              cvar_settemp("g_monsters", "1");
 +      }
 +
 +      MUTATOR_ONROLLBACK_OR_REMOVE
 +      {
 +              // we actually cannot roll back invasion_Initialize here
 +              // BUT: we don't need to! If this gets called, adding always
 +              // succeeds.
 +      }
 +
 +      MUTATOR_ONREMOVE
 +      {
 +              print("This is a game type and it cannot be removed at runtime.");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
index 3c3e97e4d72e12f3feb4cfdad0337b0d30036ca7,6f01db2cdf567766fffe3a4c2fa3c9d54b9049fa..dc2398609f1d08e821514e7bd6b51ceaf813a871
@@@ -407,12 -407,12 +407,12 @@@ void onslaught_generator_think(
                                                d = d + 1;
                                e = e.chain;
                        }
-                       
                        if(autocvar_g_campaign && autocvar__campaign_testrun)
                                d = d * self.max_health;
                        else
                                d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
-                       
                        Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
                }
                else if (overtime_msg_time)
@@@ -910,13 -910,13 +910,13 @@@ void spawnfunc_onslaught_generator(
        precache_sound("onslaught/electricity_explode.wav");
        if (!self.team)
                objerror("team must be set");
-       
        if(self.team == NUM_TEAM_1)
          ons_red_generator = self;
  
        if(self.team == NUM_TEAM_2)
          ons_blue_generator = self;
-         
        self.team_saved = self.team;
        self.colormap = 1024 + (self.team - 1) * 17;
        self.solid = SOLID_BBOX;
        waypoint_spawnforitem(self);
  
        onslaught_updatelinks();
-       
        self.reset = onslaught_generator_reset;
  }
  
@@@ -1084,7 -1084,7 +1084,7 @@@ void onslaught_controlpoint_icon_think(
          float _friendly_count = 0;
          float _dist;
          entity _player;
-         
          FOR_EACH_PLAYER(_player)
          {
              if(!_player.deadflag)
@@@ -1226,7 -1226,7 +1226,7 @@@ void onslaught_controlpoint_icon_buildt
        a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
        if(!a)
                return;
-     
        self.health = self.health + self.count;
  
        if (self.health >= self.max_health)
@@@ -1304,6 -1304,12 +1304,12 @@@ void onslaught_controlpoint_touch(
        onslaught_updatelinks();
  }
  
+ void onslaught_controlpoint_think()
+ {
+       self.nextthink = time;
+       CSQCMODEL_AUTOUPDATE();
+ }
  void onslaught_controlpoint_reset()
  {
        if(self.goalentity && self.goalentity != world)
        self.isshielded = TRUE;
        self.enemy.solid = SOLID_NOT;
        self.enemy.colormap = self.colormap;
-       self.think = self.enemy.think = func_null;
-       self.nextthink = 0; // don't like func_null :P
+       self.think = onslaught_controlpoint_think;
+       self.enemy.think = func_null;
+       self.nextthink = time; // don't like func_null :P
        setmodel(self, "models/onslaught/controlpoint_pad.md3");
        //setsize(self, '-32 -32 0', '32 32 8');
  
  
        activator = self;
        SUB_UseTargets(); // to reset the structures, playerspawns etc.
+       
+       CSQCMODEL_AUTOUPDATE();
  }
  
  /*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
@@@ -1397,6 -1406,9 +1406,9 @@@ void spawnfunc_onslaught_controlpoint(
        self.enemy.colormap = self.colormap;
  
        waypoint_spawnforitem(self);
+       
+       self.think = onslaught_controlpoint_think;
+       self.nextthink = time;
  
        WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
        WaypointSprite_UpdateRule(self.sprite, NUM_TEAM_2, SPRITERULE_TEAMPLAY);
        onslaught_updatelinks();
  
        self.reset = onslaught_controlpoint_reset;
+       
+       CSQCMODEL_AUTOINIT();
  }
  
  float onslaught_link_send(entity to, float sendflags)
@@@ -1524,24 -1538,24 +1538,24 @@@ MUTATOR_HOOKFUNCTION(ons_BuildMutatorsP
  
  MUTATOR_HOOKFUNCTION(ons_Spawn_Score)
  {
-     
      /*
      float _neer_home = (random() > 0.5 ? TRUE : FALSE);
-     
        RandomSelection_Init();
-       
        if(self.team == NUM_TEAM_1)
          RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1);
-       
        if(self.team == NUM_TEAM_2)
          RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1);
-       
        entity _cp = findchain(classname, "onslaught_controlpoint"):
        while _cp;
        {
-           if(_cp.team == self.team)            
+           if(_cp.team == self.team)
              RandomSelection_Add(_cp, 0, string_null, 1, 1);
-               
                _cp = _cp.chain;
        }
  
        }
        else if(self.team == spawn_spot.team)
                spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
-     
      */
-     
        return 0;
  }
  
@@@ -1562,55 -1576,55 +1576,55 @@@ MUTATOR_HOOKFUNCTION(ons_PlayerSpawn
  {
      if(!autocvar_g_onslaught_spawn_at_controlpoints)
          return 0;
-         
      if(random() < 0.5)  // 50/50 chane to use default spawnsystem.
          return 0;
-     
      float _close_to_home = ((random() > 0.5) ? TRUE : FALSE);
      entity _best = world, _trg_gen = world;
      float _score, _best_score = MAX_SHOT_DISTANCE;
-     
        RandomSelection_Init();
-     
        if(self.team == NUM_TEAM_1)
        {
            if(!_close_to_home)
              _trg_gen = ons_blue_generator;
-         else    
-             _trg_gen  = ons_red_generator;        
+         else
+             _trg_gen  = ons_red_generator;
        }
-       
        if(self.team == NUM_TEAM_2)
        {
            if(_close_to_home)
              _trg_gen = ons_blue_generator;
-         else    
-             _trg_gen  = ons_red_generator;        
+         else
+             _trg_gen  = ons_red_generator;
        }
-       
        entity _cp = findchain(classname, "onslaught_controlpoint");
        while(_cp)
        {
-           if(_cp.team == self.team)            
-         {            
+           if(_cp.team == self.team)
+         {
              _score = vlen(_trg_gen.origin - _cp.origin);
              if(_score < _best_score)
              {
                  _best = _cp;
-                 _best_score = _score;            
+                 _best_score = _score;
              }
-         }             
+         }
                _cp = _cp.chain;
        }
-       
-     vector _loc;        
-     float i;    
+     vector _loc;
+     float i;
      if(_best)
      {
          for(i = 0; i < 10; ++i)
          {
              _loc = _best.origin + '0 0 96';
-             _loc += ('0 1 0' * random()) * 128; 
+             _loc += ('0 1 0' * random()) * 128;
              tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
              if(trace_fraction == 1.0 && !trace_startsolid)
              {
      {
          if(!autocvar_g_onslaught_spawn_at_generator)
              return 0;
-         
          _trg_gen = ((self.team == NUM_TEAM_1) ? ons_red_generator : ons_blue_generator);
-         
          for(i = 0; i < 10; ++i)
          {
              _loc = _trg_gen.origin + '0 0 96';
-             _loc += ('0 1 0' * random()) * 128; 
+             _loc += ('0 1 0' * random()) * 128;
              tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
              if(trace_fraction == 1.0 && !trace_startsolid)
              {
              }
          }
      }
-     
      return 0;
  }
  
 +MUTATOR_HOOKFUNCTION(ons_MonsterThink)
 +{
 +      entity e = find(world, targetname, self.target);
 +      if (e != world)
 +              self.team = e.team;
 +
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
 +{
 +      entity e, ee = world;
 +      
 +      if(self.targetname)
 +      {
 +              e = find(world,target,self.targetname);
 +              if(e != world)
 +              {
 +                      self.team = e.team;
 +                      ee = e;
 +              }
 +      }
 +      
 +      if(ee)
 +      {
 +        activator = ee;
 +        self.use();
 +    }
 +
 +      return FALSE;
 +}
 +
  MUTATOR_DEFINITION(gamemode_onslaught)
  {
        MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MonsterMove, ons_MonsterThink, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MonsterSpawn, ons_MonsterSpawn, CBC_ORDER_ANY);
        //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
index b979ee0642e55798771035fdf23a774837386023,3f808499a8d4a5bd015b28ceec3a86d62797b3b2..6e187b90c4c765fdcc109be419e82e62de60513e
@@@ -34,8 -34,8 +34,8 @@@ MUTATOR_HOOKFUNCTION(dodging_PlayerPhys
        float velocity_difference;
        float clean_up_and_do_nothing;
        float horiz_speed = autocvar_sv_dodging_horiz_speed;
-       
 -      if(self.freezetag_frozen)
 +      if(self.frozen)
                horiz_speed = autocvar_sv_dodging_horiz_speed_frozen;
  
      if (self.deadflag != DEAD_NO)
        // make sure v_up, v_right and v_forward are sane
        makevectors(self.angles);
  
-       // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code 
-       // will be called ramp_time/frametime times = 2 times. so, we need to 
+       // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
+       // will be called ramp_time/frametime times = 2 times. so, we need to
        // add 0.5 * the total speed each frame until the dodge action is done..
        common_factor = sys_frametime / autocvar_sv_dodging_ramp_time;
  
        // if ramp time is smaller than frametime we get problems ;D
-       if (common_factor > 1) 
+       if (common_factor > 1)
                common_factor = 1;
  
        new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
@@@ -81,8 -81,8 +81,8 @@@
                //disable jump key during dodge accel phase
                if (self.movement_z > 0) self.movement_z = 0;
  
-               self.velocity = 
-                         self.velocity 
+               self.velocity =
+                         self.velocity
                        + ((self.dodging_direction_y * velocity_difference) * v_right)
                        + ((self.dodging_direction_x * velocity_difference) * v_forward);
  
@@@ -93,8 -93,8 +93,8 @@@
        if (self.dodging_single_action == 1) {
                self.flags &= ~FL_ONGROUND;
  
-               self.velocity = 
-                         self.velocity 
+               self.velocity =
+                         self.velocity
                        + (autocvar_sv_dodging_up_speed * v_up);
  
                if (autocvar_sv_dodging_sound == 1)
@@@ -168,9 -168,9 +168,9 @@@ MUTATOR_HOOKFUNCTION(dodging_GetPressed
  
        tap_direction_x = 0;
        tap_direction_y = 0;
-       
        float frozen_dodging;
 -      frozen_dodging = (self.freezetag_frozen && autocvar_sv_dodging_frozen);
 +      frozen_dodging = (self.frozen && autocvar_sv_dodging_frozen);
  
        float dodge_detected;
        if (g_dodging == 0)
        if ((time - self.last_dodging_time) < autocvar_sv_dodging_delay)
                return 0;
  
-       if (check_close_to_ground(autocvar_sv_dodging_height_threshold) != 1 
+       if (check_close_to_ground(autocvar_sv_dodging_height_threshold) != 1
                && check_close_to_wall(autocvar_sv_dodging_wall_distance_threshold) != 1)
                return 0;
  
        if (self.movement_x > 0) {
                // is this a state change?
                if (!(self.pressedkeys & KEY_FORWARD) || frozen_dodging) {
-                       if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) { 
+                       if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) {
                                tap_direction_x = 1.0;
                                dodge_detected = 1;
                        }
                // is this a state change?
                if (!(self.pressedkeys & KEY_BACKWARD) || frozen_dodging) {
                        tap_direction_x = -1.0;
-                       if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        { 
+                       if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        {
                                dodge_detected = 1;
                        }
                        self.last_BACKWARD_KEY_time = time;
                // is this a state change?
                if (!(self.pressedkeys & KEY_RIGHT) || frozen_dodging) {
                        tap_direction_y = 1.0;
-                       if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   { 
+                       if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   {
                                dodge_detected = 1;
                        }
                        self.last_RIGHT_KEY_time = time;
                // is this a state change?
                if (!(self.pressedkeys & KEY_LEFT) || frozen_dodging) {
                        tap_direction_y = -1.0;
-                       if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    { 
+                       if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    {
                                dodge_detected = 1;
                        }
                        self.last_LEFT_KEY_time = time;
index e71ad760ea40efe3a8ec166035dec32e024a4caf,ac6e158ed4c824c2b8f41fb9d5422211c413ea14..1b7e03be34626b10d18ef77119fc8d24b8e525f8
@@@ -1,9 -1,9 +1,9 @@@
- void spawnfunc_item_minst_cells (void) 
+ void spawnfunc_item_minst_cells (void)
  {
        if (!g_minstagib) { remove(self); return; }
        if (!self.ammo_cells)
                self.ammo_cells = autocvar_g_minstagib_ammo_drop;
-               
        StartItem ("models/items/a_cells.md3",
                           "misc/itempickup.wav", 45, 0,
                           "MinstaNex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100);
@@@ -105,31 -105,15 +105,31 @@@ MUTATOR_HOOKFUNCTION(minstagib_MatchEnd
        entity head;
        FOR_EACH_PLAYER(head)
                minstagib_stop_countdown(head);
-               
        return FALSE;
  }
  
 +MUTATOR_HOOKFUNCTION(minstagib_MonsterLoot)
 +{
 +      other.monster_loot = spawnfunc_item_minst_cells;
 +
 +      return FALSE;
 +}
 +
 +MUTATOR_HOOKFUNCTION(minstagib_MonsterSpawn)
 +{
 +      // always refill ammo
 +      if(self.monsterid == MON_MAGE)
 +              self.skin = 1;
 +      
 +      return FALSE;
 +}
 +
  MUTATOR_HOOKFUNCTION(minstagib_BotShouldAttack)
  {
        if(checkentity.items & IT_STRENGTH)
                return TRUE;
-               
        return FALSE;
  }
  
@@@ -204,7 -188,7 +204,7 @@@ MUTATOR_HOOKFUNCTION(minstagib_PlayerPh
  {
        if(self.items & IT_INVINCIBLE)
                self.stat_sv_maxspeed = self.stat_sv_maxspeed * autocvar_g_minstagib_speed_highspeed;
-               
        return FALSE;
  }
  
@@@ -212,7 -196,7 +212,7 @@@ MUTATOR_HOOKFUNCTION(minstagib_SplitHea
  {
        damage_save = 0;
        damage_take = frag_damage;
-       
        return FALSE;
  }
  
@@@ -228,7 -212,7 +228,7 @@@ MUTATOR_HOOKFUNCTION(minstagib_PlayerDa
  {
        if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker))
                frag_damage = 0;
-               
        if(IS_PLAYER(frag_target))
        {
                if ((frag_deathtype == DEATH_FALL)  ||
                {
                        frag_damage = 0;
                }
-               
                if(IS_PLAYER(frag_attacker))
                if(DEATH_ISWEAPON(frag_deathtype, WEP_MINSTANEX))
                if(frag_target.armorvalue)
                        frag_target.hitsound += 1;
                        frag_attacker.hitsound += 1; // TODO change this to a future specific hitsound for armor hit
                }
-               
                if(IS_PLAYER(frag_attacker))
                if (DEATH_ISWEAPON(frag_deathtype, WEP_LASER))
                {
                        }
                }
        }
-       
        if(IS_PLAYER(frag_attacker))
        if(frag_mirrordamage > 0)
        {
                }
                frag_mirrordamage = 0;
        }
-       
        if(frag_target.items & IT_STRENGTH)
                yoda = 1;
-               
        return FALSE;
  }
  
  MUTATOR_HOOKFUNCTION(minstagib_SetStartItems)
  {
        start_ammo_cells = cvar("g_minstagib_ammo_start");
-       
        start_health = 100;
        start_armorvalue = 0;
        start_weapons = WEPSET_MINSTANEX;
        warmup_start_weapons = WEPSET_MINSTANEX;
        start_items |= IT_UNLIMITED_SUPERWEAPONS;
-               
        return FALSE;
  }
  
@@@ -302,13 -286,13 +302,13 @@@ MUTATOR_HOOKFUNCTION(minstagib_FilterIt
  {
        if(self.classname == "item_cells")
                return TRUE; // no normal cells?
-               
        if(self.weapon == WEP_MINSTANEX && self.classname == "droppedweapon")
        {
                self.ammo_cells = autocvar_g_minstagib_ammo_drop;
                return FALSE;
        }
-       
        if(self.weapon == WEP_ROCKET_LAUNCHER || self.weapon == WEP_NEX)
        {
                entity e = spawn();
                self = oldself;
                return TRUE;
        }
-               
        if(self.flags & FL_POWERUP)
                return FALSE;
-               
        if(self.ammo_cells > autocvar_g_minstagib_ammo_drop && self.classname != "item_minst_cells")
                self.ammo_cells = autocvar_g_minstagib_ammo_drop;
-               
        if(self.ammo_cells && !self.weapon)
                return FALSE;
-               
        return TRUE;
  }
  
  MUTATOR_HOOKFUNCTION(minstagib_CustomizeWaypoint)
  {
        entity e = WaypointSprite_getviewentity(other);
-       
        // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
        // but only apply this to real players, not to spectators
        if((self.owner.flags & FL_CLIENT) && (self.owner.items & IT_STRENGTH) && (e == other))
        if(DIFF_TEAM(self.owner, e))
                return TRUE;
-       
        return FALSE;
  }
  
@@@ -375,14 -359,14 +375,14 @@@ MUTATOR_HOOKFUNCTION(minstagib_ItemTouc
  
                return MUT_ITEMTOUCH_CONTINUE;
        }
-       
        if(self.max_health)
        {
                other.armorvalue = bound(other.armorvalue, 999, other.armorvalue + autocvar_g_minstagib_extralives);
                Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
                return MUT_ITEMTOUCH_PICKUP;
        }
-               
        return MUT_ITEMTOUCH_CONTINUE;
  }
  
@@@ -391,21 -375,21 +391,21 @@@ MUTATOR_HOOKFUNCTION(minstagib_OnEntity
        if (!autocvar_g_powerups) { return FALSE; }
        if (!(self.classname == "item_strength" || self.classname == "item_invincible" || self.classname == "item_health_mega"))
                return FALSE;
-       
        entity e = spawn();
-       
        if(random() < 0.3)
                e.think = spawnfunc_item_strength;
        else if(random() < 0.6)
                e.think = minstagib_health_mega;
        else
                e.think = spawnfunc_item_invincible;
-               
        e.nextthink = time + 0.1;
        e.spawnflags = self.spawnflags;
        e.noalign = self.noalign;
        setorigin(e, self.origin);
-       
        return TRUE;
  }
  
@@@ -430,8 -414,6 +430,8 @@@ MUTATOR_HOOKFUNCTION(minstagib_SetModna
  MUTATOR_DEFINITION(mutator_minstagib)
  {
        MUTATOR_HOOK(MatchEnd, minstagib_MatchEnd, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MonsterDropItem, minstagib_MonsterLoot, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MonsterSpawn, minstagib_MonsterSpawn, CBC_ORDER_ANY);
        MUTATOR_HOOK(BotShouldAttack, minstagib_BotShouldAttack, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPhysics, minstagib_PlayerPhysics, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, minstagib_PlayerSpawn, CBC_ORDER_ANY);
index 9df8d80697d8031943689e4941bf9b8df70ba3a1,3c17651a039ed99e935fc185fc581dfcca1c03a5..ecab77c88025dff20bf63c4778f811dbd5261da4
@@@ -1,5 -1,4 +1,4 @@@
  MUTATOR_DECLARATION(gamemode_assault);
- MUTATOR_DECLARATION(gamemode_arena);
  MUTATOR_DECLARATION(gamemode_ca);
  MUTATOR_DECLARATION(gamemode_keyhunt);
  MUTATOR_DECLARATION(gamemode_freezetag);
@@@ -9,7 -8,6 +8,7 @@@ MUTATOR_DECLARATION(gamemode_nexball)
  MUTATOR_DECLARATION(gamemode_onslaught);
  MUTATOR_DECLARATION(gamemode_domination);
  MUTATOR_DECLARATION(gamemode_lms);
 +MUTATOR_DECLARATION(gamemode_invasion);
  
  MUTATOR_DECLARATION(mutator_dodging);
  MUTATOR_DECLARATION(mutator_invincibleprojectiles);
diff --combined qcsrc/server/progs.src
index c082d09faaa0c583ee7b8e3d3c1b0b1862a5748d,f29324a478a8e622e4b72b81d995681a01d511b4..78f69eb8382d871c03f3f4c756466126e561d8f9
@@@ -27,11 -27,6 +27,11 @@@ sys-post.q
  ../common/net_notice.qh
  ../common/animdecide.qh
  
 +../common/monsters/monsters.qh
 +../common/monsters/sv_monsters.qh
 +../common/monsters/spawn.qh
 +
 +
  autocvars.qh
  constants.qh
  defs.qh               // Should rename this, it has fields and globals
@@@ -42,7 -37,6 +42,6 @@@
  mutators/base.qh
  mutators/mutators.qh
  mutators/gamemode_assault.qh
- mutators/gamemode_arena.qh
  mutators/gamemode_ca.qh
  mutators/gamemode_ctf.qh
  mutators/gamemode_domination.qh
@@@ -50,7 -44,6 +49,7 @@@ mutators/gamemode_keyhunt.qh // TODO fi
  mutators/gamemode_keepaway.qh
  mutators/gamemode_nexball.qh 
  mutators/gamemode_lms.qh
 +mutators/gamemode_invasion.qh
  mutators/mutator_dodging.qh
  mutators/mutator_nades.qh
  
@@@ -232,14 -225,8 +231,13 @@@ round_handler.q
  
  ../common/explosion_equation.qc
  
 +../common/monsters/sv_monsters.qc
 +../common/monsters/monsters.qc
 +
 +../common/monsters/spawn.qc
 +
  mutators/base.qc
  mutators/gamemode_assault.qc
- mutators/gamemode_arena.qc
  mutators/gamemode_ca.qc
  mutators/gamemode_ctf.qc
  mutators/gamemode_domination.qc
@@@ -249,7 -236,6 +247,7 @@@ mutators/gamemode_keepaway.q
  mutators/gamemode_nexball.qc
  mutators/gamemode_onslaught.qc
  mutators/gamemode_lms.qc
 +mutators/gamemode_invasion.qc
  mutators/mutator_invincibleproj.qc
  mutators/mutator_new_toys.qc
  mutators/mutator_nix.qc
diff --combined qcsrc/server/sv_main.qc
index 623b54651cb2c588d2cbb3c90cd6924661f83fb7,043cb7713d24cc61c91b162efba963ec0dd73065..86f2e80fb3d01dc42901584e8b4f0d81080962af
@@@ -2,16 -2,15 +2,16 @@@ void CreatureFrame (void
  {
        entity oldself;
        float dm;
-       
        oldself = self;
        for(self = world; (self = findfloat(self, damagedbycontents, TRUE)); )
        {
                if (self.movetype == MOVETYPE_NOCLIP) { continue; }
-               
                float vehic = (self.vehicle_flags & VHF_ISVEHICLE);
                float projectile = (self.flags & FL_PROJECTILE);
-               
 +              float monster = (self.flags & FL_MONSTER);
                if (self.watertype <= CONTENT_WATER && self.waterlevel > 0) // workaround a retarded bug made by id software :P (yes, it's that old of a bug)
                {
                        if (!(self.flags & FL_INWATER))
@@@ -20,7 -19,7 +20,7 @@@
                                self.dmgtime = 0;
                        }
  
 -                      if(!vehic && !projectile) // vehicles and projectiles don't drown
 +                      if(!vehic && !projectile && !monster) // vehicles, monsters and projectiles don't drown
                        {
                                if (self.waterlevel != WATERLEVEL_SUBMERGED)
                                {
                                        }
                                }
                        }
-                       
                        if (self.dmgtime < time)
                        {
-                               self.dmgtime = time + autocvar_g_balance_contents_damagerate; 
-                               
+                               self.dmgtime = time + autocvar_g_balance_contents_damagerate;
                                if (projectile)
                                {
                                        if (self.watertype == CONTENT_LAVA)
                                }
                        }
                }
-               
          self.oldvelocity = self.velocity;
        }
        self = oldself;
diff --combined qcsrc/server/t_items.qc
index be37aadcd27b6606e76c137600641f8ed6d887a5,1db8f6cd7630fcf7913d3fd229696986f8aa5e7d..b0e2e5e993bd7fa19c4424cc73b36b2440ff1f6a
@@@ -28,37 -28,37 +28,37 @@@ var string autocvr_cl_simpleitems_postf
  .float  gravity;
  .vector colormod;
  void ItemDraw()
- {    
+ {
      if(self.gravity)
-     {        
+     {
          Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
-         if(self.move_flags & FL_ONGROUND) 
+         if(self.move_flags & FL_ONGROUND)
          { // For some reason move_avelocity gets set to '0 0 0' here ...
              self.oldorigin = self.origin;
              self.gravity = 0;
  
-             if(autocvar_cl_animate_items)   
-             { // ... so reset it if animations are requested. 
+             if(autocvar_cl_animate_items)
+             { // ... so reset it if animations are requested.
                  if(self.ItemStatus & ITS_ANIMATE1)
                      self.move_avelocity = '0 180 0';
-                 
                  if(self.ItemStatus & ITS_ANIMATE2)
                      self.move_avelocity = '0 -90 0';
              }
          }
      }
      else if (autocvar_cl_animate_items)
-     {        
+     {
          if(self.ItemStatus & ITS_ANIMATE1)
          {
              self.angles += self.move_avelocity * frametime;
-             setorigin(self, '0 0 10' + self.oldorigin + '0 0 8' * sin(time * 2));        
-         }    
-         
+             setorigin(self, '0 0 10' + self.oldorigin + '0 0 8' * sin(time * 2));
+         }
          if(self.ItemStatus & ITS_ANIMATE2)
          {
              self.angles += self.move_avelocity * frametime;
-             setorigin(self, '0 0 8' + self.oldorigin + '0 0 4' * sin(time * 3));        
+             setorigin(self, '0 0 8' + self.oldorigin + '0 0 4' * sin(time * 3));
          }
      }
  }
@@@ -66,9 -66,9 +66,9 @@@
  void ItemDrawSimple()
  {
      if(self.gravity)
-     {        
-         Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);    
-         
+     {
+         Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
          if(self.move_flags & FL_ONGROUND)
              self.gravity = 0;
      }
@@@ -86,19 -86,19 +86,19 @@@ void ItemRead(float _IsNew
          setorigin(self, self.origin);
          self.oldorigin = self.origin;
      }
-     
-     if(sf & ISF_ANGLES) 
+     if(sf & ISF_ANGLES)
      {
          self.angles_x = ReadCoord();
          self.angles_y = ReadCoord();
-         self.angles_z = ReadCoord();        
+         self.angles_z = ReadCoord();
          self.move_angles = self.angles;
      }
-     
      if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc.
      {
-         self.ItemStatus = ReadByte();    
-         
+         self.ItemStatus = ReadByte();
          if(self.ItemStatus & ITS_AVAILABLE)
          {
              self.alpha = 1;
              }
              else
                  self.alpha = -1;
-         }    
-         
+         }
          if(autocvar_cl_fullbright_items)
              if(self.ItemStatus & ITS_ALLOWFB)
                  self.effects |= EF_FULLBRIGHT;
-             
          if(self.ItemStatus & ITS_STAYWEP)
          {
              self.colormod = self.glowmod = autocvar_cl_weapon_stay_color;
              self.alpha = autocvar_cl_weapon_stay_alpha;
-             
          }
-         
          if(self.ItemStatus & ITS_POWERUP)
          {
              if(self.ItemStatus & ITS_AVAILABLE)
                   self.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
          }
      }
-     
      if(sf & ISF_MODEL)
      {
          self.drawmask  = MASK_NORMAL;
          self.movetype  = MOVETYPE_NOCLIP;
          self.draw       = ItemDraw;
-         
          if(self.mdl)
              strunzone(self.mdl);
-         
          self.mdl = "";
          string _fn = ReadString();
-         
          if(autocvar_cl_simple_items && (self.ItemStatus & ITS_ALLOWSI))
          {
              string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
              self.draw = ItemDrawSimple;
-                     
-             
-             
              if(fexists(sprintf("%s%s.md3", _fn2, autocvr_cl_simpleitems_postfix)))
                  self.mdl = strzone(sprintf("%s%s.md3", _fn2, autocvr_cl_simpleitems_postfix));
              else if(fexists(sprintf("%s%s.dpm", _fn2, autocvr_cl_simpleitems_postfix)))
                  dprint("Simple item requested for ", _fn, " but no model exsist for it\n");
              }
          }
-         
-         if(self.draw != ItemDrawSimple)        
-             self.mdl = strzone(_fn);                
-         
-         
+         if(self.draw != ItemDrawSimple)
+             self.mdl = strzone(_fn);
          if(self.mdl == "")
              dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, " tell tZork aboute this!\n");
-         
          precache_model(self.mdl);
          setmodel(self, self.mdl);
      }
-     
      if(sf & ISF_COLORMAP)
          self.colormap = ReadShort();
-     
      if(sf & ISF_DROP)
      {
          self.gravity = 1;
          self.move_velocity_z = ReadCoord();
          self.velocity = self.move_velocity;
          self.move_origin = self.oldorigin;
-         
          if(!self.move_time)
          {
              self.move_time = time;
          else
              self.move_time = max(self.move_time, time);
      }
-         
      if(autocvar_cl_animate_items)
-     {        
+     {
          if(self.ItemStatus & ITS_ANIMATE1)
              self.move_avelocity = '0 180 0';
-                 
          if(self.ItemStatus & ITS_ANIMATE2)
              self.move_avelocity = '0 -90 0';
      }
@@@ -223,8 -223,8 +223,8 @@@ float ItemSend(entity to, float sf
          sf |= ISF_DROP;
      else
          sf &= ~ISF_DROP;
-       
-       WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM); 
+       WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
        WriteByte(MSG_ENTITY, sf);
  
        //WriteByte(MSG_ENTITY, self.cnt);
          WriteCoord(MSG_ENTITY, self.origin_y);
          WriteCoord(MSG_ENTITY, self.origin_z);
      }
-     
      if(sf & ISF_ANGLES)
      {
          WriteCoord(MSG_ENTITY, self.angles_x);
  
      if(sf & ISF_MODEL)
      {
-         
          if(self.mdl == "")
              dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, "exspect a crash just aboute now\n");
-         
          WriteString(MSG_ENTITY, self.mdl);
      }
-         
-         
      if(sf & ISF_COLORMAP)
          WriteShort(MSG_ENTITY, self.colormap);
  
          WriteCoord(MSG_ENTITY, self.velocity_y);
          WriteCoord(MSG_ENTITY, self.velocity_z);
      }
-         
      return TRUE;
  }
  
@@@ -374,7 -374,7 +374,7 @@@ float Item_Customize(
  */
  
  void Item_Show (entity e, float mode)
- {    
+ {
        e.effects &= ~(EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST);
        e.ItemStatus &= ~ITS_STAYWEP;
        if (mode > 0)
                e.spawnshieldtime = 1;
                e.ItemStatus &= ~ITS_AVAILABLE;
        }
-       
        if (e.items & IT_STRENGTH || e.items & IT_INVINCIBLE)
-           e.ItemStatus |= ITS_POWERUP;                
-       
+           e.ItemStatus |= ITS_POWERUP;
        if (autocvar_g_nodepthtestitems)
                e.effects |= EF_NODEPTHTEST;
-               
-     
      if (autocvar_g_fullbrightitems)
                e.ItemStatus |= ITS_ALLOWFB;
-       
        if (autocvar_sv_simple_items)
          e.ItemStatus |= ITS_ALLOWSI;
  
@@@ -651,7 -651,7 +651,7 @@@ float Item_GiveTo(entity item, entity p
        }
  
  :skip
-       
        // always eat teamed entities
        if(item.team)
                pickedup = TRUE;
  void Item_Touch (void)
  {
        entity e, head;
-       
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
        if(self.classname == "droppedweapon")
        {
  
        if (!IS_PLAYER(other))
                return;
 +      if (other.frozen)
 +              return;
        if (other.deadflag)
                return;
        if (self.solid != SOLID_TRIGGER)
                return;
        if (self.owner == other)
                return;
 +      if (time < self.item_spawnshieldtime)
 +              return;
  
        switch(MUTATOR_CALLHOOK(ItemTouch))
        {
@@@ -934,16 -930,16 +934,16 @@@ void StartItem (string itemmodel, strin
  
        if(self.model == "")
                self.model = itemmodel;
-       
        if(self.model == "")
      {
          error(strcat("^1Tried to spawn ", itemname, " with no model!\n"));
          return;
      }
-         
        if(self.item_pickupsound == "")
                self.item_pickupsound = pickupsound;
-       
        if(!self.respawntime) // both need to be set
        {
                self.respawntime = defaultrespawntime;
  
        if(weaponid)
                self.weapons = WepSet_FromWeapon(weaponid);
-       
        self.flags = FL_ITEM | itemflags;
  
        if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item
                        remove (self);
                        return;
                }
-               
                if(self.angles != '0 0 0')
              self.SendFlags |= ISF_ANGLES;
  
        self.netname = itemname;
        self.touch = Item_Touch;
        setmodel(self, "null"); // precision set below
-       //self.effects |= EF_LOWPRECISION; 
-       
+       //self.effects |= EF_LOWPRECISION;
        if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
      {
          self.pos1 = '-16 -16 0';
          self.pos2 = '16 16 32';
      }
      setsize (self, self.pos1, self.pos2);
-     
-     if(itemflags & FL_POWERUP) 
+     if(itemflags & FL_POWERUP)
          self.ItemStatus |= ITS_ANIMATE1;
-       
        if(self.armorvalue || self.health)
          self.ItemStatus |= ITS_ANIMATE2;
-       
        if(itemflags & FL_WEAPON)
        {
                if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely
              self.colormap = 1024; // color shirt=0 pants=0 grey
          else
              self.gravity = 1;
-             
                self.ItemStatus |= ITS_ANIMATE1;
                self.ItemStatus |= ISF_COLORMAP;
        }
        {
                if(!self.cnt)
                        self.cnt = 1; // item probability weight
-                       
                self.effects |= EF_NODRAW; // marker for item team search
                InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
        }
        else
                Item_Reset();
-         
      Net_LinkEntity(self, FALSE, 0, ItemSend);
  
        // call this hook after everything else has been done
@@@ -1807,7 -1803,7 +1807,7 @@@ float GiveItems(entity e, float beginar
        e.strength_finished = max(0, e.strength_finished - time);
        e.invincible_finished = max(0, e.invincible_finished - time);
        e.superweapons_finished = max(0, e.superweapons_finished - time);
-       
        PREGIVE(e, items);
        PREGIVE_WEAPONS(e);
        PREGIVE(e, strength_finished);
index 55029775454e3b4202539ee60b5e70d91aeb8985,4da6b13bbbb948060ec40541307eeffddf84be95..657e7cb0e7b0a3082c03c9b2b36825b7fdd24556
@@@ -216,8 -216,7 +216,8 @@@ void target_spawn_edit_entity(entity e
                        self = e;
                        activator = act;
  
 -                      self.target_spawn_spawnfunc();
 +                      if(self.target_spawn_spawnfunc)
 +                              self.target_spawn_spawnfunc();
  
                        self = oldself;
                        activator = oldactivator;
@@@ -257,7 -256,7 +257,7 @@@ float target_spawn_cancreate(
        ++c; // increase count to not include MYSELF
        for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
                ;
-       
        // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
        if(c == 0)
                return 0;
diff --combined qcsrc/server/teamplay.qc
index 90582acf7c4563a738617e8d5f85e66fd1f2c88f,5185b2c611a639093d1f343911182e98d8009cf3..8ad3b5ac2b4e436024926a8ac6682ed9863af6e4
@@@ -115,13 -115,6 +115,6 @@@ void InitGameplayMode(
                MUTATOR_ADD(gamemode_lms);
        }
  
-       if(g_arena)
-       {
-               fraglimit_override = autocvar_g_arena_point_limit;
-               leadlimit_override = autocvar_g_arena_point_leadlimit;
-               MUTATOR_ADD(gamemode_arena);
-       }
        if(g_ca)
        {
                ActivateTeamplay();
          have_team_spawns = -1; // request team spawns
            MUTATOR_ADD(gamemode_nexball);
        }
-         
        if(g_keepaway)
        {
                MUTATOR_ADD(gamemode_keepaway);
        }
 +      
 +      if(g_invasion)
 +      {
 +              timelimit_override = 0; // no timelimit in invasion, round based
 +              fraglimit_override = autocvar_g_invasion_round_limit;
 +              MUTATOR_ADD(gamemode_invasion);
 +      }
  
        if(teamplay)
                entcs_init();
                else
                        g_race_qualifying = 0;
        }
 +      
 +      if(g_invasion)
 +      {
 +              maxrounds = cvar("fraglimit");
 +              cvar_set("fraglimit", "0");
 +      }
  
        if(g_race || g_cts)
        {
@@@ -613,7 -593,7 +606,7 @@@ float TeamSmallerEqThanTeam(float ta, f
                        cb -= cbb * 0.999;
                }
        }
-       
        // keep teams alive (teams of size 0 always count as smaller, ignoring score)
        if(ca < 1)
                if(cb >= 1)
@@@ -684,7 -664,7 +677,7 @@@ float FindSmallestTeam(entity pl, floa
                GetTeamCounts(world);
  
        RandomSelection_Init();
-       
        t = 1;
        if(TeamSmallerEqThanTeam(2, t, pl))
                t = 2;
index 02c823b6ee9a2d359041806cdf6d96eccfadacea,b4ad709b4d0b547278fd74c4f20fff29a0d64b62..cced0ec369f2f47119da322907cdc49bb96988dc
@@@ -1,49 -1,49 +1,49 @@@
  #define cvar_base "g_turrets_unit_"
  .float clientframe;
  void turrets_setframe(float _frame, float client_only)
- {        
+ {
      if((client_only ? self.clientframe : self.frame ) != _frame)
      {
          self.SendFlags |= TNSF_ANIM;
          self.anim_start_time = time;
      }
-     
       if(client_only)
          self.clientframe = _frame;
      else
          self.frame = _frame;
-    
  }
  
  float turret_send(entity to, float sf)
  {
-       
-       WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET);    
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET);
        WriteByte(MSG_ENTITY, sf);
        if(sf & TNSF_SETUP)
        {
            WriteByte(MSG_ENTITY, self.turret_type);
-           
            WriteCoord(MSG_ENTITY, self.origin_x);
            WriteCoord(MSG_ENTITY, self.origin_y);
            WriteCoord(MSG_ENTITY, self.origin_z);
-           
            WriteAngle(MSG_ENTITY, self.angles_x);
            WriteAngle(MSG_ENTITY, self.angles_y);
      }
-     
      if(sf & TNSF_ANG)
      {
          WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x));
          WriteShort(MSG_ENTITY, rint(self.tur_head.angles_y));
      }
-     
      if(sf & TNSF_AVEL)
-     {        
+     {
          WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_x));
          WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_y));
      }
-     
      if(sf & TNSF_MOVE)
      {
          WriteShort(MSG_ENTITY, rint(self.origin_x));
  
          WriteShort(MSG_ENTITY, rint(self.velocity_x));
          WriteShort(MSG_ENTITY, rint(self.velocity_y));
-         WriteShort(MSG_ENTITY, rint(self.velocity_z));        
-         
-         WriteShort(MSG_ENTITY, rint(self.angles_y));        
+         WriteShort(MSG_ENTITY, rint(self.velocity_z));
+         WriteShort(MSG_ENTITY, rint(self.angles_y));
      }
-     
      if(sf & TNSF_ANIM)
      {
          WriteCoord(MSG_ENTITY, self.anim_start_time);
          WriteByte(MSG_ENTITY, self.frame);
      }
-     
      if(sf & TNSF_STATUS)
      {
          WriteByte(MSG_ENTITY, self.team);
-         
          if(self.health <= 0)
              WriteByte(MSG_ENTITY, 0);
          else
              WriteByte(MSG_ENTITY, ceil((self.health / self.tur_health) * 255));
      }
-     
        return TRUE;
  }
  
@@@ -143,9 -143,9 +143,9 @@@ void load_unit_settings(entity ent, str
  
  void turret_projectile_explode()
  {
-     
      self.takedamage = DAMAGE_NO;
-     self.event_damage = func_null;    
+     self.event_damage = func_null;
  #ifdef TURRET_DEBUG
      float d;
      d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world);
@@@ -183,12 -183,12 +183,12 @@@ entity turret_projectile(string _snd, f
      proj.owner           = self;
      proj.realowner       = self;
      proj.bot_dodge       = TRUE;
-     proj.bot_dodgerating = self.shot_dmg;    
+     proj.bot_dodgerating = self.shot_dmg;
      proj.think           = turret_projectile_explode;
      proj.touch           = turret_projectile_touch;
-     proj.nextthink       = time + 9;    
+     proj.nextthink       = time + 9;
      proj.movetype        = MOVETYPE_FLYMISSILE;
-     proj.velocity        = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;    
+     proj.velocity        = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;
      proj.flags           = FL_PROJECTILE;
      proj.enemy           = self.enemy;
      proj.totalfrags      = _death;
          proj.flags |= FL_NOTARGET;
  
      CSQCProjectile(proj, _cli_anim, _proj_type, _cull);
-     
      return proj;
  }
  
@@@ -241,8 -241,8 +241,8 @@@ void turret_do_updates(entity t_turret
      }
      else*/
          tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self);
-       
-       self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5);                
+       self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5);
        self.tur_impactent             = trace_ent;
        self.tur_impacttime            = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed;
  
@@@ -328,7 -328,7 +328,7 @@@ void turret_stdproc_track(
      vector v1, v2;
      v1 = self.tur_head.angles;
      v2 = self.tur_head.avelocity;
-     
      if (self.track_flags == TFL_TRACK_NO)
          return;
  
      }
      else
      {
-         target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); 
+         target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg));
      }
-     
      self.tur_head.angles_x = anglemods(self.tur_head.angles_x);
      self.tur_head.angles_y = anglemods(self.tur_head.angles_y);
  
      // Find the diffrence between where we currently aim and where we want to aim
      //move_angle = target_angle - (self.angles + self.tur_head.angles);
      //move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles));
-     
-     move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles; 
+     move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles;
      move_angle = shortangle_vxy(move_angle, self.tur_head.angles);
  
      switch(self.track_type)
                  if(self.tur_head.angles_y  < -self.aim_maxrot)
                      self.tur_head.angles_y = self.aim_maxrot;
              }
-             
              // CSQC
              self.SendFlags  |= TNSF_ANG;
-             
              return;
  
          case TFL_TRACKTYPE_FLUIDINERTIA:
          {
              self.tur_head.avelocity_x = 0;
              self.tur_head.angles_x = self.aim_maxpitch;
-             
              self.SendFlags  |= TNSF_ANG;
          }
-         
          if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) < -self.aim_maxpitch)
          {
              self.tur_head.avelocity_x = 0;
              self.tur_head.angles_x = -self.aim_maxpitch;
-                         
              self.SendFlags  |= TNSF_ANG;
          }
      }
          {
              self.tur_head.avelocity_y = 0;
              self.tur_head.angles_y = self.aim_maxrot;
-             
              self.SendFlags  |= TNSF_ANG;
          }
  
          {
              self.tur_head.avelocity_y = 0;
              self.tur_head.angles_y = -self.aim_maxrot;
-             
              self.SendFlags  |= TNSF_ANG;
          }
      }
-         
      self.SendFlags  |= TNSF_AVEL;
-     
      // Force a angle update every 10'th frame
      self.turret_framecounter += 1;
      if(self.turret_framecounter >= 10)
-     {        
+     {
          self.SendFlags |= TNSF_ANG;
          self.turret_framecounter = 0;
-     }            
+     }
  }
  
  
  float turret_stdproc_firecheck()
  {
      // This one just dont care =)
-     if (self.firecheck_flags & TFL_FIRECHECK_NO) 
+     if (self.firecheck_flags & TFL_FIRECHECK_NO)
          return 1;
  
      if (self.enemy == world)
      if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
          if (self.volly_counter != self.shot_volly)
                        if(self.ammo >= self.shot_dmg)
-                               return 1;               
+                               return 1;
  
      // Lack of zombies makes shooting dead things unnecessary :P
      if (self.firecheck_flags & TFL_FIRECHECK_DEAD)
      if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO)
          if (self.enemy.ammo >= self.enemy.ammo_max)
              return 0;
-       
        // Target of opertunity?
        if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)
        {
                self.enemy = self.tur_impactent;
                return 1;
-       }                               
+       }
  
      if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES)
      {
          // To close?
          if (self.tur_dist_aimpos < self.target_range_min)
-                       if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)                    
+                       if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0)
                                return 1; // Target of opertunity?
-                       else 
-                               return 0;                               
+                       else
+                               return 0;
      }
  
      // Try to avoid FF?
  float turret_validate_target(entity e_turret, entity e_target, float validate_flags)
  {
      vector v_tmp;
-         
      //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN)
      //    return -0.5;
  
          return -0.5;
  
      if (!checkpvs(e_target.origin, e_turret))
-         return -1;        
+         return -1;
  
      if (!e_target)
          return -2;
          return -5;
  
      // Cant touch this
-     if(e_target.vehicle_flags & VHF_ISVEHICLE)    
+     if(e_target.vehicle_flags & VHF_ISVEHICLE)
      {
          if (e_target.vehicle_health <= 0)
              return -6;
@@@ -729,7 -729,7 +729,7 @@@ entity turret_select_target(
      e = findradius(self.origin, self.target_range);
  
      // Nothing to aim at?
-     if (!e) 
+     if (!e)
                return world;
  
      while (e)
@@@ -759,7 -759,7 +759,7 @@@ void turret_think(
      entity e;
  
      self.nextthink = time + self.ticrate;
-     
      // ONS uses somewhat backwards linking.
      if (teamplay)
      {
      if (!(self.spawnflags & TSF_NO_AMMO_REGEN))
      if (self.ammo < self.ammo_max)
          self.ammo = min(self.ammo + self.ammo_recharge, self.ammo_max);
-                       
      // Inactive turrets needs to run the think loop,
      // So they can handle animation and wake up if need be.
      if (!self.active)
@@@ -1021,14 -1021,14 +1021,14 @@@ float turret_stdproc_init (string cvar_
      // Are turrets allowed?
      if (autocvar_g_turrets == 0)
          return 0;
-     
      if(_turret_type < 1 || _turret_type > TID_LAST)
      {
          dprint("Invalid / Unkown turret type\"", ftos(_turret_type), "\", aborting!\n");
          return 0;
-     }    
+     }
      self.turret_type = _turret_type;
-     
      e = find(world, classname, "turret_manager");
      if (!e)
      {
          e.think = turrets_manager_think;
          e.nextthink = time + 2;
      }
-     
      if (!(self.spawnflags & TSF_SUSPENDED))
          builtin_droptofloor(); // why can't we use regular droptofloor here?
  
      load_unit_settings(self, self.cvar_basename, 0);
  
      self.effects = EF_NODRAW;
-     
      // Handle turret teams.
      if (!teamplay)
                self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so they dont kill eachother.
      * as possible beforehand.
      */
      if (!self.ticrate)
-     {        
+     {
          if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)
              self.ticrate = 0.2;     // Support units generaly dont need to have a high speed ai-loop
          else
              self.ticrate = 0.1;     // 10 fps for normal turrets
      }
-     
      self.ticrate = bound(sys_frametime, self.ticrate, 60);  // keep it sane
  
  // General stuff
      if (!self.health)
          self.health = 1000;
      self.tur_health = max(1, self.health);
 +      self.bot_attack = TRUE;
 +    self.monster_attack = TRUE;
  
      if (!self.turrcaps_flags)
          self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
  
  // Offsets & origins
      if (!self.tur_shotorg)   self.tur_shotorg = '50 0 50';
-     
      if (!self.health)
          self.health = 150;
  
      self.turret_firecheckfunc   = turret_stdproc_firecheck;
      self.turret_firefunc        = turret_stdproc_fire;
      self.event_damage           = turret_stdproc_damage;
-     
      if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)
          self.turret_score_target    = turret_stdproc_targetscore_support;
      else
          self.turret_score_target    = turret_stdproc_targetscore_generic;
  
      self.use = turret_stdproc_use;
 -    self.bot_attack = TRUE;
  
      ++turret_count;
      self.nextthink = time + 1;
          activator = ee;
          self.use();
      }
-     
        turret_link();
-       turret_stdproc_respawn();           
+       turret_stdproc_respawn();
      turret_tag_fire_update();
-     
      return 1;
  }
  
index 1734b02758c3e9656a413de4bb5713d8eeecf6da,1e5537a259934cd2faab7750716d1b9d57ea22de..3e9a96f01003cc8c0a9d47a191862bef5546fc6e
@@@ -103,13 -103,13 +103,13 @@@ void CSQCVehicleSetup(entity own, floa
  {
      if (!IS_REAL_CLIENT(own))
          return;
-       
        msg_entity = own;
  
        WriteByte(MSG_ONE, SVC_TEMPENTITY);
        WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP);
        if(vehicle_id != 0)
-           WriteByte(MSG_ONE, vehicle_id);        
+           WriteByte(MSG_ONE, vehicle_id);
        else
          WriteByte(MSG_ONE, 1 + own.vehicle.vehicle_weapon2mode + HUD_VEHICLE_LAST);
  }
@@@ -492,10 -492,10 +492,10 @@@ void vehicles_spawn(
      setorigin(self, self.pos1 + '0 0 0');
      // Show it
      pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
-     
      if(self.vehicle_controller)
          self.team = self.vehicle_controller.team;
-        
      vehicles_reset_colors();
      self.vehicle_spawn(VHSF_NORMAL);
  }
@@@ -539,7 -539,7 +539,7 @@@ void vehicles_touch(
  {
        if(MUTATOR_CALLHOOK(VehicleTouch))
                return;
-       
      // Vehicle currently in use
      if(self.owner)
      {
  
      if(other.deadflag != DEAD_NO)
          return;
 +              
 +      if(other.frozen)
 +              return;
  
      if(other.vehicle != world)
          return;
@@@ -577,8 -574,8 +577,8 @@@ var float autocvar_g_vehicles_allow_bot
  void vehicles_enter()
  {
     // Remove this when bots know how to use vehicles
-    
-     if (IS_BOT_CLIENT(other))    
+     if (IS_BOT_CLIENT(other))
          if (autocvar_g_vehicles_allow_bots)
              dprint("Bot enters vehicle\n"); // This is where we need to disconnect (some, all?) normal bot AI and hand over to vehicle's _aiframe()
          else
  
      if(self.phase > time)
          return;
 +              
 +      if(other.frozen)
 +              return;
  
      if(teamplay)
      if(self.team)
  
      self.team                 = self.owner.team;
      self.flags               -= FL_NOTARGET;
-     
 +      self.monster_attack               = TRUE;
      if (IS_REAL_CLIENT(other))
      {
          msg_entity = other;
          WriteByte (MSG_ONE, SVC_SETVIEWPORT);
          WriteEntity(MSG_ONE, self.vehicle_viewport);
-                 
          WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
          if(self.tur_head)
          {
      vehicles_clearrturn();
  
      CSQCVehicleSetup(self.owner, self.hud);
-     
      vh_player = other;
      vh_vehicle = self;
      MUTATOR_CALLHOOK(VehicleEnter);
@@@ -742,31 -735,31 +742,31 @@@ void vehicles_exit(float eject
      entity _vehicle;
      entity _player;
      entity _oldself = self;
-     
      if(vehicles_exit_running)
      {
          dprint("^1vehicles_exit allready running! this is not good..\n");
          return;
      }
-     
      vehicles_exit_running = TRUE;
      if(IS_CLIENT(self))
      {
          _vehicle = self.vehicle;
-             
          if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
          {
              _vehicle.vehicle_exit(eject);
              self = _oldself;
              vehicles_exit_running = FALSE;
-             return;            
+             return;
          }
      }
      else
          _vehicle = self;
-     
      _player = _vehicle.owner;
-     
      self = _vehicle;
  
      if (_player)
              WriteAngle(MSG_ONE, _vehicle.angles_y);
              WriteAngle(MSG_ONE, 0);
          }
-         
          setsize(_player, PL_MIN,PL_MAX);
  
          _player.takedamage     = DAMAGE_AIM;
          CSQCVehicleSetup(_player, HUD_NORMAL);
      }
      _vehicle.flags |= FL_NOTARGET;
-     
      if(_vehicle.deadflag == DEAD_NO)
          _vehicle.avelocity          = '0 0 0';
-     
      _vehicle.tur_head.nodrawtoclient             = world;
-     
      if(!teamplay)
          _vehicle.team = 0;
  
      _vehicle = vh_vehicle;
  
      _vehicle.team = _vehicle.tur_head.team;
-         
      sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTEN_NORM);
-     _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle;  
+     _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle;
      _vehicle.phase = time + 1;
-     
 +      _vehicle.monster_attack = FALSE;
      _vehicle.vehicle_exit(eject);
-     
      vehicles_setreturn();
-     vehicles_reset_colors();        
+     vehicles_reset_colors();
      _vehicle.owner = world;
      self = _oldself;
-     
      vehicles_exit_running = FALSE;
  }
  
@@@ -840,7 -832,7 +840,7 @@@ void vehicles_regen(float timer, .floa
      {
          if(_healthscale)
              regen = regen * (self.vehicle_health / self.tur_health);
-             
          self.regen_field = min(self.regen_field + regen * delta_time, field_max);
  
          if(self.owner)
@@@ -888,24 -880,24 +888,24 @@@ void vehicles_painframe(
  void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
  {
      self.dmg_time = time;
-     
      if(DEATH_ISWEAPON(deathtype, WEP_NEX))
          damage *= autocvar_g_vehicles_nex_damagerate;
-         
      if(DEATH_ISWEAPON(deathtype, WEP_UZI))
          damage *= autocvar_g_vehicles_uzi_damagerate;
-         
      if(DEATH_ISWEAPON(deathtype, WEP_RIFLE))
          damage *= autocvar_g_vehicles_rifle_damagerate;
-         
      if(DEATH_ISWEAPON(deathtype, WEP_MINSTANEX))
          damage *= autocvar_g_vehicles_minstanex_damagerate;
  
      if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
          damage *= autocvar_g_vehicles_tag_damagerate;
-     
      self.enemy = attacker;
-     
      if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0))
      {
          if (wasfreed(self.vehicle_shieldent) || self.vehicle_shieldent == world)
          if(sound_allowed(MSG_BROADCAST, attacker))
              spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
      }
-     
        if(self.damageforcescale < 1 && self.damageforcescale > 0)
                self.velocity += force * self.damageforcescale;
        else
@@@ -1141,7 -1133,7 +1141,7 @@@ void vehicle_use(
          self.active = ACTIVE_NOT;
      else
          self.active = ACTIVE_ACTIVE;
-     
      if(self.active == ACTIVE_ACTIVE && self.deadflag == DEAD_NO)
      {
          dprint("^3Eat shit yall!\n");
      }
      else if(self.active == ACTIVE_NOT && self.deadflag != DEAD_NO)
      {
-         
      }
  }
  
- float vehicle_addplayerslot(    entity _owner, 
-                                 entity _slot, 
-                                 float _hud, 
+ float vehicle_addplayerslot(    entity _owner,
+                                 entity _slot,
+                                 float _hud,
                                  string _hud_model,
-                                 float() _framefunc, 
+                                 float() _framefunc,
                                  void(float) _exitfunc)
  {
      if (!(_owner.vehicle_flags & VHF_MULTISLOT))
      _slot.vehicle_hudmodel = spawn();
      _slot.vehicle_hudmodel.viewmodelforclient = _slot;
      _slot.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT);
-     
      setmodel(_slot.vehicle_hudmodel, _hud_model);
      setmodel(_slot.vehicle_viewport, "null");
-     
      setattachment(_slot.vehicle_hudmodel, _slot, "");
      setattachment(_slot.vehicle_viewport, _slot.vehicle_hudmodel, "");
-     
      return TRUE;
  }
  
@@@ -1206,7 -1198,7 +1206,7 @@@ float vehicle_initialize(string  net_na
  {
        if(!autocvar_g_vehicles)
                return FALSE;
-       
      if(self.targetname)
      {
          self.vehicle_controller = find(world, target, self.targetname);
          }
          else
          {
-             self.team = self.vehicle_controller.team;        
+             self.team = self.vehicle_controller.team;
              self.use = vehicle_use;
-             
              if(teamplay)
              {
                  if(self.vehicle_controller.team == 0)
                      self.active = ACTIVE_NOT;
                  else
-                     self.active = ACTIVE_ACTIVE;                
+                     self.active = ACTIVE_ACTIVE;
              }
          }
      }
-     
      precache_sound("onslaught/ons_hit2.wav");
      precache_sound("onslaught/electricity_explode.wav");
  
          tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
          setorigin(self, trace_endpos);
      }
-     
      self.pos1 = self.origin;
      self.pos2 = self.angles;
      self.tur_head.team = self.team;
-       
        if(MUTATOR_CALLHOOK(VehicleSpawn))
                return FALSE;
  
      return TRUE;
  }
  
- vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname, 
-                          float _pichlimit_min, float _pichlimit_max, 
+ vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname,
+                          float _pichlimit_min, float _pichlimit_max,
                           float _rotlimit_min, float _rotlimit_max, float _aimspeed)
  {
      vector vtmp, vtag;
      ftmp = _aimspeed * frametime;
      vtmp_y = bound(-ftmp, vtmp_y, ftmp);
      vtmp_x = bound(-ftmp, vtmp_x, ftmp);
-     _turrret.angles_y = bound(_rotlimit_min, _turrret.angles_y + vtmp_y, _rotlimit_max);    
+     _turrret.angles_y = bound(_rotlimit_min, _turrret.angles_y + vtmp_y, _rotlimit_max);
      _turrret.angles_x = bound(_pichlimit_min, _turrret.angles_x + vtmp_x, _pichlimit_max);
      return vtag;
  }
@@@ -1378,23 -1370,23 +1378,23 @@@ entity vehicle_tossgib(entity _template
        _gib.movetype = MOVETYPE_TOSS;
        _gib.solid = SOLID_CORPSE;
        _gib.colormod = '-0.5 -0.5 -0.5';
-       _gib.effects = EF_LOWPRECISION; 
+       _gib.effects = EF_LOWPRECISION;
        _gib.avelocity = _rot;
-       
        if(_burn)
                _gib.effects |= EF_FLAME;
-       
        if(_explode)
        {
-               _gib.think = vehicles_gib_explode; 
+               _gib.think = vehicles_gib_explode;
                _gib.nextthink = time + random() * _explode;
                _gib.touch = vehicles_gib_explode;
        }
        else
        {
                _gib.cnt = time + _maxtime;
-               _gib.think = vehicles_gib_think; 
-               _gib.nextthink = time + _maxtime - 1;           
+               _gib.think = vehicles_gib_think;
+               _gib.nextthink = time + _maxtime - 1;
                _gib.alpha = 1;
        }
        return _gib;
index 0ea0e3f2d8bd7fb75c097f1acda4927035690a56,0ad23a137ec53177cf29fb60390b8ba28f6512ee..ea2cf8bf0560dfee7a879789377231dc82e8cd0f
@@@ -96,10 -96,10 +96,10 @@@ void W_Plasma_Damage (entity inflictor
  
        // note: combos are usually triggered by W_Plasma_TriggerCombo, not damage
        float is_combo = (inflictor.classname == "plasma_chain" || inflictor.classname == "plasma_prim");
-       
        if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_combo ? 1 : -1)))
-               return; // g_projectiles_damage says to halt    
-       
+               return; // g_projectiles_damage says to halt
        self.health = self.health - damage;
        if (self.health <= 0)
        {
@@@ -263,7 -263,7 +263,7 @@@ void lgbeam_think(
                return;
        }
  
 -      if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.freezetag_frozen)
 +      if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.frozen)
        {
                if(self == owner_player.lgbeam)
                        owner_player.lgbeam = world;
index c0353da69031cfe8f08c9b0de6b846b0c6b0f506,2fb6214017c0f1de374817fc596b44d9d3f66104..84573547cffd4642c3dda01cd954520623900920
@@@ -35,9 -35,9 +35,9 @@@ void W_Shotgun_Attack (void
  
        W_DecreaseAmmo(ammo_shells, ammoamount, autocvar_g_balance_shotgun_reload_ammo);
  
-       W_SetupShot (self, autocvar_g_antilag_bullets && bulletspeed >= autocvar_g_antilag_bullets, 5, "weapons/shotgun_fire.wav", CH_WEAPON_A, d * bullets);
+       W_SetupShot (self, TRUE, 5, "weapons/shotgun_fire.wav", CH_WEAPON_A, d * bullets);
        for (sc = 0;sc < bullets;sc = sc + 1)
-               fireBallisticBullet(w_shotorg, w_shotdir, spread, bulletspeed, 5, d, f, WEP_SHOTGUN, 0, 1, bulletconstant);
+               fireBallisticBullet(w_shotorg, w_shotdir, spread, bulletspeed, 5, d, f, WEP_SHOTGUN, 0, bulletconstant);
        endFireBallisticBullet();
  
        pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, autocvar_g_balance_shotgun_primary_ammo);
  void shotgun_meleethink (void)
  {
        // declarations
 -      float i, f, swing, swing_factor, swing_damage, meleetime, is_player;
 +      float i, f, swing, swing_factor, swing_damage, meleetime, is_player, is_monster;
        entity target_victim;
        vector targpos;
  
        if(!self.cnt) // set start time of melee
        {
-               self.cnt = time; 
+               self.cnt = time;
                W_PlayStrengthSound(self.realowner);
        }
  
        makevectors(self.realowner.v_angle); // update values for v_* vectors
-       
        // calculate swing percentage based on time
        meleetime = autocvar_g_balance_shotgun_secondary_melee_time * W_WeaponRateFactor();
        swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10);
        f = ((1 - swing) * autocvar_g_balance_shotgun_secondary_melee_traces);
-       
        // check to see if we can still continue, otherwise give up now
        if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_shotgun_secondary_melee_no_doubleslap)
        {
                remove(self);
                return;
        }
-       
-       // if okay, perform the traces needed for this frame 
+       // if okay, perform the traces needed for this frame
        for(i=self.swing_prev; i < f; ++i)
        {
                swing_factor = ((1 - (i / autocvar_g_balance_shotgun_secondary_melee_traces)) * 2 - 1);
-               
-               targpos = (self.realowner.origin + self.realowner.view_ofs 
+               targpos = (self.realowner.origin + self.realowner.view_ofs
                        + (v_forward * autocvar_g_balance_shotgun_secondary_melee_range)
                        + (v_up * swing_factor * autocvar_g_balance_shotgun_secondary_melee_swing_up)
                        + (v_right * swing_factor * autocvar_g_balance_shotgun_secondary_melee_swing_side));
  
                WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self, ANTILAG_LATENCY(self.realowner));
-               
                // draw lightning beams for debugging
-               //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); 
+               //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5);
                //te_customflash(targpos, 40,  2, '1 1 1');
-               
                is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body");
 +              is_monster = (trace_ent.flags & FL_MONSTER);
  
                if((trace_fraction < 1) // if trace is good, apply the damage and remove self
-                       && (trace_ent.takedamage == DAMAGE_AIM)  
+                       && (trace_ent.takedamage == DAMAGE_AIM)
                        && (trace_ent != self.swing_alreadyhit)
 -                      && (is_player || autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage))
 +                      && ((is_player || is_monster) || autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage))
                {
                        target_victim = trace_ent; // so it persists through other calls
-                       
 -                      if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught.
 +                      if(is_player || is_monster) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught.
                                swing_damage = (autocvar_g_balance_shotgun_secondary_damage * min(1, swing_factor + 1));
                        else
                                swing_damage = (autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage * min(1, swing_factor + 1));
-                       
                        //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n"));
-                       
-                       Damage(target_victim, self.realowner, self.realowner, 
-                               swing_damage, WEP_SHOTGUN | HITTYPE_SECONDARY, 
-                               self.realowner.origin + self.realowner.view_ofs, 
+                       Damage(target_victim, self.realowner, self.realowner,
+                               swing_damage, WEP_SHOTGUN | HITTYPE_SECONDARY,
+                               self.realowner.origin + self.realowner.view_ofs,
                                v_forward * autocvar_g_balance_shotgun_secondary_force);
-                               
                        if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_SHOTGUN, 0, swing_damage); }
-                               
                        // draw large red flash for debugging
                        //te_customflash(targpos, 200, 2, '15 0 0');
-                       
                        if(autocvar_g_balance_shotgun_secondary_melee_multihit) // allow multiple hits with one swing, but not against the same player twice.
                        {
                                self.swing_alreadyhit = target_victim;
                        }
                }
        }
-       
        if(time >= self.cnt + meleetime)
        {
                // melee is finished
        }
        else
        {
-               // set up next frame 
+               // set up next frame
                self.swing_prev = i;
                self.nextthink = time;
        }
@@@ -179,12 -178,7 +179,7 @@@ float w_shotgun(float req
                if(vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_shotgun_secondary_melee_range)
                        self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
                else
-               {
-                       if(autocvar_g_antilag_bullets)
-                               self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
-                       else
-                               self.BUTTON_ATCK = bot_aim(autocvar_g_balance_shotgun_primary_speed, 0, 0.001, FALSE);
-               }
+                       self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
  
        else if (req == WR_THINK)
        {