]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/cl_player.qc
Merge branch 'master' into mirceakitsune/damage_effects
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cl_player.qc
index 6f82d9f1043c7b30d21d98a6a2c52830944737ca..5337219f5573223b46dc7ecc6bc0cea1c78eee37 100644 (file)
@@ -13,23 +13,26 @@ void WeaponStats_Init()
 
 #define WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot) (((vwep) + (awep) * (WEP_LAST - WEP_FIRST + 1) - (WEP_FIRST + WEP_FIRST * (WEP_LAST - WEP_FIRST + 1))) * 4 + (abot) * 2 + (vbot))
 
-void WeaponStats_Shutdown()
+void WeaponStats_ready(entity fh, entity pass, float status)
 {
-       float i, j, ibot, jbot, idx;
-       float fh;
+       float i, j, n, ibot, jbot, idx;
        vector v;
-       string prefix;
-       if(weaponstats_buffer < 0)
-               return;
-       prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t");
-       if(autocvar_sv_weaponstats_file != "")
+       string prefix, s;
+       switch(status)
        {
-               fh = fopen(autocvar_sv_weaponstats_file, FILE_APPEND);
-               if(fh >= 0)
-               {
-                       fputs(fh, "#begin statsfile\n");
-                       fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
-                       fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_changes)), "\n"));
+               case URL_READY_CANWRITE:
+                       // we can write
+                       prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t");
+                       url_fputs(fh, "#begin statsfile\n");
+                       url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
+#ifdef WATERMARK
+                       url_fputs(fh, strcat("#version ", WATERMARK(), "\n"));
+#endif
+                       url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n"));
+                       url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n"));
+                       n = tokenizebyseparator(cvar_purechanges, "\n");
+                       for(i = 0; i < n; ++i)
+                               url_fputs(fh, strcat("#cvar_purechange ", argv(i), "\n"));
                        for(i = WEP_FIRST; i <= WEP_LAST; ++i) for(ibot = 0; ibot <= 1; ++ibot)
                                for(j = WEP_FIRST; j <= WEP_LAST; ++j) for(jbot = 0; jbot <= 1; ++jbot)
                                {
@@ -38,17 +41,50 @@ void WeaponStats_Shutdown()
                                        if(v != '0 0 0')
                                        {
                                                //vector is: kills hits damage
-                                               fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot));
-                                               fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z));
+                                               url_fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot));
+                                               url_fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z));
                                        }
                                }
-                       fputs(fh, "#end\n\n");
-                       fclose(fh);
+                       url_fputs(fh, "#end\n\n");
+                       url_fclose(fh);
+                       break;
+               case URL_READY_CANREAD:
+                       // url_fclose is processing, we got a response for writing the data
+                       // this must come from HTTP
+                       print("Got response from weapon stats server:\n");
+                       while((s = url_fgets(fh)))
+                               print("  ", s, "\n");
+                       print("End of response.\n");
+                       url_fclose(fh);
+                       break;
+               case URL_READY_CLOSED:
+                       // url_fclose has finished
                        print("Weapon stats written\n");
-               }
+                       buf_del(weaponstats_buffer);
+                       weaponstats_buffer = -1;
+                       break;
+               case URL_READY_ERROR:
+               default:
+                       print("Weapon stats writing failed: ", ftos(status), "\n");
+                       buf_del(weaponstats_buffer);
+                       weaponstats_buffer = -1;
+                       break;
+       }
+}
+
+void WeaponStats_Shutdown()
+{
+       if(weaponstats_buffer < 0)
+               return;
+       if(autocvar_sv_weaponstats_file != "")
+       {
+               url_multi_fopen(autocvar_sv_weaponstats_file, FILE_APPEND, WeaponStats_ready, world);
+       }
+       else
+       {
+               buf_del(weaponstats_buffer);
+               weaponstats_buffer = -1;
        }
-       buf_del(weaponstats_buffer);
-       weaponstats_buffer = -1;
 }
 
 void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item)
@@ -90,7 +126,7 @@ void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot)
 
 void CopyBody(float keepvelocity)
 {
-       local entity oldself;
+       entity oldself;
        if (self.effects & EF_NODRAW)
                return;
        oldself = self;
@@ -100,6 +136,7 @@ void CopyBody(float keepvelocity)
        self.colormap = oldself.colormap;
        self.glowmod = oldself.glowmod;
        self.iscreature = oldself.iscreature;
+       self.damagedbycontents = oldself.damagedbycontents;
        self.angles = oldself.angles;
        self.avelocity = oldself.avelocity;
        self.classname = "body";
@@ -149,6 +186,7 @@ void CopyBody(float keepvelocity)
 
        Drag_MoveDrag(oldself, self);
 
+       self.owner = oldself;
        self = oldself;
 }
 
