/*
 * TK Police v1.0beta
 *
 * Author: <[NN]>Soul (sefanja_severin@hotmail.com, http://nn.kicks-ass.net/)
 * Ideas and code borrowed from Bud-froggy and [WHO]Them, all honor to them ;)
 *
 * Protects your server from lame TKers.
 *
 *
 *
 * Short description:
 * - Detect and response to team kills (TK) and team hurts (TH).
 * - Let the victim decide what to do with his TKer: kill, slap or forgive.
 * - Slay the TKer when he has reached the TK limit, and ban him (for a few
 *   minutes) when he has exceeded the limit.
 * - By default, 100hp team damage triggers 1 extra TK warning.
 *
 * IMPORTANT: mp_logdetail should be set to 0, 2 or 3 if you want this script
 * IMPORTANT: to detect TH as well.
 *
 */
 
#include <core>
#include <console>
#include <string>
#include <admin>
#include <adminlib>
 
#define ACCESS_CONSOLE 131072
#define ACCESS_FORGIVE 256
 
 
 
// Edit these
//
#define BAN_TIME 1            // Ban the TKer for ... minutes. 0 means permanent ban
#define TH_DAMAGE_LIMIT 100   // Amount of health (not armor) a THer may damage before he gets an extra TK warning
#define TK_LIMIT 3            // Number of unforgiven TKs that will be tolerated on your server during one map
//
// Stop editing
 
 
 
new STRING_VERSION[MAX_DATA_LENGTH] = "1.0beta";
 
new TkCount[MAX_PLAYERS]  = {0,...};	// Number of TK warnings (per player)
new ThDamage[MAX_PLAYERS] = {0,...};	// Amount of hp damage caused (per player)
 
new Attacker1 = 0;  // PlayerIndex of player with most recent TK warning
new Victim1   = 0;  // PlayerIndex of most recent TK victim (not TH victim)
new Attacker2 = 0;  // PlayerIndex of player with 2nd most recent TK warning
new Victim2   = 0;  // PlayerIndex of 2nd most recent TK victim (not TH victim)
 
 
 
/* * * * * * * * * * *
 * Helper Functions  *
 * * * * * * * * * * */
 
forgiveTker(i) {
	// Description of this function: Forgive attacker
	// - Determine whom to forgive: attacker1 or attacker2
	// - Check to see if both the victim and the attacker exist
	// - Substract 1 TK warning from the attacker's number of warnings so far
	// - Display a chat message: [TKPOLICE] <victim> has forgiven <attacker>. Warnings left: <i>
	// - Display the amount of warnings left, in the attacker's screen: Warnings left: <i>
	// - Let the server forget the victim as a victim, and the attacker as an attacker; they're ordinary players again
 
	new iIDA;
	new iIDV;
	new iSessionIDA;
	new sSessionIDA[MAX_NUMBER_LENGTH];
	new NameA[MAX_NAME_LENGTH];
	new NameV[MAX_NAME_LENGTH];
 
	new Message[MAX_TEXT_LENGTH];
 
	if (i==1) {
		iIDA = Attacker1;
		iIDV = Victim1;
	} else {
		iIDA = Attacker2;
		iIDV = Victim2;
	}
 
	if (iIDA == 0 || iIDV == 0) {
		return PLUGIN_CONTINUE;
	}
 
	if ( !playerinfo(iIDA,NameA,MAX_NAME_LENGTH,iSessionIDA) ) {
		return PLUGIN_CONTINUE;
	}
	numtostr(iSessionIDA, sSessionIDA);
 
	if ( !playerinfo(iIDV,NameV,MAX_NAME_LENGTH) ) {
		return PLUGIN_CONTINUE;
	}
 
	TkCount[iIDA] -= 1;
 
	snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s has forgiven %s. Warnings left: %i", NameV, NameA, TK_LIMIT - TkCount[iIDA]);
	say(Message);
	snprintf(Message, MAX_TEXT_LENGTH, "Warnings left: %i", TK_LIMIT - TkCount[iIDA]);
	messageex(sSessionIDA, Message, print_tty);
 
	if (i==1) {
		Victim1   = Victim2;
		Attacker1 = Attacker2;
	}
	Attacker2 = 0;
	Victim2   = 0;
 
	return PLUGIN_CONTINUE;
}
 
