Aprofundamento em Aurora Toolset #2 - Entendendo o NWscript
Introdução Eu sei que isso acontece, e já sofri com isso quando comecei a programar no nwscript. Mas como eu já sabia lógica de programação, foi apenas preciso entender a estrutura de programação do nwscript, que deriva da linguagem C++. Assim, esse artigo transcorrerá sobre cinco assuntos que qualquer um deve dominar quando começar a mexer com scripts: a Lógica e a Estrutura do NWScript; Eventos; Action, Set e Get; AssignCommand, DelayCommand e ActionDoCommand; Varáveis Locais e Persistentes.
1. A Lógica e a Estrutura do NWScript Estrutura é a organização de um script, a divisão dele. Vamos lá. Começaremos com algo simples e imaginário. Queremos um 'script' que pegue a primeira fruta em uma sacola, e se for uma maçã a coloque no cesto A e se for outra fruta não faz nada. Pense em como poderíamos escrever esse script em 'português'. Ficaria mais ou menos assim:
Simples, não é. Agora vamos aumentar a complexidade desse 'script'. Queremos que o script pegue a primeira fruta da sacola e cheque. Se for uma maçã, coloca no cesto A. Se for uma banana, coloca no cesto B. Se for outra fruta coloca no cesto C. Teremos mais ou menos isto:
Isso é que chamamos Lógica de Programação. Essa lógica é universal, independe da linguagem que estamos trabalhando. Este é o primeiro passo para se produzir um script no nwscript: pense nele através dessa lógica. O segundo passo é a estrutura de programação. Imagine se no exemplo anterior invertêssemos as linhas 2 e 3, o 'script' simplesmente não funcionaria. Dentro da estrutura precisamos aprender 3 coisas: declaração de variáveis, condicionais e loops. Declaração de variáveis é aonde dizemos ao script com que 'objetos' estamos trabalhando dentro do script. No caso do exemplo anterior precisamos dizer ao script que estamos trabalhando com dois objetos: 'sacola' e 'fruta'. Dentro do nwscript temos vários tipos de variáveis, e não pretendo entrar em detalhes. Temos os seguintes tipos de variáveis:
Existem outros tipos como effect e vector, menos relevantes no momento. O fato é que este é um dos passos mais importantes dentro do script. No exemplo anterior poderíamos ter algo como:
Veja que atribuímos um 'valor' para o objeto oFruta, ao dizermos que este objeto é o primeiro objeto que o script encontrar dentro do objeto oSacola. Isso ficará mais claro mais pra frente com exemplos reais de scripts usados no NwN. Condicionais são os famosos 'se' e 'se não' que aparecem dentro da lógica de programação. Dentro do nwscript temos três comandos: if, else if e else. Estes comandos seguem a estrutura:
Que traduzidos para o 'português' seria algo como: se condiçãoA for verdadeira, executa o comando1. O if também é encontrado muitas vezes sozinho dentro do script, ou acompanhado com o else. Temos três opções de comandos: if sozinho, if com else, if com else if e else. Também podemos executar multiplos comandos, colocando-os entre chaves:
Dentro da condição podemos fazer as chamadas operações de comparação, junto com operações boleanas. Operações de comparação são operações aonde comparamos dois 'valores', checando se eles são iguais (==), diferentes (!=), A maior que B (A > B), A menor que B (A < B), A maior ou igual a B (A >= B), A menor ou igual a B (A <= B). Operações boleanas são operações aonde testamos a validade de mais de um comando ao mesmo tempo, usando os operadores 'e' (&&) e 'ou' (||). Vamos incrementar um pouco mais nosso exemplo, dizendo que maçãs ou laranjas vão para o cesto A. Bananas verdes vão para o cesto B. Bananas maduras para o cesto C. O resto para o cesto D.
Perceba a utilização da estrutura if, else if, else, dos operadores lógicos e dos operadores booleanos. Vamos agora tratar dos loops. Loops são a causa de muitos erros em scripts, simplesmente porque podemos cair em loops que nunca terminam. Temos três comandos que executam loops: while, for e do while. Trataremos somente do while, porque ele é o mais usado dentro do nwscript, e o mais simples também. Um loop executa uma tarefa enquanto uma certa condição for verdadeira, seguindo a estrutura:
Muito simples esta estrutura. Aqui também funciona o uso de chaves para a execução de múltiplos comandos. Continuando nosso exemplo das frutas, vamos finalizá-lo, dizendo agora para o script que queremos checar todas as frutas dentro da sacola. Nosso 'script' será mais ou menos isso:
Repare que nossa condição dentro do loop é que execute os comandos if...else enquanto o objeto oFruta for um objeto válido, ou seja, enquanto existir uma fruta dentro da sacola. Para finalizar a primeira parte deste artigo, seguiremos com um exemplo real, passo-a-passo de um script do nwn. Queremos que quando um jogador entre em uma área, que nosso script conte quantas espadas longas o jogador possui no inventário e armazene esse número em uma variável, e se esse número for maior que 3 e menor que 10, armazene a palavra 'espada' em outra variável. Não se preocupe em entender os comandos, preste atenção somente na lógica e na estrutura.
Aí está nosso script, um exemplo dentro do nwscript. Reitero aqui que você deve apenas se preocupar em entender a lógica e estrutura dele. As barras duplas (//) servem para inserir pequenos comentários dentro dos scripts, para que você e outras pessoas possam entendê-lo futuramente. Recomendo que você sempre comente as passagens mais importantes de seu script.
2. Eventos Saber trabalhar com estes eventos é extremamente importante para a customização de seu módulo. É com estes eventos que podemos fazer sistema de rest, de death, de respawn, etc. Não é necessária um detalhamento do que cada evento faz, pois o próprio nome dele já indica isso. Assim, se você deseja ativar um script quando o jogador entra em determinada área, você irá usar o evento OnEnter da área. Repare que o evento OnEnter também existe para o Módulo e para Triggers. Não confundi-los é extremamente importante. Um script no OnEnter do módulo só irá ser executado quando um jogador entrar no módulo, enquanto o jogador permanecer no módulo o script não será mais executado. Preste muita atenção se você não está cometendo erros deste tipo. Pra finalizar, uma dica que não pode ser deixada para trás. Minimize o máximo que puder a utilização do evento OnHeartBeat. Este evento é disparado a cada 6 segundos. Se você deixar o script dele muito pesado, este script irá demandar um processamento muito grande. A utilização de scripts grandes nos eventos do tipo OnHeartBeat são a maior causa do famoso 'lag' nos módulos (não só em servidores, módulos single player podem ficar extremamente lentos também).
3. Action, Set e Get Primeiro vou falar um pouco dos tipos Set e Get, que são os mais fáceis de entender. As funções iniciadas por Set servem para mudar algo. Assim se você quiser mudar o clima da área você irá usar SetWeather, se você quer trancar uma porta ou placeable, você irá usar SetLocked, se quiser alterar o horário use SetTime, e assim por diante. As funções iniciadas por Get são usadas para retornar um valor. Assim se quiser saber se uma criatura é um jogador, você irá usar GetIsPC, que irá retornar TRUE ou FALSE. Se quiser saber a divindade do personagem, irá usar GetDeity, etc. Aqui cabe uma dica rápida: sempre que quiser saber o nível total do player use GetHitDice. Agora as funções iniciadas por Action. Estas funções dizem a um objeto qualquer (criatura, porta, placeable, área, etc) que uma ação deve ser executada. Porém esta ação não será executada imediatamente. Se o objeto estiver executando outra ação no momento, e ação designada irá ficar 'em espera' até que ele acabe o que estava fazendo e execute a ação que você mandou. Perceba que quase todas as funções Action têm alguma função semelhante sem Action na frente. Assim qual seria a diferença entre ActionJumpToLocation e JumpToLocation? A primeira irá designar uma ação que o objeto terá que realizar, assim se o objeto estiver fazendo algo ele irá terminar a tarefa anterior para ser transportado para o local designado na função. Já a segunda função irá transportar o objeto automaticamente. Bom, mas se vocês prestarem atenção na sintaxe da função podem ver que dentro dela só podemos designar o local de destino, não podemos dizer qual o objeto que deverá ser transportado. Para esclarecer isso, entraremos no nosso próximo tópico.
4. AssignCommand, DelayCommand e ActionDoCommand Temos também a função DelayCommand. Esta função faz com que um comando seja executado após certo tempo, ótima para ser usada em scripts que precisam de coisas acontecendo em intervalos de tempo determinados. A sintaxe dela é mais ou menos assim: DelayCommand(tempo, comando). Observer que AssignCommand e DelayCommand podem ser usadas em conjunto, uma dentro da outra. Podemos usar DelayCommand(tempo, AssignCommand(objeto, comando)) ou AssignCommand(objeto, DelayCommand(tempo, comando)). A última dessas funções é a menos usada nos scripts, mas pode ser importante para algumas situações. O que ela faz é transformar um comando comum (tipo SetWeather) em uma ação. Vamos dar um exemplo. Quando o player entra na área, criamos um script para que ele ande até o meio da área, e quando ele chegar lá queremos que comece a chover. Poderia dar um grande trabalho se quiséssemos fazer usando DelayCommand. Mas podemos simplesmente criar uma lista de ações por player, assim uma ação será executada quando a outra acabar. Podemos usar a ação ActionMoveToLocation, e depois transformar SetWeather em uma ação, usando ActionDoCommand. Uma última coisa antes de terminarmos este tópico. Não se esqueça que o jogador pode cancelar ações (qualquer função com Action ou ActionDoCommand). NPCs ou objetos não podem, mas players podem. Cuidado com isso ao fazer scripts que faça com que players executem ações. Vou dar um exemplo agora, encaixando os três últimos tópicos. Vamos supor que você queira um script que, quando um player entrar em um trigger, transporte o player para outra área. O script abaixo deverá ser colocado então no evento OnEnter do trigger.
Um exemplo simples que engloba alguns dos aspectos abordados até aqui.
5. Variáveis Locais e Persistentes Usar variáveis locais é extremamente importante, principalmente em quests, para dizer se algo foi cumprido ou não. Variáveis locais são o único meio de levar uma variável de um script para outro. Variáveis persistentes são usadas quando estamos fazendo uma campanha e queremos levar informações de um módulo para outro, ou quando estamos trabalhando com um Mundo Persistente (um tipo servidor multiplayer) e queremos que as informações nunca se percam. -- Este artigo ficou maior do que eu esperava, mas acho que consegui abordar aspectos muito importantes, que 'travam' novos scripters (e mesmo scripters mais experientes) na hora de fazer um script próprio. |