#include <amxmodx>
#include <cstrike>
#include <fakemeta>
#include <hamsandwich>
 
#define PLUGIN_NAME "Real Weapon Speed"
#define PLUGIN_VERSION  "1.0"
#define PLUGIN_AUTHOR   "Numb"
 
//#define DEBUG
 
#define PDWeaponType    43
#define PDNextSecAttack 47
#define PDActiveItem    373
 
#define WEIGHT_GLOCK18      0.900
#define WEIGHT_USP          1.000
#define WEIGHT_DEAGLE       1.800
#define WEIGHT_P228         1.030
#define WEIGHT_ELITE        1.150
#define WEIGHT_FIVESEVEN    0.618
#define WEIGHT_M3           3.500
#define WEIGHT_XM1014       4.000
#define WEIGHT_MP5NAVY      3.420
#define WEIGHT_MAC10        3.820
#define WEIGHT_TMP          1.300
#define WEIGHT_P90          3.000
#define WEIGHT_UMP45        2.270
#define WEIGHT_GALIL        4.350
#define WEIGHT_FAMAS        3.400
#define WEIGHT_AK47         4.790
#define WEIGHT_M4A1         3.220
#define WEIGHT_SG552        3.100
#define WEIGHT_AUG          4.090
#define WEIGHT_G3SG1        4.410
#define WEIGHT_SG550        7.020
#define WEIGHT_SCOUT        3.300
#define WEIGHT_AWP          6.000
#define WEIGHT_M249         6.000
#define WEIGHT_FLASHBANG    0.350
#define WEIGHT_HEGRENADE    0.350
#define WEIGHT_SMOKEGRENADE 0.350
#define WEIGHT_C4           2.000
#define WEIGHT_KNIFE        0.200
#define WEIGHT_SHIELD       5.000
#define WEIGHT_USP_S        0.200
#define WEIGHT_M4A1_S       0.200
 
#define SPEED_BONUCE_SHIELD 50
#define SPEED_BONUCE_ZOOM1  20
#define SPEED_BONUCE_ZOOM2  40
#define SPEED_BONUCE_ZOOM3  80
 
 
new const Float:fWeaponWeights[] =
{
    0.0,
    WEIGHT_P228,
    0.0,
    WEIGHT_SCOUT,
    WEIGHT_HEGRENADE,
    WEIGHT_XM1014,
    WEIGHT_C4,
    WEIGHT_MAC10,
    WEIGHT_AUG,
    WEIGHT_SMOKEGRENADE,
    WEIGHT_ELITE,
    WEIGHT_FIVESEVEN,
    WEIGHT_UMP45,
    WEIGHT_SG550,
    WEIGHT_GALIL,
    WEIGHT_FAMAS,
    WEIGHT_USP,
    WEIGHT_GLOCK18,
    WEIGHT_AWP,
    WEIGHT_MP5NAVY,
    WEIGHT_M249,
    WEIGHT_M3,
    WEIGHT_M4A1,
    WEIGHT_TMP,
    WEIGHT_G3SG1,
    WEIGHT_FLASHBANG,
    WEIGHT_DEAGLE,
    WEIGHT_SG552,
    WEIGHT_AK47,
    WEIGHT_KNIFE,
    WEIGHT_P90,
};
 
new Float:g_fOldNextSecAttack;
 
new g_iCvarP_MinSpeed;
new g_iCvarP_MaxSpeed;
new g_iCvarP_MinGrav;
new g_iCvarP_MaxGrav;
 
