]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
add strafe efficiency field for CTS scoreboard
authorJuhu <5894800-Juhu_@users.noreply.gitlab.com>
Sun, 5 Jul 2020 17:17:29 +0000 (19:17 +0200)
committerJuhu <5894800-Juhu_@users.noreply.gitlab.com>
Sun, 5 Jul 2020 17:17:29 +0000 (19:17 +0200)
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc
qcsrc/common/scores.qh
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/strafe.qc [new file with mode: 0644]
qcsrc/server/strafe.qh [new file with mode: 0644]

index 120feeafa05cb858f0835c232b4e88f796e7ee88..1e7984e8da72ae551c9c118365f421f42757e4ae 100644 (file)
@@ -138,6 +138,7 @@ string Label_getInfo(string label, int mode)
                case "revivals":     if (!mode) return CTX(_("SCO^revivals"));     else LOG_HELP(strcat("^3", "revivals", "           ^7", _("Number of revivals")));
                case "rounds":       if (!mode) return CTX(_("SCO^rounds won"));   else LOG_HELP(strcat("^3", "rounds", "             ^7", _("Number of rounds won")));
                case "score":        if (!mode) return CTX(_("SCO^score"));        else LOG_HELP(strcat("^3", "score", "              ^7", _("Total score")));
+               case "strafe":       if (!mode) return CTX(_("SCO^strafe"));       else LOG_HELP(strcat("^3", "strafe", "             ^7", _("Strafe efficiency (CTS)")));
                case "suicides":     if (!mode) return CTX(_("SCO^suicides"));     else LOG_HELP(strcat("^3", "suicides", "           ^7", _("Number of suicides")));
                case "sum":          if (!mode) return CTX(_("SCO^sum"));          else LOG_HELP(strcat("^3", "sum", "                ^7", _("Number of kills minus deaths")));
                case "takes":        if (!mode) return CTX(_("SCO^takes"));        else LOG_HELP(strcat("^3", "takes", "              ^7", _("Number of domination points taken (Domination)")));
@@ -374,7 +375,7 @@ void Cmd_Scoreboard_Help()
 " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
 " +lms/lives +lms/rank" \
 " +kh/kckills +kh/losses +kh/caps" \
-" ?+rc/laps ?+rc/time +rc,cts/fastest" \
+" ?+rc/laps ?+rc/time ?+cts/strafe +rc,cts/fastest" \
 " +as/objectives +nb/faults +nb/goals" \
 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
 " +dom/ticks +dom/takes" \
@@ -698,6 +699,14 @@ string Scoreboard_GetField(entity pl, PlayerScoreField field)
                case SP_DMG: case SP_DMGTAKEN:
                        return sprintf("%.1f k", pl.(scores(field)) / 1000);
 
+               case SP_CTS_STRAFE:
+               {
+                       float strafe_efficiency = pl.(scores(field)) / 10000;
+                       if(strafe_efficiency < -1) return "";
+                       sbt_field_rgb = '1 1 1' - (strafe_efficiency > 0 ? '1 0 1' : '0 1 1') * fabs(strafe_efficiency);
+                       return sprintf("%.2f%%", strafe_efficiency * 100);
+               }
+
                default: case SP_SCORE:
                        tmp = pl.(scores(field));
                        f = scores_flags(field);
index 4e219eae7e2cc9a5c97c322a709e8aed74c20ea0..84b35bbf67ffcf07f35c385747fef12d71d55b51 100644 (file)
@@ -50,6 +50,7 @@ void cts_ScoreRules()
     GameRules_score_enabled(false);
     GameRules_scoring(0, 0, 0, {
         if (g_race_qualifying) {
+            field(SP_CTS_STRAFE, "strafe", 0);
             field(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME);
         } else {
             field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY);
@@ -134,6 +135,7 @@ MUTATOR_HOOKFUNCTION(cts, PlayerPhysics)
                                CS(player).movement_y = -M_SQRT1_2 * wishspeed;
                }
        }
+       calculate_strafe_efficiency(player, CS(player).movement);
 }
 
 MUTATOR_HOOKFUNCTION(cts, reset_map_global)
