Ir para conteúdo

Posts Recomendados

Eai galera...

 

Os npcs do meu ot apresentam dois bugs...

O primeiro acontece quando vc tenta comprar um item juntamente com a backpack (Buy with backpack), o npc acrescenta os 20 gps da backpack ao preço do item que vc deseja comprar, mas na hora de te dar o item com sua backpack, ele te da apenas o item, e "cata" os 20gps pra ele...

 

34277227.jpg

 

Uploaded with ImageShack.us

 

 

Percebam na janela do trade, que o preço da small health potion e apenas 20gps, mas como podem ver na mensagem verde no centro da tela, o npc comprou 40 gps, porque a opçao de comprar item na backpack estava ativada, e ele entregou ao player apenas a poçao, sem a backpack...

 

O segundo bug, acredito que este seja facil de corrigir, so e necessario um certo conhecimento em lua, ele acontece sempre que voce conversa com o npc, ao inves do npc falar apenas uma vez, ele fala duas vezes, uma fala de cada cor, uma sobre a outra...

 

semttulonzf.jpg

 

Uploaded with ImageShack.us

 

Reparem que a mensagem de azul esta sobreposta em cima da mensagem de amarelo...

 

Bom...

Como o problema vem acontecendo com a maioria dos npcs, se nao todos... Imagino que seja algum script da pasta lib...

 

keywordhandler.lua

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

if(KeywordHandler == nil) then
BEHAVIOR_SIMPLE = 1 -- Does not support nested keywords. If you choose this setting you must use a variable such as 'talkState' to keep track of how to handle keywords.
BEHAVIOR_NORMAL = 2 -- Default behvaior. If a sub-keyword is not found, then the root is searched, not the parent hierarchy,
BEHAVIOR_NORMAL_EXTENDED = 3 -- Same as BEHAVIOR_NORMAL but it also searches through the last node's parent.
BEHAVIOR_COMPLEX = 4 -- Extended behavior. It a sub-keyword is not found, then the entire keyword hierarchy is searched upwards until root is reached.

-- BEHAVIOR_NORMAL_EXTENDED is recommended as it (probably) mimics the behavior of real Tibia's NPCs the most.
--		However, you are strongly recommended to test some (or all) other settings as well as it might suit you better.
--		Also note that not much difference can be seen with the different settings unless you have a npc with a quite heavy
--		nestled keyword hierarchy.
-- Note: BEHAVIOR_SIMPLE should not be used unless you have any special reason to do so as it forces you to keep track of talkStates etc.
--		This was pretty much the method used in the 2.0x versions of this system. It is here mainly for compability issues.
KEYWORD_BEHAVIOR = BEHAVIOR_NORMAL_EXTENDED

KeywordNode = {
	keywords = nil,
	callback = nil,
	parameters = nil,
	children = nil,
	parent = nil
}

-- Created a new keywordnode with the given keywords, callback function and parameters and without any childNodes.
function KeywordNode:new(keys, func, param)
	local obj = {}
	obj.keywords = keys
	obj.callback = func
	obj.parameters = param
	obj.children = {}
	setmetatable(obj, self)
	self.__index = self
	return obj
end

-- Calls the underlying callback function if it is not nil.
function KeywordNode:processMessage(cid, message)
	return (self.callback == nil or self.callback(cid, message, self.keywords, self.parameters, self))
end

-- Returns true if message contains all patterns/strings found in keywords.
function KeywordNode:checkMessage(message)
	local ret = true
	if(self.keywords.callback ~= nil) then
		return self.keywords.callback(self.keywords, message)
	end
	for i,v in ipairs(self.keywords) do
		if(type(v) == 'string') then
			local a, b = string.find(message, v)
			if(a == nil or b == nil) then
				ret = false
				break
			end
		end
	end
	return ret
end

-- Returns the parent of this node or nil if no such node exists.
function KeywordNode:getParent()
	return self.parent
end

-- Returns an array of the callback function parameters assosiated with this node.
function KeywordNode:getParameters()
	return self.parameters
