Programando NPCs com lib Jiddo

 

NPCs
Lib Jiddo

 

 Introdução

Neste tutorial estarei ensinando como programar NPCs, a parte um pouco exaustiva dos OTservers. Estarei mostrando a programação em XML e em LUA, ao modo que a biblioteca do Jiddo System nos proporciona, e qual a diferença entre as duas maneiras de programar-los.

Vamos lá!

 O que são NPCs?

A sigla NPC, no mundo do RPG, significa non playable character, traduzido "personagem não jogável". Logo,
NPC é todo personagem controlado pelo sistema e não pelo jogador, no mundo do Tibia, NPCs são aqueles com que sua 
personagem pode conversar, negociar e etc.

 Criando um NPC

Desde muito tempo, um certo Jiddo, criou um padrão para a programação de NPCs em OpenTibia. Não me pergunte o por quê, 
mas que isso facilitou a vida dos scripters, facilitou e MUITO. 

Então, comecemos a analisar esse sistema do arquivo XML que define a aparência do nosso NPC e outros tipos de parâmetros.



<?xml version="1.0" encoding="UTF-8"?>
<npc name="" script="" walkinterval="" floorchange="">
    <health now="" max=""/>
    <look type="" head="" body="" legs="" feet="" addons=""/>
</npc>

Vou fazer um dicionário com os significados de cada parâmetro encontrado no código acima, vejamos,



 
name - O nome do seu NPC
script - O caminho do sistema que se encontra o arquivo de execução do seu NPC (a partir de data/)
walkinterval - Intervalo entre cada passo que o NPC dará em milisegundos (1s = 1000ms)
floorchange - Ele pode mudar de "andar"? Ou seja, ele pode subir escadas, descer escadas...

health ;
now - Definirá os pontos de vida atuais do seu NPC
max - Definirá os pontos de vida máximos do seu NPC

look ;
type - Definirá o tipo de outfit que seu NPC terá. Vide Anexo 1
head, body, legs, feet - Definirá a cor da cabeça, do tronco, das pernas e dos pés (respectivamente) de seu NPC. Vide Anexo 2
addons - Caso a outfit ser do tipo de jogador, definirá quantos addons o NPC mostrará.

Okay, vamos configurar um NPC para o nosso tutorial,



Código:
<?xml version="1.0" encoding="UTF-8"?>
<npc name="Tutorial" script="data/npc/scripts/tutorial.lua" walkinterval="0" floorchange="0">
    <health now="150" max="150"/>
    <look type="128" head="114" body="132" legs="114" feet="115" addons="0"/>
</npc>

Criamos o NPC para o tutorial, mas uma coisa é certa, se colocarmos ele em um servidor, ele fará alguma coisa?
Então vamos estudar sobre os parâmetros de configuração!

 Parâmetros

Os parâmetros de NPC servem para configurar, de um modo rápido, como o NPC agirá quando o jogador dialogar com ele.
Existem vários parâmetros e cada um tem uma função específica: Desde configurar as mensagens que o NPC falará, até mudar os itens 
que ele venderá. Os parâmetros são configurados no próprio arquivo XML do NPC, desta maneira,



Código:
<npc name="Tutorial" script="data/npc/scripts/tutorial.lua" walkinterval="0" floorchange="0">
    <health now="150" max="150"/>
    <look type="128" head="114" body="132" legs="114" feet="115" addons="0"/>
        <parameters>
            <parameter key="" value=""/>
        </parameters>
</npc>

Para cada parâmetro que você configurar em um NPC será necessário uma nova linha dentro da tag parameters



 
key - O nome do parâmetro
value - O valor do parâmetro

Sabendo das definições de cada parte acima, vamos configurar os parâmetros do nosso NPC de tutorial, mas primeiro, quero mostrar-lhes todos os parâmetros e seus significados. (A maioria diz a respeito de mensagens)



 
message_ ;
greet - Mensagem de boas-vindas, o que o NPC falará quando o jogador falar um 'hi', um 'hello'.
farewell - Mensagem de despedida, o que o NPC falará quando o jogador falar um 'bye'.
buy - A mensagem que o NPC falará, perguntando, se o jogador quer comprar um item.
onbuy - A mensagem que o NPC falará, quando o jogador comprar um item via fala (antigo sistema).
bought - Mensagem que será falada quando o jogador comprar algo na janela de Trade.
sell - A mensagem que será falada quando o NPC perguntar se o jogador quer vender um item.
onsell - A mensagem que o NPC falará, quando o jogador vender um item via fala (antigo sistema).
sold - A mensagem que será exibida quando o jogador vender, definitivamente, um item na janela de Trade.
missingmoney - Mensagem exibida quando o jogador quer comprar algo e não tem dinheiro (antigo sistema).
needmoney - Igual ao parâmetro acima, usado na janela de Trade.
missingitem - Mensagem exibida quando o jogador quer vender um item que não tem (antigo sistema).
needitem - Igual ao parâmetro acima, usado na janela de Trade.
needspace - Mensagem exibida quando o jogador quer comprar um item e não tem nenhum espaço para guardá-lo.
needmorespace - Mensagem exibida quando o jogador quer comprar um item e tem espaço, mas não o suficiente.
idletimeout - Mensagem exibida quando o jogador não falou nada com o NPC por muito tempo.
walkaway - Mensagem exibida quando o jogador foi embora, sem encerrar a conversa.
decline - Mensagem exibida quando o jogador rejeita algo.
sendtrade - Mensagem exibida quando o NPC abre a janela de Trade para o jogador.
noshop - Mensagem exibida quando o jogador requisita uma janela de Trade que o NPC não tem.
oncloseshop - Mensagem exibida quando o jogador fecha a janela de Trade.
alreadyfocused - Quando o jogador já iniciou uma conversa com o NPC e quer iniciar a conversa denovo.
placedinqueue - Quando o jogador inicia uma conversa, mas existem outros jogadores na "frente" dele (antigo sistema).

