// keepaway.cpp : Defines the entry point for the DLL application.

#include "bzfsAPI.h"
#include <map>
#include <cmath>

class KeepAwayMapHandler : public bz_CustomMapObjectHandler
{
public:
    virtual bool MapObject ( bz_ApiString object, bz_CustomMapObjectInfo *data );
};

KeepAwayMapHandler  keepawaymaphandler;

class KeepAwayEventHandler : public bz_Plugin
{
public:
    virtual const char* Name()
    {
        return "Keep Away";
    }
    virtual void Init ( const char* config );
    virtual void Cleanup ( void );
    virtual void Event ( bz_EventData *eventData );
};

BZ_PLUGIN(KeepAwayEventHandler)

class KeepAwayCommands : public bz_CustomSlashCommandHandler
{
public:
    virtual ~KeepAwayCommands() {};
    virtual bool SlashCommand ( int playerID, bz_ApiString command, bz_ApiString message, bz_APIStringList *param );
};

KeepAwayCommands keepawaycommands;

void KeepAwayEventHandler::Init(const char* /*commandLine*/)
{
    MaxWaitTime = 0.5f;

    bz_registerCustomMapObject("keepaway",&keepawaymaphandler);
    Register(bz_ePlayerUpdateEvent);
    Register(bz_ePlayerPausedEvent);
    Register(bz_ePlayerPartEvent);
    Register(bz_ePlayerJoinEvent);
    Register(bz_ePlayerDieEvent);
    bz_registerCustomSlashCommand("kastatus",&keepawaycommands);
    bz_registerCustomSlashCommand("kaon",&keepawaycommands);
    bz_registerCustomSlashCommand("kaoff",&keepawaycommands);
    bz_registerCustomSlashCommand("katimemult",&keepawaycommands);
    bz_registerCustomSlashCommand("katimemultmin",&keepawaycommands);
    bz_registerCustomSlashCommand("katime",&keepawaycommands);
    bz_registerCustomSlashCommand("kaautotimeon",&keepawaycommands);
    bz_registerCustomSlashCommand("kaautotimeoff",&keepawaycommands);
    bz_registerCustomSlashCommand("kas",&keepawaycommands);
    bz_registerCustomSlashCommand("kaffon",&keepawaycommands);
    bz_registerCustomSlashCommand("kaffoff",&keepawaycommands);
    bz_registerCustomSlashCommand("kaf",&keepawaycommands);
    bz_registerCustomSlashCommand("kaf+",&keepawaycommands);
    bz_registerCustomSlashCommand("kasoundon",&keepawaycommands);
    bz_registerCustomSlashCommand("kasoundoff",&keepawaycommands);
    bz_registerCustomSlashCommand("kaflagreseton",&keepawaycommands);
    bz_registerCustomSlashCommand("kaflagresetoff",&keepawaycommands);
}

void KeepAwayEventHandler::Cleanup(void)
{
    Flush();
    bz_removeCustomMapObject("keepaway");
    bz_removeCustomSlashCommand("kastatus");
    bz_removeCustomSlashCommand("kaon");
    bz_removeCustomSlashCommand("kaoff");
    bz_removeCustomSlashCommand("katimemult");
    bz_removeCustomSlashCommand("katimemultmin");
    bz_removeCustomSlashCommand("katime");
    bz_removeCustomSlashCommand("kaautotimeon");
    bz_removeCustomSlashCommand("kaautotimeoff");
    bz_removeCustomSlashCommand("kas");
    bz_removeCustomSlashCommand("kaffon");
    bz_removeCustomSlashCommand("kaffoff");
    bz_removeCustomSlashCommand("kaf");
    bz_removeCustomSlashCommand("kaf+");
    bz_removeCustomSlashCommand("kasoundon");
    bz_removeCustomSlashCommand("kasoundoff");
    bz_removeCustomSlashCommand("kaflagreseton");
    bz_removeCustomSlashCommand("kaflagresetoff");
}

class KeepAway
{
public:
    KeepAway()
    {
        id = -1;
        startTime = 0;
        team = eNoTeam;
        callsign = "";
        flagsList.clear();
        TTH = 120;
        adjustedTime = 120;
        timeMult = 0.03;
        timeMultMin = 0.50;
        lastReminder = bz_getCurrentTime();
        reminderPeriod = 60;
        enabled = true;
        toldFlagFree = false;
        oneTeamWarn = false;
        autoTimeOn = false;
        forcedFlags = false;
        notEnoughTeams = true;
        soundEnabled = true;
        teamPlay = false;
        flagResetEnabled = true;
        TTHminutes = 0;
        TTHseconds = 30;
        flagToKeepIndex = 0;
        flagToKeep = "Initiate";
    }
    bz_eTeamType team;
    std::string callsign;
    std::string flagToKeep;
    std::vector <std::string> flagsList;
    bool teamPlay;
    double TTH;
    double adjustedTime;
    double timeMult;
    double timeMultMin;
    double lastReminder;
    double reminderPeriod;
    double startTime;
    bool enabled;
    bool toldFlagFree;
    bool oneTeamWarn;
    bool autoTimeOn;
    bool forcedFlags;
    bool notEnoughTeams;
    bool soundEnabled;
    bool flagResetEnabled;
    int TTHminutes;
    int TTHseconds;
    int flagToKeepIndex;
    int id;
};

