#pragma semicolon 1
 
#include <amxmodx>
#include <reapi>
#include <nvault>
 
enum _:szRank {
    Rank_Name,
    Rank_MaxXp,
    Rank_TxtSprFileName
}
 
/* {"RankName", Maximum XP to level up, "txt_spr_name"} */
new const szRankNames[][][] = {
    {"Unranked", 20, ""},
    {"Silver I", 20, "silver1"},
    {"Silver II", 24, "silver2"},
    {"Silver III", 28, "silver3"},
    {"Silver IV", 32, "silver4"},
    {"Silver Elite", 36, "silverelite"},
    {"Silver Elite Master", 40, "silverelitemaster"},
    {"Gold Nova I", 45, "goldnova1"},
    {"Gold Nova II", 50, "goldnova2"},
    {"Gold Nova III", 55, "goldnova3"},
    {"Gold Nova Master", 60, "goldnovamaster"},
    {"Master Guardian I", 66, "masterguardian1"},
    {"Master Guardian II", 72, "masterguardian2"},
    {"Master Guardian Elite", 78, "masterguardianelite"},
    {"Distinguished Master Guardian", 86, "distinguishedmasterguardian"},
    {"Legendary Eagle", 95, "legendaryeagle"},
    {"Legendary Eagle Master", 105, "legendaryeaglemaster"},
    {"Supreme Master First Class", 110, "suprememasterfirstclass"},
    {"The Global Elite", 120, "theglobalelite"}
};
 
new bool:g_PlayerRankedUp[MAX_CLIENTS + 1],
    g_rank[MAX_CLIENTS + 1],
    g_xp[MAX_CLIENTS + 1],
    pCvars[6],
    pMsgIds[4],
    pForward[2],
    hud_sync;
 
public plugin_init() {
    register_plugin("Advanced Rank System", "0.0.2", "PurposeLess");
 
    register_event("CurWeapon", "@Event_CurWeapon", "be", "1=1");
 
    RegisterHookChain(RG_CBasePlayer_Killed, "@CBasePlayer_Killed", .post = true);
 
    if(get_member_game(m_bMapHasBombZone)) {
        RegisterHookChain(RG_CGrenade_DefuseBombEnd, "@CGrenade_DefuseBombEnd", .post = true);
        RegisterHookChain(RG_PlantBomb, "@PlantBomb", .post = true);
    }
 
    bind_pcvar_num(create_cvar("ars_dead_xp", "-2"), pCvars[0]);
    bind_pcvar_num(create_cvar("ars_kill_xp", "3"), pCvars[1]);
    bind_pcvar_num(create_cvar("ars_kill_hp_xp", "2"), pCvars[2]);
    bind_pcvar_num(create_cvar("ars_defuse_xp", "3"), pCvars[3]);
    bind_pcvar_num(create_cvar("ars_plant_xp", "3"), pCvars[4]);
    bind_pcvar_num(create_cvar("ars_spr_appear_time", "5"), pCvars[5]);
 
    pMsgIds[0] = get_user_msgid("WeaponList");
    pMsgIds[1] = get_user_msgid("SetFOV");
    pMsgIds[2] = get_user_msgid("CurWeapon");
    pMsgIds[3] = get_user_msgid("HideWeapon");
 
    pForward[0] = CreateMultiForward("ars_rank_up", ET_IGNORE, FP_CELL);
    pForward[1] = CreateMultiForward("ars_rank_down", ET_IGNORE, FP_CELL);
 
    hud_sync = CreateHudSyncObj();
}
 
public plugin_precache() {
    for(new i = 1; i < sizeof(szRankNames); i++) {
        precache_generic(fmt("sprites/ars/%s.txt", szRankNames[i][Rank_TxtSprFileName][0]));
        precache_generic(fmt("sprites/ars/%s.spr", szRankNames[i][Rank_TxtSprFileName][0]));
    }
}
 
public plugin_natives() {
    register_native("ars_get_user_xp", "@ars_get_user_xp");
    register_native("ars_get_user_rank", "@ars_get_user_rank");
    register_native("ars_get_user_rankname", "@ars_get_user_rankname");
}
 
@ars_get_user_xp() {
    new pPlayer = get_param(1);
 
    return g_xp[pPlayer];
}
 
@ars_get_user_rank() {
    new pPlayer = get_param(1);
 
    return g_rank[pPlayer];
}
 
@ars_get_user_rankname() {
    new pPlayer = get_param(1);
 
    set_array(2, szRankNames[g_rank[pPlayer]][Rank_Name], get_param(3));
}
 
