]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/player.qc
Cleanup common includes
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / player.qc
index ed1d938a294128758632d7e5be46caf7c5519fb5..ed824bd04c091168aa28802f9b8135531c9c4e60 100644 (file)
@@ -3,37 +3,44 @@
 #include <common/effects/all.qh>
 #include "bot/api.qh"
 #include "cheats.qh"
+#include "client.qh"
 #include "clientkill.qh"
-#include "g_damage.qh"
+#include "damage.qh"
+#include <server/mutators/_mod.qh>
+#include "world.qh"
 #include "handicap.qh"
-#include "miscfunctions.qh"
 #include "portals.qh"
 #include "teamplay.qh"
+#include <server/main.qh>
+#include "weapons/common.qh"
 #include "weapons/throwing.qh"
 #include "command/common.qh"
-#include "../common/state.qh"
-#include "../common/anim.qh"
-#include "../common/animdecide.qh"
-#include "../common/csqcmodel_settings.qh"
-#include "../common/gamemodes/sv_rules.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/mapobjects/subs.qh"
-#include "../common/playerstats.qh"
-#include "../lib/csqcmodel/sv_model.qh"
-
-#include "../common/minigames/sv_minigames.qh"
+#include "command/vote.qh"
+#include <common/state.qh>
+#include <common/anim.qh>
+#include <common/animdecide.qh>
+#include <common/csqcmodel_settings.qh>
+#include <common/gamemodes/sv_rules.qh>
+#include <common/deathtypes/all.qh>
+#include <common/mapobjects/subs.qh>
+#include <common/mapobjects/teleporters.qh>
+#include <common/playerstats.qh>
+#include <lib/csqcmodel/sv_model.qh>
+
+#include <common/minigames/sv_minigames.qh>
 
 #include <common/gamemodes/_mod.qh>
 
-#include "../common/physics/player.qh"
-#include "../common/effects/qc/_mod.qh"
-#include "../common/mutators/mutator/waypoints/waypointsprites.qh"
-#include "../common/mapobjects/_mod.qh"
-#include "../common/wepent.qh"
+#include <common/physics/player.qh>
+#include <common/effects/qc/_mod.qh>
+#include <common/mutators/mutator/waypoints/waypointsprites.qh>
+#include <common/mapobjects/_mod.qh>
+#include <common/wepent.qh>
 
 #include "weapons/weaponstats.qh"
+#include <server/weapons/weaponsystem.qh>
 
-#include "../common/animdecide.qh"
+#include <common/animdecide.qh>
 
 void Drop_Special_Items(entity player)
 {
@@ -165,7 +172,7 @@ void player_anim(entity this)
                animbits |= ANIMSTATE_FROZEN;
        if(this.move_movetype == MOVETYPE_FOLLOW)
                animbits |= ANIMSTATE_FOLLOW;
-       if(this.crouch)
+       if(IS_DUCKED(this))
                animbits |= ANIMSTATE_DUCK;
        animdecide_setstate(this, animbits, false);
        animdecide_setimplicitstate(this, IS_ONGROUND(this));
@@ -222,154 +229,68 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da
        }
 }
 
-void calculate_player_respawn_time(entity this)
+void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
 {
-       if(MUTATOR_CALLHOOK(CalculateRespawnTime, this))
-               return;
-
-       float gametype_setting_tmp;
-       float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
-       float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
-       float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large);
-       float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count);
-       float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count);
-       float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves);
+       vector v;
+       float dh = max(GetResource(this, RES_HEALTH), 0);
+       float da = max(GetResource(this, RES_ARMOR), 0);
+       float take = 0, save = 0;
 
-       float pcount = 1;  // Include myself whether or not team is already set right and I'm a "player".
-       if (teamplay)
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
-                       if(it.team == this.team)
-                               ++pcount;
-               });
-               if (sdelay_small_count == 0)
-                       sdelay_small_count = 1;
-               if (sdelay_large_count == 0)
-                       sdelay_large_count = 1;
-       }
-       else
+       if (damage)
        {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
-                       ++pcount;
-               });
-               if (sdelay_small_count == 0)
+               if(!DEATH_ISSPECIAL(deathtype))
                {
-                       if (IS_INDEPENDENT_PLAYER(this))
-                       {
-                               // Players play independently. No point in requiring enemies.
-                               sdelay_small_count = 1;
-                       }
-                       else
+                       damage *= Handicap_GetTotalHandicap(this);
+                       if (this != attacker && IS_PLAYER(attacker))
                        {
-                               // Players play AGAINST each other. Enemies required.
-                               sdelay_small_count = 2;
+                               damage /= Handicap_GetTotalHandicap(attacker);
                        }
                }
