Dynamic handicap.
See merge request xonotic/xonotic-data.pk3dir!485
// running guns
// ==============
set g_running_guns 0 "... or wonder, till it drives you mad, what would have followed if you had."
+
+// ==================
+// 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_min 0 "The minimum value of the handicap."
+set g_dynamic_handicap_max 0 "The maximum value of the handicap."
#include <common/mutators/mutator/damagetext/_mod.inc>
#include <common/mutators/mutator/dodging/_mod.inc>
#include <common/mutators/mutator/doublejump/_mod.inc>
+#include <common/mutators/mutator/dynamic_handicap/_mod.inc>
#include <common/mutators/mutator/globalforces/_mod.inc>
#include <common/mutators/mutator/hook/_mod.inc>
#include <common/mutators/mutator/instagib/_mod.inc>
#include <common/mutators/mutator/damagetext/_mod.qh>
#include <common/mutators/mutator/dodging/_mod.qh>
#include <common/mutators/mutator/doublejump/_mod.qh>
+#include <common/mutators/mutator/dynamic_handicap/_mod.qh>
#include <common/mutators/mutator/globalforces/_mod.qh>
#include <common/mutators/mutator/hook/_mod.qh>
#include <common/mutators/mutator/instagib/_mod.qh>
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc>
+#endif
--- /dev/null
+// generated file; do not modify
--- /dev/null
+/// \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;
+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.
+float DynamicHandicap_ClampHandicap(float handicap);
+
+//========================= Free functions ====================================
+
+/// \brief Updates the handicap of a given player.
+/// \param[in,out] player Player to update.
+/// \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)
+{
+ if (handicap == 1)
+ {
+ return 1;
+ }
+ if (autocvar_g_dynamic_handicap_scale == 1)
+ {
+ return handicap;
+ }
+ if (handicap > 1)
+ {
+ 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 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");
+}
+
+/// \brief Hook that is called when player connects to the server.
+MUTATOR_HOOKFUNCTION(dynamic_handicap, ClientConnect)
+{
+ entity player = M_ARGV(0, entity);
+ DynamicHandicap_UpdateHandicap(player);
+}
+
+/// \brief Hook that is called when player dies.
+MUTATOR_HOOKFUNCTION(dynamic_handicap, PlayerDies)
+{
+ entity attacker = M_ARGV(1, entity);
+ entity victim = M_ARGV(2, entity);
+ DynamicHandicap_UpdateHandicap(victim);
+ if (!IS_CLIENT(attacker))
+ {
+ return;
+ }
+ DynamicHandicap_UpdateHandicap(attacker);
+}
#include <server/g_models.qc>
#include <server/g_subs.qc>
#include <server/g_world.qc>
+#include <server/handicap.qc>
#include <server/impulse.qc>
#include <server/ipban.qc>
#include <server/item_key.qc>
#include <server/g_models.qh>
#include <server/g_subs.qh>
#include <server/g_world.qh>
+#include <server/handicap.qh>
#include <server/impulse.qh>
#include <server/ipban.qh>
#include <server/item_key.qh>
#include "spawnpoints.qh"
#include "resources.qh"
#include "g_damage.qh"
+#include "handicap.qh"
#include "g_hook.qh"
#include "command/common.qh"
#include "cheats.qh"
it.init_for_player(it, this);
});
+ Handicap_Initialize(this);
+
MUTATOR_CALLHOOK(ClientConnect, this);
if (IS_REAL_CLIENT(this))
--- /dev/null
+#include "handicap.qh"
+
+/// \file
+/// \brief Source file that contains implementation of the handicap system.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+#include <common/state.qh>
+#include "client.qh"
+
+.float m_handicap; ///< Holds the handicap value.
+
+void Handicap_Initialize(entity player)
+{
+ CS(player).m_handicap = 1;
+}
+
+float Handicap_GetVoluntaryHandicap(entity player)
+{
+ return bound(1.0, CS(player).cvar_cl_handicap, 10.0);
+}
+
+float Handicap_GetForcedHandicap(entity player)
+{
+ return CS(player).m_handicap;
+}
+
+void Handicap_SetForcedHandicap(entity player, float value)
+{
+ if (value <= 0)
+ {
+ error("Handicap_SetForcedHandicap: Invalid handicap value.");
+ }
+ CS(player).m_handicap = value;
+}
+
+float Handicap_GetTotalHandicap(entity player)
+{
+ return Handicap_GetForcedHandicap(player) * Handicap_GetVoluntaryHandicap(
+ player);
+}
--- /dev/null
+#pragma once
+
+/// \file
+/// \brief Header file that describes the handicap system.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+// Handicap is used to make the game harder for strong players and easier for
+// weak players. Values greater than 1 make the game harder and values less than
+// 1 make the game easier. Right now handicap only affects damage. There are 2
+// types of handicap: voluntary and forced. Voluntary handicap can be set via
+// cl_handicap cvar. For obvious reasons, it can't be less than 1. Forced
+// handicap can be set by server mutators. The total handicap is the product of
+// voluntary and forced handicap.
+
+/// \brief Initializes handicap to its default value.
+/// \param[in,out] player Player to initialize.
+/// \return No return.
+void Handicap_Initialize(entity player);
+
+/// \brief Returns the voluntary handicap of the player.
+/// \param[in] player Player to check.
+/// \return Voluntary handicap of the player.
+float Handicap_GetVoluntaryHandicap(entity player);
+
+/// \brief Returns the forced handicap of the player.
+/// \param[in] player Player to check.
+/// \return Forced handicap of the player.
+float Handicap_GetForcedHandicap(entity player);
+
+/// \brief Sets the forced handicap of the player.
+/// \param[in] player Player to alter.
+/// \param[in] value Handicap value to set.
+/// \return No return.
+void Handicap_SetForcedHandicap(entity player, float value);
+
+/// \brief Returns the total handicap of the player.
+/// \param[in] player Player to check.
+/// \return Total handicap of the player.
+float Handicap_GetTotalHandicap(entity player);
#include "bot/api.qh"
#include "cheats.qh"
#include "g_damage.qh"
+#include "handicap.qh"
#include "g_subs.qh"
#include "miscfunctions.qh"
#include "portals.qh"
if(!DEATH_ISSPECIAL(deathtype))
{
- damage *= bound(1.0, CS(this).cvar_cl_handicap, 10.0);
- if(this != attacker && IS_PLAYER(attacker))
- damage /= bound(1.0, CS(attacker).cvar_cl_handicap, 10.0);
+ damage *= Handicap_GetTotalHandicap(this);
+ if (this != attacker && IS_PLAYER(attacker))
+ {
+ damage /= Handicap_GetTotalHandicap(attacker);
+ }
}
if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)