end

-- Returns an array of the triggering keywords assosiated with this node.
function KeywordNode:getKeywords()
	return self.keywords
end

-- Adds a childNode to this node. Creates the childNode based on the parameters (k = keywords, c = callback, p = parameters)
function KeywordNode:addChildKeyword(keywords, callback, parameters)
	local new = KeywordNode:new(keywords, callback, parameters)
	return self:addChildKeywordNode(new)
end

-- Adds a pre-created childNode to this node. Should be used for example if several nodes should have a common child.
function KeywordNode:addChildKeywordNode(childNode)
	table.insert(self.children, childNode)
	childNode.parent = self
	return childNode
end

KeywordHandler = {
	root = nil,
	lastNode = nil
}

-- Creates a new keywordhandler with an empty rootnode.
function KeywordHandler:new()
	local obj = {}
	obj.root = KeywordNode:new(nil, nil, nil)
	setmetatable(obj, self)
	self.__index = self
	return obj
end

-- Resets the lastNode field, and this resetting the current position in the node hierarchy to root.
function KeywordHandler:reset()
	self.lastNode = nil
end

-- Makes sure the correct childNode of lastNode gets a chance to process the message.
--	The behavior of this function depends much on the KEYWORD_BEHAVIOR.
function KeywordHandler:processMessage(cid, message)
	local node = self:getLastNode()
	if(node == nil) then
		error('No root node found.')
		return false
	end
	if(KEYWORD_BEHAVIOR == BEHAVIOR_SIMPLE) then
		local ret = self:processNodeMessage(node, cid, message)
		if(ret) then
			return true
		end
	elseif(KEYWORD_BEHAVIOR == BEHAVIOR_NORMAL or KEYWORD_BEHAVIOR == BEHAVIOR_NORMAL_EXTENDED) then
		local ret = self:processNodeMessage(node, cid, message)
		if(ret) then
			return true
		end
		if(KEYWORD_BEHAVIOR == BEHAVIOR_NORMAL_EXTENDED and node:getParent()) then
			node = node:getParent() -- Search through the parent.
			local ret = self:processNodeMessage(node, cid, message)
			if(ret) then
				return true
			end
		end
		if(node ~= self:getRoot()) then
			node = self:getRoot() -- Search through the root.
			local ret = self:processNodeMessage(node, cid, message)
			if(ret) then
				return true
			end
		end
	elseif(KEYWORD_BEHAVIOR == BEHAVIOR_COMPLEX) then
		while true do
			local ret = self:processNodeMessage(node, cid, message)
			if(ret) then
				return true
			end

			if(node:getParent() ~= nil) then
				node = node:getParent() -- Move one step upwards in the hierarchy.
			else
				break
			end
		end
	else
		error('Unknown keyword behavior.')
	end
	return false
end

-- Tries to process the given message using the node parameter's children and calls the node's callback function if found.
--	Returns the childNode which processed the message or nil if no such node was found.
function KeywordHandler:processNodeMessage(node, cid, message)
	local messageLower = string.lower(message)
	for i, childNode in pairs(node.children) do
		if(childNode:checkMessage(messageLower)) then
			local oldLast = self.lastNode
			self.lastNode = childNode
			childNode.parent = node -- Make sure node is the parent of childNode (as one node can be parent to several nodes).
			if(childNode:processMessage(cid, message)) then
				return true
			else
				self.lastNode = oldLast
			end
		end
	end
	return false
end

-- Returns the root keywordnode
function KeywordHandler:getRoot()
	return self.root
end

-- Returns the last processed keywordnode or root if no last node is found.
function KeywordHandler:getLastNode()
	if(KEYWORD_BEHAVIOR == BEHAVIOR_SIMPLE) then
		return self:getRoot()
	else
		return self.lastNode or self:getRoot()
	end
end

-- Adds a new keyword to the root keywordnode. Returns the new node.
function KeywordHandler:addKeyword(keys, callback, parameters)
	return self:getRoot():addChildKeyword(keys, callback, parameters)
