Tutorial -  PAWN Iniciante/Intermediário (Estude Isso)
Autor: jjffg4fdfdfdsg6s5dg | Publicado: 2010-07-17T19:18:00-07:00

Índice:<!--sizec--><!--/sizec-->IniciandoStrings e VariáveisStrtokMulti-Array StringsStock FunctionsUser Databases (dudb)Y_ObjectsRaces<!--sizeo:5--><!--/sizeo-->1. Iniciando<!--sizec--><!--/sizec-->Neste tutorial vou explicar algumas funções do pawno que algumas pessoas vem me perguntando bastante. Lembrando que este é umtutorial de nível Iniciante/Intermediário. Para aprender o seguinte, você deve saber no mínimo as funções básicas do pawno, e a função de cada callback (você deve no mínimo dos mínimos saber o que é um callback). Logo estarei postando outros tutoriais, de nível Intermediário/Avançado e de Nível Iniciante.<!--sizeo:5--><!--/sizeo-->2. Strings e Variáveis<!--sizec--><!--/sizec-->Variáveis são as funções que vão definir o que vai acontecer se esta variável tiver um certo valor. Ex: Se o jogador fulano tiver a profissão número 12, ele vai explodir, etc... E strings são funções que geralmente são criadas para mandar mensagens que tenham variáveis, Ex.: Você está jogando e vai no banco para ver seu saldo. Então aparece a mensagem: "Seu saldo bancário: 504031904913". Sendo que 504031904913 é uma variável que define a quantia que você tem no banco.Então, vamos começar criando uma variável. Ponha isto no topo do seu gamemode:<!--c1-->CÓDIGO<!--ec1-->new kills[MAX_PLAYERS];new deaths[MAX_PLAYERS];<!--c2--><!--ec2-->Estas variáveis vao ser usadas para mostrar o numero de kills e mortes de um player.Agora vamos botar a seguinte função no OnPlayerDeath:<!--c1-->CÓDIGO<!--ec1-->kills[killerid] ++;deaths[playerid] ++;<!--c2--><!--ec2-->E agora vamos fazer um comando para mostrar os status do player. Ponha isso no OnPlayerCommandText:<!--c1-->CÓDIGO<!--ec1-->if(strcmp(cmdtext, "/stats", true) == 0){new stringkills[256];new stringmortes[256];format(stringkills,sizeof(stringkills), "Kills: %d",kills[playerid]);format(stringmortes,sizeof(stringmortes), "Mortes: %d",mortes[playerid]);SendClientMessage(playerid, 0xFFFF00AA, "Seus stats:");SendClientMessage(playerid, 0xFFFF00AA, stringkills);SendClientMessage(playerid, 0xFFFF00AA, stringmortes);return 1;}<!--c2--><!--ec2--><!--sizeo:3--><!--/sizeo-->O que acabamos de fazer:<!--sizec--><!--/sizec-->Quando botamos new kills[MAX_PLAYERS]; nós criamos uma variável, que seria executada particularmente para cada player. Após isso, colocamos no OnPlayerDeath kills[killerid] ++; &amp; deaths[playerid] ++;, isso fará com que o assassino ganhe + 1 na variável kills, e o morto + 1 na variavel deaths. Após isso, fizemos um comando que mostraria o numero de kills e mortes do player quando ele digitasse /stats, usando strings. Como você pode perceber, usamos a função format(stringkills,sizeof(stringkills), "Kills: %d",kills[playerid]);, que significa que formatou a string para aparecer o número de kills, e usamos SendClientMessage(playerid, 0xFFFF00AA, stringkills); para mandar uma mensagem com a string para o player.<!--coloro:#FF0000--><!--/coloro-->OBS: Nunca se pode usar uma variável em SendClientMessage sem ter usado uma string!<!--colorc--><!--/colorc--><!--coloro:#FF0000--><!--/coloro-->OBS²: Você não precisa usar strings em todas as mensagens, só precisa usar nas que tem variáveis!<!--colorc--><!--/colorc--><!--sizeo:5--><!--/sizeo-->3. Strtok<!--sizec--><!--/sizec-->A função strtok é usada para usar comandos com variáveis, por exemplo /vida id vida. Se nós não usarmos strtok para fazer comandos como /vida id vida, teríamos que fazer um comando para cada id, por exemplo /vida 1 100 depois /vida 2 100 depois /vida 3 100, e isso gastaria muito do tempo e totalmente inútil. Então vamos começar fazendo um comando com strtok.Adicione esta função (strtok) no final do seu Gamemode/Filterscript:<!--c1-->CÓDIGO<!--ec1-->strtok(const string[], &amp;index){new length = strlen(string);while ((index < offset =" index;"> ' ') &amp;&amp; ((index - offset) < (sizeof(result) - 1))) { result[index - offset] = string[index]; index++; } result[index - offset] = EOS; return result; }<!--c2--><!--ec2-->Agora vamos colocar isso em cima de tudo no OnPlayerCommandText:<!--c1-->CÓDIGO<!--ec1-->new cmd[256];new idx;cmd = strtok(cmdtext, idx);<!--c2--><!--ec2-->E agora vamos fazer o comando, que neste caso será o comando /vida id vida.<!--c1-->CÓDIGO<!--ec1-->if(strcmp(cmd,"/vida", true)==0 &amp;&amp; IsPlayerAdmin(playerid)){new string [256];new tmp[256];new plid, vida;new nomeentregador[MAX_PLAYER_NAME];new nomerecebedor[MAX_PLAYER_NAME];tmp = strtok(cmdtext, idx);if(!strlen(tmp)){SendClientMessage(playerid, cinza, "[ERRO DE SINTAXE] /vida [id] [vida]");return 1;}plid = strval(tmp);tmp = strtok(cmdtext, idx);if(!strlen(tmp)) {SendClientMessage(playerid, cinza, "[ERRO DE SINTAXE] /vida [id] [vida]");return 1;}vida = strval(tmp);if(IsPlayerConnected(plid)){GetPlayerName(plid, nomerecebedor, sizeof(nomerecebedor));GetPlayerName(playerid, nomeentregador, sizeof(nomeentregador));format(string, sizeof(string), "O Admnistrador %s setou a vida de %s para %d.", nomeentregador,nomerecebedor,vida);SendClientMessageToAll(branco, string);SetPlayerHealth(plid,vida);return 1;}else{SendClientMessage(playerid, cinza, "Jogador não conectado.");return 1;}}<!--c2--><!--ec2--><!--sizeo:3--><!--/sizeo-->Traduzindo:<!--sizec--><!--/sizec-->Fizemos um comando, que se o player for admin, vai setar a vida do jogador escolhido para a vida escolhida. Criamos a variável tmp, que representaria o strtok. Você pode notar que botamos a função if(!strlen(tmp)){, que significa que se o jogador não digitar o id e a vida desejada, vai aparecer a mensagem "[ERRO DE SINTAXE] /vida id vida". Então criamos uma variável chamada vida e uma chamada plid. A variável plid será o id do player escolhido, e a variável vida seria a vida que seria dada ao player. Então usamos uma string para mandar a mensagem "O Admnistrador %s setou a vida de %s para %d." e depois como final do code, usamos o SetPlayerHealth(plid, vida);, que setaria a vida do player desejado.<!--sizeo:5--><!--/sizeo-->4. Multi-Array Strings<!--sizec--><!--/sizec-->Multi array strings são strings que carregam várias mensagens ao mesmo tempo, ou vários números, coordenadas, etc... Um exemplo muito conhecido de multi-array string é a multi-array do nome dos carros.<!--c1-->CÓDIGO<!--ec1-->new VehicleName[212][0] = {"Landstalker",//--------> Numero 0"Bravura",//      --------> Numero 1"Buffalo",//      -------> Numero 2"Linerunner",// -------> E assim vai..."Pereniel","Sentinel","Dumper","Firetruck","Trashmaster","Stretch","Manana","Infernus","Voodoo","Pony","Mule","Cheetah","Ambulance","Leviathan","Moonbeam","Esperanto","Taxi","Washington","Bobcat","Mr Whoopee","BF Injection","Hunter","Premier","Enforcer","Securicar","Banshee","Predator","Bus","Rhino","Barracks","Hotknife","Trailer","Previon","Coach","Cabbie","Stallion","Rumpo","RC Bandit","Romero","Packer","Monster Truck","Admiral","Squalo","Seasparrow","Pizzaboy","Tram","Trailer","Turismo","Speeder","Reefer","Tropic","Flatbed","Yankee","Caddy","Solair","Berkley's RC Van","Skimmer","PCJ-600","Faggio","Freeway","RC Baron","RC Raider","Glendale","Oceanic","Sanchez","Sparrow","Patriot","Quad","Coastguard","Dinghy","Hermes","Sabre","Rustler","ZR-350","Walton","Regina","Comet","BMX","Burrito","Camper","Marquis","Baggage","Dozer","Maverick","News Chopper","Rancher","FBI Rancher","Virgo","Greenwood","Jetmax","Hotring","Sandking","Blista Compact","Police Maverick","Boxville","Benson","Mesa","RC Goblin","Hotring Racer","Hotring Racer","Bloodring Banger","Rancher","Super GT","Elegant","Journey","Bike","Mountain Bike","Beagle","Cropdust","Stunt","Tanker","RoadTrain","Nebula","Majestic","Buccaneer","Shamal","Hydra","FCR-900","NRG-500","HPV1000","Cement Truck","Tow Truck","Fortune","Cadrona","FBI Truck","Willard","Forklift","Tractor","Combine","Feltzer","Remington","Slamvan","Blade","Freight","Streak","Vortex","Vincent","Bullet","Clover","Sadler","Firetruck","Hustler","Intruder","Primo","Cargobob","Tampa","Sunrise","Merit","Utility","Nevada","Yosemite","Windsor","Monster Truck","Monster Truck","Uranus","Jester","Sultan","Stratum","Elegy","Raindance","RC Tiger","Flash","Tahoma","Savanna","Bandito","Freight","Trailer","Kart","Mower","Duneride","Sweeper","Broadway","Tornado","AT-400","DFT-30","Huntley","Stafford","BF-400","Newsvan","Tug","Trailer","Emperor","Wayfarer","Euros","Hotdog","Club","Trailer","Trailer","Andromada","Dodo","RC Cam","Launch","Polícia (LSPD)","Polícia (SFPD)","Polícia (LVPD)","Police Ranger","Picador","S.W.A.T. Van","Alpha","Phoenix","Glendale","Sadler","Trailer de Bagagens","Trailer de Bagagens","Stair Trailer","Boxville","Farm Plow","Trailer de Utilidades"};<!--c2--><!--ec2-->Como você pode ter visto, esta string tem o nome de todos os carros. Agora vamos usá-la. Vamos fazer um comando /nomeveiculo [id], que vai mostrar o nome do veículo do id escolhido.<!--c1-->CÓDIGO<!--ec1-->if (strcmp(cmd, "/nomeveiculo", true)==0){new string [256];new tmp[256];new idveiculo;tmp = strtok(cmdtext, idx);if(!strlen(tmp)){SendClientMessage(playerid, cinza, "[ERRO DE SINTAXE] /nomeveiculo [id]");return 1;}idveiculo = strval(tmp);format(string, sizeof(string), "Nome do veículo: %s.", VehicleName[idveiculo - 400]);SendClientMessageToAll(branco, string);return 1;}<!--c2--><!--ec2-->Agora fizemos um comando que vai mostrar o nome do veículo, e usamos VehicleName[idveiculo - 400] para poder mostrar o nome do veículo certo. Ou seja, quando ele digita /nomeveiculo 400, vai mostrar o veículo Nº 0, já que mostra a variável idveiculo - 400.<!--sizeo:5--><!--/sizeo-->5. Stock Functions<!--sizec--><!--/sizec-->Stocks e são funções criadas pelo player, para simplificar funções e adicionar novas funções ao sa-mp. Aqui um exemplo bem conhecido de função stock:<!--c1-->CÓDIGO<!--ec1-->stock GetXYInFrontOfPlayer(playerid, &amp;Float:x, &amp;Float:y, Float:distance){    // Created by Y_Lessnew Float:a;GetPlayerPos(playerid, x, y, a);GetPlayerFacingAngle(playerid, a);if (GetPlayerVehicleID(playerid)) {   GetVehicleZAngle(GetPlayerVehicleID(playerid), a);}x += (distance * floatsin(-a, degrees));y += (distance * floatcos(-a, degrees));}<!--c2--><!--ec2-->Esse stock adiciona uma função ao sa-mp, de pegar as coordenadas na frente do jogador. Agora vamos criar nossa própria função no sa-mp, uma função de SETAR o dinheiro das pessoas. (Não tem função de setar, so de dar dinheiro)<!--c1-->CÓDIGO<!--ec1-->stock SetarGrana(playerid, grana){ResetPlayerMoney(playerid);GivePlayerMoney(playerid, grana);}<!--c2--><!--ec2-->Ok, nosso stock function está pronto, ou seja, vai resetar a grana do player e depois vai dar o dinheiro, sendo assim a mesma coisa que setar. Agora nós podemos usar a função que nós criamos em qualquer parte do nosso script. Por Exemplo, tente combinar o que aprendemos antes com os strtok com o SetarGrana, vamos criar um comando /setargrana id grana com a função que acabamos de fazer.<!--c1-->CÓDIGO<!--ec1-->if (strcmp(cmd, "/setargrana", true)==0 &amp;&amp; IsPlayerAdmin(playerid)){new tmp[256];new string[256];new plid, grana;new nomeentregador[MAX_PLAYER_NAME];new nomerecebedor[MAX_PLAYER_NAME];tmp = strtok(cmdtext, idx);if(!strlen(tmp)){SendClientMessage(playerid, cinza, "[ERRO DE SINTAXE] /setargrana [id] [grana]");return 1;}plid = strval(tmp);tmp = strtok(cmdtext, idx);if(!strlen(tmp)) {SendClientMessage(playerid, cinza, "[ERRO DE SINTAXE] /setargrana [id] [grana]");return 1;}grana = strval(tmp);if(IsPlayerConnected(plid)){GetPlayerName(plid, nomerecebedor, sizeof(nomerecebedor));GetPlayerName(playerid, nomeentregador, sizeof(nomeentregador));format(string, sizeof(string), "O Admnistrador %s setou o dinheiro de %s para R$%d.", nomeentregador,nomerecebedor,grana);SendClientMessageToAll(branco, string);SetarGrana(plid, grana);return 1;}else{SendClientMessage(playerid, cinza, "Jogador não conectado.");return 1;}}<!--c2--><!--ec2-->Pronto! Aí está o nosso code simplificado de setar grana(na verdade nem tanto, mas vc pode simplificar mais do jeito q vc quiser fazer seu stock function). Stocks podem fazer qualquer função, e são geralmente usados para fazer includes.<!--sizeo:5--><!--/sizeo-->6. User Databases<!--sizec--><!--/sizec-->User databases são includes usados para salvar os dados de algum jogador. Neste tutorial vou explicar como usar  o DUDB.O dudb, na minha opinião é o melhor include de database. Aqui vou explicar como utilizar as funções do dudb:<!--quoteo--><!--quotec--><!--coloro:#0000FF--><!--/coloro-->udb_Create(Filename[], password[])<!--colorc--><!--/colorc-->: Função utilizada para criar uma conta no dudb. Veja no exemplo de sistema de registro/login.<!--coloro:#0000FF--><!--/coloro-->udb_UserSet(Filename[], key[], value[])<!--colorc--><!--/colorc-->: Função utilizada para salvar algum dado em formato de texto.<!--coloro:#0000FF--><!--/coloro-->udb_UserSetInt(Filename[], key[], value)<!--colorc--><!--/colorc-->: Função utilizada para salvar algum dado em formato de número INTEIRO. Ex: Variáveis de kills /mortes<!--coloro:#0000FF--><!--/coloro-->udb_UserSetFloat(Filename[], key[], Float:value)<!--colorc--><!--/colorc-->: Função utilizada para salvar algum dado em formato de float ( como por exemplo, as coordenadas de spawn de algum player )<!--coloro:#0000FF--><!--/coloro-->udb_UserInt(Filename[], key[])<!--colorc--><!--/colorc-->: Checa o valor de alguma variável de número INTEIRO no arquivo, utilizada para carregar os dados.<!--coloro:#0000FF--><!--/coloro-->udb_UserFloat(Filename[], key[])<!--colorc--><!--/colorc-->: Checa o valor de alguma variável de número FLOAT no arquivo, utilizada para carregar dados de coordenadas.<!--coloro:#0000FF--><!--/coloro-->udb_CheckLogin(Filename[], password[])<!--colorc--><!--/colorc-->: Função utilizada nos sistemas de login/registro para ver se a senha está correta.<!--QuoteEnd--><!--QuoteEEnd-->Bom, agora que sabemos as funções principais do DUDB e pra que elas servem, vamos criar nosso sistema de login/registro.Vamos adicionar isso no nosso gamemode:<!--c1-->CÓDIGO<!--ec1-->//No topo de tudo, junto com os outros includes:#include #define amarelo                 0xFFFF00AA#define verde                     0x33FF33AA#define vermelho                 0xFF0000AA#define vermelhoescuro             0xAA3333AA#define branco                     0xFFFFFFAA#define rosa                     0xCCFF00FFAA#define azul                     0x057ABDAA#define cinza                     0xC0C0C0AA#define yellow                     0xFFFF00AA#define laranja                 0xFFA500AA//agora no OnPlayerConnect:new nomepl[MAX_PLAYER_NAME];GetPlayerName(playerid, nomepl, sizeof(nomepl))if(!udb_Exists(nomepl)){SendClientMessage(playerid, azul, "[LOGIN/REGISTRO] Você não está registrado, registre-se com /registrar senha");}else{SendClientMessage(playerid, azul, "[LOGIN/REGISTRO] Você já está registrado, logue-se com /logar senha");}//agora no OnPlayerCommandTextif (strcmp(cmd, "/registrar", true)==0){new tmp[256];new senha[256];tmp = strtok(cmdtext, idx);if(!strlen(tmp)){SendClientMessage(playerid,vermelhoescuro,"/registrar [senha] - Exemplo: /registrar 123");return 1;}if(udb_Exists(PlayerName(playerid))){SendClientMessage(playerid, vermelhoescuro, "Você já tem uma conta no Renaissance RPG!");return 1;}format(senha, sizeof(senha), "%s", tmp);   new nomepl[MAX_PLAYER_NAME];   GetPlayerName(playerid, nomepl, sizeof(nomepl));udb_Create(nomepl, senha);SendClientMessage(playerid, azul, "Registrado com sucesso!");return 1;}if (strcmp(cmd, "/logar", true)==0){new tmp[256];new senha[256];tmp = strtok(cmdtext, idx);if(!strlen(tmp)){SendClientMessage(playerid,vermelhoescuro,"/logar [senha] - Exemplo: /logar 123");return 1;}if(logado[playerid] == 1){SendClientMessage(playerid, vermelhoescuro, "Você já está logado!");return 1;}format(senha, sizeof(senha), "%s", tmp);if (udb_CheckLogin(PlayerName(playerid),tmp)) {SendClientMessage(playerid, azul, "Logado com sucesso!");return 1;}else{SendClientMessage(playerid, vermelho, "Senha errada!");}return 1;}<!--c2--><!--ec2-->Agora acabamos de criar nosso sistema de login/registro. Vamos criar agora um sistema para salvar o dinheiro de alguém.<!--c1-->CÓDIGO<!--ec1-->//no OnPlayerDisconnectnew grana;new nomepl[MAX_PLAYER_NAME];GetPlayerName(playerid, nomepl, sizeof(nomepl));grana = GetPlayerMoney(playerid);udb_UserSetInt(nomepl, "Grana", grana);//agora no OnPlayerSpawnnew grana;new nomepl[MAX_PLAYER_NAME];GetPlayerName(playerid, nomepl, sizeof(nomepl));grana = udb_UserInt(nomepl, "Grana");GivePlayerMoney(playerid, grana);<!--c2--><!--ec2-->Pronto! Agora criamos um sistema que salva o dinheiro de alguém quando esta pessoa volta ao servidor! Para fazer as variáveis salvarem, é muito simples, basta adicionar o seguinte code no /logar e no OnPlayerDisconnect:<!--c1-->CÓDIGO<!--ec1-->//No OnPlayerDisconectnew nomepl[MAX_PLAYER_NAME];GetPlayerName(playerid, nomepl, sizeof(nomepl));udb_UserSetInt(nomepl, "Banco", banco[playerid]);/*isso faz salvar, coloquei a variável banco[playerid] por pedido do Cronos, mas dependendo do que vc quiser salvar, mude isto*///no sistema de /logarnew nomepl[MAX_PLAYER_NAME];GetPlayerName(playerid, nomepl, sizeof(nomepl));Banco[playerid] = udb_UserInt(nomepl, "Banco");//Isso faz carregar, mude a variável Banco[playerid] e "Banco" pela variavel que quiser carregar<!--c2--><!--ec2-->Isso vai fazer salvar a variável banco quando o player desconectar, e carregar quando ele logar.<!--sizeo:5--><!--/sizeo-->7. Y_Objects<!--sizec--><!--/sizec-->Y_Objects é um streamer de objetos usado para poder exceder o limite de objetos no sa-mp, evitando crashs. O ponto bom dele, é que você pode criar muitos objetos, o ponto ruim é que deixa seu script muito pesado e demora um pouco para carregar os objetos dentro do jogo. Enfim, vamos ao que interessa:Para começar criando seus objetos, você deve trocar a função CreateObject por CreateDynamicObject, sem mudar as funções dentro dos parênteses. Após ter feito isso, você bota o seguinte no gamemode:<!--c1-->CÓDIGO<!--ec1-->//no OnGameModeInitObject_Object();AQUI VC POE OS CREATEDYNAMICOBJECT//no OnPlayerDisconnectObject_OnPlayerDisconnect();<!--c2--><!--ec2-->Pronto! Agora os objetos foram criados.Caso vc queira saber um pouco mais sobre o Y_Objects, vou colocar aqui o que faz cada função.<!--quoteo--><!--quotec-->Object_AddToPlayer(objectid, playerid) - Adiciona um certo objeto apenas para um player(use uma variável para definir o objeto)Object_AddToWorld(objectid, world) - Adiciona um certo objeto apenas para um mundo virtual(lembrando que se não definir o world, o objeto será setado no inicial)Object_AddToAllPlayers(objectid) - Adiciona um certo objeto a todos os players conectados(quem se conectar depois não verá os objetos)Object_AddToAllWorlds(objectid) - Adiciona um certo objeto a todos os virtual worldsObject_RemoveFromPlayer(objectid, playerid) - Remove um certo objeto para apenas um playerObject_RemoveFromWorld(objectid, world) - Remove um certo objeto de apenas um world.Object_RemoveFromAllPlayers(objectid) - Remove o objeto de todos os playersObject_RemoveFromAllWorlds(objectid) - Remove de todos os virtual worldsObject_SetViewDistance(objectid, distance) - Seta a distância no qual o objeto pode ser visto (normal = 1000)MoveDynamicObject(object, x, y, z, speed) - Move um objetoStopDynamicObject(objectid) - Para de mover um objetoIsValidDynamicObject(objectid) - Checa se o objeto (do Y_Objects) existeCreateDynamicObject(object, x, y, z, rx, ry, rz) - Cria um objeto do includeDestroyDynamicObject(object) - Destroy o objetoAttachDynamicObjectToPlayer - Puxa o objeto junto ao jogador, ou seja, se o jogador se mover, o objeto vai juntoAttachDynamicObjectToObject - Puxa o objeto junto ao objeto, ou seja, se o  objeto se mover, este objeto vai juntoDettachDynamicObjectFromPlayer - Despuxa o objeto do jogador<!--QuoteEnd--><!--QuoteEEnd--><!--sizeo:5--><!--/sizeo-->8. Races<!--sizec--><!--/sizec-->Para fazer corridas, é um processo meio complicado, estaremos usando a função SetPlayerRaceCheckpoint.Primeiramente, vou explicar como funciona cada função necessária para se criar uma corrida:<!--c1-->CÓDIGO<!--ec1-->SetPlayerRaceCheckpoint(playerid, tipo, x, y, z, nextx, nexty, nextz, tamanho):No playerid, vamos colocar o id da pessoa que vai receber o checkpoint, no tipo, o tipo do checkpoint, estes são os tipos:0 - Normal: Checkpoints normais da corrida, aqueles que tem a seta apontando para onde vai ser o próximo checkpoint.1 - Finish: Checkpoint Final de uma Corrida.2 - Nothing: Checkpoint que não tem nada dentro dele.3 - Air: Checkpoint feito para corridas aéreas.4 - Air Finish: Checkpoint feito para final de corridas aéreas.Em x,y e z colocaremos as coordenadas onde serão os checkpoints, e em nextx, nexty e nextz, as coordenadas do próximo checkpoint(caso seja um checkpoint de final, deixe as coordenadas do mesmo).E por último, em tamanho colocaremos o tamanho do checkpoint(o natural é geralmente colocar 8.0)DisablePlayerRaceCheckpoint(playerid):Usaremos essa função para trocar de checkpoint quando o player chegar em algum checkpoint da corrida.<!--c2--><!--ec2-->Agora vamos fazer nossa corrida. Para simplificar as coordenadas de checkpoint da corrida, vamos usar um Float Multi-Array.Acrescentaremos isso em cima de OnGamemodeInit:<!--c1-->CÓDIGO<!--ec1-->new checkcorrida[MAX_PLAYERS];new Float:ChecksAvenida[17][3] = {{2021.9279,851.2713,6.4387},{1806.2888,851.1154,10.3762},{1597.7745,850.9503,6.4374},{1315.8704,886.0849,6.5154},{1227.3850,1170.5470,6.5219},{1228.7192,1804.4149,6.4378},{1229.5260,2301.2026,6.4387},{1447.5416,2452.2988,6.4377},{1849.4731,2497.6997,6.5153},{2287.5569,2607.9063,6.4585},{2609.7202,2514.3423,5.8201},{2706.6592,2158.3931,6.4492},{2705.5173,1674.4374,6.4448},{2706.8931,1278.5399,6.4373},{2624.1135,932.2136,6.4433},{2312.5583,854.1749,6.4375},{2049.3154,853.4050,6.4387}};<!--c2--><!--ec2-->Pronto! Acabamos de fazer um Multi-Array com as coordenadas dos checkpoints da corrida! Essa corrida está localizada na avenida, em LV. Agora vamos fazer um comando para o player entrar na corrida. (vamos fazer uma corrida singleplayer por enquanto, apenas para explicar como fazer a troca de checkpoints) Logo que ele digitar o comando, o checkpoint 1 da corrida vai aparecer no mapa do player, e quando entrar no checkpoint, vai aparecer o 2, e assim por diante... Então, aqui o code do comando:<!--c1-->CÓDIGO<!--ec1-->if(strcmp(cmdtext,"/entrarcorrida",true)==0){   checkcorrida[playerid] = 0;SetPlayerRaceCheckpoint(playerid, 0, ChecksAvenida[checkcorrida[playerid]][0], ChecksAvenida[checkcorrida[playerid]][1], ChecksAvenida[checkcorrida[playerid]][2], ChecksAvenida[checkcorrida[playerid] + 1][0], ChecksAvenida[checkcorrida[playerid] + 1][1], ChecksAvenida[checkcorrida[playerid] + 1][2], 8.0);   return 1;}<!--c2--><!--ec2-->E agora, faremos um sistema para trocar de checkpoint, usando o callback OnPlayerEnterRaceCheckpoint. Caso não o tenha no seu gamemode, adicione.<!--c1-->CÓDIGO<!--ec1-->public OnPlayerEnterRaceCheckpoint(playerid){if(checkcorrida[playerid] == 17){SendClientMessage(playerid, 0xFFFF00AA, "Você terminou a corrida!");DisablePlayerRaceCheckpoint(playerid);checkcorrida[playerid] = 0;return 1;}else{DisablePlayerCheckpoint(playerid);checkcorrida[playerid] ++;SetPlayerRaceCheckpoint(playerid, 0, ChecksAvenida[checkcorrida[playerid]][0], ChecksAvenida[checkcorrida[playerid]][1], ChecksAvenida[checkcorrida[playerid]][2], ChecksAvenida[checkcorrida[playerid] + 1][0], ChecksAvenida[checkcorrida[playerid] + 1][1], ChecksAvenida[checkcorrida[playerid] + 1][2], 8.0);return 1;}}<!--c2--><!--ec2--><!--sizeo:4--><!--/sizeo-->Então, o que acabamos de fazer?<!--sizec--><!--/sizec-->Criamos uma multi-array para simplificar as coordenadas dos checkpoints, e também criamos uma variável para definir o checkpoint que o player está. Então, cada vez que o player entrava em um checkpoint, a variável aumentava e se setava um checkpoint de acordo com o número que estava a variável. Quando a variável chegasse em 17, finalizaria a corrida. Assim fizemos nossa corrida.<!--sizeo:3--><!--/sizeo-->Feito por waxhunter