-               if (sdelay_large_count == 0)
+
+               if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
+                       damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage);
+
+               if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
                {
-                       if (IS_INDEPENDENT_PLAYER(this))
+                       vector ear1, ear2;
+                       vector d;
+                       float f;
+                       ear1 = this.origin;
+                       ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8
+                       ear2 = ear1;
+                       makevectors(this.angles);
+                       ear1 += v_right * -10;
+                       ear2 += v_right * +10;
+                       d = inflictor.origin - this.origin;
+                       if (d)
+                               f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right!
+                       else
+                               f = 0;  // Assum ecenter.
+                       force = v_right * vlen(force);
+                       Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker);
+                       Violence_GibSplash_At(ear2, force,      2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker);
+                       if(f > 0)
                        {
-                               // Players play independently. No point in requiring enemies.
-                               sdelay_large_count = 1;
+                               hitloc = ear1;
+                               force = force * -1;
                        }
                        else
                        {
-                               // Players play AGAINST each other. Enemies required.
-                               sdelay_large_count = 2;
+                               hitloc = ear2;
+                               // force is already good
                        }
                }
-       }
-
-       float sdelay;
-
-       if (pcount <= sdelay_small_count)
-               sdelay = sdelay_small;
-       else if (pcount >= sdelay_large_count)
-               sdelay = sdelay_large;
-       else  // NOTE: this case implies sdelay_large_count > sdelay_small_count.
-               sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count);
-
-       if(waves)
-               this.respawn_time = ceil((time + sdelay) / waves) * waves;
-       else
-               this.respawn_time = time + sdelay;
-
-       if(sdelay < sdelay_max)
-               this.respawn_time_max = time + sdelay_max;
-       else
-               this.respawn_time_max = this.respawn_time;
-
-       if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75))
-               this.respawn_countdown = 10; // first number to count down from is 10
-       else
-               this.respawn_countdown = -1; // do not count down
-
-       if(autocvar_g_forced_respawn)
-               this.respawn_flags = this.respawn_flags | RESPAWN_FORCE;
-}
-
-void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
-{
-       float take, save, dh, da;
-       vector v;
-       float excess;
-
-       dh = max(GetResource(this, RES_HEALTH), 0);
-       da = max(GetResource(this, RES_ARMOR), 0);
-
-       if(!DEATH_ISSPECIAL(deathtype))
-       {
-               damage *= Handicap_GetTotalHandicap(this);
-               if (this != attacker && IS_PLAYER(attacker))
-               {
-                       damage /= Handicap_GetTotalHandicap(attacker);
-               }
-       }
-
-       if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
-               damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage);
-
-       if(DEATH_ISWEAPON(deathtype, WEP_TUBA))
-       {
-               // tuba causes blood to come out of the ears
-               vector ear1, ear2;
-               vector d;
-               float f;
-               ear1 = this.origin;
-               ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8
-               ear2 = ear1;
-               makevectors(this.angles);
-               ear1 += v_right * -10;
-               ear2 += v_right * +10;
-               d = inflictor.origin - this.origin;
-               if (d)
-                       f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right!
                else
-                       f = 0;  // Assum ecenter.
-               force = v_right * vlen(force);
-               Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker);
-               Violence_GibSplash_At(ear2, force,      2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker);
-               if(f > 0)
-               {
-                       hitloc = ear1;
-                       force = force * -1;
-               }
-               else
-               {
-                       hitloc = ear2;
-                       // force is already good
-               }
-       }
-       else
-               Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
+                       Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
 
-       v = healtharmor_applydamage(GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
-       take = v.x;
-       save = v.y;
+               v = healtharmor_applydamage(GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
+               take = v.x;
+               save = v.y;
+       }
 
        if(attacker == this)
        {
-               // don't reset pushltime for this damage as it may be an attempt to
+               // don't reset pushltime for self damage as it may be an attempt to
                // escape a lava pit or similar
                //this.pushltime = 0;
                this.istypefrag = 0;
@@ -394,11 +315,11 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage);
        take = bound(0, M_ARGV(4, float), GetResource(this, RES_HEALTH));
        save = bound(0, M_ARGV(5, float), GetResource(this, RES_ARMOR));
-       excess = max(0, damage - take - save);
+       float excess = max(0, damage - take - save);
 
        if(sound_allowed(MSG_BROADCAST, attacker))
        {
-               if (save > 10)
+               if (save > 10 && (dh - take) > 0) // don't play armor sound if the attack is fatal
                        sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM);
                else if (take > 30)
                        sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM);