module_shop - Parâmetro que ativa o modo de janela de Trade. 0 é desativado, 1 é ativado.
shop_buyable - Parâmetro que define os itens que serão vendidos. Você deve usar-lo da seguinte maneira:


Código:
<parameter key="shop_buyable" value="nome, ID do item, valor ; nome, ID do item, valor"/>
Separando por ponto e vírgula os diferentes itens que serão vendidos.

Certo, já temos os parâmetros essenciais para a configuração de um NPC, então vamos criar um NPC qualquer baseado no exemplo já dado.
Algumas observações que tenho que fazer:

- Use {} para negritar uma palavra em uma mensagem. Por exemplo: {}. Funciona apenas no novo sistema de NPCs.
shop_sellable funciona do mesmo jeito que o shop_buyable, mas serve para vender itens para o NPC.
shop_buyable_containers serve para vender itens em backpacks. Não sei configurá-lo no XML.



Código:
<npc name="Tutorial" script="data/npc/scripts/tutorial.lua" walkinterval="0" floorchange="0">
    <health now="150" max="150"/>
    <look type="128" head="114" body="132" legs="114" feet="115" addons="0"/>
        <parameters>
            <parameter key="message_greet" value="Hello! You want to {trade} some items with me?"/>
                <parameter key="message_farewell" value="Bye, Bye!"/>
                <parameter key="message_idletimeout" value="Hey! Are you okay? Say something!"/>
                <parameter key="message_walkway" value="..."/>
                <parameter key="module_shop" value="1"/>
                <parameter key="shop_buyable" value="raspberries,8840,10;brown bread,2691,8;ham,2671,8;carrot,2684,8;meat,2666,8;apple,2674,8;brown mushroom,2789,8;egg,2695,8"/>
        </parameters>
</npc>

Creio que terminamos a parte exaustiva do XML, vamos agora a uma parte divertida, a configuração de um NPC via LUA.

 Programando um NPC via LUA.

Eu sempre achei limitada a programação XML de um NPC, já que em LUA, você pode dar certo dinamismo, tornando o NPC mais real.
Por exemplo: Você pode criar várias mensagens de boas-vindas, relacionados com o andamento de uma missão, coisa que não pode 
ser feita em XML.

Vamos, então, estudar a programação LUA de um NPC!



Código:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)            npcHandler:onCreatureSay(cid, type, msg)        end
function onThink()                    npcHandler:onThink()                    end

Acima, consta a base de programação LUA de um NPC. Não entraremos em detalhes agora, mas ela sempre deve constar no arquivo LUA.
Vamos agora, criar um exemplo de NPC de missão,



 
_Inicio da Conversa
Tutorial = "Olá, senhor, eu estou precisando de {ajuda}."
_Jogador = "ajuda"
Tutorial = "Eu estou precisando de 5 peles de lobo para tecer uma vestimenta, mas eles me dão muito medo!!. Você poderia pegar essas peles?"

Jogador (sim/não)
sim
Tutorial = "Oh! Obrigado! Estarei esperando pelas peles."
não
Tutorial = "Oh! É uma pena, iria te recompensar..."

Certo, já temos uma base concreta para trabalhar, vamos então passar isso para a programação:



Código:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)            npcHandler:onCreatureSay(cid, type, msg)        end
function onThink()                    npcHandler:onThink()                    end

function msgCallback(cid, type, msg)
   if(not npcHandler:isFocused(cid)) then
      return false
   end

   #local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid

Você pode observar que adicionei uma função chamada msgCallback, ela funciona da seguinte maneira, quando o jogador falar
alguma coisa, o NPC responderá conforme o que o jogador disse, mas isso só acontece se você programar.

A linha marcada pelo sustenido (retire o sustenido!), serve para atribuir um valor a variável talkUser, ou seja, ao usuário que o NPC estará falando. A parte interessante é: 

