/***************************************************************************
 *   amxx_mines.sma               Version 1.0              Date: APR/10/2004
 *
 *   Shootable Proximity Mines for Counter-Strike!
 *
 *   By:      Rob Secord
 *   Alias:   xeroblood (aka; sufferer)                    xeroblood@msn.com
 *
 *   Tested / Developed On:
 *      STEAM CS 1.6 - AMX Mod X 0.16
 *
 *   Credits:
 *    - Progress Bar Effect:  bad-at-this
 *    - Explosions Effects:   AssKicR & WC3FT Mod
 *
 ***************************************************************************
 *  Client Commands:
 *
 *  +amxx_mine      Bind this command! Used to Plant a mine!
 *                  ex: bind p +amxx_mine
 *  buymine	    Bind or type in Console, used to buy a mine!
 *  say /buymine    Say this in caht to buy a mine!
 *  say /mines      Say this in chat to Display Help File!
 *
 ***************************************************************************
 *  Features:
 *  - Buy Time Restrictions
 *  - Buy Zone Restrictions
 *  - Mine Carry/Plant Limit
 *  - Shootable Mines
 *  - Planting Delay (Progress Bar)
 *  - Proximity Activation Delay
 *  - Free Mines And/Or Buy Mines
 *  - Explosions & Screen-Shake
 *  - Proximity Warning Beeps
 *  - Admin Immunity CVAR
 *
 ***************************************************************************/

#include <amxmodx>
#include <amxmisc>
#include <engine>
#include <cstrike>
#include <fun>

#define REQD_LVL ADMIN_LEVEL_A

// Change this if CVAR 'sv_mine_limit' is more than 5
#define MAX_MINES 5

// No need to change any of these..
#define MAX_PLAYERS   32
#define TASK_PLANT    15100
#define TASK_MOVE     15200
#define TASK_NOTIFY   15300
#define TASK_BUYTIME  15400
#define TASK_TIGGER   15500
#define TASK_ACTIVATE 15600
#define TASK_TIMER    15700

// Temp Entity Message Flags for Explosion Effects..
#define TE_EXPLFLAG_NONE 0  // Effects Flag used for TE_EXPLOSION
#define TE_EXPLOSION     3
#define TE_SMOKE         5
#define TE_EXPLOSION2    12
#define TE_BEAMCYLINDER  21

// Entity IDs of Planted Mines
new g_nMineEnts[MAX_PLAYERS][MAX_MINES]

// Tracks players Mine Count: [0]=carrying, [1]=planted
new g_nMineAmmo[MAX_PLAYERS][2]

// Tracks seconds until Proximity Activation
new g_nProxTime[MAX_PLAYERS][MAX_MINES]

// Tracks players Shots-Fired: [0]=WeaponID, [1]=Ammo
new g_nCurWeapon[MAX_PLAYERS][2]

// The Model/Size used for the Mine Entity (Size is Mins/Maxs)..
// (You can safely change these, if you have or know of a different model and its size)
new g_szMdlBomb[] = "models/w_c4.mdl"
new Float:g_fMdlSize[2][3] = { {-2.15, -4.3, -2.15}, {2.15, 4.3, 2.15} }

// The Sound file used when Planting a Mine..
// (You can safely change this too, if you have or know of a different wav file!)
new g_szSndPlant[] = "weapons/c4_plant.wav"

// The Sound file used when Triggering a Mine..
// (You can safely change this too, if you have or know of a different wav file!)
new g_szSndBeep[] = "common/menu1.wav"

// Used for BuyZone/BuyTime Rules..
new bool:g_bBuyZone[MAX_PLAYERS] = false
new bool:g_bBuyTime = false
new Float:g_fBuyTime = 0.0

// Players can Plant only during game-play time..
new bool:g_bCanPlant = false

// Global var for Turning Plugin On/Off
new bool:g_bMinesEnabled = false

// Sprites Used for Mine Explosion Effects..
new sprBoom, sprSmoke, sprShock, sprFire

///////////////////////////////////////////////////////////////////////////////////////////
//////////   PLUGIN FORWARDS SECTION   ////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