killTker(i) {
	// Description of this function: Slay attacker
	// - Determine whom to slay: attacker1 or attacker2
	// - Check to see if both the victim and the attacker exist
	// - Slay the atacker
	// - Display a chat message: [TKPOLICE] <victim> decided to kill <attacker> for TK revenge
	// - Let the server forget the victim as a victim, and the attacker as an attacker; they're ordinary players again
 
	new iIDA;
	new iIDV;
	new iSessionIDA;
	new sSessionIDA[MAX_NUMBER_LENGTH];
	new NameA[MAX_NAME_LENGTH];
	new NameV[MAX_NAME_LENGTH];
 
	new Message[MAX_TEXT_LENGTH];
 
	if (i==1) {
		iIDA = Attacker1;
		iIDV = Victim1;
	} else {
		iIDA = Attacker2;
		iIDV = Victim2;
	}
 
	if (iIDA == 0 || iIDV == 0) {
		return PLUGIN_CONTINUE;
	}
 
	if ( !playerinfo(iIDA,NameA,MAX_NAME_LENGTH,iSessionIDA) ) {
		return PLUGIN_CONTINUE;
	}
	numtostr(iSessionIDA, sSessionIDA);
 
	if ( !playerinfo(iIDV,NameV,MAX_NAME_LENGTH) ) {
		return PLUGIN_CONTINUE;
	}
 
	slay(sSessionIDA);
	snprintf(Message,MAX_TEXT_LENGTH,"[TKPOLICE] %s decided to kill %s for TK revenge", NameV, NameA);
	say(Message);
 
	if (i==1) {
		Victim1   = Victim2;
		Attacker1 = Attacker2;
	}
	Attacker2 = 0;
	Victim2   = 0;
 
	return PLUGIN_CONTINUE;
}
 
slapTker(i) {
	// Description of this function: Slap attacker
	// - Determine whom to slap: attacker1 or attacker2
	// - Check to see if both the victim and the attacker exist
	// - Slap the attacker 5 times / 30 hp
	// - Display a chat message: [TKPOLICE] <victim> decided to slap <attacker> 30 hp for TK revenge
	// - Let the server forget the victim as a victim, and the attacker as an attacker; they're ordinary players again
 
	new iIDA;
	new iIDV;
	new iSessionIDA;
	new sSessionIDA[MAX_NUMBER_LENGTH];
	new NameA[MAX_NAME_LENGTH];
	new NameV[MAX_NAME_LENGTH];
 
	new Message[MAX_TEXT_LENGTH];
 
	if (i==1) {
		iIDA = Attacker1;
		iIDV = Victim1;
	} else {
		iIDA = Attacker2;
		iIDV = Victim2;
	}
 
	if (iIDA == 0 || iIDV == 0) {
		return PLUGIN_CONTINUE;
	}
 
	if ( !playerinfo(iIDA,NameA,MAX_NAME_LENGTH,iSessionIDA) ) {
		return PLUGIN_CONTINUE;
	}
	numtostr(iSessionIDA, sSessionIDA);
 
	if ( !playerinfo(iIDV,NameV,MAX_NAME_LENGTH) ) {
		return PLUGIN_CONTINUE;
	}
 
	slap(sSessionIDA); slap(sSessionIDA); slap(sSessionIDA); slap(sSessionIDA); slap(sSessionIDA); slap(sSessionIDA);
	snprintf(Message,MAX_TEXT_LENGTH,"[TKPOLICE] %s decided to slap %s 30hp for TK revenge", NameV, NameA);
	say(Message);
 
	if (i==1) {
		Victim1   = Victim2;
		Attacker1 = Attacker2;
	}
	Attacker2 = 0;
	Victim2   = 0;
 
	return PLUGIN_CONTINUE;
}
 