public plugin_init()
{
    register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);
   
    g_iCvarP_MinSpeed = register_cvar("rw_min_speed",            "400.0");
    g_iCvarP_MaxSpeed = register_cvar("rw_max_speed",            "400.0");
    g_iCvarP_MinGrav  = register_cvar("rw_highest_gravity_proc", "0.81");
    g_iCvarP_MaxGrav  = register_cvar("rw_lowest_gravity_proc",  "1.01");
   
    new const iWeaponList[29][] =
    {
        "weapon_glock18",
        "weapon_usp",
        "weapon_deagle",
        "weapon_p228",
        "weapon_elite",
        "weapon_fiveseven",
        "weapon_m3",
        "weapon_xm1014",
        "weapon_mp5navy",
        "weapon_mac10",
        "weapon_tmp",
        "weapon_p90",
        "weapon_ump45",
        "weapon_galil",
        "weapon_famas",
        "weapon_ak47",
        "weapon_m4a1",
        "weapon_sg552",
        "weapon_aug",
        "weapon_g3sg1",
        "weapon_sg550",
        "weapon_scout",
        "weapon_awp",
        "weapon_m249",
        "weapon_flashbang",
        "weapon_hegrenade",
        "weapon_smokegrenade",
        "weapon_knife",
        "weapon_c4"
    };
   
    for( new iTemp; iTemp<29; iTemp++ )
    {
        RegisterHam(Ham_Item_Deploy,         iWeaponList[iTemp], "Ham_Item_Deploy_Post",    1);
        RegisterHam(Ham_CS_Item_GetMaxSpeed, iWeaponList[iTemp], "Ham_GetItemMaxSpeed_Pre", 0);
    }
   
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_usp",   "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_m4a1",  "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_sg552", "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_aug",   "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_g3sg1", "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_sg550", "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_scout", "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_awp",   "Ham_SecondaryAttack_Pre",  0);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_usp",   "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_m4a1",  "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_sg552", "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_aug",   "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_g3sg1", "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_sg550", "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_scout", "Ham_SecondaryAttack_Post", 1);
    RegisterHam(Ham_Weapon_SecondaryAttack, "weapon_awp",   "Ham_SecondaryAttack_Post", 1);
   
    RegisterHam(Ham_AddPlayerItem, "player", "Ham_AddPlayerItem_Post", 1);
    RegisterHam(Ham_Spawn,         "player", "Ham_Spawn_player_Post",  1);
   
#if defined DEBUG
    register_forward(FM_PlayerPreThink, "FM_PlayerPreThink_Post", 1);
#endif
}
 
#if defined DEBUG
public FM_PlayerPreThink_Post(iPlrId)
{
    if( is_user_alive(iPlrId) )
    {
        static Float:s_fVelocity[3];
        pev(iPlrId, pev_velocity, s_fVelocity);
        client_print(iPlrId, print_center, "Speed: %f", vector_length(s_fVelocity));
    }
}
#endif
 
public Ham_Item_Deploy_Post(iEnt)
{
    if( !pev_valid(iEnt) )
        return HAM_IGNORED;
   
    new iPlrId = pev(iEnt, pev_owner);
    if( !is_user_alive(iPlrId) )
        return HAM_IGNORED;
   
    new iCsWeaponType = get_pdata_int(iEnt, PDWeaponType, 4);
    if( iCsWeaponType<1 || iCsWeaponType==2 || iCsWeaponType>30 )
        return HAM_IGNORED;
   
    new Float:fWeight = fWeaponWeights[iCsWeaponType];
    if( cs_get_user_shield(iPlrId) && (iCsWeaponType==CSW_KNIFE || iCsWeaponType==CSW_GLOCK18 || iCsWeaponType==CSW_USP
     || iCsWeaponType==CSW_DEAGLE || iCsWeaponType==CSW_P228 || iCsWeaponType==CSW_FIVESEVEN
     || iCsWeaponType==CSW_FLASHBANG || iCsWeaponType==CSW_HEGRENADE || iCsWeaponType==CSW_SMOKEGRENADE) )
        fWeight += WEIGHT_SHIELD;
    else if( cs_get_weapon_silen(iEnt) )
        fWeight += ((iCsWeaponType==CSW_USP)?WEIGHT_USP_S:WEIGHT_M4A1_S);
   
   
    new Float:fSpeed;
    pev(iPlrId, pev_maxspeed, fSpeed);
    if( fSpeed!=1.0 )
    {
        fSpeed = float(floatround(get_speed_by_weight(fWeight)));
        engfunc(EngFunc_SetClientMaxspeed, iPlrId, fSpeed);
        set_pev(iPlrId, pev_maxspeed, fSpeed);
    }
    set_gravity_by_weight(iPlrId, fWeight);
   
    return HAM_IGNORED;
}
 