@@ -466,8 +387,10 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                                this.v_angle_x = bound(-90, this.v_angle.x, 90);
                        }
 
-                       if (this != attacker) {
-                               float realdmg = damage - excess;
+                       float realdmg = damage - excess;
+                       if (this != attacker && realdmg)
+                       if (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime)
+                       {
                                if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this)) {
                                        GameRules_scoring_add(attacker, DMG, realdmg);
                                }
@@ -489,6 +412,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        bool valid_damage_for_weaponstats = false;
        Weapon awep = WEP_Null;
 
+       if (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime)
        if(vbot || IS_REAL_CLIENT(this))
        if(abot || IS_REAL_CLIENT(attacker))
        if(attacker && this != attacker)
@@ -551,7 +475,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
                // print an obituary message
                if(this.classname != "body")
-                       Obituary (attacker, inflictor, this, deathtype, weaponentity);
+                       Obituary(attacker, inflictor, this, deathtype, weaponentity);
 
                // increment frag counter for used weapon type
                Weapon w = DEATH_WEAPONOF(deathtype);
@@ -612,8 +536,17 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                this.avelocity = '0 0 0';
                // view from the floor
                this.view_ofs = '0 0 -8';
-               // toss the corpse
-               set_movetype(this, MOVETYPE_TOSS);
+               if(this.move_movetype == MOVETYPE_NOCLIP)
+               {
+                       // don't toss the corpse in this case, it can get stuck in solid (causing low fps)
+                       // or fall indefinitely into the void if out of the map
+                       this.velocity = '0 0 0';
+               }
+               else
+               {
+                       // toss the corpse
+                       set_movetype(this, MOVETYPE_TOSS);
+               }
                // shootable corpse
                this.solid = SOLID_CORPSE;
                PS(this).ballistics_density = autocvar_g_ballistics_density_corpse;
@@ -676,3 +609,78 @@ bool PlayerHeal(entity targ, entity inflictor, float amount, float limit)
        GiveResourceWithLimit(targ, RES_HEALTH, amount, limit);
        return true;
 }
+
+void precache_playermodel(string m)
+{
+       int globhandle, i, n;
+       string f;
+
+       // remove :<skinnumber> suffix
+       int j = strstrofs(m, ":", 0);
+       if(j >= 0)
+               m = substring(m, 0, j);
+
+       if(substring(m, -9, 5) == "_lod1")
+               return;
+       if(substring(m, -9, 5) == "_lod2")
+               return;
+       precache_model(m);
+       f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
+       if(fexists(f))
+               precache_model(f);
+       f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
+       if(fexists(f))
+               precache_model(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);
+               PrecachePlayerSounds(f);
+       }
+       search_end(globhandle);
+}
+void precache_all_playermodels(string pattern)
+{
+       int globhandle = search_begin(pattern, true, false);
+       if (globhandle < 0) return;
+       int n = search_getsize(globhandle);
+       for (int i = 0; i < n; ++i)
+       {
+               string s = search_getfilename(globhandle, i);
+               precache_playermodel(s);
+       }
+       search_end(globhandle);
+}
+
+void precache_playermodels(string s)
+{
+       FOREACH_WORD(s, true, { precache_playermodel(it); });
+}
+
+PRECACHE(PlayerModels)
+{
+    // Precache all player models if desired
+    if (autocvar_sv_precacheplayermodels)
+    {
+        PrecachePlayerSounds("sound/player/default.sounds");
+        precache_all_playermodels("models/player/*.zym");
+        precache_all_playermodels("models/player/*.dpm");
+        precache_all_playermodels("models/player/*.md3");
+        precache_all_playermodels("models/player/*.psk");
+        precache_all_playermodels("models/player/*.iqm");
+    }
+
+    if (autocvar_sv_defaultcharacter)
+    {
+               precache_playermodels(autocvar_sv_defaultplayermodel_red);
+               precache_playermodels(autocvar_sv_defaultplayermodel_blue);
+               precache_playermodels(autocvar_sv_defaultplayermodel_yellow);
+               precache_playermodels(autocvar_sv_defaultplayermodel_pink);
+               precache_playermodels(autocvar_sv_defaultplayermodel);
+    }
+}