Ir para conteúdo
  • 0

NPC dando erro na hora de comprar ou vender


Tiagone

Pergunta

pessoal eu to com outro problema aqui que não consigo resolve e não acho nem um Topico ou tutorial ajudando a resolver isso,queria sabe o seguinte no NPC eu consigo cria tudo normal... mais na hr q eu vou comprar ou vender Itens novos ele não vende e nem compra é como se eles não responde-se,e se eu coloco algo q ja tenha tipo bag 1999,1998 as bags,itens de tibia,rings... ele vende e compra normal,e quando eu fasso por .lua a venda e troca da esse erro


[Error - Npc interface]
(Unknown script file)
Description:
data/npc/lib/npcsystem/modules.lua:1151: bad argument #2 to 'error' (number expe
cted, got string)
stack traceback:
[C]: in function 'error'
data/npc/lib/npcsystem/modules.lua:1151: in function 'callbackOnBuy'
data/npc/lib/npcsystem/npchandler.lua:263: in function 'processModuleCal
lback'
data/npc/lib/npcsystem/npchandler.lua:440: in function 'onBuy'
data/npc/lib/npcsystem/modules.lua:1292: in function <data/npc/lib/npcsy
stem/modules.lua:1291>

 

 

eu ja trokei a pasta do OT(lib da "data",a lib do "npc",e não para de dar esse erro

Link para o comentário
Compartilhar em outros sites

6 respostass a esta questão

Posts Recomendados

  • 0

Npchandler

 

 

-- Advanced NPC System (Created by Jiddo),
-- Modified by Talaturen.
if(NpcHandler == nil) then
-- Constant talkdelay behaviors.
TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
TALKDELAY_EVENT = 2 -- Not yet implemented
-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
-- Constant conversation behaviors.
CONVERSATION_DEFAULT = 0 -- Conversation through default window, like it was before 8.2 update.
CONVERSATION_PRIVATE = 1 -- Conversation through NPCs chat window, as of 8.2 update. (Default)
--Small Note: Private conversations also means the NPC will use multi-focus system.
-- Currently applied conversation behavior. CONVERSATION_PRIVATE is default.
NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE
-- Constant indexes for defining default messages.
MESSAGE_GREET = 1 -- When the player greets the npc.
MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.
MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.
MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.
MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.
MESSAGE_SOLD = 8 -- When the player sold something through the shop window.
MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.
MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window.
MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have.
MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.
MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item
MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space
MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.
MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.
MESSAGE_DECLINE = 17 -- When the player says no to something.
MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player
MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any
MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window
MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.
MESSAGE_PLACEDINQUEUE = 22 -- When the player has been placed in the costumer queue.
-- Constant indexes for callback functions. These are also used for module callback ids.
CALLBACK_CREATURE_APPEAR = 1
CALLBACK_CREATURE_DISAPPEAR = 2
CALLBACK_CREATURE_SAY = 3
CALLBACK_ONTHINK = 4
CALLBACK_GREET = 5
CALLBACK_FAREWELL = 6
CALLBACK_MESSAGE_DEFAULT = 7
CALLBACK_PLAYER_ENDTRADE = 8
CALLBACK_PLAYER_CLOSECHANNEL = 9
CALLBACK_ONBUY = 10
CALLBACK_ONSELL = 11
-- Addidional module callback ids
CALLBACK_MODULE_INIT = 12
CALLBACK_MODULE_RESET = 13
-- Constant strings defining the keywords to replace in the default messages.
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'
NpcHandler = {
keywordHandler = nil,
focuses = nil,
talkStart = nil,
idleTime = 90,
talkRadius = 4,
talkDelayTime = 1, -- Seconds to delay outgoing messages.
queue = nil,
talkDelay = nil,
callbackFunctions = nil,
modules = nil,
shopItems = nil, -- They must be here since ShopModule uses "static" functions
messages = {
-- These are the default replies of all npcs. They can/should be changed individually for each npc.
[MESSAGE_GREET] = 'Welcome, |PLAYERNAME|! I have been expecting you.',
[MESSAGE_FAREWELL] = 'Good bye, |PLAYERNAME|!',
[MESSAGE_BUY] = 'Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONBUY] = 'It was a pleasure doing business with you.',
[MESSAGE_BOUGHT] = 'Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
[MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONSELL] = 'Thank you for this |ITEMNAME|, |PLAYERNAME| gold.',
[MESSAGE_SOLD] = 'Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
[MESSAGE_MISSINGMONEY] = 'Sorry, you don\'t have enough money.',
[MESSAGE_NEEDMONEY] = 'You do not have enough money.',
[MESSAGE_MISSINGITEM] = 'You don\'t even have that item, |PLAYERNAME|!',
[MESSAGE_NEEDITEM] = 'You do not have this object.',
[MESSAGE_NEEDSPACE] = 'You do not have enough capacity.',
[MESSAGE_NEEDMORESPACE] = 'You do not have enough capacity for all items.',
[MESSAGE_IDLETIMEOUT] = 'Next, please!',
[MESSAGE_WALKAWAY] = 'How rude!',
[MESSAGE_DECLINE] = 'Not good enough, is it... ?',
[MESSAGE_SENDTRADE] = 'Here\'s my offer, |PLAYERNAME|. Don\'t you like it?',
[MESSAGE_NOSHOP] = 'Sorry, I\'m not offering anything.',
[MESSAGE_ONCLOSESHOP] = 'Thank you, come back when you want something more.',
[MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|! I am already talking to you...',
[MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, please wait for your turn. There are |QUEUESIZE| customers before you.'
}
}
-- Creates a new NpcHandler with an empty callbackFunction stack.
function NpcHandler:new(keywordHandler)
local obj = {}
obj.callbackFunctions = {}
obj.modules = {}
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
obj.focuses = {}
obj.talkStart = {}
else
obj.queue = Queue:new(obj)
obj.focuses = 0
obj.talkStart = 0
end
obj.talkDelay = {}
obj.keywordHandler = keywordHandler
obj.messages = {}
obj.shopItems = {}
setmetatable(obj.messages, self.messages)
self.messages.__index = self.messages
setmetatable(obj, self)
self.__index = self
return obj
end
-- Re-defines the maximum idle time allowed for a player when talking to this npc.
function NpcHandler:setMaxIdleTime(newTime)
self.idleTime = newTime
end
-- Attackes a new keyword handler to this npchandler
function NpcHandler:setKeywordHandler(newHandler)
self.keywordHandler = newHandler
end
-- Function used to change the focus of this npc.
function NpcHandler:addFocus(newFocus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(self:isFocused(newFocus)) then
return
end
table.insert(self.focuses, newFocus)
else
self.focuses = newFocus
end
self:updateFocus()
end
NpcHandler.changeFocus = NpcHandler.addFocus --"changeFocus" looks better for CONVERSATION_DEFAULT
-- Function used to verify if npc is focused to certain player
function NpcHandler:isFocused(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for k,v in pairs(self.focuses) do
if v == focus then
return true
end
end
return false
end
return (self.focuses == focus)
end
-- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
-- Should also be called whenever a new player is focused.
function NpcHandler:updateFocus()
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
doNpcSetCreatureFocus(focus)
return
end
end
doNpcSetCreatureFocus(0)
else
doNpcSetCreatureFocus(self.focuses)
end
end
-- Used when the npc should un-focus the player.
function NpcHandler:releaseFocus(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(not self:isFocused(focus)) then
return
end
local pos = nil
for k,v in pairs(self.focuses) do
if v == focus then
pos = k
end
end
table.remove(self.focuses, pos)
self.talkStart[focus] = nil
closeShopWindow(focus) --Even if it can not exist, we need to prevent it.
self:updateFocus()
else
closeShopWindow(focus)
self:changeFocus(0)
end
end
-- Returns the callback function with the specified id or nil if no such callback function exists.
function NpcHandler:getCallback(id)
local ret = nil
if(self.callbackFunctions ~= nil) then
ret = self.callbackFunctions[id]
end
return ret
end
-- Changes the callback function for the given id to callback.
function NpcHandler:setCallback(id, callback)
if(self.callbackFunctions ~= nil) then
self.callbackFunctions[id] = callback
end
end
-- Adds a module to this npchandler and inits it.
function NpcHandler:addModule(module)
if(self.modules == nil or module == nil) then
return false
end
module:init(self)
if(module.parseParameters ~= nil) then
module:parseParameters()
end
table.insert(self.modules, module)
return true
end
-- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
function NpcHandler:processModuleCallback(id, ...)
local ret = true
for i, module in pairs(self.modules) do
local tmpRet = true
if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
tmpRet = module:callbackOnCreatureAppear(unpack(arg))
elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
tmpRet = module:callbackOnCreatureDisappear(unpack(arg))
elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
tmpRet = module:callbackOnCreatureSay(unpack(arg))
elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then
tmpRet = module:callbackOnPlayerEndTrade(unpack(arg))
elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then
tmpRet = module:callbackOnPlayerCloseChannel(unpack(arg))
elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then
tmpRet = module:callbackOnBuy(unpack(arg))
elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then
tmpRet = module:callbackOnSell(unpack(arg))
elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
tmpRet = module:callbackOnThink(unpack(arg))
elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
tmpRet = module:callbackOnGreet(unpack(arg))
elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
tmpRet = module:callbackOnFarewell(unpack(arg))
elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
tmpRet = module:callbackOnMessageDefault(unpack(arg))
elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
tmpRet = module:callbackOnModuleReset(unpack(arg))
end
if(not tmpRet) then
ret = false
break
end
end
return ret
end
-- Returns the message represented by id.
function NpcHandler:getMessage(id)
local ret = nil
if(self.messages ~= nil) then
ret = self.messages[id]
end
return ret
end
-- Changes the default response message with the specified id to newMessage.
function NpcHandler:setMessage(id, newMessage)
if(self.messages ~= nil) then
self.messages[id] = newMessage
end
end
-- Translates all message tags found in msg using parseInfo
function NpcHandler:parseMessage(msg, parseInfo)
local ret = msg
for search, replace in pairs(parseInfo) do
ret = string.gsub(ret, search, replace)
end
return ret
end
-- Makes sure the npc un-focuses the currently focused player
function NpcHandler:unGreet(cid)
if(not self:isFocused(cid)) then
return
end
local callback = self:getCallback(CALLBACK_FAREWELL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_FAREWELL)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_FAREWELL)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
-- Greets a new player.
function NpcHandler:greet(cid)
if(cid ~= 0) then
local callback = self:getCallback(CALLBACK_GREET)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_GREET, cid)) then
local msg = self:getMessage(MESSAGE_GREET)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
self:addFocus(cid)
self:say(msg, cid)
end
end
end
end
-- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
function NpcHandler:onCreatureAppear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
--
end
end
end
-- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
function NpcHandler:onCreatureDisappear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
function NpcHandler:onCreatureSay(cid, class, msg)
local callback = self:getCallback(CALLBACK_CREATURE_SAY)
if(callback == nil or callback(cid, class, msg)) then
if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, class, msg)) then
if(not self:isInRange(cid)) then
return
end
if(self.keywordHandler ~= nil) then
if((self:isFocused(cid) and (class == TALKTYPE_PRIVATE_PN or NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT)) or not self:isFocused(cid)) then
local ret = self.keywordHandler:processMessage(cid, msg)
if(not ret) then
local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
if(callback ~= nil and callback(cid, class, msg)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
else
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
end
end
end
end
end
-- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.
function NpcHandler:onPlayerEndTrade(cid)
local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid, class, msg)) then
if(self:isFocused(cid)) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)
self:say(msg, cid)
end
end
end
end
-- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.
function NpcHandler:onPlayerCloseChannel(cid)
local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid, class, msg)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.
function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONBUY)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.
function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONSELL)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.
function NpcHandler:onThink()
local callback = self:getCallback(CALLBACK_ONTHINK)
if(callback == nil or callback()) then
if(NPCHANDLER_TALKDELAY == TALKDELAY_ONTHINK) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for cid, talkDelay in pairs(self.talkDelay) do
if(talkDelay.time ~= nil and talkDelay.message ~= nil and os.time() >= talkDelay.time) then
selfSay(talkDelay.message, cid)
self.talkDelay[cid] = nil
end
end
elseif(self.talkDelay.time ~= nil and self.talkDelay.message ~= nil and os.time() >= self.talkDelay.time) then
selfSay(self.talkDelay.message)
self.talkDelay.time = nil
self.talkDelay.message = nil
end
end
if(self:processModuleCallback(CALLBACK_ONTHINK)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
if(not self:isInRange(focus)) then
self:onWalkAway(focus)
elseif((os.time() - self.talkStart[focus]) > self.idleTime) then
self:unGreet(focus)
else
self:updateFocus()
end
end
end
elseif(self.focuses ~= 0) then
if(not self:isInRange(self.focuses)) then
self:onWalkAway(self.focuses)
elseif(os.time()-self.talkStart > self.idleTime) then
self:unGreet(self.focuses)
else
self:updateFocus()
end
end
end
end
end
-- Tries to greet the player with the given cid.
function NpcHandler:onGreet(cid)
if(self:isInRange(cid)) then
if(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE) then
if(not self:isFocused(cid)) then
self:greet(cid)
return
end
elseif(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT) then
if(self.focuses == 0) then
self:greet(cid)
elseif(self.focuses == cid) then
local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
else
if(not self.queue:isInQueue(cid)) then
self.queue:push(cid)
end
local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
end
end
end
end
-- Simply calls the underlying unGreet function.
function NpcHandler:onFarewell(cid)
self:unGreet(cid)
end
-- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
function NpcHandler:onWalkAway(cid)
if(self:isFocused(cid)) then
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_WALKAWAY)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
end
-- Returns true if cid is within the talkRadius of this npc.
function NpcHandler:isInRange(cid)
local distance = getDistanceTo(cid) or -1
if(distance == -1) then
return false
end
return (distance <= self.talkRadius)
end
-- Resets the npc into it's initial state (in regard of the keyrodhandler).
-- All modules are also receiving a reset call through their callbackOnModuleReset function.
function NpcHandler:resetNpc()
if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
self.keywordHandler:reset()
end
end
-- Makes the npc represented by this instance of NpcHandler say something.
-- This implements the currently set type of talkdelay.
-- shallDelay is a boolean value. If it is false, the message is not delayed. Default value is false.
function NpcHandler:say(message, focus, shallDelay)
local shallDelay = shallDelay or false
if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or not shallDelay) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
selfSay(message, focus)
return
else
selfSay(message)
return
end
end
-- TODO: Add an event handling method for delayed messages
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkDelay[focus] = {
message = message,
time = os.time() + self.talkDelayTime,
}
else
self.talkDelay = {
message = message,
time = os.time() + self.talkDelayTime
}
end
end
end

 

 

modules

 

-- Advanced NPC System (Created by Jiddo),