KeepAway keepaway;

std::string convertFlag(std::string flagAbbrev)
{
    if (flagAbbrev == "V")
        return "High Speed (V)";
    if (flagAbbrev == "QT")
        return "Quick Turn (QT)";
    if (flagAbbrev == "A")
        return "Agility (A)";
    if (flagAbbrev == "OO")
        return "Oscillation Overthruster (OO)";
    if (flagAbbrev == "F")
        return "Rapid Fire (F)";
    if (flagAbbrev == "MG")
        return "Machine Gun (MG)";
    if (flagAbbrev == "GM")
        return "Guided Missile (GM)";
    if (flagAbbrev == "L")
        return "Laser (L)";
    if (flagAbbrev == "R")
        return "Ricochet (R)";
    if (flagAbbrev == "SB")
        return "Super Bullet (SB)";
    if (flagAbbrev == "ST")
        return "Stealth (ST)";
    if (flagAbbrev == "CL")
        return "Cloaking (CL)";
    if (flagAbbrev == "IB")
        return "Invisible Bullet (IB)";
    if (flagAbbrev == "T")
        return "Tiny (T)";
    if (flagAbbrev == "N")
        return "Narrow (N)";
    if (flagAbbrev == "SH")
        return "Shield (SH)";
    if (flagAbbrev == "SR")
        return "Steamroller (SR)";
    if (flagAbbrev == "SW")
        return "ShockWave (SW)";
    if (flagAbbrev == "PZ")
        return "Phantom Zone (PZ)";
    if (flagAbbrev == "G")
        return "Genocide (G)";
    if (flagAbbrev == "JP")
        return "Jumping (JP)";
    if (flagAbbrev == "ID")
        return "Identify (ID)";
    if (flagAbbrev == "MQ")
        return "Masquerade (MQ)";
    if (flagAbbrev == "BU")
        return "Burrow (BU)";
    if (flagAbbrev == "SE")
        return "Seer (SE)";
    if (flagAbbrev == "TH")
        return "Thief (TH)";
    if (flagAbbrev == "US")
        return "Useless (US)";
    if (flagAbbrev == "WG")
        return "Wings (WG)";
    if (flagAbbrev == "CB")
        return "Colorblindness (CB)";
    if (flagAbbrev == "OB")
        return "Obesity (OB)";
    if (flagAbbrev == "LT")
        return "Left Turn Only (LT)";
    if (flagAbbrev == "RT")
        return "Right Turn Only (RT)";
    if (flagAbbrev == "FO")
        return "Forward Only (FO)";
    if (flagAbbrev == "RO")
        return "Reverse Only (RO)";
    if (flagAbbrev == "M")
        return "Momentum (M)";
    if (flagAbbrev == "B")
        return "Blindness (B)";
    if (flagAbbrev == "JM")
        return "Jamming (JM)";
    if (flagAbbrev == "WA")
        return "Wide Angle (WA)";
    if (flagAbbrev == "NJ")
        return "No Jumping (NJ)";
    if (flagAbbrev == "TR")
        return "Trigger Happy (TR)";
    if (flagAbbrev == "RC")
        return "Reverse Controls (RC)";
    if (flagAbbrev == "R*")
        return "Red Team (R*)";
    if (flagAbbrev == "G*")
        return "Green Team (G*)";
    if (flagAbbrev == "B*")
        return "Blue Team (B*)";
    if (flagAbbrev == "P*")
        return "Purple Team (P*)";

    return "";
}

