init()
{
    // Registra os novos comandos no CoD4x
    addscriptcommand( "stats", 1 );
	addscriptcommand( "rstats", 1);
	addscriptcommand("top", 1 );
    addscriptcommand( "fov", 1 );
    addscriptcommand( "fps", 1 );
    addscriptcommand( "promod", 1 );
    addscriptcommand( "lvl", 1 );
    addscriptcommand( "class", 1 );
	addscriptcommand( "mapsave", 1 );
	addscriptcommand( "check", 1 );
	//com senha
	addscriptcommand( "mapa", 1 );
	addscriptcommand( "restart", 1 );
	addscriptcommand( "multixp", 1 );
	addscriptcommand( "rglobal", 1 );
}

commandHandler( command, arguments )
{
    if(!isDefined(self) || !isPlayer(self)) 
        return;

    switch( toLower( command ) )
    {
		/*
		case "stats":
            pTarget = self;
            
            if( isDefined( arguments ) && arguments != "" )
            {
                if( isInt( arguments ) ) 
                    pTarget = getEntByNum( int( arguments ) );
                else 
                    pTarget = self getEntByStr( arguments );
            }

            if( !isDefined( pTarget ) )
            {
                self iPrintlnBold( "Jogador nao encontrado!" );
                break;
            }

            // --- BUSCA EXCLUSIVA E DIRETA DOS DADOS DO BANCO EM MEMÓRIA ---
            // Ignora completamente o scoreboard nativo do jogo (self.kills/self.deaths)
            totalKillsGlobais = 0;
            totalDeathsGlobais = 0;
            
            if( isDefined( pTarget.pers["total_kills"] ) ) 
                totalKillsGlobais = int(pTarget.pers["total_kills"]);
            
            if( isDefined( pTarget.pers["total_deaths"] ) ) 
                totalDeathsGlobais = int(pTarget.pers["total_deaths"]);
            
            // --- CÁLCULO SEGURO DO TRUE SKILL / RANK ---
            mu = 25.0; 
            sigma = 8.33;
            if( isDefined( pTarget.pers["mu"] ) ) mu = float(pTarget.pers["mu"]);
            if( isDefined( pTarget.pers["sigma"] ) ) sigma = float(pTarget.pers["sigma"]);

            ts_skill = int( mu - (3 * sigma) );
            if( ts_skill < 0 ) ts_skill = 0;

            sRank = "^3Recruta";
            if(ts_skill >= 10) sRank = "^2Veterano";
            if(ts_skill >= 20) sRank = "^1Elite";
            if(ts_skill >= 30) sRank = "^8Lenda";

            // Impressao limpa no chat global buscando apenas as chaves estaticas do .db
            exec("say ^7Stats Globais para ^2" + pTarget.name + " : ");
            exec("say ^7 Kills Totais: ^3" + totalKillsGlobais + "^7 | Deaths Totais: ^3" + totalDeathsGlobais + " ^7 | Rank: ^3" + sRank);
            break;
		case "rstats":
			// 1. Zera a Memoria RAM de Progresso
			self.pers["total_kills"] = 0;
			self.pers["total_deaths"] = 0;
			self.pers["mu"] = 25;
			self.pers["sigma"] = 8.33;
			self.kills = 0;
			self.deaths = 0;

			self notify("stats_reset"); 

			// 2. RECONSTRUCAO DO BUFFER (Preserva FOV e Promod [fps])
			self thread code\player::updateSaveBuffer(); 

			// 3. GRAVACAO IMEDIATA NO ARQUIVO .DB
			guid = self getGuid();
			self thread code\player::FSSave( guid );
			self thread code\toprank::updateGlobalRanking();

			self iPrintLnBold("^1Seus Status foram resetados!");
			self iPrintLn("^7FOV e Promod foram ^2preservados^7.");
			self playLocalSound( "mp_war_objective_taken" ); 
			break;	
			*/

		case "stats":
            pTarget = self; // Definido com T maiúsculo
            
            if( isDefined( arguments ) && arguments != "" )
            {
                if( isInt( arguments ) ) 
                    pTarget = getEntByNum( int( arguments ) );
                else 
                    pTarget = self getEntByStr( arguments );
            }

            if( !isDefined( pTarget ) )
            {
                self iPrintlnBold( "Jogador nao encontrado!" );
                break;
            }

            // --- BUSCA OS DADOS ACUMULADOS (TOTAL DA RAM) ---
            k = 0;
            d = 0;
            
            if( isDefined( pTarget.pers["total_kills"] ) ) 
                k = pTarget.pers["total_kills"];
            
            if( isDefined( pTarget.pers["total_deaths"] ) ) 
                d = pTarget.pers["total_deaths"];
            
            // --- BUSCA O RANK ---
            mu = 25.0; 
            sigma = 8.33;
            if( isDefined( pTarget.pers["mu"] ) ) mu = pTarget.pers["mu"];
            if( isDefined( pTarget.pers["sigma"] ) ) sigma = pTarget.pers["sigma"];

            ts_skill = int( mu - (3 * sigma) );
            if( ts_skill < 0 ) ts_skill = 0;

            sRank = "^3Recruta";
            if(ts_skill >= 10) sRank = "^2Veterano";
            if(ts_skill >= 20) sRank = "^1Elite";
            if(ts_skill >= 30) sRank = "^8Lenda";

            // Corrigido para pTarget em todos os anúncios
            exec ("say ^7Stats para ^2" + pTarget.name + " : ");
            exec ("say ^7 Kills: ^3" + k + "^7 | Deaths: ^3" + d + " ^7 | Rank: ^3" + sRank);
            break;

        case "rstats":
			// 1. Zera a Memória RAM (Placar e Totais)
			self.pers["kills"] = 0;
			self.pers["deaths"] = 0;
			self.pers["total_kills"] = 0;
			self.pers["total_deaths"] = 0;
			self.pers["mu"] = 25;
			self.pers["sigma"] = 8.33;

			// 2. Avisa o monitor para não somar lixo
			self notify("stats_reset"); 

			// 3. RECONSTRUÇÃO DA TABELA (Com índices fixos para garantir o Save)
			guid = self getGuid();
			level.FSCD[ guid ] = []; 
			
			// Mantemos a ordem exata que o seu FSLookup e FSSave esperam
			level.FSCD[ guid ][ 0 ] = "fullbright;" + self.pers[ "fullbright" ];
			level.FSCD[ guid ][ 1 ] = "fov;" + self.pers[ "fov" ];
			level.FSCD[ guid ][ 2 ] = "promod;" + self.pers[ "promod" ];
			level.FSCD[ guid ][ 3 ] = "mu;25";
			level.FSCD[ guid ][ 4 ] = "sigma;8.33";
			level.FSCD[ guid ][ 5 ] = "kills;0";
			level.FSCD[ guid ][ 6 ] = "deaths;0";
			
			// 4. GRAVAÇÃO IMEDIATA
			self thread code\player::FSSave( guid );
			self thread code\toprank::updateGlobalRanking();

			// 5. Feedback
			//self iPrintLnBold("^1Seus Status Globais foram resetados!");
			self iPrintLnBold("^1Seus Status foram resetados!");
			self iPrintLn("^7FOV e Promod foram ^2preservados^7.");
			self playLocalSound( "mp_war_objective_taken" );
			break;	

		case "fov":
            if(!isDefined(self.pers["fov"])) 
                self.pers["fov"] = 0; // Padrão inicial: Padrao (0)

            // Rotação linear oficial e estável: 0 (Padrao) -> 1 (1.30) -> 2 (1.25) -> 3 (1.125) -> volta para 0
            if(self.pers["fov"] == 0)
			{
				self setClientDvar("cg_fovscale", 1.30);
				self.pers["fov"] = 1; // Salva o index 1 para cravar os 1.30 no banco!
				self iPrintLn("FOV: ^11.30");
			}
			else if(self.pers["fov"] == 1)
			{
				self setClientDvar("cg_fovscale", 1.25);
				self.pers["fov"] = 2;
				self iPrintLn("FOV: ^61.25");
			}
			else if(self.pers["fov"] == 2)
			{
				self setClientDvar("cg_fovscale", 1.125);
				self.pers["fov"] = 3;
				self iPrintLn("FOV: ^51.125");
			}
			else if(self.pers["fov"] == 3)
			{
				self setClientDvar("cg_fovscale", 1.0);
				self.pers["fov"] = 0;
				self iPrintLn("FOV: ^3Padrao");
			}
            
            self playLocalSound( "ui_pulse_text_type" );
            
            // Grava na hora usando o banco de dados principal
            self thread code\player::updateSaveBuffer();
			self thread code\player::FSSave( self getGuid() );
            break;

		case "top":
            topLines = code\file::readFile( "ne_db/ranking_global.db" );

            if( !isDefined( topLines ) || !isArray( topLines ) || topLines.size == 0 )
            {
                self iPrintLnBold( "^1Ranking ainda vazio!" );
                break;
            }

            topMsg = "^3TOP 5: ";

            for( i = 0; i < 5; i++ )
            {
                if( !isDefined( topLines[i] ) ) 
                    break;

                data = strTok( topLines[i], ";" );
                
                if( !isDefined( data[0] ) || !isDefined( data[1] ) )
                    continue;

                pName = data[0];
                pSkill = data[1];

                color = "^7";
                if( i == 0 ) color = "^3"; 
                else if( i == 1 ) color = "^2"; 
                else if( i == 2 ) color = "^1"; 

                topMsg += color + (i+1) + ". " + pName + " ^7| ";
            }

            exec( "say " + topMsg );
            break;


		case "promod":
            // 1. GARANTE QUE A VARIÁVEL EXISTA NO BANCO DE DADOS
            if(!isDefined(self.pers["promod"]))
                self.pers["promod"] = 1; // Padrão inicial: PADRAO (1)

            // 2. INCREMENTA A VISÃO (Lógica de rotação idêntica ao Menu)
            self.pers["promod"]++;
            
            if(self.pers["promod"] > 5) 
                self.pers["promod"] = 0; // Retorna para 0 (CRASH)

			// Sincroniza a interface do menu na hora
			self setClientDvar("ui_promod_vision", self.pers["promod"]);
         
			// 3. APLICAÇÃO DOS TONS VISUAIS REORGANIZADOS
			if (self.pers["promod"] == 0) // PADRAO DO JOGO
			{
				self setClientDvars("r_filmTweakBrightness", "0", "r_filmTweakContrast", "1.4", "r_filmTweakDarkTint", "0.7 0.85 1","r_filmTweakDesaturation", "0.2","r_filmTweakInvert", "0","r_filmTweakLightTint", "1.1 1.05 0.85","r_filmtweaks", "0","r_filmUseTweaks", "0","r_lightTweakSunColor", "1 0.941177 0.8 1","r_lightTweakSunLight","1","r_glow_allowed","0","r_glow_enable", "0", "sm_maxLights", "4", "r_specularcolorscale", "1", "r_glowTweakBloomCutoff", "0.5","r_glowtweakbloomintensity0", "1", "r_blur", "0");
				self setClientDvar("r_filmusetweaks", 0);
				self setClientDvar("r_filmtweakenable", 0);
				self setClientDvar("r_blur", 0); 
				self setClientDvar("r_fullbright", 0);
				
				self iPrintLnBold("VISAO: ^7PADRAO DO JOGO");
				self playLocalSound("ui_pulse_text_type");
				self code\_common::setCvar("filmtweak", "0");
				self.pers["filmtweak"] = 0;
			}
			else if (self.pers["promod"] == 1) // TARDE
			{
				self setClientDvars("r_filmTweakBrightness", "0.1", "r_filmTweakContrast", "1.1", "r_filmTweakDarkTint", "0.7 0.72 0.95", "r_filmTweakDesaturation", "0", "r_filmTweakInvert", "0", "r_filmTweakLightTint", "1 1.02 1.2", "r_filmtweaks", "1", "r_filmUseTweaks", "1", "r_lightTweakSunColor", "0.85098 0.45098 0.2 0", "r_lightTweakSunLight", "1.5", "r_glow_allowed", "0",  "r_glow_enable", "0", "sm_maxLights", "4", "r_specularcolorscale", "8", "r_glowTweakBloomCutoff", "0.8", "r_glowtweakbloomintensity0", "1", "r_blur", "0");
				self setClientDvar("r_filmusetweaks", 1);
				self setClientDvar("r_filmtweakenable", 1);
				
				self iPrintLnBold("VISAO: ^8Tardezinha");
				self playLocalSound("ui_pulse_text_type");
				self code\_common::setCvar("filmtweak", "1");
				self.pers["filmtweak"] = 1;
			}	
			else if (self.pers["promod"] == 2) // PROMOD
			{
				self setClientDvars("r_filmTweakInvert", "0", "r_filmTweakContrast", "0.5","r_filmTweakBrightness", "0", "r_filmtweakLighttint", "0.8 0.8 1",  "r_filmTweakContrast", "1.2", "r_filmTweakDesaturation", "0", "r_filmTweakDarkTint", "1.8 1.8 2", "r_lightTweakSunColor", "1 0.921569 0.878431 1", "r_lightTweakSunLight", "1.15", "r_glow_allowed", "0",  "r_glow_enable", "0", "sm_maxLights", "4", "r_specularcolorscale", "8", "r_glowTweakBloomCutoff", "0.8", "r_glowtweakbloomintensity0", "1", "r_blur", "0");
				self setClientDvar("r_filmusetweaks", 1);
				self setClientDvar("r_filmtweakenable", 1);
				
				self iPrintLnBold("VISAO: ^6Promod");
				self playLocalSound("ui_pulse_text_type"); 
				self code\_common::setCvar("filmtweak", "1");   
				self.pers["filmtweak"] = 1;
			}
			else if (self.pers["promod"] == 3) // SUNNY
			{
				self setClientDvars("r_filmtweakLighttint","0.9 0.9 1","r_filmTweakContrast", "1.15", "r_filmtweakDarktint","1.1 1.05 0.85","r_filmtweakbrightness","0.1", "r_lightTweakSunColor", "1 0.941177 0.8 1", "r_lightTweakSunLight", "1.5", "r_glow_allowed", "0",  "r_glow_enable", "0", "sm_maxLights", "4", "r_specularcolorscale", "8", "r_glowTweakBloomCutoff", "0.8", "r_glowtweakbloomintensity0", "1", "r_blur", "0");
				self setClientDvar("r_filmusetweaks", 1);
				self setClientDvar("r_filmtweakenable", 1);
				
				self iPrintLnBold("VISAO: ^9Sunny");
				self playLocalSound("ui_pulse_text_type");
				self code\_common::setCvar("filmtweak", "1");
				self.pers["filmtweak"] = 1;
			}
			else if (self.pers["promod"] == 4) // NUBLADO
			{
				self setClientDvars("r_filmtweakenable", 1, "r_filmtweakContrast", 1.15, "r_filmtweakLighttint", "1.1 1.1 1.1", "r_filmtweakDarktint", "1.8 1.8 1.8", "r_filmtweakDesaturation", 0, "r_filmTweakInvert", "0", "r_gamma", 1.1, "r_filmtweakbrightness", 0, "r_lightTweakSunLight", 0);
				self setClientDvar("r_filmusetweaks", 1);
				
				self iPrintLnBold("VISAO: ^4Nublado");
				self playLocalSound("ui_pulse_text_type");
				self code\_common::setCvar("filmtweak", "1");
				self.pers["filmtweak"] = 1;
			}
			else if (self.pers["promod"] == 5) // CRASH
			{
				self setClientDvar("r_filmusetweaks", 1);
				self setClientDvar("r_filmtweakenable", 1);
				self setClientDvars("r_filmtweakContrast","1.15","r_filmtweakLighttint","0.85 0.85 0.98","r_filmtweakDarktint","1.24 1.44 1.9","r_filmtweakDesaturation","0","r_filmtweakbrightness","0.1", "r_lightTweakSunColor", "1 0.921569 0.878431 1", "r_lightTweakSunLight", "1.15", "r_glow_allowed", "0",  "r_glow_enable", "0", "sm_maxLights", "4", "r_specularcolorscale", "8", "r_glowTweakBloomCutoff", "0.8", "r_glowtweakbloomintensity0", "1", "r_blur", "0");
				
				self iPrintLnBold("VISAO: ^5Crash");
				self playLocalSound("ui_pulse_text_type");
				self code\_common::setCvar("filmtweak", "1");
				self.pers["filmtweak"] = 1;
			}

            // 4. ATUALIZACAO DO BUFFER E GRAVACAO AUTOMATICA NO FINAL
            self thread code\player::updateSaveBuffer();
            
			guid = self getGuid();
            self thread code\player::FSSave( guid );

            break;

        case "fps":
            self setFullbright();
            break;

		case "rglobal": 
            // 1. Define sua senha aqui
            adminPassword = "animeforever"; 

            if( !isDefined( arguments ) || arguments != adminPassword )
            {
                self iPrintLnBold( "^1Senha de Admin incorreta!" );
                // Log de segurança (opcional)
                exec( "say Tentativa de reset ^1Placar Total ^7falhou: " + self.name + " GUID: " + self getGuid() + "\n" );
                break;
            }
   

            // 4. RESET DO RANKING DE PLAYERS (Zera o arquivo de Ranking)
            path_players = "ne_db/ranking_global.db";
            code\file::writeToFile( path_players, "" ); // Limpa o arquivo completamente

            // 5. Feedback Global
            exec( "say ^1[ADMIN] ^7Todos os status globais foram resetados por ^2 UM ADM" );
            // exec( "say ^1[ADMIN] ^7Todos os status globais foram resetados por ^2" + self.name );
            self playLocalSound( "mp_war_objective_taken" );
            break;	

        case "restart": 
            senhaCorreta = "animeforever"; 

            // 1. Monta a senha (0 ao 11)
            senhaLida = "";
            for(i = 0; i < 12; i++) {
                if(isDefined(arguments[i]))
                    senhaLida += arguments[i];
            }

            // 2. Validação da Senha
            if( senhaLida != senhaCorreta )
            {
                self iPrintLnBold( "^1Senha de Admin incorreta!" );
                break;
            }

            // 3. PROCESSO DE SALVAMENTO REAL
            players = level.players;
            for(i = 0; i < players.size; i++)
            {
                if( isDefined(players[i]) )
                {
                    // CRÍTICO: Atualiza os dados na memória antes de mandar para o arquivo
                    players[i] code\player::updateSaveBuffer();
                    players[i] thread code\player::FSSave(players[i] getGuid());
                }
            }

            // 4. CONTADOR REGRESSIVO
            tempo = 5; 
            while( tempo > 0 )
            {
                iPrintLnBold( "Reiniciando mapa em: ^1" + tempo );
                iPrintLn("Salvando Banco de Dados... ^2AGUARDE ^1" + tempo );  
                wait 1.0;
                tempo--;
            }

            wait 0.5;

            // 5. EXECUTA O RESTART
            // map_restart recarrega o mapa atual limpando entidades
            exec("map_restart");
            break;

        case "mapa": 
            senhaCorreta = "animeforever"; 

            // Tratamento direto de string via CoD4x Arguments
            if( !isDefined( arguments ) ) {
                self iPrintLn("^1Uso: ^7mapa [senha-admin] [nome-do-mapa]");
                break;
            }

            // Separa os argumentos por espaco: data[0] = senha, data[1] = mapa
            data = strTok( arguments, " " );

            if( !isDefined( data[0] ) || data[0] != senhaCorreta ) {
                self iPrintLnBold( "^1Senha de Admin incorreta!" );
                break;
            }

            if( !isDefined( data[1] ) || data[1] == "" ) {
                self iPrintLn("^1Erro: ^7Especifique o apelido do mapa. Ex: /mapa animeforever crash");
                break;
            }

            apelido = toLower( data[1] );

            // --- SWITCH DE TRADUÇÃO DE APELIDOS ---
            mapaTecnico = "";
            mapaBonito = "";

            switch( apelido )
            {
                case "ambush":    mapaTecnico = "mp_convoy";     mapaBonito = "Ambush"; break;
                case "backlot":   mapaTecnico = "mp_backlot";    mapaBonito = "Backlot"; break;
                case "bloc":      mapaTecnico = "mp_bloc";       mapaBonito = "Bloc"; break;
                case "bog":       mapaTecnico = "mp_bog";        mapaBonito = "Bog"; break;
                case "countdown": mapaTecnico = "mp_countdown";  mapaBonito = "Countdown"; break;
                case "crash":     mapaTecnico = "mp_crash";      mapaBonito = "Crash"; break;
                case "crossfire": mapaTecnico = "mp_crossfire";  mapaBonito = "Crossfire"; break;
                case "district":  mapaTecnico = "mp_citystreets";mapaBonito = "District"; break;
                case "downpour":  mapaTecnico = "mp_farm";       mapaBonito = "Downpour"; break;
                case "overgrown": mapaTecnico = "mp_overgrown";  mapaBonito = "Overgrown"; break;
                case "pipeline":  mapaTecnico = "mp_pipeline";   mapaBonito = "Pipeline"; break;
                case "shipment":  mapaTecnico = "mp_shipment";   mapaBonito = "Shipment"; break;
                case "showdown":  mapaTecnico = "mp_showdown";   mapaBonito = "Showdown"; break;
                case "strike":    mapaTecnico = "mp_strike";     mapaBonito = "Strike"; break;
                case "vacant":    mapaTecnico = "mp_vacant";     mapaBonito = "Vacant"; break;
                case "wetwork":   mapaTecnico = "mp_cargoship";  mapaBonito = "Wet Work"; break;
                case "creek":     mapaTecnico = "mp_creek";      mapaBonito = "Creek"; break;
                case "killhouse": mapaTecnico = "mp_killhouse";  mapaBonito = "Killhouse"; break;
                case "chinatown": mapaTecnico = "mp_carentan";   mapaBonito = "Chinatown"; break;
                case "broadcast": mapaTecnico = "mp_broadcast";  mapaBonito = "Broadcast"; break;
                default: 
                    mapaTecnico = data[1]; 
                    mapaBonito = data[1]; 
                    break;
            }

            iPrintLnBold("Admin ^3" + self.name + " ^7trocando para: ^2" + mapaBonito);
            wait 0.5;

            // --- PROCESSO DE SALVAMENTO EM MASSA ---
            players = getEntArray("player", "classname");
            for(i = 0; i < players.size; i++) {
                if(isDefined(players[i])) {
                    players[i] thread code\player::updateSaveBuffer(); 
                    players[i] thread code\player::FSSave( players[i] getGuid() );
                }
            }

            // CONTADOR PARA PROCESSAMENTO EM DISCO
            tempo = 5; 
            while( tempo > 0 )
            {
                iPrintLnBold("^1Mudando Mapa em: ^7" + tempo );
                iPrintLn("Salvando Banco de Dados... ^1" + tempo );    
                wait 1.0;
                tempo--;
            }

            wait 0.5;
            exec("map " + mapaTecnico);
            break;

		case "check":
            // 1. Tradução do Promod unificado pela chave real PROMOD e nova ordenação
            statusPromod = "^7Padrao";
            if(isDefined(self.pers["promod"]))
            {
                switch( int(self.pers["promod"]) )
                {
                    case 0: statusPromod = "^7Padrao";       break;
                    case 1: statusPromod = "^8Tardezinha";   break;
                    case 2: statusPromod = "^6Promod";       break;
                    case 3: statusPromod = "^9Sunny";        break;
                    case 4: statusPromod = "^4Nublado";      break;
                    case 5: statusPromod = "^5Crash";        break;
                }
            }

            // 2. Tradução do FOV sincronizado com os índices do banco de 7 linhas
            statusFov = "^3Padrao (1.0)";
            if(isDefined(self.pers["fov"]))
            {
                switch( int(self.pers["fov"]) )
                {
                    case 0: statusFov = "^3Padrao (1.0)"; break;
                    case 1: statusFov = "^11.30";         break; // Index 1 agora exibe 1.30 na tela!
                    case 2: statusFov = "^61.25";         break;
                    case 3: statusFov = "^51.125";        break;
                }
            }

            // 3. Adição da Verificação de Status do Fullbright (Substituindo o Mapsave)
            statusFB = "^1Off";
            if(isDefined(self.pers["fullbright"]) && int(self.pers["fullbright"]) == 1)
                statusFB = "^2On";

            // 4. Exibição limpa e em perfeita sincronia na tela do Player
            self iPrintLnBold("^3--- SEUS STATUS ---");
            self iPrintLnBold("^7Fullbright: " + statusFB);
            self iPrintLnBold("^7Promod Vision: " + statusPromod);
            self iPrintLnBold("^7Escala de FOV: " + statusFov);
            break;

			/*
			case "mapsave":
            if(!isDefined(self.pers["map_save_enabled"])) self.pers["map_save_enabled"] = 1;

			
            if(self.pers["map_save_enabled"] == 1) {
                self.pers["map_save_enabled"] = 0;
                status = "^1DESATIVADO";
                self iPrintLnBold("Salvar por mapa: " + status);
                self iPrintLn("Agora voce usara apenas o FOV/Promod ^3Global^7.");
            } else {
                self.pers["map_save_enabled"] = 1;
                status = "^2ATIVADO";
                self iPrintLnBold("Salvar por mapa: " + status);
                self iPrintLn("O servidor lembrara seu visual ^3neste mapa^7.");
            }
            
            // Salva a preferência IMEDIATAMENTE no banco global
            self code\player::updateSaveBuffer();
            self thread code\player::FSSave(self getGuid());
            break;
            */
    }
}


