/*
        Dessloks's Friendly Fire Monitor (FFMon)
        Version 1.30
 
        See readme, changelog and howto for more information.
*/
 
#pragma dynamic  24576 // 16384
 
#include <core>
#include <console>
#include <string>
#include <admin>
#include <adminlib>
 
/*
 * Im folgenden Block werden die moeglichen Bestrafungen eingestellt.
 * Die einzelnen Zahlen sind zu addieren und spaeter im Konfigurationsblock
 * einzustellen
 */
 
#define ACCESS_FFMON          ACCESS_BAN
 
#define ACTION_NONE           0
#define ACTION_KICK           1
#define ACTION_BAN            2
#define ACTION_SLAP           4
#define ACTION_BURY           8
#define ACTION_CHICKEN       16
#define ACTION_OVERRIDE_BAN  32
#define ACTION_SLAY          64
 
/* If you want to use adminmod to change to the next map when a skunk is detected
   set this define to 1.  Using adminmod for changing to the nextmap seems to get the 
   map cycle out of order.  The alternative is to set a server cvar that makes the engine
   think the round is over and switch to the next map.
*/ 
#define FORCE_NEXTMAP_ADMINMOD 0
 
/* If you want to turn the debugging on, which logs to the server console */
/* and to your log files, set this to 1.  0 is off. */
#define DEBUG 0
 
/* To use adminmod's beta menu feature, set this to 1.  For adminmod 2.50.50 you need
   to put amv_enable_beta "menu 1" in your adminmod.cfg or server.cfg file.
   Set to 0 to use the old experimental method. */
#define USE_ADMINMOD_MENUS 1
 
 
#define ACCESS_CONSOLE 131072
 
new STRING_VERSION[MAX_DATA_LENGTH] = "$Revision: 1.20 $";
 
#define TD_CLEAN_SLATE  0
#define TK_CLEAN_SLATE  0
 
#define USER_ID_INVALID -1
 
#define SKUNK_TYPE_NORMAL       0
#define SKUNK_TYPE_TRADITIONAL  1
 
new g_TdCount[MAX_PLAYERS + 1]   = {TD_CLEAN_SLATE,...};
new g_TkCount[MAX_PLAYERS + 1]   = {TK_CLEAN_SLATE,...};
new g_tkedby[MAX_PLAYERS + 1]    = {USER_ID_INVALID,...};
new g_Chickened[MAX_PLAYERS + 1] = {0};
 
// TK revenge action stored here if the attacker is dead
// revenge is taken out the start of the next round
new g_TkRevenge[MAX_PLAYERS + 1] = {ACTION_NONE};
 
new g_RoundStart     = 0;
new g_RoundSpawn     = 0;
new g_RoundStartTimer= 0;
new g_VoteStarted    = 0;    
new g_CtScore        = 0;
new g_TScore         = 0;
new g_DebugMessages  = 0;
 
new g_LastTkBan[MAX_AUTHID_LENGTH];
 
/* Menu defines and variables */
#define MAX_TK_OPTIONS        10
 
#define MENU_STATE_OPEN       0
#define MENU_STATE_KILLED     1
 
new g_TkMenuAction[MAX_TK_OPTIONS + 1][MAX_TEXT_LENGTH];
new g_MenuStates[MAX_PLAYERS + 1][3];
 
/* 
*
*  Configuration variables available through commands and their default values
*
*/
 
/*
 * german comments by [-=THH=-]Stillsetzhut (stillsetzhut@thh-clan.de) for adminmod.de
 *
 * Der Konfigurationblock. Hier koennen die Grundeinstellungen des FFMon vorgenommen werden.
 */
 
new g_Enabled               = 1;     /* Schaltet das komplette Plugin ein */
new g_TkProtection          = 1;     /* TK Schutz ein/aus */
new g_TkLimit               = 3;     /* TK limit bevor gebannt wird */
new g_TkLimitBanTime        = 30;    /* Bannzeit bei Erreichen des TK Limits */
new g_TkMenu                = 1;     /* schaltet das TK Menu ein */
new g_TkReset               = 0;     /* Setzt das TK Konto am Ende der Runde auf 0 */
 
new g_TkSave                = 1;     /* Speichert TK's */
new g_TkSaveTime            = 24;    /* Stunden in denen TK's gespeichert werden */
new g_TkSaveLimit           = 6;     /* wieviele TK'S sind in der TkSaveTime erlaubt */
new g_TkSaveBanTime         = 1440;  /* Bannzeit bei Erreichen von TKSavelimit (def: 1440 min = 24 Stunden) */
new g_TkSavePurgeTime       = 24;    /* Stunden nach denen die gespeicherten TK's bereinigt werden */
 
new g_TdProtection          = 1;     /* TD Schutz ein/aus */
new g_TdLimit               = 5;     /* TD limit wird gleichgesetzt = 1 TK */
new g_TdAction              = ACTION_SLAY; /* Massnahme bei TD's */
 
new g_AllowPunish           = 47;    /* HIER KOMMT DIE ZAHL HINEIN; DIE IHR AUS DEM OBEREN BLOCK ERRECHNET HABT  */
new g_UserSlapCount         = 1;     /* Zahl der Schlaege wenn der TK'te slap nutzt */
new g_UserOverrideLevel     = 0;     /* Rechtelevel um einen TK Bann aufzuheben */
new g_BuryGlowTime          = 30;    /* Leuchtzeit bei TK Bestrafung bury */
 
new g_RoundStartTime        = 10;    /* Rundenstartzeit mit verschaerfter Bestrafung */
new g_RoundStartBanTime     = 300;   /* Bannzeit bei TK's in der Rundenstartzeit */
new g_RoundStartBanTk       = 1;     /* Bann bei TK's in der Rundenstartzeit erlauben */
new g_RoundStartTdSlap      = 1;     /* Schlagen bei TD's in der Rundenstartzeit */
new g_RoundStartTdSlapTimes = 5;     /* Anzahl der Schlaege bei TD's in der Rundenstartzeit */
new g_RoundStartTdAction    = ACTION_BAN; /* Art der Bestrafung bei TD's in der Rundenstartzeit */
new g_RoundStartTdCount     = 3;     /* TD limit in der Rundenstartzeit bevor gebannt wird */
new g_RoundStartTdBanTime   = 2;     /* Bannzeit fuer TD's in der Rundenstartzeit*/
 
new g_SpawnShotProtection   = 0;     /* Schnellschuss Schutz */
new g_SpawnShotAction       = ACTION_KICK; /* Bestrafung gegen Schnellschuesse */
new g_SpawnShotTime         = 2;     /* Zeit des Schutzes gegen Schnellschuesse */
new g_SpawnShotBanTime      = 10;    /* Bannzeit gegen Schnellschuesse */
 
new g_SkunkProtection       = 1;     /* wechselt zur naechsten Map bei Dominanz eines Teams */
new g_SkunkType             = SKUNK_TYPE_NORMAL;     /* Typ der Teamueberwachung */
new g_SkunkLimit            = 10;    /* Win Differenz bei der zur naechsten Map gewechselt wird */
 
new g_Immunity              = 0;     /* admin Immunitaet ein/aus */
new g_ImmunityLevel         = 65536; /* Rechtelevel fuer Immunitaet */
 
new g_DisplayScores         = 1;     /* Zeigt den Spielstand am Ende jeder Runde */
new g_LogMessages           = 1;     /* Schreibt Ausgaben des FFMon in die logfiles */
new g_DisplayConsgreet      = 1;     /* Schaltet die Konsolenbegruessung ein/aus */
new g_BlockAttackMessages   = 1;     /* Unterdrueckt das Schreiben von Beschaedigungen in die Lofiles */
 
new g_StartVote             = 1;     /* Startet einen Mapvote vor Erreichen von mp_winlimit  */
new g_StartVoteRound        = 2;     /* Zahl der Runden vor Erreichen von mp_winlimit um Mapvote auszuloesen */
 
#if DEBUG == 1
new g_HeapStart = 0;
#endif
 
/*
*
*
*  Helper functions
*
*/
 
HeapCheckStart()
{
    #if DEBUG == 1
    g_HeapStart = heapspace();
    #endif
}
 
HeapCheckStop(sFunctionName[])
{
    strlen(sFunctionName);
 
    #if DEBUG == 1
    new sDebugMessage[MAX_DATA_LENGTH];
    new iHeap2;
    iHeap2 = heapspace();
    snprintf (sDebugMessage, 
              MAX_DATA_LENGTH, 
              "%s: Heapspace started with %i, ended with %i.", 
              sFunctionName, 
              g_HeapStart, 
              iHeap2);
    LogMessage(sDebugMessage);
    #endif
}
 
DebugMessage(sMessage[])
{
    if (g_DebugMessages)
    {
        LogMessage(sMessage);
    }
}
 
LogMessage(sMessage[])
{
    new sRealMessage[MAX_DATA_LENGTH];
 
    if (g_LogMessages)
    {
        snprintf(sRealMessage, MAX_DATA_LENGTH, "[FFMon] %s", sMessage);
        log(sRealMessage);
    }
}
 
SayMessage(sMessage[])
{
    new sRealMessage[MAX_DATA_LENGTH];
 
    snprintf(sRealMessage, MAX_DATA_LENGTH, "<FFMon> %s", sMessage);
 
    //typesay( sRealMessage, 10, 255, 255, 255 );
    say(sRealMessage);
}
 
IsImmune(sUser[])
{
    new iImmune = 0;
 
    if (g_Immunity)
    {
        if (access(g_ImmunityLevel, sUser) != 0)
        {
            iImmune = 1;
        }
    }
 
    return iImmune;
}
 
//
// Generate the file name for this user
//
GetTkFileName(sWONID[], sFileName[])
{
    snprintf(sFileName, MAX_DATA_LENGTH, "ffmon/%s.tks", sWONID);
}
 
//
// remove the tk file for this WONID
//
RemoveTkFile(sWONID[])
{
    new sFileName[MAX_DATA_LENGTH];
 
    GetTkFileName(sWONID, sFileName);
 
    deletefile(sFileName);
 
    RemoveFromMasterTkFile(sWONID);
}
 
//
// This function should only be called after AddTk, RemoveTk
// or AgeTks.  That will ensure that the list is up to date
// since all this function does is get a line count
//
GetTkCount(sWONID[])
{
    new sFileName[MAX_DATA_LENGTH];
 
    GetTkFileName(sWONID, sFileName);
 
    return filesize(sFileName);
}
 
// 
// Read in all currently active TKs for the given file
//
ReadTkInfo(sFileName[], iTks[], iStartTk, iFileOffset)
{
    new sData[MAX_DATA_LENGTH];
    new iCurLine = iFileOffset;
    new iCurTime = systemtime();
    new iCurPos = iStartTk;
 
    if (fileexists(sFileName))
    {
        while (readfile(sFileName, sData, iCurLine, MAX_DATA_LENGTH))
        {
            new iTkTime = strtonum(sData);
            if (iTkTime + (g_TkSaveTime * 60 * 60) > iCurTime)
            {
                // tk hasn't expired yet.  save
                iTks[iCurPos] = iTkTime;
            }
            else
            {
                // tks are in order in the file, so once we find one that is
                // expired, they all will be expired after this one.
                // so we are done
                break;
            }
 
            iCurLine++;
            iCurPos++;
        }
    }
 
    return iCurPos;
}
 
//
// returns true if user has reached the limit
//
AddTk(sWONID[])
{
    new sFileName[MAX_DATA_LENGTH];
    new iTks[MAX_DATA_LENGTH] = {0};
    new iCount = 0;
 
    if (g_TkSave)
    {
        GetTkFileName(sWONID, sFileName);
 
        // stick the new one in the head of the list
        iTks[0] = systemtime();
 
        // read the data from the file, starting at the first line
        iCount = ReadTkInfo(sFileName, iTks, 1, 1);
 
        // now save the TK info
        SaveTkInfo(sFileName, iTks, iCount);
 
        // add to the master list
        AddToMasterTkFile(sWONID);
    }
}
 
//
// Function removes the last TK from the file
//
RemoveTk(sWONID[])
{
    // we pass in 2 so that we skip the first entry in the file
    // the first entry in the file is always the most recent
    AgeTksEx(sWONID, 2);
}
 
AgeTks(sWONID[])
{
    // pass in 1 for the offset meaning to start with the first 
    // entry in the file, not skipping any
    AgeTksEx(sWONID, 1);
}
 
