]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/player.qc
Merged master
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / player.qc
index 904447c218418c936f5f933cce7cf13062813c61..cd6c23edb2f764ec325af110c56c55a598ac2294 100644 (file)
@@ -24,6 +24,7 @@
 #include "../common/effects/qc/all.qh"
 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
 #include "../common/triggers/include.qh"
+#include "../common/wepent.qh"
 
 #include "weapons/weaponstats.qh"
 
@@ -96,7 +97,6 @@ void CopyBody(entity this, float keepvelocity)
        clone.move_qcphysics = false; // don't run gamecode logic on clones, too many
        set_movetype(clone, this.move_movetype);
        clone.solid = this.solid;
-       clone.ballistics_density = this.ballistics_density;
        clone.takedamage = this.takedamage;
        setcefc(clone, getcefc(this));
        clone.uncustomizeentityforclient = this.uncustomizeentityforclient;
@@ -110,7 +110,6 @@ void CopyBody(entity this, float keepvelocity)
        //clone.weapon = this.weapon;
        setorigin(clone, this.origin);
        setsize(clone, this.mins, this.maxs);
-       clone.prevorigin = this.origin;
        clone.reset = SUB_Remove;
        clone._ps = this._ps;
 
@@ -252,7 +251,7 @@ void calculate_player_respawn_time(entity this)
                ));
                if (sdelay_small_count == 0)
                {
-                       if (g_cts)
+                       if (IS_INDEPENDENT_PLAYER(this))
                        {
                                // Players play independently. No point in requiring enemies.
                                sdelay_small_count = 1;
@@ -265,7 +264,7 @@ void calculate_player_respawn_time(entity this)
                }
                if (sdelay_large_count == 0)
                {
-                       if (g_cts)
+                       if (IS_INDEPENDENT_PLAYER(this))
                        {
                                // Players play independently. No point in requiring enemies.
                                sdelay_large_count = 1;
@@ -310,7 +309,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 {
        float take, save, dh, da;
        vector v;
-       float valid_damage_for_weaponstats;
        float excess;
 
        dh = max(this.health, 0);
@@ -318,11 +316,14 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
        if(!DEATH_ISSPECIAL(deathtype))
        {
-               damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0));
+               damage *= bound(1.0, this.cvar_cl_handicap, 10.0);
                if(this != attacker)
-                       damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0));
+                       damage /= bound(1.0, attacker.cvar_cl_handicap, 10.0);
        }
 
+       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
@@ -357,7 +358,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        else
                Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
 
-
        v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage);
        take = v.x;
        save = v.y;
@@ -386,13 +386,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                this.istypefrag = 0;
        }
 
-       if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
-       {
-               vector v = healtharmor_applydamage(this.armorvalue, max(0, autocvar_g_spawnshield_blockdamage), deathtype, damage);
-               take = v.x;
-               save = v.y;
-       }
-
        MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage);
        take = bound(0, M_ARGV(4, float), this.health);
        save = bound(0, M_ARGV(5, float), this.armorvalue);
@@ -440,7 +433,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                                        }
 
                                        if(sound_allowed(MSG_BROADCAST, attacker))
-                                       if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser
+                                       if(this.health < 25 || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || take > 20 || attacker != this)
                                        if(this.health > 1)
                                        // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two
                                        {
@@ -488,8 +481,9 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        bool abot = (IS_BOT_CLIENT(attacker));
        bool vbot = (IS_BOT_CLIENT(this));
 
-       valid_damage_for_weaponstats = 0;
+       bool valid_damage_for_weaponstats = false;
        Weapon awep = WEP_Null;
+       .entity weaponentity = weaponentities[0]; // TODO: unhardcode
 
        if(vbot || IS_REAL_CLIENT(this))
        if(abot || IS_REAL_CLIENT(attacker))
@@ -497,23 +491,21 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        if(DIFF_TEAM(this, attacker))
        {
                if(DEATH_ISSPECIAL(deathtype))
-                       awep = PS(attacker).m_weapon;
+                       awep = attacker.(weaponentity).m_weapon;
                else
                        awep = DEATH_WEAPONOF(deathtype);
-               valid_damage_for_weaponstats = 1;
+               valid_damage_for_weaponstats = true;
        }
 
        dh = dh - max(this.health, 0);
        da = da - max(this.armorvalue, 0);
        if(valid_damage_for_weaponstats)
        {
-               WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da);
-       }
-       if (damage)
-       {
-               MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage);
+               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);
+
        if (this.health < 1)
        {
                float defer_ClientKill_Now_TeamChange;
@@ -526,7 +518,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                }
 
                if(valid_damage_for_weaponstats)
