-- Advanced NPC System (Created by Jiddo)
-- Initial release date: 2007-02-21
-- Modified by Ruthless (based on Realots for OriginalTibia.com) date: 2012-03-12
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 = 1 -- (1 = second) Seconds to delay outgoing messages. It's based on addevent.
-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
-- Constant indexes for defining default messages.
MESSAGE_GREET = 1
MESSAGE_FAREWELL = 2
MESSAGE_BUY = 3
MESSAGE_SELL = 4
MESSAGE_ONBUY = 5
MESSAGE_ONSELL = 6
MESSAGE_NEEDMOREMONEY = 7
MESSAGE_NOTHAVEITEM = 8
MESSAGE_IDLETIMEOUT = 9
MESSAGE_WALKAWAY = 10
MESSAGE_PLACEDINQUEUE = 11
MESSAGE_DECLINE_BOTH = 12
MESSAGE_DECLINE_SELL = 13
MESSAGE_DECLINE_BUY = 14
MESSAGE_TRAVEL = 15
MESSAGE_NOTHAVEITEMS = 16
MESSAGE_GREET_MALE = 17
MESSAGE_GREET_FEMALE = 18
-- 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
-- Addidional module callback ids
CALLBACK_MODULE_INIT = 10
CALLBACK_MODULE_RESET = 11
-- 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|'
TAG_TIME = '|TIME|'
NpcHandler = {
keywordHandler = nil,
queue = nil,
focus = 0,
Topic = 0,
talkStart = 0,
idleTime = 35,
onWalkAwayTime = 0,
unGreetTime = 0,
prepareToQueue = 0,
talkRadius = 3,
talkDelay = 0,
callbackFunctions = nil,
focusGreetKeywords = nil,
focusFarewellKeywords = nil,
modules = nil,
pauseTime = nil,
pauseTime_ = nil,
NoMove = nil,
story = {},
prevEvent = {},
msgEvent = {},
shopEventType = 0,
shopType = 0,
sexCallBack = 0,
messages = {
-- These are the default replies of all npcs. They can/should be changed individually for each npc.
[MESSAGE_GREET] = 'Hello. How may I help you |PLAYERNAME|?',
[MESSAGE_GREET_MALE] = 'Welcome Sir |PLAYERNAME|.',
[MESSAGE_GREET_FEMALE] = 'Welcome Madam |PLAYERNAME|.',
[MESSAGE_FAREWELL] = 'Good bye, |PLAYERNAME|!',
[MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_ONBUY] = 'Thank you. Here it is.',
[MESSAGE_ONSELL] = 'Ok. Here is your money.',
[MESSAGE_NEEDMOREMONEY] = 'Sorry, you do not have enough gold.',
[MESSAGE_NOTHAVEITEM] = 'Sorry, you do not have one.',
[MESSAGE_NOTHAVEITEMS] = 'Sorry, you do not have so many.',
[MESSAGE_IDLETIMEOUT] = 'Next please!',
[MESSAGE_WALKAWAY] = 'Good bye!',
[MESSAGE_PLACEDINQUEUE] = 'Just a minute |PLAYERNAME|!',
[MESSAGE_DECLINE_BOTH] = 'Maybe later.',
[MESSAGE_DECLINE_SELL] = 'Maybe next time.',
[MESSAGE_DECLINE_BUY] = 'Maybe you will buy it another time.',
[MESSAGE_BUY] = 'Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
[MESSAGE_TRAVEL] = 'Set the sails!'
}
}
-- Creates a new NpcHandler with an empty callbackFunction stack.
function NpcHandler:new(keywordHandler)
local obj = {}
obj.callbackFunctions = {}
obj.modules = {}
obj.talkDelay = {
message = nil,
time = nil
}
obj.queue = Queue:new(obj)
obj.keywordHandler = keywordHandler
obj.messages = {}
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
-- Attaches a new costumer queue to this npchandler.
function NpcHandler:setQueue(newQueue)
self.queue = newQueue
self.queue:setHandler(self)
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:changeFocus(newFocus)
self.focus = newFocus
self:updateFocus()
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()
doNpcSetCreatureFocus(self.focus)
end
-- Used when the npc should un-focus the player.
function NpcHandler:releaseFocus()
self:changeFocus(0)
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) then
table.insert(self.modules, module)
module:init(self)
end
end
function NpcHandler:freezeNpc(delay)
if (getCreatureNoMove(getNpcCid()) == FALSE) then
doCreatureSetNoMove(getNpcCid(), TRUE)
self.NoMove = addEvent(doCreatureSetNoMove, delay, getNpcCid(), FALSE)
else
stopEvent(self.NoMove)
doCreatureSetNoMove(getNpcCid(), TRUE)
self.NoMove = addEvent(doCreatureSetNoMove, delay, getNpcCid(), FALSE)
end
return 1
end
function NpcHandler:isFreeFocus(cid)
if (self.focus == 0) then
return 1
end
end
function NpcHandler:isQueue(cid)
if (self.focus > 0) and (self.focus ~= cid) then
return 1
end
end
function NpcHandler:isFocused(cid)
if (self.focus > 0) and (self.focus == cid) then
return 1
end
end
function NpcHandler:doTopic(cid, value)
self.Topic = value
return 1
end
function NpcHandler:OnDeclineMsg(cid)
if (self.focus == cid) then
self.keywordHandler:reset()
if (self.shopEventType == 1) then
self.shopEventType = 0
if (self.shopType ~= 1) then
local msg = self:getMessage(MESSAGE_DECLINE_SELL)
self:doNpcSay(getNpcCid(), msg, 1, TALKDELAY_EVENT*1000)
end
elseif (self.shopEventType == 2) then
self.shopEventType = 0
if (self.shopType ~= 1) then
local msg = self:getMessage(MESSAGE_DECLINE_BUY)
self:doNpcSay(getNpcCid(), msg, 1, TALKDELAY_EVENT*1000)
end
end
end
return 1
end
function NpcHandler:AddFocus(keywords, response, response2, cid, msg)
for i, word in pairs(keywords) do
if (msgcontains(msg, word)) and (self:isFreeFocus(cid)) then
if (response2 ~= 0) then
self.sexCallBack = 1
self:setMessage(MESSAGE_GREET_FEMALE, response2)
self:setMessage(MESSAGE_GREET_MALE, response)
else
self:setMessage(MESSAGE_GREET, response)
end
self:greet(cid)
end
end
return 1
end
function NpcHandler:AddQueue(keywords, response, response2, cid, msg)
for i, word in pairs(keywords) do
if (msgcontains(msg, word)) and (self:isQueue(cid)) then
if (response2 ~= 0) then
if (isFemale(cid) == TRUE) then
self:setMessage(MESSAGE_PLACEDINQUEUE, response2)
else
self:setMessage(MESSAGE_PLACEDINQUEUE, response)
end
else
self:setMessage(MESSAGE_PLACEDINQUEUE, response)
end
self:onGreet(cid)
end
end
return 1
end
function NpcHandler:RemoveFocus(keywords, response, response2, cid, msg)
for i, word in pairs(keywords) do
if (msgcontains(msg, word)) and (self:isFocused(cid)) then
if (response2 ~= 0) then
if (isFemale(cid) == TRUE) then
self:setMessage(MESSAGE_FAREWELL, response2)
else
self:setMessage(MESSAGE_FAREWELL, response)
end
else
self:setMessage(MESSAGE_FAREWELL, response)
end
self:onFarewell(cid)
end
end
return 1
end
function NpcHandler:getFocusGreetKeywords()
return self.focusGreetKeywords
end
function NpcHandler:setFocusGreetKeywords(...)
self.focusGreetKeywords = arg
end
function NpcHandler:getFocusFarewellKeywords()
return self.focusFarewellKeywords
end
function NpcHandler:setFocusFarewellKeywords(...)
self.focusFarewellKeywords = arg
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.m