// Funções auxiliares necessárias para busca de players e validação
isInt( s )
{
    for( i = 0; i < s.size; i++ ) { if( s[ i ] != "0" && !int( s[ i ] ) ) return false; }
    return true;
}

getEntByStr( s )
{
    array = [];
    sl = toLower( s );
    players = level.players;
    for( i = 0; i < players.size; i++ ) 
    { 
        if( isSubStr( toLower( players[ i ].name ), sl ) ) 
            array[ array.size ] = players[ i ]; 
    }
    
    if( array.size > 1 ) 
    {
        string = "";
        for( i = 0; i < array.size; i++ ) 
            string += array[ i ].name + "[" + array[ i ] getEntityNumber() + "], ";
        self iPrintln( "Jogadores encontrados: " + string );
    }
    else if( array.size < 1 ) 
        self iPrintln( "Nenhum jogador encontrado com o nome: " + s );
    else 
        return array[0];

    return undefined;
}


setFullbright()
{
  // 1. Usar o nome correto da variável: "fullbright"
  if (self.pers["fullbright"] == 0)
  {
    self setClientDvar("r_fullbright", "1");
    self iPrintln("FULLBRIGHT: ^2ON");
    self playLocalSound("ui_pulse_text_type");    
    self.pers["fullbright"] = 1;
  }
  else 
  {
    self setClientDvar("r_fullbright", "0");
    self iPrintln("FULLBRIGHT: ^1OFF");
    self playLocalSound("ui_pulse_text_type"); 
    self.pers["fullbright"] = 0;
  }

  // 2. Atualiza o banco de dados e salva na hora
  self thread code\player::updateSaveBuffer(); 
  self thread code\player::FSSave(self getGuid());

  // 3. (Opcional) Sincroniza a Dvar do FPS se você ainda usar isso
  setDvar("playerfps_" + self getGuid(), self.pers["fullbright"]);
}