-                       WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot);
+                       WeaponStats_LogKill(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot);
 
                if(autocvar_sv_gentle < 1)
                if(sound_allowed(MSG_BROADCAST, attacker))
@@ -542,7 +534,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                {
                        delete(this.killindicator);
                        this.killindicator = NULL;
-                       if(this.killindicator_teamchange)
+                       if(CS(this).killindicator_teamchange)
                                defer_ClientKill_Now_TeamChange = true;
 
                        if(this.classname == "body")
@@ -560,21 +552,23 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
         // increment frag counter for used weapon type
         Weapon w = DEATH_WEAPONOF(deathtype);
                if(w != WEP_Null && accuracy_isgooddamage(attacker, this))
-                       attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1;
+                       CS(attacker).accuracy.(accuracy_frags[w.m_id-1]) += 1;
 
                MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
-               excess = M_ARGV(4, float);
+               damage = M_ARGV(4, float);
+               excess = max(0, damage - take - save);
 
-               Weapon wep = PS(this).m_weapon;
-               /*for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               //Weapon wep = this.(weaponentity).m_weapon;
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
-                       .entity weaponentity = weaponentities[slot];
-                       wep.wr_playerdeath(wep, this, weaponentity);
-               }*/
-               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-               wep.wr_playerdeath(wep, this, weaponentity);
+                       .entity went = weaponentities[slot];
+                       if(!this.(weaponentity))
+                               continue; // TODO: clones have no weapon, but we don't want to have to check this all the time
+                       Weapon wep = this.(weaponentity).m_weapon;
+                       wep.wr_playerdeath(wep, this, went);
+               }
 
-               RemoveGrapplingHook(this);
+               RemoveGrapplingHooks(this);
 
                Portal_ClearAllLater(this);
 
@@ -596,7 +590,11 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                // clear waypoints
                WaypointSprite_PlayerDead(this);
                // throw a weapon
-               SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id);
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity went = weaponentities[slot];
+                       SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, this.(went).m_weapon, went);
+               }
 
                // become fully visible
                this.alpha = default_player_alpha;
@@ -611,12 +609,14 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                set_movetype(this, MOVETYPE_TOSS);
                // shootable corpse
                this.solid = SOLID_CORPSE;
-               this.ballistics_density = autocvar_g_ballistics_density_corpse;
+               PS(this).ballistics_density = autocvar_g_ballistics_density_corpse;
                // don't stick to the floor
                UNSET_ONGROUND(this);
                // dying animation
                this.deadflag = DEAD_DYING;
 
+               STAT(MOVEVARS_SPECIALCOMMAND, this) = false; // sweet release
+
                // when to allow respawn
                calculate_player_respawn_time(this);
 
@@ -652,25 +652,34 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                }
 
                // reset fields the weapons may use just in case
-               FOREACH(Weapons, it != WEP_Null, LAMBDA(
-                       it.wr_resetplayer(it, this);
-                       for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               if(this.classname != "body")
+               {
+                       FOREACH(Weapons, it != WEP_Null,
                        {
-                               ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0;
-                       }
-               ));
+                               it.wr_resetplayer(it, this);
+                               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+                               {
+                                       ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0;
+                               }
+                       });
+               }
+               MUTATOR_CALLHOOK(PlayerDied, this);
        }
 }
 
-void MoveToTeam(entity client, int team_colour, int type)
+bool MoveToTeam(entity client, int team_colour, int type)
 {
        int lockteams_backup = lockteams;  // backup any team lock
        lockteams = 0;  // disable locked teams
        TeamchangeFrags(client);  // move the players frags
-       SetPlayerColors(client, team_colour - 1);  // set the players colour
+       if (!SetPlayerTeamSimple(client, team_colour))
+       {
+               return false;
+       }
        Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0');  // kill the player
        lockteams = lockteams_backup;  // restore the team lock
        LogTeamchange(client.playerid, client.team, type);
+       return true;
 }
 
 /** print(), but only print if the server is not local */
@@ -679,6 +688,54 @@ void dedicated_print(string input)
        if (server_is_dedicated) print(input);
 }
 
