]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/intrusive_2
authorMario <mario@smbclan.net>
Sat, 22 Oct 2016 17:48:51 +0000 (03:48 +1000)
committerMario <mario@smbclan.net>
Sat, 22 Oct 2016 17:48:51 +0000 (03:48 +1000)
15 files changed:
1  2 
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/weapons/weapon/arc.qc
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/hagar.qc
qcsrc/common/weapons/weapon/hook.qc
qcsrc/common/weapons/weapon/mortar.qc
qcsrc/common/weapons/weapon/seeker.qc
qcsrc/server/client.qc
qcsrc/server/defs.qh
qcsrc/server/mutators/mutator/gamemode_ctf.qc
qcsrc/server/player.qc

index 68754393446c61808b489ee15994cafa24add716,bbaf2e4696cf85949282cad609ee7e0f24a47b95..aae268666f1e72d501150f8e0fff59fa9c786272
@@@ -142,7 -142,6 +142,7 @@@ void M_Shambler_Attack_Lightning(entit
        gren.damageforcescale = 0;
        gren.event_damage = M_Shambler_Attack_Lightning_Damage;
        gren.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false);
  
@@@ -201,7 -200,7 +201,7 @@@ bool M_Shambler_Attack(int attack_type
        return false;
  }
  
- spawnfunc(monster_shambler) { Monster_Spawn(this, MON_SHAMBLER.monsterid); }
+ spawnfunc(monster_shambler) { Monster_Spawn(this, true, MON_SHAMBLER.monsterid); }
  #endif // SVQC
  
  #ifdef SVQC
index 4d701d04790060f40e9d8bbd67b956d6af8d807d,51122dfcf57d73a5575aff79dab4e8f25b31fdef..a122865321f0a5a69897245c38f69b9df00a4b11
@@@ -161,7 -161,6 +161,7 @@@ void M_Spider_Attack_Web(entity this
        IL_PUSH(g_projectiles, proj);
        IL_PUSH(g_bot_dodge, proj);
        proj.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, proj);
  
        proj.bouncefactor = 0.3;
        proj.bouncestop = 0.05;
@@@ -191,7 -190,7 +191,7 @@@ bool M_Spider_Attack(int attack_type, e
        return false;
  }
  
- spawnfunc(monster_spider) { Monster_Spawn(this, MON_SPIDER.monsterid); }
+ spawnfunc(monster_spider) { Monster_Spawn(this, true, MON_SPIDER.monsterid); }
  #endif // SVQC
  
  #ifdef SVQC
index 69543a74569932bbe7d2295067a2742182991cf6,f680f118b3dd5acbe8a14093c03556173cf3cc21..72e79d58ac60e563be638bc961dca1b0e76e7376
@@@ -99,10 -99,10 +99,10 @@@ bool Monster_ValidTarget(entity this, e
                return false;
        }
  
-       traceline(this.origin + this.view_ofs, targ.origin, 0, this);
+       traceline(this.origin + this.view_ofs, targ.origin, MOVE_NOMONSTERS, this);
  
-       if((trace_fraction < 1) && (trace_ent != targ))
-               return false;
+       if(trace_fraction < 1)
+               return false; // solid
  
        if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
        if(this.enemy != targ)
@@@ -505,7 -505,7 +505,7 @@@ bool Monster_Respawn_Check(entity this
        return true;
  }
  
- void Monster_Respawn(entity this) { Monster_Spawn(this, this.monsterid); }
+ void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterid); }
  
  .vector       pos1, pos2;
  