thPunish(iIDA) {
	// Description of this function: Punish THer
	// - Add 1 TK warning to the attacker's number of TK warnings
	// - Based on the number of TK warnings, perform one the folowwing actions:
	//   o Less then TK_LIMIT: Display a warning
	//   o TK_LIMIT reached: kill the attacker
	//   o TK_LIMIT exceeded: ban the attacker for BAN_TIME minutes
 
	new iSessionIDA;
	new sSessionIDA[MAX_NUMBER_LENGTH];
	new NameA[MAX_NAME_LENGTH];
 
	new Message[MAX_TEXT_LENGTH];
 
	if ( !playerinfo(iIDA,NameA,MAX_NAME_LENGTH,iSessionIDA) ) {
		return PLUGIN_CONTINUE;
	}
	numtostr(iSessionIDA, sSessionIDA);
 
	TkCount[iIDA] += 1;
 
	if (TkCount[iIDA] < TK_LIMIT) {
		snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s: TK warning %i of %i (%ihp team damage)", NameA, TkCount[iIDA], TK_LIMIT, TH_DAMAGE_LIMIT);
		say(Message);
		snprintf(Message, MAX_TEXT_LENGTH, "TK warning %i of %i (%ihp team damage)", TkCount[iIDA], TK_LIMIT, TH_DAMAGE_LIMIT);
		messageex(sSessionIDA, Message, print_tty);
	} else if (TkCount[iIDA] == TK_LIMIT) {
		slay(NameA);
		snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s violated %i TK warnings (%ihp team damage)", NameA, TK_LIMIT, TH_DAMAGE_LIMIT);
		say(Message);
		snprintf(Message, MAX_TEXT_LENGTH, "You violated %i TK warnings (%ihp team damage)", TK_LIMIT, TH_DAMAGE_LIMIT);
		messageex(sSessionIDA, Message, print_tty);
	} else {
		snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s exceeded %i TK warnings (%ihp team damage)", NameA, TK_LIMIT, TH_DAMAGE_LIMIT);
		messageex(sSessionIDA, Message, print_console);
		say(Message);
		ban(sSessionIDA,BAN_TIME);
	}
 
	return PLUGIN_CONTINUE;
}
 
tkPunish(iIDA,iIDV) {
	// Description of this function: Punish TKer
	// - Add 1 TK warning to the attacker's number of TK warnings
	// - Based on the number of TK warnings, perform one the folowwing actions:
	//   o Less than TK_LIMIT: Display a warning
	//   o TK_LIMIT reached: kill the attacker
	//   o TK_LIMIT exceeded: ban the attacker for BAN_TIME minutes
	// - Tell the server who are the last attacker and victim (so it knows on whom to perform future actions, like slap, slay or forgive)
 
	new iSessionIDA;
	new iSessionIDV;
	new sSessionIDA[MAX_NUMBER_LENGTH];
	new sSessionIDV[MAX_NUMBER_LENGTH];
	new NameA[MAX_NAME_LENGTH];
	new NameV[MAX_NAME_LENGTH];
 
	new Message[MAX_TEXT_LENGTH];
 
	if ( !playerinfo(iIDA, NameA, MAX_NAME_LENGTH, iSessionIDA) ) {
		return PLUGIN_CONTINUE;
	}
	numtostr(iSessionIDA,sSessionIDA);
 
	if ( !playerinfo(iIDV, NameV, MAX_NAME_LENGTH, iSessionIDV) ) {
		return PLUGIN_CONTINUE;
	}
	numtostr(iSessionIDV,sSessionIDV);
 
	TkCount[iIDA] += 1;
 
	if (TkCount[iIDA] < TK_LIMIT) {
		snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s: TK warning %i of %i. %s may say !SLAP, !KILL or !FORGIVE.", NameA, TkCount[iIDA], TK_LIMIT, NameV);
		say(Message);
		snprintf(Message, MAX_TEXT_LENGTH, "TK warning %i of %i", TkCount[iIDA], TK_LIMIT);
		messageex(sSessionIDA, Message, print_tty);
		messageex(sSessionIDV, "Say !SLAP, !KILL or !FORGIVE", print_tty);
	} else if (TkCount[iIDA] == TK_LIMIT) {
		slay(sSessionIDA);
		snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s violated %i TK warnings. %s may say !FORGIVE.", NameA, TK_LIMIT, NameV);
		say(Message);
		snprintf(Message, MAX_TEXT_LENGTH, "You violated %i TK warnings", TK_LIMIT);
		messageex(sSessionIDA, Message, print_tty);
		messageex(sSessionIDV, "You may say !FORGIVE", print_tty);
	} else {
		snprintf(Message, MAX_TEXT_LENGTH, "[TKPOLICE] %s exceeded %i TK warnings", NameA, TK_LIMIT);
		messageex(sSessionIDA, Message, print_console);
		say(Message);
		ban(sSessionIDA,BAN_TIME);
	}
 
	Attacker2 = Attacker1;
	Victim2   = Victim1;
	Attacker1 = iIDA;
	Victim1   = iIDV;
 
	return PLUGIN_CONTINUE;
}
 
 
 
/* * * * * * * * * *
 * LogD Functions  *
 * * * * * * * * * */
 