-- Modified by Talaturen.
-- Modified by Elf.
if(Modules == nil) then
-- Constants used to separate buying from selling.
SHOPMODULE_SELL_ITEM = 1
SHOPMODULE_BUY_ITEM = 2
SHOPMODULE_BUY_ITEM_CONTAINER = 3
-- Constants used for shop mode. Notice: addBuyableItemContainer is working on all modes
SHOPMODULE_MODE_TALK = 1 -- Old system used before Tibia 8.2: sell/buy item name
SHOPMODULE_MODE_TRADE = 2 -- Trade window system introduced in Tibia 8.2
SHOPMODULE_MODE_BOTH = 3 -- Both working at one time
-- Used in shop mode
SHOPMODULE_MODE = SHOPMODULE_MODE_BOTH
-- Constants used for outfit giving mode
OUTFITMODULE_FUNCTION_OLD = { doPlayerAddOutfit, canPlayerWearOutfit } -- lookType usage
OUTFITMODULE_FUNCTION_NEW = { doPlayerAddOutfitId, canPlayerWearOutfitId } -- OutfitId usage
-- Used in outfit module
OUTFITMODULE_FUNCTION = OUTFITMODULE_FUNCTION_NEW
if(OUTFITMODULE_FUNCTION[1] == nil or OUTFITMODULE_FUNCTION[2] == nil) then
OUTFITMODULE_FUNCTION = OUTFITMODULE_FUNCTION_OLD
end
Modules = {
parseableModules = {}
}
StdModule = {}
-- These callback function must be called with parameters.npcHandler = npcHandler in the parameters table or they will not work correctly.
-- Notice: The members of StdModule have not yet been tested. If you find any bugs, please report them to me.
-- Usage:
-- keywordHandler:addKeyword({'offer'}, StdModule.say, {npcHandler = npcHandler, text = 'I sell many powerful melee weapons.'})
function StdModule.say(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.say called without any npcHandler instance.')
end
local onlyFocus = (parameters.onlyFocus == nil or parameters.onlyFocus == true)
if(not npcHandler:isFocused(cid) and onlyFocus) then
return false
end
local parseInfo = {[TAG_PLAYERNAME] = getCreatureName(cid)}
npcHandler:say(npcHandler:parseMessage(parameters.text or parameters.message, parseInfo), cid, parameters.publicize and true)
if(parameters.reset == true) then
npcHandler:resetNpc()
elseif(parameters.moveup ~= nil and type(parameters.moveup) == 'number') then
npcHandler.keywordHandler:moveUp(parameters.moveup)
end
return true
end
--Usage:
-- local node1 = keywordHandler:addKeyword({'promot'}, StdModule.say, {npcHandler = npcHandler, text = 'I can promote you for 20000 brozne coins. Do you want me to promote you?'})
-- node1:addChildKeyword({'yes'}, StdModule.promotePlayer, {npcHandler = npcHandler, cost = 20000, promotion = 1, level = 20}, text = 'Congratulations! You are now promoted.')
-- node1:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, text = 'Alright then, come back when you are ready.'}, reset = true)
function StdModule.promotePlayer(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.promotePlayer called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('premiumForPromotion')) or not(parameters.premium)) then
if(getPlayerPromotionLevel(cid) >= parameters.promotion) then
npcHandler:say('You are already promoted!', cid)
elseif(getPlayerLevel(cid) < parameters.level) then
npcHandler:say('I am sorry, but I can only promote you once you have reached level ' .. parameters.level .. '.', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then
npcHandler:say('You do not have enough money!', cid)
else
setPlayerPromotionLevel(cid, parameters.promotion)
npcHandler:say(parameters.text, cid)
end
else
npcHandler:say("You need a premium account in order to get promoted.", cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.learnSpell(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.learnSpell called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not(parameters.premium)) then
if(getPlayerLearnedInstantSpell(cid, parameters.spellName)) then
npcHandler:say('You already know this spell.', cid)
elseif(getPlayerLevel(cid) < parameters.level) then
npcHandler:say('You need to obtain a level of ' .. parameters.level .. ' or higher to be able to learn ' .. parameters.spellName .. '.', cid)
elseif(not parameters.vocation(cid)) then
npcHandler:say('This spell is not for your vocation', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.price)) then
npcHandler:say('You do not have enough money, this spell costs ' .. parameters.price .. ' gold coins.', cid)
else
npcHandler:say('You have learned ' .. parameters.spellName .. '.', cid)
playerLearnInstantSpell(cid, parameters.spellName)
end
else
npcHandler:say('You need a premium account in order to buy ' .. parameters.spellName .. '.', cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.bless(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.bless called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('blessingsOnlyPremium')) or not parameters.premium) then
local price = parameters.baseCost
if(getPlayerLevel(cid) > parameters.startLevel) then
price = (price + ((math.min(parameters.endLevel, getPlayerLevel(cid)) - parameters.startLevel) * parameters.levelCost))
end
if(getPlayerBlessing(cid, parameters.number)) then
npcHandler:say("Gods have already blessed you with this blessing!", cid)
elseif(not doPlayerRemoveMoney(cid, price)) then
npcHandler:say("You don't have enough money for blessing.", cid)
else
npcHandler:say("You have been blessed by one of the five gods!", cid)
doPlayerAddBlessing(cid, parameters.number)
end
else
npcHandler:say('You need a premium account in order to be blessed.', cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.travel(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.travel called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
local storage, pzLocked = parameters.storageValue or (EMPTY_STORAGE + 1), parameters.allowLocked or false
if(parameters.premium and not isPlayerPremiumCallback(cid)) then
npcHandler:say('I can only allow premium players to travel with me.', cid)
elseif(parameters.level ~= nil and getPlayerLevel(cid) < parameters.level) then
npcHandler:say('You must reach level ' .. parameters.level .. ' before I can let you go there.', cid)
elseif(parameters.storageId ~= nil and getPlayerStorageValue(cid, parameters.storageId) < storage) then
npcHandler:say(parameters.storageInfo or 'You may not travel there!', cid)
elseif(not pzLocked and isPlayerPzLocked(cid)) then
npcHandler:say('Get out of there with this blood!', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then
npcHandler:say('You do not have enough money.', cid)
else
npcHandler:say('It was a pleasure doing business with you.', cid)
npcHandler:releaseFocus(cid)
doTeleportThing(cid, parameters.destination, false)
doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT)
end
npcHandler:resetNpc()
return true
end
FocusModule = {
npcHandler = nil
}
-- Creates a new instance of FocusModule without an associated NpcHandler.
function FocusModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Inits the module and associates handler to it.
function FocusModule:init(handler)
self.npcHandler = handler
for i, word in pairs(FOCUS_GREETWORDS) do
local obj = {}
table.insert(obj, word)
obj.callback = FOCUS_GREETWORDS.callback or FocusModule.messageMatcher
handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self})
end
for i, word in pairs(FOCUS_FAREWELLWORDS) do
local obj = {}
table.insert(obj, word)
obj.callback = FOCUS_FAREWELLWORDS.callback or FocusModule.messageMatcher
handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self})
end
end
-- Greeting callback function.
function FocusModule.onGreet(cid, message, keywords, parameters)
parameters.module.npcHandler:onGreet(cid)
return true
end
-- UnGreeting callback function.
function FocusModule.onFarewell(cid, message, keywords, parameters)
if(not parameters.module.npcHandler:isFocused(cid)) then
return false
end
parameters.module.npcHandler:onFarewell(cid)
return true
end
-- Custom message matching callback function for greeting messages.
function FocusModule.messageMatcher(keywords, message)
local spectators = getSpectators(getCreaturePosition(getNpcId()), 7, 7)
for i, word in pairs(keywords) do
if(type(word) == 'string') then
if(string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]')) then
if(string.find(message, getCreatureName(getNpcId()))) then
return true
end
for i, uid in ipairs(spectators) do
if(string.find(message, getCreatureName(uid))) then
return false
end
end
return true
end
end
end
return false
end
KeywordModule = {
npcHandler = nil
}
-- Add it to the parseable module list.
Modules.parseableModules['module_keywords'] = KeywordModule
function KeywordModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function KeywordModule:init(handler)
self.npcHandler = handler
end
-- Parses all known parameters.
function KeywordModule:parseParameters()
local ret = NpcSystem.getParameter('keywords')
if(ret ~= nil) then
self:parseKeywords(ret)
end
end
function KeywordModule:parseKeywords(data)
local n = 1
for keys in string.gmatch(data, '[^;]+') do
local i = 1
local keywords = {}
for temp in string.gmatch(keys, '[^,]+') do
table.insert(keywords, temp)
i = i + 1
end
if(i ~= 1) then
local reply = NpcSystem.getParameter('keyword_reply' .. n)
if(reply ~= nil) then
self:addKeyword(keywords, reply)
else
print('[Warning] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...')
end
else
print('[Warning] NpcSystem:', 'No keywords found for keyword set #' .. n .. '. Skipping...')
end
n = n + 1
end
end
function KeywordModule:addKeyword(keywords, reply)
self.npcHandler.keywordHandler:addKeyword(keywords, StdModule.say, {npcHandler = self.npcHandler, onlyFocus = true, text = reply, reset = true})
end
TravelModule = {
npcHandler = nil,
destinations = nil,
yesNode = nil,
noNode = nil,
}
-- Add it to the parseable module list.
Modules.parseableModules['module_travel'] = TravelModule
function TravelModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function TravelModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, TravelModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, TravelModule.onDecline, {module = self})
self.destinations = {}
end
-- Parses all known parameters.
function TravelModule:parseParameters()
local ret = NpcSystem.getParameter('travel_destinations')
if(ret ~= nil) then
self:parseDestinations(ret)
for _, word in ipairs({'destination', 'list', 'where', 'travel'}) do
self.npcHandler.keywordHandler:addKeyword({word}, TravelModule.listDestinations, {module = self})
end
end
end
function TravelModule:parseDestinations(data)
for destination in string.gmatch(data, '[^;]+') do
local i, name, pos, cost, premium, level, storage = 1, nil, {x = nil, y = nil, z = nil}, nil, false
for tmp in string.gmatch(destination, '[^,]+') do
if(i == 1) then
name = tmp
elseif(i == 2) then
pos.x = tonumber(tmp)
elseif(i == 3) then
pos.y = tonumber(tmp)
elseif(i == 4) then
pos.z = tonumber(tmp)
elseif(i == 5) then
cost = tonumber(tmp)
elseif(i == 6) then
premium = getBooleanFromString(tmp)
else
print('[Warning] NpcSystem:', 'Unknown parameter found in travel destination parameter.', tmp, destination)
end
i = i + 1
end
if(name ~= nil and pos.x ~= nil and pos.y ~= nil and pos.z ~= nil and cost ~= nil) then
self:addDestination(name, pos, cost, premium)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for travel destination:', name, pos, cost, premium)
end
end
end
function TravelModule:addDestination(name, position, price, premium)
table.insert(self.destinations, name)
local parameters = {
cost = price,
destination = position,
premium = premium,
module = self
}
local keywords, bringwords = {}, {}
table.insert(keywords, name)
table.insert(bringwords, 'bring me to ' .. name)
self.npcHandler.keywordHandler:addKeyword(bringwords, TravelModule.bring, parameters)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, TravelModule.travel, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
function TravelModule.travel(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say('Do you want to travel to ' .. keywords[1] .. ' for ' .. parameters.cost .. ' gold coins?', cid)
return true
end
function TravelModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parent = node:getParent():getParameters()
if(isPlayerPremiumCallback(cid) or not parent.premium) then
if(not isPlayerPzLocked(cid)) then
if(doPlayerRemoveMoney(cid, parent.cost)) then
module.npcHandler:say('It was a pleasure doing business with you.', cid)
module.npcHandler:releaseFocus(cid)
doTeleportThing(cid, parent.destination, true)
doSendMagicEffect(parent.destination, CONST_ME_TELEPORT)
else
module.npcHandler:say('You do not have enough money.', cid)
end
else
module.npcHandler:say('Get out of there with this blood!', cid)
end
else
modulenpcHandler:say('I can only allow premium players to travel there.', cid)
end
module.npcHandler:resetNpc()
return true
end
-- onDecline keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function TravelModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid)
module.npcHandler:resetNpc()
return true
end
function TravelModule.bring(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
if((isPlayerPremiumCallback(cid) or not parameters.premium) and not isPlayerPzLocked(cid) and doPlayerRemoveMoney(cid, parameters.cost)) then
module.npcHandler:say('Sure!', cid)
module.npcHandler:releaseFocus(cid)
doTeleportThing(cid, parameters.destination, false)
doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT)
end
module.npcHandler:releaseFocus(cid)
return true
end
function TravelModule.listDestinations(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local msg = nil
for _, destination in ipairs(module.destinations) do
if(msg ~= nil) then
msg = msg .. ", "
else
msg = ""
end
msg = msg .. "{" .. destination .. "}"
end
module.npcHandler:say(msg .. ".", cid)
module.npcHandler:resetNpc()
return true
end
OutfitModule = {
npcHandler = nil,
outfits = nil,
yesNode = nil,
noNode = nil,
}
-- Add it to the parseable module list.
Modules.parseableModules['module_outfit'] = OutfitModule
function OutfitModule:new()
if(OUTFITMODULE_FUNCTION[1] == nil or OUTFITMODULE_FUNCTION[2] == nil) then
return nil
end
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function OutfitModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, OutfitModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, OutfitModule.onDecline, {module = self})
self.outfits = {}
end
-- Parses all known parameters.
function OutfitModule:parseParameters()
local ret = NpcSystem.getParameter('outfits')
if(ret ~= nil) then
self:parseKeywords(ret)
for _, word in ipairs({'outfits', 'addons'}) do
self.npcHandler.keywordHandler:addKeyword({word}, OutfitModule.listOutfits, {module = self})
end
end
end
function OutfitModule:parseKeywords(data)
local n = 1
for outfit in string.gmatch(data, '[^;]+') do
local i, keywords = 1, {}
for tmp in string.gmatch(outfit, '[^,]+') do
table.insert(keywords, tmp)
i = i + 1
end
if(i > 0) then
local ret = NpcSystem.getParameter('outfit' .. n)
if(ret ~= nil) then
self:parseList(keywords, ret)
else
print('[Warning] NpcSystem:', 'Missing \'outfit' .. n .. '\' parameter, skipping...')
end
else
print('[Warning] NpcSystem:', 'No keywords found for outfit set #' .. n .. ', skipping...')
end
n = n + 1
end
end
function OutfitModule:parseList(keywords, data)
local outfit, items = nil, {}
for list in string.gmatch(data, '[^;]+') do
local a, b, c, d, e = nil, nil, nil, nil, 1
for tmp in string.gmatch(list, '[^,]+') do
if(e == 1) then
a = tmp
elseif(e == 2) then
b = tmp
elseif(e == 3) then
c = tmp
elseif(e == 4) then
d = tmp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in outfit list while parsing ' .. (outfit == nil and 'outfit' or 'item') .. '.', tmp, list)
end
e = e + 1
end
if(outfit == nil) then
outfit = {tonumber(a), tonumber(b), getBooleanFromString©, d}
elseif(a ~= nil) then
local tmp = tonumber(a)
if((tmp ~= nil or tostring(a) == "money") and b ~= nil and c ~= nil) then
a = tmp or 20000
tmp = tonumber(d)
if(tmp == nil) then
tmp = -1
end
items[a] = {b, tmp, c}
else
print('[Warning] NpcSystem:', 'Missing parameter(s) for outfit items.', b, c, d)
end
else
print('[Warning] NpcSystem:', 'Missing base parameter for outfit items.', a)
end
end
if(type(outfit) == 'table') then
local tmp = true
for i = 1, 2 do
if(outfit == nil) then
tmp = false
break
end
end
if(tmp and table.maxn(items) > 0) then
self:addOutfit(keywords, outfit, items)
else
print('[Warning] NpcSystem:', 'Invalid outfit, addon or empty items pool.', data)
end
end
end
function OutfitModule:addOutfit(keywords, outfit, items)
table.insert(self.outfits, keywords[1])
local parameters = {
outfit = outfit[1],
addon = outfit[2],
premium = outfit[3],
gender = nil,
items = items,
module = self
}
if(outfit[4] ~= nil) then
local tmp = string.lower(tostring(outfit[5]))
if(tmp == 'male' or tmp == '1') then
parameters.gender = 1
elseif(tmp == 'female' or tmp == '0') then
parameters.gender = 0
end
end
for i, name in pairs(keywords) do
local words = {}
table.insert(words, name)
local node = self.npcHandler.keywordHandler:addKeyword(words, OutfitModule.obtain, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
function OutfitModule.obtain(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local i, items, size = 0, nil, table.maxn(parameters.items)
for k, v in pairs(parameters.items) do
if(v[1] ~= "storageset") then
i = i + 1
if(items ~= nil) then
if(i == size) then
items = items .. " and "
else
items = items .. ", "
end
else
items = ""
end
if(tonumber(v[1]) ~= nil and tonumber(v[1]) > 1) then
items = items .. v[1] .. " "
end
items = items .. v[3]
end
end
module.npcHandler:say('Do you want ' .. keywords[1] .. ' ' .. (addon == 0 and "outfit" or "addon") .. ' for ' .. items .. '?', cid)
return true
end
function OutfitModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parent = node:getParent():getParameters()
if(isPlayerPremiumCallback(cid) or not parent.premium) then
if(not OUTFITMODULE_FUNCTION[2](cid, parent.outfit, parent.addon)) then
if(parent.addon == 0 or OUTFITMODULE_FUNCTION[2](cid, parent.outfit)) then
if(parent.gender == nil or parent.gender == getPlayerSex(cid)) then
local found = true
for k, v in pairs(parent.items) do
local tmp = tonumber(v[1])
if(tmp == nil) then
if(v[1] == "storagecheck") then
if(getCreatureStorage(cid, k) < v[2]) then
found = false
end
elseif(v[1] == "outfitid") then
if(not canPlayerWearOutfitId(cid, k, v[2])) then
found = false
end
elseif(v[1] == "outfit") then
if(not canPlayerWearOutfit(cid, k, v[2])) then
found = false
end
else
found = false
end
elseif(k == 20000) then
if(getPlayerMoney(cid) < tmp) then
found = false
end
elseif(getPlayerItemCount(cid, k, v[2]) < tmp) then
found = false
end
if(not found) then
break
end
end
if(found) then
for k, v in pairs(parent.items) do
if(tonumber(v[1]) ~= nil) then
if(k == 20000) then
doPlayerRemoveMoney(cid, v[1])
else
doPlayerRemoveItem(cid, k, v[1], v[2])
end
elseif(v[1] == "storageset") then
doCreatureSetStorage(cid, k, v[2])
end
end
module.npcHandler:say('It was a pleasure to dress you.', cid)
OUTFITMODULE_FUNCTION[1](cid, parent.outfit, parent.addon)
doPlayerSetStorageValue(cid, parent.storageId, storage)
else
module.npcHandler:say('You don\'t have these items!', cid)
end
else
module.npcHandler:say('Sorry, this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. ' is not for your gender.', cid)
end
else
module.npcHandler:say('I will not dress you with addon of outfit you cannot wear!', cid)
end
else
module.npcHandler:say('You alrady have this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. '!', cid)
end
else
module.npcHandler:say('Sorry, I dress only premium players.', cid)
end
module.npcHandler:resetNpc()
return true
end
-- onDecline keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function OutfitModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid)
module.npcHandler:resetNpc()
return true
end
function OutfitModule.listOutfits(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local msg, size = nil, table.maxn(module.outfits)
if(size > 0) then
for i, outfit in ipairs(module.outfits) do
if(msg ~= nil) then
if(i == size) then
msg = msg .. " and "
else
msg = msg .. ", "
end
else
msg = "I can dress you into "
end
msg = msg .. "{" .. outfit .. "}"
end
else
msg = "Sorry, I have nothing to offer right now."
end
module.npcHandler:say(msg .. ".", cid)
module.npcHandler:resetNpc()
return true
end
ShopModule = {
npcHandler = nil,
yesNode = nil,
noNode = nil,
noText = '',
maxCount = 100,
amount = 0
}
-- Add it to the parseable module list.
Modules.parseableModules['module_shop'] = ShopModule
-- Creates a new instance of ShopModule
function ShopModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Parses all known parameters.
function ShopModule:parseParameters()
local ret = NpcSystem.getParameter('shop_buyable')
if(ret ~= nil) then
self:parseBuyable(ret)
end
local ret = NpcSystem.getParameter('shop_sellable')
if(ret ~= nil) then
self:parseSellable(ret)
end
local ret = NpcSystem.getParameter('shop_buyable_containers')
if(ret ~= nil) then
self:parseBuyableContainers(ret)
end
end
-- Parse a string contaning a set of buyable items.
function ShopModule:parseBuyable(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
cost = tonumber(temp)
elseif(i == 4) then
subType = tonumber(temp)
elseif(i == 5) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item)
end
i = i + 1
end
if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
if(itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
self:addBuyableItem(nil, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
end
elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
local names = {}
table.insert(names, name)
self:addBuyableItem(names, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost)
end
end
end
-- Parse a string contaning a set of sellable items.
function ShopModule:parseSellable(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, itemid, cost, realName = 1, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
cost = tonumber(temp)
elseif(i == 4) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in sellable items parameter.', temp, item)
end
i = i + 1
end
if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
if(itemid ~= nil and cost ~= nil) then
self:addSellableItem(nil, itemid, cost, realName)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
end
elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
local names = {}
table.insert(names, name)
self:addSellableItem(names, itemid, cost, realName)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost)
end
end
end
-- Parse a string contaning a set of buyable items.
function ShopModule:parseBuyableContainers(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, container, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
itemid = tonumber(temp)
elseif(i == 4) then
cost = tonumber(temp)
elseif(i == 5) then
subType = tonumber(temp)
elseif(i == 6) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item)
end
i = i + 1
end
if(name ~= nil and container ~= nil and itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
local names = {}
table.insert(names, name)
self:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, container, itemid, cost)
end
end
end
-- Initializes the module and associates handler to it.
function ShopModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, ShopModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, ShopModule.onDecline, {module = self})
self.noText = handler:getMessage(MESSAGE_DECLINE)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
for i, word in pairs(SHOP_TRADEREQUEST) do
local obj = {}
table.insert(obj, word)
obj.callback = SHOP_TRADEREQUEST.callback or ShopModule.messageMatcher
handler.keywordHandler:addKeyword(obj, ShopModule.requestTrade, {module = self})
end
end
end
-- Custom message matching callback function for requesting trade messages.
function ShopModule.messageMatcher(keywords, message)
for i, word in pairs(keywords) do
if(type(word) == 'string' and string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]')) then
return true
end
end
return false
end
-- Resets the module-specific variables.
function ShopModule:reset()
self.amount = 0
end
-- Function used to match a number value from a string.
function ShopModule:getCount(message)
local ret, b, e = 1, string.find(message, PATTERN_COUNT)
if(b ~= nil and e ~= nil) then
ret = tonumber(string.sub(message, b, e))
end
return math.max(1, math.min(self.maxCount, ret))
end
-- Adds a new buyable item.
-- names = A table containing one or more strings of alternative names to this item. Used only for old buy/sell system.
-- itemid = The itemid of the buyable item
-- cost = The price of one single item
-- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addBuyableItem(names, itemid, cost, subType, realName)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
local item = {
id = itemid,
buy = cost,
sell = -1,
subType = subType or 1,
name = realName or getItemNameById(itemid)
}
for i, shopItem in ipairs(self.npcHandler.shopItems) do
if(shopItem.id == item.id and shopItem.subType == item.subType) then
if(item.sell ~= shopItem.sell) then
item.sell = shopItem.sell
end
self.npcHandler.shopItems = item
item = nil
break
end
end
if(item ~= nil) then
table.insert(self.npcHandler.shopItems, item)
end
end
if(names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE) then
local parameters = {
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_BUY_ITEM,
module = self,
realName = realName or getItemNameById(itemid),
subType = subType or 1
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'buy')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- Adds a new buyable container of items.
-- names = A table containing one or more strings of alternative names to this item.
-- container = Backpack, bag or any other itemid of container where bought items will be stored
-- itemid = The itemid of the buyable item
-- cost = The price of one single item
-- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
if(names ~= nil) then
local parameters = {
container = container,
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_BUY_ITEM_CONTAINER,
module = self,
realName = realName or getItemNameById(itemid),
subType = subType or 1
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'buy')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- Adds a new sellable item.
-- names = A table containing one or more strings of alternative names to this item. Used only by old buy/sell system.
-- itemid = The itemid of the sellable item
-- cost = The price of one single item
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addSellableItem(names, itemid, cost, realName)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
local item = {
id = itemid,
buy = -1,
sell = cost,
subType = 1,
name = realName or getItemNameById(itemid)
}
for i, shopItem in ipairs(self.npcHandler.shopItems) do
if(shopItem.id == item.id and shopItem.subType == item.subType) then
if(item.buy ~= shopItem.buy) then
item.buy = shopItem.buy
end
self.npcHandler.shopItems = item
item = nil
break
end
end
if(item ~= nil) then
table.insert(self.npcHandler.shopItems, item)
end
end
if(names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE) then
local parameters = {
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_SELL_ITEM,
module = self,
realName = realName or getItemNameById(itemid)
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'sell')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- onModuleReset callback function. Calls ShopModule:reset()
function ShopModule:callbackOnModuleReset()
self:reset()
return true
end
-- Callback onBuy() function. If you wish, you can change certain Npc to use your onBuy().
function ShopModule:callbackOnBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local shopItem = nil
for _, item in ipairs(self.npcHandler.shopItems) do
if(item.id == itemid and item.subType == subType) then
shopItem = item
break
end
end
if(shopItem == nil) then
error("[shopModule.onBuy]", "Item not found on shopItems list")
return false
end
if(shopItem.buy == -1) then
error("[shopModule.onSell]", "Attempt to purchase an item which only sellable")
return false
end
local backpack, totalCost = 1988, amount * shopItem.buy
if(inBackpacks) then
totalCost = totalCost + (math.max(1, math.floor(amount / getContainerCapById(backpack))) * 20)
end
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = amount,
[TAG_TOTALCOST] = totalCost,
[TAG_ITEMNAME] = shopItem.name
}
if(getPlayerMoney(cid) < totalCost) then
local msg = self.npcHandler:getMessage(MESSAGE_NEEDMONEY)
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
return false
end
local subType = shopItem.subType or 1
local a, b = doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, backpack)
if(a < amount) then
local msgId = MESSAGE_NEEDMORESPACE
if(a == 0) then
msgId = MESSAGE_NEEDSPACE
end
local msg = self.npcHandler:getMessage(msgId)
parseInfo[TAG_ITEMCOUNT] = a
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
if(a > 0) then
doPlayerRemoveMoney(cid, ((a * shopItem.buy) + (b * 20)))
return true
end
return false
end
local msg = self.npcHandler:getMessage(MESSAGE_BOUGHT)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo))
doPlayerRemoveMoney(cid, totalCost)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return true
end
-- Callback onSell() function. If you wish, you can change certain Npc to use your onSell().
function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local shopItem = nil
for _, item in ipairs(self.npcHandler.shopItems) do
if(item.id == itemid and item.subType == subType) then
shopItem = item
break
end
end
if(shopItem == nil) then
error("[shopModule.onBuy]", "Item not found on shopItems list")
return false
end
if(shopItem.sell == -1) then
error("[shopModule.onSell]", "Attempt to sell an item which is only buyable")
return false
end
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = amount,
[TAG_TOTALCOST] = amount * shopItem.sell,
[TAG_ITEMNAME] = shopItem.name
}
if(subType < 1 or getItemInfo(itemid).stackable) then
subType = -1
end
if(doPlayerRemoveItem(cid, itemid, amount, subType)) then
local msg = self.npcHandler:getMessage(MESSAGE_SOLD)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo))
doPlayerAddMoney(cid, amount * shopItem.sell)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return true
end
local msg = self.npcHandler:getMessage(MESSAGE_NEEDITEM)
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return false
end
-- Callback for requesting a trade window with the NPC.
function ShopModule.requestTrade(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
if(table.maxn(module.npcHandler.shopItems) == 0) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_NOSHOP), parseInfo)
module.npcHandler:say(msg, cid)
return true
end
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_SENDTRADE), parseInfo)
openShopWindow(cid, module.npcHandler.shopItems,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
module.npcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
end,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
module.npcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
end
)
module.npcHandler:say(msg, cid)
return true
end
-- onConfirm keyword callback function. Sells/buys the actual item.
function ShopModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parentParameters = node:getParent():getParameters()
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parentParameters.cost * module.amount,
[TAG_ITEMNAME] = parentParameters.realName
}
if(parentParameters.eventType == SHOPMODULE_SELL_ITEM) then
local ret = doPlayerSellItem(cid, parentParameters.itemid, module.amount, parentParameters.cost * module.amount)
if(ret) then
local msg = module.npcHandler:getMessage(MESSAGE_ONSELL)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGITEM)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM) then
local ret = doPlayerBuyItem(cid, parentParameters.itemid, module.amount, parentParameters.cost * module.amount, parentParameters.subType)
if(ret) then
if parentParameters.itemid == ITEM_PARCEL then
doPlayerBuyItem(cid, ITEM_LABEL, module.amount, 0, parentParameters.subType)
end
local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM_CONTAINER) then
local ret = doPlayerBuyItemContainer(cid, parentParameters.container, parentParameters.itemid, module.amount, parentParameters.cost * module.amount, parentParameters.subType)
if(ret) then
local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
end
module.npcHandler:resetNpc()
return true
end
-- onDecliune keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function ShopModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parentParameters = node:getParent():getParameters()
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parentParameters.cost * module.amount,
[TAG_ITEMNAME] = parentParameters.realName
}
local msg = module.npcHandler:parseMessage(module.noText, parseInfo)
module.npcHandler:say(msg, cid)
module.npcHandler:resetNpc()
return true
end
-- tradeItem callback function. Makes the npc say the message defined by MESSAGE_BUY or MESSAGE_SELL
function ShopModule.tradeItem(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local count = module:getCount(message)
module.amount = count
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parameters.cost * module.amount,
[TAG_ITEMNAME] = parameters.realName
}
if(parameters.eventType == SHOPMODULE_SELL_ITEM) then
local msg = module.npcHandler:getMessage(MESSAGE_SELL)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
elseif(parameters.eventType == SHOPMODULE_BUY_ITEM) then
local msg = module.npcHandler:getMessage(MESSAGE_BUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
elseif(parameters.eventType == SHOPMODULE_BUY_ITEM_CONTAINER) then
local msg = module.npcHandler:getMessage(MESSAGE_BUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
return true
end
end

desculpe a demora.
Editado por Tiagone
Link para o comentário
Compartilhar em outros sites

  • 0

Tenta assim:

 

Modules:

 

-- Advanced NPC System (Created by Jiddo),

-- Modified by Talaturen.
-- Modified by Elf.
if(Modules == nil) then
-- Constants used to separate buying from selling.
SHOPMODULE_SELL_ITEM = 1
SHOPMODULE_BUY_ITEM = 2
SHOPMODULE_BUY_ITEM_CONTAINER = 3
-- Constants used for shop mode. Notice: addBuyableItemContainer is working on all modes
SHOPMODULE_MODE_TALK = 1 -- Old system used before Tibia 8.2: sell/buy item name
SHOPMODULE_MODE_TRADE = 2 -- Trade window system introduced in Tibia 8.2
SHOPMODULE_MODE_BOTH = 3 -- Both working at one time
-- Used in shop mode
SHOPMODULE_MODE = SHOPMODULE_MODE_BOTH
-- Constants used for outfit giving mode
OUTFITMODULE_FUNCTION_OLD = { doPlayerAddOutfit, canPlayerWearOutfit } -- lookType usage
OUTFITMODULE_FUNCTION_NEW = { doPlayerAddOutfitId, canPlayerWearOutfitId } -- OutfitId usage
-- Used in outfit module
OUTFITMODULE_FUNCTION = OUTFITMODULE_FUNCTION_NEW
if(OUTFITMODULE_FUNCTION[1] == nil or OUTFITMODULE_FUNCTION[2] == nil) then
OUTFITMODULE_FUNCTION = OUTFITMODULE_FUNCTION_OLD
end
Modules = {
parseableModules = {}
}
StdModule = {}
-- These callback function must be called with parameters.npcHandler = npcHandler in the parameters table or they will not work correctly.
-- Notice: The members of StdModule have not yet been tested. If you find any bugs, please report them to me.
-- Usage:
-- keywordHandler:addKeyword({'offer'}, StdModule.say, {npcHandler = npcHandler, text = 'I sell many powerful melee weapons.'})
function StdModule.say(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.say called without any npcHandler instance.')
end
local onlyFocus = (parameters.onlyFocus == nil or parameters.onlyFocus == true)
if(not npcHandler:isFocused(cid) and onlyFocus) then
return false
end
local parseInfo = {[TAG_PLAYERNAME] = getCreatureName(cid)}
npcHandler:say(npcHandler:parseMessage(parameters.text or parameters.message, parseInfo), cid, parameters.publicize and true)
if(parameters.reset == true) then
npcHandler:resetNpc()
elseif(parameters.moveup ~= nil and type(parameters.moveup) == 'number') then
npcHandler.keywordHandler:moveUp(parameters.moveup)
end
return true
end
--Usage:
-- local node1 = keywordHandler:addKeyword({'promot'}, StdModule.say, {npcHandler = npcHandler, text = 'I can promote you for 20000 brozne coins. Do you want me to promote you?'})
-- node1:addChildKeyword({'yes'}, StdModule.promotePlayer, {npcHandler = npcHandler, cost = 20000, promotion = 1, level = 20}, text = 'Congratulations! You are now promoted.')
-- node1:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, text = 'Alright then, come back when you are ready.'}, reset = true)
function StdModule.promotePlayer(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.promotePlayer called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('premiumForPromotion')) or not(parameters.premium)) then
if(getPlayerPromotionLevel(cid) >= parameters.promotion) then
npcHandler:say('You are already promoted!', cid)
elseif(getPlayerLevel(cid) < parameters.level) then
npcHandler:say('I am sorry, but I can only promote you once you have reached level ' .. parameters.level .. '.', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then
npcHandler:say('You do not have enough money!', cid)
else
setPlayerPromotionLevel(cid, parameters.promotion)
npcHandler:say(parameters.text, cid)
end
else
npcHandler:say("You need a premium account in order to get promoted.", cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.learnSpell(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.learnSpell called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not(parameters.premium)) then
if(getPlayerLearnedInstantSpell(cid, parameters.spellName)) then
npcHandler:say('You already know this spell.', cid)
elseif(getPlayerLevel(cid) < parameters.level) then
npcHandler:say('You need to obtain a level of ' .. parameters.level .. ' or higher to be able to learn ' .. parameters.spellName .. '.', cid)
elseif(not parameters.vocation(cid)) then
npcHandler:say('This spell is not for your vocation', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.price)) then
npcHandler:say('You do not have enough money, this spell costs ' .. parameters.price .. ' gold coins.', cid)
else
npcHandler:say('You have learned ' .. parameters.spellName .. '.', cid)
playerLearnInstantSpell(cid, parameters.spellName)
end
else
npcHandler:say('You need a premium account in order to buy ' .. parameters.spellName .. '.', cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.bless(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.bless called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('blessingsOnlyPremium')) or not parameters.premium) then
local price = parameters.baseCost
if(getPlayerLevel(cid) > parameters.startLevel) then
price = (price + ((math.min(parameters.endLevel, getPlayerLevel(cid)) - parameters.startLevel) * parameters.levelCost))
end
if(getPlayerBlessing(cid, parameters.number)) then
npcHandler:say("Gods have already blessed you with this blessing!", cid)
elseif(not doPlayerRemoveMoney(cid, price)) then
npcHandler:say("You don't have enough money for blessing.", cid)
else
npcHandler:say("You have been blessed by one of the five gods!", cid)
doPlayerAddBlessing(cid, parameters.number)
end
else
npcHandler:say('You need a premium account in order to be blessed.', cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.travel(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.travel called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
local storage, pzLocked = parameters.storageValue or (EMPTY_STORAGE + 1), parameters.allowLocked or false
if(parameters.premium and not isPlayerPremiumCallback(cid)) then
npcHandler:say('I can only allow premium players to travel with me.', cid)
elseif(parameters.level ~= nil and getPlayerLevel(cid) < parameters.level) then
npcHandler:say('You must reach level ' .. parameters.level .. ' before I can let you go there.', cid)
elseif(parameters.storageId ~= nil and getPlayerStorageValue(cid, parameters.storageId) < storage) then
npcHandler:say(parameters.storageInfo or 'You may not travel there!', cid)
elseif(not pzLocked and isPlayerPzLocked(cid)) then
npcHandler:say('Get out of there with this blood!', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then
npcHandler:say('You do not have enough money.', cid)
else
npcHandler:say('It was a pleasure doing business with you.', cid)
npcHandler:releaseFocus(cid)
doTeleportThing(cid, parameters.destination, false)
doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT)
end
npcHandler:resetNpc()
return true
end
FocusModule = {
npcHandler = nil
}
-- Creates a new instance of FocusModule without an associated NpcHandler.
function FocusModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Inits the module and associates handler to it.
function FocusModule:init(handler)
self.npcHandler = handler
for i, word in pairs(FOCUS_GREETWORDS) do
local obj = {}
table.insert(obj, word)
obj.callback = FOCUS_GREETWORDS.callback or FocusModule.messageMatcher
handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self})
end
for i, word in pairs(FOCUS_FAREWELLWORDS) do
local obj = {}
table.insert(obj, word)
obj.callback = FOCUS_FAREWELLWORDS.callback or FocusModule.messageMatcher
handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self})
end
end
-- Greeting callback function.
function FocusModule.onGreet(cid, message, keywords, parameters)
parameters.module.npcHandler:onGreet(cid)
return true
end
-- UnGreeting callback function.
function FocusModule.onFarewell(cid, message, keywords, parameters)
if(not parameters.module.npcHandler:isFocused(cid)) then
return false
end
parameters.module.npcHandler:onFarewell(cid)
return true
end
-- Custom message matching callback function for greeting messages.
function FocusModule.messageMatcher(keywords, message)
local spectators = getSpectators(getCreaturePosition(getNpcId()), 7, 7)
for i, word in pairs(keywords) do
if(type(word) == 'string') then
if(string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]')) then
if(string.find(message, getCreatureName(getNpcId()))) then
return true
end
for i, uid in ipairs(spectators) do
if(string.find(message, getCreatureName(uid))) then
return false
end
end
return true
end
end
end
return false
end
KeywordModule = {
npcHandler = nil
}
-- Add it to the parseable module list.
Modules.parseableModules['module_keywords'] = KeywordModule
function KeywordModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function KeywordModule:init(handler)
self.npcHandler = handler
end
-- Parses all known parameters.
function KeywordModule:parseParameters()
local ret = NpcSystem.getParameter('keywords')
if(ret ~= nil) then
self:parseKeywords(ret)
end
end
function KeywordModule:parseKeywords(data)
local n = 1
for keys in string.gmatch(data, '[^;]+') do
local i = 1
local keywords = {}
for temp in string.gmatch(keys, '[^,]+') do
table.insert(keywords, temp)
i = i + 1
end
if(i ~= 1) then
local reply = NpcSystem.getParameter('keyword_reply' .. n)
if(reply ~= nil) then
self:addKeyword(keywords, reply)
else
print('[Warning] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...')
end
else
print('[Warning] NpcSystem:', 'No keywords found for keyword set #' .. n .. '. Skipping...')
end
n = n + 1
end
end
function KeywordModule:addKeyword(keywords, reply)
self.npcHandler.keywordHandler:addKeyword(keywords, StdModule.say, {npcHandler = self.npcHandler, onlyFocus = true, text = reply, reset = true})
end
TravelModule = {
npcHandler = nil,
destinations = nil,
yesNode = nil,
noNode = nil,
}
-- Add it to the parseable module list.
Modules.parseableModules['module_travel'] = TravelModule
function TravelModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function TravelModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, TravelModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, TravelModule.onDecline, {module = self})
self.destinations = {}
end
-- Parses all known parameters.
function TravelModule:parseParameters()
local ret = NpcSystem.getParameter('travel_destinations')
if(ret ~= nil) then
self:parseDestinations(ret)
for _, word in ipairs({'destination', 'list', 'where', 'travel'}) do
self.npcHandler.keywordHandler:addKeyword({word}, TravelModule.listDestinations, {module = self})
end
end
end
function TravelModule:parseDestinations(data)
for destination in string.gmatch(data, '[^;]+') do
local i, name, pos, cost, premium, level, storage = 1, nil, {x = nil, y = nil, z = nil}, nil, false
for tmp in string.gmatch(destination, '[^,]+') do
if(i == 1) then
name = tmp
elseif(i == 2) then
pos.x = tonumber(tmp)
elseif(i == 3) then
pos.y = tonumber(tmp)
elseif(i == 4) then
pos.z = tonumber(tmp)
elseif(i == 5) then
cost = tonumber(tmp)
elseif(i == 6) then
premium = getBooleanFromString(tmp)
else
print('[Warning] NpcSystem:', 'Unknown parameter found in travel destination parameter.', tmp, destination)
end
i = i + 1
end
if(name ~= nil and pos.x ~= nil and pos.y ~= nil and pos.z ~= nil and cost ~= nil) then
self:addDestination(name, pos, cost, premium)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for travel destination:', name, pos, cost, premium)
end
end
end
function TravelModule:addDestination(name, position, price, premium)
table.insert(self.destinations, name)
local parameters = {
cost = price,
destination = position,
premium = premium,
module = self
}
local keywords, bringwords = {}, {}
table.insert(keywords, name)
table.insert(bringwords, 'bring me to ' .. name)
self.npcHandler.keywordHandler:addKeyword(bringwords, TravelModule.bring, parameters)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, TravelModule.travel, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
function TravelModule.travel(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say('Do you want to travel to ' .. keywords[1] .. ' for ' .. parameters.cost .. ' gold coins?', cid)
return true
end
function TravelModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parent = node:getParent():getParameters()
if(isPlayerPremiumCallback(cid) or not parent.premium) then
if(not isPlayerPzLocked(cid)) then
if(doPlayerRemoveMoney(cid, parent.cost)) then
module.npcHandler:say('It was a pleasure doing business with you.', cid)
module.npcHandler:releaseFocus(cid)
doTeleportThing(cid, parent.destination, true)
doSendMagicEffect(parent.destination, CONST_ME_TELEPORT)
else
module.npcHandler:say('You do not have enough money.', cid)
end
else
module.npcHandler:say('Get out of there with this blood!', cid)
end
else
modulenpcHandler:say('I can only allow premium players to travel there.', cid)
end
module.npcHandler:resetNpc()
return true
end
-- onDecline keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function TravelModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid)
module.npcHandler:resetNpc()
return true
end
function TravelModule.bring(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
if((isPlayerPremiumCallback(cid) or not parameters.premium) and not isPlayerPzLocked(cid) and doPlayerRemoveMoney(cid, parameters.cost)) then
module.npcHandler:say('Sure!', cid)
module.npcHandler:releaseFocus(cid)
doTeleportThing(cid, parameters.destination, false)
doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT)
end
module.npcHandler:releaseFocus(cid)
return true
end
function TravelModule.listDestinations(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local msg = nil
for _, destination in ipairs(module.destinations) do
if(msg ~= nil) then
msg = msg .. ", "
else
msg = ""
end
msg = msg .. "{" .. destination .. "}"
end
module.npcHandler:say(msg .. ".", cid)
module.npcHandler:resetNpc()
return true
end
OutfitModule = {
npcHandler = nil,
outfits = nil,
yesNode = nil,
noNode = nil,
}
-- Add it to the parseable module list.
Modules.parseableModules['module_outfit'] = OutfitModule
function OutfitModule:new()
if(OUTFITMODULE_FUNCTION[1] == nil or OUTFITMODULE_FUNCTION[2] == nil) then
return nil
end
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function OutfitModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, OutfitModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, OutfitModule.onDecline, {module = self})
self.outfits = {}
end
-- Parses all known parameters.
function OutfitModule:parseParameters()
local ret = NpcSystem.getParameter('outfits')
if(ret ~= nil) then
self:parseKeywords(ret)
for _, word in ipairs({'outfits', 'addons'}) do
self.npcHandler.keywordHandler:addKeyword({word}, OutfitModule.listOutfits, {module = self})
end
end
end
function OutfitModule:parseKeywords(data)
local n = 1
for outfit in string.gmatch(data, '[^;]+') do
local i, keywords = 1, {}
for tmp in string.gmatch(outfit, '[^,]+') do
table.insert(keywords, tmp)
i = i + 1
end
if(i > 0) then
local ret = NpcSystem.getParameter('outfit' .. n)
if(ret ~= nil) then
self:parseList(keywords, ret)
else
print('[Warning] NpcSystem:', 'Missing \'outfit' .. n .. '\' parameter, skipping...')
end
else
print('[Warning] NpcSystem:', 'No keywords found for outfit set #' .. n .. ', skipping...')
end
n = n + 1
end
end
function OutfitModule:parseList(keywords, data)
local outfit, items = nil, {}
for list in string.gmatch(data, '[^;]+') do
local a, b, c, d, e = nil, nil, nil, nil, 1
for tmp in string.gmatch(list, '[^,]+') do
if(e == 1) then
a = tmp
elseif(e == 2) then
b = tmp
elseif(e == 3) then
c = tmp
elseif(e == 4) then
d = tmp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in outfit list while parsing ' .. (outfit == nil and 'outfit' or 'item') .. '.', tmp, list)
end
e = e + 1
end
if(outfit == nil) then
outfit = {tonumber(a), tonumber(b), getBooleanFromString©, d}
elseif(a ~= nil) then
local tmp = tonumber(a)
if((tmp ~= nil or tostring(a) == "money") and b ~= nil and c ~= nil) then
a = tmp or 20000
tmp = tonumber(d)
if(tmp == nil) then
tmp = -1
end
items[a] = {b, tmp, c}
else
print('[Warning] NpcSystem:', 'Missing parameter(s) for outfit items.', b, c, d)
end
else
print('[Warning] NpcSystem:', 'Missing base parameter for outfit items.', a)
end
end
if(type(outfit) == 'table') then
local tmp = true
for i = 1, 2 do
if(outfit == nil) then
tmp = false
break
end
end
if(tmp and table.maxn(items) > 0) then
self:addOutfit(keywords, outfit, items)
else
print('[Warning] NpcSystem:', 'Invalid outfit, addon or empty items pool.', data)
end
end
end
function OutfitModule:addOutfit(keywords, outfit, items)
table.insert(self.outfits, keywords[1])
local parameters = {
outfit = outfit[1],
addon = outfit[2],
premium = outfit[3],
gender = nil,
items = items,
module = self
}
if(outfit[4] ~= nil) then
local tmp = string.lower(tostring(outfit[5]))
if(tmp == 'male' or tmp == '1') then
parameters.gender = 1
elseif(tmp == 'female' or tmp == '0') then
parameters.gender = 0
end
end
for i, name in pairs(keywords) do
local words = {}
table.insert(words, name)
local node = self.npcHandler.keywordHandler:addKeyword(words, OutfitModule.obtain, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
function OutfitModule.obtain(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local i, items, size = 0, nil, table.maxn(parameters.items)
for k, v in pairs(parameters.items) do
if(v[1] ~= "storageset") then
i = i + 1
if(items ~= nil) then
if(i == size) then
items = items .. " and "
else
items = items .. ", "
end
else
items = ""
end
if(tonumber(v[1]) ~= nil and tonumber(v[1]) > 1) then
items = items .. v[1] .. " "
end
items = items .. v[3]
end
end
module.npcHandler:say('Do you want ' .. keywords[1] .. ' ' .. (addon == 0 and "outfit" or "addon") .. ' for ' .. items .. '?', cid)
return true
end
function OutfitModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parent = node:getParent():getParameters()
if(isPlayerPremiumCallback(cid) or not parent.premium) then
if(not OUTFITMODULE_FUNCTION[2](cid, parent.outfit, parent.addon)) then
if(parent.addon == 0 or OUTFITMODULE_FUNCTION[2](cid, parent.outfit)) then
if(parent.gender == nil or parent.gender == getPlayerSex(cid)) then
local found = true
for k, v in pairs(parent.items) do
local tmp = tonumber(v[1])
if(tmp == nil) then
if(v[1] == "storagecheck") then
if(getCreatureStorage(cid, k) < v[2]) then
found = false
end
elseif(v[1] == "outfitid") then
if(not canPlayerWearOutfitId(cid, k, v[2])) then
found = false
end
elseif(v[1] == "outfit") then
if(not canPlayerWearOutfit(cid, k, v[2])) then
found = false
end
else
found = false
end
elseif(k == 20000) then
if(getPlayerMoney(cid) < tmp) then
found = false
end
elseif(getPlayerItemCount(cid, k, v[2]) < tmp) then
found = false
end
if(not found) then
break
end
end
if(found) then
for k, v in pairs(parent.items) do
if(tonumber(v[1]) ~= nil) then
if(k == 20000) then
doPlayerRemoveMoney(cid, v[1])
else
doPlayerRemoveItem(cid, k, v[1], v[2])
end
elseif(v[1] == "storageset") then
doCreatureSetStorage(cid, k, v[2])
end
end
module.npcHandler:say('It was a pleasure to dress you.', cid)
OUTFITMODULE_FUNCTION[1](cid, parent.outfit, parent.addon)
doPlayerSetStorageValue(cid, parent.storageId, storage)
else
module.npcHandler:say('You don\'t have these items!', cid)
end
else
module.npcHandler:say('Sorry, this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. ' is not for your gender.', cid)
end
else
module.npcHandler:say('I will not dress you with addon of outfit you cannot wear!', cid)
end
else
module.npcHandler:say('You alrady have this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. '!', cid)
end
else
module.npcHandler:say('Sorry, I dress only premium players.', cid)
end
module.npcHandler:resetNpc()
return true
end
-- onDecline keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function OutfitModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid)
module.npcHandler:resetNpc()
return true
end
function OutfitModule.listOutfits(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local msg, size = nil, table.maxn(module.outfits)
if(size > 0) then
for i, outfit in ipairs(module.outfits) do
if(msg ~= nil) then
if(i == size) then
msg = msg .. " and "
else
msg = msg .. ", "
end
else
msg = "I can dress you into "
end
msg = msg .. "{" .. outfit .. "}"
end
else
msg = "Sorry, I have nothing to offer right now."
end
module.npcHandler:say(msg .. ".", cid)
module.npcHandler:resetNpc()
return true
end
ShopModule = {
npcHandler = nil,
yesNode = nil,
noNode = nil,
noText = '',
maxCount = 100,
amount = 0
}
-- Add it to the parseable module list.
Modules.parseableModules['module_shop'] = ShopModule
-- Creates a new instance of ShopModule
function ShopModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Parses all known parameters.
function ShopModule:parseParameters()
local ret = NpcSystem.getParameter('shop_buyable')
if(ret ~= nil) then
self:parseBuyable(ret)
end
local ret = NpcSystem.getParameter('shop_sellable')
if(ret ~= nil) then
self:parseSellable(ret)
end
local ret = NpcSystem.getParameter('shop_buyable_containers')
if(ret ~= nil) then
self:parseBuyableContainers(ret)
end
end
-- Parse a string contaning a set of buyable items.
function ShopModule:parseBuyable(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
cost = tonumber(temp)
elseif(i == 4) then
subType = tonumber(temp)
elseif(i == 5) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item)
end
i = i + 1
end
if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
if(itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
self:addBuyableItem(nil, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
end
elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
local names = {}
table.insert(names, name)
self:addBuyableItem(names, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost)
end
end
end
-- Parse a string contaning a set of sellable items.
function ShopModule:parseSellable(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, itemid, cost, realName = 1, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
cost = tonumber(temp)
elseif(i == 4) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in sellable items parameter.', temp, item)
end
i = i + 1
end
if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
if(itemid ~= nil and cost ~= nil) then
self:addSellableItem(nil, itemid, cost, realName)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
end
elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
local names = {}
table.insert(names, name)
self:addSellableItem(names, itemid, cost, realName)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost)
end
end
end
-- Parse a string contaning a set of buyable items.
function ShopModule:parseBuyableContainers(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, container, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
itemid = tonumber(temp)
elseif(i == 4) then
cost = tonumber(temp)
elseif(i == 5) then
subType = tonumber(temp)
elseif(i == 6) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item)
end
i = i + 1
end
if(name ~= nil and container ~= nil and itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
local names = {}
table.insert(names, name)
self:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, container, itemid, cost)
end
end
end
-- Initializes the module and associates handler to it.
function ShopModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, ShopModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, ShopModule.onDecline, {module = self})
self.noText = handler:getMessage(MESSAGE_DECLINE)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
for i, word in pairs(SHOP_TRADEREQUEST) do
local obj = {}
table.insert(obj, word)
obj.callback = SHOP_TRADEREQUEST.callback or ShopModule.messageMatcher
handler.keywordHandler:addKeyword(obj, ShopModule.requestTrade, {module = self})
end
end
end
-- Custom message matching callback function for requesting trade messages.
function ShopModule.messageMatcher(keywords, message)
for i, word in pairs(keywords) do
if(type(word) == 'string' and string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]')) then
return true
end
end
return false
end
-- Resets the module-specific variables.
function ShopModule:reset()
self.amount = 0
end
-- Function used to match a number value from a string.
function ShopModule:getCount(message)
local ret, b, e = 1, string.find(message, PATTERN_COUNT)
if(b ~= nil and e ~= nil) then
ret = tonumber(string.sub(message, b, e))
end
return math.max(1, math.min(self.maxCount, ret))
end
-- Adds a new buyable item.
-- names = A table containing one or more strings of alternative names to this item. Used only for old buy/sell system.
-- itemid = The itemid of the buyable item
-- cost = The price of one single item
-- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addBuyableItem(names, itemid, cost, subType, realName)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
local item = {
id = itemid,
buy = cost,
sell = -1,
subType = subType or 1,
name = realName or getItemNameById(itemid)
}
for i, shopItem in ipairs(self.npcHandler.shopItems) do
if(shopItem.id == item.id and shopItem.subType == item.subType) then
if(item.sell ~= shopItem.sell) then
item.sell = shopItem.sell
end
self.npcHandler.shopItems = item
item = nil
break
end
end
if(item ~= nil) then
table.insert(self.npcHandler.shopItems, item)
end
end
if(names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE) then
local parameters = {
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_BUY_ITEM,
module = self,
realName = realName or getItemNameById(itemid),
subType = subType or 1
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'buy')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- Adds a new buyable container of items.
-- names = A table containing one or more strings of alternative names to this item.
-- container = Backpack, bag or any other itemid of container where bought items will be stored
-- itemid = The itemid of the buyable item
-- cost = The price of one single item
-- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
if(names ~= nil) then
local parameters = {
container = container,
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_BUY_ITEM_CONTAINER,
module = self,
realName = realName or getItemNameById(itemid),
subType = subType or 1
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'buy')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- Adds a new sellable item.
-- names = A table containing one or more strings of alternative names to this item. Used only by old buy/sell system.
-- itemid = The itemid of the sellable item
-- cost = The price of one single item
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addSellableItem(names, itemid, cost, realName)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
local item = {
id = itemid,
buy = -1,
sell = cost,
subType = 1,
name = realName or getItemNameById(itemid)
}
for i, shopItem in ipairs(self.npcHandler.shopItems) do
if(shopItem.id == item.id and shopItem.subType == item.subType) then
if(item.buy ~= shopItem.buy) then
item.buy = shopItem.buy
end
self.npcHandler.shopItems = item
item = nil
break
end
end
if(item ~= nil) then
table.insert(self.npcHandler.shopItems, item)
end
end
if(names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE) then
local parameters = {
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_SELL_ITEM,
module = self,
realName = realName or getItemNameById(itemid)
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'sell')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- onModuleReset callback function. Calls ShopModule:reset()
function ShopModule:callbackOnModuleReset()
self:reset()
return true
end
-- Callback onBuy() function. If you wish, you can change certain Npc to use your onBuy().
function ShopModule:callbackOnBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local shopItem = nil
for _, item in ipairs(self.npcHandler.shopItems) do
if(item.id == itemid and item.subType == subType) then
shopItem = item
break
end
end
if(shopItem == nil) then
print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Item not found on shopItems list')
return false
end
if(shopItem.buy == -1) then
error("[shopModule.onSell]", "Attempt to purchase an item which only sellable")
return false
end
local backpack, totalCost = 1988, amount * shopItem.buy
if(inBackpacks) then
totalCost = totalCost + (math.max(1, math.floor(amount / getContainerCapById(backpack))) * 20)
end
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = amount,
[TAG_TOTALCOST] = totalCost,
[TAG_ITEMNAME] = shopItem.name
}
if(getPlayerMoney(cid) < totalCost) then
local msg = self.npcHandler:getMessage(MESSAGE_NEEDMONEY)
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
return false
end
local subType = shopItem.subType or 1
local a, b = doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, backpack)
if(a < amount) then
local msgId = MESSAGE_NEEDMORESPACE
if(a == 0) then
msgId = MESSAGE_NEEDSPACE
end
local msg = self.npcHandler:getMessage(msgId)
parseInfo[TAG_ITEMCOUNT] = a
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
if(a > 0) then
doPlayerRemoveMoney(cid, ((a * shopItem.buy) + (b * 20)))
return true
end
return false
end
local msg = self.npcHandler:getMessage(MESSAGE_BOUGHT)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo))
doPlayerRemoveMoney(cid, totalCost)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return true
end
-- Callback onSell() function. If you wish, you can change certain Npc to use your onSell().
function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local shopItem = nil
for _, item in ipairs(self.npcHandler.shopItems) do
if(item.id == itemid and item.subType == subType) then
shopItem = item
break
end
end
if(shopItem == nil) then
error("[shopModule.onBuy]", "Item not found on shopItems list")
return false
end
if(shopItem.sell == -1) then
error("[shopModule.onSell]", "Attempt to sell an item which is only buyable")
return false
end
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = amount,
[TAG_TOTALCOST] = amount * shopItem.sell,
[TAG_ITEMNAME] = shopItem.name
}
if(subType < 1 or getItemInfo(itemid).stackable) then
subType = -1
end
if(doPlayerRemoveItem(cid, itemid, amount, subType)) then
local msg = self.npcHandler:getMessage(MESSAGE_SOLD)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo))
doPlayerAddMoney(cid, amount * shopItem.sell)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return true
end
local msg = self.npcHandler:getMessage(MESSAGE_NEEDITEM)
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return false
end
-- Callback for requesting a trade window with the NPC.
function ShopModule.requestTrade(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
if(table.maxn(module.npcHandler.shopItems) == 0) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_NOSHOP), parseInfo)
module.npcHandler:say(msg, cid)
return true
end
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_SENDTRADE), parseInfo)
openShopWindow(cid, module.npcHandler.shopItems,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
module.npcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
end,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
module.npcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
end
)
module.npcHandler:say(msg, cid)
return true
end
-- onConfirm keyword callback function. Sells/buys the actual item.
function ShopModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parentParameters = node:getParent():getParameters()
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parentParameters.cost * module.amount,
[TAG_ITEMNAME] = parentParameters.realName
}
if(parentParameters.eventType == SHOPMODULE_SELL_ITEM) then
local ret = doPlayerSellItem(cid, parentParameters.itemid, module.amount, parentParameters.cost * module.amount)
if(ret) then
local msg = module.npcHandler:getMessage(MESSAGE_ONSELL)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGITEM)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM) then
local ret = doPlayerBuyItem(cid, parentParameters.itemid, module.amount, parentParameters.cost * module.amount, parentParameters.subType)
if(ret) then
if parentParameters.itemid == ITEM_PARCEL then
doPlayerBuyItem(cid, ITEM_LABEL, module.amount, 0, parentParameters.subType)
end
local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM_CONTAINER) then
local ret = doPlayerBuyItemContainer(cid, parentParameters.container, parentParameters.itemid, module.amount, parentParameters.cost * module.amount, parentParameters.subType)
if(ret) then
local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
end
module.npcHandler:resetNpc()
return true
end
-- onDecliune keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function ShopModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parentParameters = node:getParent():getParameters()
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parentParameters.cost * module.amount,
[TAG_ITEMNAME] = parentParameters.realName
}
local msg = module.npcHandler:parseMessage(module.noText, parseInfo)
module.npcHandler:say(msg, cid)
module.npcHandler:resetNpc()
return true
end
-- tradeItem callback function. Makes the npc say the message defined by MESSAGE_BUY or MESSAGE_SELL
function ShopModule.tradeItem(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local count = module:getCount(message)
module.amount = count
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parameters.cost * module.amount,
[TAG_ITEMNAME] = parameters.realName
}
if(parameters.eventType == SHOPMODULE_SELL_ITEM) then
local msg = module.npcHandler:getMessage(MESSAGE_SELL)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
elseif(parameters.eventType == SHOPMODULE_BUY_ITEM) then
local msg = module.npcHandler:getMessage(MESSAGE_BUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
elseif(parameters.eventType == SHOPMODULE_BUY_ITEM_CONTAINER) then
local msg = module.npcHandler:getMessage(MESSAGE_BUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
return true
end
end

Npchandler:

-- Advanced NPC System (Created by Jiddo),
-- Modified by Talaturen.
if(NpcHandler == nil) then
-- Constant talkdelay behaviors.
TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
TALKDELAY_EVENT = 2 -- Not yet implemented
-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
-- Constant conversation behaviors.
CONVERSATION_DEFAULT = 0 -- Conversation through default window, like it was before 8.2 update.
CONVERSATION_PRIVATE = 1 -- Conversation through NPCs chat window, as of 8.2 update. (Default)
--Small Note: Private conversations also means the NPC will use multi-focus system.
-- Currently applied conversation behavior. CONVERSATION_PRIVATE is default.
NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE
-- Constant indexes for defining default messages.
MESSAGE_GREET = 1 -- When the player greets the npc.
MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.
MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.
MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.
MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.
MESSAGE_SOLD = 8 -- When the player sold something through the shop window.
MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.
MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window.
MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have.
MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.
MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item
MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space
MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.
MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.
MESSAGE_DECLINE = 17 -- When the player says no to something.
MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player
MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any
MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window
MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.
MESSAGE_PLACEDINQUEUE = 22 -- When the player has been placed in the costumer queue.
-- Constant indexes for callback functions. These are also used for module callback ids.
CALLBACK_CREATURE_APPEAR = 1
CALLBACK_CREATURE_DISAPPEAR = 2
CALLBACK_CREATURE_SAY = 3
CALLBACK_ONTHINK = 4
CALLBACK_GREET = 5
CALLBACK_FAREWELL = 6
CALLBACK_MESSAGE_DEFAULT = 7
CALLBACK_PLAYER_ENDTRADE = 8
CALLBACK_PLAYER_CLOSECHANNEL = 9
CALLBACK_ONBUY = 10
CALLBACK_ONSELL = 11
-- Addidional module callback ids
CALLBACK_MODULE_INIT = 12
CALLBACK_MODULE_RESET = 13
-- Constant strings defining the keywords to replace in the default messages.
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'
NpcHandler = {
keywordHandler = nil,
focuses = nil,
talkStart = nil,
idleTime = 90,
talkRadius = 4,
talkDelayTime = 1, -- Seconds to delay outgoing messages.
queue = nil,
talkDelay = nil,
callbackFunctions = nil,
modules = nil,
shopItems = nil, -- They must be here since ShopModule uses "static" functions
messages = {
-- These are the default replies of all npcs. They can/should be changed individually for each npc.
[MESSAGE_GREET] = 'Welcome, |PLAYERNAME|! I have been expecting you.',
[MESSAGE_FAREWELL] = 'Good bye, |PLAYERNAME|!',
[MESSAGE_BUY] = 'Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONBUY] = 'It was a pleasure doing business with you.',
[MESSAGE_BOUGHT] = 'Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
[MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONSELL] = 'Thank you for this |ITEMNAME|, |PLAYERNAME| gold.',
[MESSAGE_SOLD] = 'Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
[MESSAGE_MISSINGMONEY] = 'Sorry, you don\'t have enough money.',
[MESSAGE_NEEDMONEY] = 'You do not have enough money.',
[MESSAGE_MISSINGITEM] = 'You don\'t even have that item, |PLAYERNAME|!',
[MESSAGE_NEEDITEM] = 'You do not have this object.',
[MESSAGE_NEEDSPACE] = 'You do not have enough capacity.',
[MESSAGE_NEEDMORESPACE] = 'You do not have enough capacity for all items.',
[MESSAGE_IDLETIMEOUT] = 'Next, please!',
[MESSAGE_WALKAWAY] = 'How rude!',
[MESSAGE_DECLINE] = 'Not good enough, is it... ?',
[MESSAGE_SENDTRADE] = 'Here\'s my offer, |PLAYERNAME|. Don\'t you like it?',
[MESSAGE_NOSHOP] = 'Sorry, I\'m not offering anything.',
[MESSAGE_ONCLOSESHOP] = 'Thank you, come back when you want something more.',
[MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|! I am already talking to you...',
[MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, please wait for your turn. There are |QUEUESIZE| customers before you.'
}
}
-- Creates a new NpcHandler with an empty callbackFunction stack.
function NpcHandler:new(keywordHandler)
local obj = {}
obj.callbackFunctions = {}
obj.modules = {}
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
obj.focuses = {}
obj.talkStart = {}
else
obj.queue = Queue:new(obj)
obj.focuses = 0
obj.talkStart = 0
end
obj.talkDelay = {}
obj.keywordHandler = keywordHandler
obj.messages = {}
obj.shopItems = {}
setmetatable(obj.messages, self.messages)
self.messages.__index = self.messages
setmetatable(obj, self)
self.__index = self
return obj
end
-- Re-defines the maximum idle time allowed for a player when talking to this npc.
function NpcHandler:setMaxIdleTime(newTime)
self.idleTime = newTime
end
-- Attackes a new keyword handler to this npchandler
function NpcHandler:setKeywordHandler(newHandler)
self.keywordHandler = newHandler
end
-- Function used to change the focus of this npc.
function NpcHandler:addFocus(newFocus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(self:isFocused(newFocus)) then
return
end
table.insert(self.focuses, newFocus)
else
self.focuses = newFocus
end
self:updateFocus()
end
NpcHandler.changeFocus = NpcHandler.addFocus --"changeFocus" looks better for CONVERSATION_DEFAULT
-- Function used to verify if npc is focused to certain player
function NpcHandler:isFocused(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for k,v in pairs(self.focuses) do
if v == focus then
return true
end
end
return false
end
return (self.focuses == focus)
end
-- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
-- Should also be called whenever a new player is focused.
function NpcHandler:updateFocus()
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
doNpcSetCreatureFocus(focus)
return
end
end
doNpcSetCreatureFocus(0)
else
doNpcSetCreatureFocus(self.focuses)
end
end
-- Used when the npc should un-focus the player.
function NpcHandler:releaseFocus(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(not self:isFocused(focus)) then
return
end
local pos = nil
for k,v in pairs(self.focuses) do
if v == focus then
pos = k
end
end
table.remove(self.focuses, pos)
self.talkStart[focus] = nil
closeShopWindow(focus) --Even if it can not exist, we need to prevent it.
self:updateFocus()
else
closeShopWindow(focus)
self:changeFocus(0)
end
end
-- Returns the callback function with the specified id or nil if no such callback function exists.
function NpcHandler:getCallback(id)
local ret = nil
if(self.callbackFunctions ~= nil) then
ret = self.callbackFunctions[id]
end
return ret
end
-- Changes the callback function for the given id to callback.
function NpcHandler:setCallback(id, callback)
if(self.callbackFunctions ~= nil) then
self.callbackFunctions[id] = callback
end
end
-- Adds a module to this npchandler and inits it.
function NpcHandler:addModule(module)
if(self.modules == nil or module == nil) then
return false
end
module:init(self)
if(module.parseParameters ~= nil) then
module:parseParameters()
end
table.insert(self.modules, module)
return true
end
-- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
function NpcHandler:processModuleCallback(id, ...)
local ret = true
for i, module in pairs(self.modules) do
local tmpRet = true
if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
tmpRet = module:callbackOnCreatureAppear(unpack(arg))
elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
tmpRet = module:callbackOnCreatureDisappear(unpack(arg))
elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
tmpRet = module:callbackOnCreatureSay(unpack(arg))
elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then
tmpRet = module:callbackOnPlayerEndTrade(unpack(arg))
elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then
tmpRet = module:callbackOnPlayerCloseChannel(unpack(arg))
elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then
tmpRet = module:callbackOnBuy(...)
elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then
tmpRet = module:callbackOnSell(unpack(arg))
elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
tmpRet = module:callbackOnThink(unpack(arg))
elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
tmpRet = module:callbackOnGreet(unpack(arg))
elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
tmpRet = module:callbackOnFarewell(unpack(arg))
elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
tmpRet = module:callbackOnMessageDefault(unpack(arg))
elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
tmpRet = module:callbackOnModuleReset(unpack(arg))
end
if(not tmpRet) then
ret = false
break
end
end
return ret
end
-- Returns the message represented by id.
function NpcHandler:getMessage(id)
local ret = nil
if(self.messages ~= nil) then
ret = self.messages[id]
end
return ret
end
-- Changes the default response message with the specified id to newMessage.
function NpcHandler:setMessage(id, newMessage)
if(self.messages ~= nil) then
self.messages[id] = newMessage
end
end
-- Translates all message tags found in msg using parseInfo
function NpcHandler:parseMessage(msg, parseInfo)
local ret = msg
for search, replace in pairs(parseInfo) do
ret = string.gsub(ret, search, replace)
end
return ret
end
-- Makes sure the npc un-focuses the currently focused player
function NpcHandler:unGreet(cid)
if(not self:isFocused(cid)) then
return
end
local callback = self:getCallback(CALLBACK_FAREWELL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_FAREWELL)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_FAREWELL)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
-- Greets a new player.
function NpcHandler:greet(cid)
if(cid ~= 0) then
local callback = self:getCallback(CALLBACK_GREET)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_GREET, cid)) then
local msg = self:getMessage(MESSAGE_GREET)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
self:addFocus(cid)
self:say(msg, cid)
end
end
end
end
-- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
function NpcHandler:onCreatureAppear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
--
end
end
end
-- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
function NpcHandler:onCreatureDisappear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
function NpcHandler:onCreatureSay(cid, class, msg)
local callback = self:getCallback(CALLBACK_CREATURE_SAY)
if(callback == nil or callback(cid, class, msg)) then
if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, class, msg)) then
if(not self:isInRange(cid)) then
return
end
if(self.keywordHandler ~= nil) then
if((self:isFocused(cid) and (class == TALKTYPE_PRIVATE_PN or NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT)) or not self:isFocused(cid)) then
local ret = self.keywordHandler:processMessage(cid, msg)
if(not ret) then
local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
if(callback ~= nil and callback(cid, class, msg)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
else
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
end
end
end
end
end
-- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.
function NpcHandler:onPlayerEndTrade(cid)
local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid, class, msg)) then
if(self:isFocused(cid)) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)
self:say(msg, cid)
end
end
end
end
-- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.
function NpcHandler:onPlayerCloseChannel(cid)
local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid, class, msg)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.
function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONBUY)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.
function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONSELL)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.
function NpcHandler:onThink()
local callback = self:getCallback(CALLBACK_ONTHINK)
if(callback == nil or callback()) then
if(NPCHANDLER_TALKDELAY == TALKDELAY_ONTHINK) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for cid, talkDelay in pairs(self.talkDelay) do
if(talkDelay.time ~= nil and talkDelay.message ~= nil and os.time() >= talkDelay.time) then
selfSay(talkDelay.message, cid)
self.talkDelay[cid] = nil
end
end
elseif(self.talkDelay.time ~= nil and self.talkDelay.message ~= nil and os.time() >= self.talkDelay.time) then
selfSay(self.talkDelay.message)
self.talkDelay.time = nil
self.talkDelay.message = nil
end
end
if(self:processModuleCallback(CALLBACK_ONTHINK)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
if(not self:isInRange(focus)) then
self:onWalkAway(focus)
elseif((os.time() - self.talkStart[focus]) > self.idleTime) then
self:unGreet(focus)
else
self:updateFocus()
end
end
end
elseif(self.focuses ~= 0) then
if(not self:isInRange(self.focuses)) then
self:onWalkAway(self.focuses)
elseif(os.time()-self.talkStart > self.idleTime) then
self:unGreet(self.focuses)
else
self:updateFocus()
end
end
end
end
end
-- Tries to greet the player with the given cid.
function NpcHandler:onGreet(cid)
if(self:isInRange(cid)) then
if(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE) then
if(not self:isFocused(cid)) then
self:greet(cid)
return
end
elseif(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT) then
if(self.focuses == 0) then
self:greet(cid)
elseif(self.focuses == cid) then
local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
else
if(not self.queue:isInQueue(cid)) then
self.queue:push(cid)
end
local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
end
end
end
end
-- Simply calls the underlying unGreet function.
function NpcHandler:onFarewell(cid)
self:unGreet(cid)
end
-- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
function NpcHandler:onWalkAway(cid)
if(self:isFocused(cid)) then
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_WALKAWAY)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
end
-- Returns true if cid is within the talkRadius of this npc.
function NpcHandler:isInRange(cid)
local distance = getDistanceTo(cid) or -1
if(distance == -1) then
return false
end
return (distance <= self.talkRadius)
end
-- Resets the npc into it's initial state (in regard of the keyrodhandler).
-- All modules are also receiving a reset call through their callbackOnModuleReset function.
function NpcHandler:resetNpc()
if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
self.keywordHandler:reset()
end
end
-- Makes the npc represented by this instance of NpcHandler say something.
-- This implements the currently set type of talkdelay.
-- shallDelay is a boolean value. If it is false, the message is not delayed. Default value is false.
function NpcHandler:say(message, focus, shallDelay)
local shallDelay = shallDelay or false
if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or not shallDelay) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
selfSay(message, focus)
return
else
selfSay(message)
return
end
end
-- TODO: Add an event handling method for delayed messages
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkDelay[focus] = {
message = message,
time = os.time() + self.talkDelayTime,
}
else
self.talkDelay = {
message = message,
time = os.time() + self.talkDelayTime
}
end
end
end

 

