--- /dev/null
+#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();
+}