public client_putinserver(pPlayer) {
    if(is_user_bot(pPlayer)) {
        return;
    }
 
    set_task(1.0, "@ShowHudmessage", pPlayer, .flags = "b");
}
 
@ShowHudmessage(const pPlayer) {
    set_hudmessage(210, 105, 30, 0.01, 0.15, 0, _, 1.0, 0.1, 0.1);
    ShowSyncHudMsg(pPlayer, hud_sync, "Rank: %s^nRank Xp: %i/%i", szRankNames[g_rank[pPlayer]][Rank_Name], g_xp[pPlayer], szRankNames[g_rank[pPlayer]][Rank_MaxXp]);
}
 
@Event_CurWeapon(const pPlayer) {
    if(!g_PlayerRankedUp[pPlayer] || get_member(pPlayer, m_iFOV) != 90) {
        return;
    }
 
    Show_Rank_Event(pPlayer);
}
 
@CBasePlayer_Killed(const pVictim, pAttacker, iGib) {
    if(pVictim == pAttacker || !is_user_connected(pAttacker)) {
        return;
    }
 
    g_xp[pVictim] += pCvars[0];
    g_xp[pAttacker] += pCvars[1];
 
    if(get_member(pVictim, m_bHeadshotKilled)) {
        g_xp[pAttacker] += pCvars[2];
    }
 
    RankCheck(pAttacker);
    RankCheck(pVictim);
}
 
@CGrenade_DefuseBombEnd(const pBomb, const pPlayer, bool:bDefused) {
    if(bDefused && pCvars[3]) {
        g_xp[pPlayer] += pCvars[3];
        RankCheck(pPlayer);
    }
}
 
@PlantBomb(const pPlayer, Float:vecStart[3], Float:vecVelocity[3]) {
    if(pCvars[4]) {
        g_xp[pPlayer] += pCvars[4];
        RankCheck(pPlayer);
    }
}
 
const TASKID_ARS = 1337;
 
RankCheck(const pPlayer) {
    if(g_xp[pPlayer] >= szRankNames[g_rank[pPlayer]][Rank_MaxXp][0]) {
        if(g_rank[pPlayer] == sizeof(szRankNames) - 1) {
            g_xp[pPlayer] = szRankNames[g_rank[pPlayer]][Rank_MaxXp][0];
            return;
        }
        g_xp[pPlayer] -= szRankNames[g_rank[pPlayer]][Rank_MaxXp][0];
        g_rank[pPlayer]++;
        g_PlayerRankedUp[pPlayer] = true;
        Show_Rank_Event(pPlayer);
        remove_task(pPlayer + TASKID_ARS);
        set_task(float(pCvars[5]), "@Clear_Rank_Event", pPlayer + TASKID_ARS);
 
        ExecuteForward(pForward[0], _, pPlayer);
    }
    else if(g_xp[pPlayer] <= 0) {
        if(g_rank[pPlayer] < 2) {
            g_xp[pPlayer] = 0;
            return;
        }
 
        g_rank[pPlayer]--;
        g_xp[pPlayer] = szRankNames[g_rank[pPlayer]][Rank_MaxXp][0] - g_xp[pPlayer];
        ExecuteForward(pForward[1], _, pPlayer);
    }
}
 
