terça-feira, 1 de julho de 2008

Tela de Boas Vindas

Ontem andei mexendo com a criação de GUI atravéz dos arquivos XML. Parti de um tutorial que achei na vault. Tentei criar minha própria imagem para a janela de mensagem mas ficou muito tosca, ai andei fuçando nos arquivos do próprio NeverWinter e achei uma muito legal.

Aqui está o código do meu arquivo XML:












OnLeftClick=UIButton_Input_ScreenClose() >







Enfim, era pra aparecer aí em cima, mas o Blogspot não aceita nem a tag code e nem a tag blockcode para mostrá-los corretamente. Caso queira ver como ficou meu arquivo XML, pode baixá-lo aqui no 4Shared.

E aqui esta o script que criei e adicionei ao evento OnClientEnter do módulo:

//:://////////////////////////////////////////////////
//:: sh_m_cliententer
/*
Mostra uma mensagem de bem vindo ao jogador.

Será adicionado mais funcionalidades, como apresentação, tutorial e gravar isso
na base de dados pois essa mensagem deve aparecer apenas uma vez para cada personagem.
*/
//:://////////////////////////////////////////////////
//:: Copyright (c) 2008 SubHeaven World
//:: Created By: SubHeaven
//:: Created On: 06/30/2008
//:://////////////////////////////////////////////////

void main()
{
//SendMessageToPC(GetFirstPC(), "OnClientEnter");
object oPC = GetEnteringObject();
DisplayGuiScreen(oPC, "TESTE", FALSE, "bemvindo.xml");
}

Sem esquecer que precisa da imagem também (A imagem está em formato tga e não tem como fazer upload aqui, então ela pode ser baixada aqui na minha pasta no 4Shared).

E finalmente, uma ScreenShot de como ficou no jogo:

E finalmente a #&@#@ da criatura andou.

Olááááá terráqueos.

Pois bem, tive meu primeiro final de semana de férias. Porém foram dias de explorar as catacumbas esquecidas do meu kitinete e dar uma faxina lá.

Porém, consegui fazer o bendito monstro andar. O comando WalkWayPoint parece não fazer nada. Detodas as formas que o coloquei ele não fazia meu monstro andar pelos WayPoints, eu então experimentei o comando ActionRandomWalk e funcionou perfeitamente, mas eu não estava satisfeito, então criei um script para o evento OnHeartBeat da criatura para ela mover de um WayPoint para outro, andar aleatoriamente por um tempo, mover para o próximo WayPoint, andar aleatoriamente e assim seguir o ciclo.

Aqui está o cript que criei:

//:://////////////////////////////////////////////////
//:: sh_c_heartbeat
/*
Default OnHeartbeat script for NPCs.

This script causes NPCs to perform default animations
while not otherwise engaged.

This script duplicates the behavior of the default
script and just cleans up the code and removes
redundant conditional checks.

*/
//:://////////////////////////////////////////////////
//:: Copyright (c) 2002 Floodgate Entertainment
//:: Created By: Naomi Novik
//:: Created On: 12/22/2002
//:://////////////////////////////////////////////////
// ChazM 6/20/05 if is encounter crature no longer checked - ambient anims flag set on spawn instead.
// ChazM 1/6/06 modified call to WalkWayPoints()
// ChazM 3/6/06 fixed AI level exit condition bug
// ChazM 3/23/06 added DoDebug(); no functionality changes
// SubHeaven 06/28/2008 added custom waypoint control

#include "nw_i0_generic"
#include "ginc_debug"

void DoDebug(string sMessage)
{

//if ((GetTag(OBJECT_SELF) != "3030_dwarf") && (GetTag(OBJECT_SELF) != "c_cow"))
return;
PrettyDebug("NW_C2_DEFAULT1 " + GetName(OBJECT_SELF) + ": " + sMessage);
}

void main()
{
DoDebug("I'm haveing a heartbeat!!!");
// * if not runnning normal or better Ai then exit for performance reasons
if (GetAILevel() == AI_LEVEL_VERY_LOW)
{
DoDebug("goodness gracious - i have very low AI!");
//SendMessageToPC(GetFirstPC(), "goodness gracious - i have very low AI!");
return;
}

// Buff ourselves up right away if we should
if(GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY))
{
// This will return TRUE if an enemy was within 40.0 m
// and we buffed ourselves up instantly to respond --
// simulates a spellcaster with protections enabled
// already.
if(TalentAdvancedBuff(40.0))
{
// This is a one-shot deal
SetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY, FALSE);

//SendMessageToPC(GetFirstPC(), "GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY) e TalentAdvancedBuff(40.0");

// This return means we skip sending the user-defined
// heartbeat signal in this one case.
return;
}
}


