Merge branch 'master' into Lyberta/StandaloneOverkillWeapons
authorLyberta <lyberta@lyberta.net>
Sat, 2 Dec 2017 15:55:11 +0000 (18:55 +0300)
committerLyberta <lyberta@lyberta.net>
Sat, 2 Dec 2017 15:55:11 +0000 (18:55 +0300)
34 files changed:
.gitlab-ci.yml
mutators.cfg
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/client/view.qc
qcsrc/common/mutators/mutator/_mod.inc
qcsrc/common/mutators/mutator/_mod.qh
qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
qcsrc/common/mutators/mutator/random_items/sv_random_items.qc
qcsrc/common/notifications/all.inc
qcsrc/common/physics/movetypes/movetypes.qc
qcsrc/common/scores.qh
qcsrc/common/triggers/misc/teleport_dest.qc
qcsrc/common/triggers/teleporters.qc
qcsrc/common/triggers/trigger/teleport.qc
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/lib/warpzone/common.qc
qcsrc/server/antilag.qc
qcsrc/server/antilag.qh
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/compat/quake3.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_world.qc
qcsrc/server/items.qc
qcsrc/server/items.qh
qcsrc/server/scores.qc
qcsrc/server/scores.qh
qcsrc/server/scores_rules.qc
qcsrc/server/weapons/tracing.qc

index 9305f52..9e6a7b6 100644 (file)
@@ -29,7 +29,7 @@ test_sv_game:
     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
     - make
-    - EXPECT=ed9be8d1b1a544f89bcdd7d36876fede
+    - EXPECT=3fb0c7a99263dd44e026804c12da2fa2
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index e37c190..79ac417 100644 (file)
@@ -480,6 +480,12 @@ set g_dynamic_handicap_exponent 1 "The exponent used to calculate handicap. 1 me
 set g_dynamic_handicap_min 0 "The minimum value of the handicap."
 set g_dynamic_handicap_max 0 "The maximum value of the handicap."
 
+// ===============
+// kick teamkiller
+// ===============
+set g_kick_teamkiller_rate 0 "Limit for teamkills per minute before the client gets dropped. 0 means that the teamkillers don't get kicked automatically"
+set g_kick_teamkiller_lower_limit 5 "Minimum number of teamkills before the teamkill rate is considered"
+
 // =====================
 //  stale-move negation
 // =====================
index 8e0d0f0..063cb88 100644 (file)
@@ -96,6 +96,7 @@ string TranslateScoresLabel(string l)
                case "kd": return CTX(_("SCO^k/d"));
                case "kdr": return CTX(_("SCO^kdr"));
                case "kills": return CTX(_("SCO^kills"));
+               case "teamkills": return CTX(_("SCO^teamkills"));
                case "laps": return CTX(_("SCO^laps"));
                case "lives": return CTX(_("SCO^lives"));
                case "losses": return CTX(_("SCO^losses"));
@@ -312,6 +313,7 @@ void Cmd_Scoreboard_Help()
        LOG_INFO(_("^3deaths^7                   Number of deaths"));
        LOG_INFO(_("^3suicides^7                 Number of suicides"));
        LOG_INFO(_("^3frags^7                    kills - suicides"));
+       LOG_INFO(_("^3teamkills^7                Number of teamkills"));
        LOG_INFO(_("^3kd^7                       The kill-death ratio"));
        LOG_INFO(_("^3dmg^7                      The total damage done"));
        LOG_INFO(_("^3dmgtaken^7                 The total damage taken"));
@@ -361,6 +363,7 @@ void Cmd_Scoreboard_Help()
 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
 " -teams,lms/deaths +ft,tdm/deaths" \
 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" +teams/teamkills"\
 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
index e2aadf1..d3c1314 100644 (file)
@@ -12,6 +12,7 @@
 #include "mutators/events.qh"
 
 #include <common/animdecide.qh>
+#include <common/deathtypes/all.qh>
 #include <common/ent_cs.qh>
 #include <common/anim.qh>
 #include <common/constants.qh>