public logd_ThDetect(HLCommand,HLData,HLUserName,UserIndex) {
	// Description of this function: POLICE TH
	// - Fires when one player attacks another
	// - Reads the log line, and determines if it really was a team attack
	// - Adds the amount of hp damaged to the amount of hp the attacker already has damaged
	// - If the hp damage exceeds TH_DAmAGE_LIMIT, thPunish() is called
	// - TH damage of the attacker is reset to 0
 
	new Data[MAX_DATA_LENGTH];
 
	new iIDA;
	new iIDV;
	new sIDA[MAX_NUMBER_LENGTH];
	new sIDV[MAX_NUMBER_LENGTH];
	new Name[MAX_NAME_LENGTH];
	new SessionID;
	new WONID;
	new TeamA;
	new TeamV;
	new iDamage;
	new sDamage[MAX_NUMBER_LENGTH];
 
	new dummy[MAX_DATA_LENGTH];
	new dummy1[MAX_DATA_LENGTH];
 
	convert_string(HLData, Data, MAX_DATA_LENGTH);
 
	strsplit(Data, "#", dummy,MAX_DATA_LENGTH, dummy1,MAX_DATA_LENGTH);
	strsplit(dummy, " ", sIDA,MAX_NUMBER_LENGTH, sIDV,MAX_NUMBER_LENGTH);
	strsplit(dummy1, " ", sDamage,MAX_NUMBER_LENGTH);
 
	iIDA = strtonum(sIDA);
	iIDV = strtonum(sIDV);
	iDamage = strtonum(sDamage);
 
	if (iIDA == iIDV) {
		return PLUGIN_HANDLED;
	}
	if ( !playerinfo(iIDA,Name,MAX_NAME_LENGTH,SessionID,WONID,TeamA) ) {
		return PLUGIN_HANDLED;
	}
	if ( !playerinfo(iIDV,Name,MAX_NAME_LENGTH,SessionID,WONID,TeamV) ) {
		return PLUGIN_HANDLED;
	}
	if (TeamV != TeamA) {
		return PLUGIN_HANDLED;
	}
 
	ThDamage[iIDA] = ThDamage[iIDA] + iDamage;
	if (ThDamage[iIDA] > TH_DAMAGE_LIMIT) { 
		thPunish(iIDA);
		ThDamage[iIDA] = 0;
	}
 
	return PLUGIN_HANDLED;
}
 
public logd_TkDetect(HLCommand,HLData,HLUserName,UserIndex) {
	// Description of this function: Detect TK
	// - Fires when one player kills another
	// - Reads the log line, and determines if it really was a TK
	// - tkPunish() is called
 
	new Data[MAX_DATA_LENGTH];
 
	new iIDA;
	new iIDV;
	new sIDA[MAX_NUMBER_LENGTH];
	new sIDV[MAX_NUMBER_LENGTH];
	new Name[MAX_NAME_LENGTH];
	new SessionID;
	new WONID;
	new TeamA;
	new TeamV;
 
	convert_string(HLData,Data,MAX_DATA_LENGTH);
	strsplit(Data, " ", sIDA,MAX_NUMBER_LENGTH, sIDV,MAX_NUMBER_LENGTH);
 
	iIDA = strtonum(sIDA);
	iIDV = strtonum(sIDV);
 
	if (iIDA == iIDV) {
		return PLUGIN_HANDLED;
	}
	if ( !playerinfo(iIDA,Name,MAX_NAME_LENGTH,SessionID,WONID,TeamA) ) {
		return PLUGIN_HANDLED;
	}
	if ( !playerinfo(iIDV,Name,MAX_NAME_LENGTH,SessionID,WONID,TeamV) ) {
		return PLUGIN_HANDLED;
	}
	if (TeamV != TeamA) {
		return PLUGIN_HANDLED;
	}
 
	tkPunish(iIDA,iIDV);
 
	return PLUGIN_HANDLED;
}
 
 
 
/* * * * * * * * * * * *
 * Admin Mod Functions *
 * * * * * * * * * * * */
 
public admin_forgivetk(HLCommand,HLData,HLUserName,UserIndex) {
	// Description of this function: Forgive attacker
	// - Determines whom to forgive: attacker1 or attacker2
	// - forgiveTker() is called
	// - Echos the command as a chat message on the server
 
	new Command[MAX_COMMAND_LENGTH];
	new Data[MAX_DATA_LENGTH];
	new User[MAX_NAME_LENGTH];
 
	convert_string(HLCommand, Command, MAX_COMMAND_LENGTH);
	convert_string(HLData, Data, MAX_DATA_LENGTH);
	convert_string(HLUserName, User, MAX_NAME_LENGTH);
 
	if ( strcmp(Data,"new") ) {
		forgiveTker(1);
	} else if ( strcmp(Data,"old") ) {
		forgiveTker(2);
	} else {
		selfmessage("Syntax: admin_forgivetk <old|new>");
		return PLUGIN_HANDLED;
	}
 
	say_command(User,Command,Data);
 
	return PLUGIN_HANDLED;
}
 
 
 