public Ham_GetItemMaxSpeed_Pre(iEnt, Float:fSpeed)
{
    new iPlrId = pev(iEnt, pev_owner);
    if( !is_user_alive(iPlrId) )
        return HAM_IGNORED;
   
    new iCsWeaponType = get_pdata_int(iEnt, PDWeaponType, 4);
    if( iCsWeaponType<1 || iCsWeaponType==2 || iCsWeaponType>30 )
        return HAM_IGNORED;
   
    if( cs_get_user_shield(iPlrId) && (iCsWeaponType==CSW_KNIFE || iCsWeaponType==CSW_GLOCK18 || iCsWeaponType==CSW_USP
     || iCsWeaponType==CSW_DEAGLE || iCsWeaponType==CSW_P228 || iCsWeaponType==CSW_FIVESEVEN
     || iCsWeaponType==CSW_FLASHBANG || iCsWeaponType==CSW_HEGRENADE || iCsWeaponType==CSW_SMOKEGRENADE) )
    {
        new Float:fSpeed;
        ExecuteHam(Ham_CS_Item_GetMaxSpeed, iEnt, fSpeed);
        if( fSpeed==180.0 )
            SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType]+WEIGHT_SHIELD)-SPEED_BONUCE_SHIELD)));
        else
            SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType]+WEIGHT_SHIELD))));
       
        return HAM_SUPERCEDE;
    }
    else
    {
        if( cs_get_weapon_silen(iEnt) )
        {
            SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType]+((iCsWeaponType==CSW_USP)?WEIGHT_USP_S:WEIGHT_M4A1_S)))));
               
            return HAM_SUPERCEDE;
        }
        else
        {
            switch( cs_get_user_zoom(iPlrId) )
            {
                case CS_SET_AUGSG552_ZOOM: SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])-SPEED_BONUCE_ZOOM1)));
                case CS_SET_FIRST_ZOOM:    SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])-SPEED_BONUCE_ZOOM2)));
                case CS_SET_SECOND_ZOOM:   SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])-SPEED_BONUCE_ZOOM3)));
                default:                   SetHamReturnFloat(float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType]))));
            }
           
            return HAM_SUPERCEDE;
        }
    }
   
    return HAM_IGNORED;
}
 
public Ham_SecondaryAttack_Pre(iEnt)
    g_fOldNextSecAttack = get_pdata_float(iEnt, PDNextSecAttack, 4);
 
