]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/player.qc
Merge branch 'master' into terencehill/scoreboard_ui
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / player.qc
index 1333b2386d81e59d4673858b5f1cac89a81bbc8c..ad3ba94e4a21d18c9aa7c293a1c68cfa9a04edeb 100644 (file)
@@ -1,39 +1,43 @@
 #include "player.qh"
 
+#include <common/anim.qh>
+#include <common/animdecide.qh>
+#include <common/csqcmodel_settings.qh>
+#include <common/deathtypes/all.qh>
 #include <common/effects/all.qh>
-#include "bot/api.qh"
-#include "cheats.qh"
-#include "clientkill.qh"
-#include "g_damage.qh"
-#include "handicap.qh"
-#include "miscfunctions.qh"
-#include "portals.qh"
-#include "teamplay.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 <common/effects/qc/_mod.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 "weapons/weaponstats.qh"
-
-#include "../common/animdecide.qh"
+#include <common/gamemodes/sv_rules.qh>
+#include <common/mapobjects/_mod.qh>
+#include <common/mapobjects/subs.qh>
+#include <common/mapobjects/teleporters.qh>
+#include <common/minigames/sv_minigames.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
+#include <common/mutators/mutator/waypoints/waypointsprites.qh>
+#include <common/physics/player.qh>
+#include <common/playerstats.qh>
+#include <common/state.qh>
+#include <common/wepent.qh>
+#include <lib/csqcmodel/sv_model.qh>
+#include <server/bot/api.qh>
+#include <server/cheats.qh>
+#include <server/client.qh>
+#include <server/clientkill.qh>
+#include <server/command/common.qh>
+#include <server/command/vote.qh>
+#include <server/damage.qh>
+#include <server/handicap.qh>
+#include <server/hook.qh>
+#include <server/main.qh>
+#include <server/mutators/_mod.qh>
+#include <server/portals.qh>
+#include <server/teamplay.qh>
+#include <server/weapons/accuracy.qh>
+#include <server/weapons/common.qh>
+#include <server/weapons/throwing.qh>
+#include <server/weapons/weaponstats.qh>
+#include <server/weapons/weaponsystem.qh>
+#include <server/world.qh>
 
 void Drop_Special_Items(entity player)
 {
@@ -165,7 +169,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,97 +226,11 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da
        }
 }
 
-void calculate_player_respawn_time(entity this)
-{
-       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);
-
-       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
-       {
-               FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
-                       ++pcount;
-               });
-               if (sdelay_small_count == 0)
-               {
-                       if (IS_INDEPENDENT_PLAYER(this))
-                       {
-                               // Players play independently. No point in requiring enemies.
-                               sdelay_small_count = 1;
-                       }
-                       else
-                       {
-                               // Players play AGAINST each other. Enemies required.
-                               sdelay_small_count = 2;
-                       }
-               }
-               if (sdelay_large_count == 0)
-               {
-                       if (IS_INDEPENDENT_PLAYER(this))
-                       {
-                               // Players play independently. No point in requiring enemies.
-                               sdelay_large_count = 1;
-                       }
-                       else
-                       {
-                               // Players play AGAINST each other. Enemies required.
-                               sdelay_large_count = 2;
-                       }
-               }
-       }
-
-       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)
 {
        vector v;
-       float dh = max(GetResource(this, RES_HEALTH), 0);
-       float da = max(GetResource(this, RES_ARMOR), 0);
+       float initial_health = max(GetResource(this, RES_HEALTH), 0);
+       float initial_armor = max(GetResource(this, RES_ARMOR), 0);
        float take = 0, save = 0;
 
        if (damage)
@@ -326,8 +244,13 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                        }
                }
 