Show_Rank_Event(const pPlayer) {
    new ammo, weapon = get_user_weapon(pPlayer, ammo);
 
    switch(weapon) {
        case CSW_P228: SetMessage_WeaponList(pPlayer, 9, 52);
        case CSW_HEGRENADE: SetMessage_WeaponList(pPlayer, 12, 1);
        case CSW_XM1014: SetMessage_WeaponList(pPlayer, 5, 32);
        case CSW_C4: SetMessage_WeaponList(pPlayer, 14, 1);
        case CSW_MAC10: SetMessage_WeaponList(pPlayer, 6, 100);
        case CSW_AUG: SetMessage_WeaponList(pPlayer, 4, 90);
        case CSW_SMOKEGRENADE: SetMessage_WeaponList(pPlayer, 13, 1);
        case CSW_ELITE: SetMessage_WeaponList(pPlayer, 10, 120);
        case CSW_FIVESEVEN: SetMessage_WeaponList(pPlayer, 7, 100);
        case CSW_UMP45: SetMessage_WeaponList(pPlayer, 6, 100);
        case CSW_GALIL: SetMessage_WeaponList(pPlayer, 4, 90);
        case CSW_FAMAS: SetMessage_WeaponList(pPlayer, 4, 90);
        case CSW_USP: SetMessage_WeaponList(pPlayer, 6, 100);
        case CSW_GLOCK18: SetMessage_WeaponList(pPlayer, 10, 120);
        case CSW_MP5NAVY: SetMessage_WeaponList(pPlayer, 10, 120);
        case CSW_M249: SetMessage_WeaponList(pPlayer, 3, 200);
        case CSW_M3: SetMessage_WeaponList(pPlayer, 5, 32);
        case CSW_M4A1: SetMessage_WeaponList(pPlayer, 4, 90);
        case CSW_TMP: SetMessage_WeaponList(pPlayer, 10, 120);
        case CSW_FLASHBANG: SetMessage_WeaponList(pPlayer, 11, 2);
        case CSW_DEAGLE: SetMessage_WeaponList(pPlayer, 8, 35);
        case CSW_SG552: SetMessage_WeaponList(pPlayer, 4, 90);
        case CSW_AK47: SetMessage_WeaponList(pPlayer, 2, 90);
        case CSW_KNIFE: SetMessage_WeaponList(pPlayer, -1, -1);
        case CSW_P90: SetMessage_WeaponList(pPlayer, 7, 100);
        case CSW_SCOUT: SetMessage_WeaponList(pPlayer, 2, 90);
        case CSW_SG550: SetMessage_WeaponList(pPlayer, 4, 90);
        case CSW_AWP: SetMessage_WeaponList(pPlayer, 1, 30);
        case CSW_G3SG1: SetMessage_WeaponList(pPlayer, 2, 90);
        default: return;
    }
 
    SetMessage_SetFOV(pPlayer, 89);
    SetMessage_CurWeapon(pPlayer, ammo);
    SetMessage_SetFOV(pPlayer, 90);
}
 
@Clear_Rank_Event(TaskId) {
    new pPlayer = TaskId - TASKID_ARS;
 
    g_PlayerRankedUp[pPlayer] = false;
    SetMessage_HideWeapon(pPlayer);
}
 
SetMessage_WeaponList(const pPlayer, const pAmmoId, const pAmmoMaxAmount) {
    message_begin(MSG_ONE, pMsgIds[0], .player = pPlayer); {
        write_string(fmt("ars/%s", szRankNames[g_rank[pPlayer]][Rank_TxtSprFileName]));
        write_byte(pAmmoId);
        write_byte(pAmmoMaxAmount);
        write_byte(-1);
        write_byte(-1);
        write_byte(0);
        write_byte(11);
        write_byte(2);
        write_byte(0);
    }
    message_end();
}
 
SetMessage_SetFOV(const pPlayer, const FOV) {
    message_begin(MSG_ONE, pMsgIds[1], .player = pPlayer); {
        write_byte(FOV);
    }
    message_end();
}
 
SetMessage_CurWeapon(const pPlayer, const ammo) {
    message_begin(MSG_ONE, pMsgIds[2], .player = pPlayer); {
        write_byte(1);
        write_byte(2);
        write_byte(ammo);
    }
    message_end();
}
 
SetMessage_HideWeapon(const pPlayer) {
    message_begin(MSG_ONE, pMsgIds[3], .player = pPlayer);
    write_byte(0);
    message_end();
}
 
/* nvault */
 
new g_vault;
 
public plugin_cfg() {
    g_vault = nvault_open("AdvancedRankSystem");
 
    if(g_vault == INVALID_HANDLE) {
        set_fail_state("Unknown nvault for AdvancedRankSystem");
    }
}
 
public plugin_end() {
    nvault_close(g_vault);
}
 
public client_authorized(pPlayer, const authid[]) {
    g_xp[pPlayer] = nvault_get(g_vault, fmt("%s_xp", authid));
    g_rank[pPlayer] = nvault_get(g_vault, fmt("%s_rank", authid));
}
 
public client_disconnected(pPlayer) {
    new authid[MAX_AUTHID_LENGTH], data[MAX_AUTHID_LENGTH+10];
    get_user_authid(pPlayer, authid, charsmax(authid));
 
    num_to_str(g_xp[pPlayer], data, charsmax(data));
    nvault_pset(g_vault, fmt("%s_xp", authid), data);
 
    num_to_str(g_rank[pPlayer], data, charsmax(data));
    nvault_pset(g_vault, fmt("%s_rank", authid), data);
 
    remove_task(pPlayer);
    remove_task(pPlayer + TASKID_ARS);
}