]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
New dynamic handicap algorithm. 494/head
authorLyberta <lyberta@lyberta.net>
Sat, 23 Sep 2017 02:22:41 +0000 (05:22 +0300)
committerLyberta <lyberta@lyberta.net>
Sat, 23 Sep 2017 02:22:41 +0000 (05:22 +0300)
mutators.cfg
qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc
qcsrc/server/mutators/events.qh
qcsrc/server/scores.qc

index be74e81872eb3c64a85325789450a6ef686bf159..c57088dd92910c1e1915d8bd61ba31918b3eb085 100644 (file)
@@ -469,6 +469,7 @@ set g_running_guns 0 "... or wonder, till it drives you mad, what would have fol
 //  dynamic handicap
 // ==================
 set g_dynamic_handicap 0 "Whether to enable dynamic handicap."
-set g_dynamic_handicap_scale 1 "The scale of the handicap. Larger values mean more penalties for strong players and more buffs for weak players."
+set g_dynamic_handicap_scale 0.2 "The scale of the handicap. Larger values mean more penalties for strong players and more buffs for weak players."
+set g_dynamic_handicap_exponent 1 "The exponent used to calculate handicap. 1 means linear scale. Values more than 1 mean stronger non-linear handicap. Values less than 1 mean weaker non-linear handicap"
 set g_dynamic_handicap_min 0 "The minimum value of the handicap."
 set g_dynamic_handicap_max 0 "The maximum value of the handicap."
index 5b8ab107494f2f07406b9ef710d13b729985e811..d5d3ba40f8ee233127078a6e4ff735d855f9f05a 100644 (file)
@@ -11,21 +11,15 @@ int autocvar_g_dynamic_handicap; ///< Whether to enable dynamic handicap.
 /// \brief The scale of the handicap. Larget values mean more penalties for
 /// strong players and more buffs for weak players.
 float autocvar_g_dynamic_handicap_scale;
+/// \brief The exponent used to calculate handicap. 1 means linear scale. Values
+/// more than 1 mean stronger non-linear handicap. Values less than 1 mean
+/// weaker non-linear handicap.
+float autocvar_g_dynamic_handicap_exponent;
 float autocvar_g_dynamic_handicap_min; ///< The minimum value of the handicap.
 float autocvar_g_dynamic_handicap_max; ///< The maximum value of the handicap.
 
 //====================== Forward declarations =================================
 
-/// \brief Returns the base value of the handicap.
-/// \param[in] player Player to evaluate.
-/// \return Base handicap value.
-float DynamicHandicap_GetBaseValue(entity player);
-
-/// \brief Scales the base value of the handicap.
-/// \param[in] handicap Value to scale.
-/// \return Scaled value.
-float DynamicHandicap_ScaleHandicap(float handicap);
-
 /// \brief Clamps the value of the handicap.
 /// \param[in] handicap Value to clamp.
 /// \return Clamped value.
@@ -33,61 +27,39 @@ float DynamicHandicap_ClampHandicap(float handicap);
 
 //========================= Free functions ====================================
 
-/// \brief Updates the handicap of a given player.
-/// \param[in,out] player Player to update.
+/// \brief Updates the handicap of all players.
 /// \return No return.