end

-- Moves the current position in the keyword hierarchy count steps upwards. Count defalut value = 1.
--	This function MIGHT not work properly yet. Use at your own risk.
function KeywordHandler:moveUp(count)
	local steps = count
	if(steps == nil) then
		steps = 1
	end
	for i = 1, steps,1 do
		if(self.lastNode == nil) then
			break
		else
			self.lastNode = self.lastNode:getParent() or self:getRoot()
		end
	end
	return self.lastNode
end
end

main.lua

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

if(NpcSystem == nil) then
-- Loads the underlying classes of the npcsystem.
dofile(getDataDir() .. 'npc/lib/npcsystem/keywordhandler.lua')
dofile(getDataDir() .. 'npc/lib/npcsystem/queue.lua')
dofile(getDataDir() .. 'npc/lib/npcsystem/npchandler.lua')
dofile(getDataDir() .. 'npc/lib/npcsystem/modules.lua')

-- Global npc constants:

-- Keyword nestling behavior. For more information look at the top of keywordhandler.lua
KEYWORD_BEHAVIOR = BEHAVIOR_NORMAL_EXTENDED

-- Greeting and unGreeting keywords. For more information look at the top of modules.lua
FOCUS_GREETWORDS = {'hi', 'hello', 'hey'}
FOCUS_FAREWELLWORDS = {'bye', 'farewell', 'cya'}

-- The word for requesting trade window. For more information look at the top of modules.lua
SHOP_TRADEREQUEST = {'offer', 'trade'}

-- The word for accepting/declining an offer. CAN ONLY CONTAIN ONE FIELD! For more information look at the top of modules.lua
SHOP_YESWORD = {'yes'}
SHOP_NOWORD = {'no'}

-- Pattern used to get the amount of an item a player wants to buy/sell.
PATTERN_COUNT = '%d+'

-- Talkdelay behavior. For more information, look at the top of npchandler.lua.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK

-- Conversation behavior. For more information, look at the top of npchandler.lua.
NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE

-- Constant strings defining the keywords to replace in the default messages.
--	For more information, look at the top of npchandler.lua...
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'

NpcSystem = {}

-- Gets an npcparameter with the specified key. Returns nil if no such parameter is found.
function NpcSystem.getParameter(key)
	local ret = getNpcParameter(tostring(key))
	if((type(ret) == 'number' and ret == 0) or ret == nil) then
		return nil
	else
		return ret
	end
end

-- Parses all known parameters for the npc. Also parses parseable modules.
function NpcSystem.parseParameters(npcHandler)
	local ret = NpcSystem.getParameter('idletime')
	if(ret ~= nil) then
		npcHandler.idleTime = tonumber(ret)
	end
	local ret = NpcSystem.getParameter('talkradius')
	if(ret ~= nil) then
		npcHandler.talkRadius = tonumber(ret)
	end
	local ret = NpcSystem.getParameter('message_greet')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_GREET, ret)
	end
	local ret = NpcSystem.getParameter('message_farewell')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_FAREWELL, ret)
	end
	local ret = NpcSystem.getParameter('message_decline')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_DECLINE, ret)
	end
	local ret = NpcSystem.getParameter('message_needmorespace')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDMORESPACE, ret)
	end
	local ret = NpcSystem.getParameter('message_needspace')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDSPACE, ret)
	end
	local ret = NpcSystem.getParameter('message_sendtrade')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_SENDTRADE, ret)
	end
	local ret = NpcSystem.getParameter('message_noshop')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NOSHOP, ret)
	end
	local ret = NpcSystem.getParameter('message_oncloseshop')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ONCLOSESHOP, ret)
	end
	local ret = NpcSystem.getParameter('message_onbuy')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ONBUY, ret)
	end
	local ret = NpcSystem.getParameter('message_onsell')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ONSELL, ret)
	end
	local ret = NpcSystem.getParameter('message_missingmoney')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_MISSINGMONEY, ret)
	end
	local ret = NpcSystem.getParameter('message_needmoney')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDMONEY, ret)
	end
	local ret = NpcSystem.getParameter('message_missingitem')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_MISSINGITEM, ret)
	end
	local ret = NpcSystem.getParameter('message_needitem')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDITEM, ret)
	end
	local ret = NpcSystem.getParameter('message_idletimeout')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_IDLETIMEOUT, ret)
	end
	local ret = NpcSystem.getParameter('message_walkaway')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_WALKAWAY, ret)
	end
	local ret = NpcSystem.getParameter('message_alreadyfocused')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ALREADYFOCUSED, ret)
	end
	local ret = NpcSystem.getParameter('message_placedinqueue')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_PLACEDINQUEUE, ret)
	end
	local ret = NpcSystem.getParameter('message_buy')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_BUY, ret)
	end
	local ret = NpcSystem.getParameter('message_sell')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_SELL, ret)
	end
	local ret = NpcSystem.getParameter('message_bought')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_BOUGHT, ret)
	end
	local ret = NpcSystem.getParameter('message_sold')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_SOLD, ret)
	end

	-- Parse modules.
	for parameter, module in pairs(Modules.parseableModules) do
		local ret = NpcSystem.getParameter(parameter)
		if(ret ~= nil) then
			local number = tonumber(ret)
			if(number ~= nil and number ~= 0) then
				npcHandler:addModule(module:new())
			end
		end
	end