public plugin_init()
{
    register_plugin( "Proximity Mines", "1.0", "xeroblood" )

    // Toggle Plugin On or Off
    register_clcmd( "amxx_mines", "ToggleMode", REQD_LVL, "<1|0> - Turns Proximity Mines On or Off." )

    // Registered Player Events
    register_event( "ResetHUD",  "Event_ResetUser",  "b" ) // Also used for BuyTime Start
    register_event( "DeathMsg",  "Event_PlayerDied", "a" )
    register_event( "CurWeapon", "Event_ShotFired",  "b" )

    // Used for BuyZone Restrictions on Mines
    register_event( "StatusIcon", "Event_BuyZone", "be", "2=buyzone" )

    // Primarily used for BuyTime Restrictions on Mines
    register_logevent( "Event_FreezeEnd", 2, "0=World triggered", "1=Round_Start" )
    register_event( "SendAudio", "Event_RoundEnd", "a", "2=%!MRAD_terwin", "2=%!MRAD_ctwin", "2=%!MRAD_rounddraw" )
    // Like RoundEnd above, but on Restart users cant keep weapons from previous round..
    register_event( "TextMsg", "Event_RestartRound", "a", "2&#Game_C", "2&#Game_w" )

    // Client Console Commands
    register_clcmd( "+amxx_mine",   "ClCmd_MineHold",    -1, "- Bind m +amxx_mine   Then hold m!" )
    register_clcmd( "-amxx_mine",   "ClCmd_MineRelease", -1, "" )
    register_clcmd( "buymine",      "ClCmd_BuyMine",     -1, "- Bind b buymine   Then Press b!" )
    register_clcmd( "say /buymine", "ClCmd_BuyMine",     -1, "- Say in chat" )
    register_clcmd( "say /mines",   "ClCmd_MinesHelp",   -1, "- Say in chat" )

    // Server CVARs (Default Values)
    register_cvar( "sv_mine_limit",      "5" )      // Max Mines Per Player
    register_cvar( "sv_mine_size",       "9" )      // Mine Size for Shooting purposes
    register_cvar( "sv_mine_plant_time", "2" )      // Time required to plant a mine
    register_cvar( "sv_mine_delay_time", "3" )      // Time before Proximity Activates Itself
    register_cvar( "sv_mine_freebies",   "2" )      // # of free mines on each round
    register_cvar( "sv_mine_beeps",      "5" )      // # of Beeps Heard before Proximity Explosion
    register_cvar( "sv_mine_help",       "1" )      // Display Help info for Mines? (1 or 0)
    register_cvar( "sv_mine_buyzone",    "1" )      // Obey BuyZone Rules? (1 or 0)
    register_cvar( "sv_mine_buytime",    "1" )      // Obey BuyTime Rules? (1 or 0)
    register_cvar( "sv_mine_immunity",   "0" )      // Obey Immunity Rights On Admins?
    register_cvar( "sv_mine_logs",       "0" )      // Log to File On: Buy/Plant/Explode
    register_cvar( "sv_mine_cost",       "500" )    // The cost of each Mine bought
    register_cvar( "sv_mine_radius",     "300" )    // The Mine Proximity/Explosion Radius
    register_cvar( "sv_mine_damage",     "100" )    // The MAX Amount of Damge done by a Mine
}

public plugin_cfg()
{
    g_bMinesEnabled = true
    set_task( 10.0, "ProximityTimer", TASK_TIMER )
}

public plugin_precache()
{
    // Mine Model/Sound
    precache_model( g_szMdlBomb )
    precache_sound( g_szSndPlant )
    precache_sound( g_szSndBeep )

    // Explosion Sprites
    sprBoom  = precache_model( "sprites/zerogxplode.spr" )
    sprSmoke = precache_model( "sprites/steam1.spr" )
    sprShock = precache_model( "sprites/shockwave.spr" )
    sprFire  = precache_model( "sprites/explode1.spr" )
}

public client_authorized( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // Ensure this users Mine-Index is cleared..
    g_nMineAmmo[id-1][0] = 0
    RemovePlantedMines( id )

    // If Help is Enabled, Display a Notice of Mines Plugin on Connect..
    if( get_cvar_num( "sv_mine_help" ) )
    {
        new pID[2]
        pID[0] = id
        set_task( 10.0, "NotifyUser", (TASK_NOTIFY+id), pID, 1 )
    }
    return PLUGIN_CONTINUE
}

