From 0a3675f2dea4a16db0a3df20ca0be1a9ee128e76 Mon Sep 17 00:00:00 2001
From: Freddy <schro.sb@gmail.com>
Date: Thu, 21 Sep 2017 17:10:12 +0200
Subject: [PATCH] Add a mutator to automatically kick teamkilling trolls after
 a certain amount of teamkills

For this, count teamkills separately in the scoreboard
---
 mutators.cfg                                  |  5 +++++
 qcsrc/client/hud/panel/scoreboard.qc          |  3 +++
 qcsrc/common/mutators/mutator/_mod.inc        |  1 +
 qcsrc/common/mutators/mutator/_mod.qh         |  1 +
 .../mutators/mutator/kick_teamkiller/_mod.inc |  4 ++++
 .../mutators/mutator/kick_teamkiller/_mod.qh  |  1 +
 .../kick_teamkiller/sv_kick_teamkiller.qc     | 22 +++++++++++++++++++
 qcsrc/common/notifications/all.inc            |  1 +
 qcsrc/common/scores.qh                        |  1 +
 qcsrc/server/g_damage.qc                      |  2 +-
 qcsrc/server/scores.qc                        |  2 +-
 qcsrc/server/scores.qh                        |  2 +-
 qcsrc/server/scores_rules.qc                  |  3 +++
 13 files changed, 45 insertions(+), 3 deletions(-)
 create mode 100644 qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc
 create mode 100644 qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh
 create mode 100644 qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc

diff --git a/mutators.cfg b/mutators.cfg
index be74e81872..e2a38f9229 100644
--- a/mutators.cfg
+++ b/mutators.cfg
@@ -472,3 +472,8 @@ 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."
+
+// ===============
+// kick teamkiller
+// ===============
+set g_kick_teamkiller_limit 0 "Limit for teamkills before the client gets dropped."
diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc
index 8e0d0f0576..5677a50cc0 100644
--- a/qcsrc/client/hud/panel/scoreboard.qc
+++ b/qcsrc/client/hud/panel/scoreboard.qc
@@ -96,6 +96,7 @@ string TranslateScoresLabel(string l)
 		case "kd": return CTX(_("SCO^k/d"));
 		case "kdr": return CTX(_("SCO^kdr"));
 		case "kills": return CTX(_("SCO^kills"));
+		case "teamkills": return CTX(_("SC0^teamkills"));
 		case "laps": return CTX(_("SCO^laps"));
 		case "lives": return CTX(_("SCO^lives"));
 		case "losses": return CTX(_("SCO^losses"));
@@ -312,6 +313,7 @@ void Cmd_Scoreboard_Help()
 	LOG_INFO(_("^3deaths^7                   Number of deaths"));
 	LOG_INFO(_("^3suicides^7                 Number of suicides"));
 	LOG_INFO(_("^3frags^7                    kills - suicides"));
+	LOG_INFO(_("^3teamkills^7                Number of teamkills"));
 	LOG_INFO(_("^3kd^7                       The kill-death ratio"));
 	LOG_INFO(_("^3dmg^7                      The total damage done"));
 	LOG_INFO(_("^3dmgtaken^7                 The total damage taken"));
@@ -361,6 +363,7 @@ void Cmd_Scoreboard_Help()
 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
 " -teams,lms/deaths +ft,tdm/deaths" \
 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" +teams/teamkills"\
 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
diff --git a/qcsrc/common/mutators/mutator/_mod.inc b/qcsrc/common/mutators/mutator/_mod.inc
index eeb93ba5e8..eb2dd70297 100644
--- a/qcsrc/common/mutators/mutator/_mod.inc
+++ b/qcsrc/common/mutators/mutator/_mod.inc
@@ -15,6 +15,7 @@
 #include <common/mutators/mutator/instagib/_mod.inc>
 #include <common/mutators/mutator/invincibleproj/_mod.inc>
 #include <common/mutators/mutator/itemstime/_mod.inc>
+#include <common/mutators/mutator/kick_teamkiller/_mod.inc>
 #include <common/mutators/mutator/melee_only/_mod.inc>
 #include <common/mutators/mutator/midair/_mod.inc>
 #include <common/mutators/mutator/multijump/_mod.inc>
diff --git a/qcsrc/common/mutators/mutator/_mod.qh b/qcsrc/common/mutators/mutator/_mod.qh
index 956c0d9753..c62f62a1d5 100644
--- a/qcsrc/common/mutators/mutator/_mod.qh
+++ b/qcsrc/common/mutators/mutator/_mod.qh
@@ -15,6 +15,7 @@
 #include <common/mutators/mutator/instagib/_mod.qh>
 #include <common/mutators/mutator/invincibleproj/_mod.qh>
 #include <common/mutators/mutator/itemstime/_mod.qh>