end
end

modules.lua

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

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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.say - Call without any npcHandler instance.')
		return false
	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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.promotePlayer - Call without any npcHandler instance.')
		return false
	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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.learnSpell - Call without any npcHandler instance.')
		return false
	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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.bless - Call without any npcHandler instance.')
		return false
	end

	if(not getBooleanFromString(getConfigValue('blessings'))) then
		npcHandler:say("Sorry, but Gods moved back my permission to bless anyone.", cid)
		return false
	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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.travel - Call without any npcHandler instance.')
		return false
	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 - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...')
			end
		else
			print('[Warning - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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
		module.npcHandler: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 - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Missing \'outfit' .. n .. '\' parameter, skipping...')
			end
		else
			print('[Warning - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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(c), 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 - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Missing parameter(s) for outfit items.', b, c, d)
			end
		else
			print('[Warning - ' .. getCreatureName(getNpcId()) .. '] 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[i] == nil) then
				tmp = false
				break
			end
		end

		if(tmp and table.maxn(items) > 0) then
			self:addOutfit(keywords, outfit, items)
		else
			print('[Warning - ' .. getCreatureName(getNpcId()) .. '] 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 already 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 tmp in string.gmatch(item, '[^,]+') do
			if(i == 1) then
				name = tmp
			elseif(i == 2) then
				itemid = tonumber(tmp)
			elseif(i == 3) then
				cost = tonumber(tmp)
			elseif(i == 4) then
				subType = tonumber(tmp)
			elseif(i == 5) then
				realName = tmp
			else
				print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Unknown parameter found in buyable items parameter.', tmp, item)
			end

			i = i + 1
		end

		if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then
			if(itemid ~= nil and cost ~= nil) then
				if(isItemFluidContainer(itemid) and subType == nil) then
					print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'SubType missing for parameter item:', item)
				else
					self:addBuyableItem(nil, itemid, cost, subType, realName)
				end
			else
				print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost)
			end
		elseif(name ~= nil and itemid ~= nil and cost ~= nil) then
			if(isItemFluidContainer(itemid) and subType == nil) then
				print('[Warning - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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(isItemFluidContainer(itemid) and subType == nil) then
				print('[Warning - ' .. getCreatureName(getNpcId()) .. '] 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 - ' .. getCreatureName(getNpcId()) .. '] 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[i] = 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[i] = 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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Attempt to purchase an item which only sellable')
		return false
	end

	if(amount <= 0) then
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Attempt to purchase ' .. amount .. ' items')
		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
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onSell - Item not found on shopItems list')
		return false
	end

	if(shopItem.sell == -1) then
		print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', '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)
	addEvent(openShopWindow, 500, 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.lua

-- Advanced NPC System (Created by Jiddo),
-- Modified by TheForgottenServer 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]	 = '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(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)
				self:say(msg)
			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)

			self:say(msg)
			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[i] = 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[i] = nil
				end
			else
				self.talkDelay[i] = 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, force = delay or 0, force or false
	if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE and 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, {
		cid = focus,
		message = message,
		time = os.mtime() + (delay <= 0 and self.talkDelayTime or delay),
		start = os.time(),
		force = force
	})