Editado por XScupion
Link para o comentário
Compartilhar em outros sites

  • 0

Tenta assim:

 

Modules:

 

-- Advanced NPC System (Created by Jiddo),

-- Modified by Talaturen.
-- Modified by Elf.
if(Modules == nil) then
-- Constants used to separate buying from selling.
SHOPMODULE_SELL_ITEM = 1
SHOPMODULE_BUY_ITEM = 2
SHOPMODULE_BUY_ITEM_CONTAINER = 3
-- Constants used for shop mode. Notice: addBuyableItemContainer is working on all modes
SHOPMODULE_MODE_TALK = 1 -- Old system used before Tibia 8.2: sell/buy item name
SHOPMODULE_MODE_TRADE = 2 -- Trade window system introduced in Tibia 8.2
SHOPMODULE_MODE_BOTH = 3 -- Both working at one time
-- Used in shop mode
SHOPMODULE_MODE = SHOPMODULE_MODE_BOTH
-- Constants used for outfit giving mode
OUTFITMODULE_FUNCTION_OLD = { doPlayerAddOutfit, canPlayerWearOutfit } -- lookType usage
OUTFITMODULE_FUNCTION_NEW = { doPlayerAddOutfitId, canPlayerWearOutfitId } -- OutfitId usage
-- Used in outfit module
OUTFITMODULE_FUNCTION = OUTFITMODULE_FUNCTION_NEW
if(OUTFITMODULE_FUNCTION[1] == nil or OUTFITMODULE_FUNCTION[2] == nil) then
OUTFITMODULE_FUNCTION = OUTFITMODULE_FUNCTION_OLD
end
Modules = {
parseableModules = {}
}
StdModule = {}
-- These callback function must be called with parameters.npcHandler = npcHandler in the parameters table or they will not work correctly.
-- Notice: The members of StdModule have not yet been tested. If you find any bugs, please report them to me.
-- Usage:
-- keywordHandler:addKeyword({'offer'}, StdModule.say, {npcHandler = npcHandler, text = 'I sell many powerful melee weapons.'})
function StdModule.say(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.say called without any npcHandler instance.')
end
local onlyFocus = (parameters.onlyFocus == nil or parameters.onlyFocus == true)
if(not npcHandler:isFocused(cid) and onlyFocus) then
return false
end
local parseInfo = {[TAG_PLAYERNAME] = getCreatureName(cid)}
npcHandler:say(npcHandler:parseMessage(parameters.text or parameters.message, parseInfo), cid, parameters.publicize and true)
if(parameters.reset == true) then
npcHandler:resetNpc()
elseif(parameters.moveup ~= nil and type(parameters.moveup) == 'number') then
npcHandler.keywordHandler:moveUp(parameters.moveup)
end
return true
end
--Usage:
-- local node1 = keywordHandler:addKeyword({'promot'}, StdModule.say, {npcHandler = npcHandler, text = 'I can promote you for 20000 brozne coins. Do you want me to promote you?'})
-- node1:addChildKeyword({'yes'}, StdModule.promotePlayer, {npcHandler = npcHandler, cost = 20000, promotion = 1, level = 20}, text = 'Congratulations! You are now promoted.')
-- node1:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, text = 'Alright then, come back when you are ready.'}, reset = true)
function StdModule.promotePlayer(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.promotePlayer called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('premiumForPromotion')) or not(parameters.premium)) then
if(getPlayerPromotionLevel(cid) >= parameters.promotion) then
npcHandler:say('You are already promoted!', cid)
elseif(getPlayerLevel(cid) < parameters.level) then
npcHandler:say('I am sorry, but I can only promote you once you have reached level ' .. parameters.level .. '.', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then
npcHandler:say('You do not have enough money!', cid)
else
setPlayerPromotionLevel(cid, parameters.promotion)
npcHandler:say(parameters.text, cid)
end
else
npcHandler:say("You need a premium account in order to get promoted.", cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.learnSpell(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.learnSpell called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not(parameters.premium)) then
if(getPlayerLearnedInstantSpell(cid, parameters.spellName)) then
npcHandler:say('You already know this spell.', cid)
elseif(getPlayerLevel(cid) < parameters.level) then
npcHandler:say('You need to obtain a level of ' .. parameters.level .. ' or higher to be able to learn ' .. parameters.spellName .. '.', cid)
elseif(not parameters.vocation(cid)) then
npcHandler:say('This spell is not for your vocation', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.price)) then
npcHandler:say('You do not have enough money, this spell costs ' .. parameters.price .. ' gold coins.', cid)
else
npcHandler:say('You have learned ' .. parameters.spellName .. '.', cid)
playerLearnInstantSpell(cid, parameters.spellName)
end
else
npcHandler:say('You need a premium account in order to buy ' .. parameters.spellName .. '.', cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.bless(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.bless called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('blessingsOnlyPremium')) or not parameters.premium) then
local price = parameters.baseCost
if(getPlayerLevel(cid) > parameters.startLevel) then
price = (price + ((math.min(parameters.endLevel, getPlayerLevel(cid)) - parameters.startLevel) * parameters.levelCost))
end
if(getPlayerBlessing(cid, parameters.number)) then
npcHandler:say("Gods have already blessed you with this blessing!", cid)
elseif(not doPlayerRemoveMoney(cid, price)) then
npcHandler:say("You don't have enough money for blessing.", cid)
else
npcHandler:say("You have been blessed by one of the five gods!", cid)
doPlayerAddBlessing(cid, parameters.number)
end
else
npcHandler:say('You need a premium account in order to be blessed.', cid)
end
npcHandler:resetNpc()
return true
end
function StdModule.travel(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if(npcHandler == nil) then
error('StdModule.travel called without any npcHandler instance.')
end
if(not npcHandler:isFocused(cid)) then
return false
end
local storage, pzLocked = parameters.storageValue or (EMPTY_STORAGE + 1), parameters.allowLocked or false
if(parameters.premium and not isPlayerPremiumCallback(cid)) then
npcHandler:say('I can only allow premium players to travel with me.', cid)
elseif(parameters.level ~= nil and getPlayerLevel(cid) < parameters.level) then
npcHandler:say('You must reach level ' .. parameters.level .. ' before I can let you go there.', cid)
elseif(parameters.storageId ~= nil and getPlayerStorageValue(cid, parameters.storageId) < storage) then
npcHandler:say(parameters.storageInfo or 'You may not travel there!', cid)
elseif(not pzLocked and isPlayerPzLocked(cid)) then
npcHandler:say('Get out of there with this blood!', cid)
elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then
npcHandler:say('You do not have enough money.', cid)
else
npcHandler:say('It was a pleasure doing business with you.', cid)
npcHandler:releaseFocus(cid)
doTeleportThing(cid, parameters.destination, false)
doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT)
end
npcHandler:resetNpc()
return true
end
FocusModule = {
npcHandler = nil
}
-- Creates a new instance of FocusModule without an associated NpcHandler.
function FocusModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Inits the module and associates handler to it.
function FocusModule:init(handler)
self.npcHandler = handler
for i, word in pairs(FOCUS_GREETWORDS) do
local obj = {}
table.insert(obj, word)
obj.callback = FOCUS_GREETWORDS.callback or FocusModule.messageMatcher
handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self})
end
for i, word in pairs(FOCUS_FAREWELLWORDS) do
local obj = {}
table.insert(obj, word)
obj.callback = FOCUS_FAREWELLWORDS.callback or FocusModule.messageMatcher
handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self})
end
end
-- Greeting callback function.
function FocusModule.onGreet(cid, message, keywords, parameters)
parameters.module.npcHandler:onGreet(cid)
return true
end
-- UnGreeting callback function.
function FocusModule.onFarewell(cid, message, keywords, parameters)
if(not parameters.module.npcHandler:isFocused(cid)) then
return false
end
parameters.module.npcHandler:onFarewell(cid)
return true
end
-- Custom message matching callback function for greeting messages.
function FocusModule.messageMatcher(keywords, message)
local spectators = getSpectators(getCreaturePosition(getNpcId()), 7, 7)
for i, word in pairs(keywords) do
if(type(word) == 'string') then
if(string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]')) then
if(string.find(message, getCreatureName(getNpcId()))) then
return true
end
for i, uid in ipairs(spectators) do
if(string.find(message, getCreatureName(uid))) then
return false
end
end
return true
end
end
end
return false
end
KeywordModule = {
npcHandler = nil
}
-- Add it to the parseable module list.
Modules.parseableModules['module_keywords'] = KeywordModule
function KeywordModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function KeywordModule:init(handler)
self.npcHandler = handler
end
-- Parses all known parameters.
function KeywordModule:parseParameters()
local ret = NpcSystem.getParameter('keywords')
if(ret ~= nil) then
self:parseKeywords(ret)
end
end
function KeywordModule:parseKeywords(data)
local n = 1
for keys in string.gmatch(data, '[^;]+') do
local i = 1
local keywords = {}
for temp in string.gmatch(keys, '[^,]+') do
table.insert(keywords, temp)
i = i + 1
end
if(i ~= 1) then
local reply = NpcSystem.getParameter('keyword_reply' .. n)
if(reply ~= nil) then
self:addKeyword(keywords, reply)
else
print('[Warning] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...')
end
else
print('[Warning] NpcSystem:', 'No keywords found for keyword set #' .. n .. '. Skipping...')
end
n = n + 1
end
end
function KeywordModule:addKeyword(keywords, reply)
self.npcHandler.keywordHandler:addKeyword(keywords, StdModule.say, {npcHandler = self.npcHandler, onlyFocus = true, text = reply, reset = true})
end
TravelModule = {
npcHandler = nil,
destinations = nil,
yesNode = nil,
noNode = nil,
}
-- Add it to the parseable module list.
Modules.parseableModules['module_travel'] = TravelModule
function TravelModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function TravelModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, TravelModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, TravelModule.onDecline, {module = self})
self.destinations = {}
end
-- Parses all known parameters.
function TravelModule:parseParameters()
local ret = NpcSystem.getParameter('travel_destinations')
if(ret ~= nil) then
self:parseDestinations(ret)
for _, word in ipairs({'destination', 'list', 'where', 'travel'}) do
self.npcHandler.keywordHandler:addKeyword({word}, TravelModule.listDestinations, {module = self})
end
end
end
function TravelModule:parseDestinations(data)
for destination in string.gmatch(data, '[^;]+') do
local i, name, pos, cost, premium, level, storage = 1, nil, {x = nil, y = nil, z = nil}, nil, false
for tmp in string.gmatch(destination, '[^,]+') do
if(i == 1) then
name = tmp
elseif(i == 2) then
pos.x = tonumber(tmp)
elseif(i == 3) then
pos.y = tonumber(tmp)
elseif(i == 4) then
pos.z = tonumber(tmp)
elseif(i == 5) then
cost = tonumber(tmp)
elseif(i == 6) then
premium = getBooleanFromString(tmp)
else
print('[Warning] NpcSystem:', 'Unknown parameter found in travel destination parameter.', tmp, destination)
end
i = i + 1
end
if(name ~= nil and pos.x ~= nil and pos.y ~= nil and pos.z ~= nil and cost ~= nil) then
self:addDestination(name, pos, cost, premium)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for travel destination:', name, pos, cost, premium)
end
end
end
function TravelModule:addDestination(name, position, price, premium)
table.insert(self.destinations, name)
local parameters = {
cost = price,
destination = position,
premium = premium,
module = self
}
local keywords, bringwords = {}, {}
table.insert(keywords, name)
table.insert(bringwords, 'bring me to ' .. name)
self.npcHandler.keywordHandler:addKeyword(bringwords, TravelModule.bring, parameters)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, TravelModule.travel, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
function TravelModule.travel(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say('Do you want to travel to ' .. keywords[1] .. ' for ' .. parameters.cost .. ' gold coins?', cid)
return true
end
function TravelModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parent = node:getParent():getParameters()
if(isPlayerPremiumCallback(cid) or not parent.premium) then
if(not isPlayerPzLocked(cid)) then
if(doPlayerRemoveMoney(cid, parent.cost)) then
module.npcHandler:say('It was a pleasure doing business with you.', cid)
module.npcHandler:releaseFocus(cid)
doTeleportThing(cid, parent.destination, true)
doSendMagicEffect(parent.destination, CONST_ME_TELEPORT)
else
module.npcHandler:say('You do not have enough money.', cid)
end
else
module.npcHandler:say('Get out of there with this blood!', cid)
end
else
modulenpcHandler:say('I can only allow premium players to travel there.', cid)
end
module.npcHandler:resetNpc()
return true
end
-- onDecline keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function TravelModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid)
module.npcHandler:resetNpc()
return true
end
function TravelModule.bring(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
if((isPlayerPremiumCallback(cid) or not parameters.premium) and not isPlayerPzLocked(cid) and doPlayerRemoveMoney(cid, parameters.cost)) then
module.npcHandler:say('Sure!', cid)
module.npcHandler:releaseFocus(cid)
doTeleportThing(cid, parameters.destination, false)
doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT)
end
module.npcHandler:releaseFocus(cid)
return true
end
function TravelModule.listDestinations(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local msg = nil
for _, destination in ipairs(module.destinations) do
if(msg ~= nil) then
msg = msg .. ", "
else
msg = ""
end
msg = msg .. "{" .. destination .. "}"
end
module.npcHandler:say(msg .. ".", cid)
module.npcHandler:resetNpc()
return true
end
OutfitModule = {
npcHandler = nil,
outfits = nil,
yesNode = nil,
noNode = nil,
}
-- Add it to the parseable module list.
Modules.parseableModules['module_outfit'] = OutfitModule
function OutfitModule:new()
if(OUTFITMODULE_FUNCTION[1] == nil or OUTFITMODULE_FUNCTION[2] == nil) then
return nil
end
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function OutfitModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, OutfitModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, OutfitModule.onDecline, {module = self})
self.outfits = {}
end
-- Parses all known parameters.
function OutfitModule:parseParameters()
local ret = NpcSystem.getParameter('outfits')
if(ret ~= nil) then
self:parseKeywords(ret)
for _, word in ipairs({'outfits', 'addons'}) do
self.npcHandler.keywordHandler:addKeyword({word}, OutfitModule.listOutfits, {module = self})
end
end
end
function OutfitModule:parseKeywords(data)
local n = 1
for outfit in string.gmatch(data, '[^;]+') do
local i, keywords = 1, {}
for tmp in string.gmatch(outfit, '[^,]+') do
table.insert(keywords, tmp)
i = i + 1
end
if(i > 0) then
local ret = NpcSystem.getParameter('outfit' .. n)
if(ret ~= nil) then
self:parseList(keywords, ret)
else
print('[Warning] NpcSystem:', 'Missing \'outfit' .. n .. '\' parameter, skipping...')
end
else
print('[Warning] NpcSystem:', 'No keywords found for outfit set #' .. n .. ', skipping...')
end
n = n + 1
end
end
function OutfitModule:parseList(keywords, data)
local outfit, items = nil, {}
for list in string.gmatch(data, '[^;]+') do
local a, b, c, d, e = nil, nil, nil, nil, 1
for tmp in string.gmatch(list, '[^,]+') do
if(e == 1) then
a = tmp
elseif(e == 2) then
b = tmp
elseif(e == 3) then
c = tmp
elseif(e == 4) then
d = tmp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in outfit list while parsing ' .. (outfit == nil and 'outfit' or 'item') .. '.', tmp, list)
end
e = e + 1
end
if(outfit == nil) then
outfit = {tonumber(a), tonumber(b), getBooleanFromString©, d}
elseif(a ~= nil) then
local tmp = tonumber(a)
if((tmp ~= nil or tostring(a) == "money") and b ~= nil and c ~= nil) then
a = tmp or 20000
tmp = tonumber(d)
if(tmp == nil) then
tmp = -1
end
items[a] = {b, tmp, c}
else
print('[Warning] NpcSystem:', 'Missing parameter(s) for outfit items.', b, c, d)
end
else
print('[Warning] NpcSystem:', 'Missing base parameter for outfit items.', a)
end
end
if(type(outfit) == 'table') then
local tmp = true
for i = 1, 2 do
if(outfit == nil) then
tmp = false
break
end
end
if(tmp and table.maxn(items) > 0) then
self:addOutfit(keywords, outfit, items)
else
print('[Warning] NpcSystem:', 'Invalid outfit, addon or empty items pool.', data)
end
end
end
function OutfitModule:addOutfit(keywords, outfit, items)
table.insert(self.outfits, keywords[1])
local parameters = {
outfit = outfit[1],
addon = outfit[2],
premium = outfit[3],
gender = nil,
items = items,
module = self
}
if(outfit[4] ~= nil) then
local tmp = string.lower(tostring(outfit[5]))
if(tmp == 'male' or tmp == '1') then
parameters.gender = 1
elseif(tmp == 'female' or tmp == '0') then
parameters.gender = 0
end
end
for i, name in pairs(keywords) do
local words = {}
table.insert(words, name)
local node = self.npcHandler.keywordHandler:addKeyword(words, OutfitModule.obtain, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
function OutfitModule.obtain(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local i, items, size = 0, nil, table.maxn(parameters.items)
for k, v in pairs(parameters.items) do
if(v[1] ~= "storageset") then
i = i + 1
if(items ~= nil) then
if(i == size) then
items = items .. " and "
else
items = items .. ", "
end
else
items = ""
end
if(tonumber(v[1]) ~= nil and tonumber(v[1]) > 1) then
items = items .. v[1] .. " "
end
items = items .. v[3]
end
end
module.npcHandler:say('Do you want ' .. keywords[1] .. ' ' .. (addon == 0 and "outfit" or "addon") .. ' for ' .. items .. '?', cid)
return true
end
function OutfitModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parent = node:getParent():getParameters()
if(isPlayerPremiumCallback(cid) or not parent.premium) then
if(not OUTFITMODULE_FUNCTION[2](cid, parent.outfit, parent.addon)) then
if(parent.addon == 0 or OUTFITMODULE_FUNCTION[2](cid, parent.outfit)) then
if(parent.gender == nil or parent.gender == getPlayerSex(cid)) then
local found = true
for k, v in pairs(parent.items) do
local tmp = tonumber(v[1])
if(tmp == nil) then
if(v[1] == "storagecheck") then
if(getCreatureStorage(cid, k) < v[2]) then
found = false
end
elseif(v[1] == "outfitid") then
if(not canPlayerWearOutfitId(cid, k, v[2])) then
found = false
end
elseif(v[1] == "outfit") then
if(not canPlayerWearOutfit(cid, k, v[2])) then
found = false
end
else
found = false
end
elseif(k == 20000) then
if(getPlayerMoney(cid) < tmp) then
found = false
end
elseif(getPlayerItemCount(cid, k, v[2]) < tmp) then
found = false
end
if(not found) then
break
end
end
if(found) then
for k, v in pairs(parent.items) do
if(tonumber(v[1]) ~= nil) then
if(k == 20000) then
doPlayerRemoveMoney(cid, v[1])
else
doPlayerRemoveItem(cid, k, v[1], v[2])
end
elseif(v[1] == "storageset") then
doCreatureSetStorage(cid, k, v[2])
end
end
module.npcHandler:say('It was a pleasure to dress you.', cid)
OUTFITMODULE_FUNCTION[1](cid, parent.outfit, parent.addon)
doPlayerSetStorageValue(cid, parent.storageId, storage)
else
module.npcHandler:say('You don\'t have these items!', cid)
end
else
module.npcHandler:say('Sorry, this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. ' is not for your gender.', cid)
end
else
module.npcHandler:say('I will not dress you with addon of outfit you cannot wear!', cid)
end
else
module.npcHandler:say('You alrady have this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. '!', cid)
end
else
module.npcHandler:say('Sorry, I dress only premium players.', cid)
end
module.npcHandler:resetNpc()
return true
end
-- onDecline keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function OutfitModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid)
module.npcHandler:resetNpc()
return true
end
function OutfitModule.listOutfits(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local msg, size = nil, table.maxn(module.outfits)
if(size > 0) then
for i, outfit in ipairs(module.outfits) do
if(msg ~= nil) then
if(i == size) then
msg = msg .. " and "
else
msg = msg .. ", "
end
else
msg = "I can dress you into "
end
msg = msg .. "{" .. outfit .. "}"
end
else
msg = "Sorry, I have nothing to offer right now."
end
module.npcHandler:say(msg .. ".", cid)
module.npcHandler:resetNpc()
return true
end
ShopModule = {
npcHandler = nil,
yesNode = nil,
noNode = nil,
noText = '',
maxCount = 100,
amount = 0
}
-- Add it to the parseable module list.
Modules.parseableModules['module_shop'] = ShopModule
-- Creates a new instance of ShopModule
function ShopModule:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Parses all known parameters.
function ShopModule:parseParameters()
local ret = NpcSystem.getParameter('shop_buyable')
if(ret ~= nil) then
self:parseBuyable(ret)
end
local ret = NpcSystem.getParameter('shop_sellable')
if(ret ~= nil) then
self:parseSellable(ret)
end
local ret = NpcSystem.getParameter('shop_buyable_containers')
if(ret ~= nil) then
self:parseBuyableContainers(ret)
end
end
-- Parse a string contaning a set of buyable items.
function ShopModule:parseBuyable(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
cost = tonumber(temp)
elseif(i == 4) then
subType = tonumber(temp)
elseif(i == 5) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item)
end
i = i + 1
end
if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
if(itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
self:addBuyableItem(nil, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
end
elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
local names = {}
table.insert(names, name)
self:addBuyableItem(names, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost)
end
end
end
-- Parse a string contaning a set of sellable items.
function ShopModule:parseSellable(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, itemid, cost, realName = 1, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
cost = tonumber(temp)
elseif(i == 4) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in sellable items parameter.', temp, item)
end
i = i + 1
end
if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
if(itemid ~= nil and cost ~= nil) then
self:addSellableItem(nil, itemid, cost, realName)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
end
elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
local names = {}
table.insert(names, name)
self:addSellableItem(names, itemid, cost, realName)
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost)
end
end
end
-- Parse a string contaning a set of buyable items.
function ShopModule:parseBuyableContainers(data)
for item in string.gmatch(data, '[^;]+') do
local i, name, container, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil, nil
for temp in string.gmatch(item, '[^,]+') do
if(i == 1) then
name = temp
elseif(i == 2) then
itemid = tonumber(temp)
elseif(i == 3) then
itemid = tonumber(temp)
elseif(i == 4) then
cost = tonumber(temp)
elseif(i == 5) then
subType = tonumber(temp)
elseif(i == 6) then
realName = temp
else
print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item)
end
i = i + 1
end
if(name ~= nil and container ~= nil and itemid ~= nil and cost ~= nil) then
if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then
print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item)
else
local names = {}
table.insert(names, name)
self:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
end
else
print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, container, itemid, cost)
end
end
end
-- Initializes the module and associates handler to it.
function ShopModule:init(handler)
self.npcHandler = handler
self.yesNode = KeywordNode:new(SHOP_YESWORD, ShopModule.onConfirm, {module = self})
self.noNode = KeywordNode:new(SHOP_NOWORD, ShopModule.onDecline, {module = self})
self.noText = handler:getMessage(MESSAGE_DECLINE)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
for i, word in pairs(SHOP_TRADEREQUEST) do
local obj = {}
table.insert(obj, word)
obj.callback = SHOP_TRADEREQUEST.callback or ShopModule.messageMatcher
handler.keywordHandler:addKeyword(obj, ShopModule.requestTrade, {module = self})
end
end
end
-- Custom message matching callback function for requesting trade messages.
function ShopModule.messageMatcher(keywords, message)
for i, word in pairs(keywords) do
if(type(word) == 'string' and string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]')) then
return true
end
end
return false
end
-- Resets the module-specific variables.
function ShopModule:reset()
self.amount = 0
end
-- Function used to match a number value from a string.
function ShopModule:getCount(message)
local ret, b, e = 1, string.find(message, PATTERN_COUNT)
if(b ~= nil and e ~= nil) then
ret = tonumber(string.sub(message, b, e))
end
return math.max(1, math.min(self.maxCount, ret))
end
-- Adds a new buyable item.
-- names = A table containing one or more strings of alternative names to this item. Used only for old buy/sell system.
-- itemid = The itemid of the buyable item
-- cost = The price of one single item
-- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addBuyableItem(names, itemid, cost, subType, realName)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
local item = {
id = itemid,
buy = cost,
sell = -1,
subType = subType or 1,
name = realName or getItemNameById(itemid)
}
for i, shopItem in ipairs(self.npcHandler.shopItems) do
if(shopItem.id == item.id and shopItem.subType == item.subType) then
if(item.sell ~= shopItem.sell) then
item.sell = shopItem.sell
end
self.npcHandler.shopItems = item
item = nil
break
end
end
if(item ~= nil) then
table.insert(self.npcHandler.shopItems, item)
end
end
if(names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE) then
local parameters = {
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_BUY_ITEM,
module = self,
realName = realName or getItemNameById(itemid),
subType = subType or 1
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'buy')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- Adds a new buyable container of items.
-- names = A table containing one or more strings of alternative names to this item.
-- container = Backpack, bag or any other itemid of container where bought items will be stored
-- itemid = The itemid of the buyable item
-- cost = The price of one single item
-- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
if(names ~= nil) then
local parameters = {
container = container,
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_BUY_ITEM_CONTAINER,
module = self,
realName = realName or getItemNameById(itemid),
subType = subType or 1
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'buy')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- Adds a new sellable item.
-- names = A table containing one or more strings of alternative names to this item. Used only by old buy/sell system.
-- itemid = The itemid of the sellable item
-- cost = The price of one single item
-- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used)
function ShopModule:addSellableItem(names, itemid, cost, realName)
if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then
local item = {
id = itemid,
buy = -1,
sell = cost,
subType = 1,
name = realName or getItemNameById(itemid)
}
for i, shopItem in ipairs(self.npcHandler.shopItems) do
if(shopItem.id == item.id and shopItem.subType == item.subType) then
if(item.buy ~= shopItem.buy) then
item.buy = shopItem.buy
end
self.npcHandler.shopItems = item
item = nil
break
end
end
if(item ~= nil) then
table.insert(self.npcHandler.shopItems, item)
end
end
if(names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE) then
local parameters = {
itemid = itemid,
cost = cost,
eventType = SHOPMODULE_SELL_ITEM,
module = self,
realName = realName or getItemNameById(itemid)
}
for i, name in pairs(names) do
local keywords = {}
table.insert(keywords, 'sell')
table.insert(keywords, name)
local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
node:addChildKeywordNode(self.yesNode)
node:addChildKeywordNode(self.noNode)
end
end
end
-- onModuleReset callback function. Calls ShopModule:reset()
function ShopModule:callbackOnModuleReset()
self:reset()
return true
end
-- Callback onBuy() function. If you wish, you can change certain Npc to use your onBuy().
function ShopModule:callbackOnBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local shopItem = nil
for _, item in ipairs(self.npcHandler.shopItems) do
if(item.id == itemid and item.subType == subType) then
shopItem = item
break
end
end
if(shopItem == nil) then
print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Item not found on shopItems list')
return false
end
if(shopItem.buy == -1) then
error("[shopModule.onSell]", "Attempt to purchase an item which only sellable")
return false
end
local backpack, totalCost = 1988, amount * shopItem.buy
if(inBackpacks) then
totalCost = totalCost + (math.max(1, math.floor(amount / getContainerCapById(backpack))) * 20)
end
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = amount,
[TAG_TOTALCOST] = totalCost,
[TAG_ITEMNAME] = shopItem.name
}
if(getPlayerMoney(cid) < totalCost) then
local msg = self.npcHandler:getMessage(MESSAGE_NEEDMONEY)
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
return false
end
local subType = shopItem.subType or 1
local a, b = doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, backpack)
if(a < amount) then
local msgId = MESSAGE_NEEDMORESPACE
if(a == 0) then
msgId = MESSAGE_NEEDSPACE
end
local msg = self.npcHandler:getMessage(msgId)
parseInfo[TAG_ITEMCOUNT] = a
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
if(a > 0) then
doPlayerRemoveMoney(cid, ((a * shopItem.buy) + (b * 20)))
return true
end
return false
end
local msg = self.npcHandler:getMessage(MESSAGE_BOUGHT)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo))
doPlayerRemoveMoney(cid, totalCost)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return true
end
-- Callback onSell() function. If you wish, you can change certain Npc to use your onSell().
function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local shopItem = nil
for _, item in ipairs(self.npcHandler.shopItems) do
if(item.id == itemid and item.subType == subType) then
shopItem = item
break
end
end
if(shopItem == nil) then
error("[shopModule.onBuy]", "Item not found on shopItems list")
return false
end
if(shopItem.sell == -1) then
error("[shopModule.onSell]", "Attempt to sell an item which is only buyable")
return false
end
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = amount,
[TAG_TOTALCOST] = amount * shopItem.sell,
[TAG_ITEMNAME] = shopItem.name
}
if(subType < 1 or getItemInfo(itemid).stackable) then
subType = -1
end
if(doPlayerRemoveItem(cid, itemid, amount, subType)) then
local msg = self.npcHandler:getMessage(MESSAGE_SOLD)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo))
doPlayerAddMoney(cid, amount * shopItem.sell)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return true
end
local msg = self.npcHandler:getMessage(MESSAGE_NEEDITEM)
doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo))
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.npcHandler.talkStart[cid] = os.time()
else
self.npcHandler.talkStart = os.time()
end
return false
end
-- Callback for requesting a trade window with the NPC.
function ShopModule.requestTrade(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
if(table.maxn(module.npcHandler.shopItems) == 0) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_NOSHOP), parseInfo)
module.npcHandler:say(msg, cid)
return true
end
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_SENDTRADE), parseInfo)
openShopWindow(cid, module.npcHandler.shopItems,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
module.npcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
end,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
module.npcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
end
)
module.npcHandler:say(msg, cid)
return true
end
-- onConfirm keyword callback function. Sells/buys the actual item.
function ShopModule.onConfirm(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parentParameters = node:getParent():getParameters()
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parentParameters.cost * module.amount,
[TAG_ITEMNAME] = parentParameters.realName
}
if(parentParameters.eventType == SHOPMODULE_SELL_ITEM) then
local ret = doPlayerSellItem(cid, parentParameters.itemid, module.amount, parentParameters.cost * module.amount)
if(ret) then
local msg = module.npcHandler:getMessage(MESSAGE_ONSELL)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGITEM)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM) then
local ret = doPlayerBuyItem(cid, parentParameters.itemid, module.amount, parentParameters.cost * module.amount, parentParameters.subType)
if(ret) then
if parentParameters.itemid == ITEM_PARCEL then
doPlayerBuyItem(cid, ITEM_LABEL, module.amount, 0, parentParameters.subType)
end
local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM_CONTAINER) then
local ret = doPlayerBuyItemContainer(cid, parentParameters.container, parentParameters.itemid, module.amount, parentParameters.cost * module.amount, parentParameters.subType)
if(ret) then
local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
else
local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
end
module.npcHandler:resetNpc()
return true
end
-- onDecliune keyword callback function. Generally called when the player sais 'no' after wanting to buy an item.
function ShopModule.onDecline(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local parentParameters = node:getParent():getParameters()
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parentParameters.cost * module.amount,
[TAG_ITEMNAME] = parentParameters.realName
}
local msg = module.npcHandler:parseMessage(module.noText, parseInfo)
module.npcHandler:say(msg, cid)
module.npcHandler:resetNpc()
return true
end
-- tradeItem callback function. Makes the npc say the message defined by MESSAGE_BUY or MESSAGE_SELL
function ShopModule.tradeItem(cid, message, keywords, parameters, node)
local module = parameters.module
if(not module.npcHandler:isFocused(cid)) then
return false
end
local count = module:getCount(message)
module.amount = count
local parseInfo = {
[TAG_PLAYERNAME] = getPlayerName(cid),
[TAG_ITEMCOUNT] = module.amount,
[TAG_TOTALCOST] = parameters.cost * module.amount,
[TAG_ITEMNAME] = parameters.realName
}
if(parameters.eventType == SHOPMODULE_SELL_ITEM) then
local msg = module.npcHandler:getMessage(MESSAGE_SELL)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
elseif(parameters.eventType == SHOPMODULE_BUY_ITEM) then
local msg = module.npcHandler:getMessage(MESSAGE_BUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
elseif(parameters.eventType == SHOPMODULE_BUY_ITEM_CONTAINER) then
local msg = module.npcHandler:getMessage(MESSAGE_BUY)
msg = module.npcHandler:parseMessage(msg, parseInfo)
module.npcHandler:say(msg, cid)
end
return true
end
end

Npchandler:

 

-- Advanced NPC System (Created by Jiddo),
-- Modified by Talaturen.
if(NpcHandler == nil) then
-- Constant talkdelay behaviors.
TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
TALKDELAY_EVENT = 2 -- Not yet implemented
-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
-- Constant conversation behaviors.
CONVERSATION_DEFAULT = 0 -- Conversation through default window, like it was before 8.2 update.
CONVERSATION_PRIVATE = 1 -- Conversation through NPCs chat window, as of 8.2 update. (Default)
--Small Note: Private conversations also means the NPC will use multi-focus system.
-- Currently applied conversation behavior. CONVERSATION_PRIVATE is default.
NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE
-- Constant indexes for defining default messages.
MESSAGE_GREET = 1 -- When the player greets the npc.
MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.
MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.
MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.
MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.
MESSAGE_SOLD = 8 -- When the player sold something through the shop window.
MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.
MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window.
MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have.
MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.
MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item
MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space
MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.
MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.
MESSAGE_DECLINE = 17 -- When the player says no to something.
MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player
MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any
MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window
MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.
MESSAGE_PLACEDINQUEUE = 22 -- When the player has been placed in the costumer queue.
-- Constant indexes for callback functions. These are also used for module callback ids.
CALLBACK_CREATURE_APPEAR = 1
CALLBACK_CREATURE_DISAPPEAR = 2
CALLBACK_CREATURE_SAY = 3
CALLBACK_ONTHINK = 4
CALLBACK_GREET = 5
CALLBACK_FAREWELL = 6
CALLBACK_MESSAGE_DEFAULT = 7
CALLBACK_PLAYER_ENDTRADE = 8
CALLBACK_PLAYER_CLOSECHANNEL = 9
CALLBACK_ONBUY = 10
CALLBACK_ONSELL = 11
-- Addidional module callback ids
CALLBACK_MODULE_INIT = 12
CALLBACK_MODULE_RESET = 13
-- Constant strings defining the keywords to replace in the default messages.
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'
NpcHandler = {
keywordHandler = nil,
focuses = nil,
talkStart = nil,
idleTime = 90,
talkRadius = 4,
talkDelayTime = 1, -- Seconds to delay outgoing messages.
queue = nil,
talkDelay = nil,
callbackFunctions = nil,
modules = nil,
shopItems = nil, -- They must be here since ShopModule uses "static" functions
messages = {
-- These are the default replies of all npcs. They can/should be changed individually for each npc.
[MESSAGE_GREET] = 'Welcome, |PLAYERNAME|! I have been expecting you.',
[MESSAGE_FAREWELL] = 'Good bye, |PLAYERNAME|!',
[MESSAGE_BUY] = 'Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONBUY] = 'It was a pleasure doing business with you.',
[MESSAGE_BOUGHT] = 'Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
[MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONSELL] = 'Thank you for this |ITEMNAME|, |PLAYERNAME| gold.',
[MESSAGE_SOLD] = 'Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
[MESSAGE_MISSINGMONEY] = 'Sorry, you don\'t have enough money.',
[MESSAGE_NEEDMONEY] = 'You do not have enough money.',
[MESSAGE_MISSINGITEM] = 'You don\'t even have that item, |PLAYERNAME|!',
[MESSAGE_NEEDITEM] = 'You do not have this object.',
[MESSAGE_NEEDSPACE] = 'You do not have enough capacity.',
[MESSAGE_NEEDMORESPACE] = 'You do not have enough capacity for all items.',
[MESSAGE_IDLETIMEOUT] = 'Next, please!',
[MESSAGE_WALKAWAY] = 'How rude!',
[MESSAGE_DECLINE] = 'Not good enough, is it... ?',
[MESSAGE_SENDTRADE] = 'Here\'s my offer, |PLAYERNAME|. Don\'t you like it?',
[MESSAGE_NOSHOP] = 'Sorry, I\'m not offering anything.',
[MESSAGE_ONCLOSESHOP] = 'Thank you, come back when you want something more.',
[MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|! I am already talking to you...',
[MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, please wait for your turn. There are |QUEUESIZE| customers before you.'
}
}
-- Creates a new NpcHandler with an empty callbackFunction stack.
function NpcHandler:new(keywordHandler)
local obj = {}
obj.callbackFunctions = {}
obj.modules = {}
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
obj.focuses = {}
obj.talkStart = {}
else
obj.queue = Queue:new(obj)
obj.focuses = 0
obj.talkStart = 0
end
obj.talkDelay = {}
obj.keywordHandler = keywordHandler
obj.messages = {}
obj.shopItems = {}
setmetatable(obj.messages, self.messages)
self.messages.__index = self.messages
setmetatable(obj, self)
self.__index = self
return obj
end
-- Re-defines the maximum idle time allowed for a player when talking to this npc.
function NpcHandler:setMaxIdleTime(newTime)
self.idleTime = newTime
end
-- Attackes a new keyword handler to this npchandler
function NpcHandler:setKeywordHandler(newHandler)
self.keywordHandler = newHandler
end
-- Function used to change the focus of this npc.
function NpcHandler:addFocus(newFocus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(self:isFocused(newFocus)) then
return
end
table.insert(self.focuses, newFocus)
else
self.focuses = newFocus
end
self:updateFocus()
end
NpcHandler.changeFocus = NpcHandler.addFocus --"changeFocus" looks better for CONVERSATION_DEFAULT
-- Function used to verify if npc is focused to certain player
function NpcHandler:isFocused(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for k,v in pairs(self.focuses) do
if v == focus then
return true
end
end
return false
end
return (self.focuses == focus)
end
-- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
-- Should also be called whenever a new player is focused.
function NpcHandler:updateFocus()
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
doNpcSetCreatureFocus(focus)
return
end
end
doNpcSetCreatureFocus(0)
else
doNpcSetCreatureFocus(self.focuses)
end
end
-- Used when the npc should un-focus the player.
function NpcHandler:releaseFocus(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(not self:isFocused(focus)) then
return
end
local pos = nil
for k,v in pairs(self.focuses) do
if v == focus then
pos = k
end
end
table.remove(self.focuses, pos)
self.talkStart[focus] = nil
closeShopWindow(focus) --Even if it can not exist, we need to prevent it.
self:updateFocus()
else
closeShopWindow(focus)
self:changeFocus(0)
end
end
-- Returns the callback function with the specified id or nil if no such callback function exists.
function NpcHandler:getCallback(id)
local ret = nil
if(self.callbackFunctions ~= nil) then
ret = self.callbackFunctions[id]
end
return ret
end
-- Changes the callback function for the given id to callback.
function NpcHandler:setCallback(id, callback)
if(self.callbackFunctions ~= nil) then
self.callbackFunctions[id] = callback
end
end
-- Adds a module to this npchandler and inits it.
function NpcHandler:addModule(module)
if(self.modules == nil or module == nil) then
return false
end
module:init(self)
if(module.parseParameters ~= nil) then
module:parseParameters()
end
table.insert(self.modules, module)
return true
end
-- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
function NpcHandler:processModuleCallback(id, ...)
local ret = true
for i, module in pairs(self.modules) do
local tmpRet = true
if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
tmpRet = module:callbackOnCreatureAppear(unpack(arg))
elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
tmpRet = module:callbackOnCreatureDisappear(unpack(arg))
elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
tmpRet = module:callbackOnCreatureSay(unpack(arg))
elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then
tmpRet = module:callbackOnPlayerEndTrade(unpack(arg))
elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then
tmpRet = module:callbackOnPlayerCloseChannel(unpack(arg))
elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then
tmpRet = module:callbackOnBuy(...)
elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then
tmpRet = module:callbackOnSell(unpack(arg))
elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
tmpRet = module:callbackOnThink(unpack(arg))
elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
tmpRet = module:callbackOnGreet(unpack(arg))
elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
tmpRet = module:callbackOnFarewell(unpack(arg))
elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
tmpRet = module:callbackOnMessageDefault(unpack(arg))
elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
tmpRet = module:callbackOnModuleReset(unpack(arg))
end
if(not tmpRet) then
ret = false
break
end
end
return ret
end
-- Returns the message represented by id.
function NpcHandler:getMessage(id)
local ret = nil
if(self.messages ~= nil) then
ret = self.messages[id]
end
return ret
end
-- Changes the default response message with the specified id to newMessage.
function NpcHandler:setMessage(id, newMessage)
if(self.messages ~= nil) then
self.messages[id] = newMessage
end
end
-- Translates all message tags found in msg using parseInfo
function NpcHandler:parseMessage(msg, parseInfo)
local ret = msg
for search, replace in pairs(parseInfo) do
ret = string.gsub(ret, search, replace)
end
return ret
end
-- Makes sure the npc un-focuses the currently focused player
function NpcHandler:unGreet(cid)
if(not self:isFocused(cid)) then
return
end
local callback = self:getCallback(CALLBACK_FAREWELL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_FAREWELL)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_FAREWELL)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
-- Greets a new player.
function NpcHandler:greet(cid)
if(cid ~= 0) then
local callback = self:getCallback(CALLBACK_GREET)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_GREET, cid)) then
local msg = self:getMessage(MESSAGE_GREET)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
self:addFocus(cid)
self:say(msg, cid)
end
end
end
end
-- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
function NpcHandler:onCreatureAppear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
--
end
end
end
-- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
function NpcHandler:onCreatureDisappear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
function NpcHandler:onCreatureSay(cid, class, msg)
local callback = self:getCallback(CALLBACK_CREATURE_SAY)
if(callback == nil or callback(cid, class, msg)) then
if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, class, msg)) then
if(not self:isInRange(cid)) then
return
end
if(self.keywordHandler ~= nil) then
if((self:isFocused(cid) and (class == TALKTYPE_PRIVATE_PN or NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT)) or not self:isFocused(cid)) then
local ret = self.keywordHandler:processMessage(cid, msg)
if(not ret) then
local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
if(callback ~= nil and callback(cid, class, msg)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
else
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
end
end
end
end
end
-- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.
function NpcHandler:onPlayerEndTrade(cid)
local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid, class, msg)) then
if(self:isFocused(cid)) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)
self:say(msg, cid)
end
end
end
end
-- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.
function NpcHandler:onPlayerCloseChannel(cid)
local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid, class, msg)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.
function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONBUY)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.
function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONSELL)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.
function NpcHandler:onThink()
local callback = self:getCallback(CALLBACK_ONTHINK)
if(callback == nil or callback()) then
if(NPCHANDLER_TALKDELAY == TALKDELAY_ONTHINK) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for cid, talkDelay in pairs(self.talkDelay) do
if(talkDelay.time ~= nil and talkDelay.message ~= nil and os.time() >= talkDelay.time) then
selfSay(talkDelay.message, cid)
self.talkDelay[cid] = nil
end
end
elseif(self.talkDelay.time ~= nil and self.talkDelay.message ~= nil and os.time() >= self.talkDelay.time) then
selfSay(self.talkDelay.message)
self.talkDelay.time = nil
self.talkDelay.message = nil
end
end
if(self:processModuleCallback(CALLBACK_ONTHINK)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
if(not self:isInRange(focus)) then
self:onWalkAway(focus)
elseif((os.time() - self.talkStart[focus]) > self.idleTime) then
self:unGreet(focus)
else
self:updateFocus()
end
end
end
elseif(self.focuses ~= 0) then
if(not self:isInRange(self.focuses)) then
self:onWalkAway(self.focuses)
elseif(os.time()-self.talkStart > self.idleTime) then
self:unGreet(self.focuses)
else
self:updateFocus()
end
end
end
end
end
-- Tries to greet the player with the given cid.
function NpcHandler:onGreet(cid)
if(self:isInRange(cid)) then
if(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE) then
if(not self:isFocused(cid)) then
self:greet(cid)
return
end
elseif(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT) then
if(self.focuses == 0) then
self:greet(cid)
elseif(self.focuses == cid) then
local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
else
if(not self.queue:isInQueue(cid)) then
self.queue:push(cid)
end
local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
end
end
end
end
-- Simply calls the underlying unGreet function.
function NpcHandler:onFarewell(cid)
self:unGreet(cid)
end
-- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
function NpcHandler:onWalkAway(cid)
if(self:isFocused(cid)) then
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_WALKAWAY)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
end
-- Returns true if cid is within the talkRadius of this npc.
function NpcHandler:isInRange(cid)
local distance = getDistanceTo(cid) or -1
if(distance == -1) then
return false
end
return (distance <= self.talkRadius)
end
-- Resets the npc into it's initial state (in regard of the keyrodhandler).
-- All modules are also receiving a reset call through their callbackOnModuleReset function.
function NpcHandler:resetNpc()
if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
self.keywordHandler:reset()
end
end
-- Makes the npc represented by this instance of NpcHandler say something.
-- This implements the currently set type of talkdelay.
-- shallDelay is a boolean value. If it is false, the message is not delayed. Default value is false.
function NpcHandler:say(message, focus, shallDelay)
local shallDelay = shallDelay or false
if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or not shallDelay) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
selfSay(message, focus)
return
else
selfSay(message)
return
end
end
-- TODO: Add an event handling method for delayed messages
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkDelay[focus] = {
message = message,
time = os.time() + self.talkDelayTime,
}
else
self.talkDelay = {
message = message,
time = os.time() + self.talkDelayTime
}
end
end
end

 

 