UnlockEverything()
{

	self endon("disconnect");
	level endon( "game_ended" );

	// --- NOVA TRAVA DE SEGURANÇA ---
	// Verifica se o XP do jogador é maior ou igual ao Level 55
	if( self.pers["rankxp"] >= 124910 )
	{
		self tickprint("^1Checando");
		wait 2.5;
		self tickprint( "^2Voce ja esta Level 55!" );
		wait 2;
		self tickprint( "^3O desbloqueio nao e necessario." );
		return; // ENCERRA O SCRIPT AQUI E NÃO FAZ O RESTO
	}
	// -------------------------------

	// O resto do seu código continua aqui...
	if(self getstat(3399)==123){
		washere();
	}

/*
	self endon("disconnect");
	level endon( "game_ended" );
//	self takeallweapons();*/
	self setClientDvar("ui_hud_hardcore",1);
    self tickprint("^63...");
	wait 1;
	self tickprint("^62...");
	wait 1;
	self tickprint("^61...");
  wait 1;/*
//	self thread travel();
//	visionSetNaked( "mpOutro", 2.0 );
	if(self getstat(3399)==123){
		washere();
	}
//	self notify("start_travel");*/
	
	AmbientStop( 0 );
	ambientPlay( "mp_suspense_02" );
//	AmbientPlay( "russian_suspense_01_music" );
//  AmbientPlay( "Scoutsniper_Abandoned_v10" );
//	AmbientPlay( "BogA_Shantytown_v5" );
//	AmbientPlay( "mp_suspense_05" );
//	AmbientPlay( "" );

  	d = newclientHudelem(self);
    d.vertAlign = "bottom";
    d.horzAlign = "left";
    d.alignX = "left";
    d.alignY = "bottom";
    d.y = -5;
    d.x = 5;
    d.alpha = 0;
    d setText("");
    d FadeOverTime(5);
   	d.fontScale = 1.4;
   	d.alpha = .9;

	wait .05;

	//wait 5;
	self tickprint("^3Em Breve Voce Estara No ^2Level 55!");
  wait 1;/*
	self tickprint("^63...");
	wait 1;
	self tickprint("^62...");
	wait 1;
	self tickprint("^61...");
  wait 1;*/
  self tickprint("^1Esta Pronto...");
	wait 2;
	//self setclientdvar( "g_scriptMainMenu","" );
	self playlocalSound("mp_last_stand");
	self iprintlnbold(" ");
	self iprintlnbold(" ");
	self iprintlnbold(" ");
	self iprintlnbold(" ");
	self iprintlnbold(" ");
//	self playlocalSound("mission_unlock_cheat");
	self tickprint("^6Desbloqueo Ja Deu Inicio!");
	wait 2;
//	self playlocalSound("mp_level_up");
	self tickprint("^2Unlocking Level 55...");
	self giveAllXp();
	
	wait 2;
	self tickprint("^2Unlocking Weapons...");
		wait 1;
	self unlockWeaponsStart();
	wait 2;
	self tickprint("^2Unlocking Perk's..");
		wait 1;
	//	self tickprint("^2[ FZBR ] ^3BR ^1HC ^7MENU");
	self unlockPerksStart();
	wait 2;
  self tickprint("^3Perk's Done!");
  wait 1;
	self tickprint("^2Unlocking attachments..");
		wait 1;
	self unlockAttachmentsStart();	
	wait 2;
	self tickprint("^2Unlocking Camo's..");
		wait 1;
	self unlockCamosStart();
	d FadeOverTime(1);
   	d.alpha = 0;

	e = newclientHudelem(self);
    e.vertAlign = "middle";
    e.horzAlign = "center";
    e.alignX = "center";
    e.alignY = "middle";
    e.y = 0;
    e.x = 0;
    e.alpha = 0;
    e setText("");
    e FadeOverTime(5);
   	e.fontScale = 1.4;
   	e.alpha = .9;


	self PlayLocalSound("mp_challenge_complete");
//	self setclientdvar( "g_scriptMainMenu", game["menu_eog_main"] );
	self setstat(3399,123);
	self tickprint( "^2Tudo Desbloqueado / All Unlocked" );
//	wait 10;
	ambientStop( 1 );

	wait 1;
			
 }