bool KeepAwayMapHandler::MapObject ( bz_ApiString object, bz_CustomMapObjectInfo *data )
{
    if (object != "KEEPAWAY" || !data)
        return false;

    // parse all the chunks
    for ( unsigned int i = 0; i < data->data.size(); i++ )
    {
        std::string line = data->data.get(i).c_str();

        bz_APIStringList *nubs = bz_newStringList();
        nubs->tokenize(line.c_str()," ",0,true);

        if ( nubs->size() > 0)
        {
            std::string key = bz_toupper(nubs->get(0).c_str());

            if ( key == "TEAMPLAY" && nubs->size() > 0 )
                keepaway.teamPlay = true;

            else if ( key == "AUTOTIME" && nubs->size() > 0 )
                keepaway.autoTimeOn = true;

            else if ( key == "AUTOTIME" && nubs->size() > 2 )
            {
                double temp1 = (double)atof(nubs->get(1).c_str());
                double temp2 = (double)atof(nubs->get(2).c_str());
                if (temp1 >= 1 && temp1 <= 99) // if parameter out of range, keeps default
                    keepaway.timeMult = temp1 / 100;
                if (temp2 >= 1 && temp2 <= 99) // if parameter out of range, keeps default
                    keepaway.timeMultMin = temp2 / 100;
                keepaway.autoTimeOn = true;
            }

            else if ( key == "NOSOUND" && nubs->size() > 0 )
                keepaway.soundEnabled = false;

            else if ( key == "NOFLAGRESET" && nubs->size() > 0 )
                keepaway.flagResetEnabled = false;

            else if ( key == "HOLDTIME" && nubs->size() > 1 )
            {
                double temp = (double)atof(nubs->get(1).c_str());
                if (temp >= 1 && temp <= 7200) // if parameter out of range, keeps default
                    keepaway.TTH = temp;
            }

            else if ( key == "KEEPAWAYFLAGS" && nubs->size() > 1)
            {
                for (unsigned int j = 1; j < nubs->size(); j++)
                {
                    std::string flag = nubs->get(j).c_str();
                    if (convertFlag(flag) != "")  // must be valid flag type, reject nub otherwise
                        keepaway.flagsList.push_back(flag);
                }
            }

            else if ( key == "FORCEDFLAGS" && nubs->size() > 0 )
                keepaway.forcedFlags = true;
        }
        bz_deleteStringList(nubs);
    }

    if (!keepaway.flagsList.empty())
        keepaway.flagToKeepIndex = -1; // this will increment 1 when we get to getFlag() function;
    else
    {
        keepaway.flagToKeep = ""; // map file didn't give us any flags
        keepaway.flagToKeepIndex = 0;
    }

    return true;
}

std::string truncate(std::string cllsn, int maxStringLength)
{
    std::string fixed = "";

    for (int i = 0; i < maxStringLength; i++)
        fixed.push_back(cllsn[i]);

    fixed.append("~");

    return fixed;
}

const char* getTeamColor(bz_eTeamType testteam)
{
    if (testteam == eRedTeam)
        return "RED";
    if (testteam == eGreenTeam)
        return "GREEN";
    if (testteam == eBlueTeam)
        return "BLUE";
    if (testteam == ePurpleTeam)
        return "PURPLE";
    if (testteam == eRogueTeam)
        return "ROGUE";

    return "";
}

inline bool oneTeam(bz_eTeamType leavingPlayerTeam)
{
    int RT = bz_getTeamCount(eRedTeam);
    int GT = bz_getTeamCount(eGreenTeam);
    int BT = bz_getTeamCount(eBlueTeam);
    int PT = bz_getTeamCount(ePurpleTeam);
    int RGT = bz_getTeamCount(eRogueTeam);

    if (leavingPlayerTeam == eRedTeam)
        RT--;
    if (leavingPlayerTeam == eGreenTeam)
        GT--;
    if (leavingPlayerTeam == eBlueTeam)
        BT--;
    if (leavingPlayerTeam == ePurpleTeam)
        PT--;
    if (leavingPlayerTeam == eRogueTeam)
        RGT--;

    int Test1 = (RT * GT) + (RT * BT) + (RT * PT) + (GT * BT) + (GT * PT) + (BT * PT);
    int Test2 = RT + GT + BT + PT + RGT;

    if (Test1 < 1 && Test2 < 2)
    {
        if (!keepaway.oneTeamWarn)
            bz_sendTextMessage (BZ_SERVER, BZ_ALLUSERS, "Keep Away disabled: less than 2 teams.");

        keepaway.oneTeamWarn = true;
        return true;
    }
    else
    {
        if (keepaway.oneTeamWarn)
            bz_sendTextMessage (BZ_SERVER, BZ_ALLUSERS, "Keep Away enabled: more than 1 team.");

        keepaway.oneTeamWarn = false;
        return false;
    }
}

void autoTime()
{
    int numPlayers = bz_getTeamCount(eRedTeam) + bz_getTeamCount(eGreenTeam) + bz_getTeamCount(eBlueTeam) + bz_getTeamCount(
                         ePurpleTeam) + bz_getTeamCount(eRogueTeam);

    if (!keepaway.autoTimeOn || numPlayers < 3)
    {
        keepaway.adjustedTime = keepaway.TTH;
        return;
    }

    double timeDown = ( 1 - ((double)numPlayers - 2) * keepaway.timeMult);

    if (timeDown < keepaway.timeMultMin)
        timeDown = keepaway.timeMultMin;

    keepaway.adjustedTime = (int)(keepaway.TTH * timeDown);

    return;
}