@@ -912,7 +913,8 @@ vector crosshair_getcolor(entity this, float health_stat)
 
                case 2: // crosshair_color_by_health
                {
-                       float hp = health_stat;
+                       vector v = healtharmor_maxdamage(health_stat, STAT(ARMOR), armorblockpercent, DEATH_WEAPON.m_id);
+                       float hp = floor(v.x + 1);
 
                        //x = red
                        //y = green
index d1a6acf..9d52fa2 100644 (file)
@@ -15,6 +15,7 @@
 #include <common/mutators/mutator/instagib/_mod.inc>
 #include <common/mutators/mutator/invincibleproj/_mod.inc>
 #include <common/mutators/mutator/itemstime/_mod.inc>
+#include <common/mutators/mutator/kick_teamkiller/_mod.inc>
 #include <common/mutators/mutator/melee_only/_mod.inc>
 #include <common/mutators/mutator/midair/_mod.inc>
 #include <common/mutators/mutator/multijump/_mod.inc>
index a865d19..f9edf4c 100644 (file)
@@ -15,6 +15,7 @@
 #include <common/mutators/mutator/instagib/_mod.qh>
 #include <common/mutators/mutator/invincibleproj/_mod.qh>
 #include <common/mutators/mutator/itemstime/_mod.qh>
+#include <common/mutators/mutator/kick_teamkiller/_mod.qh>
 #include <common/mutators/mutator/melee_only/_mod.qh>
 #include <common/mutators/mutator/midair/_mod.qh>
 #include <common/mutators/mutator/multijump/_mod.qh>
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc
new file mode 100644 (file)
index 0000000..a374a0e
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh
new file mode 100644 (file)
index 0000000..98fb481
--- /dev/null
@@ -0,0 +1 @@
+// generated file; do not modify
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc
new file mode 100644 (file)
index 0000000..a3b028f
--- /dev/null
@@ -0,0 +1,33 @@
+
+float autocvar_g_kick_teamkiller_rate;
+float autocvar_g_kick_teamkiller_lower_limit;
+
+REGISTER_MUTATOR(kick_teamkiller, (autocvar_g_kick_teamkiller_rate > 0));
+
+MUTATOR_HOOKFUNCTION(kick_teamkiller, PlayerDies)
+{
+       if (!teamplay)
+       {
+               return;
+       }
+       if (warmup_stage)
+       {
+               return;
+       }
+       entity attacker = M_ARGV(1, entity);
+       if (!IS_REAL_CLIENT(attacker))
+       {
+               return;
+       }
+
+       int teamkills = PlayerScore_Get(attacker, SP_TEAMKILLS);
+       // use the players actual playtime
+       float playtime = time - CS(attacker).startplaytime;
+       // rate is in teamkills/minutes, playtime in seconds
+       if (teamkills >= autocvar_g_kick_teamkiller_lower_limit && 
+           teamkills >= autocvar_g_kick_teamkiller_rate*playtime/60.0)
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname);
+               dropclient(attacker);
+       }
+}
index 4ba441b..371d164 100644 (file)
@@ -158,7 +158,7 @@ MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
                        setorigin(wep, ent.origin);
                        setmodel(wep, MDL_OK_HMG);
                        wep.ok_item = true;
-                       wep.noalign = ent.noalign;
+                       wep.noalign = Item_ShouldKeepPosition(ent);
                        wep.cnt = ent.cnt;
                        wep.team = ent.team;
                        wep.respawntime = g_pickup_respawntime_superweapon;
@@ -174,7 +174,7 @@ MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
                        setorigin(wep, ent.origin);
                        setmodel(wep, MDL_OK_RPC);
                        wep.ok_item = true;
-                       wep.noalign = ent.noalign;
+                       wep.noalign = Item_ShouldKeepPosition(ent);
                        wep.cnt = ent.cnt;
                        wep.team = ent.team;
                        wep.respawntime = g_pickup_respawntime_superweapon;