@@ -165,74 +203,35 @@ float player_getspecies()
 
 void player_setupanimsformodel()
 {
-       local string animfilename;
-       local float animfile;
        // defaults for legacy .zym models without animinfo files
-       self.anim_die1 = '0 1 0.5'; // 2 seconds
-       self.anim_die2 = '1 1 0.5'; // 2 seconds
-       self.anim_draw = '2 1 3'; // TODO: analyze models and set framerate
-       self.anim_duck = '3 1 100'; // this anim seems bogus in most models, so make it play VERY briefly!
-       self.anim_duckwalk = '4 1 1';
-       self.anim_duckjump = '5 1 100'; // zym anims keep playing until changed, so this only has to start the anim, landing will end it
-       self.anim_duckidle = '6 1 1';
-       self.anim_idle = '7 1 1';
-       self.anim_jump = '8 1 100'; // zym anims keep playing until changed, so this only has to start the anim, landing will end it
-       self.anim_pain1 = '9 1 2'; // 0.5 seconds
-       self.anim_pain2 = '10 1 2'; // 0.5 seconds
-       self.anim_shoot = '11 1 5'; // TODO: analyze models and set framerate
-       self.anim_taunt = '12 1 0.33'; // FIXME?  there is no code using this anim
-       self.anim_run = '13 1 1';
-       self.anim_runbackwards = '14 1 1';
-       self.anim_strafeleft = '15 1 1';
-       self.anim_straferight = '16 1 1';
-       self.anim_dead1 = '17 1 1';
-       self.anim_dead2 = '18 1 1';
-       self.anim_forwardright = '19 1 1';
-       self.anim_forwardleft = '20 1 1';
-       self.anim_backright = '21 1 1';
-       self.anim_backleft  = '22 1 1';
-       self.anim_melee = '23 1 1';
-       animparseerror = FALSE;
-       animfilename = strcat(self.model, ".animinfo");
-       animfile = fopen(animfilename, FILE_READ);
-       if (animfile >= 0)
-       {
-               self.anim_die1         = animparseline(animfile);
-               self.anim_die2         = animparseline(animfile);
-               self.anim_draw         = animparseline(animfile);
-               self.anim_duck         = animparseline(animfile);
-               self.anim_duckwalk     = animparseline(animfile);
-               self.anim_duckjump     = animparseline(animfile);
-               self.anim_duckidle     = animparseline(animfile);
-               self.anim_idle         = animparseline(animfile);
-               self.anim_jump         = animparseline(animfile);
-               self.anim_pain1        = animparseline(animfile);
-               self.anim_pain2        = animparseline(animfile);
-               self.anim_shoot        = animparseline(animfile);
-               self.anim_taunt        = animparseline(animfile);
-               self.anim_run          = animparseline(animfile);
-               self.anim_runbackwards = animparseline(animfile);
-               self.anim_strafeleft   = animparseline(animfile);
-               self.anim_straferight  = animparseline(animfile);
-               self.anim_forwardright = animparseline(animfile);
-               self.anim_forwardleft  = animparseline(animfile);
-               self.anim_backright    = animparseline(animfile);
-               self.anim_backleft     = animparseline(animfile);
-               self.anim_melee        = animparseline(animfile);
-               fclose(animfile);
-
-               // derived anims
-               self.anim_dead1 = '0 1 1' + '1 0 0' * (self.anim_die1_x + self.anim_die1_y - 1);
-               self.anim_dead2 = '0 1 1' + '1 0 0' * (self.anim_die2_x + self.anim_die2_y - 1);
-
-               if (animparseerror)
-                       print("Parse error in ", animfilename, ", some player animations are broken\n");
-       }
-       else
-               dprint("File ", animfilename, " not found, assuming legacy .zym model animation timings\n");
+       self.anim_die1 = animfixfps(self, '0 1 0.5'); // 2 seconds
+       self.anim_die2 = animfixfps(self, '1 1 0.5'); // 2 seconds
+       self.anim_draw = animfixfps(self, '2 1 3');
+       // self.anim_duck = '3 1 100'; // This anim is broken, use slot 3 as a new free slot in the future ;)
+       self.anim_duckwalk = animfixfps(self, '4 1 1');
+       self.anim_duckjump = '5 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
+       self.anim_duckidle = animfixfps(self, '6 1 1');
+       self.anim_idle = animfixfps(self, '7 1 1');
+       self.anim_jump = '8 1 100'; // NOTE: zym anims keep playing until changed, so this only has to start the anim, landing will end it
+       self.anim_pain1 = animfixfps(self, '9 1 2'); // 0.5 seconds
+       self.anim_pain2 = animfixfps(self, '10 1 2'); // 0.5 seconds
+       self.anim_shoot = animfixfps(self, '11 1 5'); // analyze models and set framerate
+       self.anim_taunt = animfixfps(self, '12 1 0.33');
+       self.anim_run = animfixfps(self, '13 1 1');
+       self.anim_runbackwards = animfixfps(self, '14 1 1');
+       self.anim_strafeleft = animfixfps(self, '15 1 1');
+       self.anim_straferight = animfixfps(self, '16 1 1');
+       self.anim_dead1 = animfixfps(self, '17 1 1');
+       self.anim_dead2 = animfixfps(self, '18 1 1');
+       self.anim_forwardright = animfixfps(self, '19 1 1');
+       self.anim_forwardleft = animfixfps(self, '20 1 1');
+       self.anim_backright = animfixfps(self, '21 1 1');
+       self.anim_backleft  = animfixfps(self, '22 1 1');
+       self.anim_melee = animfixfps(self, '23 1 1');
+       // TODO introspect models for finding right "fps" value (1/duration)
        // reset animstate now
        setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
-};
+}
 
 void player_anim (void)
 {
@@ -256,13 +255,32 @@ void player_anim (void)
 
        if (!self.animstate_override)
        {
-               if (!(self.flags & FL_ONGROUND))
+               if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP)
                {
                        if (self.crouch)
-                               setanim(self, self.anim_duckjump, FALSE, TRUE, self.restart_jump);
+                       {
+                               if (self.animstate_startframe != self.anim_duckjump_x) // don't perform another trace if already playing the crouch jump anim
+                               {
+                                       traceline(self.origin + '0 0 1' * PL_CROUCH_MIN_z, self.origin + '0 0 1' * (PL_CROUCH_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
+                                       if(!trace_startsolid && trace_fraction == 1 || !(self.animstate_startframe == self.anim_duckwalk_x || self.animstate_startframe == self.anim_duckidle_x)) // don't get stuck on non-crouch anims
+                                       {
+                                               setanim(self, self.anim_duckjump, FALSE, TRUE, self.restart_jump);
+                                               self.restart_jump = FALSE;
+                                       }
+                               }
+                       }
                        else
-                               setanim(self, self.anim_jump, FALSE, TRUE, self.restart_jump);
-                       self.restart_jump = FALSE;
+                       {
+                if (self.animstate_startframe != self.anim_jump_x) // don't perform another trace if already playing the jump anim
+                {
+                    traceline(self.origin + '0 0 1' * PL_MIN_z, self.origin + '0 0 1' * (PL_MIN_z - autocvar_sv_player_jumpanim_minfall), TRUE, self);
+                    if(!trace_startsolid && trace_fraction == 1 || self.animstate_startframe == self.anim_idle_x || (self.animstate_startframe == self.anim_melee_x && time - self.animstate_starttime >= 21/20)) // don't get stuck on idle animation in midair, nor melee after it finished
+                    {
+                        setanim(self, self.anim_jump, FALSE, TRUE, self.restart_jump);
+                        self.restart_jump = FALSE;
+                    }
+                }
+                       }
                }
                else if (self.crouch)
                {
@@ -326,7 +344,7 @@ void SpawnThrownWeapon (vector org, float w)
 
 void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
-       local float take, save;
+       float take, save;
        vector v;
        Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
 
@@ -374,6 +392,7 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float
                Violence_GibSplash(self, 1, 1, attacker);
                self.modelindex = 0; // restore later
                self.solid = SOLID_NOT; // restore later
+               self.takedamage = DAMAGE_NO; // restore later
        }
 }
 
@@ -382,12 +401,12 @@ void freezetag_CheckWinner();
 
 void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
-       local float take, save, waves, sdelay, dh, da, j;
+       float take, save, waves, sdelay, dh, da, j;
        vector v;
        float valid_damage_for_weaponstats;
        float excess;
 
-       if((g_arena && numspawned < 2) || (g_ca && ca_players < required_ca_players) && !inWarmupStage)
+       if((g_arena && numspawned < 2) || (g_ca && !ca_teams_ok) && !inWarmupStage)
                return;
 
        dh = max(self.health, 0);
@@ -476,7 +495,8 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        self.armorvalue = self.armorvalue - save;
                        self.health = self.health - take;
                        // pause regeneration for 5 seconds
-                       self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
+                       if(take)
+                self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
 
                        if (time > self.pain_finished)          //Don't switch pain sequences like crazy
                        {
@@ -485,10 +505,13 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                                if(sv_gentle < 1) {
                                        if(self.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
                                        {
-                                               if (random() > 0.5)
-                                                       setanim(self, self.anim_pain1, FALSE, TRUE, TRUE);
-                                               else
-                                                       setanim(self, self.anim_pain2, FALSE, TRUE, TRUE);
+                                               if (!self.animstate_override)
+                                               {
+                                                       if (random() > 0.5)
+                                                               setanim(self, self.anim_pain1, FALSE, TRUE, TRUE);
+                                                       else
+                                                               setanim(self, self.anim_pain2, FALSE, TRUE, TRUE);
+                                               }
                                        }
 
                                        if(sound_allowed(MSG_BROADCAST, attacker))
@@ -510,7 +533,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                                }
 
                                // throw off bot aim temporarily
-                               local float shake;
+                               float shake;
                                shake = damage * 5 / (bound(0,skill,100) + 1);
                                self.v_angle_x = self.v_angle_x + (random() * 2 - 1) * shake;
                                self.v_angle_y = self.v_angle_y + (random() * 2 - 1) * shake;
@@ -640,6 +663,8 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                MUTATOR_CALLHOOK(PlayerDies);
                weapon_action(self.weapon, WR_PLAYERDEATH);
 
+               RemoveGrapplingHook(self);
+
                if(self.flagcarried)
                {
                        if(attacker.classname != "player")