//PrettyDebug("Entering big if");
if(GetHasEffect(EFFECT_TYPE_SLEEP))
{
// If we're asleep and this is the result of sleeping
// at night, apply the floating 'z's visual effect
// every so often

if(GetSpawnInCondition(NW_FLAG_SLEEPING_AT_NIGHT))
{
effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
if(d10() > 6)
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF);
}
}
}
// If we have the 'constant' waypoints flag set, walk to the next
// waypoint.
else if ( GetWalkCondition(NW_WALK_FLAG_CONSTANT) )
{
DoDebug("Calling WalkWayPoints!");
//string sMensagem = "Calling WalkWayPoints!";
//SendMessageToPC(GetFirstPC(), sMensagem);
WalkWayPoints(TRUE, "heartbeat");
}

// Check to see if we should be playing default animations
// - make sure we don't have any current targets
else if ( !GetIsObjectValid(GetAttemptedAttackTarget())
&& !GetIsObjectValid(GetAttemptedSpellTarget())
// && !GetIsPostOrWalking())
&& !GetIsObjectValid(GetNearestSeenEnemy()))
{
DoDebug(" I'm gonna look at playing some animations.");
if (GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL) || GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE) ||
GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE))
{
// This handles special attacking/fleeing behavior
// for omnivores & herbivores.
DoDebug("I'm gonna do something special!");
DetermineSpecialBehavior();
}
else if (!IsInConversation(OBJECT_SELF))
{
if (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS)
|| GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN))
//|| GetIsEncounterCreature())
{
DoDebug("I'm gonna play some ambient animations!");
//SpawnScriptDebugger();
PlayMobileAmbientAnimations();
}
else if (GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS))
{
DoDebug("I'm gonna play some immobile ambient animations!");
PlayImmobileAmbientAnimations();
}
else
{
DoDebug("I don't have any flags telling me to play animations.");
}
}
else
{
DoDebug("I must be in a conversation!");
}
}

// Send the user-defined event signal if specified
if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT))
{
SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_HEARTBEAT));
}

//Recuperar o valor do contador global
int iContador = GetLocalInt(GetModule(), "i_hbCounter");

//Se o valor do contador é divisível por 20...
if ((iContador % 20) == 0 || GetLocalInt(OBJECT_SELF, "iWalking") == 0)
{
//Se a criatura não está em combate...
if (!GetIsInCombat(OBJECT_SELF))
{
//Recuperar a posição atual da criatura, incrementá-lo e
//procurar um Waypoint com essa numeração
int iAtual = GetLocalInt(OBJECT_SELF, "CurrWP");
iAtual = iAtual + 1;

string sLocal;

if (iAtual < 10)
{
sLocal = "WP_" + GetTag(OBJECT_SELF) + "_0" + IntToString(iAtual);
}
else
{
sLocal = "WP_" + GetTag(OBJECT_SELF) + "_" + IntToString(iAtual);
}

SendMessageToPC(GetFirstPC(), sLocal);
object oLocal = GetWaypointByTag(sLocal);

//Se não existe esse Waypoint e não é o primeiro,
//então procurar o primeiro Waypoint
if (oLocal == OBJECT_INVALID && iAtual != 1)
{
iAtual = 1;
sLocal = "WP_" + GetTag(OBJECT_SELF) + "_0" + IntToString(iAtual);
SendMessageToPC(GetFirstPC(), sLocal);
oLocal = GetWaypointByTag(sLocal);
}

//Se o local procurado é válido então...
if (oLocal != OBJECT_INVALID)
{
SendMessageToPC(GetFirstPC(), "oLocal != OBJECT_INVALID");
//Carregar o Waypoint
location lLocal = GetLocation(oLocal);

//Parar o que está fazendo
ClearAllActions();
//Ir até o Waypoint
//ActionMoveToLocation(lLocal);
ActionMoveToObject(oLocal);
//Mover-se aleatoriamente próximo ao Waypoint
ActionRandomWalk();

//Atualizar a posição atual
SetLocalInt(OBJECT_SELF, "CurrWP", iAtual);
}
//Senão, andar aleatoriamente no local
else
{
SendMessageToPC(GetFirstPC(), "oLocal == OBJECT_INVALID");
ActionRandomWalk();
}
}

SetLocalInt(OBJECT_SELF, "iWalking", 1);
}

}