//
// age the TKs in the file.  Offset specifies where to start
// in the file.
//
AgeTksEx(sWONID[], iOffset, iRemove = 1)
{
    new sFileName[MAX_DATA_LENGTH];
    new iTks[MAX_DATA_LENGTH] = {0};
    new iCount = 0;
 
    GetTkFileName(sWONID, sFileName);
 
    if (fileexists(sFileName))
    {
        // read the info from the file for active TKs
        iCount = ReadTkInfo(sFileName, iTks, 0, iOffset);
 
        SaveTkInfo(sFileName, iTks, iCount);
 
        if ( (iCount == 0) &&
             (iRemove) )
        {
            RemoveFromMasterTkFile(sWONID);
        }
    }
}
 
//
// Save off the TK info 
// if there isn't any, remove the file
//
SaveTkInfo(sFileName[], iTks[], iCount)
{
    new sData[MAX_DATA_LENGTH];
 
    if (iCount > 0)
    {
        // clear the file
        resetfile(sFileName);
 
        // write out all the TKs for this ID
        for (new i = 0; i < iCount; i++)
        {
            snprintf(sData, MAX_DATA_LENGTH, "%d", iTks[i]);
 
            writefile(sFileName, sData);
        }
    }
    else
    {
        // no more TKs so just delete the file
        deletefile(sFileName);
    }
}
 
CheckPeriodicPurge()
{
    // maybe this should be a vault.ini entry
    new sVaultData[MAX_DATA_LENGTH];
    new iCurTime = systemtime();
    new iLastTime = iCurTime;
 
    if (get_vaultdata("FFMON_TKSAVELASTPURGE", sVaultData, MAX_DATA_LENGTH ) != 0)
    {
        iLastTime = strtonum(sVaultData);
 
        // check to see if the purge time has elapsed or the maximum 
        // amount of time between purges has elapsed.  This check forces
        // a purge at least once a week.
        if ( ((iLastTime + (g_TkSavePurgeTime * 60 * 60)) < iCurTime) ||
             ((iLastTime + (7 * 24 * 60 * 60)) < iCurTime) )
        {
            // time to do a purge
            PurgeTkFiles();
            UpdateLastPurgeTime();
        }
    }
    else
    {
        // first time
        UpdateLastPurgeTime();
    }
}
 
UpdateLastPurgeTime()
{
    SetVaultInt("FFMON_TKSAVELASTPURGE", systemtime());
}
 
//
// Walk all of the tk files and age them... we do this
// once a day to try and keep the number of files down.
//
PurgeTkFiles()
{
    new sMasterFilename[MAX_TEXT_LENGTH];
    new sFilename[MAX_TEXT_LENGTH];
    new sData[MAX_DATA_LENGTH];
    new iCurLine = 1;
    new iCurTime = systemtime();
    new iTotal = 0;
    new iRemoved = 0;
 
    // get the current master file name
    GetMasterTkFileName(sMasterFilename);
 
    if (fileexists(sMasterFilename))
    {
        while (readfile(sMasterFilename, sData, iCurLine, MAX_DATA_LENGTH))
        {
            if (strlen(sData) > 0)
            {
                iTotal++;
 
                // get the filename for later
                GetTkFileName(sData, sFilename);
 
                // age the tks for this guy
                AgeTksEx(sData, 1, 0);
 
                // if the file is gone, then remove from the master list
                if (!fileexists(sFilename))
                {
                    writefile(sMasterFilename, "", iCurLine);
                    iRemoved++;
                }
            }
 
            iCurLine++;
        }
    }
 
    snprintf(sData, 
             MAX_DATA_LENGTH, 
             "%d of %d entries removed in %d seconds w/%d players online",
             iRemoved,
             iTotal,
             iCurTime - systemtime(),
             playercount());
    LogMessage(sData);
}
 
// 
// add this WONID to the master file that keeps track of all the
// TK files we have.  We do this so we can purge the files at
// some later date.  The reason we need this stupid master file
// is because no functionality exists to walk the files in the 
// directory on linux.
//
AddToMasterTkFile(sWONID[])
{
    new sFilename[MAX_TEXT_LENGTH];
    new sData[MAX_DATA_LENGTH];
    new iCurLine = 1;
    new iFound = 0;
    new iEmptyLine = -1;
 
    // get the current master file name
    GetMasterTkFileName(sFilename);
 
    if (fileexists(sFilename))
    {
        while (readfile(sFilename, sData, iCurLine, MAX_DATA_LENGTH))
        {
            new sMessage[MAX_DATA_LENGTH];
 
            if (streq(sData, sWONID))
            {
                // already in the file
                iFound = 1;
                break;
            }
 
            new iLen = strlen(sData);
 
            if ( (iLen == 0) && 
                 (iEmptyLine == -1) )
            {
                // found an open slot
                // save this line for later
                snprintf(sMessage, MAX_DATA_LENGTH, "AddToMasterTkFile - Found empty line %d", iCurLine);
                LogMessage(sMessage);
 
                iEmptyLine = iCurLine;
            }
 
            iCurLine++;
        }
    }
 
    // did not find anyplace for it, append
    if (!iFound)
    {
        DebugMessage("AddToMasterTkFile - new AuthID, adding to file");
        writefile(sFilename, sWONID, iEmptyLine);
    }
}
 
// 
// remove a wonid from the file
// because file access is limited to a few functions
// we just set the line to be blank if we remove it
//
RemoveFromMasterTkFile(sWONID[])
{
    new sFilename[MAX_TEXT_LENGTH];
    new sData[MAX_DATA_LENGTH];
    new iCurLine = 1;
 
    // get the current master file name
    GetMasterTkFileName(sFilename);
 
    if (fileexists(sFilename))
    {
        while (readfile(sFilename, sData, iCurLine, MAX_DATA_LENGTH))
        {
            if (streq(sData, sWONID))
            {
                // already in the file
                writefile(sFilename, "", iCurLine);
                break;
            }
 
            iCurLine++;
        }
    }
}
 
GetMasterTkFileName(sFilename[])
{
    strcpy(sFilename, "ffmon\tks.def", MAX_TEXT_LENGTH);
}
 
#if USE_ADMINMOD_MENUS != 1
BindToMenuselect(iUserIndex)
{
    new sTarget[MAX_NAME_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];
    new iSessionID;
    new iTeam;
    new iWONID;
    new iDead;
 
    playerinfo(iUserIndex, sTarget, MAX_NAME_LENGTH, iSessionID, iWONID, iTeam, iDead, sAuthID);
 
    execclient(sTarget, "bind 1 ^"menuselect 1^";bind 2 ^"menuselect 2^";bind 3 ^"menuselect 3^""); 
    execclient(sTarget, "bind 4 ^"menuselect 4^";bind 5 ^"menuselect 5^";bind 6 ^"menuselect 6^""); 
    execclient(sTarget, "bind 7 ^"menuselect 7^";bind 8 ^"menuselect 8^";bind 9 ^"menuselect 9^""); 
    execclient(sTarget, "bind 0 ^"menuselect 0^""); 
}
 
BindToSlot(iUserIndex)
{
    new sTarget[MAX_NAME_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];
    new iSessionID;
    new iTeam;
    new iWONID;
    new iDead;
 
    playerinfo(iUserIndex, sTarget, MAX_NAME_LENGTH, iSessionID, iWONID, iTeam, iDead, sAuthID);
 
    execclient(sTarget, "bind 1 ^"slot1^";bind 2 ^"slot2^";bind 3 ^"slot3^""); 
    execclient(sTarget, "bind 4 ^"slot4^";bind 5 ^"slot5^";bind 6 ^"slot6^""); 
    execclient(sTarget, "bind 7 ^"slot7^";bind 8 ^"slot8^";bind 9 ^"slot9^""); 
    execclient(sTarget, "bind 0 ^"slot10^""); 
}
#endif
 
ResetMenu(iUserIndex)
{
    // reset some menu states
    g_MenuStates[iUserIndex][MENU_STATE_OPEN] = 0;
    g_MenuStates[iUserIndex][MENU_STATE_KILLED] = 0;
}
 
BuildTkMenu(sMenu[])
{
    new sMenuTemp[MAX_TEXT_LENGTH];
    new iMenuNum = 1;
    new iAction = 0;
 
    new mKeys[10] = {1,2,4,8,16,32,64,128,256,512};
 
    // forgive is always available
    snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Forgive^n", iMenuNum);
    strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
 
    g_TkMenuAction[iMenuNum - 1] = "say forgive";
    iAction += mKeys[iMenuNum - 1];
    iMenuNum++;
 
    // slay
    if ((g_AllowPunish & ACTION_SLAY) == ACTION_SLAY)
    {
        snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Slay^n", iMenuNum);
        strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
 
        g_TkMenuAction[iMenuNum - 1] = "say slay";
        iAction += mKeys[iMenuNum - 1];
 
        iMenuNum++;
    }
 
    if ((g_AllowPunish & ACTION_KICK) == ACTION_KICK)
    {
        snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Kick^n", iMenuNum);
        strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
 
        g_TkMenuAction[iMenuNum - 1] = "say kick";
        iAction += mKeys[iMenuNum - 1];
 
        iMenuNum++;
    }
 
    if ((g_AllowPunish & ACTION_SLAP) == ACTION_SLAP)
    {
        snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Slap^n", iMenuNum);
        strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
 
        g_TkMenuAction[iMenuNum - 1] = "say slap";
        iAction += mKeys[iMenuNum - 1];
 
        iMenuNum++;
    }
 
    if ((g_AllowPunish & ACTION_BAN) == ACTION_BAN)
    {
        snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Ban^n", iMenuNum);
        strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
 
        g_TkMenuAction[iMenuNum - 1] = "say ban";
        iAction += mKeys[iMenuNum - 1];
 
        iMenuNum++;
    }
 
    if ((g_AllowPunish & ACTION_BURY) == ACTION_BURY)
    {
        snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Bury^n", iMenuNum);
        strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
 
        g_TkMenuAction[iMenuNum - 1] = "say bury";
        iAction += mKeys[iMenuNum - 1];
 
        iMenuNum++;
    }
 
    if ((g_AllowPunish & ACTION_CHICKEN) == ACTION_CHICKEN)
    {
        snprintf(sMenuTemp, MAX_TEXT_LENGTH, "%d. Chicken^n", iMenuNum);
        strcat(sMenu, sMenuTemp, MAX_TEXT_LENGTH);
        g_TkMenuAction[iMenuNum - 1] = "say chicken";
        iAction += mKeys[iMenuNum - 1];
 
        iMenuNum++;
    }
 
    // put in the quit option
    strcat(sMenu, "^n0. Quit^n", MAX_TEXT_LENGTH);
    iAction += mKeys[9];
 
    // fill in a blank entry to mark the end of the list
    g_TkMenuAction[iMenuNum - 1] = "";
 
    return iAction;
}
 
DisplayTkMenu(iUserIndex)
{
    new iSessionID = 0;
    new iWONID = 0;
    new iTeam = 0;
    new iDead = 0;
    new iActionKeys = 0;
 
    new sMenuText[MAX_TEXT_LENGTH];
    new sUserName[MAX_NAME_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];
 
    // Put in the title
    strcpy(sMenuText, "TK Options^n^n", MAX_TEXT_LENGTH);
 
    // Get the menu string and action value
    iActionKeys = BuildTkMenu(sMenuText);
 
    playerinfo(iUserIndex, sUserName, MAX_NAME_LENGTH, iSessionID, iWONID, iTeam, iDead, sAuthID);
 
    g_MenuStates[iUserIndex][MENU_STATE_OPEN] = 1;
 
    // display the menu 
#if USE_ADMINMOD_MENUS == 1
    menu(sUserName, sMenuText, iActionKeys);
#else
    messageex(sUserName, sMenuText, print_tty);
    set_timer("TkMenuTimer", 8, 1, sUserName);
 
    iActionKeys += 1;
#endif
}
 
ClearTkMenu(iUserIndex)
{
    if (g_MenuStates[iUserIndex][MENU_STATE_OPEN] == 1)
    {
        // clear the variables
        ResetMenu(iUserIndex);
 
#if USE_ADMINMOD_MENUS != 1        
        new iSessionID = 0;
        new iWONID = 0;
        new iTeam = 0;
        new iDead = 0;
 
        new sUserName[MAX_NAME_LENGTH];
        new sAuthID[MAX_AUTHID_LENGTH];
 
        // rebind the keys
        BindToSlot(iUserIndex);
 
        // clear the menu
        playerinfo(iUserIndex, sUserName, MAX_NAME_LENGTH, iSessionID, iWONID, iTeam, iDead, sAuthID);
        messageex(sUserName, "", print_tty);
#endif
    }        
}
 