double ConvertToNum(std::string inmessage, double minNum, double maxNum)
{

    int messagelength = (int)inmessage.length();

    if (messagelength > 0 && messagelength < 5)
    {
        double messagevalue = 0;
        double tens = 1;

        for ( int i = (messagelength - 1); i >= 0; i-- )
        {

            if (inmessage[i] < '0' || inmessage[i] > '9')  // got something other than a number
                return 0;

            tens *= 10;
            messagevalue +=  (((double)inmessage[i] - '0') / 10) * tens;
        }

        if (messagevalue >= minNum && messagevalue <= maxNum)
            return messagevalue;
    }

    return 0;
}

void killTeams(bz_eTeamType safeteam, std::string keepawaycallsign)
{
    bz_APIIntList *playerList = bz_newIntList();
    bz_getPlayerIndexList ( playerList );

    for ( unsigned int i = 0; i < playerList->size(); i++ )
    {

        bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));

        if (player)
        {

            if (player->team != safeteam)
            {
                bz_killPlayer(player->playerID, true, BZ_SERVER);
                if (keepaway.soundEnabled)
                    bz_sendPlayCustomLocalSound(player->playerID,"flag_lost");
            }
            else if (keepaway.soundEnabled)
                bz_sendPlayCustomLocalSound(player->playerID,"flag_won");
        }

        bz_freePlayerRecord(player);
    }
    bz_deleteIntList(playerList);

    bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) Kept the Flag Away!", getTeamColor(safeteam),
                         keepawaycallsign.c_str());

    if (keepaway.flagResetEnabled)
        bz_resetFlags(true);

    return;
}

void killPlayers(int safeid, std::string keepawaycallsign)
{
    bz_APIIntList *playerList = bz_newIntList();
    bz_getPlayerIndexList ( playerList );

    for ( unsigned int i = 0; i < playerList->size(); i++ )
    {

        bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));

        if (player)
        {

            if (player->playerID != safeid)
            {
                bz_killPlayer(player->playerID, true, keepaway.id);
                if (keepaway.soundEnabled)
                    bz_sendPlayCustomLocalSound(player->playerID,"flag_lost");
            }
            else if (keepaway.soundEnabled)
                bz_sendPlayCustomLocalSound(player->playerID,"flag_won");
        }

        bz_freePlayerRecord(player);
    }

    bz_deleteIntList(playerList);

    bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s Kept the Flag Away!", keepawaycallsign.c_str());

    if (keepaway.flagResetEnabled)
        bz_resetFlags(true);

    return;
}

void sendWarnings(const char* teamcolor, std::string playercallsign, double keepawaystartedtime)
{
    double TimeElapsed = bz_getCurrentTime() - keepawaystartedtime;
    double TimeRemaining = keepaway.adjustedTime - TimeElapsed;
    int toTens = int((TimeRemaining + 5) / 10) * 10;

    if ((TimeRemaining/60) < keepaway.TTHminutes && keepaway.adjustedTime > 59 && TimeRemaining >= 1)
    {
        if (!keepaway.teamPlay || keepaway.team == eRogueTeam)
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s has %s flag; %i secs left!", playercallsign.c_str(),
                                 keepaway.flagToKeep.c_str(), toTens);
        else
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) has %s flag; %i secs left!", teamcolor, playercallsign.c_str(),
                                 keepaway.flagToKeep.c_str(), toTens);

        keepaway.TTHminutes--;
    }

    if (keepaway.adjustedTime < keepaway.TTHseconds)
    {
        keepaway.TTHseconds = keepaway.TTHseconds - 10;
        return;
    }

    if (TimeRemaining < keepaway.TTHseconds && TimeRemaining >= 1)
    {
        if (!keepaway.teamPlay || keepaway.team == eRogueTeam)
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s has %s flag; %i secs left!", playercallsign.c_str(),
                                 keepaway.flagToKeep.c_str(), keepaway.TTHseconds);
        else
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) has %s flag; %i secs left!", teamcolor, playercallsign.c_str(),
                                 keepaway.flagToKeep.c_str(), keepaway.TTHseconds);

        keepaway.TTHseconds = keepaway.TTHseconds - 10;
    }
    return;
}