public Ham_SecondaryAttack_Post(iEnt)
{
    if( !pev_valid(iEnt) )
        return HAM_IGNORED;
   
    if( g_fOldNextSecAttack==get_pdata_float(iEnt, PDNextSecAttack, 4) )
        return HAM_IGNORED;
   
    new iPlrId = pev(iEnt, pev_owner);
    if( !is_user_alive(iPlrId) )
        return HAM_IGNORED;
   
    new iCsWeaponType = get_pdata_int(iEnt, PDWeaponType, 4);
    if( iCsWeaponType<1 || iCsWeaponType==2 || iCsWeaponType>30 )
        return HAM_IGNORED;
   
    if( cs_get_user_shield(iPlrId) && iCsWeaponType==CSW_USP )
        return HAM_IGNORED;
   
    new Float:fSpeed, Float:fWeight = fWeaponWeights[iCsWeaponType];
    pev(iPlrId, pev_maxspeed, fSpeed);
    if( iCsWeaponType==CSW_USP || iCsWeaponType==CSW_M4A1 )
    {
        if( cs_get_weapon_silen(iEnt) )
            fWeight += ((iCsWeaponType==CSW_USP)?WEIGHT_USP_S:WEIGHT_M4A1_S);
        if( fSpeed!=1.0 )
        {
            fSpeed = float(floatround(get_speed_by_weight(fWeight)));
            engfunc(EngFunc_SetClientMaxspeed, iPlrId, fSpeed);
            set_pev(iPlrId, pev_maxspeed, fSpeed);
        }
        set_gravity_by_weight(iPlrId, fWeight);
    }
    else if( fSpeed!=1.0 )
    {
        switch( cs_get_user_zoom(iPlrId) )
        {
            case CS_SET_AUGSG552_ZOOM: fSpeed = float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])-SPEED_BONUCE_ZOOM1));
            case CS_SET_FIRST_ZOOM:    fSpeed = float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])-SPEED_BONUCE_ZOOM2));
            case CS_SET_SECOND_ZOOM:   fSpeed = float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])-SPEED_BONUCE_ZOOM3));
            default:                   fSpeed = float(floatround(get_speed_by_weight(fWeaponWeights[iCsWeaponType])));
        }
        engfunc(EngFunc_SetClientMaxspeed, iPlrId, fSpeed);
        set_pev(iPlrId, pev_maxspeed, fSpeed);
    }
   
    return HAM_IGNORED;
}
 
public Ham_AddPlayerItem_Post(iPlrId, iEnt)
{
    if( !is_user_alive(iPlrId) )
        return HAM_IGNORED;
   
    if( !pev_valid(iEnt) )
        return HAM_IGNORED;
   
    if( iEnt!=get_pdata_cbase(iPlrId, PDActiveItem, 4) )
        return HAM_IGNORED;
   
    new iCsWeaponType = get_pdata_int(iEnt, PDWeaponType, 4);
    if( iCsWeaponType<1 || iCsWeaponType==2 || iCsWeaponType>30 )
        return HAM_IGNORED;
   
    new Float:fWeight = fWeaponWeights[iCsWeaponType];
    if( cs_get_user_shield(iPlrId) && (iCsWeaponType==CSW_KNIFE || iCsWeaponType==CSW_GLOCK18 || iCsWeaponType==CSW_USP
     || iCsWeaponType==CSW_DEAGLE || iCsWeaponType==CSW_P228 || iCsWeaponType==CSW_FIVESEVEN
     || iCsWeaponType==CSW_FLASHBANG || iCsWeaponType==CSW_HEGRENADE || iCsWeaponType==CSW_SMOKEGRENADE) )
        fWeight += WEIGHT_SHIELD;
    else if( cs_get_weapon_silen(iEnt) )
        fWeight += ((iCsWeaponType==CSW_USP)?WEIGHT_USP_S:WEIGHT_M4A1_S);
   
   
    new Float:fSpeed;
    pev(iPlrId, pev_maxspeed, fSpeed);
    if( fSpeed!=1.0 )
    {
        fSpeed = float(floatround(get_speed_by_weight(fWeight)));
        engfunc(EngFunc_SetClientMaxspeed, iPlrId, fSpeed);
        set_pev(iPlrId, pev_maxspeed, fSpeed);
    }
    set_gravity_by_weight(iPlrId, fWeight);
   
    return HAM_IGNORED;
}
 