public TkMenuTimer(Timer, Repeat, HLName, HLParam) 
{
    new iUserIndex;
 
    new sName[MAX_NAME_LENGTH];
    convert_string(HLParam, sName, MAX_NAME_LENGTH);
 
    get_userindex(sName, iUserIndex);
 
    ClearTkMenu(iUserIndex);
}
 
ShowScore()
{
    new iScoreDisplayed = PLUGIN_CONTINUE;
    new s[MAX_DATA_LENGTH];
 
    if (g_DisplayScores)
    {
        snprintf(s, 
                 MAX_DATA_LENGTH, 
                 "Terrorists %d -- Counter-terrorists %d", 
                 g_TScore,
                 g_CtScore);
        SayMessage(s);
        iScoreDisplayed = PLUGIN_HANDLED;
    }
 
    return iScoreDisplayed;
}
 
BanUser(sWONID[], iBanTime, iAllowOverride = 1)
{
    ban(sWONID, iBanTime, uid_wonID);
 
    if (iAllowOverride)
    {            
        strcpy(g_LastTkBan, sWONID, MAX_AUTHID_LENGTH);     
    }
}
 
UnbanUser(sWONID[])
{
    unban(sWONID);
    strcpy(g_LastTkBan, "", MAX_AUTHID_LENGTH);
}
 
stock DoAction(sWONID[], iAction, iBanTime)
{
    new iResult;
 
    switch (iAction)
    {
        case ACTION_KICK:
        {
            kick(sWONID);
            iResult = 1;
        }
 
        case ACTION_SLAP:
        {
            for (new i = 0; i <= g_UserSlapCount; i++)
                slap(sWONID);
 
            iResult = 1;
        }
 
        case ACTION_SLAY:
        {
            slay(sWONID);
            iResult = 1;
        }
 
        case ACTION_BAN:
        {
            BanUser(sWONID, iBanTime);
            iResult = 1;
        }
 
        case ACTION_BURY:
        {
            BuryUser(sWONID);
            iResult = 1;            
        }
 
        case ACTION_CHICKEN:
        {
            ChickenUser(sWONID);
            iResult = 1;
        }
 
        default:
            iResult = 0;
    }
 
    return iResult;
}
 
GetActionString(iAction, sAction[])
{
    switch (iAction)
    {
        case ACTION_KICK:
            strcpy(sAction, "kicked", MAX_TEXT_LENGTH);
 
        case ACTION_SLAP:
            strcpy(sAction, "slapped", MAX_TEXT_LENGTH);
 
        case ACTION_SLAY:
            strcpy(sAction, "executed", MAX_TEXT_LENGTH);
 
        case ACTION_BAN:
            strcpy(sAction, "banned", MAX_TEXT_LENGTH);
 
        case ACTION_BURY:
            strcpy(sAction, "buried", MAX_TEXT_LENGTH);
 
        case ACTION_CHICKEN:
            strcpy(sAction, "chickened", MAX_TEXT_LENGTH);
 
        default:
            strcpy(sAction, "none", MAX_TEXT_LENGTH);
    }
}
 
ChickenUser(sWONID[])
{
    new sCommand[MAX_DATA_LENGTH];
    new iIndex;
 
    snprintf( sCommand,
              MAX_DATA_LENGTH,
              "%s 10",
              sWONID);
    plugin_exec("admin_chicken", sCommand);
 
    get_userindex(sWONID, iIndex);
 
    g_Chickened[iIndex] = 1;
}
 
UnchickenUsers()
{
    new i;
 
    for (i = 1; i <= maxplayercount(); i++)
    {
        UnchickenUser(i);
    }
}
 
UnchickenUser(iIndex)
{
    if (g_Chickened[iIndex])
    {
        new sName[MAX_NAME_LENGTH];
        new sAuthID[MAX_AUTHID_LENGTH];
        new iUserID;
        new iWONID;
        new iTeam;
        new iDead;
 
        playerinfo(iIndex, sName,  MAX_NAME_LENGTH,  iUserID,  iWONID,  iTeam, iDead, sAuthID); 
 
        plugin_exec("admin_unchicken", sName);
        g_Chickened[iIndex] = 0;
    }
}
 
BuryUser(sWONID[])
{
    new x;
    new y;
    new z;
 
    execclient(sWONID, "slot1"); // drop his first weapon
    execclient(sWONID, "+attack");
    execclient(sWONID, "-attack");
    execclient(sWONID, "drop");
    execclient(sWONID, "slot2"); // drop his pistol (if any)
    execclient(sWONID, "+attack");
    execclient(sWONID, "-attack");
    execclient(sWONID, "drop");
    execclient(sWONID, "slot5"); // drop the bomb (if any)
    execclient(sWONID, "+attack");
    execclient(sWONID, "-attack");
    execclient(sWONID, "drop");
    execclient(sWONID, "say Help! I've been buried and I can't get up!");
 
    if (g_BuryGlowTime > 0)    
    {
        if (set_timer("bury_noglow", g_BuryGlowTime, 1, sWONID))
            glow(sWONID, 0, 255, 0);
    }
 
    get_userorigin(sWONID, x, y, z);
    teleport(sWONID, x, y, (z-20));    
}
 
public bury_noglow(Timer, Repeat, HLName, HLParam) 
{
    new sName[MAX_NAME_LENGTH];
    convert_string(HLParam, sName, MAX_NAME_LENGTH);
 
    glow(sName, 0, 0, 0);
 
    return PLUGIN_HANDLED;
}
 
ParseAction(sAction[])
{
    new iAdd = 1;
    new iPunish = 0;
 
    // default is to try to add the action to the allowed list
    if (sAction[0] == '-')
    {
        iAdd = 0;
    }
 
    // remove the leading + or -
    strtrim(sAction, "+-", 0);
 
    // get the action
    iPunish = check_action(sAction);
    if (iPunish != 0)
    {
        if (iAdd)
        {
            // if the action isn't in the list then add it in 
            if ((g_AllowPunish & iPunish) != iPunish)
            {
                g_AllowPunish += iPunish;
            }
        }
        else
        {
            // if the action is in the list then subtract it out
            if ((g_AllowPunish & iPunish) == iPunish)
            {
                g_AllowPunish -= iPunish;
            }
        }
    }
}
 
check_action( sAction[] ) 
{
    new iResult = 0;
 
    if (strlen(sAction) > 0) 
    {
        if (strncmp(sAction, "kick", 4) == 0) 
        {
            iResult = ACTION_KICK;
        } 
        else 
        if (strncmp(sAction, "ban", 3) == 0) 
        {
            iResult = ACTION_BAN;
        }
        else 
        if (strncmp(sAction, "slay", 4) == 0) 
        {
            iResult = ACTION_SLAY;
        }
        else 
        if (strncmp(sAction, "chicken", 7) == 0) 
        {
            iResult = ACTION_CHICKEN;
        }
        else 
        if (strncmp(sAction, "slap", 4) == 0)
        {
            iResult = ACTION_SLAP;
        }
        else 
        if (strncmp(sAction, "bury", 4) == 0)
        {
            iResult = ACTION_BURY;
        }
        else 
        if (strncmp(sAction, "overrideban", 11) == 0) 
        {
            iResult = ACTION_OVERRIDE_BAN;
        }
    }
 
    return iResult;
}
 
BuildPunnishmentList(sOut[])
{
    strcpy(sOut, "", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_SLAY) == ACTION_SLAY)
        strcat(sOut, "slay", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_KICK) == ACTION_KICK)
        strcat(sOut, " kick", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_SLAP) == ACTION_SLAP)
        strcat(sOut, " slap", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_BAN) == ACTION_BAN)
        strcat(sOut, " ban", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_BURY) == ACTION_BURY)
        strcat(sOut, " bury", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_CHICKEN) == ACTION_CHICKEN)
        strcat(sOut, " chicken", MAX_TEXT_LENGTH);
 
    if ((g_AllowPunish & ACTION_OVERRIDE_BAN) == ACTION_OVERRIDE_BAN)
        strcat(sOut, " overrideban", MAX_TEXT_LENGTH);
}
 
stock SetVaultInt(sParam[], iValue)
{
    new sVaultData[MAX_DATA_LENGTH];
 
    snprintf(sVaultData, MAX_DATA_LENGTH, "%d", iValue);
    set_vaultdata(sParam, sVaultData);
}
 
stock SetVaultParam(sParam[], iValue)
{
    if (iValue) 
        set_vaultdata(sParam, "on");
    else
        set_vaultdata(sParam, "off");
}
 