std::string getFlag()
{
    if (keepaway.flagToKeepIndex < -1) // this should never happen, but save a crash if something goes nuts
        return "";

    // get next flag; if not free take it from player (if forced flags)

    for (unsigned int h = 0; h < keepaway.flagsList.size(); h++) // check all specified flags
    {
        keepaway.flagToKeepIndex++; // get next one in line

        if (keepaway.flagToKeepIndex > ((int)keepaway.flagsList.size() - 1)) // go back to start if at end
            keepaway.flagToKeepIndex = 0;

        std::string flagCandidate = keepaway.flagsList[keepaway.flagToKeepIndex];
        bool flagNotHeld = true;

        bz_APIIntList *playerList = bz_newIntList();
        bz_getPlayerIndexList ( playerList );

        for ( unsigned int i = 0; i < playerList->size(); i++ )
        {
            bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));

            if (player)
            {
                const char* playerFlag = bz_getPlayerFlag(player->playerID);
                if (playerFlag)
                {
                    if (playerFlag == flagCandidate && keepaway.forcedFlags) // take it, if forced flags
                    {
                        bz_removePlayerFlag (player->playerID);
                        bz_sendTextMessage (BZ_SERVER, player->playerID, "Sorry, server needs your flag for Keep Away :/");
                    }
                    if (playerFlag == flagCandidate && !keepaway.forcedFlags) // look for next free flag in list
                        flagNotHeld = false;
                }
            }
            bz_freePlayerRecord(player);
        }

        bz_deleteIntList(playerList);

        if (flagNotHeld)
            return flagCandidate;
    }

    if (!keepaway.flagsList.empty())  // we should never get here, but if we do keep going
        return keepaway.flagsList[0];
    else
        return "";
}

void initiatekeepaway(bz_eTeamType plyrteam, bz_ApiString plyrcallsign, int plyrID)
{
    keepaway.team = plyrteam;
    keepaway.callsign = plyrcallsign.c_str();

    if (keepaway.callsign.size() > 16)
    {
        std::string tofix = truncate(keepaway.callsign, 16);
        keepaway.callsign = tofix;
    }

    keepaway.id = plyrID;
    keepaway.startTime = bz_getCurrentTime();
    keepaway.TTHminutes = (int)(keepaway.adjustedTime/60 + 0.5);
    keepaway.TTHseconds = 30;
    keepaway.toldFlagFree = false;
    bool multipleof30 = false;

    if ((int)((keepaway.adjustedTime / 30) + 0.5) != (double)(keepaway.adjustedTime / 30))
        multipleof30 = false;
    else
        multipleof30 = true;

    if (!multipleof30)
    {
        if ((!keepaway.teamPlay || keepaway.team == eRogueTeam))
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s has %s flag; %i secs left!", keepaway.callsign.c_str(),
                                 keepaway.flagToKeep.c_str(), (int)keepaway.adjustedTime);
        else
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "%s (%s) has %s flag; %i secs left!", getTeamColor(keepaway.team),
                                 keepaway.callsign.c_str(), keepaway.flagToKeep.c_str(), (int)keepaway.adjustedTime);
    }

    if (keepaway.soundEnabled)
    {
        bz_APIIntList *playerList = bz_newIntList();
        bz_getPlayerIndexList ( playerList );

        for ( unsigned int i = 0; i < playerList->size(); i++ )
        {
            bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));

            if (player)
            {
                if ((player->team != keepaway.team || player->team == eRogueTeam) && player->playerID != keepaway.id)
                    bz_sendPlayCustomLocalSound(player->playerID,"flag_alert");
                else
                    bz_sendPlayCustomLocalSound(player->playerID,"teamgrab");
            }

            bz_freePlayerRecord(player);
        }
        bz_deleteIntList(playerList);
    }

    return;
}

void playAlert()
{
    bz_APIIntList *playerList = bz_newIntList();
    bz_getPlayerIndexList ( playerList );

    for ( unsigned int i = 0; i < playerList->size(); i++ )
    {
        bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));

        if (player)
            bz_sendPlayCustomLocalSound(player->playerID,"hunt_select");

        bz_freePlayerRecord(player);
    }
    bz_deleteIntList(playerList);

    return;
}

inline bool timeForReminder()
{
    double timeLeft = bz_getCurrentTime() - keepaway.lastReminder;
    if (timeLeft >= keepaway.reminderPeriod)
    {
        keepaway.lastReminder = bz_getCurrentTime();
        return true;
    }
    return false;
}

void KeepAwayPlayerPaused( bz_EventData *eventData )
{
    if (eventData->eventType != bz_ePlayerPausedEvent || !keepaway.enabled || keepaway.flagToKeep == "")
        return;

    bz_PlayerPausedEventData_V1 *PauseData = (bz_PlayerPausedEventData_V1*)eventData;

    bz_BasePlayerRecord *player = bz_getPlayerByIndex(PauseData->playerID);

    if (player)
    {
        const char* flagHeld = bz_getPlayerFlag(player->playerID);

        if (flagHeld)
        {
            if (flagHeld == keepaway.flagToKeep)
            {
                bz_removePlayerFlag (player->playerID);
                bz_sendTextMessage (BZ_SERVER, PauseData->playerID, "Flag removed - cannot pause while holding flag.");
                keepaway.id = -1;
                keepaway.team = eNoTeam;
                keepaway.toldFlagFree = false;
            }
        }
    }
    bz_freePlayerRecord(player);

    return;
}