@@ -146,6 +148,9 @@ MUTATOR_HOOKFUNCTION(cts, reset_map_global)
        PlayerScore_Sort(race_place, 0, 1, 0);
 
        FOREACH_CLIENT(true, {
+               it.strafe_efficiency_best = -2;
+               PlayerScore_Set(it, SP_CTS_STRAFE, it.strafe_efficiency_best * 10000);
+
                if(it.race_place)
                {
                        s = GameRules_scoring_add(it, RACE_FASTEST, 0);
@@ -192,6 +197,10 @@ MUTATOR_HOOKFUNCTION(cts, ClientConnect)
                {
                        race_SendRankings(i, 0, 0, MSG_ONE);
                }
+
+               player.strafe_efficiency_average = player.strafe_efficiency_tics = 0;
+               player.strafe_efficiency_best = -2;
+               PlayerScore_Set(player, SP_CTS_STRAFE, player.strafe_efficiency_best * 10000);
        }
 }
 
@@ -206,7 +215,6 @@ MUTATOR_HOOKFUNCTION(cts, AbortSpeedrun)
 MUTATOR_HOOKFUNCTION(cts, MakePlayerObserver)
 {
        entity player = M_ARGV(0, entity);
-
        if(GameRules_scoring_add(player, RACE_FASTEST, 0))
                player.frags = FRAGS_PLAYER_OUT_OF_GAME;
        else
@@ -255,6 +263,8 @@ MUTATOR_HOOKFUNCTION(cts, PlayerDies)
        frag_target.respawn_flags |= RESPAWN_FORCE;
        race_AbandonRaceCheck(frag_target);
 
+       frag_target.strafe_efficiency_average = frag_target.strafe_efficiency_tics = 0;
+
        if(autocvar_g_cts_removeprojectiles)
        {
                IL_EACH(g_projectiles, it.owner == frag_target && (it.flags & FL_PROJECTILE),
@@ -380,6 +390,13 @@ MUTATOR_HOOKFUNCTION(cts, ClientKill)
 MUTATOR_HOOKFUNCTION(cts, Race_FinalCheckpoint)
 {
        entity player = M_ARGV(0, entity);
+       float strafe_efficiency_current = player.strafe_efficiency_average / player.strafe_efficiency_tics;
+
+       if(player.strafe_efficiency_best < strafe_efficiency_current)
+       {
+               player.strafe_efficiency_best = strafe_efficiency_current;
+               PlayerScore_Set(player, SP_CTS_STRAFE, player.strafe_efficiency_best * 10000);
+       }
 
        // useful to prevent cheating by running back to the start line and starting out with more speed
        if(autocvar_g_cts_finish_kill_delay)
index 3bc6c55636c65fa4e3875d0bf2b328e0f3a0aa02..deb168fed5356be31ae6c4aaaed53191bbc8fece 100644 (file)
@@ -51,6 +51,7 @@ REGISTER_SP(RACE_FASTEST);
 //REGISTER_SP(CTS_TIME);
 //REGISTER_SP(CTS_LAPS);
 //REGISTER_SP(CTS_FASTEST);
+REGISTER_SP(CTS_STRAFE);
 
 REGISTER_SP(ASSAULT_OBJECTIVES);
 
index 2ec8386955f032bfa6d44bd4396414aea1c27f5b..e3f911a990f0119dfee2ce35f667e14c7e148d9b 100644 (file)
@@ -24,6 +24,7 @@
 #include <server/scores_rules.qc>
 #include <server/spawnpoints.qc>
 #include <server/steerlib.qc>
+#include <server/strafe.qc>
 #ifdef SVQC
     #include <server/sv_main.qc>
 #endif
index cc27baf120e93fab2379e7a8643c6e16a1c3c7fe..bd152a39173b8b2373c3e19312cbbd68937d4b38 100644 (file)
@@ -24,6 +24,7 @@
 #include <server/scores_rules.qh>
 #include <server/spawnpoints.qh>
 #include <server/steerlib.qh>
+#include <server/strafe.qh>
 #ifdef SVQC
     #include <server/sv_main.qh>
 #endif
diff --git a/qcsrc/server/strafe.qc b/qcsrc/server/strafe.qc
new file mode 100644 (file)
index 0000000..a7e6efa
--- /dev/null
@@ -0,0 +1,188 @@
+#include "strafe.qh"
+
+#include <server/miscfunctions.qh>
+
+.float race_checkpoint;
+
+.float strafe_efficiency_average;
+.float strafe_efficiency_tics;
+.float strafe_efficiency_best;
+
+void calculate_strafe_efficiency(entity strafeplayer, vector movement)
+{
+    float efficiency = 0;
+
+    if(strafeplayer)
+    {
+        // physics
+        bool   onground                      = IS_ONGROUND(strafeplayer);
+        bool   strafekeys;
+        bool   swimming                      = strafeplayer.waterlevel >= WATERLEVEL_SWIMMING;
+        float  speed                         = vlen(vec2(strafeplayer.velocity));
+        float  maxspeed_crouch_mod           = IS_DUCKED(strafeplayer) ? .5 : 1;
+        float  maxspeed_phys                 = onground ? PHYS_MAXSPEED(strafeplayer) : PHYS_MAXAIRSPEED(strafeplayer);
+        float  maxspeed                      = maxspeed_phys * maxspeed_crouch_mod;
+        float  vel_angle                     = vectoangles(strafeplayer.velocity).y;
+        float  view_angle                    = PHYS_INPUT_ANGLES(strafeplayer).y + 180;
+        float  angle;
+        int    direction;
+        int    keys_fwd;
+        float  wishangle                     = 0;
+        float  moveangle;
+        bool   fwd                           = true;
+        float  bestangle;
+
+        // determine whether the player is pressing forwards or backwards keys
+        if(movement.x > 0)
+        {
+            keys_fwd = 1;
+        }
+        else if(movement.x < 0)
+        {
+            keys_fwd = -1;
+        }
+        else
+        {
+            keys_fwd = 0;
+        }
+
+        // determine player wishdir
+        if(movement.x == 0)
+        {
+            if(movement.y < 0)
+            {
+                wishangle = -90;
+            }
+            else if(movement.y > 0)
+            {
+                wishangle = 90;
+            }
+            else
+            {
+                wishangle = 0;
+            }
+        }
+        else
+        {
+            if(movement.y == 0)
+            {
+                wishangle = 0;
+            }
+            else
+            {
+                wishangle = RAD2DEG * atan2(movement.y, movement.x);
+                // wrap the wish angle if it exceeds ±90°
+                if(fabs(wishangle) > 90)
+                {
+                    if(wishangle < 0) wishangle += 180;
+                    else wishangle -= 180;
+                    wishangle = -wishangle;
+                }
+            }
+        }
+
+        strafekeys = fabs(wishangle) == 90;
+
+        if(strafekeys && !swimming)
+        {
+            maxspeed = PHYS_MAXAIRSTRAFESPEED(strafeplayer); // no modifiers here because they don't affect air strafing
+        }
+
+        // get current strafing angle ranging from -180° to +180°
+        if(speed > 0)
+        {
+            // calculate view angle relative to the players current velocity direction
+            angle = vel_angle - view_angle;
+
+            // if the angle goes above 180° or below -180° wrap it to the opposite side
+            if (angle > 180) angle -= 360;
+            else if(angle < -180) angle += 360;
+
+            // shift the strafe angle by 180° for further calculations
+            if(angle < 0) angle += 180;
+            else angle -= 180;
+
+            // determine whether the player is strafing forwards or backwards
+            // if the player isn't strafe turning use forwards/backwards keys to determine direction
+            if(!strafekeys)
+            {
+                if(keys_fwd > 0)
+                {
+                    fwd = true;
+                }
+                else if(keys_fwd < 0)
+                {
+                    fwd = false;
+                }
+                else
+                {
+                    fwd = fabs(angle) <= 90;
+                }
+            }
+            // otherwise determine by examining the strafe angle
+            else
+            {
+                if(wishangle < 0) // detect direction since the direction is not yet set
+                {
+                    fwd = angle <= -wishangle;
+                }
+                else
+                {
+                    fwd = angle >= -wishangle;
+                }
+            }
+
+            // shift the strafe angle by 180° when strafing backwards
+            if(!fwd)
+            {
+                if(angle < 0) angle += 180;
+                else angle -= 180;
+            }
+        }
+        else
+        {
+            angle = 0;
+        }
+
+        // invert the wish angle when strafing backwards
+        if(!fwd)
+        {
+            wishangle = -wishangle;
+        }
+
+        moveangle = angle + wishangle;
+
+        // best angle to strafe at
+        bestangle = (speed > maxspeed ? acos(maxspeed / speed) : 0) * RAD2DEG;
+
+        if(speed > 0 && !swimming && strafeplayer.race_checkpoint > 0) // only calculate a new average if all conditions are met
+        {
+            ++strafeplayer.strafe_efficiency_tics;
+            if(fabs(vlen(vec2(movement))) > 0)
+            {
+                if(fabs(moveangle) > 90)
+                {
+                    efficiency = -((fabs(moveangle) - 90) / 90);
+                    if(efficiency < -1) efficiency = -2 - efficiency;
+                }
+                else
+                {
+                    if((moveangle) >= bestangle)
+                    {
+                        efficiency = 1 - (moveangle - bestangle) / (90 - bestangle);
+                    }
+                    else if((moveangle) <= -bestangle)
+                    {
+                        efficiency = 1 - (moveangle + bestangle) / (-90 + bestangle);
+                    }
+                }
+            }
+        }
+        else if(strafeplayer.race_checkpoint <= 0)
+        {
+            strafeplayer.strafe_efficiency_average = strafeplayer.strafe_efficiency_tics = 0;
+        }
+
+        strafeplayer.strafe_efficiency_average += efficiency;
+    }
+}
diff --git a/qcsrc/server/strafe.qh b/qcsrc/server/strafe.qh
new file mode 100644 (file)
index 0000000..51ab72c
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void calculate_strafe_efficiency(entity, vector);