index 47234be..3d30574 100644 (file)
@@ -299,7 +299,7 @@ entity RandomItems_ReplaceMapItem(entity item)
        if (!expr_evaluate(autocvar_g_overkill))
        {
                new_item = Item_Create(strzone(new_classname), item.origin,
-                       item.noalign);
+                       Item_ShouldKeepPosition(item));
                random_items_is_spawning = false;
                if (new_item == NULL)
                {
@@ -311,7 +311,7 @@ entity RandomItems_ReplaceMapItem(entity item)
                new_item = spawn();
                new_item.classname = strzone(new_classname);
                new_item.spawnfunc_checked = true;
-               new_item.noalign = item.noalign;
+               new_item.noalign = Item_ShouldKeepPosition(item);
                new_item.ok_item = true;
                Item_Initialize(new_item, new_classname);
                random_items_is_spawning = false;
index 3b5492a..b4c6146 100644 (file)
     MSG_INFO_NOTIF(QUIT_DISCONNECT,                         N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 disconnected"), "")
     MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for idling"), "")
     MSG_INFO_NOTIF(QUIT_KICK_SPECTATING,                    N_CONSOLE,  0, 0, "", "",           "",             _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "")
+    MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL,                      N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for excessive teamkilling"), "")
     MSG_INFO_NOTIF(QUIT_SPECTATE,                           N_CONSOLE,  1, 0, "s1", "",         "",             _("^BG%s^F3 is now spectating"), "")
 
     MSG_INFO_NOTIF(RACE_ABANDONED,                          N_CONSOLE,  1, 0, "s1", "",                                                                     "",                         _("^BG%s^BG has abandoned the race"), "")
index 995c65b..3fe2808 100644 (file)
@@ -544,7 +544,6 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
                        _Movetype_CheckWater(this);
                        this.origin = this.origin + movedt * this.velocity;
                        this.angles = this.angles + movedt * this.avelocity;
-                       _Movetype_LinkEdict(this, false);
                        break;
                case MOVETYPE_STEP:
                        _Movetype_Physics_Step(this, movedt);
@@ -563,6 +562,12 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt)
                case MOVETYPE_PHYSICS:
                        break;
        }
+
+       //_Movetype_CheckVelocity(this);
+
+       _Movetype_LinkEdict(this, true);
+
+       //_Movetype_CheckVelocity(this);
 }
 
 void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)  // to be run every move frame
index 646638a..476d0db 100644 (file)
@@ -34,6 +34,7 @@ REGISTER_SP(DMGTAKEN);
 REGISTER_SP(KILLS);
 REGISTER_SP(DEATHS);
 REGISTER_SP(SUICIDES);
+REGISTER_SP(TEAMKILLS);
 REGISTER_SP(FRAGS);
 
 REGISTER_SP(ELO);
index fc3cec8..3f52038 100644 (file)
@@ -55,11 +55,6 @@ spawnfunc(misc_teleporter_dest)
        spawnfunc_info_teleport_destination(this);
 }
 
-spawnfunc(target_teleporter)
-{
-       spawnfunc_info_teleport_destination(this);
-}
-
 #elif defined(CSQC)
 
 void teleport_dest_remove(entity this)
index 8e9936b..e1cd950 100644 (file)
@@ -228,9 +228,10 @@ entity Simple_TeleportPlayer(entity teleporter, entity player)
 
 void teleport_findtarget(entity this)
 {
+       bool istrigger = (this.solid == SOLID_TRIGGER);
+
        int n = 0;
-       entity e;
-       for(e = NULL; (e = find(e, targetname, this.target)); )
+       for(entity e = NULL; (e = find(e, targetname, this.target)); )
        {
                ++n;
 #ifdef SVQC
@@ -250,7 +251,7 @@ void teleport_findtarget(entity this)
        else if(n == 1)
        {
                // exactly one dest - bots love that
-               this.enemy = find(e, targetname, this.target);
+               this.enemy = find(NULL, targetname, this.target);
        }
        else
        {
@@ -259,9 +260,11 @@ void teleport_findtarget(entity this)
        }
 
        // now enable touch
-       settouch(this, Teleport_Touch);
+       if(istrigger)
+               settouch(this, Teleport_Touch);
 #ifdef SVQC
-       trigger_teleport_link(this);
+       if(istrigger)
+               trigger_teleport_link(this);
 #endif
 }
 
index 5f545f0..0330ce8 100644 (file)
@@ -12,53 +12,84 @@ void trigger_teleport_use(entity this, entity actor, entity trigger)
 }
 #endif
 
-void Teleport_Touch(entity this, entity toucher)
+bool Teleport_Active(entity this, entity player)
 {
        if (this.active != ACTIVE_ACTIVE)
-               return;
+               return false;
 
 #ifdef SVQC
-       if (!toucher.teleportable)
-               return;
+       if (!player.teleportable)
+               return false;
 
-       if(toucher.vehicle)
-       if(!toucher.vehicle.teleportable)
-               return;
+       if(player.vehicle)
+       if(!player.vehicle.teleportable)
+               return false;
 
-       if(IS_TURRET(toucher))
-               return;
+       if(IS_TURRET(player))
+               return false;
 #elif defined(CSQC)
-       if(!IS_PLAYER(toucher))
-               return;
+       if(!IS_PLAYER(player))
+               return false;
 #endif
 
-       if(IS_DEAD(toucher))
-               return;
+       if(IS_DEAD(player))
+               return false;
 
        if(this.team)
-               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
-                       return;
+               if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, player)))
+                       return false;
 