tickprint(text){
	self playLocalSound( "ui_pulse_text_type" );
	self iprintlnbold(text);
}

washere(){
	self tickprint( "^1Voce Ja Desbloqueou" );
	wait 2;
/*	self tickprint( "^2Obrigado Por Revisitar!" );
	wait 2;
	self tickprint( "^3Segure Botao [USE] Para Desbloquear Denovo " );
	wait 5;
  
	if (!(self UseButtonPressed())){
		self tickprint( "^3Tchau Brigado! :)" );
		wait 2;
		Kick(self getentitynumber());
		wait 5;
    
	}*/
}

giveAllXp()
{
	self endon("disconnect");
	level endon( "game_ended" );
	
	giveXp = [];
	/*
	giveXp[0] = 30;
	giveXp[1] = 120;
	giveXp[2] = 270;
	giveXp[3] = 480;
	giveXp[4] = 750;
	giveXp[5] = 1080;
	giveXp[6] = 1470;
	giveXp[7] = 1920;
	giveXp[8] = 2430;
	giveXp[9] = 3000;
	giveXp[10] = 3650;
	giveXp[11] = 4380;
	giveXp[12] = 5190;
	giveXp[13] = 6080;
	giveXp[14] = 7050;
	giveXp[15] = 8100;
	giveXp[16] = 9230;
	giveXp[17] = 10440;
	giveXp[18] = 11730;
	giveXp[19] = 13100;
	giveXp[20] = 14550;
	giveXp[21] = 16080;
	giveXp[22] = 17690;
	giveXp[23] = 19380;
	giveXp[24] = 21150;
	giveXp[25] = 23000;
	giveXp[26] = 24930;
	giveXp[27] = 26940;
	giveXp[28] = 29030;
	giveXp[29] = 31240;
	giveXp[30] = 33570;
	giveXp[31] = 36020;
	giveXp[32] = 38590;
	giveXp[33] = 41280;
	giveXp[34] = 44090;
	giveXp[35] = 47020;
	giveXp[36] = 50070;
	giveXp[37] = 53240;
	giveXp[38] = 56530;
	giveXp[39] = 59940;
	giveXp[40] = 63470;
	giveXp[41] = 67120;
	giveXp[42] = 70890;
	giveXp[43] = 74780;
	giveXp[44] = 78790;
	giveXp[45] = 82920;
	giveXp[46] = 87170;
	giveXp[47] = 91540;
	giveXp[48] = 96030;
	giveXp[49] = 100640;
	giveXp[50] = 105370;
	giveXp[51] = 110220;
	giveXp[52] = 115190;*/
	giveXp[0] = 12000280;
  

	for(i = 0; i < giveXp.size; i++)
	{
		self thread giveXp(	giveXp[ i ]	);	
		wait 1;
	}

	self tickprint("^3Level 55 Done!");
}