"Se o sistema é CONVERSATION_DEFAULT, o talkUser sempre terá valor 0, pois, o NPC pode falar com várias pessoas (sistema de NPCs 8.4+), se não ele falará com apenas um pessoa, ou seja, talkUser terá sempre valor cid."


Certo, seguindo aquele diálogo passado acima, iremos programar o nosso NPC Tutorial.



Código:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}

function onCreatureAppear(cid)                npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)            npcHandler:onCreatureDisappear(cid)            end
function onCreatureSay(cid, type, msg)            npcHandler:onCreatureSay(cid, type, msg)        end
function onThink()                    npcHandler:onThink()                    end

function msgCallback(cid, type, msg)
   if(not npcHandler:isFocused(cid)) then
      return false
   end

   local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid

   if(msgcontains(msg, 'ajuda')) then
      npcHandler:say("Eu estou precisando de 5 peles de lobo para fazer uma vestimenta! Você poderia me ajudar?")
      talkState[talkUser] = 1
   end

   if(msgcontains(msg, 'sim') and talkState[talkUser] == 1) then
      npcHandler:say("Oh! Muito Obrigado, estarei esperando pelas peles!", cid)
      setPlayerStorageValue(cid, 5555, 1)
   elseif(msgcontains(msg, 'não') and talkState[talkUser] == 1) then
      npcHandler:say("Oh! É uma pena, iria te recompensar...", cid)
   end
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

Certo, entraram muitos elementos novos nessa programação, irei explicá-los:



 
talkState[talkUser]

talkState é uma tabela declarada logo no ínicio do bloco de um NPC, eles não possuem nenhuma função específica, apenas guardam valores para as criaturas. São eles que, de certa forma, administram os estágios de um diálogo NPC->Jogador.

setPlayerStorageValue

É uma função LUA que funciona junto com o SQL, pois edita a tabela 'players_storages' no banco de dados do servidor.
Sua função é mais do que óbvia, de modo que ela evita que um jogador possa completar quests duas vezes e etc.

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, msgCallback)

Atribui a rotina MESSAGE_DEFAULT, a programação feita em msgCallback.
A rotina relacionada aqui é a responsável pelos diálogos do NPC na programação LUA, ela que administra a execução desses diálogos.
Se você não cria um Callback entre essa rotina e a programação da função msgCallback, o NPC não irá responder nada.

npcHandler:addModule(FocusModule:new())

Não entendi muito bem qual a função disto, mas creio que serve para a criação de um módulo de diálogo jogador-NPC do novo sistema.
De modo que quando o jogador iniciar uma conversa, abra a janela NPCs no jogo.

Lembra no começo dessa explicação, que eu disse que a programação LUA tem vantagens, pois você pode criar várias mensagens de boas-vindas conforme o andamento de uma missão? Vamos fazer isso, o interessante, é que segue o mesmo exemplo da função msgCallback, porém o callback relacionado será diferente.



 
 


Prestem atenção na função onGreet, o que ela faz?
Primeiro, ela analisa qual o valor do storage do jogador, se ele for menor ou igual a -1, o NPC entenderá que o jogador NUNCA falou com ele. Do contrário, ele entenderá que já deu a missão para o jogador,

Certo, digamos, que o jogador tenha as peles e diga sim, no caso, ele deve receber uma recompensa,


 
 


Estude o bloco marcado com sustenido, é ele que faz a função de conferir se o jogador tem as peles e dar uma recompensa.
Lembre-se, LÓGICA CONDICIONAL,


 
SE O JOGADOR DISSER SIM E ESTÁGIO DE CONVERSA IGUAL A 2, ENTÃO
 
VERIFICAR SE O POSSO REMOVER 5 ITENS DE TAL ID DO JOGADOR, SE SIM, ENTÃO
AÇÕES
DO CONTRÁRIO
AÇÕES CONTRÁRIAS
FIM
SE, DO CONTRÁRIO, O JOGADOR DISSER NÃO E ESTÁGIO DA CONVERSA IGUAL A 2, ENTÃO
AÇÕES
FIM
Agora, você pode achar estranho, vamos voltar ao XML e ver como ficaria o nosso NPC.

Código:
 
<npc name="Tutorial" script="data/npc/scripts/tutorial.lua" walkinterval="0" floorchange="0">
    <health now="150" max="150"/>
    <look type="128" head="114" body="132" legs="114" feet="115" addons="0"/>
        <parameters>
            <parameter key="message_farewell" value="Tchau!"/>
                <parameter key="message_idletimeout" value="Ei, voce ta ai?"/>
                <parameter key="message_walkaway" value="mal-educado, me deixou falando!!"/>
        </parameters>
</npc>
Veja que não é necessário, criar um parâmetro para message_greet, pois ela é programada no arquivo LUA.
Terminamos.

 
Anexos

 

 Considerações

Bom, galera. Estou terminando o tutorial por aqui!

 

Pesquisar no site

© 2012 Todos os direitos reservados.