/* * * * * * * * * * *
 * Handle Functions  *
 * * * * * * * * * * */
 
public HandleSay(HLCommand,HLData,HLUserName,UserIndex) {
	// Description of this function: Let the victim decide what to do with his TKer
	// - Fires when a player says something
	// - Reads what the user says, if he says !slap, !kill or !forgive:
	// - The server will determine if the user really is a victim
	// - The right function will be called to perform an action on the victim's attacker
 
	new Data[MAX_DATA_LENGTH];
 
	convert_string(HLData, Data, MAX_DATA_LENGTH);
	strstripquotes(Data);
 
	if (Data[0] != '!') {
		return PLUGIN_CONTINUE;
	}
 
	if ( strcasecmp(Data,"!forgive") == 0 ) {
		if (UserIndex == Victim1) {
			forgiveTker(1);
		} else if (UserIndex == Victim2) {
			forgiveTker(2);
		}
	} else if ( strcasecmp(Data,"!kill") == 0 ) {
		if (UserIndex == Victim1) {
			killTker(1);
		} else if (UserIndex == Victim2) {
			killTker(2);
		}
	} else if ( strcasecmp(Data,"!slap") == 0 ) {
		if (UserIndex == Victim1) {
			slapTker(1);
		} else if (UserIndex == Victim2) {
			slapTker(2);
		}
	}
 
	return PLUGIN_CONTINUE;
}
 
 
public plugin_connect(HLUserName, HLIP, UserIndex) {
	// Description of this function: Reset certain variables as players connect
	// - As players join the server, their TK warnings and stuff will be reset (actually not their own TK warnings, but the TK warnings of their slot)
 
	if (UserIndex >= 1 && UserIndex <= MAX_PLAYERS) {
		TkCount[UserIndex]  = 0;
		ThDamage[UserIndex] = 0;
		if (UserIndex == Attacker1 || UserIndex == Victim1) {
			Attacker1 = 0;
			Victim1   = 0;
		}
		if (UserIndex == Attacker2 || UserIndex == Victim2) {
			Attacker2 = 0;
			Victim2   = 0;
		}
	}
 
	return PLUGIN_CONTINUE;
}
 
public plugin_disconnect(HLUserName, UserIndex) {
	// Description of this function: Reset certain variables as players disconnect
	// - As players leave the server, their TK warnings and stuff will be reset (actually not their own TK warnings, but the TK warnings of their slot)
 
	if (UserIndex >= 1 && UserIndex <= MAX_PLAYERS) {
		TkCount[UserIndex]  = 0;
		ThDamage[UserIndex] = 0;
		if (UserIndex == Attacker1 || UserIndex == Victim1) {
			Attacker1 = 0;
			Victim1   = 0;
		}
		if (UserIndex == Attacker2 || UserIndex == Victim2) {
			Attacker2 = 0;
			Victim2   = 0;
		}
	}
 
	return PLUGIN_CONTINUE;
}
 
public plugin_init() {
	// Description of this function: Initialize this script
	// - Some plugin-register stuff
	// - Check the value of cvar mp_logdetail, and correct it if needed (this will not affect the server's log)
 
	new MpLogdetail;
 
	plugin_registerinfo("TK Police - by <[NN]>Soul", "Protects your server from lame TKers.", STRING_VERSION);
 
	plugin_registercmd("logd_TkDetect", "logd_TkDetect", ACCESS_CONSOLE, "");
	plugin_registercmd("logd_ThDetect", "logd_ThDetect", ACCESS_CONSOLE, "");
	plugin_registercmd("admin_forgivetk", "admin_forgivetk", ACCESS_FORGIVE, "admin_forgivetk <new|old>: forgives most or 2nd most recent TKer.");
	plugin_registercmd("say", "HandleSay", ACCESS_ALL, "say !kill, !slap or !forgive: what shall we do with your TKer?");
 
	exec("logd_reg 57 admin_command logd_TkDetect");
	exec("logd_reg 58 admin_command logd_ThDetect");
 
	MpLogdetail = getvar("mp_logdetail");
	if (MpLogdetail == 0) {
		setstrvar("mp_logdetail","2");
	} else if (MpLogdetail == 1) {
		log("[TKPOLICE] Warning: mp_logdetail is set to 1. Please set it to 0, 2 or 3 so I can detect team hurts as well.");
	}
 
	return PLUGIN_CONTINUE;
}