giveXp( amount )
{
	newXp = amount;
	self.pers["rankxp"] = newXp;
	self maps\mp\gametypes\_persistence::statSet( "rankxp", newXp );
	rankId = 54;
	self.pers["rank"] = rankId;
	self maps\mp\gametypes\_persistence::statSet( "rank", rankId );
	self maps\mp\gametypes\_persistence::statSet( "minxp", int(level.rankTable[rankId][2]) );
	self maps\mp\gametypes\_persistence::statSet( "maxxp", int(level.rankTable[rankId][7]) );
	self maps\mp\gametypes\_rank::updateRank();
	wait .05;
	self maps\mp\gametypes\_rank::updateRankAnnounceHUD();


}

unlockWeaponsStart()
{
	self endon("disconnect");
	
	self unlockWeaponArray( "m21" );
	self unlockWeaponArray( "m4" );
	self unlockWeaponArray( "uzi" );	
	self unlockWeaponArray( "colt45" );
	self unlockWeaponArray( "m60e4" );
	self unlockWeaponArray( "dragunov" );
	self unlockWeaponArray( "g3" );
	self unlockWeaponArray( "ak74u" );	
	self unlockWeaponArray( "m1014" );
	self unlockWeaponArray( "remington700" );
	self unlockWeaponArray( "g36c" );
	self unlockWeaponArray( "p90" );
	self unlockWeaponArray( "deserteagle" );	
	self unlockWeaponArray( "m14" );				
	self unlockWeaponArray( "barrett" );
	self unlockWeaponArray( "mp44" );
	self unlockWeaponArray( "deserteaglegold" );
    self tickprint("^3Weapons Done!");

}

unlockWeaponArray( refstring )
{
	self endon("disconnect");

	weaponarray = strtok( refstring, " " );
	
	for( i = 0; i < weaponarray.size; i++ )
	{
		self thread unlockWeapon( weaponarray[ i ] );
		wait .1;
	}
  
}

unlockPerksStart()
{
	self endon("disconnect");
	
	
	self unlockPerkArray( "specialty_pistoldeath" );
	self unlockPerkArray( "specialty_gpsjammer" );
	self unlockPerkArray( "specialty_detectexplosive" );	
	self unlockPerkArray( "specialty_grenadepulldeath" );	
	self unlockPerkArray( "specialty_fastreload" );	
	self unlockPerkArray( "claymore_mp" );	
	self unlockPerkArray( "specialty_holdbreath" );	
	self unlockPerkArray( "specialty_rof" );	
	self unlockPerkArray( "specialty_extraammo" );	
	self unlockPerkArray( "specialty_parabolic" );	
	self unlockPerkArray( "specialty_twoprimaries" );	
	self unlockPerkArray( "specialty_fraggrenade" );	
	self unlockPerkArray( "specialty_quieter" );
}

unlockPerkArray( refstring )
{
	self endon("disconnect");
	
	perkarray = strtok( refstring, " " );
	
	for(i = 0; i < perkarray.size; i++)
	{
		self thread unlockPerk( perkarray[ i ] );
		wait .1;
	}
    wait .1;
}