-void DynamicHandicap_UpdateHandicap(entity player)
-{
-       float handicap = DynamicHandicap_GetBaseValue(player);
-       handicap = DynamicHandicap_ScaleHandicap(handicap);
-       handicap = DynamicHandicap_ClampHandicap(handicap);
-       Handicap_SetForcedHandicap(player, handicap);
-}
-
-float DynamicHandicap_GetBaseValue(entity player)
-{
-       int kills = PlayerScore_Get(player, SP_KILLS);
-       int deaths = PlayerScore_Get(player, SP_DEATHS);
-       if (kills == deaths)
-       {
-               return 1;
-       }
-       if (deaths == 0)
-       {
-               return kills;
-       }
-       if (kills == 0)
-       {
-               return 1 / deaths;
-       }
-       return kills / deaths;
-}
-
-float DynamicHandicap_ScaleHandicap(float handicap)
+void DynamicHandicap_UpdateHandicap()
 {
-       if (handicap == 1)
-       {
-               return 1;
-       }
-       if (autocvar_g_dynamic_handicap_scale == 1)
+       float total_score = 0;
+       float total_players = 0;
+       FOREACH_CLIENT(IS_PLAYER(it),
        {
-               return handicap;
-       }
-       if (handicap > 1)
+               total_score += PlayerScore_Get(it, SP_SCORE);
+               ++total_players;
+       });
+       float mean_score = total_score / total_players;
+       FOREACH_CLIENT(true,
        {
-               handicap -= 1;
-               handicap *= autocvar_g_dynamic_handicap_scale;
-               return handicap + 1;
-       }
-       if (handicap < 1)
-       {
-               handicap = 1 / handicap;
-               handicap -= 1;
-               handicap *= autocvar_g_dynamic_handicap_scale;
-               handicap += 1;
-               return 1 / handicap;
-       }
-       return 1;
+               float score = PlayerScore_Get(it, SP_SCORE);
+               float handicap = fabs((score - mean_score) *
+                       autocvar_g_dynamic_handicap_scale);
+               handicap = handicap ** autocvar_g_dynamic_handicap_exponent;
+               if (score < mean_score)
+               {
+                       handicap = -handicap;
+               }
+               if (handicap >= 0)
+               {
+                       handicap += 1;
+               }
+               else
+               {
+                       handicap = 1 / (fabs(handicap) + 1);
+               }
+               handicap = DynamicHandicap_ClampHandicap(handicap);
+               Handicap_SetForcedHandicap(it, handicap);
+       });
 }
 
 float DynamicHandicap_ClampHandicap(float handicap)
@@ -119,22 +91,26 @@ MUTATOR_HOOKFUNCTION(dynamic_handicap, BuildMutatorsPrettyString)
        M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Dynamic handicap");
 }
 
-/// \brief Hook that is called when player connects to the server.
-MUTATOR_HOOKFUNCTION(dynamic_handicap, ClientConnect)
+MUTATOR_HOOKFUNCTION(dynamic_handicap, ClientDisconnect)
+{
+       DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, PutClientInServer)
+{
+       DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, MakePlayerObserver)
 {
-       entity player = M_ARGV(0, entity);
-       DynamicHandicap_UpdateHandicap(player);
+       DynamicHandicap_UpdateHandicap();
 }
 
-/// \brief Hook that is called when player dies.
-MUTATOR_HOOKFUNCTION(dynamic_handicap, PlayerDies)
+MUTATOR_HOOKFUNCTION(dynamic_handicap, AddedPlayerScore)
 {
-       entity attacker = M_ARGV(1, entity);
-       entity victim = M_ARGV(2, entity);
-       DynamicHandicap_UpdateHandicap(victim);
-       if (!IS_CLIENT(attacker))
+       if (M_ARGV(0, entity) != SP_SCORE)
        {
                return;
        }
-       DynamicHandicap_UpdateHandicap(attacker);
+       DynamicHandicap_UpdateHandicap();
 }
index 0110bdf1bd66c989a61f88e8e7840d7e088bcd38..e0c4198cc7adbea3354f350544950a5cf8ac7397 100644 (file)
@@ -871,6 +871,13 @@ MUTATOR_HOOKABLE(WantWeapon, EV_WantWeapon);
     /**/
 MUTATOR_HOOKABLE(AddPlayerScore, EV_AddPlayerScore);
 
+#define EV_AddedPlayerScore(i, o) \
+    /** score field */  i(entity, MUTATOR_ARGV_0_entity) \
+    /** score */        i(float, MUTATOR_ARGV_1_float) \
+    /** player */       i(entity, MUTATOR_ARGV_2_entity) \
+    /**/
+MUTATOR_HOOKABLE(AddedPlayerScore, EV_AddPlayerScore);
+
 #define EV_GetPlayerStatus(i, o) \
     /** player */    i(entity, MUTATOR_ARGV_0_entity) \
     /**/
index 11bc60238252bb071cbade6518384a3ac8cbb47c..b25a65f1a7c127f348e897c392d4db2f00fb6734 100644 (file)
@@ -348,7 +348,9 @@ float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
                s.SendFlags |= (2 ** (scorefield.m_id % 16));
        if(!warmup_stage)
                PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
-       return (s.(scores(scorefield)) += score);
+       s.(scores(scorefield)) += score;
+       MUTATOR_CALLHOOK(AddedPlayerScore, scorefield, score, player);
+       return s.(scores(scorefield));
 }
 
 float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)