public client_disconnect( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // Ensure this users Mine-Index is cleared..
    g_nMineAmmo[id-1][0] = 0
    RemovePlantedMines( id )

    return PLUGIN_CONTINUE
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////   CLIENT COMMANDS SECTION   ////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

// Toggles Plugin On or Off
public ToggleMode( id, lvl, cid )
{
    if( !cmd_access( id, lvl, cid, 2 ) )
        return PLUGIN_HANDLED

    if( read_argc() < 2 )
    {
        console_print( id, "[AMXX] Proximity Mines Are Currently %s!", (g_bMinesEnabled) ? "Enabled" : "Disabled" )
        console_print( id, "[AMXX] Use: amxx_mines %d   To %s Proximity Mines!", (g_bMinesEnabled) ? 0 : 1, (g_bMinesEnabled) ? "Disable" : "Enable" )
        return PLUGIN_HANDLED
    }

    new arg[4]
    read_argv( 1, arg, 4 )

    if( equal( arg, "on", 2 ) || equal( arg, "1", 1 ) )
    {
        if( !g_bMinesEnabled )
        {
            g_bMinesEnabled = true
            if( !task_exists( TASK_TIMER ) ) set_task( 1.0, "ProximityTimer", TASK_TIMER )
            console_print( id, "[AMXX] Proximity Mines Enabled!" )
            client_print( 0, print_chat, "[AMXX] Admin Has Enabled Proximity Mines!" )
        }else
            console_print( id, "[AMXX] Proximity Mines Are Already Enabled!" )
    }
    else
    {
        if( g_bMinesEnabled )
        {
            g_bMinesEnabled = false
            console_print( id, "[AMXX] Proximity Mines Disabled!" )
            client_print( 0, print_chat, "[AMXX] Admin Has Disabled Proximity Mines!" )

            // Clear All Player Mines
            new nPlayers[MAX_PLAYERS], nNum, i
            get_players( nPlayers, nNum, "c" )
            for( i = 0; i < nNum; i++ )
            {
                g_nMineAmmo[nPlayers[i]-1][0] = 0
                RemovePlantedMines( nPlayers[i] )
            }
        }else
            console_print( id, "[AMXX] Proximity Mines Are Already Disabled!" )
    }
    return PLUGIN_HANDLED
}


// User is starting to plant a mine..
public ClCmd_MineHold( id )
{
    if( !g_bMinesEnabled )
    {
        client_print( id, print_chat, "Proximity Mines Have Been Disabled By Admin!" )
        return PLUGIN_HANDLED
    }
    if( !is_user_alive( id ) )
    {
        client_print( id, print_chat, "You are Dead! You Have No Mines to Plant!" )
        return PLUGIN_HANDLED
    }
    if( g_nMineAmmo[id-1][0] <= 0 ) // if the user is not carrying any mines to plant
    {
        client_print( id, print_chat, "You have NO Proximity Mines to Plant!" )
        return PLUGIN_HANDLED
    }
    if( !g_bCanPlant )  // If user is still alive in between rounds (or freezetime)..
    {
        client_print( id, print_chat, "You Cannot Plant A Proximity Mine At This Time!" )
        return PLUGIN_HANDLED
    }
    if( CheckMovement( id ) ) // if the user is moving/jumping..
    {
        client_print( id, print_chat, "Stand Still To Plant a Proximity Mine!" )
        return PLUGIN_HANDLED
    }

    // if the user already has mines planted check distance between..
    if( g_nMineAmmo[id-1][1] > 0 )
    {
        new i, nDistance
        new Float:fEntOrigin[3]
        new nEntOrigin[3], nPlayerOrigin[3]
        get_user_origin( id, nPlayerOrigin )

        for( i = 0; i < MAX_MINES; i++ )
        {
            if( g_nMineEnts[id-1][i] == 0 ) continue
            entity_get_vector( g_nMineEnts[id-1][i], EV_VEC_origin, fEntOrigin )
            FVecIVec( fEntOrigin, nEntOrigin )

            nDistance = get_distance( nPlayerOrigin, nEntOrigin )
            if( nDistance <= get_cvar_num( "sv_mine_radius" ) )
            {
                client_print( id, print_chat, "You Cannot Plant Your Mines So Close Together!" )
                return PLUGIN_HANDLED
            }
        }
    }

    // All went well, now we start the progress bar..
    new nMinePlant_Time = get_cvar_num( "sv_mine_plant_time" )

    // Progress Bar (Activate)
    message_begin( MSG_ONE, 108, {0,0,0}, id )
    write_byte( nMinePlant_Time )   // duration
    write_byte( 0 )                 // duration
    message_end()

    new pID[2]
    pID[0] = id
    set_task( 0.1, "CheckMove", (TASK_MOVE+id), pID, 1 )
    set_task( float(nMinePlant_Time), "PlantMine", (TASK_PLANT+id), pID, 1 )

    return PLUGIN_HANDLED
}

public ClCmd_MineRelease( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_HANDLED

    if( task_exists( (TASK_PLANT+id) ) )
    {
        remove_task( (TASK_PLANT+id) )
        remove_task( (TASK_MOVE+id) )
    }

    // Progress Bar (Terminate)
    message_begin( MSG_ONE, 108, {0,0,0}, id )
    write_byte( 0 ) // duration
    write_byte( 0 ) // duration
    message_end()

    return PLUGIN_HANDLED
}

public ClCmd_BuyMine( id )
{
    if( !g_bMinesEnabled )
    {
        client_print( id, print_chat, "Proximity Mines Have Been Disabled By Admin!" )
        return PLUGIN_HANDLED
    }
    if( !is_user_alive( id ) )
    {
        client_print( id, print_chat, "Only The Living Can Buy Proximity Mines!" )
        return PLUGIN_CONTINUE
    }
    if( !g_bBuyTime )
    {
        client_print( id, print_center, "%d Seconds Have Passed.  You can't buy anything now!", floatround(g_fBuyTime)-get_cvar_num("mp_freezetime") )
        return PLUGIN_CONTINUE
    }
    if( !g_bBuyZone[id-1] )
    {
        client_print( id, print_center, "You Must Be In Your Buy-Zone To Buy!")
        return PLUGIN_CONTINUE
    }
    //      Carried Mines   +   Planted Mines     >=      Max Mines
    if( (g_nMineAmmo[id-1][0]+g_nMineAmmo[id-1][1]) >= clamp( get_cvar_num( "sv_mine_limit" ), 0, MAX_MINES ) )
    {
        client_print( id, print_chat, "You Already Have Enough Proximity Mines! (Max: %d)", clamp( get_cvar_num( "sv_mine_limit" ), 0, MAX_MINES ) )
        return PLUGIN_CONTINUE
    }
    if( cs_get_user_money( id ) < get_cvar_num( "sv_mine_cost" ) )
    {
        client_print( id, print_chat, "You Cannot Afford Any Proximity Mines! ($%d Each)", get_cvar_num( "sv_mine_cost" ) )
        return PLUGIN_CONTINUE
    }

    // All went well, the user successfully bought a mine..
    cs_set_user_money( id, cs_get_user_money( id ) - get_cvar_num( "sv_mine_cost" ) )
    g_nMineAmmo[id-1][0] += 1
    client_print( id, print_chat, "You Now Have %d Proximity Mine%s", g_nMineAmmo[id-1][0], (g_nMineAmmo[id-1][0]==1)?"":"s" )
    log_mine( "%s Bought A Proximity Mine!", id, 0 )

    return PLUGIN_CONTINUE
}

public ClCmd_MinesHelp( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // Display Help File from amxx/custom/mines_help.htm
    if( get_cvar_num( "sv_mine_help" ) )
    {
        new szHelpFile[64], szCustomDir[32]
        get_customdir( szCustomDir, 31 )
        format( szHelpFile, 63, "%s/mines_help.htm", szCustomDir )
        show_motd( id, szHelpFile, "Proximity Mines - Help" )
    }
    return PLUGIN_CONTINUE
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////   PLUGIN EVENTS SECTION   //////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

// Registered Event for BuyZone Status Icon Indicator..
public Event_BuyZone( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    if( read_data(1) )  // if user is inside buyzone
    {
        g_bBuyZone[id-1] = true
    }else
    {
        if( get_cvar_num( "sv_mine_buyzone" ) ) // If buying is Restricted to BuyZone
            g_bBuyZone[id-1] = false
        else
            g_bBuyZone[id-1] = true
    }
    return PLUGIN_CONTINUE
}

public Event_RoundEnd()
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // users cannot buy or plant any mines now..
    g_bBuyTime = false
    g_bCanPlant = false

    // Remove all planted mine entities..
    new nPlayers[MAX_PLAYERS], nNum, i
    get_players( nPlayers, nNum, "c" )
    for( i = 0; i < nNum; i++ )
        RemovePlantedMines( nPlayers[i] )

    return PLUGIN_CONTINUE
}

public Event_RestartRound()
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // users cannot buy or plant any mines now..
    g_bBuyTime = false
    g_bCanPlant = false

    // Remove all player mines (Give Freebies)..
    new nPlayers[MAX_PLAYERS], nNum, i
    get_players( nPlayers, nNum, "c" )
    for( i = 0; i < nNum; i++ )
    {
        RemovePlantedMines( nPlayers[i] )
        g_nMineAmmo[nPlayers[i]-1][0] = clamp( get_cvar_num( "sv_mine_freebies" ), 0, MAX_MINES )
    }
    return PLUGIN_CONTINUE
}