void KeepAwayPlayerJoined ( bz_EventData *eventData )
{
    if (eventData->eventType != bz_ePlayerJoinEvent || !keepaway.enabled || keepaway.flagToKeep == "")
        return;

    bz_PlayerJoinPartEventData_V1 *joinData = (bz_PlayerJoinPartEventData_V1*)eventData;

    if (keepaway.flagToKeep == "Initiate") //first time server starts, first player initiates it.
    {
        keepaway.flagToKeep = getFlag();
        keepaway.lastReminder = bz_getCurrentTime();
    }

    autoTime();

    if (oneTeam(eNoTeam)) // don't send message if not enough teams
    {
        keepaway.notEnoughTeams = true;
        return;
    }
    else
        keepaway.notEnoughTeams = false;

    if (keepaway.id == -1 && keepaway.enabled && keepaway.flagToKeep != "")
    {
        bz_sendTextMessagef (BZ_SERVER, joinData->playerID, "Keep Away flag is %s: find it and keep it for %i seconds!",
                             convertFlag(keepaway.flagToKeep).c_str(), (int)keepaway.adjustedTime);
        if (keepaway.soundEnabled)
            bz_sendPlayCustomLocalSound(joinData->playerID,"hunt_select");
    }

    if (keepaway.id != -1 && keepaway.enabled && keepaway.flagToKeep != "" && (joinData->record->team != keepaway.team
            || joinData->record->team == eRogueTeam))
    {
        bz_sendTextMessagef (BZ_SERVER, joinData->playerID, "%s has Keep Away flag %s - kill him/her before time's up!",
                             keepaway.callsign.c_str(), convertFlag(keepaway.flagToKeep).c_str());
        if (keepaway.soundEnabled)
            bz_sendPlayCustomLocalSound(joinData->playerID,"flag_alert");
    }

    if (keepaway.id != -1 && keepaway.enabled && keepaway.flagToKeep != "" && (joinData->record->team == keepaway.team
            && joinData->record->team != eRogueTeam))
    {
        bz_sendTextMessagef (BZ_SERVER, joinData->playerID, "%s has Keep Away flag %s - protect him/her until time's up!",
                             keepaway.callsign.c_str(), convertFlag(keepaway.flagToKeep).c_str());
        if (keepaway.soundEnabled)
            bz_sendPlayCustomLocalSound(joinData->playerID,"teamgrab");
    }

    return;
}

void KeepAwayPlayerLeft( bz_EventData *eventData )
{
    if (eventData->eventType != bz_ePlayerPartEvent || !keepaway.enabled || keepaway.flagToKeep == "")
        return;

    autoTime();

    bz_PlayerJoinPartEventData_V1 *partData = (bz_PlayerJoinPartEventData_V1*)eventData;

    if (partData->playerID == keepaway.id)
    {
        keepaway.id = -1;
        keepaway.team = eNoTeam;
        keepaway.toldFlagFree = false;
    }

    if (oneTeam(partData->record->team)) // team count check
        keepaway.notEnoughTeams = true;
    else
        keepaway.notEnoughTeams = false;

    return;
}

void KeepAwayPlayerDied ( bz_EventData *eventData )
{
    if (eventData->eventType != bz_ePlayerDieEvent || !keepaway.enabled || keepaway.flagToKeep == "")
        return;

    bz_PlayerDieEventData_V1 *dieData = (bz_PlayerDieEventData_V1*)eventData;

    if (dieData->playerID == keepaway.id)
    {
        keepaway.id = -1;
        keepaway.team = eNoTeam;
        keepaway.toldFlagFree = false;
    }

    return;
}

inline void checkKeepAwayHolder()
{
    bz_APIIntList *playerList = bz_newIntList();
    bz_getPlayerIndexList ( playerList );

    for ( unsigned int i = 0; i < playerList->size(); i++ )
    {

        bz_BasePlayerRecord *player = bz_getPlayerByIndex(playerList->operator[](i));

        if (player)
        {
            const char* flagHeld = bz_getPlayerFlag(player->playerID);
            if (flagHeld)
            {
                if (flagHeld == keepaway.flagToKeep && keepaway.id == -1) // gotta a new one; initiate
                {
                    initiatekeepaway(player->team, player->callsign, player->playerID);
                    bz_freePlayerRecord(player);
                    bz_deleteIntList(playerList);
                    return;
                }
                if (flagHeld == keepaway.flagToKeep && keepaway.id == player->playerID) // someone still has it; leave
                {
                    bz_freePlayerRecord(player);
                    bz_deleteIntList(playerList);
                    return;
                }
                if (flagHeld == keepaway.flagToKeep && keepaway.id != player->playerID) // must have stolen it
                {
                    initiatekeepaway(player->team, player->callsign, player->playerID);
                    bz_freePlayerRecord(player);
                    bz_deleteIntList(playerList);
                    return;
                }
            }
        }

        bz_freePlayerRecord(player);
    }

    keepaway.id = -1;  // no one has flag
    keepaway.team = eNoTeam;

    bz_deleteIntList(playerList);

    return;
}