fis isso e agora deu erro em todos Npc(travel,blesser...)

 

[Error - Npc interface]
data/npc/scripts/travel/boat.lua
Description:
data/npc/scripts/travel/boat.lua:3: attempt to index global 'NpcSystem' (a nil v
alue)
[Warning - NpcScript::NpcScript] Cannot load script: data/npc/scripts/travel/boa
t.lua
Link para o comentário
Compartilhar em outros sites

  • 0

Coloca esse npchandler

 

 

-- Advanced NPC System (Created by Jiddo),
-- Modified by TheForgottenServer Team,
-- Modified by OTX Server Team.

if(NpcHandler == nil) then
-- Constant talkdelay behaviors.
TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
TALKDELAY_EVENT = 2 -- Not yet implemented

-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK

-- Constant conversation behaviors.
CONVERSATION_DEFAULT = 0 -- Conversation through default window, like it was before 8.2 update.
CONVERSATION_PRIVATE = 1 -- Conversation through NPCs chat window, as of 8.2 update. (Default)
--Small Note: Private conversations also means the NPC will use multi-focus system.

-- Currently applied conversation behavior. CONVERSATION_PRIVATE is default.
NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE

-- Constant indexes for defining default messages.
MESSAGE_GREET = 1 -- When the player greets the npc.
MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.
MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.
MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.
MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.
MESSAGE_SOLD = 8 -- When the player sold something through the shop window.
MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.
MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window.
MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have.
MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.
MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item
MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space
MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.
MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.
MESSAGE_DECLINE = 17 -- When the player says no to something.
MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player
MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any
MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window
MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.
MESSAGE_PLACEDINQUEUE = 22 -- When the player has been placed in the costumer queue.