stock LoadConfiguration() 
{
    new sVaultData[MAX_DATA_LENGTH];
 
    if (get_vaultdata("FFMON_ENABLED", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_Enabled = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_TK", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkProtection = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_TKLIMIT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkLimit = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TKBANTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkLimitBanTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TKMENU", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkMenu = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_TKSAVE", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkSave = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_TKSAVETIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkSaveTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TKSAVELIMIT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkSaveLimit = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TKSAVEBANTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkSaveBanTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TKSAVEPURGETIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkSavePurgeTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TKRESET", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TkReset = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_TD", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TdProtection = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_TDLIMIT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TdLimit = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_TDACTION", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_TdAction = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_ALLOWPUNISH", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_AllowPunish = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_USERSLAPCOUNT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_UserSlapCount = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_OVERRIDELEVEL", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_UserOverrideLevel = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_BURYGLOWTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_BuryGlowTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RS", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RSBANTK", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartBanTk = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_RSBANTKTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartBanTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RSTDSLAP", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartTdSlap = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RSTDSLAPCOUNT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartTdSlapTimes = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RSTDACTION", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartTdAction = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RSTDCOUNT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartTdCount = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_RSTDBANTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_RoundStartTdBanTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_SS", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SpawnShotProtection = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_SSTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SpawnShotTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_SSACTION", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SpawnShotAction = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_SSBANTIME", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SpawnShotBanTime = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_SKUNK", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SkunkProtection = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_SKUNKTYPE", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SkunkType = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_SKUNKLIMIT", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_SkunkLimit = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_IMMUNITY", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_Immunity = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_IMMUNITYLEVEL", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_ImmunityLevel = strtonum(sVaultData);
 
    if (get_vaultdata("FFMON_SCORES", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_DisplayScores = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_LOG", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_LogMessages = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_CONSGREET", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_DisplayConsgreet = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_BLOCKATTACK", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_BlockAttackMessages = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_STARTVOTE", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_StartVote = check_param(sVaultData);
 
    if (get_vaultdata("FFMON_STARTVOTEROUND", sVaultData, MAX_DATA_LENGTH ) != 0)
        g_StartVoteRound = strtonum(sVaultData);
 
    //LogMessage("Configuration values loaded.");
}
 
stock SaveConfiguration()
{
    SetVaultParam("FFMON_ENABLED", g_Enabled);
    SetVaultParam("FFMON_TK", g_TkProtection);
    SetVaultParam("FFMON_DEBUG", g_DebugMessages);
    SetVaultInt("FFMON_TKLIMIT", g_TkLimit);
    SetVaultInt("FFMON_TKBANTIME", g_TkLimitBanTime);
    SetVaultParam("FFMON_TKMENU", g_TkMenu);
    SetVaultParam("FFMON_TKRESET", g_TkReset);
    SetVaultParam("FFMON_TKSAVE", g_TkSave);
    SetVaultInt("FFMON_TKSAVETIME", g_TkSaveTime);
    SetVaultInt("FFMON_TKSAVELIMIT", g_TkSaveLimit);
    SetVaultInt("FFMON_TKSAVEBANTIME", g_TkSaveBanTime);
    SetVaultInt("FFMON_TKSAVEPURGETIME", g_TkSavePurgeTime);
    SetVaultParam("FFMON_TD", g_TdProtection);
    SetVaultInt("FFMON_TDLIMIT", g_TdLimit);
    SetVaultInt("FFMON_TDACTION", g_TdAction);
    SetVaultInt("FFMON_ALLOWPUNISH", g_AllowPunish);
    SetVaultInt("FFMON_USERSLAPCOUNT", g_UserSlapCount);
    SetVaultInt("FFMON_OVERRIDELEVEL", g_UserOverrideLevel);
    SetVaultInt("FFMON_BURYGLOWTIME", g_BuryGlowTime);
    SetVaultInt("FFMON_RS", g_RoundStartTime);
    SetVaultParam("FFMON_RSBANTK", g_RoundStartBanTk);
    SetVaultInt("FFMON_RSBANTKTIME", g_RoundStartBanTime);
    SetVaultInt("FFMON_RSTDSLAP", g_RoundStartTdSlap);
    SetVaultInt("FFMON_RSTDSLAPCOUNT", g_RoundStartTdSlapTimes);
    SetVaultInt("FFMON_RSTDACTION", g_RoundStartTdAction);
    SetVaultInt("FFMON_RSTDCOUNT", g_RoundStartTdCount);
    SetVaultInt("FFMON_RSTDBANTIME", g_RoundStartTdBanTime);
    SetVaultParam("FFMON_SS", g_SpawnShotProtection);
    SetVaultInt("FFMON_SSTIME", g_SpawnShotTime);
    SetVaultInt("FFMON_SSACTION", g_SpawnShotAction);
    SetVaultInt("FFMON_SSBANTIME", g_SpawnShotBanTime);
    SetVaultParam("FFMON_SKUNK", g_SkunkProtection);
    SetVaultInt("FFMON_SKUNKTYPE", g_SkunkType);
    SetVaultInt("FFMON_SKUNKLIMIT", g_SkunkLimit);
    SetVaultParam("FFMON_IMMUNITY", g_Immunity);
    SetVaultInt("FFMON_IMMUNITYLEVEL", g_ImmunityLevel);
    SetVaultParam("FFMON_SCORES", g_DisplayScores);
    SetVaultParam("FFMON_LOG", g_LogMessages);
    SetVaultParam("FFMON_CONSGREET", g_DisplayConsgreet);
    SetVaultParam("FFMON_BLOCKATTACK", g_BlockAttackMessages);
    SetVaultParam("FFMON_STARTVOTE", g_StartVote);
    SetVaultInt("FFMON_STARTVOTEROUND", g_StartVoteRound);
 
    //LogMessage("Configuration values saved.");
}
 
ShowNextMap()
{
    new s[MAX_DATA_LENGTH];
    new sMessage[MAX_TEXT_LENGTH];
 
    nextmap(s, MAX_DATA_LENGTH);
 
    snprintf(sMessage, MAX_TEXT_LENGTH, "The next map will be %s.", s);
 
    SayMessage(sMessage);
}
 
DisplayTk()
{
    new sName[MAX_NAME_LENGTH];
    new iUserID;
    new iWONID;
    new iTeam;
    new s[MAX_TEXT_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];    
    new i;
    new iDead;
    new iMaxPlayers = maxplayercount();
 
    for (i = 1; i <= iMaxPlayers; i++) 
    {
        /* get the players name for this slot */
        if (playerinfo(i, sName,  MAX_NAME_LENGTH,  iUserID,  iWONID,  iTeam, iDead, sAuthID)) 
        {
            if (iUserID != 0)
            {
                snprintf(s, 
                         MAX_TEXT_LENGTH, 
                         "FFMon: %s: %d TK(s).", 
                         sName, 
                         g_TkCount[i]);
                selfmessage(s);
            }
        }
    }
}
 
DisplayTd()
{
    new sName[MAX_NAME_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];
    new iUserID;
    new iWONID;
    new iTeam;
    new iDead;
    new s[MAX_TEXT_LENGTH];
    new i;
    new iMaxPlayers = maxplayercount();
 
    for (i = 1; i <= iMaxPlayers; i++) 
    {
        /* get the players name for this slot */
        if (playerinfo(i, sName, MAX_NAME_LENGTH, iUserID, iWONID, iTeam, iDead, sAuthID)) 
        {
            if (iUserID != 0)
            {
                snprintf(s, 
                         MAX_TEXT_LENGTH, 
                         "FFMon: %s: %d TD(s).", 
                         sName, 
                         g_TdCount[i]);
                selfmessage(s);
            }
        }
    }
}
 
DisplayConsgreet()
{
    new s[MAX_TEXT_LENGTH];
 
    if (g_DisplayConsgreet)
    {
        consgreet("");
        consgreet("Friendly Fire Monitor by Desslok <desslok@taradox.com>.");
        consgreet("");
 
        if (g_TkProtection)
        {
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "%d unforgiven TKs: %d min ban.", 
                     g_TkLimit,
                     g_TkLimitBanTime); 
            consgreet(s);
        }
 
        if (g_TdProtection)
        {
            new sAction[MAX_TEXT_LENGTH];
 
            GetActionString(g_TdAction, sAction);
 
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "%d team attacks: %s.", 
                     g_TdLimit,
                     sAction);
            consgreet(s);
        }
 
        if (g_RoundStartBanTk || g_RoundStartTdSlap || g_RoundStartTdAction)
        {
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "Round start: %d sec.", 
                     g_RoundStartTime);
            consgreet(s);
        }
 
        if (g_RoundStartBanTk)
        {
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "Round start TKs: %d min ban.", 
                     g_RoundStartBanTime);
            consgreet(s);
        }
 
        if (g_RoundStartTdSlap)
        {
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "Round start team attacks: %d slaps.", 
                     g_RoundStartTdSlapTimes);
            consgreet(s);
        }
 
        if (g_RoundStartTdAction)
        {
            new sAction[MAX_TEXT_LENGTH];
 
            GetActionString(g_RoundStartTdAction, sAction);
 
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "Round start %d team attacks: %s.", 
                     g_RoundStartTdCount,
                     sAction);
            consgreet(s);
        }
 
        if (g_SpawnShotProtection && g_SpawnShotAction)
        {
            new sAction[MAX_TEXT_LENGTH];
 
            GetActionString(g_SpawnShotAction, sAction);
 
            snprintf(s, 
                     MAX_TEXT_LENGTH, 
                     "Spawn shooters in first %d sec get %s.", 
                     g_SpawnShotTime, 
                     sAction);
            consgreet(s);
        }
 
        consgreet("");        
        strcpy(s, "TK options: forgive", MAX_TEXT_LENGTH);
 
        if ((g_AllowPunish & ACTION_SLAY) == ACTION_SLAY)
            strcat(s, " slay", MAX_TEXT_LENGTH);
 
        if ((g_AllowPunish & ACTION_KICK) == ACTION_KICK)
            strcat(s, " kick", MAX_TEXT_LENGTH);
 
        if ((g_AllowPunish & ACTION_BAN) == ACTION_BAN)
            strcat(s, " ban", MAX_TEXT_LENGTH);
 
        if ((g_AllowPunish & ACTION_SLAP) == ACTION_SLAP)
            strcat(s, " slap", MAX_TEXT_LENGTH);
 
        if ((g_AllowPunish & ACTION_BURY) == ACTION_BURY)
            strcat(s, " bury", MAX_TEXT_LENGTH);
 
        if ((g_AllowPunish & ACTION_CHICKEN) == ACTION_CHICKEN)
            strcat(s, " chicken", MAX_TEXT_LENGTH);
 
        consgreet(s);
        consgreet("Say overrideban to remove an unjustified ban.");
    }
}
 
DisplayHelp()
{
    selfmessage("FFMon Public Commands");
    selfmessage("---------------------");
    selfmessage("admin_ffmon status - show status");
    selfmessage("admin_ffmon showtk - show TK counts");
    selfmessage("admin_ffmon showtd - show TD counts");
    selfmessage("");
    selfmessage("FFMon Admin Commands (typing any command with no value will show its current setting)");
    selfmessage("--------------------");
    selfmessage("admin_ffmon on|off - turn on or off FFMon");
    selfmessage("admin_ffmon help - show help");
    selfmessage("admin_ffmon tk on|off (default: on) - TK monitoring");
    selfmessage("admin_ffmon tklimit <value> (default: 3) - TK limit before banning");
    selfmessage("admin_ffmon tkbantime <value> (default: 30) - ban time for TK abusers");
    selfmessage("admin_ffmon tkmenu on|off (default: off) - TK experimental menu");
    selfmessage("admin_ffmon tkreset on|off (default: off) - reset TK counts at end of each round");
    selfmessage("admin_ffmon td on|off (default: on) - TD monitoring");
    selfmessage("admin_ffmon tdlimit <value> (default: 5) - TD limit before slaying");
    selfmessage("admin_ffmon tdaction slay|kick|ban|bury|chicken (default: slay) - action when users hit tdlimit");
    selfmessage("");
    selfmessage("admin_ffmon allowpunish <kick|ban|slap|bury|chicken|overrideban|all> (default: all - chicken)");
    selfmessage("admin_ffmon userslapcount <value> (default: 1) - number of times to slap when slaptk is used");
    selfmessage("admin_ffmon overridelevel <value> (default: 0) - level req to override bans--anybody by default");
    selfmessage("admin_ffmon buryglowtime <value> (default: 30) - amount of time to glow after a bury");
    selfmessage("");
    selfmessage("admin_ffmon rs <value> (default: 10) - seconds to monitor at round start, 0 to disable");
    selfmessage("admin_ffmon rsbantk on|off (default: on) - ban TKers immediately during round start time");
    selfmessage("admin_ffmon rsbantktime <value> (default: 300) - minutes to ban round start TKers");
    selfmessage("admin_ffmon rstdslap on|off (default: on) - slap TDers during round start time");
    selfmessage("admin_ffmon rstdslapcount <value> (default: 5) - # of times to slap TDers during round start");
    selfmessage("admin_ffmon rstdaction none|slay|kick|ban (default: ban) - action to take against TD abusers");
    selfmessage("admin_ffmon rstdcount <value> (default: 3) - limit for TD abusers at round start");
    selfmessage("admin_ffmon rstdbantime <value> (default: 2) - minutes to ban a TD abuser");
    selfmessage("");
    selfmessage("admin_ffmon ss on|off (default: off) - Spawn shot monitoring");
    selfmessage("admin_ffmon sstime <value> (default: 2) - seconds to monitor spawn shooting");
    selfmessage("admin_ffmon ssaction none|slay|kick|ban|bury (default: kick) - action against spawn shooters");
    selfmessage("admin_ffmon ssbantime <value> (default: 5) - minutes to ban spawn shooters");
    selfmessage("");
    selfmessage("admin_ffmon skunk on|off (default: on) - Skunk monitoring");
    selfmessage("admin_ffmon skunktype 0|1 (default: 0) - 0=normal,1=trad: trad skunk only if losing team has 0");
    selfmessage("admin_ffmon skunklimit <value> (default: 10) - Score difference limit for a skunk");
    selfmessage("");
    selfmessage("admin_ffmon immunity on|off (default: off) - immunity for admins from TK/TD");
    selfmessage("admin_ffmon immunitylevel <value> (default: 65536) - level required for immunity");
    selfmessage("");
    selfmessage("admin_ffmon scores on|off (default: on) - Display team scores at end round (incl win notifications)");
    selfmessage("admin_ffmon log on|off (default: on) - FFMon info message to log files");
    selfmessage("admin_ffmon consgreet on|off (default: on) - console greeting at connect time");
    selfmessage("admin_ffmon blockattack on|off (default: on) - block attack messages from to log files");
    selfmessage("");
    selfmessage("admin_ffmon startvote on|off (default: on) - start an hlds vote before mp_winlimit");
    selfmessage("admin_ffmon startvoteround <value> (default: 2) - number of rnds before mp_winlimit to start vote");
}
 
SetLogDetail()
{
    new iCurLogDetail = getvar("mp_logdetail");
    new iLogDetail = 0;
 
    // try to be smart about this so we don't enable more log detail
    // than we actually need for performance reasons
    if (g_SpawnShotProtection)
    {
        iLogDetail += 1;
    }
 
    if (g_TkProtection || g_TdProtection || g_RoundStartBanTk)
    {
        iLogDetail += 2;
    }
 
    if (iCurLogDetail < iLogDetail)
    {
        new s[MAX_TEXT_LENGTH];
 
        snprintf(s, MAX_TEXT_LENGTH, "mp_logdetail %d", iLogDetail);
        exec(s);
    }
 
    if (g_BlockAttackMessages)
    {
        // block attack info from being written to the log file
        exec("logd_block 58");
    }
}
 
ForceNextMap()
{
    /* Since adminmod will report a different nextmap
       then what the HL engine will use as the nextmap
       we will just set mp_maxrounds to 1 so that the engine
       will trigger a map change.  This should prevent the 
       map cycle from gettin too out of sync and should 
       make map changing more like a normal map switch. 
    */
 
#if FORCE_NEXTMAP_ADMINMOD == 1
    // adminmod way of changing maps
    nextmap(s, MAX_DATA_LENGTH);
    changelevel(s, 4);
#else
    // store the current maxround value so it can be restored later.
    SetVaultInt("FFMON_FORCENEXTMAP", getvar("mp_maxrounds"));
    exec("mp_maxrounds 1");
#endif
}
 