end
end

npcsystem.lua

-- Advanced NPC System (Created by Jiddo),
-- Modified by Talaturen.

if(NpcSystem == nil) then
-- Loads the underlying classes of the npcsystem.
dofile(getDataDir() .. 'npc/lib/npcsystem/keywordhandler.lua')
dofile(getDataDir() .. 'npc/lib/npcsystem/queue.lua')
dofile(getDataDir() .. 'npc/lib/npcsystem/npchandler.lua')
dofile(getDataDir() .. 'npc/lib/npcsystem/modules.lua')

-- Global npc constants:

-- Keyword nestling behavior. For more information look at the top of keywordhandler.lua
KEYWORD_BEHAVIOR = BEHAVIOR_NORMAL_EXTENDED

-- Greeting and unGreeting keywords. For more information look at the top of modules.lua
FOCUS_GREETWORDS = {'hi', 'hello', 'hey'}
FOCUS_FAREWELLWORDS = {'bye', 'farewell', 'cya'}

-- The word for requesting trade window. For more information look at the top of modules.lua
SHOP_TRADEREQUEST = {'offer', 'trade'}

-- The word for accepting/declining an offer. CAN ONLY CONTAIN ONE FIELD! For more information look at the top of modules.lua
SHOP_YESWORD = {'yes'}
SHOP_NOWORD = {'no'}

-- Pattern used to get the amount of an item a player wants to buy/sell.
PATTERN_COUNT = '%d+'

-- Talkdelay behavior. For more information, look at the top of npchandler.lua.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK

-- Conversation behavior. For more information, look at the top of npchandler.lua.
NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE

-- Constant strings defining the keywords to replace in the default messages.
--	For more information, look at the top of npchandler.lua...
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'

NpcSystem = {}

-- Gets an npcparameter with the specified key. Returns nil if no such parameter is found.
function NpcSystem.getParameter(key)
	local ret = getNpcParameter(tostring(key))
	if((type(ret) == 'number' and ret == 0) or ret == nil) then
		return nil
	else
		return ret
	end
end