-- Constant indexes for callback functions. These are also used for module callback ids.
CALLBACK_CREATURE_APPEAR = 1
CALLBACK_CREATURE_DISAPPEAR = 2
CALLBACK_CREATURE_SAY = 3
CALLBACK_ONTHINK = 4
CALLBACK_GREET = 5
CALLBACK_FAREWELL = 6
CALLBACK_MESSAGE_DEFAULT = 7
CALLBACK_PLAYER_ENDTRADE = 8
CALLBACK_PLAYER_CLOSECHANNEL = 9
CALLBACK_ONBUY = 10
CALLBACK_ONSELL = 11

-- Addidional module callback ids
CALLBACK_MODULE_INIT = 12
CALLBACK_MODULE_RESET = 13

-- Constant strings defining the keywords to replace in the default messages.
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'

NpcHandler = {
keywordHandler = nil,
focuses = nil,
talkStart = nil,
idleTime = 300,
talkRadius = 3,
talkDelayTime = 400, -- Seconds to delay outgoing messages.
queue = nil,
talkDelay = nil,
callbackFunctions = nil,
modules = nil,
shopItems = nil, -- They must be here since ShopModule uses "static" functions
messages = {
-- These are the default replies of all npcs. They can/should be changed individually for each npc.
[MESSAGE_GREET] = 'Bem-vindo, |PLAYERNAME|! Estava esperando você.',
[MESSAGE_FAREWELL] = 'Até logo, |PLAYERNAME|!',
[MESSAGE_BUY] = 'Quer comprar |ITEMCOUNT| |ITEMNAME| para |TOTALCOST| gold coins?',
[MESSAGE_ONBUY] = 'Foi um prazer fazer negócios com você.',
[MESSAGE_BOUGHT] = 'Comprei |ITEMCOUNT|x |ITEMNAME| para |TOTALCOST| gold.',
[MESSAGE_SELL] = 'Você quer vender |ITEMCOUNT| |ITEMNAME| para |TOTALCOST| gold coins?',
[MESSAGE_ONSELL] = 'Obrigado por isso |ITEMNAME|, |PLAYERNAME| gold.',
[MESSAGE_SOLD] = 'Vendida |ITEMCOUNT|x |ITEMNAME| para |TOTALCOST| gold.',
[MESSAGE_MISSINGMONEY] = 'Desculpe, que você não tem dinheiro suficiente.',
[MESSAGE_NEEDMONEY] = 'Você não tem dinheiro suficiente.',
[MESSAGE_MISSINGITEM] = 'Você não tem esse item, |PLAYERNAME|!',
[MESSAGE_NEEDITEM] = 'Você não tem este objeto.',
[MESSAGE_NEEDSPACE] = 'Você não tem capacidade suficiente.',
[MESSAGE_NEEDMORESPACE] = 'Você não tem capacidade suficiente para todos os itens.',
[MESSAGE_IDLETIMEOUT] = 'Próximo, por favor!',
[MESSAGE_WALKAWAY] = 'Que falta de educação!',
[MESSAGE_DECLINE] = 'Não é bom o suficiente, é... ?',
[MESSAGE_SENDTRADE] = 'Eis a minha oferta, |PLAYERNAME|. Você não gosta?',
[MESSAGE_NOSHOP] = 'Desculpa, eu não estou oferecendo qualquer coisa.',
[MESSAGE_ONCLOSESHOP] = 'Obrigado, volte quando você quer algo mais.',
[MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|! Já estou falando com você...',
[MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, por favor, espere a sua vez. Existem |QUEUESIZE| clientes antes de você.'
}
}

-- Creates a new NpcHandler with an empty callbackFunction stack.
function NpcHandler:new(keywordHandler)
local obj = {}
obj.callbackFunctions = {}
obj.modules = {}
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
obj.focuses = {}
obj.talkStart = {}
else
obj.queue = Queue:new(obj)
obj.focuses = 0
obj.talkStart = 0
end

obj.talkDelay = {}
obj.keywordHandler = keywordHandler
obj.messages = {}
obj.shopItems = {}

setmetatable(obj.messages, self.messages)
self.messages.__index = self.messages

setmetatable(obj, self)
self.__index = self
return obj
end

-- Re-defines the maximum idle time allowed for a player when talking to this npc.
function NpcHandler:setMaxIdleTime(newTime)
self.idleTime = newTime
end

-- Attackes a new keyword handler to this npchandler
function NpcHandler:setKeywordHandler(newHandler)
self.keywordHandler = newHandler
end

-- Function used to change the focus of this npc.
function NpcHandler:addFocus(newFocus)
if(not isCreature(newFocus)) then
return
end

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(self:isFocused(newFocus, true)) then
return
end

table.insert(self.focuses, newFocus)
else
self.focuses = newFocus
end

self:updateFocus(true)
end
NpcHandler.changeFocus = NpcHandler.addFocus -- "changeFocus" looks better for CONVERSATION_DEFAULT

-- Function used to verify if npc is focused to certain player
function NpcHandler:isFocused(focus, creatureCheck)
local creatureCheck = creatureCheck or false
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for k, v in pairs(self.focuses) do
if(v == focus) then
if(creatureCheck or isCreature(v)) then
return true
end

self:unsetFocus(focus, k)
return false
end
end

return false
end

if(creatureCheck or isCreature(self.focuses)) then
return self.focuses == focus
end

self:changeFocus(0)
return false
end

-- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
-- Should also be called whenever a new player is focused.
function NpcHandler:updateFocus(creatureCheck)
local creatureCheck = creatureCheck or false
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for _, focus in pairs(self.focuses) do
if(creatureCheck or isCreature(focus)) then
doNpcSetCreatureFocus(focus)
return
end
end
elseif(creatureCheck or isCreature(self.focuses)) then
doNpcSetCreatureFocus(self.focuses)
return
end

doNpcSetCreatureFocus(0)
end

-- Used when the npc should un-focus the player.
function NpcHandler:releaseFocus(focus)
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
if(not self:isFocused(focus)) then
return
end

local pos = nil
for k, v in pairs(self.focuses) do
if(v == focus) then
pos = k
end
end

if(pos ~= nil) then
closeShopWindow(focus)
self:unsetFocus(focus, pos)
end
elseif(self.focuses == focus) then
if(isCreature(focus)) then
closeShopWindow(focus)
end

self:changeFocus(0)
end
end

-- Internal un-focusing function, beware using!
function NpcHandler:unsetFocus(focus, pos)
if(type(self.focuses) ~= "table" or pos == nil or self.focuses[pos] == nil) then
return
end

table.remove(self.focuses, pos)
self.talkStart[focus] = nil
self:updateFocus()
end

-- Returns the callback function with the specified id or nil if no such callback function exists.
function NpcHandler:getCallback(id)
local ret = nil
if(self.callbackFunctions ~= nil) then
ret = self.callbackFunctions[id]
end

return ret
end

-- Changes the callback function for the given id to callback.
function NpcHandler:setCallback(id, callback)
if(self.callbackFunctions ~= nil) then
self.callbackFunctions[id] = callback
end
end

-- Adds a module to this npchandler and inits it.
function NpcHandler:addModule(module)
if(self.modules == nil or module == nil) then
return false
end

module:init(self)
if(module.parseParameters ~= nil) then
module:parseParameters()
end

table.insert(self.modules, module)
return true
end

-- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
function NpcHandler:processModuleCallback(id, ...)
local ret = true
for _, module in pairs(self.modules) do
local tmpRet = true
if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
tmpRet = module:callbackOnCreatureAppear(...)
elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
tmpRet = module:callbackOnCreatureDisappear(...)
elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
tmpRet = module:callbackOnCreatureSay(...)
elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then
tmpRet = module:callbackOnPlayerEndTrade(...)
elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then
tmpRet = module:callbackOnPlayerCloseChannel(...)
elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then
tmpRet = module:callbackOnBuy(...)
elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then
tmpRet = module:callbackOnSell(...)
elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
tmpRet = module:callbackOnThink(...)
elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
tmpRet = module:callbackOnGreet(...)
elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
tmpRet = module:callbackOnFarewell(...)
elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
tmpRet = module:callbackOnMessageDefault(...)
elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
tmpRet = module:callbackOnModuleReset(...)
end

if(not tmpRet) then
ret = false
break
end
end

return ret
end

-- Returns the message represented by id.
function NpcHandler:getMessage(id)
local ret = nil
if(self.messages ~= nil) then
ret = self.messages[id]
end

return ret
end

-- Changes the default response message with the specified id to newMessage.
function NpcHandler:setMessage(id, newMessage)
if(self.messages ~= nil) then
self.messages[id] = newMessage
end
end

-- Translates all message tags found in msg using parseInfo
function NpcHandler:parseMessage(msg, parseInfo)
for search, replace in pairs(parseInfo) do
if(replace ~= nil) then
msg = msg:gsub(search, replace)
end
end

return msg
end

-- Makes sure the npc un-focuses the currently focused player
function NpcHandler:unGreet(cid)
if(not self:isFocused(cid)) then
return
end

local callback = self:getCallback(CALLBACK_FAREWELL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_FAREWELL)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_FAREWELL)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)