-               if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
-                       damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage);
+               if (STAT(FROZEN, this))
+               {
+                       if (!ITEM_DAMAGE_NEEDKILL(deathtype))
+                               damage = 0;
+               }
+               else if (StatusEffects_active(STATUSEFFECT_SpawnShield, this) && autocvar_g_spawnshield_blockdamage < 1)
+                       damage *= 1 - bound(0, autocvar_g_spawnshield_blockdamage, 1);
 
                if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
                {
@@ -398,7 +321,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
        if(sound_allowed(MSG_BROADCAST, attacker))
        {
-               if (save > 10 && (dh - take) > 0) // don't play armor sound if the attack is fatal
+               if (save > 10 && (initial_health - 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);
@@ -411,7 +334,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        if (take > 100)
                Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
 
-       if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1)
+       if (!StatusEffects_active(STATUSEFFECT_SpawnShield, this) || autocvar_g_spawnshield_blockdamage < 1)
        {
                if (!(this.flags & FL_GODMODE))
                {
@@ -421,7 +344,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                        if(take)
                                this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
 
-                       if (time > this.pain_finished)          //Don't switch pain sequences like crazy
+                       if (time > this.pain_finished && !STAT(FROZEN, this)) // Don't switch pain sequences like crazy
                        {
                                this.pain_finished = time + 0.5;        //Supajoe
 
@@ -465,18 +388,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                                this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake;
                                this.v_angle_x = bound(-90, this.v_angle.x, 90);
                        }
-
-                       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);
-                               }
-                               if (IS_PLAYER(this)) {
-                                       GameRules_scoring_add(this, DMGTAKEN, realdmg);
-                               }
-                       }
                }
                else
                        this.max_armorvalue += (save + take);
@@ -495,7 +406,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        if(vbot || IS_REAL_CLIENT(this))
        if(abot || IS_REAL_CLIENT(attacker))
        if(attacker && this != attacker)
-       if(DIFF_TEAM(this, attacker))
+       if (DIFF_TEAM(this, attacker) && (!STAT(FROZEN, this) || this.freeze_time > time))
        {
                if(DEATH_ISSPECIAL(deathtype))
                        awep = attacker.(weaponentity).m_weapon;
@@ -504,19 +415,31 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                valid_damage_for_weaponstats = true;
        }
 
-       dh = dh - max(GetResource(this, RES_HEALTH), 0);
-       da = da - max(GetResource(this, RES_ARMOR), 0);
+       float dh = initial_health - max(GetResource(this, RES_HEALTH), 0); // health difference
+       float da = initial_armor - max(GetResource(this, RES_ARMOR), 0); // armor difference
        if(valid_damage_for_weaponstats)
        {
                WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da);
        }
 
-       MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage);
+       bool forbid_logging_damage = MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage);
+
+       if ((dh || da) && !forbid_logging_damage)
+       {
+               float realdmg = damage - excess;
+               if ((this != attacker || deathtype == DEATH_KILL.m_id) && realdmg && !STAT(FROZEN, this)
+                       && (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime))
+               {
+                       if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this) && deathtype != DEATH_KILL.m_id)
+                               GameRules_scoring_add(attacker, DMG, realdmg);
+                       if (IS_PLAYER(this))
+                               GameRules_scoring_add(this, DMGTAKEN, realdmg);
+               }
+       }
 
        if (GetResource(this, RES_HEALTH) < 1)
        {
-               float defer_ClientKill_Now_TeamChange;
-               defer_ClientKill_Now_TeamChange = false;
+               bool defer_ClientKill_Now_TeamChange = false;
 
                if(this.alivetime)
                {
@@ -633,8 +556,10 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                UNSET_ONGROUND(this);
                // dying animation
                this.deadflag = DEAD_DYING;
+               // don't play teleportation sounds
+               this.teleportable = TELEPORT_SIMPLE;
 
-               STAT(MOVEVARS_SPECIALCOMMAND, this) = false; // sweet release
+               STAT(AIR_FINISHED, this) = 0;
 
                this.death_time = time;
                if (random() < 0.5)
@@ -688,3 +613,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);
+    }
+}