Script Roleplay SA-MP
تم الإنشاء في: ٦ يناير ٢٠٢٥
تم الإنشاء في: ٦ يناير ٢٠٢٥
Crafting a roleplay script
I’m mapping out a comprehensive Pawn script for an SAMP roleplay server, including gamemode, includes, and roleplay commands. A basic script covers event handling, text handling, and possibly advanced features.
Crafting the script
I'm assembling a minimal Pawn script with basic commands like /register
and /login
. For more advanced features like a comprehensive roleplay system, a larger, intricate script will be necessary.
Thought about script para servidor roleplay SAMP por 10 segundos
Segue abaixo um exemplo de um gamemode básico em Pawn para SA-MP que você pode adaptar ao seu projeto de Roleplay. Ele não é um script pronto e finalizado (pois, em geral, um script completo de Roleplay é algo extenso e cheio de funcionalidades específicas), mas deve servir como um ponto de partida ou exemplo de organização de código.
Ele contém:
IMPORTANTE: Para uso em produção, você vai precisar expandir e adaptar este script. Também é possível substituir o sistema de arquivo por MySQL, R40+ ou até mesmo SQLite interno do SA-MP, caso prefira.
As estruturas mostradas aqui usam o zcmd, portanto certifique-se de que o include zcmd.inc
esteja na sua pasta pawno/include
.
//////////////////////////////////////////// // gm_roleplay.pwn // Exemplo de GameMode Pawn SA-MP //////////////////////////////////////////// #include <a_samp> #include <zcmd> // Certifique-se de ter este include // Definições e constantes #define MAX_PLAYER_NAME 24 #define MAX_PLAYERS 500 // Arquivo onde salvaremos os dados dos jogadores #define ACCOUNT_PATH "scriptfiles/Accounts/%s.ini" // Variáveis globais // Aqui podemos guardar informações importantes de cada jogador new bool:gPlayerLogged[MAX_PLAYERS]; // Indica se o jogador está logado new gPlayerAdminLevel[MAX_PLAYERS]; // Nível de admin new gPlayerCash[MAX_PLAYERS]; // Dinheiro (exemplo simples) new gPlayerID[MAX_PLAYERS][MAX_PLAYER_NAME]; // Nome do jogador (redundante, mas às vezes útil) // Prototipações forward LoadPlayerData(playerid); forward SavePlayerData(playerid); // Início do GameMode public OnGameModeInit() { print("\n---------------------------------------"); print(" GameMode RolePlay - Exemplo Básico"); print(" Desenvolvido como exemplo para SA-MP"); print("---------------------------------------\n"); // Defina o limite máximo de players ou use defaut do server.cfg SetGameModeText("RolePlay Exemplo"); SetTeamCount(1); // Defina o ícone do mapa / cor do servidor EnableStuntBonusForAll(false); // Mensagem de console print(" [OnGameModeInit] Gamemode carregado com sucesso!"); return 1; } public OnGameModeExit() { print(" [OnGameModeExit] Gamemode finalizando."); return 1; } // Callback disparado sempre que um player entra no servidor public OnPlayerConnect(playerid) { // Vamos armazenar o nome para consultas posteriores GetPlayerName(playerid, gPlayerID[playerid], MAX_PLAYER_NAME); // Assim que o player conecta, não está logado gPlayerLogged[playerid] = false; gPlayerAdminLevel[playerid] = 0; gPlayerCash[playerid] = 0; SendClientMessage(playerid, 0xFFFF00AA, "Bem-vindo ao Servidor RolePlay de Exemplo!"); SendClientMessage(playerid, 0xFFFF00AA, "Use /register [senha] caso não tenha conta ou /login [senha] para entrar."); return 1; } // Callback disparado quando o jogador sai do servidor public OnPlayerDisconnect(playerid, reason) { // Se ele estiver logado, salvamos os dados if(gPlayerLogged[playerid]) { SavePlayerData(playerid); } return 1; } // Callback disparado toda vez que um player spawna public OnPlayerSpawn(playerid) { // Para um modo RP, podemos definir o spawn inicial, skins etc. // Exemplo de posição (Centro de Los Santos) SetPlayerPos(playerid, 1520.0, -1700.0, 13.5); SetPlayerFacingAngle(playerid, 270.0); GivePlayerWeapon(playerid, 0, 0); // Sem armas no spawn return 1; } // Callback disparado quando o jogador envia uma mensagem normal (/o chat local) public OnPlayerText(playerid, text[]) { // Se o jogador não estiver logado, bloqueamos o chat if(!gPlayerLogged[playerid]) { SendClientMessage(playerid, 0xFF0000AA, "Você não está logado. Use /login [senha]."); return 0; // Retornamos 0 para não exibir a mensagem no chat global } // Chat IC (In Character) básico (rádio ~ 20 metros, exemplo). // Podemos mandar a mensagem para quem estiver por perto. new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z); // Montar mensagem new msg[144]; format(msg, sizeof(msg), "%s diz: %s", gPlayerID[playerid], text); for(new i = 0; i < MAX_PLAYERS; i++) { if(IsPlayerConnected(i) && gPlayerLogged[i]) { // Se o player 'i' está dentro de 20.0 unidades do emissor, recebe a msg new Float:ix, Float:iy, Float:iz; GetPlayerPos(i, ix, iy, iz); if(GetDistanceBetweenCoords(x, y, z, ix, iy, iz) <= 20.0) { SendClientMessage(i, 0xFFFFFFFF, msg); } } } // Retorna 0 para não imprimir no chat padrão return 0; } // Exemplo de callback para capturar comandos public OnPlayerCommandText(playerid, cmdtext[]) { // Se quiser usar zcmd, normalmente dispensamos esse callback return 0; } //////////////////////////////////////////////////////////////////////// // SISTEMA DE CONTAS (ini) // //////////////////////////////////////////////////////////////////////// stock bool:AccountExists(playerName[]) { new file[128]; format(file, sizeof(file), ACCOUNT_PATH, playerName); if(fexist(file)) return true; return false; } stock bool:CreateAccount(playerName[], password[]) { new file[128]; format(file, sizeof(file), ACCOUNT_PATH, playerName); new File:fp = fopen(file, io_write); if(fp) { fwrite(fp, password); fclose(fp); return true; } return false; } stock bool:CheckPassword(playerName[], password[]) { new file[128], storedPass[64]; format(file, sizeof(file), ACCOUNT_PATH, playerName); if(!fexist(file)) return false; new File:fp = fopen(file, io_read); if(!fp) return false; fread(fp, storedPass); fclose(fp); if(!strcmp(storedPass, password, true)) { return true; } return false; } forward SavePlayerData(playerid); public SavePlayerData(playerid) { // Caso queira salvar mais informações (posição, dinheiro, etc.), mude a lógica // Aqui, como exemplo, salvamos só dinheiro e admin level, caso queira expandir // Ex.: Salvar em arquivo .ini separado, ou substitua por MySQL etc. return 1; } forward LoadPlayerData(playerid); public LoadPlayerData(playerid) { // Carrega as informações, no momento deixamos em branco return 1; } //////////////////////////////////////////////////////////////////////// // COMANDOS // //////////////////////////////////////////////////////////////////////// CMD:register(playerid, params[]) { // /register [senha] if(gPlayerLogged[playerid]) { return SendClientMessage(playerid, 0xFF0000AA, "Você já está logado."); } new password[32]; if(sscanf(params, "s[32]", password)) // falta ou erro ao digitar senha { return SendClientMessage(playerid, 0xFF0000AA, "Uso correto: /register [senha]"); } new playerName[MAX_PLAYER_NAME]; GetPlayerName(playerid, playerName, sizeof(playerName)); if(AccountExists(playerName)) { return SendClientMessage(playerid, 0xFF0000AA, "Esta conta já existe. Use /login [senha]."); } if(CreateAccount(playerName, password)) { SendClientMessage(playerid, 0x00FF00AA, "Conta criada com sucesso! Agora use /login [senha]."); } else { SendClientMessage(playerid, 0xFF0000AA, "Falha ao criar conta. Contate a administração."); } return 1; } CMD:login(playerid, params[]) { // /login [senha] if(gPlayerLogged[playerid]) { return SendClientMessage(playerid, 0xFF0000AA, "Você já está logado."); } new password[32]; if(sscanf(params, "s[32]", password)) { return SendClientMessage(playerid, 0xFF0000AA, "Uso correto: /login [senha]"); } new playerName[MAX_PLAYER_NAME]; GetPlayerName(playerid, playerName, sizeof(playerName)); if(!AccountExists(playerName)) { return SendClientMessage(playerid, 0xFF0000AA, "Conta não existe. Use /register [senha]."); } if(CheckPassword(playerName, password)) { gPlayerLogged[playerid] = true; SendClientMessage(playerid, 0x00FF00AA, "Login efetuado com sucesso!"); LoadPlayerData(playerid); SpawnPlayer(playerid); // Força o spawn do player } else { SendClientMessage(playerid, 0xFF0000AA, "Senha incorreta. Tente novamente."); } return 1; } // Comando simples /me para ação em terceira pessoa (Roleplay) CMD:me(playerid, params[]) { if(!gPlayerLogged[playerid]) { return SendClientMessage(playerid, 0xFF0000AA, "Você precisa estar logado para usar este comando."); } if(!strlen(params)) { return SendClientMessage(playerid, 0xFF0000AA, "Uso correto: /me [ação]"); } new string[128], name[MAX_PLAYER_NAME]; GetPlayerName(playerid, name, sizeof(name)); format(string, sizeof(string), "* %s %s", name, params); // Envia a mensagem para players próximos (similar ao que fizemos no OnPlayerText) new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z); for(new i = 0; i < MAX_PLAYERS; i++) { if(IsPlayerConnected(i) && gPlayerLogged[i]) { new Float:ix, Float:iy, Float:iz; GetPlayerPos(i, ix, iy, iz); if(GetDistanceBetweenCoords(x, y, z, ix, iy, iz) <= 20.0) { SendClientMessage(i, 0xFFB6C1AA, string); // Cor rosa claro } } } return 1; } // Comando simples /do para descrever uma cena ou ambiente CMD:do(playerid, params[]) { if(!gPlayerLogged[playerid]) { return SendClientMessage(playerid, 0xFF0000AA, "Você precisa estar logado para usar este comando."); } if(!strlen(params)) { return SendClientMessage(playerid, 0xFF0000AA, "Uso correto: /do [descrição]"); } new string[128]; format(string, sizeof(string), "[DO]: %s", params); new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z); for(new i = 0; i < MAX_PLAYERS; i++) { if(IsPlayerConnected(i) && gPlayerLogged[i]) { new Float:ix, Float:iy, Float:iz; GetPlayerPos(i, ix, iy, iz); if(GetDistanceBetweenCoords(x, y, z, ix, iy, iz) <= 20.0) { SendClientMessage(i, 0x00FFFFFF, string); // Cor branca } } } return 1; } // Exemplo simples de comando OOC global (/ooc [mensagem]) CMD:ooc(playerid, params[]) { if(!gPlayerLogged[playerid]) { return SendClientMessage(playerid, 0xFF0000AA, "Você precisa estar logado para usar este comando."); } if(!strlen(params)) { return SendClientMessage(playerid, 0xFF0000AA, "Uso correto: /ooc [mensagem]"); } new name[MAX_PLAYER_NAME]; GetPlayerName(playerid, name, sizeof(name)); new string[128]; format(string, sizeof(string), "((OOC) %s: %s))", name, params); SendClientMessageToAll(0xC0C0C0AA, string); // Cinza return 1; } // Exemplo de comando do admin /setmoney [id] [valor] CMD:setmoney(playerid, params[]) { // Verifica se é admin if(gPlayerAdminLevel[playerid] < 1) { return SendClientMessage(playerid, 0xFF0000AA, "Você não tem permissão para usar este comando."); } new tmpid, amount; if(sscanf(params, "ii", tmpid, amount)) { return SendClientMessage(playerid, 0xFF0000AA, "Uso correto: /setmoney [playerid] [valor]"); } if(!IsPlayerConnected(tmpid)) { return SendClientMessage(playerid, 0xFF0000AA, "Jogador inválido ou desconectado."); } gPlayerCash[tmpid] = amount; format(params, sizeof(params), "Você setou o dinheiro do jogador %d para %d.", tmpid, amount); SendClientMessage(playerid, 0x00FF00AA, params); format(params, sizeof(params), "Um administrador setou seu dinheiro para %d.", amount); SendClientMessage(tmpid, 0x00FF00AA, params); return 1; }
Expansão de sistemas
Para um Roleplay completo, normalmente se incluem sistemas de casas, veículos pessoais, inventário, empregos, facções (polícia, governo, máfia, gangues), mapeamento de áreas, entre outros. Cada sistema pode ser salvo num banco de dados (MySQL/SQLite) e carregado ao entrar no servidor.
Segurança
Para produção, você deve hashear as senhas (por exemplo, usando Whirlpool ou outra biblioteca de criptografia) em vez de salvá-las em texto puro. Nunca armazene senhas sem criptografia.
Performance
Em servidores com muitos jogadores, usar loops como for(new i=0; i < MAX_PLAYERS; i++)
com checagem de distância pode ser custoso. Otimizações como “streaming de chat” ou “limitar buscas apenas a jogadores conectados” podem ser necessárias.
Estrutura de pastas
gamemodes/gm_roleplay.pwn
(este script)pawno/include/zcmd.inc
scriptfiles/Accounts/
(onde serão criados os arquivos de conta)Compilação
gm_roleplay.pwn
no Pawno
e compile.server.cfg
para apontar gamemode0 gm_roleplay 1
.Este é apenas um exemplo para você iniciar a estrutura do seu servidor Roleplay em SA-MP. Adapte-o conforme as necessidades do seu projeto, adicionando mais sistemas e funções típicas de um bom RP!