Claro que o que eu realmente criei está comentado em português. Mas enfim, eu criei um contador no OnHeartBeat do módulo, para definir quando o código no OnHeartBeat da criatura será executado, no código acima, a cada 20 HeartBeat do módulo, eu executo o código no OnHeartBeat da criatura e com isso não fico fazendo os mesmos comandos a cada 6 segundos. Eu também criei duas variáveis na criatura, uma para armazenar em qual WayPoint ela está (CurrWP) e outra para indicar que a criatura já esta em movimento (iWalking). A CurrWP grava o numero do WayPoint em que a criatura está no momento, quando a criatura é criada, ela começa com valor 1 e eu então crio a tag "WP_Tag_01" e vou modificando apenas esse numero final para indicar pra onde a criatura está indo. Quando não eu não mais encontrar uma tag na sequência (Digamos que tenha criado 5 WayPoints e o programa procure o WP_tag_06) ele volta para o 1.
Como esse evento apenas ocorre a cada 20 HeartBeat do módulo, a criatura ficava estática logo depois do seu respawn, eu criei então a variável iWalking que começa com valor 0 e assim, mesmo que não tenha passado os 20 HeartBeat, se a criatura ainda não esta andando, o script executa os comandos e muda o valor de iWalking para 1, indicando que a criatura agora está andando normalmente pelo mapa. Porém, quando ele anda para outro WayPoint e executa o comando ActionRandomWalk, a criatura volta para o primeiro WayPoint e só então começa a andar aleatoriamente. Estranho... Mas pelo menos não ficou como uma estátua como antes. Depois vou tentar descobrir como resolver isso.

E pra finalizar esse post, está o script do OnHeartBeat do módulo:

// sh_m_heartbeat
/*
Script para o evento OnHeartBeat do modulo.

*/
//:://////////////////////////////////////////////////
//:: Copyright (c) 2008 SubHeaven World
//:: Created By: SubHeaven
//:: Created On: 06/28/2008
//:://////////////////////////////////////////////////

void main()
{
//Inclementar o contador de heartbeat. Esse contador serve para evitar
//que rotinas ligadas ao evento heartbeat de outros objetos sejam executadas
//a cada 6 segundos.
int iModuleCont = GetLocalInt(GetModule(), "i_hbCounter");

if (iModuleCont == 100)
iModuleCont = 1;
else
iModuleCont = iModuleCont + 1;

SetLocalInt(GetModule(), "i_hbCounter", iModuleCont);

//SendMessageToPC(GetFirstPC(), "HeartBeat " + IntToString(iModuleCont));
}

quinta-feira, 26 de junho de 2008

Combate!!!!!

Mais algumas imagens de como está ficando.























A #@#$& do lobo não anda! >.<

Um pouco de mimimi...

Ando meio sem tempo. Tive aula durante o ultimo final de semana inteiro, literalmente. É o lado ruim de ser nerd.

Essa última semana eu tentei entrar em um grupo para montar um modulo básico para servir de template para outros builders. Como sempre, apareceram um monte de gente disposta a judar, cada pessoa com uma idéia diferente (Sempre diferentes das minhas idéias) e uma meia dúzia com seus mimimis de sempre.

Minha idéia era fazer um template de PW básico com scripts principais ja configurados e mapas já feitos. Assim cada pessoa que quisesse montar seu servidor poderia pegar essa base e apenas adicionar mais coisas.

Também queria focar em mapas maiores e com menos detalhes (Apenas uma pequena parte do mapa mais trabalhado apenas para as SS dos jogadores), pois sempre prefiro ter um load maior do que ficar fazendo load a cada 3 monstros.

Independente do que saia la no grupo (Os chorões vivem dizendo que ja tem isso ou aquilo pronto mas nunca deram o link pra baixar) eu vou continuar meu projeto solo. Minha idéia é a seguinte:

Criar um mapa inicial onde seja possível jogar do lvl 1 ao 4.

Colocar algumas quests nela para ajudar no desenvolvimento do personagem.

Ajustar os scripts básicos (Morte, Banco de Dados, Respawn e loot);

E só.

Tendo isso funcional, minha próxima meta será disponibilizar o módulo e encontrar alguém para hostear o jogo e testar, pois estou sem internet em casa e a internet depende de aumento de salário.

Tendo os scripts básicos funcionando, será mais fácil adicionar mais mapas, monstros e quests (Apesar de que os diálogos são igualmente cansativos de se fazer. São divertidos no começo, mas se tornam cansativos depois de milhares de linhas escritas e outras milhares ainda por escrever).

O importante, é conseguir criar esse mapa inicial e disponibiliza-lo pra comunidade, pois assim, mesmo que eu não tenha mais tempo, outras pessoas podem continuar de onde parei e não fazer como as dezenas de projetos que começaram e nunca sairam do papel, pois seus idelizadores resolveram viver suas vidas e não disponibilizaram o que ja tinham feito.

Então antes de disponibilizar o módulo, eu preciso ter as seguintes implementações:

- 1 Mapa inicial. [Vila dos Tropeiros quase pronta. Ainda faltam mais algumas implementações, terminar as laterais e consertar a ponte]

- 3 Interiores de casas (Taverna, Celeiro e uma casa de NPC). [Nada]

- 1 Dungeon (Dungeon das aranhas gigantes). [Nada]