public Ham_Spawn_player_Post(iPlrId)
{
    if( !is_user_alive(iPlrId) )
        return HAM_IGNORED;
   
    new iEnt = get_pdata_cbase(iPlrId, PDActiveItem, 4);
    if( !pev_valid(iEnt) )
        return HAM_IGNORED;
   
    new iCsWeaponType = get_pdata_int(iEnt, PDWeaponType, 4);
    if( iCsWeaponType<1 || iCsWeaponType==2 || iCsWeaponType>30 )
        return HAM_IGNORED;
   
    new Float:fWeight = fWeaponWeights[iCsWeaponType];
    if( cs_get_user_shield(iPlrId) && (iCsWeaponType==CSW_KNIFE || iCsWeaponType==CSW_GLOCK18 || iCsWeaponType==CSW_USP
     || iCsWeaponType==CSW_DEAGLE || iCsWeaponType==CSW_P228 || iCsWeaponType==CSW_FIVESEVEN
     || iCsWeaponType==CSW_FLASHBANG || iCsWeaponType==CSW_HEGRENADE || iCsWeaponType==CSW_SMOKEGRENADE) )
        fWeight += WEIGHT_SHIELD;
    else if( cs_get_weapon_silen(iEnt) )
        fWeight += ((iCsWeaponType==CSW_USP)?WEIGHT_USP_S:WEIGHT_M4A1_S);
   
   
    new Float:fSpeed;
    pev(iPlrId, pev_maxspeed, fSpeed);
    if( fSpeed!=1.0 )
    {
        fSpeed = float(floatround(get_speed_by_weight(fWeight)));
        engfunc(EngFunc_SetClientMaxspeed, iPlrId, fSpeed);
        set_pev(iPlrId, pev_maxspeed, fSpeed);
    }
    set_gravity_by_weight(iPlrId, fWeight);
   
    return HAM_IGNORED;
}
 
Float:clamp_f(Float:Value, Float:MinValue, Float:MaxValue)
{
    if( Value<=MinValue )
    {
        return MinValue;
    }
   
    if( Value>=MaxValue )
    {
        return MaxValue;
    }
   
    return Value;
}
 
Float:get_cvar_float_minspeed()
    return clamp_f(get_pcvar_float(g_iCvarP_MinSpeed), 2.0, 400.0);
 
Float:get_cvar_float_maxspeed()
    return clamp_f(get_pcvar_float(g_iCvarP_MaxSpeed), 2.0, 400.0);
 
Float:get_cvar_float_mingravp()
    return clamp_f(get_pcvar_float(g_iCvarP_MinGrav), 0.0, 150.0);
 
Float:get_cvar_float_maxgravp()
    return clamp_f(get_pcvar_float(g_iCvarP_MaxGrav), 0.0, 150.0);
 
bool:set_gravity_by_weight(iPlrId, Float:fWeight)
{
    if( fWeight<=0.0 )
        set_pev(iPlrId, pev_gravity, 1.0);
    else
    {
        new Float:fMinValue = get_cvar_float_mingravp();
        new Float:fMaxValue = get_cvar_float_maxgravp();
       
        if( !fMinValue )
            fMinValue = 1.0;
       
        if( !fMaxValue )
            fMaxValue = 1.0;
       
        if( fMinValue==fMaxValue )
        {
            set_pev(iPlrId, pev_gravity, fMinValue);
            return true;
        }
        else if( fMinValue>fMaxValue )
        {
            new Float:fTempValue = fMaxValue;
            fMaxValue = fMinValue;
            fMinValue = fTempValue;
        }
       
#if defined DEBUG
        client_print(iPlrId, print_chat, "set grav: %f", (1.0+(1.0-(fMaxValue-((fMaxValue-fMinValue)*fWeight/7.02)))));
#endif
        set_pev(iPlrId, pev_gravity, (1.0+(1.0-(fMaxValue-((fMaxValue-fMinValue)*fWeight/7.02)))));
        return true;
    }
   
    return false;
}
 
Float:get_speed_by_weight(Float:fWeight)
{
    if( fWeight<=0.0 )
        return get_cvar_float_maxspeed();
   
    new Float:fMinValue = get_cvar_float_minspeed();
    new Float:fMaxValue = get_cvar_float_maxspeed();
   
    if( fMinValue==fMaxValue )
        return fMinValue;
    else if( fMinValue>fMaxValue )
    {
        new Float:fTempValue = fMaxValue;
        fMaxValue = fMinValue;
        fMinValue = fTempValue;
    }
   
    return (fMaxValue-((fMaxValue-fMinValue)*fWeight/7.02));
}
 
/*
3.51*100%/7.02 = 50%
 
(260-210)*50%/100% = 25
 
260-25 = 235
*/