Mostrando postagens com marcador Download. Mostrar todas as postagens
Mostrando postagens com marcador Download. Mostrar todas as postagens

sexta-feira, 11 de julho de 2008

Primeiras experiências com o NWNX4

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.

terça-feira, 8 de julho de 2008

Bancos e cadeiras - Parte 1

Bom... Antes de fazer a integração do meu mapa inicial com o MySQL eu resolvi fazer mais alguns ajustes no mapa e criar bancos sentáveis para os jogadores.
Comecei com as funções do Malese Kish, pois ja tinha testado o módulo e ele é cheio de ótimas idéias. Porém, os bancos só tinham espaço para uma pessoa sentar. Ficava estranho, e fui pesquisar uma maneira de resolver isso.
Acabei encontrando e baixando as Sittable Chair do Pacha e dentro dele tinha um blueprint chamado SitBox. Eu então montei um grupo onde colocava um banco comum e tres SitBox dentro dele (O SitBox é invisível) e funcionou quase perfeitamente. O personagem sentava no ar e só depois era transportado para o banco. Nos scripts do Malese Kish isso não acontecia e, comparando os dois códigos, eu percebi que o Patcha usava os comandos ActionJumpToLocation e ActionPlayCustomAnimation enquanto que o Moloch usava apenas JumpToLocation e PlayCustomAnimation. Eu alterei as linhas e, como suspeitava, o problema foi resolvido. Alterei também o comando onde era calculado a direção pra onde o jogador ficaria depois de sentado, pois a versão do Patcha era bem estranho.

O evento OnUse do SitBox ficou assim:

//::///////////////////////////////////////////////
//:: OnUse: Sit
//:: pat_sitted
//:://////////////////////////////////////////////
/*
Simple script to make PCs sit on a placeable
*/
//:://////////////////////////////////////////////
//:: Created By: Patcha
//:: Created On: 2006-12-08
//:: v1.76 By: Patcha
//:: v1.73 On: 2007-06-15
//:: dates: aaaa-mm-gg
//:://////////////////////////////////////////////
//::Change: SubHeaven 07/05/2008 Changed the functions ActionJumpToLocation for
// JumpToLocation and ActionPlayCustomAnimation
// for PlayCustonAnimation to fix the where the
// PC sit in the air before be transported for
// the chair location.
//::Change: SubHeaven 07/05/2008 Changed the funcion GetNormalizedDirection() for
// only the GetFacing to fix bugs with the PC directions
// after sitting.


void ActionPlayCustomAnimation(object oObject, string sAnimationName, int nLooping, float fSpeed = 1.0f)
{
PlayCustomAnimation(oObject, sAnimationName, nLooping, fSpeed);
}

// float GetNormalizedDirection(float fDirection):
// * This script returns a direction normalized to the range 0.0 - 360.0
// * Copyright (c) 2002 Floodgate Entertainment
// * Created By: Naomi Novik
// * Created On: 11/08/2002
float GetNormalizedDirection(float fDirection)
{
float fNewDir = fDirection;
while (fNewDir >= 360.0) {
fNewDir -= 360.0;
}
while (fNewDir <= 0.0) {
fNewDir += 360.0;
}

return fNewDir;
}

void main()
{
object oChair = OBJECT_SELF;
object oSitter = GetLastUsedBy();
object oLastSitter = GetLocalObject(oChair, "lastsitted");
string sChair = GetTag(oChair);
string sAutofit = GetLocalString(oChair, "autofit");
int iHeading = GetLocalInt(oChair, "degree");
int iPC_size = GetLocalInt(oChair, "size");
//Assign the heading degrees
location lChair_o = GetLocation(oChair);

//Old code: Change #2
//location lChair = Location(GetArea(oChair), GetPositionFromLocation(lChair_o), GetNormalizedDirection(GetFacingFromLocation(lChair_o) + iHeading));
//New code : Change #2
location lChair = Location(GetArea(oChair), GetPositionFromLocation(lChair_o), GetFacing(oChair));
//End Change #2

//Check if seat is free
if(GetDistanceBetween(oLastSitter, oChair) == 0.0f && GetArea(oLastSitter) == GetArea(oChair))
{
SetLocalInt(oChair, "taken", 1);
SpeakOneLinerConversation("", OBJECT_INVALID, TALKVOLUME_WHISPER);
}
else //if seat is free...
{
SetLocalInt(oChair, "taken", 0);

//Check for Character Race with original Creature Size
switch (iPC_size)
{
case 0:
//Check for Character Race with original Creature Size
if( ((GetRacialType(oSitter) == RACIAL_TYPE_ELF) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)) ||
((GetRacialType(oSitter) == RACIAL_TYPE_HALFELF) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)) ||
((GetRacialType(oSitter) == RACIAL_TYPE_HALFORC) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)) ||
((GetRacialType(oSitter) == RACIAL_TYPE_HUMAN) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)) ||
((GetSubRace(oSitter) == RACIAL_SUBTYPE_AASIMAR) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)) ||
((GetSubRace(oSitter) == RACIAL_SUBTYPE_TIEFLING) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)))
{
if(GetIsObjectValid(oChair) && GetIsObjectValid(oSitter))
{
//Debug
//SendMessageToPC(GetFirstPC(), "Testando Sitting PC size 0");

//Old code: Change #1
//AssignCommand(oSitter, ActionJumpToLocation(lChair));
//AssignCommand(oSitter, ActionPlayCustomAnimation(oSitter, "sitidle", 1));
//NewCode: Change #1
AssignCommand(oSitter, JumpToLocation(lChair));
PlayCustomAnimation(oSitter,"sitidle",1);
//End Change #1
SetLocalObject(oChair, "lastsitted", oSitter);
}
}
else
{
if(sAutofit != "")
{
AssignCommand(oChair, SetIsDestroyable(TRUE, FALSE, FALSE));
AssignCommand(oSitter, DestroyObject(oChair));
oChair = CreateObject(OBJECT_TYPE_PLACEABLE, "pat_low_" + sAutofit, lChair_o, FALSE, sChair);
if(!(GetIsObjectValid(oChair)))
oChair = CreateObject(OBJECT_TYPE_PLACEABLE, "pat_low_stool01", lChair_o, FALSE, sChair);
//Old code: Change #1
//AssignCommand(oSitter, ActionJumpToLocation(lChair));
//AssignCommand(oSitter, ActionPlayCustomAnimation(oSitter, "sitidle", 1))
//NewCode: Change #1
AssignCommand(oSitter, JumpToLocation(lChair));
PlayCustomAnimation(oSitter,"sitidle",1);
//End Change #1;
SetLocalString(oChair, "autofit", sAutofit);
SetLocalInt(oChair, "degree", iHeading);
SetLocalInt(oChair, "size", 1);
SetLocalObject(oChair, "lastsitted", oSitter);
}
else
SpeakOneLinerConversation("", OBJECT_INVALID, TALKVOLUME_WHISPER);
}
break;

