]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc
Merge branch 'master' into Lyberta/RandomStartWeapons
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / dynamic_handicap / sv_dynamic_handicap.qc
diff --git a/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc b/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc
new file mode 100644 (file)
index 0000000..d5d3ba4
--- /dev/null
@@ -0,0 +1,116 @@
+#include "sv_dynamic_handicap.qh"
+/// \file
+/// \brief Source file that contains implementation of the Dynamic handicap
+/// mutator.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+//======================= Global variables ====================================
+
+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 Clamps the value of the handicap.
+/// \param[in] handicap Value to clamp.
+/// \return Clamped value.
+float DynamicHandicap_ClampHandicap(float handicap);
+
+//========================= Free functions ====================================
+
+/// \brief Updates the handicap of all players.
+/// \return No return.
+void DynamicHandicap_UpdateHandicap()
+{
+       float total_score = 0;
+       float total_players = 0;
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               total_score += PlayerScore_Get(it, SP_SCORE);
+               ++total_players;
+       });
+       float mean_score = total_score / total_players;
+       FOREACH_CLIENT(true,
+       {
+               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)
+{
+       if ((autocvar_g_dynamic_handicap_min >= 0) && (handicap <
+               autocvar_g_dynamic_handicap_min))
+       {
+               handicap = autocvar_g_dynamic_handicap_min;
+       }
+       if ((autocvar_g_dynamic_handicap_max > 0) && (handicap >
+               autocvar_g_dynamic_handicap_max))
+       {
+               handicap = autocvar_g_dynamic_handicap_max;
+       }
+       return handicap;
+}
+
+//============================= Hooks ========================================
+
+REGISTER_MUTATOR(dynamic_handicap, autocvar_g_dynamic_handicap);
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, BuildMutatorsString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ":handicap");
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, BuildMutatorsPrettyString)
+{
+       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Dynamic handicap");
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, ClientDisconnect)
+{
+       DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, PutClientInServer)
+{
+       DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, MakePlayerObserver)
+{
+       DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, AddedPlayerScore)
+{
+       if (M_ARGV(0, entity) != SP_SCORE)
+       {
+               return;
+       }
+       DynamicHandicap_UpdateHandicap();
+}