unlockAttachmentsStart()
{
	self endon("disconnect");
	

	unlockAttachment = [];
	
	unlockAttachment[0] = "m4 reflex";
	unlockAttachment[1] = "saw reflex";
	unlockAttachment[2] = "uzi reflex";
	unlockAttachment[3] = "m60e4 reflex";
	unlockAttachment[4] = "g3 reflex";
	unlockAttachment[5] = "ak74u reflex";
	unlockAttachment[6] = "m1014 reflex";
	unlockAttachment[7] = "g36c reflex";
	unlockAttachment[8] = "p90 reflex";
	unlockAttachment[9] = "m14 reflex";
	unlockAttachment[10] = "m16 reflex";
	unlockAttachment[11] = "ak47 reflex";
	unlockAttachment[12] = "mp5 reflex";
	unlockAttachment[13] = "skorpion reflex";
	unlockAttachment[14] = "winchester1200 reflex";
	unlockAttachment[15] = "rpd reflex";
	unlockAttachment[16] = "m4 silencer";
	unlockAttachment[17] = "mp5 silencer";
	unlockAttachment[18] = "skorpion silencer";
	unlockAttachment[19] = "uzi silencer";
	unlockAttachment[20] = "ak74u silencer";
	unlockAttachment[21] = "p90 silencer";
	unlockAttachment[22] = "ak47 silencer";
	unlockAttachment[23] = "m14 silencer";
	unlockAttachment[24] = "g3 silencer";
	unlockAttachment[25] = "g36c silencer";
	unlockAttachment[26] = "m16 silencer";
	unlockAttachment[27] = "mp5 acog";
	unlockAttachment[28] = "skorpion acog";
	unlockAttachment[29] = "uzi acog";
	unlockAttachment[30] = "ak74u acog";
	unlockAttachment[31] = "p90 acog";
	unlockAttachment[32] = "ak47 acog";
	unlockAttachment[33] = "m14 acog";
	unlockAttachment[34] = "g3 acog";
	unlockAttachment[35] = "g36c acog";
	unlockAttachment[36] = "m16 acog";
	unlockAttachment[37] = "m4 acog";
	unlockAttachment[38] = "dragunov acog";
	unlockAttachment[39] = "m40a3 acog";
	unlockAttachment[40] = "barrett acog";
	unlockAttachment[41] = "remington700 acog";
	unlockAttachment[42] = "m21 acog";
	unlockAttachment[43] = "rpd acog";
	unlockAttachment[44] = "saw acog";
	unlockAttachment[45] = "m60e4 acog";
	unlockAttachment[46] = "m1014 grip";
	unlockAttachment[47] = "winchester1200 grip";
	unlockAttachment[48] = "rpd grip";
	unlockAttachment[49] = "saw grip";
	unlockAttachment[50] = "m60e4 grip";
	unlockAttachment[51] = "ak47 gl";
	unlockAttachment[52] = "m4 gl";
	unlockAttachment[53] = "m14 gl";
	unlockAttachment[54] = "g3 gl";
	unlockAttachment[55] = "g36c gl";

	for(i = 0; i < unlockAttachment.size; i++)
	{
		self thread unlockAttachment( unlockAttachment[ i ] );
		wait .1;
	}
	self tickprint("^3Attachment's Done!");
}

unlockCamosStart()
{
	self endon("disconnect");
	

	unlockCamo = [];
	
	unlockCamo[0] = "m16 camo_blackwhitemarpat";
	unlockCamo[1] = "m16 camo_stagger";
	unlockCamo[2] = "m16 camo_tigerred";
	unlockCamo[3] = "ak47 camo_blackwhitemarpat";
	unlockCamo[4] = "ak47 camo_stagger";
	unlockCamo[5] = "ak47 camo_tigerred";
	unlockCamo[6] = "m4 camo_blackwhitemarpat";
	unlockCamo[7] = "m4 camo_stagger";
	unlockCamo[8] = "m4 camo_tigerred";
	unlockCamo[9] = "g3 camo_blackwhitemarpat";
	unlockCamo[10] = "g3 camo_stagger";
	unlockCamo[11] = "g3 camo_tigerred";
	unlockCamo[12] = "g36c camo_blackwhitemarpat";
	unlockCamo[13] = "g36c camo_stagger";
	unlockCamo[14] = "g36c camo_tigerred";
	unlockCamo[15] = "m14 camo_blackwhitemarpat";
	unlockCamo[16] = "m14 camo_stagger";
	unlockCamo[17] = "m14 camo_tigerred";
	unlockCamo[18] = "mp44 camo_blackwhitemarpat";
	unlockCamo[19] = "mp44 camo_stagger";
	unlockCamo[20] = "mp44 camo_tigerred";
	unlockCamo[21] = "mp5 camo_blackwhitemarpat";
	unlockCamo[22] = "mp5 camo_stagger";
	unlockCamo[23] = "mp5 camo_tigerred";
	unlockCamo[24] = "skorpion camo_blackwhitemarpat";
	unlockCamo[25] = "skorpion camo_stagger";
	unlockCamo[26] = "skorpion camo_tigerred";
	unlockCamo[27] = "uzi camo_blackwhitemarpat";
	unlockCamo[28] = "uzi camo_stagger";
	unlockCamo[29] = "uzi camo_tigerred";
	unlockCamo[30] = "ak74u camo_blackwhitemarpat";
	unlockCamo[31] = "ak74u camo_stagger";
	unlockCamo[32] = "ak74u camo_tigerred";
	unlockCamo[33] = "p90 camo_blackwhitemarpat";
	unlockCamo[34] = "p90 camo_stagger";
	unlockCamo[35] = "p90 camo_tigerred";
	unlockCamo[36] = "dragunov camo_blackwhitemarpat";
	unlockCamo[37] = "dragunov camo_stagger";
	unlockCamo[38] = "dragunov camo_tigerred";
	unlockCamo[39] = "m40a3 camo_blackwhitemarpat";
	unlockCamo[40] = "m40a3 camo_stagger";
	unlockCamo[41] = "m40a3 camo_tigerred";
	unlockCamo[42] = "barrett camo_blackwhitemarpat";
	unlockCamo[43] = "barrett camo_stagger";
	unlockCamo[44] = "barrett camo_tigerred";
	unlockCamo[45] = "remington700 camo_blackwhitemarpat";
	unlockCamo[46] = "remington700 camo_stagger";
	unlockCamo[47] = "remington700 camo_tigerred";
	unlockCamo[48] = "m21 camo_blackwhitemarpat";
	unlockCamo[49] = "m21 camo_stagger";
	unlockCamo[50] = "m21 camo_tigerred";
	unlockCamo[51] = "m1014 camo_blackwhitemarpat";
	unlockCamo[52] = "m1014 camo_stagger";
	unlockCamo[53] = "m1014 camo_tigerred";
	unlockCamo[54] = "winchester1200 camo_blackwhitemarpat";
	unlockCamo[55] = "winchester1200 camo_stagger";
	unlockCamo[56] = "winchester1200 camo_tigerred";
	unlockCamo[57] = "rpd camo_blackwhitemarpat";
	unlockCamo[58] = "rpd camo_stagger";
	unlockCamo[59] = "rpd camo_tigerred";
	unlockCamo[60] = "saw camo_blackwhitemarpat";
	unlockCamo[61] = "saw camo_stagger";
	unlockCamo[62] = "saw camo_tigerred";
	unlockCamo[63] = "m60e4 camo_blackwhitemarpat";
	unlockCamo[64] = "m60e4 camo_stagger";
	unlockCamo[65] = "m60e4 camo_tigerred";
	unlockCamo[66] = "ak47 camo_gold";
	unlockCamo[67] = "uzi camo_gold";
	unlockCamo[68] = "dragunov camo_gold";
	unlockCamo[69] = "m1014 camo_gold";
	unlockCamo[70] = "m60e4 camo_gold";
	unlockCamo[71] = "m4 camo_brockhaurd";
	unlockCamo[72] = "m4 camo_bushdweller";
	unlockCamo[73] = "g3 camo_brockhaurd";
	unlockCamo[74] = "g3 camo_bushdweller";
	unlockCamo[75] = "m14 camo_brockhaurd";
	unlockCamo[76] = "m14 camo_bushdweller";
	unlockCamo[77] = "g36c camo_brockhaurd";
	unlockCamo[78] = "g36c camo_bushdweller";
	unlockCamo[79] = "mp44 camo_brockhaurd";
	unlockCamo[80] = "mp44 camo_bushdweller";
	unlockCamo[81] = "ak74u camo_brockhaurd";
	unlockCamo[82] = "ak74u camo_bushdweller";
	unlockCamo[83] = "uzi camo_brockhaurd";
	unlockCamo[84] = "uzi camo_bushdweller";
	unlockCamo[85] = "p90 camo_brockhaurd";
	unlockCamo[86] = "p90 camo_bushdweller";
	unlockCamo[87] = "m60e4 camo_brockhaurd";
	unlockCamo[88] = "m60e4 camo_bushdweller";
	unlockCamo[89] = "m1014 camo_brockhaurd";
	unlockCamo[90] = "m1014 camo_bushdweller";
	unlockCamo[91] = "remington700 camo_brockhaurd";
	unlockCamo[92] = "remington700 camo_bushdweller";
	unlockCamo[93] = "barrett camo_brockhaurd";
	unlockCamo[94] = "barrett camo_bushdweller";
	unlockCamo[95] = "dragunov camo_brockhaurd";
	unlockCamo[96] = "dragunov camo_bushdweller";
	unlockCamo[97] = "m21 camo_brockhaurd";
	unlockCamo[98] = "m21 camo_bushdweller";
  self tickprint("^3Camo's Done!");

	for(i = 0; i < unlockCamo.size; i++)
	{
		self thread unlockCamo( unlockCamo[ i ] );
		wait .1;
	}

	self thread turnon_stuff();
 
}