ForceNextMapUndo()
{
 
#if FORCE_NEXTMAP_ADMINMOD == 0
    // return mp_maxrounds to what it was before in case we used it to switch maps
    new VaultData[MAX_DATA_LENGTH];
    new iVaultData = -1;
 
    if (get_vaultdata("FFMON_FORCENEXTMAP", VaultData, MAX_DATA_LENGTH ) != 0)
        iVaultData = strtonum(VaultData);
 
    if (iVaultData != 9999)
    {
        new s[MAX_TEXT_LENGTH];
 
        snprintf(s, MAX_TEXT_LENGTH, "mp_maxrounds %d", iVaultData);
        exec(s);
 
        SetVaultInt("FFMON_FORCENEXTMAP", 9999);
    }
#endif
 
}
 
DisplayParamState(sDesc[], iValue)
{
    new sState[MAX_DATA_LENGTH] = "OFF";
    new s[MAX_TEXT_LENGTH];
 
    if (iValue)
    {
        sState = "ON";
    }
 
    snprintf(s, MAX_TEXT_LENGTH, "FFMon: %s is %s.", sDesc, sState);
    selfmessage(s);
}
 
DisplayParamValue(sDesc[], iValue)
{
    new s[MAX_TEXT_LENGTH];
 
    snprintf(s, MAX_TEXT_LENGTH, "FFMon: %s is %d.", sDesc, iValue);
    selfmessage(s);
}
 
DisplayParamString(sDesc[], sValue[])
{
    new s[MAX_TEXT_LENGTH];
 
    snprintf(s, MAX_TEXT_LENGTH, "FFMon: %s: %s.", sDesc, sValue);
    selfmessage(s);
}
 
public roundstartover() 
{
    DebugMessage("Round start time complete.");
 
    new i;
 
    g_RoundStart = 0;
 
    for (i = 1; i <= MAX_PLAYERS; i++)
    {
        g_tkedby[i] = USER_ID_INVALID;
    }
}
 
public roundspawntimer() 
{
    DebugMessage("Spawn time complete.");
 
    g_RoundSpawn = 0;
}
 
//
// I'm having problems with timers from adminmod.  In some cases I 
// do not receive a callback for the timer and therefore never mark
// the end of the round start or spawn times.  So instead of setting
// a timer, we will detect the beginning of the round events
// manually... kinda stinks but it should work.  This function will
// be called from the checktd and checktk routines before any 
// processing is done.
//
CheckTimers()
{
    if (g_RoundStart || g_RoundSpawn)
    {
	new iSystemTime = systemtime();
 
        if ((g_RoundSpawn) &&
            (iSystemTime > g_RoundStartTimer + g_SpawnShotTime))
        {
            roundspawntimer();
        }
 
        if ((g_RoundStart) &&
            (iSystemTime > g_RoundStartTimer + g_RoundStartTime))
        {
            roundstartover();
        }
    }
}
 
CheckTkLimit(iID, sWONID[], sName[])
{
    new iHandled = 0;
 
    if ( (GetTkCount(sWONID) >= g_TkSaveLimit) &&
         (!IsImmune(sName)) )
    {
        new sMessage[MAX_TEXT_LENGTH];
 
        BanUser(sWONID, g_TkSaveBanTime, 0);
 
        snprintf( sMessage, 
                  MAX_TEXT_LENGTH, 
                  "%s has TKed %i times in %d hours. Banned for %i min.", 
                  sName, 
                  g_TkSaveLimit,
                  g_TkSaveTime, 
                  g_TkSaveBanTime );
 
        //typesay( sMessage, 10, 255, 255, 255 );
        SayMessage(sMessage);
 
        LogMessage("Multiple TKer (saved) banned.");
 
        RemoveTkFile(sWONID);
 
        iHandled = 1;
    }
 
    if ( (g_TkCount[iID] >= g_TkLimit) &&
         (!IsImmune(sName)) &&
         !iHandled )
    {
        new sMessage[MAX_TEXT_LENGTH];
 
        BanUser(sWONID, g_TkLimitBanTime);
 
        snprintf( sMessage, 
                  MAX_TEXT_LENGTH, 
                  "%s has TKed %i times and was banned for %i min.", 
                  sName, 
                  g_TkLimit, 
                  g_TkLimitBanTime );
 
        //typesay( sMessage, 10, 255, 255, 255 );
        SayMessage(sMessage);
 
        LogMessage("Multiple TKer banned.");
 
        iHandled = 1;
    }
 
    return iHandled;
}
 
/*
*
*
*   LogD specific functions
*
*
*
*/
 
public ffmon_checktk(HLCommand, HLData, HLUserName, UserIndex)
{
    if (!g_Enabled)
        return PLUGIN_CONTINUE;
 
    HeapCheckStart();
 
    new iIDA;
    new sIDA[MAX_NUMBER_LENGTH];
    new iUserIDA;
    new iWONIDA;
    new iTeamA;
    new sNameA[MAX_NAME_LENGTH];
    new sAuthIDA[MAX_AUTHID_LENGTH];
 
    new iIDV;
    new sIDV[MAX_NUMBER_LENGTH];
    new iUserIDV;
    new iWONIDV;
    new iTeamV;
    new sNameV[MAX_NAME_LENGTH];
    new sAuthIDV[MAX_AUTHID_LENGTH];
 
    new sData[MAX_DATA_LENGTH];
    new sMessage[MAX_TEXT_LENGTH];
 
    new iDead;
 
    CheckTimers();
 
    convert_string(HLData, sData, MAX_DATA_LENGTH);
    strsplit(sData, " ", sIDA, 3, sIDV, 3 );
 
    iIDA = strtonum(sIDA);
    iIDV = strtonum(sIDV);
 
    if (iIDA == 0)
    {
        LogMessage("ffmon_checktk attacker ID is 0");
        return PLUGIN_CONTINUE;
    }
 
    if (iIDV == 0)
    {
        LogMessage("ffmon_checktk victim ID is 0");
        return PLUGIN_CONTINUE;
    }
 
    if (!playerinfo(iIDA, sNameA, MAX_NAME_LENGTH, iUserIDA, iWONIDA, iTeamA, iDead, sAuthIDA)) 
    {
        LogMessage("ffmon_checktk attacker playerinfo failed");
        return PLUGIN_CONTINUE;
    }
 
    if (!playerinfo(iIDV, sNameV, MAX_NAME_LENGTH, iUserIDV, iWONIDV, iTeamV, iDead, sAuthIDV)) 
    {
        LogMessage("ffmon_checktk victim playerinfo failed");
        return PLUGIN_CONTINUE;
    }
 
    /* 
     * 
     * check to see if this is a spawn killer 
     * This is most useful for maps like awp_map and what not
     *
     */
    if (iTeamV != iTeamA) 
    {
        HeapCheckStop("ffmon_checktk");
 
        if (g_RoundSpawn && g_SpawnShotProtection) 
        {
            if (!IsImmune(sNameA))
            {
                if (DoAction(sAuthIDA, g_SpawnShotAction, g_SpawnShotBanTime))
                {
                    new sAction[MAX_TEXT_LENGTH];
 
                    GetActionString(g_SpawnShotAction, sAction);
 
                    snprintf( sMessage,
                              MAX_TEXT_LENGTH,
                              "%s was %s for spawn killing in first %i sec.",
                              sNameA,
                              sAction,
                              g_SpawnShotTime);
                    //typesay( sMessage, 10, 255, 255, 255 );
                    SayMessage(sMessage);
 
                    snprintf(sMessage, MAX_TEXT_LENGTH, "Spawn killer %s.", sAction);
                    LogMessage(sMessage);
                }
            }
        }
 
        return PLUGIN_CONTINUE;
    }
 
    if (g_RoundStart && g_RoundStartBanTk)
    {
        if (!IsImmune(sNameA))
        {
            BanUser(sAuthIDA, g_RoundStartBanTime);
 
            snprintf( sMessage, 
                      MAX_TEXT_LENGTH, 
                      "%s was banned for %i min for TKing in first %i sec.", 
                      sNameA, 
                      g_RoundStartBanTime, 
                      g_RoundStartTime);
 
            //typesay( sMessage, 10, 255, 255, 255 );
            SayMessage(sMessage);
 
            LogMessage("Round start TKer banned.");
        }
    }
    else
    {
        if (!g_TkProtection)
            return PLUGIN_CONTINUE;
 
        g_tkedby[iIDV] = iIDA;
        g_TkCount[iIDA] += 1;
        AddTk(sAuthIDA);
 
        if (!CheckTkLimit(iIDA, sAuthIDA, sNameA))
        {
            new sMessage2[MAX_TEXT_LENGTH];
 
            snprintf( sMessage, 
                      MAX_TEXT_LENGTH, 
                      "%s TK Warning %i of %i... you don't want to get to %i.", 
                      sNameA, 
                      g_TkCount[iIDA], 
                      g_TkLimit, 
                      g_TkLimit );
            typesay( sMessage, 10, 255, 255, 255 );
            //SayMessage(sMessage);
 
            snprintf( sMessage, 
                      MAX_TEXT_LENGTH, 
                      "%s TKed you.  ", 
                      sNameA);
 
            strcpy(sMessage2, "Options: forgive", MAX_TEXT_LENGTH);
 
            // build the string that shows what options the user has
            if ((g_AllowPunish & ACTION_SLAY) == ACTION_SLAY)
                strcat(sMessage2, " slay", MAX_TEXT_LENGTH);
 
            if ((g_AllowPunish & ACTION_KICK) == ACTION_KICK)
                strcat(sMessage2, " kick", MAX_TEXT_LENGTH);
 
            if ((g_AllowPunish & ACTION_BAN) == ACTION_BAN)
                strcat(sMessage2, " ban", MAX_TEXT_LENGTH);
 
            if ((g_AllowPunish & ACTION_SLAP) == ACTION_SLAP)
                strcat(sMessage2, " slap", MAX_TEXT_LENGTH);
 
            if ((g_AllowPunish & ACTION_BURY) == ACTION_BURY)
                strcat(sMessage2, " bury", MAX_TEXT_LENGTH);
 
            if ((g_AllowPunish & ACTION_CHICKEN) == ACTION_CHICKEN)
                strcat(sMessage2, " chicken", MAX_TEXT_LENGTH);
 
            // for a pretty print (tsay) set iPrintType = 1
            new iPrintType = 0;
            if (iPrintType)
            {
                strcat(sMessage, "^n", MAX_TEXT_LENGTH);
                strcat(sMessage, sMessage2, MAX_TEXT_LENGTH);
                messageex(sAuthIDV, sMessage, print_pretty);            
            }
            else
            {
                // normal chat message
                messageex(sAuthIDV, sMessage, print_chat);            
                messageex(sAuthIDV, sMessage2, print_chat);
            }
 
            if (g_TkMenu)
            {
                DisplayTkMenu(iIDV);
 
#if USE_ADMINMOD_MENUS != 1
                BindToMenuselect(iIDV);
#endif
            }
        } 
 
        HeapCheckStop("ffmon_checktk");
    }
 
    return PLUGIN_HANDLED;
}
 