case 1:
//Check for Character Race with original Creature Size
if( ((GetRacialType(oSitter) == RACIAL_TYPE_DWARF) && (GetCreatureSize(oSitter) == CREATURE_SIZE_MEDIUM)) ||
((GetRacialType(oSitter) == RACIAL_TYPE_GNOME) && (GetCreatureSize(oSitter) == CREATURE_SIZE_SMALL)) ||
((GetRacialType(oSitter) == RACIAL_TYPE_HALFLING) && (GetCreatureSize(oSitter) == CREATURE_SIZE_SMALL)))
{
if(GetIsObjectValid(oChair) && GetIsObjectValid(oSitter))
{
//Old code: Change #1
//AssignCommand(oSitter, ActionJumpToLocation(lChair));
//AssignCommand(oSitter, ActionPlayCustomAnimation(oSitter, "sitidle", 1))
//NewCode: Change #1
AssignCommand(oSitter, JumpToLocation(lChair));
PlayCustomAnimation(oSitter,"sitidle",1);
//End Change #1;
AssignCommand(oSitter, ActionJumpToLocation(lChair));
PlayCustomAnimation(oSitter,"sitidle",1);
SetLocalObject(oChair, "lastsitted", oSitter);
}
}
else
{
if(sAutofit != "")
{
AssignCommand(oChair, SetIsDestroyable(TRUE, FALSE, FALSE));
AssignCommand(oSitter, DestroyObject(oChair));
oChair = CreateObject(OBJECT_TYPE_PLACEABLE, "pat_mid_" + sAutofit, lChair_o, FALSE, sChair);
if(!(GetIsObjectValid(oChair)))
oChair = CreateObject(OBJECT_TYPE_PLACEABLE, "pat_mid_stool01", lChair_o, FALSE, sChair);
//Old code: Change #1
//AssignCommand(oSitter, ActionJumpToLocation(lChair));
//AssignCommand(oSitter, ActionPlayCustomAnimation(oSitter, "sitidle", 1));
//NewCode: Change #1
AssignCommand(oSitter, JumpToLocation(lChair));
PlayCustomAnimation(oSitter,"sitidle",1);
//End Change #1
SetLocalString(oChair, "autofit", sAutofit);
SetLocalInt(oChair, "degree", iHeading);
SetLocalInt(oChair, "size", 0);
SetLocalObject(oChair, "lastsitted", oSitter);
}
else
SpeakOneLinerConversation("", OBJECT_INVALID, TALKVOLUME_WHISPER);
}
break;

default:
//Character with no original Race and/or Creature size
SpeakOneLinerConversation("", OBJECT_INVALID, TALKVOLUME_WHISPER);
break;
}
}
}

Por fim, faltava apenas um problema. O personagem atravessava o banco e isso parecia meio estranho. Eu então apenas adicionei um CollisionBox, mudei seu tamanho para 1;0,02;1 e posicionei no encosto do banco.

Se quiser, você pode baixar o Prefab aqui. Basta copiar o arquivo para a pasta Override e reiniciar o toolset. Ele aparecerá lá em BluePrints -> Prefabs.

Você vai notar que ele esta sem nome. Na verdade, o que era pra ser o nome está em tag, e o nome ficou em branco. Ainda não consegui resolver esse bug.

Agora uma ScreenShot de como ficou:





Aqui um erf apenas com o SitBox do Patcha ja com a nova script.
BlogBlogs.Com.Br