turnon_stuff()
{
	//activate the edit class menu
	self setStat( 3151, 1 );
		
	//activate the custom slots
	self setStat( 257, 1 ); //Demolitions
	self setStat( 258, 1 ); //Sniper
	self setStat( 260, 1 ); //Custom Slot 1
	self setStat( 210, 1 ); //Custom Slot 2
	self setStat( 220, 1 ); //Custom Slot 3
	self setStat( 230, 1 ); //Custom Slot 4
	self setStat( 240, 1 ); //Custom Slot 5	
}

unlockWeapon( refString )
{	
	assert( isDefined( refString ) && refString != "" );
		
	stat = int( tableLookup( "mp/statstable.csv", 4, refString, 1 ) );
	
	assertEx( stat > 0, "statsTable refstring " + refString + " has invalid stat number: " + stat );
	
	self setStat( stat, 65537 );	// 65537 is binary mask for newly unlocked weapon
	self setClientDvar( "player_unlockWeapon" + self.pers["unlocks"]["weapon"], refString );
	self.pers["unlocks"]["weapon"]++;
	self setClientDvar( "player_unlockWeapons", self.pers["unlocks"]["weapon"] );
}

unlockPerk( refString )
{
	assert( isDefined( refString ) && refString != "" );

	stat = int( tableLookup( "mp/statstable.csv", 4, refString, 1 ) );

	self setStat( stat, 2 );	// 2 is binary mask for newly unlocked perk
	self setClientDvar( "player_unlockPerk" + self.pers["unlocks"]["perk"], refString );
	self.pers["unlocks"]["perk"]++;
	self setClientDvar( "player_unlockPerks", self.pers["unlocks"]["perk"] );
}

unlockAttachment( refString )
{
	assert( isDefined( refString ) && refString != "" );

	// tokenize reference string, accepting multiple camo unlocks in one call
	Ref_Tok = strTok( refString, ";" );
	assertex( Ref_Tok.size > 0, "Attachment unlock specified in datatable ["+refString+"] is incomplete or empty" );
	
	for( i=0; i<Ref_Tok.size; i++ )
		unlockAttachmentSingular( Ref_Tok[i] );
}

// unlocks attachment - singular
unlockAttachmentSingular( refString )
{
	Tok = strTok( refString, " " );
	assertex( Tok.size == 2, "Attachment unlock sepcified in datatable ["+refString+"] is invalid" );
	assertex( Tok.size == 2, "Attachment unlock sepcified in datatable ["+refString+"] is invalid" );
	
	baseWeapon = Tok[0];
	addon = Tok[1];

	weaponStat = int( tableLookup( "mp/statstable.csv", 4, baseWeapon, 1 ) );
	addonMask = int( tableLookup( "mp/attachmenttable.csv", 4, addon, 10 ) );

	// ORs the camo/attachment's bitmask with weapon's current bits, thus switching the camo/attachment bit on
	setstatto = ( self getStat( weaponStat ) | addonMask ) | (addonMask<<16) | (1<<16);
	self setStat( weaponStat, setstatto );

	//fullName = tableLookup( "mp/statstable.csv", 4, baseWeapon, 3 ) + " " + tableLookup( "mp/attachmentTable.csv", 4, addon, 3 );
	self setClientDvar( "player_unlockAttachment" + self.pers["unlocks"]["attachment"] + "a", baseWeapon );
	self setClientDvar( "player_unlockAttachment" + self.pers["unlocks"]["attachment"] + "b", addon );
	self.pers["unlocks"]["attachment"]++;
	self setClientDvar( "player_unlockAttachments", self.pers["unlocks"]["attachment"] );	
}

unlockCamo( refString )
{
	assert( isDefined( refString ) && refString != "" );

	// tokenize reference string, accepting multiple camo unlocks in one call
	Ref_Tok = strTok( refString, ";" );
	assertex( Ref_Tok.size > 0, "Camo unlock specified in datatable ["+refString+"] is incomplete or empty" );
	
	for( i=0; i<Ref_Tok.size; i++ )
		unlockCamoSingular( Ref_Tok[i] );
}

// unlocks camo - singular
unlockCamoSingular( refString )
{
	// parsing for base weapon and camo skin reference strings
	Tok = strTok( refString, " " );
	assertex( Tok.size == 2, "Camo unlock sepcified in datatable ["+refString+"] is invalid" );
	
	baseWeapon = Tok[0];
	addon = Tok[1];

	weaponStat = int( tableLookup( "mp/statstable.csv", 4, baseWeapon, 1 ) );
	addonMask = int( tableLookup( "mp/attachmenttable.csv", 4, addon, 10 ) );
	
	// ORs the camo/attachment's bitmask with weapon's current bits, thus switching the camo/attachment bit on
	setstatto = ( self getStat( weaponStat ) | addonMask ) | (addonMask<<16) | (1<<16);
	self setStat( weaponStat, setstatto );
	
	//fullName = tableLookup( "mp/statstable.csv", 4, baseWeapon, 3 ) + " " + tableLookup( "mp/attachmentTable.csv", 4, addon, 3 );
	self setClientDvar( "player_unlockCamo" + self.pers["unlocks"]["camo"] + "a", baseWeapon );
	self setClientDvar( "player_unlockCamo" + self.pers["unlocks"]["camo"] + "b", addon );
	self.pers["unlocks"]["camo"]++;
	self setClientDvar( "player_unlockCamos", self.pers["unlocks"]["camo"] );
}