public Event_FreezeEnd()
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // users can start planting mines now..
    g_bCanPlant = true

    return PLUGIN_CONTINUE
}

public Event_ResetUser( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // Start BuyTime if it hasn't already started..
    if( get_cvar_num( "sv_mine_buytime" ) && !g_bBuyTime )
        BuyTimeStart()

    return PLUGIN_CONTINUE
}

public Event_PlayerDied()
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // player loses the mines he/she carries
    //but not the ones planted, those may still hurt/kill people..
    new id = read_data(2)
    g_nMineAmmo[id-1][0] = 0

    return PLUGIN_CONTINUE
}

public Event_ShotFired( id )
{
    if( !g_bMinesEnabled ) return PLUGIN_CONTINUE

    // Players current weapon data..
    new wID = read_data( 2 )
    new wAmmo = read_data( 3 )

    if( g_nCurWeapon[id-1][0] != wID ) // User Changed Weapons..
    {
        g_nCurWeapon[id-1][0] = wID
        g_nCurWeapon[id-1][1] = wAmmo
        return PLUGIN_CONTINUE
    }
    if( g_nCurWeapon[id-1][1] < wAmmo ) // User Reloaded..
    {
        g_nCurWeapon[id-1][1] = wAmmo
        return PLUGIN_CONTINUE
    }
    if( g_nCurWeapon[id-1][1] == wAmmo ) // User did something else, but didn't shoot..
        return PLUGIN_CONTINUE

    // This far means user shot his/her gun..
    // Save new weapon data..
    g_nCurWeapon[id-1][1] = wAmmo
    g_nCurWeapon[id-1][0] = wID

    new nPoint[3], nSize = get_cvar_num( "sv_mine_size" )

    // Get the origin of weapon hit-point
    get_user_origin( id, nPoint, 3 )

    // Find any Mine Entites hit..
    new nEntity = FindMine( nPoint, nSize )
    if( nEntity > 0 )
    {
        // [0] = Owner ID, [1] = Mine Index
        new pCmdArgs[2]
        GetMine_Owner_Index( nEntity, pCmdArgs )

        if( pCmdArgs[0] == id )
        {
            client_print( id, print_chat, "You Shot One Of Your Own Mines!" )
            log_mine( "%s Shot One Of His Own Proximity Mines!", id, 0 )
        }else
        {
            new szMineOwner[32]
            get_user_name( pCmdArgs[0], szMineOwner, 31 )
            client_print( id, print_chat, "You Shot One Of %s's Proximity Mines!!", szMineOwner )
            log_mine( "%s Shot One Of %s's Proximity Mines!", id, pCmdArgs[0] )
        }

        // Explode Mine and Minus Player Planted-Mine Count..
        ExplodeMine( pCmdArgs[0], pCmdArgs[1] )
    }

    return PLUGIN_CONTINUE
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////   PLANT MINE FUNCTION   ////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

// Progress Bar finished, plant a mine entity..
public PlantMine( p_aCmdArgs[] )
{
    new id = p_aCmdArgs[0]
    new Float:PlayerOrigin[3]
    new pCmdArgs[3]
    new nActivationTime = 0

    // Remove Task that checks if user is moving (finished progress bar)..
    if( task_exists( (TASK_MOVE+id) ) ) remove_task( (TASK_MOVE+id) )

    // Create the Mine Entity and ensure it was created properly..
    g_nMineEnts[id-1][g_nMineAmmo[id-1][1]] = create_entity( "info_target" )
    if( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]] == 0 )
    {
        client_print( id, print_chat, "You Planted A Dud!    :(" )
        log_mine( "[ERROR] Cannot Create Temp-Entity for %s", id, 0 )
        return PLUGIN_HANDLED
    }

    // Sets the Classname of the Mine Entity
    entity_set_string( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], EV_SZ_classname, "amxx_prox_mine" )

    // Sets the Entity Model Type (Default C4 Model)
    entity_set_model( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], g_szMdlBomb )

    // Sets the Size of the Entity using MINS/MAXS
    set_size( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], g_fMdlSize[0], g_fMdlSize[1] )

    // Sets the Mine Entity at the users current origin
    entity_get_vector( id, EV_VEC_origin, PlayerOrigin )
    entity_set_origin( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], PlayerOrigin )

    // Sets Mine Entity Properties & Owner
    entity_set_int( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], EV_INT_solid, SOLID_BBOX )
    entity_set_int( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], EV_INT_movetype, MOVETYPE_TOSS )
    entity_set_edict( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], EV_ENT_owner, id )

    // Creates a Planting Sound..
    emit_sound( g_nMineEnts[id-1][g_nMineAmmo[id-1][1]], CHAN_WEAPON, g_szSndPlant, 1.0, ATTN_NORM, 0, PITCH_NORM )

    // Proximity Mine Activation Time (Gives Planter Time to Escape Proximity of Mine)
    nActivationTime = get_cvar_num( "sv_mine_delay_time" )
    g_nProxTime[id-1][g_nMineAmmo[id-1][1]] = (nActivationTime>0) ? nActivationTime : 1
    pCmdArgs[0] = id
    pCmdArgs[1] = g_nMineAmmo[id-1][1]
    set_task( float( nActivationTime ), "ActivateMine", (TASK_ACTIVATE+id), pCmdArgs, 2 )

    // Remove one carried Mine and Add it to Planted Mines..
    g_nMineAmmo[id-1][0] -= 1
    g_nMineAmmo[id-1][1] += 1

    // Inform user Mine was Planted and how many Mines the user has left..
    client_print( id, print_chat, "Proximity Mine Planted! %d Seconds Until Activation!", get_cvar_num( "sv_mine_delay_time" ) )
    client_print( id, print_chat, "You Now Have %d Proximity Mine%s Left!", g_nMineAmmo[id-1][0], (g_nMineAmmo[id-1][0]==1)?"":"s" )
    log_mine( "%s Planted A Proximity Mine!", id, 0 )

    return PLUGIN_HANDLED
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////   EXPLODE MINE FUNCTION  ///////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