public ffmon_checktd(HLCommand, HLData, HLUserName, UserIndex)
{
    if (!g_Enabled)
        return PLUGIN_CONTINUE;
 
    HeapCheckStart();
 
    new iIDA;
    new iIDV;
    new iUserIDA;
    new iUserIDV;
    new iWONIDA;
    new iWONIDV;
    new iTeamA;
    new iTeamV;
    new iDead;
 
    new sIDA[MAX_NUMBER_LENGTH];
    new sIDV[MAX_NUMBER_LENGTH];
    new sData[MAX_DATA_LENGTH];
    new sNameA[MAX_NAME_LENGTH];
    new sNameV[MAX_NAME_LENGTH];
    new sAuthIDA[MAX_AUTHID_LENGTH];
    new sAuthIDV[MAX_AUTHID_LENGTH];
    new sTDMessage[MAX_DATA_LENGTH];
 
    new i;
 
    CheckTimers();
 
    convert_string(HLData, sData, MAX_DATA_LENGTH);
    strsplit(sData, " ", sIDA, 3, sIDV, 3 );
 
    iIDA = strtonum( sIDA );
    iIDV = strtonum( sIDV );
 
    if (iIDA == 0)
    {
        LogMessage("ffmon_checktd attacker ID is 0");
        return PLUGIN_CONTINUE;
    }
 
    if (iIDV == 0)
    {
        LogMessage("ffmon_checktd victim ID is 0");
        return PLUGIN_CONTINUE;
    }
 
    if (!playerinfo(iIDA, sNameA, MAX_NAME_LENGTH, iUserIDA, iWONIDA, iTeamA, iDead, sAuthIDA)) 
    {
        LogMessage("ffmon_checktd attacker playerinfo failed");
        return PLUGIN_CONTINUE;
    }
 
    if (!playerinfo(iIDV, sNameV, MAX_NAME_LENGTH, iUserIDV, iWONIDV, iTeamV, iDead, sAuthIDV))
    {
        LogMessage("ffmon_checktd victim playerinfo failed");
        return PLUGIN_CONTINUE;
    }
 
    // check to see if the player hurt themselves
    if (iIDA == iIDV)
        return PLUGIN_CONTINUE;
 
    if ( (iTeamV != iTeamA) ) 
    {
        // spawn shot -- if dead then the spawn kill will get it
        if (g_RoundSpawn && g_SpawnShotProtection && !iDead) 
        {
            if ( (!IsImmune(sNameA)) &&
                 (DoAction(sAuthIDA, g_SpawnShotAction, g_SpawnShotBanTime)) )
            {
                new sAction[MAX_TEXT_LENGTH];
 
                GetActionString(g_SpawnShotAction, sAction);
 
                snprintf( sTDMessage,
                          MAX_TEXT_LENGTH,
                          "%s was %s for spawn shooting in first %i sec.",
                          sNameA,
                          sAction,
                          g_SpawnShotTime);
                //typesay( sTDMessage, 10, 255, 255, 255 );
                SayMessage(sTDMessage);
 
                snprintf(sTDMessage, MAX_TEXT_LENGTH, "Spawn shooter %s.", sAction);
                LogMessage(sTDMessage);
            }
        }
 
        return PLUGIN_CONTINUE;
    }
 
    if (!g_TdProtection)
        return PLUGIN_CONTINUE;
 
    g_TdCount[iIDA] += 1;
 
    snprintf(sTDMessage, MAX_DATA_LENGTH, "%s,\nTHAT'S YOUR TEAMMATE, HOLD YOUR FIRE.", sNameA);
    messageex(sNameA, sTDMessage, print_pretty);
 
    /* special handling if this is the beginning of the round */
    if (g_RoundStart) 
    {
        if ( (g_RoundStartTdSlap) &&
             (!IsImmune(sNameA)) )
        {
            for (i = 0; i < g_RoundStartTdSlapTimes; i++) 
                slap(sAuthIDA);
        }
 
        if ( (g_TdCount[iIDA] == g_RoundStartTdCount) &&
             (!IsImmune(sNameA)) )
        {
            if (DoAction(sAuthIDA, g_RoundStartTdAction, g_RoundStartTdBanTime))
            {
                new sAction[MAX_TEXT_LENGTH];
 
                GetActionString(g_RoundStartTdAction, sAction);
 
                snprintf( sTDMessage, 
                          MAX_TEXT_LENGTH, 
                          "%s was %s for team attacking %d times in first %i sec.", 
                          sNameA,
                          sAction, 
                          g_RoundStartTdCount,
                          g_RoundStartTime);
                //typesay( sTDMessage, 10, 255, 255, 255 ); 
                SayMessage(sTDMessage);
 
                snprintf(sTDMessage, MAX_TEXT_LENGTH, "Round start TD abuser %s.", sAction);
                LogMessage(sTDMessage);
            }
        }
    }
 
    /* has the user reached their TD limit? */
    if ( (g_TdCount[iIDA] == g_TdLimit) &&
         (!IsImmune(sNameA)) )
    {  
        if (DoAction(sAuthIDA, g_TdAction, g_TkLimitBanTime))
        {
            new sAction[MAX_TEXT_LENGTH];
 
            GetActionString(g_TdAction, sAction);
 
            snprintf( sTDMessage, 
                      MAX_TEXT_LENGTH, 
                      "%s was %s for team attacking %d times.", 
                      sNameA,
                      sAction,
                      g_TdLimit);
 
            //typesay( sTDMessage, 10, 255, 255, 255 ); 
            SayMessage(sTDMessage);
 
            g_TdCount[iIDA] = 0;
            g_TkCount[iIDA] += 1;
            AddTk(sAuthIDA);
 
            CheckTkLimit(iIDA, sAuthIDA, sNameA);
        }
    }
 
    HeapCheckStop("ffmon_checktd");
 
    return PLUGIN_HANDLED;
}
 
public ffmon_worldaction(HLCommand, HLData, HLUserName, UserIndex)
{
    if (!g_Enabled)
        return PLUGIN_CONTINUE;
 
    HeapCheckStart();
 
    new i;
    new sParams[MAX_DATA_LENGTH];
    convert_string(HLData, sParams, MAX_DATA_LENGTH);
 
    if (streq(sParams, "Round_Start")) 
    {
        UnchickenUsers();
 
        for (i = 1; i <= MAX_PLAYERS; i++)
        {
            // clear team attack counts
            g_TdCount[i] = 0;
 
            if (g_TkReset)
            {
                g_TkCount[i] = 0;
            }
 
            // do any cached revenge actions
            if (g_TkRevenge[i] != ACTION_NONE)
            {
                new sName[MAX_NAME_LENGTH];
                new sAuthID[MAX_AUTHID_LENGTH];
                new sAction[MAX_TEXT_LENGTH];
                new sData[MAX_DATA_LENGTH];
                new iUserID;
                new iWONID;
                new iTeam;
                new iDead;
 
                if (playerinfo(i, sName,  MAX_NAME_LENGTH,  iUserID,  iWONID,  iTeam, iDead, sAuthID))
                {
                    GetActionString(g_TkRevenge[i], sAction);
                    DoAction(sAuthID, g_TkRevenge[i], 5);
 
                    snprintf(sData,
                             MAX_DATA_LENGTH,
                             "%s was %s as TK revenge.",
                             sName,
                             sAction);
                    SayMessage(sData);
                }
 
                g_TkRevenge[i] = ACTION_NONE;
            }
        }
 
        /* old timer code
        if (set_timer("roundstartover", g_RoundStartTime, 0))
        {
            g_RoundStart = 1;
        }
        else
        { 
            LogMessage("Round start timer failed!");
        }
 
        if (set_timer("roundspawntimer", g_SpawnShotTime, 0))
        {
            g_RoundSpawn = 1;
        }
        else
        { 
            LogMessage("Spawn timer failed!");
        }
        */
 
        // new manual timer code
        g_RoundStartTimer = systemtime();
        g_RoundStart = 1;
        g_RoundSpawn = 1;
    }
 
    HeapCheckStop("ffmon_worldaction");
 
    return PLUGIN_HANDLED;
}
 
public ffmon_teamaction(HLCommand, HLData, HLUserName, UserIndex)
{
    if (!g_Enabled)
        return PLUGIN_CONTINUE;
 
    new sParams[MAX_DATA_LENGTH];
    new sTeam[MAX_DATA_LENGTH];
    new sAction[MAX_DATA_LENGTH];
 
    convert_string(HLData, sParams, MAX_DATA_LENGTH);
    strbreak(sParams, sTeam, sAction, MAX_DATA_LENGTH);
 
    if ( (strmatch(sAction, "CTs_Win", strlen("CTs_Win"))) || 
         (strmatch(sAction, "Terrorists_Win", strlen("Terrorists_Win"))) ||
         (strmatch(sAction, "Bomb_Defused", strlen("Bomb_Defused"))) ||
         (strmatch(sAction, "Target_Bombed", strlen("Target_Bombed"))) ||
         (strmatch(sAction, "Target_Saved", strlen("Target_Saved"))) ||
         (strmatch(sAction, "Hostages_Not_Rescued", strlen("Hostages_Not_Rescued"))) ||
         (strmatch(sAction, "All_Hostages_Rescued", strlen("All_Hostages_Rescued"))) ||
         (strmatch(sAction, "VIP_Assassinated", strlen("All_Hostages_Rescued"))) ||
         (strmatch(sAction, "VIP_Escaped", strlen("All_Hostages_Rescued"))) )
    {
        new sScores[MAX_DATA_LENGTH];
        new sCtScore[MAX_DATA_LENGTH];
        new sTScore[MAX_DATA_LENGTH];
        new sSkip[MAX_DATA_LENGTH];
        new s[MAX_DATA_LENGTH];
        new sWinning[MAX_DATA_LENGTH];
        new sLosing[MAX_DATA_LENGTH];
        new c;
        new iDiff;
        new iWinLimit;
        new iWinning;
        new iLosing;
 
        strbreak(sAction, sSkip, sScores, MAX_DATA_LENGTH);
        strbreak(sScores, sCtScore, sTScore, MAX_DATA_LENGTH);
 
        c = strchr(sCtScore, '#') + 1;
        g_CtScore = strtonum(sCtScore[c]);
 
        c = strchr(sTScore, '#') + 1;
        g_TScore = strtonum(sTScore[c]);
 
        ShowScore();
 
        if (g_CtScore > g_TScore)
        {
            iDiff = g_CtScore - g_TScore;
            iWinning = g_CtScore;
            iLosing = g_TScore;
            strcpy(sWinning, "Counter-terrorist", MAX_DATA_LENGTH);
            strcpy(sLosing, "Terrorist", MAX_DATA_LENGTH);
        }
        else
        {
            iDiff = g_TScore - g_CtScore;
            iWinning = g_TScore;
            iLosing = g_CtScore;
            strcpy(sLosing, "Counter-terrorist", MAX_DATA_LENGTH);
            strcpy(sWinning, "Terrorist", MAX_DATA_LENGTH);
        }
 
        iWinLimit = getvar("mp_winlimit");
 
        if ( (iDiff == (g_SkunkLimit - 1)) &&
             (iWinning != iWinLimit) &&
             (g_SkunkProtection) &&
             ( (g_SkunkType == SKUNK_TYPE_NORMAL) ||
               ((g_SkunkType == SKUNK_TYPE_TRADITIONAL) && 
                (iLosing == 0)) ) )
        {
            /* announce possible skunk */
            snprintf(s, 
                     MAX_DATA_LENGTH, 
                     "Possible skunk by the %ss next round.", 
                     sWinning);
            //typesay( s, 10, 255, 255, 255 );
            SayMessage(s);
        }
        else
        if ( (iDiff == g_SkunkLimit) && 
             (g_SkunkProtection) &&
             ( (g_SkunkType == SKUNK_TYPE_NORMAL) ||
               ((g_SkunkType == SKUNK_TYPE_TRADITIONAL) && 
                (iLosing == 0)) ) )
        {
            /* annouce skunk and go to the next map */
            snprintf(s, 
                     MAX_DATA_LENGTH, 
                     "%ss skunk the %ss!", 
                     sWinning,
                     sLosing);
            //typesay( s, 10, 255, 255, 255 );
            SayMessage(s);
 
            ShowNextMap();
            ForceNextMap();
        }
        else
        if ( (iWinLimit > 0) &&
             (iWinning  == (iWinLimit - 1)) &&
             (iLosing == (iWinLimit - 1)) && 
             (g_DisplayScores) )
        {
            snprintf(s, 
                     MAX_DATA_LENGTH, 
                     "SUDDEN DEATH!! Winner take all!", 
                     sWinning);
            //typesay( s, 10, 255, 255, 255 );
            SayMessage(s);
        }
        else
        if ( (iWinLimit > 0) &&
             (iWinning == (iWinLimit - 1)) && 
             (g_DisplayScores) )
        {
            snprintf(s, 
                     MAX_DATA_LENGTH, 
                     "Possible %s victory next round.", 
                     sWinning);
            //typesay( s, 10, 255, 255, 255 );
            SayMessage(s);
        }
        else
        if ( (iWinLimit > 0) &&
             (iWinning >= (iWinLimit)) &&
             (g_DisplayScores) )
        {
            snprintf(s, 
                     MAX_DATA_LENGTH, 
                     "%ss win!!!", 
                     sWinning);
            //typesay( s, 10, 255, 255, 255 );
            SayMessage(s);
 
            ShowNextMap();
        }
 
        // start a map vote if requested
        if ( (g_StartVote) &&
             (!g_VoteStarted) &&
             ((iWinLimit - iWinning) == g_StartVoteRound) )
        {
            DebugMessage("Starting map vote due to round count");
            plugin_exec("admin_startvote", "");
            g_VoteStarted = 1;
        }
    }
 
    return PLUGIN_CONTINUE;
}
 
/*
*
*
*  Adminmod functions
*
*/
 