-       EXACTTRIGGER_TOUCH(this, toucher);
+       return true;
+}
+
+void Teleport_Touch(entity this, entity toucher)
+{
+       entity player = toucher;
+
+       if(!Teleport_Active(this, player))
+               return;
+
+       EXACTTRIGGER_TOUCH(this, player);
 
 #ifdef SVQC
-       if(IS_PLAYER(toucher))
-               RemoveGrapplingHooks(toucher);
+       if(IS_PLAYER(player))
+               RemoveGrapplingHooks(player);
 #endif
 
        entity e;
-       e = Simple_TeleportPlayer(this, toucher);
+       e = Simple_TeleportPlayer(this, player);
 
 #ifdef SVQC
        string s = this.target; this.target = string_null;
-       SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for trigger too?
+       SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
        if (!this.target) this.target = s;
 
-       SUB_UseTargets(e, toucher, toucher);
+       SUB_UseTargets(e, player, player);
 #endif
 }
 
 #ifdef SVQC
+void target_teleport_use(entity this, entity actor, entity trigger)
+{
+       entity player = actor;
+
+       if(!Teleport_Active(this, player))
+               return;
+
+       if(IS_PLAYER(player))
+               RemoveGrapplingHooks(player);
+
+       entity e = Simple_TeleportPlayer(this, player);
+
+       string s = this.target; this.target = string_null;
+       SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
+       if (!this.target) this.target = s;
+
+       SUB_UseTargets(e, player, player);
+}
+#endif
+
+#ifdef SVQC
 float trigger_teleport_send(entity this, entity to, float sf)
 {
        WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_TELEPORT);
@@ -101,6 +132,25 @@ spawnfunc(trigger_teleport)
 
        IL_PUSH(g_teleporters, this);
 }
+
+spawnfunc(target_teleporter)
+{
+       if(this.target == "")
+       {
+               // actually a destination!
+               spawnfunc_info_teleport_destination(this);
+               return;
+       }
+
+       this.active = ACTIVE_ACTIVE;
+
+       this.use = target_teleport_use;
+
+       if(this.noise != "")
+               FOREACH_WORD(this.noise, true, precache_sound(it));
+
+       InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET);
+}
 #elif defined(CSQC)
 NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew)
 {
index 990bc29..0ca4d52 100644 (file)
@@ -316,13 +316,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity)
        if(autocvar_g_antilag == 0 || noantilag)
                lag = 0; // only do hitscan, but no antilag
        if(lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag));
-               IL_EACH(g_monsters, it != actor,
-               {
-                       antilag_takeback(it, it, time - lag);
-               });
-       }
+               antilag_takeback_all(actor, lag);
 
        while(head)
        {
@@ -596,13 +590,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity)
        }
 
        if(lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it)));
-               IL_EACH(g_monsters, it != actor,
-               {
-                       antilag_restore(it, it);
-               });
-       }
+               antilag_restore_all(actor);
 }
 
 METHOD(Shockwave, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
index 65b1a7f..82d3a50 100644 (file)
@@ -819,12 +819,12 @@ bool WarpZoneLib_MoveOutOfSolid(entity e)
        vector m1 = e.maxs;
        e.mins = '0 0 0';
        e.maxs = '0 0 0';
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m0.x); e.mins.x = m0.x;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m1.x); e.maxs.x = m1.x;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m0.y); e.mins.y = m0.y;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m1.y); e.maxs.y = m1.y;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m0.z); e.mins.z = m0.z;
-       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m1.z); e.maxs.z = m1.z;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m0.x); e.mins_x = m0.x;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m1.x); e.maxs_x = m1.x;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m0.y); e.mins_y = m0.y;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m1.y); e.maxs_y = m1.y;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m0.z); e.mins_z = m0.z;
+       WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m1.z); e.maxs_z = m1.z;
        setorigin(e, e.origin);
 
        tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
index 46c8718..4062f7f 100644 (file)
@@ -119,3 +119,30 @@ void antilag_clear(entity e, entity store)
        }
        store.antilag_index = ANTILAG_MAX_ORIGINS - 1; // next one is 0
 }
