From: Lyberta Date: Sat, 23 Sep 2017 02:22:41 +0000 (+0300) Subject: New dynamic handicap algorithm. X-Git-Tag: xonotic-v0.8.5~2460^2 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=1d2572e36c11512af047c460cf58edd19954a0aa;p=xonotic%2Fxonotic-data.pk3dir.git New dynamic handicap algorithm. --- diff --git a/mutators.cfg b/mutators.cfg index be74e8187..c57088dd9 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -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." diff --git a/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc b/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc index 5b8ab1074..d5d3ba40f 100644 --- a/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc +++ b/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc @@ -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(); } diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 0110bdf1b..e0c4198cc 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -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) \ /**/ diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index 11bc60238..b25a65f1a 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -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)