- Script de Respawn. [Quase pronto. Falta apenas fazer a @#$&# do monstro andar depois do respawn]

- Script de Loot. [Pronto, mas falta bolar um jeito de limitar sem bloquear por completo a habilidade de pickpocket]

- Script de banco de dados. [Ja instalei o NWNX4 e o MySQL, mas agora falta fazer as implementações para salvar no banco]

- Script de morte. [É o que mais existe. Provavelmente usarei o sistema do Defiance IV e dicionarei mais coisas no futuro, como o sistema de Fuge Death do HCR2]

- Criaturas. [Dois Blueprints prontos: Cão Selvagem e Aranhas Marrons. Precisarei de mais uma variação das aranhas e um boss]

sexta-feira, 13 de junho de 2008

Meu primeiro Script (Precisa de melhoria. Muita melhoria)

Como prometido. Aqui esta meu primeiro script. Esta funcionando perfeitamente como eu era meu objetivo inicial, mas agora quero adicionar a capacidade de diminuir a chance de drop de item a medida que a diferença de level entre o jogador e a criatura for maior.

Em todo caso aí está:

//:: sh_calculardrop
/*
Calcule the loot of creature. You can change the drop rate creating and modifing a
variable called sh_iDropRate in the item variable of type float that can be between
0.00 to 100.00 that represent the chance drop of the item (More is better) and create
a variable named sh_mDropRate in the in the module set of type float tha can be any
positive number. If sh_iDropRate don't exists, I use 0 instead (No chance to drop), if
sh_mDropRate don't exists, I use 1 instead (No modification).
*/
//:://////////////////////////////////////////////////
//:: Copyright (c) 2008 SubHeaven World
//:: Created By: SubHeaven
//:: Created On: 06/12/2008
//:://////////////////////////////////////////////////

void CalcularDrop(object oCriatura)
{
//Vejo se a criatura é válida e se não é um jogador
if (oCriatura != OBJECT_INVALID && !GetIsPC(oCriatura))
{

//Recupero o modificador de chance de drop do modulo
float lfGlobalRate = GetLocalFloat(GetModule(), "sh_mDropRate");

//Pego o primeiro item do inventário da criatura
object oItem = GetFirstItemInInventory(oCriatura);

//Faço um loop no inventário da criatura
while (oItem != OBJECT_INVALID)
{
//Pego a chance de drop do item
float lfItemRate = GetLocalFloat(oItem, "sh_iDropRate");

//Calculo um numero aleatorio de 0 a 10000
float lfChance = IntToFloat(Random(10000)) / 100;


//Verifico se a chance de drop do item está dentro dos
//valores permitidos (entre 0 e 100%)
//Se não eu ajusto para um valor padrão
if (lfItemRate > 100.0)
{
lfItemRate = 100.0;
}
else if (lfItemRate < 0.0)
{
lfItemRate = 0.0;
}

//Aqui eu verifico se o valor aleatorio que consegui
//anteriormente é maior que a chance de drop do item
//contando com o ajuste do modulo
if ((lfItemRate * lfGlobalRate) < lfChance)
DestroyObject(oItem);

//Esse aqui são apenas algumas linhas de debug
//Ele mostram os valores aletorios conseguidos
//e as chances de drop de cada item
string sMensagem = "(" + FloatToString(lfChance) + ") DC = " + FloatToString(lfItemRate * lfGlobalRate);
SendMessageToPC(GetFirstPC(), sMensagem);
SendMessageToAllDMs(sMensagem);
PrintString(sMensagem);

//Terminado com um item eu parto pro próximo item
//do inventário
oItem = GetNextItemInInventory(oCriatura);
}
}
}

quarta-feira, 11 de junho de 2008

Eu e minhas férias

Oláá terráqueos!!!

Pois é. Pensei que já estaria em semi-férias essa semana, mas férias da faculdade mesmo só depois que os alunos que ficaram de 3ª VA (Uma éspécie de "recuperação") fizerem as provas deles. Antes disso vou ter que labutar por cada minuto extra.

Mas em todo caso, ontem comecei a juntar alguns scripts que peguei na Vault. Um dos que primeiro testei foi o script Dave's Simple Respawn. Apenas dois scripts, um para o evento OnDeath da criatura e outro para o evento OnRespawn. Criei a variável Global como indicado no readme e o respawn funcionou que é uma maravilha, porém o monstro não me atacava e nem andava depois do respawn. O problema de atacar foi fácil de resolver. Eu vi que os outros BluePrints de monstros já prontos ja tinham vários eventos setados neles (Todos os slots preenchidos), então apenas exportei o kit de script de um dos BluePrints (Para fazer isso basta clicar no BluePrint e olhar na aba Preferencias, la em cima, em um menu textual vão ter duas opções: Export e Import), renomeei para sh_Hostile, importei no BluePrint da criatura que criei, mudei os eventos OnDeath e OnRespawn para continuar usando os do Dave e mantive o resto. Agora meu Cao Selvagem ataca quando vc chega perto dele, mas ainda não anda (T_T), de acordo com o Readme dos scripts, ele cria uma cópia do monstro que está no BluePrint, logo, a tag é a mesma do BluePrint.

Essa tag é fundamental para criatura conseguir andar pelos locais programados pelo builder, enquanto quando eu criava uma criatura com a tag vt_lobo1 e criava os WayPoints wp_vt_lobo1_0x ele funcionava da primeira vez, depois da primeira morte, o script criava outro lobo, mas com a tag do bluePrint (Que é apenas lobo1). Aí bagunçava tudo.

Mudei o script dele para criar uma criatura com a mesma tag da anterior. Usei a função GetTag(OBJECT_SELF) mas ele parece ter criado um logo com a mesma tag do meu mapa (?!?!). Enfim, isso ainda está pendente para quando tiver mais tempo.

Outra mudança nos scripts foi a criação do meu próprio script para drops estilo WOW ou Ragnarock. Você cria seu um item, informa a chance de drop, joga o item no inventário do blueprint da criatura e pronto. Quando a criatura for criada, meus script calcula quais daqueles itens estão disponíves ou não. Esse funcionou legal, preciso criar uma variavel em cada item chamado sh_iDropRate do tipo float e com valor de 0 a 100 e uma variável global chamada sh_mDropRate que serve como multiplicador da chance de drop. Amanhã eu trago o script e posto aqui.

terça-feira, 10 de junho de 2008

Trabalhando no meu PW

Bom... Finalmente acabaram as provas e os trabalhos da faculdade e terei mais tempo para brincar no ToolSet e tentar programar um pouco do meu jogo.

Como todo fã de MMORPG, eu estou querendo montar um PW (Persistent World). Como não cheguei a programar muita coisa no NWN 1, terei que aprender tudo do zero agora no 2.

Resolvi começar com uma típica fazenda, a Vila dos Tropeiros. Provavelmente ela será o StartPoint dos humanos (Pretendo colocar um StartPoint diferente para cada raça do jogo, como no WOW).

Segue algumas imagens de como ela está ficando:
















BlogBlogs.Com.Br