+
+// TODO: use a single intrusive list across all antilagged entities
+void antilag_takeback_all(entity ignore, float lag)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && it != ignore, antilag_takeback(it, CS(it), time - lag));
+       IL_EACH(g_monsters, it != ignore,
+       {
+               antilag_takeback(it, it, time - lag);
+       });
+       IL_EACH(g_projectiles, it != ignore && it.classname == "nade",
+       {
+               antilag_takeback(it, it, time - lag);
+       });
+}
+
+void antilag_restore_all(entity ignore)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && it != ignore, antilag_restore(it, CS(it)));
+       IL_EACH(g_monsters, it != ignore,
+       {
+               antilag_restore(it, it);
+       });
+       IL_EACH(g_projectiles, it != ignore && it.classname == "nade",
+       {
+               antilag_restore(it, it);
+       });
+}
index 6cf392e..d57762c 100644 (file)
@@ -6,6 +6,9 @@ void antilag_takeback(entity e, entity store, float t);
 void antilag_restore(entity e, entity store);
 void antilag_clear(entity e, entity store);
 
+void antilag_takeback_all(entity ignore, float lag);
+void antilag_restore_all(entity ignore);
+
 .float antilag_debug;
 
 #define ANTILAG_LATENCY(e) min(0.4, CS(e).ping * 0.001)
index 2e2116c..856783d 100644 (file)
@@ -656,9 +656,11 @@ void PutPlayerInServer(entity this)
 
        PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
 
+       // player was spectator
        if (CS(this).killcount == FRAGS_SPECTATOR) {
                PlayerScore_Clear(this);
                CS(this).killcount = 0;
+               CS(this).startplaytime = time;
        }
 
        for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
index af81535..42d7d95 100644 (file)
@@ -85,6 +85,7 @@ CLASS(Client, Object)
     ATTRIB(Client, motd_actived_time, float, this.motd_actived_time);
     ATTRIB(Client, jointime, float, this.jointime);
     ATTRIB(Client, spectatortime, float, this.spectatortime);
+    ATTRIB(Client, startplaytime, float, this.startplaytime);
     ATTRIB(Client, version_nagtime, float, this.version_nagtime);
     ATTRIB(Client, netname_previous, string, this.netname_previous);
     ATTRIB(Client, allowed_timeouts, int, this.allowed_timeouts);
index a85f4cc..6a7f21a 100644 (file)
@@ -95,31 +95,31 @@ void target_give_init(entity this)
 {
        IL_EACH(g_items, it.targetname == this.target,
        {
-               if (it.classname == "weapon_rocketlauncher" || it.classname == "weapon_devastator") {
+               if (it.classname == "weapon_devastator") {
                        this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo);
                        this.netname = cons(this.netname, "devastator");
                }
-               else if (it.classname == "weapon_railgun") {
+               else if (it.classname == "weapon_vortex") {
                        this.ammo_cells += it.count * WEP_CVAR_PRI(vortex, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "vortex");
                }
-               else if (it.classname == "weapon_lightning") {
+               else if (it.classname == "weapon_electro") {
                        this.ammo_cells += it.count * WEP_CVAR_PRI(electro, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "electro");
                }
-               else if (it.classname == "weapon_plasmagun") {
+               else if (it.classname == "weapon_hagar") {
                        this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "hagar");
                }
-               else if (it.classname == "weapon_bfg") {
+               else if (it.classname == "weapon_crylink") {
                        this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo);
                        this.netname = cons(this.netname, "crylink");
                }
-               else if (it.classname == "weapon_grenadelauncher" || it.classname == "weapon_mortar") {
+               else if (it.classname == "weapon_mortar") {
                        this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO
                        this.netname = cons(this.netname, "mortar");
                }
-               else if (it.classname == "item_armor_body")
+               else if (it.classname == "item_armor_mega")
                        this.armorvalue = 100;
                else if (it.classname == "item_health_mega")
                        this.health = 200;
index b63b68e..3034961 100644 (file)
@@ -138,7 +138,8 @@ void checkSpectatorBlock(entity this);
 
 float game_completion_ratio; // 0 at start, 1 near end
 .float winning;
-.float jointime; // time of joining
+.float jointime; // time of connecting
+.float startplaytime; // time of switching from spectator to player
 .float alivetime; // time of being alive
 .float motd_actived_time; // used for both motd and campaign_message
 
index afe018c..c0ff6b3 100644 (file)
@@ -47,7 +47,7 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype)
                else
                {
                        // teamkill
-                       GameRules_scoring_add(attacker, KILLS, -1); // or maybe add a teamkills field?
+                       GameRules_scoring_add(attacker, TEAMKILLS, 1);
                }
        }
        else
index 713577f..d9372e0 100644 (file)
@@ -50,14 +50,7 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
                source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 
        if (lag)
-       {
-               // take players back into the past
-               FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag));
-               IL_EACH(g_monsters, it != forent,
-               {
-                       antilag_takeback(it, it, time - lag);
-               });
-       }
+               antilag_takeback_all(forent, lag);
 
        // do the trace
        if(wz)
@@ -67,13 +60,7 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma,
 
        // restore players to current positions
        if (lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it)));