+void PrintToChat(entity player, string text)
+{
+       text = strcat("\{1}^7", text, "\n");
+       sprint(player, text);
+}
+
+void DebugPrintToChat(entity player, string text)
+{
+       if (autocvar_developer)
+       {
+               PrintToChat(player, text);
+       }
+}
+
+void PrintToChatAll(string text)
+{
+       text = strcat("\{1}^7", text, "\n");
+       bprint(text);
+}
+
+void DebugPrintToChatAll(string text)
+{
+       if (autocvar_developer)
+       {
+               PrintToChatAll(text);
+       }
+}
+
+void PrintToChatTeam(int teamnum, string text)
+{
+       text = strcat("\{1}^7", text, "\n");
+       FOREACH_CLIENT(IS_REAL_CLIENT(it),
+       {
+               if (it.team == teamnum)
+               {
+                       sprint(it, text);
+               }
+       });
+}
+
+void DebugPrintToChatTeam(int teamnum, string text)
+{
+       if (autocvar_developer)
+       {
+               PrintToChatTeam(teamnum, text);
+       }
+}
+
 /**
  * message "": do not say, just test flood control
  * return value:
@@ -691,7 +748,8 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
        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);
+    if(source)
+               msgin = formatmessage(source, msgin);
 
     string colorstr;
        if (!IS_PLAYER(source))
@@ -704,7 +762,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
                teamsay = false;
        }
 
-       if(intermission_running)
+       if(game_stopped)
                teamsay = false;
 
     if (!source) {
@@ -728,7 +786,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
 
     string namestr = "";
     if (source)
-        namestr = autocvar_g_chat_teamcolors ? playername(source) : source.netname;
+        namestr = playername(source, autocvar_g_chat_teamcolors);
 
     string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7";
 
@@ -744,10 +802,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
                        privatemsgprefixlen = strlen(msgstr);
                        msgstr = strcat(msgstr, msgin);
                        cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin);
-                       if(autocvar_g_chat_teamcolors)
-                               privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7");
-                       else
-                               privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^7");
+                       privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7");
                }
                else if(teamsay)
                {
@@ -787,7 +842,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
        // FLOOD CONTROL
        int flood = 0;
        var .float flood_field = floodcontrol_chat;
-       if(floodcontrol)
+       if(floodcontrol && source)
        {
                float flood_spl;
                float flood_burst;
@@ -881,20 +936,20 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
 
        if (!privatesay && source && !IS_PLAYER(source))
        {
-               if (!intermission_running)
-                       if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover)))
-                               teamsay = -1; // spectators
+               if (!game_stopped)
+               if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))
+                       teamsay = -1; // spectators
        }
 
        if(flood)
-               LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n");
+               LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding.\n");
 
        // build sourcemsgstr by cutting off a prefix and replacing it by the other one
        if(privatesay)
                sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1));
 
     int ret;
-       if(source.muted)
+       if(source && CS(source).muted)
        {
                // always fake the message
                ret = -1;
@@ -914,6 +969,16 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
                ret = 1;
        }
 
+       if (privatesay && source && !IS_PLAYER(source))
+       {
+               if (!game_stopped)
+               if ((privatesay && !IS_PLAYER(privatesay)) || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))
+                       ret = -1; // just hide the message completely
+       }
+
+       MUTATOR_CALLHOOK(ChatMessage, source, ret);
+       ret = M_ARGV(1, int);
+
        if(sourcemsgstr != "" && ret != 0)
        {
                if(ret < 0) // faked message, because the player is muted
@@ -925,16 +990,19 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
                else if(privatesay) // private message, between 2 people only
                {
                        sprint(source, sourcemsgstr);
-                       sprint(privatesay, msgstr);
                        if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
-                       if(cmsgstr != "")
-                               centerprint(privatesay, cmsgstr);
+                       if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source))
+                       {
+                               sprint(privatesay, msgstr);
+                               if(cmsgstr != "")
+                                       centerprint(privatesay, cmsgstr);
+                       }
                }
-               else if ( teamsay && source.active_minigame )
+               else if ( teamsay && CS(source).active_minigame )
                {
                        sprint(source, sourcemsgstr);
                        dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr));
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
                }
                else if(teamsay > 0) // team message, only sent to team mates
                {
@@ -942,7 +1010,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
                        dedicated_print(msgstr); // send to server console too
                        if(sourcecmsgstr != "")
                                centerprint(source, sourcecmsgstr);
-                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, {
+                       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
                                sprint(it, msgstr);
                                if(cmsgstr != "")
                                        centerprint(it, cmsgstr);
@@ -952,16 +1020,16 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
                {
                        sprint(source, sourcemsgstr);
                        dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
+                       FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
                }
                else
                {
             if (source) {
                 sprint(source, sourcemsgstr);
                 dedicated_print(msgstr); // send to server console too
-                MX_Say(strcat(playername(source), "^7: ", msgin));
+                MX_Say(strcat(playername(source, true), "^7: ", msgin));
             }
-            FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr));
+            FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr));
         }
        }