+#include <common/mutators/mutator/kick_teamkiller/_mod.qh>
 #include <common/mutators/mutator/melee_only/_mod.qh>
 #include <common/mutators/mutator/midair/_mod.qh>
 #include <common/mutators/mutator/multijump/_mod.qh>
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc
new file mode 100644
index 0000000000..a374a0e982
--- /dev/null
+++ b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.inc
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc>
+#endif
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh
new file mode 100644
index 0000000000..98fb4815c1
--- /dev/null
+++ b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh
@@ -0,0 +1 @@
+// generated file; do not modify
diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc
new file mode 100644
index 0000000000..6355f25245
--- /dev/null
+++ b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc
@@ -0,0 +1,22 @@
+
+float autocvar_g_kick_teamkiller_limit;
+
+REGISTER_MUTATOR(kick_teamkiller, (autocvar_g_kick_teamkiller_limit > 0));
+
+MUTATOR_HOOKFUNCTION(kick_teamkiller, PlayerDies)
+{
+	if (!teamplay)
+		return;
+	entity attacker = M_ARGV(1, entity);
+	if (!IS_REAL_CLIENT(attacker))
+	{
+		return;
+	}
+
+	int teamkills = PlayerScore_Get(attacker, SP_TEAMKILLS);
+	if (teamkills >= autocvar_g_kick_teamkiller_limit)
+	{
+		Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname);
+		dropclient(attacker);
+	}
+}
diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc
index cba6023bda..227d7ee1a5 100644
--- a/qcsrc/common/notifications/all.inc
+++ b/qcsrc/common/notifications/all.inc
@@ -422,6 +422,7 @@
     MSG_INFO_NOTIF(QUIT_DISCONNECT,                         N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 disconnected"), "")
     MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for idling"), "")
     MSG_INFO_NOTIF(QUIT_KICK_SPECTATING,                    N_CONSOLE,  0, 0, "", "",           "",             _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "")
+    MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL,                      N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 was kicked for excessive teamkilling"), "")
     MSG_INFO_NOTIF(QUIT_SPECTATE,                           N_CONSOLE,  1, 0, "s1", "",         "",             _("^BG%s^F3 is now spectating"), "")
 
     MSG_INFO_NOTIF(RACE_ABANDONED,                          N_CONSOLE,  1, 0, "s1", "",                                                                     "",                         _("^BG%s^BG has abandoned the race"), "")
diff --git a/qcsrc/common/scores.qh b/qcsrc/common/scores.qh
index 646638a80c..476d0dbbaa 100644
--- a/qcsrc/common/scores.qh
+++ b/qcsrc/common/scores.qh
@@ -34,6 +34,7 @@ REGISTER_SP(DMGTAKEN);
 REGISTER_SP(KILLS);
 REGISTER_SP(DEATHS);
 REGISTER_SP(SUICIDES);
+REGISTER_SP(TEAMKILLS);
 REGISTER_SP(FRAGS);
 
 REGISTER_SP(ELO);
diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc
index 0270462961..74a0e4df6c 100644
--- a/qcsrc/server/g_damage.qc
+++ b/qcsrc/server/g_damage.qc
@@ -47,7 +47,7 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype)
 		else
 		{
 			// teamkill
-			GameRules_scoring_add(attacker, KILLS, -1); // or maybe add a teamkills field?
+			GameRules_scoring_add(attacker, TEAMKILLS, 1);
 		}
 	}
 	else
diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc
index 11bc602382..1c5ade5fd2 100644
--- a/qcsrc/server/scores.qc
+++ b/qcsrc/server/scores.qc
@@ -96,7 +96,7 @@ void TeamScore_Spawn(float t, string name)
 	PlayerStats_GameReport_AddTeam(t);
 }
 
-float TeamScore_AddToTeam(float t, float scorefield, float score)
+float TeamScore_AddToTeam(int t, float scorefield, float score)
 {
 	entity s;
 
diff --git a/qcsrc/server/scores.qh b/qcsrc/server/scores.qh
index 79b65299f4..e2a57f43fb 100644
--- a/qcsrc/server/scores.qh
+++ b/qcsrc/server/scores.qh
@@ -52,7 +52,7 @@ float TeamScore_Add(entity player, float scorefield, float score);
  * NEVER call this if team has not been set yet!
  * Returns the new score.
  */
-float TeamScore_AddToTeam(float t, float scorefield, float score);
+float TeamScore_AddToTeam(int t, float scorefield, float score);
 
 /**
  * Returns a value indicating the team score (and higher is better).
diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc
index d46d95bd1c..8d87407e64 100644
--- a/qcsrc/server/scores_rules.qc
+++ b/qcsrc/server/scores_rules.qc
@@ -44,7 +44,10 @@ void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled
 	ScoreInfo_SetLabel_PlayerScore(SP_DEATHS,       "deaths",    SFL_LOWER_IS_BETTER);
 
 	if (!INDEPENDENT_PLAYERS)
+	{
 		ScoreInfo_SetLabel_PlayerScore(SP_SUICIDES,     "suicides",  SFL_LOWER_IS_BETTER);
+		ScoreInfo_SetLabel_PlayerScore(SP_TEAMKILLS,     "teamkills", SFL_LOWER_IS_BETTER);
+	}
 
 	if(score_enabled)
 		ScoreInfo_SetLabel_PlayerScore(SP_SCORE,        "score",     sprio);
-- 
2.39.5