public HandleSay(HLCommand, HLData, HLUserName, UserIndex)
{
    if (!g_Enabled)
        return PLUGIN_CONTINUE;
 
    new iUserIDA;
    new iWONIDA;
    new iTeamA;
    new sNameA[MAX_NAME_LENGTH];
    new sNameV[MAX_NAME_LENGTH];
    new sAuthIDA[MAX_AUTHID_LENGTH];
    new sAuthIDV[MAX_AUTHID_LENGTH];    
    new sData[MAX_DATA_LENGTH];
    new iUserIDV;
    new iWONIDV;
    new iTeamV;
    new iDeadA;
    new iDeadV;
    new iReturn = PLUGIN_CONTINUE;
 
    convert_string(HLData, sData, MAX_DATA_LENGTH);
    strstripquotes(sData);
 
    sData[0] = sData[0] | 32;
 
    if ( (sData[0] != 'f') && 
         (sData[0] != 'p') && 
         (sData[0] != 'k') && 
         (sData[0] != 'b') && 
         (sData[0] != 's') && 
         (sData[0] != 'c') && 
         (sData[0] != 'o') && 
         (sData[0] != '!')  )
        return PLUGIN_CONTINUE;
 
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&  
         (!playerinfo(g_tkedby[UserIndex], sNameA, MAX_NAME_LENGTH, iUserIDA, iWONIDA, iTeamA, iDeadA, sAuthIDA)) )
    {
        LogMessage("HandleSay attacker playerinfo failed");
        return PLUGIN_CONTINUE;
    }
 
    if (!playerinfo(UserIndex, sNameV, MAX_NAME_LENGTH, iUserIDV, iWONIDV, iTeamV, iDeadV, sAuthIDV)) 
    {
        LogMessage("HandleSay victim playerinfo failed");
        return PLUGIN_CONTINUE;
    }
 
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData, "forgive", 7) == 0) || 
          (strncasecmp(sData, "!forgive", 8) == 0)) )
    {
        g_TkCount[g_tkedby[UserIndex]] -= 1;
        RemoveTk(sAuthIDA);
 
        g_tkedby[UserIndex] = USER_ID_INVALID;
 
        snprintf( sData, 
                  MAX_DATA_LENGTH, 
                  "%s decided to forgive %s and forfeit revenge.", 
                  sNameV,
                  sNameA); 
        SayMessage(sData);
 
        iReturn = PLUGIN_HANDLED;
    } 
    else 
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData,"slay",4) == 0) || 
          (strncasecmp(sData,"!slay",5) == 0) ||
          (strncasecmp(sData,"punishtk",8) == 0) || 
          (strncasecmp(sData,"!punishtk",9) == 0)) )
    {
        if ( (g_AllowPunish & ACTION_SLAY) && 
             (!IsImmune(sNameA)) )
        {
            if (!iDeadA)
            {
                slay(sAuthIDA);
 
                snprintf( sData, 
                          MAX_DATA_LENGTH, 
                          "%s decided to have %s executed for TK revenge.", 
                          sNameV,
                          sNameA); 
            }
            else
            {
                snprintf( sData,
                          MAX_DATA_LENGTH,
                          "%s is dead.  Revenge will happen next round.",
                          sNameA);
 
                g_TkRevenge[g_tkedby[UserIndex]] = ACTION_SLAY;
            }
 
            g_tkedby[UserIndex] = USER_ID_INVALID;
 
            SayMessage(sData);
 
            iReturn = PLUGIN_HANDLED;
        }
    } 
    else
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData,"ban",3) == 0) || 
          (strncasecmp(sData,"!ban",4) == 0)) )
    {
       if ( (g_AllowPunish & ACTION_BAN) && 
             (!IsImmune(sNameA)) )
        {
            BanUser(sAuthIDA, g_TkLimitBanTime);
 
            g_tkedby[UserIndex] = USER_ID_INVALID;
 
            snprintf( sData, 
                      MAX_DATA_LENGTH, 
                      "%s decided to have %s banned for TK revenge.", 
                      sNameV,
                      sNameA); 
            SayMessage(sData);
 
            iReturn = PLUGIN_HANDLED;
        }
    } 
    else
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData,"kick",4) == 0) || 
          (strncasecmp(sData,"!kick",5) == 0)) )
    {
        if ( (g_AllowPunish & ACTION_KICK) &&
             (!IsImmune(sNameA)) )
        {
            kick(sAuthIDA);
            g_tkedby[UserIndex] = USER_ID_INVALID;
 
            snprintf( sData, 
                      MAX_DATA_LENGTH, 
                      "%s decided to have %s kicked for TK revenge.", 
                      sNameV,
                      sNameA); 
            SayMessage(sData);
 
            iReturn = PLUGIN_HANDLED;
        }
    } 
    else
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData,"slap",4) == 0) || 
          (strncasecmp(sData,"!slap",5) == 0)) )
    {
        if ( (g_AllowPunish & ACTION_SLAP) &&
             (!IsImmune(sNameA)) )
        {
            if (!iDeadA)
            {
                new i;
 
                for (i = 0; i <= g_UserSlapCount; i++) 
                    slap(sAuthIDA);
 
                snprintf( sData, 
                          MAX_DATA_LENGTH, 
                          "%s decided to have %s slapped for TK revenge.", 
                          sNameV,
                          sNameA); 
            }
            else
            {
                snprintf( sData,
                          MAX_DATA_LENGTH,
                          "%s is dead.  Revenge will happen next round.",                     
                          sNameA);
 
                g_TkRevenge[g_tkedby[UserIndex]] = ACTION_SLAP;
            }
 
            g_tkedby[UserIndex] = USER_ID_INVALID;
 
            SayMessage(sData);
 
            iReturn = PLUGIN_HANDLED;
        }
    } 
    else
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData,"bury",4) == 0) || 
          (strncasecmp(sData,"!bury",5) == 0)) )
    {
        if ( (g_AllowPunish & ACTION_BURY) &&
             (!IsImmune(sNameA)) )
        {
            if (!iDeadA)
            {
                BuryUser(sAuthIDA);
 
                snprintf( sData, 
                          MAX_DATA_LENGTH, 
                          "%s decided to have %s buried for TK revenge.", 
                          sNameV,
                          sNameA); 
            }
            else
            {
                snprintf( sData,
                          MAX_DATA_LENGTH,
                          "%s is dead.  Revenge will happen next round.",                     
                          sNameA);
 
                g_TkRevenge[g_tkedby[UserIndex]] = ACTION_BURY;
            }
 
            g_tkedby[UserIndex] = USER_ID_INVALID;
 
            SayMessage(sData);
 
            iReturn = PLUGIN_HANDLED;
        }
    } 
    else
    if ( (g_tkedby[UserIndex] != USER_ID_INVALID) &&
         ((strncasecmp(sData,"chicken",7) == 0) || 
          (strncasecmp(sData,"!chicken",8) == 0)) )
    {
        if ( (g_AllowPunish & ACTION_CHICKEN) && 
             (!IsImmune(sNameA)) )
        {
            if (!iDeadA)
            {
                ChickenUser(sAuthIDA);
 
                snprintf( sData, 
                          MAX_DATA_LENGTH, 
                          "%s decided to have %s turned into a chicken for TK revenge.", 
                          sNameV,
                          sNameA); 
            }
            else
            {
                snprintf( sData,
                          MAX_DATA_LENGTH,
                          "%s is dead.  Revenge will happen next round.",                     
                          sNameA);
 
                g_TkRevenge[g_tkedby[UserIndex]] = ACTION_CHICKEN;
            }
 
            g_tkedby[UserIndex] = USER_ID_INVALID;
 
            SayMessage(sData);
 
            iReturn = PLUGIN_HANDLED;
        }
    } 
    else
    if ((strncasecmp(sData,"score",5) == 0) || 
        (strncasecmp(sData,"!score",6) == 0))
    {
        return ShowScore();
    }
    else
    if ((strncasecmp(sData,"overrideban",11) == 0) || 
        (strncasecmp(sData,"!overrideban",12) == 0))
    {
        if (strlen(g_LastTkBan) != 0)
        {
            if ( (g_AllowPunish & ACTION_OVERRIDE_BAN) && 
                 (access(g_UserOverrideLevel, sNameV) != 0) )
            {
                UnbanUser(g_LastTkBan);
 
                snprintf( sData, 
                          MAX_DATA_LENGTH, 
                          "%s removed the last ban.", 
                          sNameV); 
                SayMessage(sData);
 
                LogMessage("Last ban overridden.");
 
                iReturn = PLUGIN_HANDLED;
            }
        }
    } 
 
    return iReturn;
}
 
public plugin_connect(HLUserName, HLIP, UserIndex)
{
    new i;
 
    if (UserIndex >= 1 && UserIndex <= MAX_PLAYERS) 
    {
        g_TkCount[UserIndex] = TK_CLEAN_SLATE;
 
        // load the tk count from the file
        new sName[MAX_NAME_LENGTH];
        new sAuthID[MAX_AUTHID_LENGTH];
        new iUserID;
        new iWONID;
        new iTeam;
        new iDead;
 
        playerinfo(UserIndex, sName, MAX_NAME_LENGTH, iUserID, iWONID, iTeam, iDead, sAuthID);
 
        AgeTks(sAuthID);
 
        for (i = 1; i <= MAX_PLAYERS; i++) 
        {
            if (g_tkedby[i] == UserIndex) 
            {
                g_tkedby[i] = USER_ID_INVALID;
            }
        }
 
        g_tkedby[UserIndex] = USER_ID_INVALID;
        g_TdCount[UserIndex] = TD_CLEAN_SLATE;
        g_TkRevenge[UserIndex] = ACTION_NONE;
 
        ResetMenu(UserIndex);
    }
 
    DisplayConsgreet();
 
    return PLUGIN_CONTINUE;
}
 