self:say(msg, cid, 0, true)
self:releaseFocus(cid)
if(not isPlayerGhost(cid)) then
self:say(msg)
end
end
end
end
end

-- Greets a new player.
function NpcHandler:greet(cid)
local callback = self:getCallback(CALLBACK_GREET)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_GREET, cid)) then
local msg = self:getMessage(MESSAGE_GREET)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)

if(not isPlayerGhost(cid)) then
self:say(msg)
end
self:addFocus(cid)
self:say(msg, cid)
end
end
end

-- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
function NpcHandler:onCreatureAppear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
--
end
end
end

-- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
function NpcHandler:onCreatureDisappear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end

-- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
function NpcHandler:onCreatureSay(cid, class, msg)
local callback = self:getCallback(CALLBACK_CREATURE_SAY)
if(callback == nil or callback(cid, class, msg)) then
if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, class, msg)) then
if(not self:isInRange(cid)) then
return
end

if(self.keywordHandler ~= nil) then
if((self:isFocused(cid) and (class == TALKTYPE_PRIVATE_PN or NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT)) or not self:isFocused(cid)) then
local ret = self.keywordHandler:processMessage(cid, msg)
if(not ret) then
local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
if(callback ~= nil and callback(cid, class, msg)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
elseif(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
self.talkStart[cid] = os.time()
else
self.talkStart = os.time()
end
end
end
end
end
end

-- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.
function NpcHandler:onPlayerEndTrade(cid)
local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid)) then
if(self:isFocused(cid)) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)
self:say(msg, cid)
end
end
end
end

-- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.
function NpcHandler:onPlayerCloseChannel(cid)
local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end

-- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.
function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONBUY)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end

-- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.
function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONSELL)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end

-- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.
function NpcHandler:onThink()
local callback = self:getCallback(CALLBACK_ONTHINK)
if(callback == nil or callback()) then
for i, speech in pairs(self.talkDelay) do
if((speech.cid == nil or speech.cid == 0) and speech.time ~= nil and speech.message ~= nil) then
if(os.mtime() >= speech.time) then
selfSay(speech.message)
self.talkDelay = nil
end
elseif(isCreature(speech.cid) and speech.start ~= nil and speech.time ~= nil and speech.message ~= nil) then
if(os.mtime() >= speech.time) then
local talkStart = (NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT and self.talkStart[speech.cid] or self.talkStart)
if(speech.force or (self:isFocused(speech.cid) and talkStart == speech.start)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
selfSay(speech.message, speech.cid)
else
selfSay(speech.message)
end
end

self.talkDelay = nil
end
else
self.talkDelay = nil
end
end

if(self:processModuleCallback(CALLBACK_ONTHINK)) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
for _, focus in pairs(self.focuses) do
if(focus ~= nil) then
if(not self:isInRange(focus)) then
self:onWalkAway(focus)
elseif((os.time() - self.talkStart[focus]) > self.idleTime) then
self:unGreet(focus)
else
self:updateFocus()
end
end
end
elseif(self.focuses ~= 0) then
if(not self:isInRange(self.focuses)) then
self:onWalkAway(self.focuses)
elseif((os.time() - self.talkStart) > self.idleTime) then
self:unGreet(self.focuses)
else
self:updateFocus()
end
end
end
end
end

-- Tries to greet the player with the given cid.
function NpcHandler:onGreet(cid)
if(self:isInRange(cid)) then
if(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE) then
if(not self:isFocused(cid)) then
self:greet(cid)
return
end
elseif(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT) then
if(self.focuses == 0) then
self:greet(cid)
elseif(self.focuses == cid) then
local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
else
if(not self.queue:isInQueue(cid)) then
self.queue:push(cid)
end

local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }
msg = self:parseMessage(msg, parseInfo)
self:say(msg)
end
end
end
end