void KeepAwayEventHandler::Event ( bz_EventData *eventData )
{
    if (eventData->eventType == bz_ePlayerDieEvent)
        KeepAwayPlayerDied(eventData);
    else if (eventData->eventType == bz_ePlayerPartEvent)
        KeepAwayPlayerLeft(eventData);
    else if (eventData->eventType == bz_ePlayerJoinEvent)
        KeepAwayPlayerJoined(eventData);
    else if (eventData->eventType == bz_ePlayerPausedEvent)
        KeepAwayPlayerPaused(eventData);

    if (eventData->eventType != bz_ePlayerUpdateEvent || !keepaway.enabled || keepaway.flagToKeep == "")
        return;

    if (keepaway.notEnoughTeams) // Not enough teams - we can leave
        return;

    checkKeepAwayHolder(); // check for someone holding flag

    if (!keepaway.toldFlagFree && keepaway.id == -1) // Flag is free - inform players
    {
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away flag: %s is free; find it and keep it!",
                             convertFlag(keepaway.flagToKeep).c_str());
        keepaway.toldFlagFree = true;

        if ((bz_getCurrentTime() - keepaway.lastReminder) > 2
                && keepaway.soundEnabled) // do not play free flag sound alert if player just won/lost (overlapping sounds)
            playAlert();
    }

    if (timeForReminder() && keepaway.id == -1)
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away flag: %s is free; find it and keep it!",
                             convertFlag(keepaway.flagToKeep).c_str());

    if (keepaway.id == -1)  // no one has it, we can leave
        return;

    sendWarnings(getTeamColor(keepaway.team), keepaway.callsign, keepaway.startTime);

    double timeStanding = bz_getCurrentTime() - keepaway.startTime;

    if (timeStanding >= keepaway.adjustedTime) // time's up - kill 'em
    {
        if (keepaway.teamPlay && keepaway.team != eRogueTeam)
        {
            killTeams(keepaway.team, keepaway.callsign);
            bz_sendTextMessage (BZ_SERVER, keepaway.team, "Your team did it!  Go find the next Keep Away flag and keep it!");
        }
        else
        {
            killPlayers(keepaway.id, keepaway.callsign);
            bz_sendTextMessage (BZ_SERVER, keepaway.id, "You did it!  Go find the next Keep Away flag and keep it!");
        }

        if (!keepaway.forcedFlags)  // this will always create an open spot for getFlag(), if it's needed
            bz_removePlayerFlag (keepaway.id);

        keepaway.id = -1;
        keepaway.team = eNoTeam;
        keepaway.toldFlagFree = false;
        keepaway.flagToKeep = getFlag();
        keepaway.lastReminder = bz_getCurrentTime();

        return;
    }
}