ExplodeMine( p_nOwnerID, p_nMineIDX )
{
    if( !is_valid_ent( g_nMineEnts[p_nOwnerID-1][p_nMineIDX] ) ) return

    // Find the Mine's Origin Point
    new Float:fMineOrigin[3], nMineOrigin[3]
    entity_get_vector( g_nMineEnts[p_nOwnerID-1][p_nMineIDX], EV_VEC_origin, fMineOrigin )
    FVecIVec( fMineOrigin, nMineOrigin )

    // Remove Mine Entity and Minus Player Planted-Mine Count
    remove_entity( g_nMineEnts[p_nOwnerID-1][p_nMineIDX] )
    g_nMineEnts[p_nOwnerID-1][p_nMineIDX] = 0
    g_nMineAmmo[p_nOwnerID-1][1] -= 1

    // Get Friendly Fire CVAR
    new nFFOn = get_cvar_num( "mp_friendlyfire" )

    // Damage All Nearby Players (Including Bots)
    new nPlayers[MAX_PLAYERS], nNum, i
    new nPlayerOrigin[3]
    new nMineDmg = get_cvar_num( "sv_mine_damage" )
    new nMineRad = get_cvar_num( "sv_mine_radius" )
    new nDist, nMult, nDamage

    get_players( nPlayers, nNum, "a" ) // Only find Living Players
    for( i = 0; i < nNum; i++ )
    {
        if( (get_user_team(nPlayers[i]) != get_user_team(p_nOwnerID)) || nFFOn || (nPlayers[i] == p_nOwnerID) )
        {
            // Check Player Distance to Mine Entity
            get_user_origin( nPlayers[i], nPlayerOrigin )
            nDist = get_distance( nMineOrigin, nPlayerOrigin )

            // Shake Players Screen if in range (Mine Radius + 150)
            if( nDist <= (nMineRad+150)  )
            {
                message_begin( MSG_ONE, get_user_msgid( "ScreenShake" ), {0,0,0}, nPlayers[i] )
                write_short( 1<<14 )    // amplitude
                write_short( 1<<13 )    // duration
                write_short( 1<<14 )    // frequency
                message_end()
            }

            // Don't Damage Immune Admins..
            if( PlayerHasImmunity( nPlayers[i] ) ) continue

            // Damage Players within Range (Mine Radius)
            if( nDist <= nMineRad )
            {
                nMult = ( (nMineDmg * nMineDmg) / nMineRad )
                nDamage = ( (nMineRad - nDist) * nMult )
                nDamage = sqrt( nDamage )

                if( nPlayers[i] == p_nOwnerID )
                {
                    if( get_user_health( p_nOwnerID ) <= nDamage )
                    {
                        user_silentkill( p_nOwnerID )
                        make_deathmsg( p_nOwnerID, p_nOwnerID, 0, "grenade" )
                        log_mine( "%s Killed Self With Proximity Mine!", p_nOwnerID, 0 )
                    }else
                        set_user_health( p_nOwnerID, get_user_health( p_nOwnerID ) - nDamage )
                }else
                {
                    if( get_user_health( nPlayers[i] ) <= nDamage )
                    {
                        if( get_user_team(nPlayers[i]) == get_user_team(p_nOwnerID) )
                        {
                            // Set Owners Frags -1 for Team-Kill
                            set_user_frags( p_nOwnerID, get_user_frags( p_nOwnerID ) - 1 )
                            log_mine( "%s Killed Teammate %s With Proximity Mine!", p_nOwnerID, nPlayers[i] )
                        }else
                        {
                            // Set Owners Frags +1 for Enemy-Kill
                            set_user_frags( p_nOwnerID, get_user_frags( p_nOwnerID ) + 1 )
                            log_mine( "%s Killed %s With Proximity Mine!", p_nOwnerID, nPlayers[i] )
                        }
                        // Set Victims Frags +1 if > 0 Cuz Engine Does -1
                        //if( get_user_frags( nPlayers[i] ) > 0 )
                        //    set_user_frags( nPlayers[i], get_user_frags( nPlayers[i] ) + 1 )
                        user_silentkill( nPlayers[i] )
                        make_deathmsg( p_nOwnerID, nPlayers[i], 0, "grenade" )
                    }else
                        set_user_health( nPlayers[i], get_user_health( nPlayers[i] ) - nDamage )
                }
            }
        }
    }

    // Start Explosion Effects..
    // Shockwave Effect
    message_begin( MSG_BROADCAST, SVC_TEMPENTITY, nMineOrigin )
    write_byte( TE_BEAMCYLINDER )
    write_coord( nMineOrigin[0] )
    write_coord( nMineOrigin[1] )
    write_coord( nMineOrigin[2] + 16 )
    write_coord( nMineOrigin[0] )
    write_coord( nMineOrigin[1] )
    write_coord( nMineOrigin[2] + 1936 )
    write_short( sprShock )
    write_byte( 0 )                 // startframe
    write_byte( 0 )                 // framerate
    write_byte( 2 )                 // life
    write_byte( 20 )                // width
    write_byte( 0 )                 // noise
    write_byte( 188 )               // r
    write_byte( 220 )               // g
    write_byte( 255 )               // b
    write_byte( 255 )               // brightness
    write_byte( 0 )                 // speed
    message_end()

    // Explosion Type 1 Effect
    message_begin( MSG_BROADCAST, SVC_TEMPENTITY, nMineOrigin )
    write_byte( TE_EXPLOSION )
    write_coord( nMineOrigin[0] )
    write_coord( nMineOrigin[1] )
    write_coord( nMineOrigin[2] )
    write_short( sprFire )
    write_byte( 60 )                // byte (scale in 0.1's) 188
    write_byte( 10 )                // byte (framerate)
    write_byte( TE_EXPLFLAG_NONE )  // byte flags
    message_end()

    // Explosion Type 2 Effect
    message_begin( MSG_BROADCAST, SVC_TEMPENTITY )
    write_byte( TE_EXPLOSION2 )
    write_coord( nMineOrigin[0] )
    write_coord( nMineOrigin[1] )
    write_coord( nMineOrigin[2] )
    write_byte( 188 )               // byte (scale in 0.1's) 188
    write_byte( 10 )                // byte (framerate)
    message_end()

    // Outer Explosion Flame
    message_begin( MSG_BROADCAST, SVC_TEMPENTITY )
    write_byte( TE_EXPLOSION )
    write_coord( nMineOrigin[0] )
    write_coord( nMineOrigin[1] )
    write_coord( nMineOrigin[2] )
    write_short( sprBoom )
    write_byte( 50 )                // byte (scale in 0.1's)
    write_byte( 15 )                // byte (framerate)
    write_byte( TE_EXPLFLAG_NONE )  // byte flags
    message_end()

    // Smoke Effect
    message_begin( MSG_BROADCAST, SVC_TEMPENTITY, nMineOrigin )
    write_byte( TE_SMOKE )
    write_coord( nMineOrigin[0] )
    write_coord( nMineOrigin[1] )
    write_coord( nMineOrigin[2] )
    write_short( sprSmoke )
    write_byte( 60 )                // (scale in 0.1's)
    write_byte( 10 )                // (framerate)
    message_end()

    return
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////   PROXIMITY TIMER   ////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

public ProximityTimer()
{
    new nPoint[3], nEntity, nRadius = get_cvar_num( "sv_mine_radius" )
    new nPlayers[MAX_PLAYERS], nNum, i
    new pCmdArgs[5]

    get_players( nPlayers, nNum, "a" ) // Only find Living Players / bots
    for( i = 0; i < nNum; i++ )
    {
        // Get the origin of player
        get_user_origin( nPlayers[i], nPoint, 0 )

        // Find any Mine Entites in Players Radius..
        nEntity = FindMine( nPoint, nRadius )
        if( nEntity > 0 )
        {
            // [0] = Owner ID, [1] = Mine Index
            GetMine_Owner_Index( nEntity, pCmdArgs )
            if( get_user_team(pCmdArgs[0]) == get_user_team(nPlayers[i]) ) continue

            // If Mine Activated & Not Already Triggered
            if( (g_nProxTime[pCmdArgs[0]-1][pCmdArgs[1]] == 0) && !task_exists(TASK_TIGGER+nPlayers[i]) )
            {
                pCmdArgs[2] = nPlayers[i]
                pCmdArgs[3] = clamp( get_cvar_num( "sv_mine_beeps" ), 3, 30 )   // Min 3 Beeps, Max 30
                set_task( 0.2, "TriggerMine", (TASK_TIGGER+nPlayers[i]), pCmdArgs, 4 )
            }
        }
    }
    if( g_bMinesEnabled )
        set_task( 0.5, "ProximityTimer", 0 )

    return
}

///////////////////////////////////////////////////////////////////////////////////////////
//////////   PLUGIN SPECIFIC FUNCTIONS   //////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

public TriggerMine( p_aCmdArgs[] )
{
    if( p_aCmdArgs[3] > 0 )     // Proximity Timer (sv_mine_beeps * 0.2) Seconds..
    {
        p_aCmdArgs[3] -= 1
        // Creates a Proximity-Beeping Sound..
        emit_sound( g_nMineEnts[p_aCmdArgs[0]-1][p_aCmdArgs[1]], CHAN_WEAPON, g_szSndBeep, 1.0, ATTN_NORM, 0, PITCH_NORM )
        set_task( 0.2, "TriggerMine", (TASK_TIGGER+p_aCmdArgs[2]), p_aCmdArgs, 4 )
    }else
    {
        ExplodeMine( p_aCmdArgs[0], p_aCmdArgs[1] )
        log_mine( "%s Triggered %s's Proximity Mine!", p_aCmdArgs[2], p_aCmdArgs[0] )
    }
    return PLUGIN_HANDLED
}

public ActivateMine( p_aCmdArgs[] )
{
    g_nProxTime[p_aCmdArgs[0]-1][p_aCmdArgs[1]] = 0
    client_print( p_aCmdArgs[0], print_chat, "Proximity Mine %d is Now Active!", (p_aCmdArgs[1]+1) )
    return PLUGIN_HANDLED
}

GetMine_Owner_Index( nMineEntity, nOutput[] )
{
    new nPlayers[MAX_PLAYERS], nNum, i, j
    get_players( nPlayers, nNum, "c" )
    nOutput[0] = nOutput[1] = 0

    for( i = 0; i < nNum; i++ ) // Go thru all connected players
    {
        for( j = 0; j < MAX_MINES; j++ ) // Go thru all their mines..
        {
            if( g_nMineEnts[nPlayers[i]-1][j] > 0 ) // if this mine entity exists (is planted)
            {
                if( nMineEntity == g_nMineEnts[nPlayers[i]-1][j] ) // if the entity is the one requested
                {
                    nOutput[0] = nPlayers[i]    // Owner ID
                    nOutput[1] = j              // Mine Index
                    return
                }
            }
        }
    }
    return
}

FindMine( p_nUserOrigin[3], p_nRadius )
{
    new nPlayers[MAX_PLAYERS], nNum, i, j
    new nEOrigin[3], Float:fEOrigin[3]
    new nProximity = 0

    get_players( nPlayers, nNum, "c" )
    for( i = 0; i < nNum; i++ )
    {
        for( j = 0; j < MAX_MINES; j++ )
        {
            if( g_nMineEnts[nPlayers[i]-1][j] != 0 )
            {
                // Get Mine Origins
                entity_get_vector( g_nMineEnts[nPlayers[i]-1][j], EV_VEC_origin, fEOrigin )
                FVecIVec( fEOrigin, nEOrigin )
                // Get Distance Between Player & Mine Entity
                nProximity = get_distance( p_nUserOrigin, nEOrigin )
                if( nProximity <= p_nRadius )
                {
                    // Mine Entity Found; Return Entity ID
                    return g_nMineEnts[nPlayers[i]-1][j]
                }
            }
        }
    }
    return 0
}

public CheckMove( p_aCmdArgs[] )
{
    new id = p_aCmdArgs[0]
    // if plant time has ended or user is moving..
    if( !g_bCanPlant || CheckMovement( id ) )
        ClCmd_MineRelease( id )
    else
        set_task( 0.1, "CheckMove", (TASK_MOVE+id), p_aCmdArgs, 1 )

    return PLUGIN_HANDLED
}

// Displays One-Line Help Info in Chat to new users...
public NotifyUser( p_aCmdArgs[] )
{
    client_print( p_aCmdArgs[0], print_chat, "RS Proximity Mines: Say /mines for help on Using Mines!" )
    return PLUGIN_HANDLED
}

// Checks if a player has Admin Immunity Rights
bool:PlayerHasImmunity( id )
{
    if( get_cvar_num( "sv_mine_immunity" ) )
        return (get_user_flags( id ) & ADMIN_IMMUNITY) ? true : false

    return false
}

// Checks a users buttons, and determines if they are moving or not..
bool:CheckMovement( id )
{
    if( get_user_button( id ) & (IN_JUMP|IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT) )
        return true
    return false
}

// Removes all Planted Mine Entities (they do not explode)
RemovePlantedMines( id )
{
    for( new i = 0; i < MAX_MINES; i++ )
    {
        if( g_nMineEnts[id-1][i] > 0 )
        {
            remove_entity( g_nMineEnts[id-1][i] )
            g_nMineEnts[id-1][i] = 0
        }
    }
    g_nMineAmmo[id-1][1] = 0
    return
}

// Checks the Servers mp_buytime/mp_freezetime CVARs and starts Mine BuyTime
BuyTimeStart()
{
    g_fBuyTime = ((get_cvar_float("mp_buytime") * 60) + get_cvar_num("mp_freezetime"))
    if( g_fBuyTime > 0.0 )
        set_task( g_fBuyTime, "BuyTimeDone", TASK_BUYTIME )
    g_bBuyTime = true
    return
}

// Ends BuyTime if using BuyTime Restrictions
public BuyTimeDone()
{
    if( get_cvar_num( "sv_mine_buytime" ) == 1 )
        g_bBuyTime = false
    else
        g_bBuyTime = true
}

log_mine( p_szLog[], p_UserID, p_nVictimID )
{
    if( get_cvar_num( "sv_mine_logs" ) == 1 )
    {
        new szUser[32]
        get_user_name( p_UserID, szUser, 31 )
        if( p_nVictimID != 0 )
        {
            new szVictim[32]
            get_user_name( p_nVictimID, szVictim, 31 )
            log_amx( p_szLog, szUser, szVictim )
        }else
            log_amx( p_szLog, szUser )
    }
    return
}

sqrt( num )
{
    new div = num
    new result = 1
    while( div > result ){          // end when div == result, or just below
        div = (div + result) / 2    // take mean value as new divisor
        result = num / div
    }
    return div
}
///////////////////////////////////////////////////////////////////////////////////////////
//////////   END OF FILE   ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