-- Simply calls the underlying unGreet function.
function NpcHandler:onFarewell(cid)
self:unGreet(cid)
end

-- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
function NpcHandler:onWalkAway(cid)
if(self:isFocused(cid)) then
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self.queue == nil or not self.queue:greetNext()) then
local msg = self:getMessage(MESSAGE_WALKAWAY)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) or -1 }
msg = self:parseMessage(msg, parseInfo)

self:say(msg, cid, 0, true)
self:releaseFocus(cid)
self:say(msg)
end
end
end
end
end

-- Returns true if cid is within the talkRadius of this npc.
function NpcHandler:isInRange(cid)
if not isPlayer(cid) then
return false
end

local distance = getNpcDistanceTo(cid) or -1
return distance ~= -1 and distance <= self.talkRadius
end

-- Resets the npc into it's initial state (in regard of the keyrodhandler).
-- All modules are also receiving a reset call through their callbackOnModuleReset function.
function NpcHandler:resetNpc()
if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
self.keywordHandler:reset()
end
end

-- Makes the npc represented by this instance of NpcHandler say something.
-- This implements the currently set type of talkdelay.
function NpcHandler:say(message, focus, delay, force)
local delay = delay or 0
if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or delay <= 0) then
if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
selfSay(message, focus)
else
selfSay(message)
end
return
end

-- TODO: Add an event handling method for delayed messages
table.insert(self.talkDelay, {
id = getNpcId(),
cid = focus,
message = message,
time = os.mtime() + (delay and delay or self.talkDelayTime),
start = os.time(),
force = force or false
})
end
end

 

Link para o comentário
Compartilhar em outros sites

  • 0

Coloca esse npchandler

 

 

-- Advanced NPC System (Created by Jiddo),

-- Modified by TheForgottenServer Team,

-- Modified by OTX Server Team.

 

if(NpcHandler == nil) then

-- Constant talkdelay behaviors.

TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.

TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)

TALKDELAY_EVENT = 2 -- Not yet implemented

 

-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.

NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK

 

-- Constant conversation behaviors.

CONVERSATION_DEFAULT = 0 -- Conversation through default window, like it was before 8.2 update.

CONVERSATION_PRIVATE = 1 -- Conversation through NPCs chat window, as of 8.2 update. (Default)

--Small Note: Private conversations also means the NPC will use multi-focus system.

 

-- Currently applied conversation behavior. CONVERSATION_PRIVATE is default.

NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE

 

-- Constant indexes for defining default messages.

MESSAGE_GREET = 1 -- When the player greets the npc.

MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.

MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.

MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.

MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.

MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.

MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.

MESSAGE_SOLD = 8 -- When the player sold something through the shop window.

MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.

MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window.

MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have.

MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.

MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item

MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space

MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.

MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.

MESSAGE_DECLINE = 17 -- When the player says no to something.

MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player

MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any

MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window

MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.

MESSAGE_PLACEDINQUEUE = 22 -- When the player has been placed in the costumer queue.

 

-- Constant indexes for callback functions. These are also used for module callback ids.

CALLBACK_CREATURE_APPEAR = 1

CALLBACK_CREATURE_DISAPPEAR = 2

CALLBACK_CREATURE_SAY = 3

CALLBACK_ONTHINK = 4

CALLBACK_GREET = 5

CALLBACK_FAREWELL = 6

CALLBACK_MESSAGE_DEFAULT = 7

CALLBACK_PLAYER_ENDTRADE = 8

CALLBACK_PLAYER_CLOSECHANNEL = 9

CALLBACK_ONBUY = 10

CALLBACK_ONSELL = 11

 

-- Addidional module callback ids

CALLBACK_MODULE_INIT = 12

CALLBACK_MODULE_RESET = 13

 

-- Constant strings defining the keywords to replace in the default messages.

TAG_PLAYERNAME = '|PLAYERNAME|'

TAG_ITEMCOUNT = '|ITEMCOUNT|'

TAG_TOTALCOST = '|TOTALCOST|'

TAG_ITEMNAME = '|ITEMNAME|'

TAG_QUEUESIZE = '|QUEUESIZE|'

 

NpcHandler = {

keywordHandler = nil,

focuses = nil,

talkStart = nil,

idleTime = 300,

talkRadius = 3,

talkDelayTime = 400, -- Seconds to delay outgoing messages.

queue = nil,

talkDelay = nil,

callbackFunctions = nil,

modules = nil,

shopItems = nil, -- They must be here since ShopModule uses "static" functions

messages = {

-- These are the default replies of all npcs. They can/should be changed individually for each npc.

[MESSAGE_GREET] = 'Bem-vindo, |PLAYERNAME|! Estava esperando você.',

[MESSAGE_FAREWELL] = 'Até logo, |PLAYERNAME|!',

[MESSAGE_BUY] = 'Quer comprar |ITEMCOUNT| |ITEMNAME| para |TOTALCOST| gold coins?',

[MESSAGE_ONBUY] = 'Foi um prazer fazer negócios com você.',

[MESSAGE_BOUGHT] = 'Comprei |ITEMCOUNT|x |ITEMNAME| para |TOTALCOST| gold.',

[MESSAGE_SELL] = 'Você quer vender |ITEMCOUNT| |ITEMNAME| para |TOTALCOST| gold coins?',

[MESSAGE_ONSELL] = 'Obrigado por isso |ITEMNAME|, |PLAYERNAME| gold.',

[MESSAGE_SOLD] = 'Vendida |ITEMCOUNT|x |ITEMNAME| para |TOTALCOST| gold.',

[MESSAGE_MISSINGMONEY] = 'Desculpe, que você não tem dinheiro suficiente.',

[MESSAGE_NEEDMONEY] = 'Você não tem dinheiro suficiente.',

[MESSAGE_MISSINGITEM] = 'Você não tem esse item, |PLAYERNAME|!',

[MESSAGE_NEEDITEM] = 'Você não tem este objeto.',

[MESSAGE_NEEDSPACE] = 'Você não tem capacidade suficiente.',

[MESSAGE_NEEDMORESPACE] = 'Você não tem capacidade suficiente para todos os itens.',

[MESSAGE_IDLETIMEOUT] = 'Próximo, por favor!',

[MESSAGE_WALKAWAY] = 'Que falta de educação!',

[MESSAGE_DECLINE] = 'Não é bom o suficiente, é... ?',

[MESSAGE_SENDTRADE] = 'Eis a minha oferta, |PLAYERNAME|. Você não gosta?',

[MESSAGE_NOSHOP] = 'Desculpa, eu não estou oferecendo qualquer coisa.',

[MESSAGE_ONCLOSESHOP] = 'Obrigado, volte quando você quer algo mais.',

[MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|! Já estou falando com você...',

[MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, por favor, espere a sua vez. Existem |QUEUESIZE| clientes antes de você.'

}

}

 

-- Creates a new NpcHandler with an empty callbackFunction stack.

function NpcHandler:new(keywordHandler)

local obj = {}

obj.callbackFunctions = {}

obj.modules = {}

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

obj.focuses = {}

obj.talkStart = {}

else

obj.queue = Queue:new(obj)

obj.focuses = 0

obj.talkStart = 0

end

 

obj.talkDelay = {}

obj.keywordHandler = keywordHandler

obj.messages = {}

obj.shopItems = {}

 

setmetatable(obj.messages, self.messages)

self.messages.__index = self.messages

 

setmetatable(obj, self)

self.__index = self

return obj

end

 

-- Re-defines the maximum idle time allowed for a player when talking to this npc.

function NpcHandler:setMaxIdleTime(newTime)

self.idleTime = newTime

end

 

-- Attackes a new keyword handler to this npchandler

function NpcHandler:setKeywordHandler(newHandler)

self.keywordHandler = newHandler

end

 

-- Function used to change the focus of this npc.

function NpcHandler:addFocus(newFocus)

if(not isCreature(newFocus)) then

return

end

 

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

if(self:isFocused(newFocus, true)) then

return

end

 

table.insert(self.focuses, newFocus)

else

self.focuses = newFocus

end

 

self:updateFocus(true)

end

NpcHandler.changeFocus = NpcHandler.addFocus -- "changeFocus" looks better for CONVERSATION_DEFAULT

 

-- Function used to verify if npc is focused to certain player

function NpcHandler:isFocused(focus, creatureCheck)

local creatureCheck = creatureCheck or false

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

for k, v in pairs(self.focuses) do

if(v == focus) then

if(creatureCheck or isCreature(v)) then

return true

end

 

self:unsetFocus(focus, k)

return false

end

end

 

return false

end

 

if(creatureCheck or isCreature(self.focuses)) then

return self.focuses == focus

end

 

self:changeFocus(0)

return false

end

 

-- This function should be called on each onThink and makes sure the npc faces the player it is talking to.

-- Should also be called whenever a new player is focused.

function NpcHandler:updateFocus(creatureCheck)

local creatureCheck = creatureCheck or false

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

for _, focus in pairs(self.focuses) do

if(creatureCheck or isCreature(focus)) then

doNpcSetCreatureFocus(focus)

return

end

end

elseif(creatureCheck or isCreature(self.focuses)) then

doNpcSetCreatureFocus(self.focuses)

return

end

 

doNpcSetCreatureFocus(0)

end

 

-- Used when the npc should un-focus the player.

function NpcHandler:releaseFocus(focus)

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

if(not self:isFocused(focus)) then

return

end

 

local pos = nil

for k, v in pairs(self.focuses) do

if(v == focus) then

pos = k

end

end

 

if(pos ~= nil) then

closeShopWindow(focus)

self:unsetFocus(focus, pos)

end

elseif(self.focuses == focus) then

if(isCreature(focus)) then

closeShopWindow(focus)

end

 

self:changeFocus(0)

end

end

 

-- Internal un-focusing function, beware using!

function NpcHandler:unsetFocus(focus, pos)

if(type(self.focuses) ~= "table" or pos == nil or self.focuses[pos] == nil) then

return

end

 

table.remove(self.focuses, pos)

self.talkStart[focus] = nil

self:updateFocus()

end

 

-- Returns the callback function with the specified id or nil if no such callback function exists.

function NpcHandler:getCallback(id)

local ret = nil

if(self.callbackFunctions ~= nil) then

ret = self.callbackFunctions[id]

end

 

return ret

end

 

-- Changes the callback function for the given id to callback.

function NpcHandler:setCallback(id, callback)

if(self.callbackFunctions ~= nil) then

self.callbackFunctions[id] = callback

end

end

 

-- Adds a module to this npchandler and inits it.

function NpcHandler:addModule(module)

if(self.modules == nil or module == nil) then

return false

end

 

module:init(self)

if(module.parseParameters ~= nil) then

module:parseParameters()

end

 

table.insert(self.modules, module)

return true

end

 

-- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.

function NpcHandler:processModuleCallback(id, ...)

local ret = true

for _, module in pairs(self.modules) do

local tmpRet = true

if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then

tmpRet = module:callbackOnCreatureAppear(...)

elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then

tmpRet = module:callbackOnCreatureDisappear(...)

elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then

tmpRet = module:callbackOnCreatureSay(...)

elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then

tmpRet = module:callbackOnPlayerEndTrade(...)

elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then

tmpRet = module:callbackOnPlayerCloseChannel(...)

elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then

tmpRet = module:callbackOnBuy(...)

elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then

tmpRet = module:callbackOnSell(...)

elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then

tmpRet = module:callbackOnThink(...)

elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then

tmpRet = module:callbackOnGreet(...)

elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then

tmpRet = module:callbackOnFarewell(...)

elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then

tmpRet = module:callbackOnMessageDefault(...)

elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then

tmpRet = module:callbackOnModuleReset(...)

end

 

if(not tmpRet) then

ret = false

break

end

end

 

return ret

end

 

-- Returns the message represented by id.

function NpcHandler:getMessage(id)

local ret = nil

if(self.messages ~= nil) then

ret = self.messages[id]

end

 

return ret

end

 

-- Changes the default response message with the specified id to newMessage.

function NpcHandler:setMessage(id, newMessage)

if(self.messages ~= nil) then

self.messages[id] = newMessage

end

end

 

-- Translates all message tags found in msg using parseInfo

function NpcHandler:parseMessage(msg, parseInfo)

for search, replace in pairs(parseInfo) do

if(replace ~= nil) then

msg = msg:gsub(search, replace)

end

end

 

return msg

end

 

-- Makes sure the npc un-focuses the currently focused player

function NpcHandler:unGreet(cid)

if(not self:isFocused(cid)) then

return

end

 

local callback = self:getCallback(CALLBACK_FAREWELL)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_FAREWELL)) then

if(self.queue == nil or not self.queue:greetNext()) then

local msg = self:getMessage(MESSAGE_FAREWELL)

local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }

msg = self:parseMessage(msg, parseInfo)

 

self:say(msg, cid, 0, true)

self:releaseFocus(cid)

if(not isPlayerGhost(cid)) then

self:say(msg)

end

end

end

end

end

 

-- Greets a new player.

function NpcHandler:greet(cid)

local callback = self:getCallback(CALLBACK_GREET)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_GREET, cid)) then

local msg = self:getMessage(MESSAGE_GREET)

local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }

msg = self:parseMessage(msg, parseInfo)

 

if(not isPlayerGhost(cid)) then

self:say(msg)

end

self:addFocus(cid)

self:say(msg, cid)

end

end

end

 

-- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.

function NpcHandler:onCreatureAppear(cid)

local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then

--

end

end

end

 

-- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.

function NpcHandler:onCreatureDisappear(cid)

local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then

if(self:isFocused(cid)) then

self:unGreet(cid)

end

end

end

end

 

-- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.

function NpcHandler:onCreatureSay(cid, class, msg)

local callback = self:getCallback(CALLBACK_CREATURE_SAY)

if(callback == nil or callback(cid, class, msg)) then

if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, class, msg)) then

if(not self:isInRange(cid)) then

return

end

 

if(self.keywordHandler ~= nil) then

if((self:isFocused(cid) and (class == TALKTYPE_PRIVATE_PN or NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT)) or not self:isFocused(cid)) then

local ret = self.keywordHandler:processMessage(cid, msg)

if(not ret) then

local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)

if(callback ~= nil and callback(cid, class, msg)) then

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

self.talkStart[cid] = os.time()

else

self.talkStart = os.time()

end

end

elseif(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

self.talkStart[cid] = os.time()

else

self.talkStart = os.time()

end

end

end

end

end

end

 

-- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.

function NpcHandler:onPlayerEndTrade(cid)

local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid)) then

if(self:isFocused(cid)) then

local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }

local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)

self:say(msg, cid)

end

end

end

end

 

-- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.

function NpcHandler:onPlayerCloseChannel(cid)

local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid)) then

if(self:isFocused(cid)) then

self:unGreet(cid)

end

end

end

end

 

-- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.

function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)

local callback = self:getCallback(CALLBACK_ONBUY)

if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then

if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then

--

end

end

end

 

-- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.

function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)

local callback = self:getCallback(CALLBACK_ONSELL)

if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then

if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then

--

end

end

end

 

-- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.

function NpcHandler:onThink()

local callback = self:getCallback(CALLBACK_ONTHINK)

if(callback == nil or callback()) then

for i, speech in pairs(self.talkDelay) do

if((speech.cid == nil or speech.cid == 0) and speech.time ~= nil and speech.message ~= nil) then

if(os.mtime() >= speech.time) then

selfSay(speech.message)

self.talkDelay = nil

end

elseif(isCreature(speech.cid) and speech.start ~= nil and speech.time ~= nil and speech.message ~= nil) then

if(os.mtime() >= speech.time) then

local talkStart = (NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT and self.talkStart[speech.cid] or self.talkStart)

if(speech.force or (self:isFocused(speech.cid) and talkStart == speech.start)) then

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

selfSay(speech.message, speech.cid)

else

selfSay(speech.message)

end

end

 

self.talkDelay = nil

end

else

self.talkDelay = nil

end

end

 

if(self:processModuleCallback(CALLBACK_ONTHINK)) then

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

for _, focus in pairs(self.focuses) do

if(focus ~= nil) then

if(not self:isInRange(focus)) then

self:onWalkAway(focus)

elseif((os.time() - self.talkStart[focus]) > self.idleTime) then

self:unGreet(focus)

else

self:updateFocus()

end

end

end

elseif(self.focuses ~= 0) then

if(not self:isInRange(self.focuses)) then

self:onWalkAway(self.focuses)

elseif((os.time() - self.talkStart) > self.idleTime) then

self:unGreet(self.focuses)

else

self:updateFocus()

end

end

end

end

end

 

-- Tries to greet the player with the given cid.

function NpcHandler:onGreet(cid)

if(self:isInRange(cid)) then

if(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE) then

if(not self:isFocused(cid)) then

self:greet(cid)

return

end

elseif(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT) then

if(self.focuses == 0) then

self:greet(cid)

elseif(self.focuses == cid) then

local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)

local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }

msg = self:parseMessage(msg, parseInfo)

self:say(msg)

else

if(not self.queue:isInQueue(cid)) then

self.queue:push(cid)

end

 

local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)

local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }

msg = self:parseMessage(msg, parseInfo)

self:say(msg)

end

end

end

end

 

-- Simply calls the underlying unGreet function.

function NpcHandler:onFarewell(cid)

self:unGreet(cid)

end

 

-- Should be called on this npc's focus if the distance to focus is greater then talkRadius.

function NpcHandler:onWalkAway(cid)

if(self:isFocused(cid)) then

local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)

if(callback == nil or callback(cid)) then

if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then

if(self.queue == nil or not self.queue:greetNext()) then

local msg = self:getMessage(MESSAGE_WALKAWAY)

local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) or -1 }

msg = self:parseMessage(msg, parseInfo)

 

self:say(msg, cid, 0, true)

self:releaseFocus(cid)

self:say(msg)

end

end

end

end

end

 

-- Returns true if cid is within the talkRadius of this npc.

function NpcHandler:isInRange(cid)

if not isPlayer(cid) then

return false

end

 

local distance = getNpcDistanceTo(cid) or -1

return distance ~= -1 and distance <= self.talkRadius

end

 

-- Resets the npc into it's initial state (in regard of the keyrodhandler).

-- All modules are also receiving a reset call through their callbackOnModuleReset function.

function NpcHandler:resetNpc()

if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then

self.keywordHandler:reset()

end

end

 

-- Makes the npc represented by this instance of NpcHandler say something.

-- This implements the currently set type of talkdelay.

function NpcHandler:say(message, focus, delay, force)

local delay = delay or 0

if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or delay <= 0) then

if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then

selfSay(message, focus)

else

selfSay(message)

end

return

end

 

-- TODO: Add an event handling method for delayed messages

table.insert(self.talkDelay, {

id = getNpcId(),

cid = focus,

message = message,

time = os.mtime() + (delay and delay or self.talkDelayTime),

start = os.time(),

force = force or false

})

end

end

 

desculpe a demora,mais esse ai deu o msm erro

Link para o comentário
Compartilhar em outros sites

×
×
  • Criar Novo...