Primeiras experiências com o NWNX4
Autor: SubHeaven | Publicado: 2008-07-11T08:56:00-07:00

Oláááá terráqueos.Nesse últimos dias consegui algum tempo para mexer no meu módulo e agora estou tratando da integração dele com o MySQL.Meu primeiro passo foi tentar achar algum módulo com alguma funcionalidade já pronta. Acabei pegando o próprio módulo de exemplo que vem no NWNX4. Ele deu um erro ao tentar abrir a única área que ele ja possuía, mas o importante pra mim eram os scripts. Criei então duas áreas, uma com NPCs para testar algumas funcionalidades e outra com uma criatura hostil para testar o sistema de morte.Com tudo armado, eu abri o script e_mod_load e adicionei os seguinte comandos: if (!doesTableExist("TEXTOS")) {      SQLExecDirect("CREATE TABLE TEXTOS (" +         "PLAYER VARCHAR(64) NOT NULL DEFAULT '~'," +         "TAG VARCHAR(64) NOT NULL DEFAULT '~'," +         "NAME VARCHAR(64) NOT NULL DEFAULT '~'," +         "TEXTO TEXT," +         "EXPIRE INT(11) DEFAULT NULL," +         "PRIMARY KEY  (PLAYER, TAG, NAME)" +         ") ENGINE=MyISAM DEFAULT CHARSET=latin1;"  );  if (!doesTableExist("TEXTOS")) {    SetLocalString(oMod, "ERROR", "The MySQL plugin could not set up the table 'pwdata'.  Please make sure that the mysql userid in xp_mysql.ini has sufficient permissions to create a new table.");    PrintString("e_mod_load: CREATE pwdata failed.");   return;  }   }Ou seja, apenas copiei o código anterior que verificava a existencia das tabelas pwdata e pwobjdata e as criavam e as adaptei para verificar e criar a tabela TEXTO, que gravará um texto qualquer.Rodei o módulo no NWNX4 e funcionou legal.Meu próximo passo então era gravar um texto qualquer no banco de dados e recuperar. Tentei usar as funções que já vêem no nwnx_sql (GetPersistentString e SetPersistentString), mas não gravava nada. Eu então coloquei algumas mensagens de debug e encontrei o problema:As funções do nwnx_sql servem apenas para as tabelas ja definidas (pwdata e pwobjdata) e assim criava um comando SQL todo esquisito, como é possível conferir na screenshot acima. A solução então era criar meu próprio script de funcionalidades para a base de dados. Criei então a sh_sql, inicialmente com apenas 2 funções: SetTexto e GetTexto://::////////////////////////////////////////////////////:: sh_sql/*  Funções para o uso do NWNX4 de acordo com o esquema de   base de dados criado por mim. *///::////////////////////////////////////////////////////:: Copyright (c) 2008 SubHeaven World//:: Created By: SubHeaven//:: Created On: 07/10/2008//:://////////////////////////////////////////////////#include "nwnx_sql"/************************************//* Function prototypes              *//************************************///Gravar texto do jogador.//  oPC: O jogador que está gravando o texto;//  sTexto: O texto a ser gravado na base de dados.void SetTexto(object oPC, string sTexto);//Ler texto do jogador.//  oPC: O jogador que gravou o texto anteriormente.string GetTexto(object oPC);/************************************//* Implementation                   *//************************************/void SetTexto(object oPC, string sTexto){    string sPlayer;    string sChar;    if (GetIsPC(oPC))    {  //SendMessageToPC(oPC, "Its a PC");          sPlayer = SQLEncodeSpecialChars(GetPCPlayerName(oPC));        sChar = SQLEncodeSpecialChars(GetName(oPC));  sTexto = SQLEncodeSpecialChars(sTexto);     string sSQL = "SELECT TEXTO " +                "FROM TEXTOS " +       "WHERE PLAYER = '" + sPlayer + "' " +       "AND NAME = '" + sChar + "'";         //SendMessageToPC(oPC, sSQL);     SQLExecDirect(sSQL);     if (SQLFetch() == SQL_SUCCESS)     {   SendMessageToPC(oPC, "Exist TEXTO");         // row exists         sSQL = "UPDATE TEXTOS SET " +          "TEXTO ='" + sTexto + "', " +       "WHERE PLAYER = '" + sPlayer + "' " +       "AND NAME = '" + sChar + "'";          //SendMessageToPC(oPC, sSQL);         SQLExecDirect(sSQL);     }     else     {   SendMessageToPC(oPC, "Doesn't exists TEXTO");         // row doesn't exist         sSQL = "INSERT INTO TEXTOS (" +       "PLAYER, " +       "NAME, " +       "TEXTO" +       ") VALUES ('" +       sPlayer + "', '" +        sChar + "', '" +       sTexto + "')";          //SendMessageToPC(oPC, sSQL);         SQLExecDirect(sSQL);     } }    else    {  SendMessageToPC(oPC, "Its not a PC");    } }string GetTexto(object oPC){ string sPlayer;    string sChar;    if (GetIsPC(oPC))    {        sPlayer = SQLEncodeSpecialChars(GetPCPlayerName(oPC));        sChar = SQLEncodeSpecialChars(GetName(oPC));     string sSQL = "SELECT TEXTO "+       "FROM TEXTOS " +       "WHERE PLAYER = '" + sPlayer + "' " +       "AND NAME = '" + sChar + "'";         //SendMessageToPC(oPC, sSQL);     SQLExecDirect(sSQL);     if (SQLFetch() == SQL_SUCCESS)         return SQLGetData(1);     else     {         return "Nao existe texto gravado pra voce.";     } }    else    {  return "";        SendMessageToPC(oPC, "Its not a PC");    }}Funcionou legal para verificar se tinha alguma informação:Ainda tive um pequeno problema na hora de gravar o texto:Mas foi fácil verificar que eu não fechava o ' do texto.Para o modulo, ainda criei mais 3 scripts: sh_escrevertexto, que utiliza a sh_sql para gravar o texto na base de dados.#include "sh_sql"void main(){ object oPC = GetPCSpeaker(); string sMensagem = GetName(oPC) + " esteve aqui.";  //SendMessageToPC(oPC, "sh_escrevertexto");  SetTexto(oPC, sMensagem); }sh_lertexto, que utiliza a sh_sql para ler o texto na base de dados.#include "sh_sql"void main(){ object oPC = GetPCSpeaker();  string sMensagem = GetTexto(oPC); SendMessageToPC(oPC, sMensagem); }e um script para ligar no evento OnUse de um objeto para acionar uma conversa que permite as ações acima, a sh_conversar://::////////////////////////////////////////////////////:: sh_conversar/*  Função usada para fazer um objeto começar uma conversa com o  jogador. *///::////////////////////////////////////////////////////:: Copyright (c) 2008 SubHeaven World//:: Created By: SubHeaven//:: Created On: 07/10/2008//:://////////////////////////////////////////////////void main(){ object oPC = GetLastUsedBy(); //SendMessageToPC(oPC, "sh_conversar"); ActionStartConversation(oPC, "", TRUE, FALSE, FALSE, TRUE);}Resultado na base de dados:O módulo de teste está nesse link.A conversa está aqui.Próximos passos: - Criar as funções que gravam a localização do jogador e que o teleportam de volta praquele local; - Criar as funções de tratamento de Death e Respawn do jogador; - Criar uma função que calcule o loot das criaturas baseado em dados armazenados no banco de dados; - Criar a função que impede a criação de mais de um personagem com o mesmo nome.