@@@ -692,7 -692,8 +692,8 @@@ void Monster_CalculateVelocity(entity t
  
  void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
  {
-       if(this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
+       // update goal entity if lost
+       if(this.target2 && this.goalentity.targetname != this.target2) { this.goalentity = find(NULL, targetname, this.target2); }
  
        entity targ;
  
        }
        else
        {
-               entity e = find(NULL, targetname, this.target2);
+               entity e = this.goalentity; //find(NULL, targetname, this.target2);
                if(e.target2)
                        this.target2 = e.target2;
-               else if(e.target)
+               else if(e.target) // compatibility
                        this.target2 = e.target;
  
                movelib_brake_simple(this, stpspeed);
@@@ -933,8 -934,7 +934,7 @@@ void Monster_Dead_Think(entity this
  void Monster_Appear(entity this, entity actor, entity trigger)
  {
        this.enemy = actor;
-       this.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
-       Monster_Spawn(this, this.monsterid);
+       Monster_Spawn(this, false, this.monsterid);
  }
  
  bool Monster_Appear_Check(entity this, int monster_id)
@@@ -1128,23 -1128,19 +1128,19 @@@ void Monster_Move_2D(entity this, floa
                return;
        }
  
-       float reverse = false;
-       vector a, b;
        makevectors(this.angles);
-       a = this.origin + '0 0 16';
-       b = this.origin + '0 0 16' + v_forward * 32;
+       vector a = CENTER_OR_VIEWOFS(this);
+       vector b = CENTER_OR_VIEWOFS(this) + v_forward * 32;
  
        traceline(a, b, MOVE_NORMAL, this);
  
+       bool reverse = false;
        if(trace_fraction != 1.0)
-       {
                reverse = true;
-               if(trace_ent)
-               if(IS_PLAYER(trace_ent) && !(trace_ent.items & IT_STRENGTH))
-                       reverse = false;
-       }
+       if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid))
+               reverse = false;
+       if(trace_ent && IS_MONSTER(trace_ent))
+               reverse = true;
  
        // TODO: fix this... tracing is broken if the floor is thin
        /*
@@@ -1212,7 -1208,7 +1208,7 @@@ void Monster_Anim(entity this
  void Monster_Think(entity this)
  {
        setthink(this, Monster_Think);
-       this.nextthink = this.ticrate;
+       this.nextthink = time + this.ticrate;
  
        if(this.monster_lifetime)
        if(time >= this.monster_lifetime)
@@@ -1291,7 -1287,7 +1287,7 @@@ bool Monster_Spawn_Setup(entity this
        return true;
  }
  
- bool Monster_Spawn(entity this, int mon_id)
+ bool Monster_Spawn(entity this, bool check_appear, int mon_id)
  {
        // setup the basic required properties for a monster
        entity mon = Monsters_from(mon_id);
        if(!autocvar_g_monsters) { Monster_Remove(this); return false; }
  
        if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
 +      {
                IL_PUSH(g_monsters, this);
 +              IL_PUSH(g_damagedbycontents, this);
 +      }
  
-       if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
+       if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed
  
        if(!this.monster_skill)
                this.monster_skill = cvar("g_monsters_skill");
        this.flags                              = FL_MONSTER;
        this.classname                  = "monster";
        this.takedamage                 = DAMAGE_AIM;
+       if(!this.bot_attack)
+               IL_PUSH(g_bot_targets, this);
        this.bot_attack                 = true;
-       IL_PUSH(g_bot_targets, this);
        this.iscreature                 = true;
        this.teleportable               = true;
        this.damagedbycontents  = true;
        this.oldtarget2                 = this.target2;
        this.pass_distance              = 0;
        this.deadflag                   = DEAD_NO;
-       this.noalign                    = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM));
        this.spawn_time                 = time;
        this.gravity                    = 1;
        this.monster_moveto             = '0 0 0';
        this.monster_face               = '0 0 0';
        this.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
  
+       if(!this.noalign) { this.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)); }
        if(!this.scale) { this.scale = 1; }
        if(autocvar_g_monsters_edit) { this.grab = 1; }
        if(autocvar_g_fullbrightplayers) { this.effects |= EF_FULLBRIGHT; }
index e3cb6d6ea43a9aef2ab6bd83d9c19fc71df091df,d2094049438414112aab676a98ee430ffbd22cd8..0d804a91427374c74219435695f929a56e84a25f
@@@ -211,7 -211,7 +211,7 @@@ void napalm_damage(entity this, float d
                        if(d < dist)
                        {
                                e.fireball_impactvec = p;
-                               RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+                               RandomSelection_AddEnt(e, 1 / (1 + d), !Fire_IsBurning(e));
                        }
                }
        if(RandomSelection_chosen_ent)
@@@ -654,7 -654,7 +654,7 @@@ void nade_heal_boom(entity this
  
  void nade_monster_boom(entity this)
  {
-       entity e = spawnmonster(this.pokenade_type, 0, this.realowner, this.realowner, this.origin, false, false, 1);
+       entity e = spawnmonster(spawn(), this.pokenade_type, 0, this.realowner, this.realowner, this.origin, false, false, 1);
  
        if(autocvar_g_nades_pokenade_monster_lifetime > 0)
                e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
@@@ -911,7 -911,7 +911,7 @@@ void toss_nade(entity e, bool set_owner
                setsize(_nade, '-16 -16 -16', '16 16 16');
        set_movetype(_nade, MOVETYPE_BOUNCE);
  
-       tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade);
+       tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, MOVE_NOMONSTERS, _nade);
        if (trace_startsolid)
                setorigin(_nade, e.origin);
  
        _nade.gravity = 1;
        _nade.missile_flags = MIF_SPLASH | MIF_ARC;
        _nade.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, _nade);
        _nade.angles = vectoangles(_nade.velocity);
        _nade.flags = FL_PROJECTILE;
        IL_PUSH(g_projectiles, _nade);
index 8312fe19592217290655f4a520bc88721823d6b4,cd62f0746b69f6b2dc892f42a1938807cf566d07..6cd2c2b44e0c955d5a842c3d933621320bb7b66f
@@@ -900,7 -900,7 +900,7 @@@ bool vehicle_impulse(entity this, int i
  
  void vehicles_enter(entity pl, entity veh)
  {
-    // Remove this when bots know how to use vehicles
+       // Remove this when bots know how to use vehicles
        if((IS_BOT_CLIENT(pl) && !autocvar_g_vehicles_allow_bots))
                return;
  
@@@ -1167,7 -1167,6 +1167,7 @@@ bool vehicle_initialize(entity this, Ve
        this.iscreature                         = true;
        this.teleportable                       = false; // no teleporting for vehicles, too buggy
        this.damagedbycontents          = true;
 +      IL_PUSH(g_damagedbycontents, this);
        this.vehicleid                          = info.vehicleid;
        this.PlayerPhysplug                     = info.PlayerPhysplug;
        this.event_damage                       = func_null;
        else
                this.nextthink = time + game_starttime;
  
-       if(MUTATOR_CALLHOOK(VehicleSpawn, this))
+       if(!MUTATOR_CALLHOOK(VehicleInit, this))
                return false;
  
        return true;
index 0118f67fde7773a2e815335b9caef22ec8b03d3d,57697635791cd7b229ba5bf6639717a61a21c3c1..4c9415305ab64b5fa68b765df55ca547ed734c3f
@@@ -290,7 -290,6 +290,7 @@@ void W_Arc_Attack_Bolt(Weapon thiswep, 
        missile.damageforcescale = WEP_CVAR(arc, bolt_damageforcescale);
        missile.event_damage = W_Arc_Bolt_Damage;
        missile.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, missile);
  
        settouch(missile, W_Arc_Bolt_Touch);
        missile.use = W_Arc_Bolt_Explode_use;
@@@ -341,6 -340,8 +341,8 @@@ void W_Arc_Beam_Think(entity this
                ||
                forbidWeaponUse(this.owner)
                ||
+               PS(this.owner).m_switchweapon != WEP_ARC
+               ||
                (!PHYS_INPUT_BUTTON_ATCK(this.owner) && !burst )
                ||
                this.owner.vehicle
index 13cb289a4798777bd555471e86ef0f8be4a8806d,df9bd470a31b9fe70caaf68cde8a2d4ff6aebf7f..7e63e760ec96d4d879a6813e6e436232aef965f2
@@@ -40,6 -40,7 +40,7 @@@ CLASS(Devastator, Weapon
          P(class, prefix, remote_edgedamage, float, NONE) \
          P(class, prefix, remote_force, float, NONE) \
          P(class, prefix, remote_jump_damage, float, NONE) \
+         P(class, prefix, remote_jump_force, float, NONE) \
          P(class, prefix, remote_jump_radius, float, NONE) \
          P(class, prefix, remote_jump_velocity_z_add, float, NONE) \
          P(class, prefix, remote_jump_velocity_z_max, float, NONE) \
@@@ -141,50 -142,56 +142,56 @@@ void W_Devastator_DoRemoteExplode(entit
        this.event_damage = func_null;
        this.takedamage = DAMAGE_NO;
  
-       float handled_as_rocketjump = false;
+       bool handled_as_rocketjump = false;
+       entity head = NULL;
  
-       entity head = WarpZone_FindRadius(
-               this.origin,
-               WEP_CVAR(devastator, remote_jump_radius),
-               false
-       );
-       while(head)
+       if(WEP_CVAR(devastator, remote_jump_radius))
        {
-               if(head.takedamage && (head == this.realowner))
+               head = WarpZone_FindRadius(
+                       this.origin,
+                       WEP_CVAR(devastator, remote_jump_radius),
+                       false
+               );
+               while(head)
                {
-                       float distance_to_head = vlen(this.origin - head.WarpZone_findradius_nearest);
-                       if(distance_to_head <= WEP_CVAR(devastator, remote_jump_radius))
+                       if(head.takedamage && (head == this.realowner))
                        {
-                               // we handled this as a rocketjump :)
-                               handled_as_rocketjump = true;
-                               // modify velocity
-                               head.velocity_x *= 0.9;
-                               head.velocity_y *= 0.9;
-                               head.velocity_z = bound(
-                                       WEP_CVAR(devastator, remote_jump_velocity_z_min),
-                                       head.velocity.z + WEP_CVAR(devastator, remote_jump_velocity_z_add),
-                                       WEP_CVAR(devastator, remote_jump_velocity_z_max)
-                               );
-                               // now do the damage
-                               RadiusDamage(
-                                       this,
-                                       head,
-                                       WEP_CVAR(devastator, remote_jump_damage),
-                                       WEP_CVAR(devastator, remote_jump_damage),
-                                       WEP_CVAR(devastator, remote_jump_radius),
-                                       NULL,
-                                       head,
-                                       0,
-                                       this.projectiledeathtype | HITTYPE_BOUNCE,
-                                       NULL
-                               );
-                               break;
+                               if(vdist(this.origin - head.WarpZone_findradius_nearest, <=, WEP_CVAR(devastator, remote_jump_radius)))
+                               {
+                                       // we handled this as a rocketjump :)
+                                       handled_as_rocketjump = true;
+                                       // modify velocity
+                                       if(WEP_CVAR(devastator, remote_jump_velocity_z_add))
+                                       {
+                                               head.velocity_x *= 0.9;
+                                               head.velocity_y *= 0.9;
+                                               head.velocity_z = bound(
+                                                       WEP_CVAR(devastator, remote_jump_velocity_z_min),
+                                                       head.velocity.z + WEP_CVAR(devastator, remote_jump_velocity_z_add),
+                                                       WEP_CVAR(devastator, remote_jump_velocity_z_max)
+                                               );
+                                       }
+                                       // now do the damage
+                                       RadiusDamage(
+                                               this,
+                                               head,
+                                               WEP_CVAR(devastator, remote_jump_damage),
+                                               WEP_CVAR(devastator, remote_jump_damage),
+                                               WEP_CVAR(devastator, remote_jump_radius),
+                                               NULL,
+                                               head,
+                                               (WEP_CVAR(devastator, remote_jump_force) ? WEP_CVAR(devastator, remote_jump_force) : 0),
+                                               this.projectiledeathtype | HITTYPE_BOUNCE,
+                                               NULL
+                                       );
+                                       break;
+                               }
                        }
+                       head = head.chain;
                }
-               head = head.chain;
        }
  
        RadiusDamage(
@@@ -384,7 -391,6 +391,7 @@@ void W_Devastator_Attack(Weapon thiswep
        missile.health = WEP_CVAR(devastator, health);
        missile.event_damage = W_Devastator_Damage;
        missile.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, missile);
  
        set_movetype(missile, MOVETYPE_FLY);
        PROJECTILE_MAKETRIGGER(missile);
@@@ -518,16 -524,15 +525,15 @@@ METHOD(Devastator, wr_think, void(entit
          if(fire & 2)
          if(PS(actor).m_switchweapon == WEP_DEVASTATOR)
          {
-             entity rock;
              bool rockfound = false;
-             for(rock = NULL; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == actor)
+             IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket",
              {
-                 if(!rock.rl_detonate_later)
+                 if(!it.rl_detonate_later)
                  {
-                     rock.rl_detonate_later = true;
+                     it.rl_detonate_later = true;
                      rockfound = true;
                  }
-             }
+             });
              if(rockfound)
                  sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
          }
index 69f12eb457c6533caa5af4d6831946cd9c75ae8b,097f2bfdcccdc06fb4df27b6e486d47260a77ab9..d533f19098b7ae6f71efd6ae595f3fd4b6b14635
@@@ -155,7 -155,6 +155,7 @@@ void W_Hagar_Attack(Weapon thiswep, ent
        missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale);
        missile.event_damage = W_Hagar_Damage;
        missile.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, missile);
  
        settouch(missile, W_Hagar_Touch);
        missile.use = W_Hagar_Explode_use;
@@@ -200,7 -199,6 +200,7 @@@ void W_Hagar_Attack2(Weapon thiswep, en
        missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
        missile.event_damage = W_Hagar_Damage;
        missile.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, missile);
  
        settouch(missile, W_Hagar_Touch2);
        missile.cnt = 0;
@@@ -262,7 -260,6 +262,7 @@@ void W_Hagar_Attack2_Load_Release(entit
                missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
                missile.event_damage = W_Hagar_Damage;
                missile.damagedbycontents = true;
 +              IL_PUSH(g_damagedbycontents, missile);
  
                settouch(missile, W_Hagar_Touch); // not bouncy
                missile.use = W_Hagar_Explode2_use;
@@@ -313,7 -310,7 +313,7 @@@ void W_Hagar_Attack2_Load(Weapon thiswe
  {
        // loadable hagar secondary attack, must always run each frame
  
-       if(time < game_starttime)
+       if(time < game_starttime || PS(actor).m_switchweapon != WEP_HAGAR)
                return;
  
        bool loaded = actor.hagar_load >= WEP_CVAR_SEC(hagar, load_max);
  
  void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
  {
-       if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock)
+       if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock || PS(actor).m_switchweapon != WEP_HAGAR)
        {
                w_ready(thiswep, actor, weaponentity, fire);
                return;
index 37a7a60e9f38adb29fdbb9b513f30ff3f0d086de,ba7efe6a31a33384e1c1b0cbcb0a535e6ca72b99..f5dd96e4c3c6b22ebf31c95f09a09f3b3e7fa03c
@@@ -169,7 -169,6 +169,7 @@@ void W_Hook_Attack2(Weapon thiswep, ent
        gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale);
        gren.event_damage = W_Hook_Damage;
        gren.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
  
        gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed);
@@@ -384,13 -383,16 +384,16 @@@ void Draw_GrapplingHook(entity this
                        break;
        }
  
-       if((this.owner.sv_entnum == player_localentnum - 1) && autocvar_chase_active <= 0)
+       if((this.owner.sv_entnum == player_localentnum - 1))
        {
                switch(this.HookType)
                {
                        default:
                        case NET_ENT_CLIENT_HOOK:
-                               a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
+                               if(autocvar_chase_active > 0)
+                                       a = csqcplayer.origin;
+                               else
+                                       a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
                                b = this.origin;
                                break;
                        case NET_ENT_CLIENT_ARC_BEAM:
index 1f019ac81518dba3738504addb97606de345b7dd,1428cc4fa9d8395b74522f6fc69f2e616d3f90ea..89ff5bbd8852eef69e0c497a679da28c5bde1293
@@@ -242,7 -242,6 +242,7 @@@ void W_Mortar_Attack(Weapon thiswep, en
        gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale);
        gren.event_damage = W_Mortar_Grenade_Damage;
        gren.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_UP_PRI(gren, mortar);
  
@@@ -292,7 -291,6 +292,7 @@@ void W_Mortar_Attack2(Weapon thiswep, e
        gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale);
        gren.event_damage = W_Mortar_Grenade_Damage;
        gren.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, gren);
        gren.missile_flags = MIF_SPLASH | MIF_ARC;
        W_SetupProjVelocity_UP_SEC(gren, mortar);
  
@@@ -365,15 -363,14 +365,14 @@@ METHOD(Mortar, wr_think, void(entity th
          if(WEP_CVAR_SEC(mortar, remote_detonateprimary))
          {
              bool nadefound = false;
-             entity nade;
-             for(nade = NULL; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == actor)
+             IL_EACH(g_projectiles, it.realowner == actor && it.classname == "grenade",
              {
-                 if(!nade.gl_detonate_later)
+                 if(!it.gl_detonate_later)
                  {
-                     nade.gl_detonate_later = true;
+                     it.gl_detonate_later = true;
                      nadefound = true;
                  }
-             }
+             });
              if(nadefound)
                  sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
          }
index ce06c247dfff2d4ddb5152713f268dd3b2e9e0db,ffdbfc0d7053e704c88e9152d691ede046aa6d14..0e73adf2d3bd23da383640b9b6f875851ddeb150
@@@ -152,7 -152,7 +152,7 @@@ void W_Seeker_Missile_Think(entity this
                // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
                if(WEP_CVAR(seeker, missile_smart) && (dist > WEP_CVAR(seeker, missile_smart_mindist)))
                {
-                       // Is it a better idea (shorter distance) to trace to the target itthis?
+                       // Is it a better idea (shorter distance) to trace to the target itself?
                        if( vdist(this.origin + olddir * this.wait, <, dist))
                                traceline(this.origin, this.origin + olddir * this.wait, false, this);
                        else
@@@ -282,7 -282,6 +282,7 @@@ void W_Seeker_Fire_Missile(Weapon thisw
        missile.health          = WEP_CVAR(seeker, missile_health);
        missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale);
        missile.damagedbycontents = true;
 +      IL_PUSH(g_damagedbycontents, missile);
        //missile.think           = W_Seeker_Missile_Animate; // csqc projectiles.
  
        if(missile.enemy != NULL)
diff --combined qcsrc/server/client.qc
index 667cb11a8ac7c667f61c00d1d2231c1abf3d5757,554ef656b41dd15774f8966e004d600e1b3e13a3..db46221d5e8dad5f52947efd295a24b9b35a6570
@@@ -303,8 -303,6 +303,8 @@@ void PutObserverInServer(entity this
        TRANSMUTE(Observer, this);
        this.iscreature = false;
        this.teleportable = TELEPORT_SIMPLE;
 +      if(this.damagedbycontents)
 +              IL_REMOVE(g_damagedbycontents, this);
        this.damagedbycontents = false;
        this.health = FRAGS_SPECTATOR;
        SetSpectatee_status(this, etof(this));
@@@ -526,8 -524,6 +526,8 @@@ void PutClientInServer(entity this
                this.wasplayer = true;
                this.iscreature = true;
                this.teleportable = TELEPORT_NORMAL;
 +              if(!this.damagedbycontents)
 +                      IL_PUSH(g_damagedbycontents, this);
                this.damagedbycontents = true;
                set_movetype(this, MOVETYPE_WALK);
                this.solid = SOLID_SLIDEBOX;
                FixPlayermodel(this);
                this.drawonlytoclient = NULL;
  
+               this.viewloc = NULL;
                this.crouch = false;
-               this.view_ofs = STAT(PL_VIEW_OFS, NULL);
-               setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL));
+               this.view_ofs = STAT(PL_VIEW_OFS, this);
+               setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this));
                this.spawnorigin = spot.origin;
                setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24));
                // don't reset back to last position, even if new position is stuck in solid
  
  void ClientInit_misc(entity this);
  
- .float ebouncefactor, ebouncestop; // electro's values
  // TODO do we need all these fields, or should we stop autodetecting runtime
  // changes and just have a console command to update this?
  bool ClientInit_SendEntity(entity this, entity to, int sf)
@@@ -1146,7 -1143,10 +1147,10 @@@ void ClientConnect(entity this
  
        this.netname_previous = strzone(this.netname);
  
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname);
+       if(teamplay && IS_PLAYER(this))
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname);
+       else
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname);
  
        stuffcmd(this, clientstuff, "\n");
        stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this?
        if (IS_REAL_CLIENT(this))
                sv_notice_join(this);
  
+       // update physics stats (players can spawn before physics runs)
+       Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
        IL_EACH(g_initforplayer, it.init_for_player, {
                it.init_for_player(it, this);
        });
@@@ -1608,7 -1611,7 +1615,7 @@@ void SetZoomState(entity this, float z
  void GetPressedKeys(entity this)
  {
        MUTATOR_CALLHOOK(GetPressedKeys, this);
-       int keys = this.pressedkeys;
+       int keys = STAT(PRESSED_KEYS, this);
        keys = BITSET(keys, KEY_FORWARD,        this.movement.x > 0);
        keys = BITSET(keys, KEY_BACKWARD,       this.movement.x < 0);
        keys = BITSET(keys, KEY_RIGHT,          this.movement.y > 0);
        keys = BITSET(keys, KEY_CROUCH,         PHYS_INPUT_BUTTON_CROUCH(this));
        keys = BITSET(keys, KEY_ATCK,           PHYS_INPUT_BUTTON_ATCK(this));
        keys = BITSET(keys, KEY_ATCK2,          PHYS_INPUT_BUTTON_ATCK2(this));
-       this.pressedkeys = keys;
+       this.pressedkeys = keys; // store for other users
+       STAT(PRESSED_KEYS, this) = keys;
  }
  
  /*
@@@ -1651,7 -1656,7 +1660,7 @@@ void SpectateCopy(entity this, entity s
        this.hit_time = spectatee.hit_time;
        this.strength_finished = spectatee.strength_finished;
        this.invincible_finished = spectatee.invincible_finished;
-       this.pressedkeys = spectatee.pressedkeys;
+       STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee);
        this.weapons = spectatee.weapons;
        this.vortex_charge = spectatee.vortex_charge;
        this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
        this.angles = spectatee.v_angle;
        STAT(FROZEN, this) = STAT(FROZEN, spectatee);
        this.revive_progress = spectatee.revive_progress;
+       this.viewloc = spectatee.viewloc;
        if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
                this.fixangle = true;
        setorigin(this, spectatee.origin);
  bool SpectateUpdate(entity this)
  {
        if(!this.enemy)
-           return false;
+               return false;
  
        if(!IS_PLAYER(this.enemy) || this == this.enemy)
        {
@@@ -1870,40 -1876,34 +1880,34 @@@ void ShowRespawnCountdown(entity this
        }
  }
  
- .float caplayer;
- void LeaveSpectatorMode(entity this)
+ .bool team_selected;
+ bool ShowTeamSelection(entity this)
  {
-       if(this.caplayer)
-               return;
-       if(nJoinAllowed(this, this))
-       {
-               if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
-               {
-                       TRANSMUTE(Player, this);
-                       SetSpectatee(this, NULL);
+       if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0)
+               return false;
+       stuffcmd(this, "menu_showteamselect\n");
+       return true;
+ }
+ void Join(entity this)
+ {
+       TRANSMUTE(Player, this);
  
-                       if(autocvar_g_campaign || autocvar_g_balance_teams)
-                               { JoinBestTeam(this, false, true); }
+       if(!this.team_selected)
+       if(autocvar_g_campaign || autocvar_g_balance_teams)
+               JoinBestTeam(this, false, true);
  
-                       if(autocvar_g_campaign)
-                               { campaign_bots_may_start = true; }
+       if(autocvar_g_campaign)
+               campaign_bots_may_start = true;
  
-                       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
+       Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN);
  
-                       PutClientInServer(this);
+       PutClientInServer(this);
  
-                       if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); }
-               }
-               else
-                       stuffcmd(this, "menu_showteamselect\n");
-       }
+       if(teamplay && this.team != -1)
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_PLAY_TEAM), this.netname);
        else
-       {
-               // Player may not join because g_maxplayers is set
-               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
-       }
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname);
+       this.team_selected = false;
  }
  
  /**
   * it checks whether the number of currently playing players exceeds g_maxplayers.
   * @return int number of free slots for players, 0 if none
   */
bool nJoinAllowed(entity this, entity ignore)
int nJoinAllowed(entity this, entity ignore)
  {
        if(!ignore)
        // this is called that way when checking if anyone may be able to join (to build qcstatus)
        // so report 0 free slots if restricted
        {
                if(autocvar_g_forced_team_otherwise == "spectate")
-                       return false;
+                       return 0;
                if(autocvar_g_forced_team_otherwise == "spectator")
-                       return false;
+                       return 0;
        }
  
-       if(this.team_forced < 0)
-               return false; // forced spectators can never join
+       if(this && this.team_forced < 0)
+               return 0; // forced spectators can never join
  
        // TODO simplify this
        int totalClients = 0;
                        ++currentlyPlaying;
        ));
  
+       float free_slots = 0;
        if (!autocvar_g_maxplayers)
-               return maxclients - totalClients;
+               free_slots = maxclients - totalClients;
+       else if(currentlyPlaying < autocvar_g_maxplayers)
+               free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
  
-       if(currentlyPlaying < autocvar_g_maxplayers)
-               return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
+       static float join_prevent_msg_time = 0;
+       if(this && ignore && !free_slots && time > join_prevent_msg_time)
+       {
+               Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT);
+               join_prevent_msg_time = time + 3;
+       }
  