-- Parses all known parameters for the npc. Also parses parseable modules.
function NpcSystem.parseParameters(npcHandler)
	local ret = NpcSystem.getParameter('idletime')
	if(ret ~= nil) then
		npcHandler.idleTime = tonumber(ret)
	end
	local ret = NpcSystem.getParameter('talkradius')
	if(ret ~= nil) then
		npcHandler.talkRadius = tonumber(ret)
	end
	local ret = NpcSystem.getParameter('message_greet')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_GREET, ret)
	end
	local ret = NpcSystem.getParameter('message_farewell')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_FAREWELL, ret)
	end
	local ret = NpcSystem.getParameter('message_decline')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_DECLINE, ret)
	end
	local ret = NpcSystem.getParameter('message_needmorespace')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDMORESPACE, ret)
	end
	local ret = NpcSystem.getParameter('message_needspace')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDSPACE, ret)
	end
	local ret = NpcSystem.getParameter('message_sendtrade')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_SENDTRADE, ret)
	end
	local ret = NpcSystem.getParameter('message_noshop')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NOSHOP, ret)
	end
	local ret = NpcSystem.getParameter('message_oncloseshop')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ONCLOSESHOP, ret)
	end
	local ret = NpcSystem.getParameter('message_onbuy')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ONBUY, ret)
	end
	local ret = NpcSystem.getParameter('message_onsell')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ONSELL, ret)
	end
	local ret = NpcSystem.getParameter('message_missingmoney')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_MISSINGMONEY, ret)
	end
	local ret = NpcSystem.getParameter('message_needmoney')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDMONEY, ret)
	end
	local ret = NpcSystem.getParameter('message_missingitem')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_MISSINGITEM, ret)
	end
	local ret = NpcSystem.getParameter('message_needitem')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_NEEDITEM, ret)
	end
	local ret = NpcSystem.getParameter('message_idletimeout')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_IDLETIMEOUT, ret)
	end
	local ret = NpcSystem.getParameter('message_walkaway')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_WALKAWAY, ret)
	end
	local ret = NpcSystem.getParameter('message_alreadyfocused')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_ALREADYFOCUSED, ret)
	end
	local ret = NpcSystem.getParameter('message_placedinqueue')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_PLACEDINQUEUE, ret)
	end
	local ret = NpcSystem.getParameter('message_buy')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_BUY, ret)
	end
	local ret = NpcSystem.getParameter('message_sell')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_SELL, ret)
	end
	local ret = NpcSystem.getParameter('message_bought')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_BOUGHT, ret)
	end
	local ret = NpcSystem.getParameter('message_sold')
	if(ret ~= nil) then
		npcHandler:setMessage(MESSAGE_SOLD, ret)
	end

	-- Parse modules.
	for parameter, module in pairs(Modules.parseableModules) do
		local ret = NpcSystem.getParameter(parameter)
		if(ret ~= nil) then
			local number = tonumber(ret)
			if(number ~= 0 and module.parseParameters ~= nil) then
				local instance = module:new()
				npcHandler:addModule(instance)
				instance:parseParameters()
			end
		end
	end
end
end

queue.lua

 

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

if(Queue == nil) then
Queue = {
	customers = nil,
	handler = nil,
}

-- Creates a new queue, connected to the given NpcHandler handler
function Queue:new(handler)
	local obj = {}
	obj.handler = handler
	obj.customers = {}
	setmetatable(obj, self)
	self.__index = self
	return obj
end

-- Assigns a new handler to this queue.
function Queue:setHandler(newHandler)
	self.handler = newHandler
end

-- Pushes a new cid onto the tail of this queue.
function Queue:push(cid)
	if(isPlayer(cid)) then
		table.insert(self.customers, cid)
	end
end

-- Returns true if the given cid is already in the queue.
function Queue:isInQueue(cid)
	return (isInArray(self.customers, cid))
end

-- Removes and returns the first cid from the queue
function Queue:pop()
	return table.remove(self.customers, 1)
end

-- Returns the first cid in the queue, but does not remove it!
function Queue:peek()
	return self.customers[1]
end

-- Returns true if htis queue is empty.
function Queue:empty()
	return(self:peek() == nil)
end

-- Returns the amount of players currently in the queue.
function Queue:getSize()
	return table.maxn(self.customers)
end

-- Returns true if the creature with the given cid can be greeted by this npc.
function Queue:canGreet(cid)
	if(isPlayer(cid)) then
		return self.handler:isInRange(cid)
	else
		return false
	end
end

-- Greets the player with the given cid.
function Queue:greet(cid)
	if(self.handler ~= nil) then
		self.handler:greet(cid)
	else
		error('No handler assigned to queue!')
	end
end

-- Makes sure the next greetable player in the queue is greeted.
function Queue:greetNext()
	while (not self:empty()) do
		local nextPlayer = self:pop()
		if(self:canGreet(nextPlayer)) then
			if(callback == nil or callback(nextPlayer)) then
				self:greet(nextPlayer)
				return true
			end
		end
	end
	return false
end
end

 

 

 

E isso ai...

Sera que alguem sabe me dizer como corrigir estes dois bugs?

Link para o comentário
https://xtibia.com/forum/topic/178025-bug-nos-npcs-valendo-rep-em-duas-contas/
Compartilhar em outros sites

×
×
  • Criar Novo...