bool KeepAwayCommands::SlashCommand ( int playerID, bz_ApiString _command, bz_ApiString _message,
                                      bz_APIStringList * /*_param*/ )
{
    std::string command = _command.c_str();
    std::string message = _message.c_str();
    const char* keepermessage = _message.c_str();

    if ( command == "kas" )
    {
        if (keepaway.id != -1)
            bz_sendTextMessage (playerID, keepaway.id, keepermessage);
        else
            bz_sendTextMessage(BZ_SERVER, playerID, "There is no one keeping the flag right now.");

        return true;
    }

    if ( command == "kaf" )
    {
        if (keepaway.id == -1)
            bz_sendTextMessagef (BZ_SERVER, playerID, "The Keep Away flag is: %s", convertFlag(keepaway.flagToKeep).c_str());
        else
            bz_sendTextMessagef (BZ_SERVER, playerID, "%s has Keep Away flag: %s", keepaway.callsign.c_str(),
                                 convertFlag(keepaway.flagToKeep).c_str());

        return true;
    }

    bz_BasePlayerRecord *fromPlayer = bz_getPlayerByIndex(playerID);

    if (fromPlayer)
    {
        if (!fromPlayer->admin)
        {
            bz_sendTextMessage(BZ_SERVER, playerID, "You must be admin to use the keepaway commands.");
            bz_freePlayerRecord(fromPlayer);
            return true;
        }

        bz_freePlayerRecord(fromPlayer);
    }

    if ( command == "kasoundoff" )
    {
        keepaway.soundEnabled = false;
        bz_sendTextMessage (BZ_SERVER, playerID, "Keep Away sounds are disabled.");
        return true;
    }
    if ( command == "kasoundon" )
    {
        keepaway.soundEnabled = true;
        bz_sendTextMessage (BZ_SERVER, playerID, "Keep Away sounds are enabled.");
        return true;
    }

    if ( command == "kaflagresetoff" )
    {
        keepaway.flagResetEnabled = false;
        bz_sendTextMessage (BZ_SERVER, playerID, "Keep Away flag reset is disabled.");
        return true;
    }
    if ( command == "kaflagreseton" )
    {
        keepaway.flagResetEnabled = true;
        bz_sendTextMessage (BZ_SERVER, playerID, "Keep Away flag reset is enabled.");
        return true;
    }

    if ( command == "kaf+" )
    {
        if (!keepaway.forcedFlags)  // this will always create an open spot for getFlag(), if it's needed
            bz_removePlayerFlag (keepaway.id);

        keepaway.id = -1;
        keepaway.team = eNoTeam;
        keepaway.toldFlagFree = false;
        keepaway.flagToKeep = getFlag();
        keepaway.lastReminder = bz_getCurrentTime();

        bz_sendTextMessagef(BZ_SERVER, playerID, "Keep Away flag advanced to: %s", convertFlag(keepaway.flagToKeep).c_str());

        return true;
    }

    if ( command == "kaon")
    {
        keepaway.enabled = true;
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away is enabled.");
        return true;
    }

    if ( command == "kaoff")
    {
        keepaway.enabled = false;
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away is disabled.");
        return true;
    }

    if ( command == "katimemult")
    {
        double inputvalue = ConvertToNum(message, 1, 99);

        if (inputvalue > 0)
        {
            keepaway.timeMult = (inputvalue/100);
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away auto time multiplier set to %i percent.",
                                 (int)(keepaway.timeMult*100 + 0.5));
        }
        else
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away auto time multiplier must be between 1 and 99 percent.",
                                 (int)(keepaway.timeMult*100 + 0.5));

        autoTime();

        return true;
    }

    if ( command == "katimemultmin")
    {
        double inputvalue = ConvertToNum(message, 1, 99);

        if (inputvalue > 0)
        {
            keepaway.timeMultMin = (inputvalue/100);
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away auto time multiplier minimum set to %i percent.",
                                 (int)(keepaway.timeMultMin*100 + 0.5));
        }
        else
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away auto time multiplier minimum must be between 1 and 99 percent.");

        autoTime();

        return true;
    }

    if ( command == "kastatus")
    {
        if (keepaway.enabled)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away is currently enabled.");

        if (!keepaway.enabled)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away is currently disabled.");

        if (keepaway.autoTimeOn)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away automatic time adjustment is currently enabled.");

        if (!keepaway.autoTimeOn)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away automatic time adjustment is currently disabled.");

        bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away time multiplier = %i percent.",
                             (int)(keepaway.timeMult*100 + 0.5));

        bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away time multiplier minimum = %i percent.",
                             (int)(keepaway.timeMultMin*100 + 0.5));

        int AdjTime = (int)(keepaway.adjustedTime + 0.5);
        bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away hold time is currently set to: %i seconds", AdjTime);

        if (keepaway.forcedFlags)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away forced flags is enabled.");

        if (!keepaway.forcedFlags)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away forced flags is disabled.");

        if (keepaway.soundEnabled)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away sounds are enabled.");

        if (!keepaway.soundEnabled)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away sounds are disabled.");

        if (keepaway.flagResetEnabled)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away flag reset is enabled.");

        if (!keepaway.flagResetEnabled)
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away flag reset is disabled.");

        return true;
    }

    // explicit time command handler:

    if ( command == "katime" )
    {
        double inputvalue = ConvertToNum(message, 1, 7200);

        if (inputvalue > 0 )
        {
            keepaway.TTH = inputvalue;
            autoTime();
            int AdjTime = (int)(inputvalue + 0.5);
            bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away hold time has been set to %i seconds.", AdjTime);
        }
        else
            bz_sendTextMessagef (BZ_SERVER, playerID, "Keep Away hold time invalid: must be between 1 and 7200 seconds.");

        autoTime();

        return true;
    }

    if ( command == "kaautotimeon")
    {
        keepaway.autoTimeOn = true;
        autoTime();
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away automatic time adjustment on.");
        return true;
    }

    if ( command == "kaautotimeoff")
    {
        keepaway.autoTimeOn = false;
        keepaway.adjustedTime = keepaway.TTH;
        autoTime();
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Keep Away automatic time adjustment off.");
        return true;
    }

    if ( command == "kaffon")
    {
        keepaway.forcedFlags = true;
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Forced flags on.");
        return true;
    }

    if ( command == "kaffoff")
    {
        keepaway.forcedFlags = false;
        bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Forced flags off.");
        return true;
    }

    return false;
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 4 ***
// c-basic-offset: 4 ***
// indent-tabs-mode: nil ***
// End: ***
// ex: shiftwidth=4 tabstop=4