public plugin_disconnect(HLUserName, UserIndex)
{   
    new i;
 
    if (UserIndex >= 1 && UserIndex <= MAX_PLAYERS) 
    {
        g_TkCount[UserIndex] = TK_CLEAN_SLATE;
 
        for (i = 1; i <= MAX_PLAYERS; i++) 
        {
            if (g_tkedby[i] == UserIndex) 
            {
                g_tkedby[i] = USER_ID_INVALID;
            }
        }
 
        g_tkedby[UserIndex] = USER_ID_INVALID;
        g_TdCount[UserIndex] = TD_CLEAN_SLATE;
        g_TkRevenge[UserIndex] = ACTION_NONE;
 
        UnchickenUser(UserIndex);
        ClearTkMenu(UserIndex);
    }
 
    return PLUGIN_CONTINUE;
}
 
 
public admin_ffmon(HLCommand, HLData, HLUserName, UserIndex) 
{
    new sData[MAX_DATA_LENGTH];
    new sCommand[MAX_DATA_LENGTH];
    new sArgument[MAX_DATA_LENGTH];
    new iDisplayStatus = 0;
 
    convert_string(HLData, sData, MAX_DATA_LENGTH);
 
    strinit(sCommand);
    strinit(sArgument);
 
    if (!strlen(sData)) 
    {
        if (!access(ACCESS_FFMON, ""))
        {
            iDisplayStatus = 1;
        }
        else
        {
            DisplayHelp();
            return PLUGIN_HANDLED;
        }
    }
    else 
    {
        strsplit(sData, " ", sCommand, MAX_DATA_LENGTH, sArgument, MAX_DATA_LENGTH);
    }
 
    if (streq(sCommand, "status")) 
    { 
        iDisplayStatus = 1;
    }
    else
    if (streq(sCommand, "showtk"))
    {
        DisplayTk();
        return PLUGIN_HANDLED;
    }
    else
    if (streq(sCommand, "showtd"))
    {
        DisplayTd();
        return PLUGIN_HANDLED;
    }
 
    if (!access(ACCESS_FFMON, "")) 
    {
        selfmessage("FFMon: You are not allowed to use this command");
        return PLUGIN_HANDLED;
    }
 
    if (streq(sCommand, "help"))
    {
        DisplayHelp();
        return PLUGIN_HANDLED;
    }
    else
    if (streq(sCommand, "on"))
    {
        g_Enabled = 1;
        SaveConfiguration();
        iDisplayStatus = 1;
    }
    else
    if (streq(sCommand, "off"))
    {
        g_Enabled = 0;
        SaveConfiguration();
        iDisplayStatus = 1;
    }
 
    if (iDisplayStatus)
    {
        DisplayParamState("FFMon", g_Enabled);
    }
 
    if (streq(sCommand, "debug") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_DebugMessages = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Debug messages", g_DebugMessages);
    }
 
    if (streq(sCommand, "tk") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_TkProtection = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("TK monitoring", g_TkProtection);
    }
 
    if (streq(sCommand, "tklimit") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TkLimit = strtonum(sArgument);
            SaveConfiguration();
        }
 
        if (g_TkLimit < 1) 
            g_TkLimit = 1;
 
        DisplayParamValue("TK limit before banning", g_TkLimit);
    }
 
    if (streq(sCommand, "tkbantime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TkLimitBanTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("TK limit ban time", g_TkLimitBanTime);
    }
 
    if (streq(sCommand, "tkmenu") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_TkMenu = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("TK menu", g_TkMenu);
    }
 
    if (streq(sCommand, "tkreset") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_TkReset = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("TK reset", g_TkReset);
    }
 
    if (streq(sCommand, "tksave") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_TkSave = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("TK save", g_TkSave);
    }
 
    if (streq(sCommand, "tksavetime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TkSaveTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("TK save time", g_TkSaveTime);
    }
 
    if (streq(sCommand, "tksavelimit") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TkSaveLimit = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("TK save limit", g_TkSaveLimit);
    }
 
    if (streq(sCommand, "tksavebantime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TkSaveBanTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("TK save ban time", g_TkSaveBanTime);
    }
 
    if (streq(sCommand, "tksavepurgetime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TkSavePurgeTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("TK save purge time", g_TkSavePurgeTime);
    }
 
    if (streq(sCommand, "td") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_TdProtection = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("TD monitoring", g_TdProtection);
    }
 
    if (streq(sCommand, "tdlimit") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_TdLimit = strtonum(sArgument);
            SaveConfiguration();
        }
 
        if (g_TdLimit < 1) 
            g_TdLimit = 1;
 
        DisplayParamValue("TD limit before slaying", g_TdLimit);
    }
 
    if (streq(sCommand, "tdaction") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_TdAction = check_action(sArgument);
            SaveConfiguration();
        }
 
        new sAction[MAX_TEXT_LENGTH];
        GetActionString(g_TdAction, sAction);
 
        DisplayParamString("TD abuser", sAction);
    }
 
    if (streq(sCommand, "allowpunish") || iDisplayStatus)
    {
        new sCurrent[MAX_DATA_LENGTH];
        new sNext[MAX_DATA_LENGTH];
 
        if (strlen(sArgument))
        {
            strsep(sData, " ", sCommand, MAX_DATA_LENGTH, sArgument, MAX_DATA_LENGTH);
 
            // walk the command line added each option
            while (strlen(sArgument))
            {
                strsep(sArgument, " ", sCurrent, MAX_DATA_LENGTH, sNext, MAX_DATA_LENGTH);
 
                // do the appropriate thing with this action
                ParseAction(sCurrent);
 
                // move to the next item
                strcpy(sArgument, sNext, MAX_DATA_LENGTH);
            }
 
            SaveConfiguration();
        }
 
        sCurrent = "";
        BuildPunnishmentList(sCurrent);
 
        DisplayParamString("Punishments allowed", sCurrent);
    }
 
    if (streq(sCommand, "userslapcount") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_UserSlapCount = strtonum(sArgument);
            SaveConfiguration();
        }
 
        if (g_UserSlapCount < 1) 
            g_UserSlapCount = 1;
 
        DisplayParamValue("User slap count after TK", g_UserSlapCount);
    }
 
    if (streq(sCommand, "overridelevel") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_UserOverrideLevel = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("User override access level", g_UserOverrideLevel);
    }
 
    if (streq(sCommand, "buryglowtime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_BuryGlowTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Glow time after a bury", g_BuryGlowTime);
    }
 
    if (streq(sCommand, "rs") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_RoundStartTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Round start time", g_RoundStartTime);
    }
 
    if (streq(sCommand, "rsbantk") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_RoundStartBanTk = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Round start TK banning", g_RoundStartBanTk);
    }
 
    if (streq(sCommand, "rsbantktime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_RoundStartBanTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Round start TK ban time", g_RoundStartBanTime);
    }
 
    if (streq(sCommand, "rstdslap") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_RoundStartTdSlap = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Round start TD slapping", g_RoundStartTdSlap);
    }
 
    if (streq(sCommand, "rstdslapcount") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_RoundStartTdSlapTimes = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Round start TD slap count", g_RoundStartTdSlapTimes);
    }
 
    if (streq(sCommand, "rstdaction") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_RoundStartTdAction = check_action(sArgument);
            SaveConfiguration();
        }
 
        new sAction[MAX_TEXT_LENGTH];
        GetActionString(g_RoundStartTdAction, sAction);
 
        DisplayParamString("Round start TD abuser", sAction);
    }
 
    if (streq(sCommand, "rstdcount") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_RoundStartTdCount = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Round start TD count for action", g_RoundStartTdCount);
    }
 
    if (streq(sCommand, "rstdbantime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_RoundStartTdBanTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Round start TD ban time", g_RoundStartTdBanTime);
    }
 
    if (streq(sCommand, "ss") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_SpawnShotProtection = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Spawn shot monitoring", g_SpawnShotProtection);
    }
 
    if (streq(sCommand, "sstime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_SpawnShotTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Spawn shot monitor time", g_SpawnShotTime);
    }
 
    if (streq(sCommand, "ssaction") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_SpawnShotAction = check_action(sArgument);
            SaveConfiguration();
        }
 
        new sAction[MAX_TEXT_LENGTH];
        GetActionString(g_SpawnShotAction, sAction);
 
        DisplayParamString("Spawn shooters", sAction);
    }
 
    if (streq(sCommand, "ssbantime") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_SpawnShotBanTime = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Spawn shot ban time", g_SpawnShotBanTime);
    }
 
    if (streq(sCommand, "skunk") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_SkunkProtection = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Skunk monitoring", g_SkunkProtection);
    }
 
    if (streq(sCommand, "skunktype") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_SkunkType = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Skunk type", g_SkunkType);
    }
 
    if (streq(sCommand, "skunklimit") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_SkunkLimit = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Skunk limit", g_SkunkLimit);
    }
 
    if (streq(sCommand, "immunity") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_Immunity = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Admin immunity", g_Immunity);
    }
 
    if (streq(sCommand, "immunitylevel") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_ImmunityLevel = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Immunity level", g_ImmunityLevel);
    }
 
    if (streq(sCommand, "scores") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_DisplayScores = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Team score display", g_DisplayScores);
    }
 
    if (streq(sCommand, "log") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_LogMessages = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Message logging", g_LogMessages);
    }
 
    if (streq(sCommand, "consgreet") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_DisplayConsgreet = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Console greeting", g_DisplayConsgreet);
    }
 
    if (streq(sCommand, "blockattack") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_BlockAttackMessages = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Attack message blocking", g_BlockAttackMessages);
    }
 
    if (streq(sCommand, "startvote") || iDisplayStatus)
    {
        if (strlen(sArgument))
        {
            g_StartVote = check_param(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamState("Round vote starting", g_StartVote);
    }
 
    if (streq(sCommand, "startvoteround") || iDisplayStatus) 
    {
        if (strlen(sArgument))
        {
            g_StartVoteRound = strtonum(sArgument);
            SaveConfiguration();
        }
 
        DisplayParamValue("Start vote round", g_StartVoteRound);
    }
 
    return PLUGIN_HANDLED;
}
 
public menuselect(HLCommand, HLData, HLUserName, UserIndex)
{
    if (!g_Enabled)
        return PLUGIN_CONTINUE;
 
    new iSessionID = 0;
    new iWONID = 0;
    new iTeam = 0;
    new iDead = 0;
    new sName[MAX_NAME_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];
 
    new Data[MAX_DATA_LENGTH];
 
    if (!g_TkMenu)
        return PLUGIN_CONTINUE;
 
    convert_string(HLData, Data, MAX_DATA_LENGTH);
    new iMenu = strtonum(Data);
 
    if (g_MenuStates[UserIndex][MENU_STATE_OPEN] == 0)
    {
        /*User was not in the menu*/
        g_MenuStates[UserIndex][MENU_STATE_KILLED] = 0; 
 
        return PLUGIN_CONTINUE;
    }
 
    if (g_MenuStates[UserIndex][MENU_STATE_KILLED] == 1)
    {
        /*User was in menu system when a vote got called*/
        g_MenuStates[UserIndex][MENU_STATE_KILLED] = 0;
 
        DisplayTkMenu(UserIndex);
 
        return PLUGIN_CONTINUE;
    }
 
    if (iMenu > 10 || iMenu < 0)
    { 
        /*There is no case for this, someone else can deal with it*/
        return PLUGIN_CONTINUE;
    } 
 
    playerinfo(UserIndex, sName, MAX_NAME_LENGTH, iSessionID, iWONID, iTeam, iDead, sAuthID);
 
    ClearTkMenu(UserIndex);
 
    if (iMenu > 0 && iMenu < MAX_TK_OPTIONS)
    { 
        // do whatever action they chose
        execclient(sName, g_TkMenuAction[iMenu - 1]);
    }
 
    return PLUGIN_HANDLED;
}
 
/*
 
    Watch for vote commands and lurk around them
 
*/
public handlevote(HLCommand, HLData, HLUserName, UserIndex)
{
    new sName[MAX_NAME_LENGTH];
    new sAuthID[MAX_AUTHID_LENGTH];
    new iSessionID;
    new iTeam;
    new iWONID;
    new iDead;
    new i;
 
    for (i = 1; i <= maxplayercount(); i++)
    {
        strinit(sName);
        if (playerinfo(i, sName, MAX_NAME_LENGTH, iSessionID, iWONID, iTeam, iDead, sAuthID) == 1) 
        {
            g_MenuStates[i][MENU_STATE_KILLED] = 1;
        }
    }
 
    return PLUGIN_CONTINUE;
}
 
public resetstate(HLCommand, HLData, HLUserName, UserIndex)
{
    ResetMenu(UserIndex);
 
    return PLUGIN_CONTINUE;
}
 
public admin_ffmon_purge(HLCommand, HLData, HLUserName, UserIndex) 
{
    PurgeTkFiles();
}
 
public admin_ffmon_reload(HLCommand, HLData, HLUserName, UserIndex) 
{
    LoadConfiguration();
}
 
public plugin_init()
{
    plugin_registerinfo("FFMon", "Helps manage a Friendly Fire server.", STRING_VERSION);
 
    plugin_registercmd("ffmon_checktk",     "ffmon_checktk", ACCESS_CONSOLE);
    plugin_registercmd("ffmon_worldaction", "ffmon_worldaction",   ACCESS_CONSOLE);
    plugin_registercmd("ffmon_teamaction",  "ffmon_teamaction",   ACCESS_CONSOLE);
    plugin_registercmd("ffmon_checktd",     "ffmon_checktd", ACCESS_CONSOLE);
 
    plugin_registercmd("admin_ffmon", "admin_ffmon", ACCESS_FFMON, "admin_ffmon");
    plugin_registercmd("admin_ffmon_purge", "admin_ffmon_purge", ACCESS_FFMON, "admin_ffmon_purge");
    plugin_registercmd("admin_ffmon_reload", "admin_ffmon_reload", ACCESS_FFMON, "admin_ffmon_reload");
    plugin_registercmd("say","HandleSay",ACCESS_ALL);
 
    /* menu specific handlers */
    plugin_registercmd("admin_vote_map", "handlevote", ACCESS_ALL, "");
    plugin_registercmd("admin_vote_kick", "handlevote", ACCESS_ALL, "");
    plugin_registercmd("admin_vsay", "handlevote", ACCESS_ALL, "");
 
    plugin_registercmd("menuselect", "menuselect", ACCESS_ALL);
 
    plugin_registercmd("radio1", "resetstate", ACCESS_ALL);
    plugin_registercmd("radio2", "resetstate", ACCESS_ALL);
    plugin_registercmd("radio3", "resetstate", ACCESS_ALL);
    plugin_registercmd("buy", "resetstate", ACCESS_ALL);
    plugin_registercmd("buyequip", "resetstate", ACCESS_ALL);
    plugin_registercmd("chooseteam", "resetstate", ACCESS_ALL);
    /* end menu specific */
 
    exec( "logd_reg 57 admin_command ffmon_checktk" );
    exec( "logd_reg 58 admin_command ffmon_checktd" );
    exec( "logd_reg 61 admin_command ffmon_teamaction" );
    exec( "logd_reg 62 admin_command ffmon_worldaction" );
 
    LoadConfiguration();
    SetLogDetail();
    ForceNextMapUndo();
 
    CheckPeriodicPurge();
 
    g_VoteStarted = 0;
    g_CtScore     = 0;
    g_TScore      = 0;
 
    return PLUGIN_CONTINUE;
}