-               IL_EACH(g_monsters, it != forent,
-               {
-                       antilag_restore(it, it);
-               });
-       }
+               antilag_restore_all(forent);
 
        // restore shooter solid type
        if(source)
index ce25dfa..2eda858 100644 (file)
@@ -2093,6 +2093,10 @@ void EndFrame()
        {
                antilag_record(it, it, altime);
        });
+       IL_EACH(g_projectiles, it.classname == "nade",
+       {
+               antilag_record(it, it, altime);
+       });
        systems_update();
        IL_ENDFRAME();
 }
index 29a8609..7d24883 100644 (file)
@@ -84,6 +84,11 @@ void Item_SetLoot(entity item, bool loot)
        item.m_isloot = loot;
 }
 
+bool Item_ShouldKeepPosition(entity item)
+{
+       return item.noalign || (item.spawnflags & 1);
+}
+
 bool Item_IsExpiring(entity item)
 {
        return item.m_isexpiring;
index af55eeb..1abcf64 100644 (file)
@@ -51,6 +51,13 @@ bool Item_IsLoot(entity item);
 /// \return No return.
 void Item_SetLoot(entity item, bool loot);
 
+/// \brief Returns whether item should keep its position or be dropped to the
+/// ground.
+/// \param[in] item Item to check.
+/// \return True if item should keep its position or false if it should be
+/// dropped to the ground.
+bool Item_ShouldKeepPosition(entity item);
+
 /// \brief Returns whether the item is expiring (i.e. its strength, shield and
 /// superweapon timers expire while it is on the ground).
 /// \param[in] item Item to check.
index 32fe3c6..c994866 100644 (file)
@@ -96,7 +96,7 @@ void TeamScore_Spawn(float t, string name)
        PlayerStats_GameReport_AddTeam(t);
 }
 
-float TeamScore_AddToTeam(float t, float scorefield, float score)
+float TeamScore_AddToTeam(int t, float scorefield, float score)
 {
        entity s;
 
index 79b6529..e2a57f4 100644 (file)
@@ -52,7 +52,7 @@ float TeamScore_Add(entity player, float scorefield, float score);
  * NEVER call this if team has not been set yet!
  * Returns the new score.
  */
-float TeamScore_AddToTeam(float t, float scorefield, float score);
+float TeamScore_AddToTeam(int t, float scorefield, float score);
 
 /**
  * Returns a value indicating the team score (and higher is better).
index d46d95b..8d87407 100644 (file)
@@ -44,7 +44,10 @@ void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled
        ScoreInfo_SetLabel_PlayerScore(SP_DEATHS,       "deaths",    SFL_LOWER_IS_BETTER);
 
        if (!INDEPENDENT_PLAYERS)
+       {
                ScoreInfo_SetLabel_PlayerScore(SP_SUICIDES,     "suicides",  SFL_LOWER_IS_BETTER);
+               ScoreInfo_SetLabel_PlayerScore(SP_TEAMKILLS,     "teamkills", SFL_LOWER_IS_BETTER);
+       }
 
        if(score_enabled)
                ScoreInfo_SetLabel_PlayerScore(SP_SCORE,        "score",     sprio);
index bf272a0..157adb5 100644 (file)
@@ -357,13 +357,7 @@ void fireBullet(entity this, .entity weaponentity, vector start, vector dir, flo
        if(autocvar_g_antilag == 0 || noantilag)
                lag = 0; // only do hitscan, but no antilag
        if(lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_takeback(it, CS(it), time - lag));
-               IL_EACH(g_monsters, it != this,
-               {
-                       antilag_takeback(it, it, time - lag);
-               });
-       }
+               antilag_takeback_all(this, lag);
 
        // change shooter to SOLID_BBOX so the shot can hit corpses
        int oldsolid = this.dphitcontentsmask;
@@ -480,13 +474,7 @@ void fireBullet(entity this, .entity weaponentity, vector start, vector dir, flo
        }
 
        if(lag)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_restore(it, CS(it)));
-               IL_EACH(g_monsters, it != this,
-               {
-                       antilag_restore(it, it);
-               });
-       }
+               antilag_restore_all(this);
 
        // restore shooter solid type
        if(this)