From: Juhu <5894800-Juhu_@users.noreply.gitlab.com> Date: Sun, 5 Jul 2020 17:17:29 +0000 (+0200) Subject: add strafe efficiency field for CTS scoreboard X-Git-Tag: xonotic-v0.8.5~738^2~16 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=1444d1344376531d3e57839c9ec829105875698e;p=xonotic%2Fxonotic-data.pk3dir.git add strafe efficiency field for CTS scoreboard --- diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc index 120feeafa..1e7984e8d 100644 --- a/qcsrc/client/hud/panel/scoreboard.qc +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -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); diff --git a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc index 4e219eae7..84b35bbf6 100644 --- a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc +++ b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc @@ -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) diff --git a/qcsrc/common/scores.qh b/qcsrc/common/scores.qh index 3bc6c5563..deb168fed 100644 --- a/qcsrc/common/scores.qh +++ b/qcsrc/common/scores.qh @@ -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); diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index 2ec838695..e3f911a99 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef SVQC #include #endif diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index cc27baf12..bd152a391 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef SVQC #include #endif diff --git a/qcsrc/server/strafe.qc b/qcsrc/server/strafe.qc new file mode 100644 index 000000000..a7e6efaa6 --- /dev/null +++ b/qcsrc/server/strafe.qc @@ -0,0 +1,188 @@ +#include "strafe.qh" + +#include + +.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 index 000000000..51ab72c99 --- /dev/null +++ b/qcsrc/server/strafe.qh @@ -0,0 +1,3 @@ +#pragma once + +void calculate_strafe_efficiency(entity, vector);