-       return false;
+       return free_slots;
  }
  
  /**
@@@ -2011,6 -2018,16 +2022,16 @@@ void PrintWelcomeMessage(entity this
        }
  }
  
+ bool joinAllowed(entity this)
+ {
+       if (this.version_mismatch) return false;
+       if (!nJoinAllowed(this, this)) return false;
+       if (teamplay && lockteams) return false;
+       if (ShowTeamSelection(this)) return false;
+       if (MUTATOR_CALLHOOK(ForbidSpawn, this)) return false;
+       return true;
+ }
  void ObserverThink(entity this)
  {
        if ( this.impulse )
                MinigameImpulse(this, this.impulse);
                this.impulse = 0;
        }
        if (this.flags & FL_JUMPRELEASED) {
-               if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        this.flags |= FL_SPAWNING;
                } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) {
                        if(this.flags & FL_SPAWNING)
                        {
                                this.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode(this);
+                               Join(this);
                                return;
                        }
                }
@@@ -2058,8 -2076,9 +2080,9 @@@ void SpectatorThink(entity this
                        return;
                }
        }
        if (this.flags & FL_JUMPRELEASED) {
-               if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) {
+               if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) {
                        this.flags &= ~FL_JUMPRELEASED;
                        this.flags |= FL_SPAWNING;
                } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) {
                        if(this.flags & FL_SPAWNING)
                        {
                                this.flags &= ~FL_SPAWNING;
-                               LeaveSpectatorMode(this);
+                               Join(this);
                                return;
                        }
                }
@@@ -2321,7 -2340,7 +2344,7 @@@ void PlayerPreThink (entity this
                                        {
                                                if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max))
                                                        this.deadflag = DEAD_RESPAWNING;
-                                               else if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE))
+                                               else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)))
                                                        this.deadflag = DEAD_DEAD;
                                                break;
                                        }
diff --combined qcsrc/server/defs.qh
index 49e7236d2f3d71cc923b7dfb3df772c2501fdd51,4574b23490f3147447772eb754d41f1b6a750d08..ee5cda197efbc7bc39948f12b97e751b3bbe879f
@@@ -160,7 -160,6 +160,6 @@@ bool nJoinAllowed(entity this, entity i
  .float noalign;               // if set to 1, the item or spawnpoint won't be dropped to the floor
  
  .vector death_origin;
- .vector killer_origin;
  
  float default_player_alpha;
  float default_weapon_alpha;
@@@ -261,7 -260,7 +260,7 @@@ bool independent_players
  
  string clientstuff;
  .float phase;
- .int pressedkeys = _STAT(PRESSED_KEYS);
+ .int pressedkeys;
  
  .string fog;
  
@@@ -418,19 -417,10 +417,13 @@@ const int MIF_GUIDED_CONFUSABLE = MIF_G
  
  ////
  
- .entity player_stats;
- //.float playerid;
- .string playernick;
- .float elos;
- .float ranks;
  .string cvar_cl_physics;
  
  .void(entity this, entity player) init_for_player;
  
 +IntrusiveList g_damagedbycontents;
 +STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); }
 +
  IntrusiveList g_monsters;
  STATIC_INIT(g_monsters) { g_monsters = IL_NEW(); }
  
index 9e1531170045eb8dd2544cbfd009cbfb20b3c7ba,0a3c622a9b0a3575f2d73c570e2866505362ec60..8120e1cb348a77e35e545eaa2c78733f41a2ea02
@@@ -135,10 -135,14 +135,14 @@@ void ctf_CaptureRecord(entity flag, ent
        string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
  
        // notify about shit
-       if(ctf_oneflag) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); }
-       else if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_TIME), player.netname, (cap_time * 100)); }
-       else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
-       else { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); }
+       if(ctf_oneflag)
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname);
+       else if(!ctf_captimerecord)
+               Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_TIME), player.netname, (cap_time * 100));
+       else if(cap_time < cap_record)
+               Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100));
+       else
+               Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100));
  
        // write that shit in the database
        if(!ctf_oneflag) // but not in 1-flag mode
@@@ -363,7 -367,7 +367,7 @@@ void ctf_Handle_Drop(entity flag, entit
        flag.ctf_status = FLAG_DROPPED;
  
        // messages and sounds
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_LOST) : INFO_CTF_LOST_NEUTRAL), player.netname);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_LOST), player.netname);
        _sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("dropped", player.team, player);
  
@@@ -425,11 -429,11 +429,11 @@@ void ctf_Handle_Retrieve(entity flag, e
  
        FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA(
                if(it == sender)
-                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_SENT) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname);
+                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_SENT), player.netname);
                else if(it == player)
-                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_RECEIVED) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname);
+                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_RECEIVED), sender.netname);
                else if(SAME_TEAM(it, sender))
-                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_OTHER) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname);
+                       Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_OTHER), sender.netname, player.netname);
        ));
  
        // create new waypoint
@@@ -566,7 -570,7 +570,7 @@@ void ctf_Handle_Capture(entity flag, en
        player.throw_count = 0;
  
        // messages and sounds
-       Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT(enemy_flag, CENTER_CTF_CAPTURE) : CENTER_CTF_CAPTURE_NEUTRAL));
+       Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_NUM(enemy_flag.team, CENTER_CTF_CAPTURE));
        ctf_CaptureRecord(enemy_flag, player);
        _sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE);
  
@@@ -610,12 -614,12 +614,12 @@@ void ctf_Handle_Return(entity flag, ent
        // messages and sounds
        if(IS_MONSTER(player))
        {
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN_MONSTER), player.monster_name);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN_MONSTER), player.monster_name);
        }
        else if(flag.team)
        {
-               Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT(flag, CENTER_CTF_RETURN));
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN), player.netname);
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_RETURN));
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN), player.netname);
        }
        _sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE);
        ctf_EventLog("return", flag.team, player);
@@@ -681,13 -685,17 +685,17 @@@ void ctf_Handle_Pickup(entity flag, ent
        }
  
        // messages and sounds
-       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_PICKUP) : INFO_CTF_PICKUP_NEUTRAL), player.netname);
-       if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); }
-       if(!flag.team) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); }
-       else if(CTF_DIFFTEAM(player, flag)) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT(flag, CENTER_CTF_PICKUP)); }
-       else { Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); }
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_PICKUP), player.netname);
+       if(ctf_stalemate)
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER);
+       if(!flag.team)
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL);
+       else if(CTF_DIFFTEAM(player, flag))
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_PICKUP));
+       else
+               Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team));
  
-       Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname);
+       Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
  
        if(!flag.team)
                FOREACH_CLIENT(IS_PLAYER(it) && it != player && DIFF_TEAM(it, player), LAMBDA(Send_Notification(NOTIF_ONE, it, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname)));
                FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA(
                        if(CTF_SAMETEAM(flag, it))
                        if(SAME_TEAM(player, it))
-                               Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
+                               Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname);
                        else
                                Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname);
                ));
@@@ -760,14 -768,17 +768,17 @@@ void ctf_CheckFlagReturn(entity flag, i
                {
                        switch(returntype)
                        {
-                               case RETURN_DROPPED: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DROPPED) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break;
-                               case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DAMAGED) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break;
-                               case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_SPEEDRUN) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break;
-                               case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_NEEDKILL) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break;
+                               case RETURN_DROPPED:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DROPPED)); break;
+                               case RETURN_DAMAGE:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DAMAGED)); break;
+                               case RETURN_SPEEDRUN:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_SPEEDRUN), ctf_captimerecord); break;
+                               case RETURN_NEEDKILL:
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_NEEDKILL)); break;
                                default:
                                case RETURN_TIMEOUT:
-                                       { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_TIMEOUT) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; }
+                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_TIMEOUT)); break;
                        }
                        _sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE);
                        ctf_EventLog("returned", flag.team, NULL);
@@@ -1246,8 -1257,6 +1257,8 @@@ void ctf_FlagSetup(int teamnumber, enti
        flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP;
        flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable;
        flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable;
 +      if(flag.damagedbycontents)
 +              IL_PUSH(g_damagedbycontents, flag);
        flag.velocity = '0 0 0';
        flag.mangle = flag.angles;
        flag.reset = ctf_Reset;
@@@ -2314,7 -2323,7 +2325,7 @@@ MUTATOR_HOOKFUNCTION(ctf, AbortSpeedrun
  
        if(player.flagcarried)
        {
-               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((player.flagcarried.team) ? APP_TEAM_ENT(player.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL));
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(player.flagcarried.team, INFO_CTF_FLAGRETURN_ABORTRUN));
                ctf_RespawnFlag(player.flagcarried);
                return true;
        }
@@@ -2360,7 -2369,7 +2371,7 @@@ MUTATOR_HOOKFUNCTION(ctf, HavocBot_Choo
        return true;
  }
  
- MUTATOR_HOOKFUNCTION(ctf, GetTeamCount)
+ MUTATOR_HOOKFUNCTION(ctf, CheckAllowedTeams)
  {
        //M_ARGV(0, float) = ctf_teams;
        M_ARGV(1, string) = "ctf_team";
diff --combined qcsrc/server/player.qc
index 721120aefcc6234c5f93401db0abdeeaf9b5a580,e270f38df034c3232cdcddbb12ed034f10ace7e0..77eae0aa20c516594e1e5b71c43f637468a83a2b
@@@ -62,8 -62,6 +62,8 @@@ void CopyBody(entity this, float keepve
        clone.iscreature = this.iscreature;
        clone.teleportable = this.teleportable;
        clone.damagedbycontents = this.damagedbycontents;
 +      if(clone.damagedbycontents)
 +              IL_PUSH(g_damagedbycontents, clone);
        clone.angles = this.angles;
        clone.v_angle = this.v_angle;
        clone.avelocity = this.avelocity;
@@@ -214,8 -212,6 +214,8 @@@ void PlayerCorpseDamage(entity this, en
                this.alpha = -1;
                this.solid = SOLID_NOT; // restore later
                this.takedamage = DAMAGE_NO; // restore later
 +              if(this.damagedbycontents)
 +                      IL_REMOVE(g_damagedbycontents, this);
                this.damagedbycontents = false;
        }
  }
@@@ -559,9 -555,8 +559,8 @@@ void PlayerDamage(entity this, entity i
  
          // increment frag counter for used weapon type
          Weapon w = DEATH_WEAPONOF(deathtype);
-         if(w != WEP_Null)
-       if(accuracy_isgooddamage(attacker, this))
-         attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1;
+               if(w != WEP_Null && accuracy_isgooddamage(attacker, this))
+                       attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1;
  
                MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
                excess = M_ARGV(4, float);
@@@ -689,7 -684,7 +688,7 @@@ void dedicated_print(string input
   */
  int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol)
  {
-       if (!teamsay && !privatesay) if (substring(msgin, 0, 1) == " ")
+       if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ")
          msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!)
  
        msgin = formatmessage(source, msgin);