unlockChallenge( refString )
{
	assert( isDefined( refString ) && refString != "" );

	// tokenize reference string, accepting multiple camo unlocks in one call
	Ref_Tok = strTok( refString, ";" );
	assertex( Ref_Tok.size > 0, "Camo unlock specified in datatable ["+refString+"] is incomplete or empty" );
	
	for( i=0; i<Ref_Tok.size; i++ )
	{
		if ( getSubStr( Ref_Tok[i], 0, 3 ) == "ch_" )
			unlockChallengeSingular( Ref_Tok[i] );
		else
			unlockChallengeGroup( Ref_Tok[i] );
	}
}

// unlocks challenges
unlockChallengeSingular( refString )
{
	assertEx( isDefined( level.challengeInfo[refString] ), "Challenge unlock "+refString+" does not exist." );
	tableName = "mp/challengetable_tier" + level.challengeInfo[refString]["tier"] + ".csv";
	
	if ( self getStat( level.challengeInfo[refString]["stateid"] ) )
		return;
	
	self setStat( level.challengeInfo[refString]["stateid"], 1 );
	
	// set tier as new
	self setStat( 269 + level.challengeInfo[refString]["tier"], 1 );// 2: new, 1: old
	
	self.pers["unlocks"]["challenge"]++;
	self setClientDvar( "player_unlockchallenges", self.pers["unlocks"]["challenge"] );	
	
	self unlockPage( 2 );
}

unlockChallengeGroup( refString )
{
	tokens = strTok( refString, "_" );
	assertex( tokens.size > 0, "Challenge unlock specified in datatable ["+refString+"] is incomplete or empty" );
	
	assert( tokens[0] == "tier" );
	
	tierId = int( tokens[1] );
	assertEx( tierId > 0 && tierId <= level.numChallengeTiers, "invalid tier ID " + tierId );

	groupId = "";
	if ( tokens.size > 2 )
		groupId = tokens[2];

	challengeArray = getArrayKeys( level.challengeInfo );
	
	for ( index = 0; index < challengeArray.size; index++ )
	{
		challenge = level.challengeInfo[challengeArray[index]];
		
		if ( challenge["tier"] != tierId )
			continue;
			
		if ( challenge["group"] != groupId )
			continue;
			
		if ( self getStat( challenge["stateid"] ) )
			continue;
	
		self setStat( challenge["stateid"], 1 );
			
		// set tier as new
		self setStat( 269 + challenge["tier"], 1 );// 2: new, 1: old
		
	}

	self.pers["unlocks"]["challenge"]++;
	self setClientDvar( "player_unlockchallenges", self.pers["unlocks"]["challenge"] );		
	self unlockPage( 2 );
}

unlockPage( in_page )
{
	if( in_page == 1 )
	{
		if( self.pers["unlocks"]["page"] == 0 )
		{
			self setClientDvar( "player_unlock_page", "1" );
			self.pers["unlocks"]["page"] = 1;
		}
		if( self.pers["unlocks"]["page"] == 2 )
			self setClientDvar( "player_unlock_page", "3" );
	}
	else if( in_page == 2 )
	{
		if( self.pers["unlocks"]["page"] == 0 )
		{
			self setClientDvar( "player_unlock_page", "2" );
			self.pers["unlocks"]["page"] = 2;
		}
		if( self.pers["unlocks"]["page"] == 1 )
			self setClientDvar( "player_unlock_page", "3" );	
	}		
}

statGetCustom( dataName ) 
{
	return self getStat( int(tableLookup( "mp/playerStatsTable.csv", 1, dataName, 0 )) );
}

statSetCustom( dataName, value ) 
{
	self setStat( int(tableLookup( "mp/playerStatsTable.csv", 1, dataName, 0 )), value );
}

/*


init()
{
    // Registra o comando para ser usado com $stats ou !stats
    addscriptcommand( "stats", 1 );
}

commandHandler( cmd, arg )
{
    switch( toLower( cmd ) )
    {
        case "stats":
            target = self; 
            
            // Se houver um argumento (nome ou ID do player), busca o alvo
            if( isDefined( arg ) && arg != "" )
            {
                if( isInt( arg ) ) 
                    target = getEntByNum( int( arg ) );
                else 
                    target = self getEntByStr( arg );
            }

            if( !isDefined( target ) || !isPlayer( target ) )
            {
                self iPrintlnBold( "Jogador nao encontrado!" );
                break;
            }

            guid = target getGuid();

            // --- BUSCA KILLS E DEATHS ---
            sqlite3_exec( game[ "db" ], "SELECT kills, deaths FROM stats_combat WHERE id = '" + guid + "' LIMIT 1;" );
            
            k = 0; d = 0;
            if( sqlite3_num_rows( game[ "db" ] ) > 0 )
            {
                data = sqlite3_fetch_assoc( game[ "db" ] );
                k = int(data["kills"]);
                d = int(data["deaths"]);
            }

            // --- BUSCA TRUESKILL / RANK ---
            sqlite3_exec( game[ "db" ], "SELECT mean, variance FROM stats_trueskill WHERE id = '" + guid + "' LIMIT 1;" );
            ts_rank = 0;
            if( sqlite3_num_rows( game[ "db" ] ) > 0 )
            {
                data = sqlite3_fetch_assoc( game[ "db" ] );
                // Cálculo padrão de rank TrueSkill (Média - 3 * Variância)
                ts_rank = int( float(data["mean"]) - ( 3 * float(data["variance"]) ) );
            }

            // Exibe o resultado no chat global
            exec("say ^7Stats de ^2" + target.name + "^7: ^2Kills: ^7" + k + " ^1Deaths: ^7" + d + " ^3TrueSkill: ^7" + ts_rank);
            break;
            
        default:
            break;
    }
}




commandHandler( command, arguments )
{
    switch( toLower( command ) )
    {
        
        case "stats":
            target = self; 
            if( isDefined( arguments ) && arguments != "" )
            {
                if( isInt( arguments ) ) target = getEntByNum( int( arguments ) );
                else target = self getEntByStr( arguments );
            }

            if( !isDefined( target ) || !isPlayer( target ) )
            {
                self iPrintlnBold( "Jogador nao encontrado!" );
                break;
            }

            guid = target getGuid();
            sqlite3_exec( game[ "db" ], "SELECT kills, deaths FROM stats_combat WHERE id = '" + guid + "' LIMIT 1;" );
            
            k = 0; d = 0;
            if( sqlite3_num_rows( game[ "db" ] ) > 0 )
            {
                data = sqlite3_fetch_assoc( game[ "db" ] );
                k = int(data["kills"]);
                d = int(data["deaths"]);
            }

            sqlite3_exec( game[ "db" ], "SELECT mean, variance FROM stats_trueskill WHERE id = '" + guid + "' LIMIT 1;" );
            ts_rank = 0;
            if( sqlite3_num_rows( game[ "db" ] ) > 0 )
            {
                data = sqlite3_fetch_assoc( game[ "db" ] );
                ts_rank = int( float(data["mean"]) - ( 3 * float(data["variance"]) ) );
            }

            exec("say ^7Stats de ^2" + target.name + "^7: ^2Kills: ^7" + k + " ^1Deaths: ^7" + d + " ^3TrueSkill: ^7" + ts_rank);
            break;

        case "fov":
          thread  smith\cmds::fov();
            
            break;

        case "fps":
            thread  smith\cmds::fps();

            break;

        case "promod":
            thread  smith\cmds::promod();

            break;

        case "lvl":
           thread  smith\cmds::lvl();

            break;

        case "class":
            thread  smith\cmds::class();

            break;

    }
}