Advertisement
Guest User

npchandler.lua

a guest
Jun 20th, 2017
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.64 KB | None | 0 0
  1. -- This file is part of Jiddo's advanced NpcSystem v3.0x. This npcsystem is free to use by anyone, for any purpuse.
  2. -- Initial release date: 2007-02-21
  3. -- Credits: Jiddo, honux(I'm using a modified version of his Find function).
  4. -- Please include full credits whereever you use this system, or parts of it.
  5. -- For support, questions and updates, please consult the following thread:
  6. -- http://opentibia.net/topic/59592-release-advanced-npc-system-v30a/
  7.  
  8. if(NpcHandler == nil) then
  9.  
  10. -- Constant talkdelay behaviors.
  11. TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
  12. TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
  13. TALKDELAY_EVENT = 2 -- Not yet implemented
  14.  
  15. -- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
  16. NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
  17.  
  18.  
  19.  
  20. -- Constant indexes for defining default messages.
  21. MESSAGE_GREET = 1 -- When the player greets the npc.
  22. MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
  23. MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
  24. MESSAGE_SELL = 4 -- When the npc asks the player if he wants to sell something.
  25. MESSAGE_ONBUY = 5 -- When the player successfully buys something
  26. MESSAGE_ONSELL = 6 -- When the player successfully sells something
  27. MESSAGE_NEEDMOREMONEY = 7 -- When the player does not have enough money
  28. MESSAGE_NOTHAVEITEM = 8 -- When the player is trying to sell an item he does not have.
  29. MESSAGE_IDLETIMEOUT = 9 -- When the player has been idle for longer then idleTime allows.
  30. MESSAGE_WALKAWAY = 10 -- When the player walks out of the talkRadius of the npc.
  31. MESSAGE_ALREADYFOCUSED = 11 -- When the player already has the focus of this nopc.
  32. MESSAGE_PLACEDINQUEUE = 12 -- When the player has been placed in the costumer queue.
  33. MESSAGE_DECLINE = 13 -- When the player sais no to something.
  34.  
  35. -- Constant indexes for callback functions. These are also used for module callback ids.
  36. CALLBACK_CREATURE_APPEAR = 1
  37. CALLBACK_CREATURE_DISAPPEAR = 2
  38. CALLBACK_CREATURE_SAY = 3
  39. CALLBACK_ONTHINK = 4
  40. CALLBACK_GREET = 5
  41. CALLBACK_FAREWELL = 6
  42. CALLBACK_MESSAGE_DEFAULT = 7
  43.  
  44. -- Addidional module callback ids
  45. CALLBACK_MODULE_INIT = 10
  46. CALLBACK_MODULE_RESET = 11
  47.  
  48.  
  49. -- Constant strings defining the keywords to replace in the default messages.
  50. TAG_PLAYERNAME = '|PLAYERNAME|'
  51. TAG_ITEMCOUNT = '|ITEMCOUNT|'
  52. TAG_TOTALCOST = '|TOTALCOST|'
  53. TAG_ITEMNAME = '|ITEMNAME|'
  54. TAG_QUEUESIZE = '|QUEUESIZE|'
  55. TAG_TIME = '|TIME|'
  56. TAG_TRAVELCOST = '|TRAVELCOST|'
  57.  
  58.  
  59. NpcHandler = {
  60. keywordHandler = nil,
  61. queue = nil,
  62. focus = 0,
  63. talkStart = 0,
  64. idleTime = 30,
  65. talkRadius = 5,
  66. talkDelayTime = 1, -- Seconds to delay outgoing messages.
  67. talkDelay = nil,
  68. callbackFunctions = nil,
  69. modules = nil,
  70. messages = {
  71. -- These are the default replies of all npcs. They can/should be changed individually for each npc.
  72. [MESSAGE_GREET] = 'Welcome, |PLAYERNAME|! I have been expecting you.',
  73. [MESSAGE_FAREWELL] = 'Good bye, |PLAYERNAME|!',
  74. [MESSAGE_BUY] = 'Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
  75. [MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
  76. [MESSAGE_ONBUY] = 'It was a pleasure doing business with you.',
  77. [MESSAGE_ONSELL] = 'Thank you for this item, |PLAYERNAME|.',
  78. [MESSAGE_NEEDMOREMONEY] = 'You do not have enough money.',
  79. [MESSAGE_NOTHAVEITEM] = 'You don\'t even have that item!',
  80. [MESSAGE_IDLETIMEOUT] = 'Next please!',
  81. [MESSAGE_WALKAWAY] = 'How rude!',
  82. [MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|, I am already talking to you.',
  83. [MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, please wait for your turn. There are |QUEUESIZE| customers before you.',
  84. [MESSAGE_DECLINE] = 'Not good enough, is it?'
  85. }
  86. }
  87.  
  88.  
  89. -- Creates a new NpcHandler with an empty callbackFunction stack.
  90. function NpcHandler:new(keywordHandler)
  91. local obj = {}
  92. obj.callbackFunctions = {}
  93. obj.modules = {}
  94. obj.talkDelay = {
  95. message = nil,
  96. time = nil
  97. }
  98. obj.queue = Queue:new(obj)
  99. obj.keywordHandler = keywordHandler
  100. obj.messages = {}
  101. setmetatable(obj.messages, self.messages)
  102. self.messages.__index = self.messages
  103.  
  104. setmetatable(obj, self)
  105. self.__index = self
  106. return obj
  107. end
  108.  
  109. -- Re-defines the maximum idle time allowed for a player when talking to this npc.
  110. function NpcHandler:setMaxIdleTime(newTime)
  111. self.idleTime = newTime
  112. end
  113.  
  114. -- Attaches a new costumer queue to this npchandler.
  115. function NpcHandler:setQueue(newQueue)
  116. self.queue = newQueue
  117. self.queue:setHandler(self)
  118. end
  119.  
  120. -- Attackes a new keyword handler to this npchandler
  121. function NpcHandler:setKeywordHandler(newHandler)
  122. self.keywordHandler = newHandler
  123. end
  124.  
  125. -- Function used to change the focus of this npc.
  126. function NpcHandler:changeFocus(newFocus)
  127. self.focus = newFocus
  128. self:updateFocus()
  129. end
  130.  
  131. -- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
  132. -- Should also be called whenever a new player is focused.
  133. function NpcHandler:updateFocus()
  134. doNpcSetCreatureFocus(self.focus)
  135. end
  136.  
  137. -- Used when the npc should un-focus the player.
  138. function NpcHandler:releaseFocus()
  139. self:changeFocus(0)
  140. end
  141.  
  142. -- Returns the callback function with the specified id or nil if no such callback function exists.
  143. function NpcHandler:getCallback(id)
  144. local ret = nil
  145. if(self.callbackFunctions ~= nil) then
  146. ret = self.callbackFunctions[id]
  147. end
  148. return ret
  149. end
  150.  
  151. -- Changes the callback function for the given id to callback.
  152. function NpcHandler:setCallback(id, callback)
  153. if(self.callbackFunctions ~= nil) then
  154. self.callbackFunctions[id] = callback
  155. end
  156. end
  157.  
  158. -- Adds a module to this npchandler and inits it.
  159. function NpcHandler:addModule(module)
  160. if(self.modules ~= nil) then
  161. table.insert(self.modules, module)
  162. module:init(self)
  163. end
  164. end
  165.  
  166. -- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
  167. function NpcHandler:processModuleCallback(id, ...)
  168. local ret = true
  169. for i, module in pairs(self.modules) do
  170. local tmpRet = true
  171. if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
  172. tmpRet = module:callbackCreatureAppear(unpack(arg))
  173.  
  174. elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
  175. tmpRet = module:callbackCreatureDisappear(unpack(arg))
  176.  
  177. elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
  178. tmpRet = module:callbackCreatureSay(unpack(arg))
  179.  
  180. elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
  181. tmpRet = module:callbackOnThink(unpack(arg))
  182.  
  183. elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
  184. tmpRet = module:callbackOnGreet(unpack(arg))
  185.  
  186. elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
  187. tmpRet = module:callbackOnFarewell(unpack(arg))
  188.  
  189. elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
  190. tmpRet = module:callbackOnMessageDefault(unpack(arg))
  191.  
  192. elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
  193. tmpRet = module:callbackOnModuleReset(unpack(arg))
  194. end
  195. if(not tmpRet) then
  196. ret = false
  197. break
  198. end
  199. end
  200. return ret
  201. end
  202.  
  203. -- Returns the message represented by id.
  204. function NpcHandler:getMessage(id)
  205. local ret = nil
  206. if(self.messages ~= nil) then
  207. ret = self.messages[id]
  208. end
  209. return ret
  210. end
  211.  
  212. -- Changes the default response message with the specified id to newMessage.
  213. function NpcHandler:setMessage(id, newMessage)
  214. if(self.messages ~= nil) then
  215. self.messages[id] = newMessage
  216. end
  217. end
  218.  
  219. -- Translates all message tags found in msg using parseInfo
  220. function NpcHandler:parseMessage(msg, parseInfo)
  221. local ret = msg
  222. for search, replace in pairs(parseInfo) do
  223. ret = string.gsub(ret, search, replace)
  224. end
  225. return ret
  226. end
  227.  
  228. -- Makes sure the npc un-focuses the furrently focused player, and greets the next player in the queue is it is not empty.
  229. function NpcHandler:unGreet()
  230. if(self.focus == 0) then
  231. return
  232. end
  233. local callback = self:getCallback(CALLBACK_FAREWELL)
  234. if(callback == nil or callback()) then
  235. if(self:processModuleCallback(CALLBACK_FAREWELL)) then
  236. if(self.queue == nil or not self.queue:greetNext()) then
  237. local msg = self:getMessage(MESSAGE_FAREWELL)
  238. local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(self.focus) }
  239. msg = self:parseMessage(msg, parseInfo)
  240. self:say(msg)
  241. self:releaseFocus()
  242. end
  243. end
  244. end
  245. end
  246.  
  247. -- Greets a new player.
  248. function NpcHandler:greet(cid)
  249. if(cid ~= 0) then
  250. local callback = self:getCallback(CALLBACK_GREET)
  251. if(callback == nil or callback(cid)) then
  252. if(self:processModuleCallback(CALLBACK_GREET, cid)) then
  253. local msg = self:getMessage(MESSAGE_GREET)
  254. local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
  255. msg = self:parseMessage(msg, parseInfo)
  256. self:say(msg)
  257. else
  258. return
  259. end
  260. else
  261. return
  262. end
  263. end
  264. self:changeFocus(cid)
  265. end
  266.  
  267. -- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
  268. function NpcHandler:onCreatureAppear(cid)
  269. local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
  270. if(callback == nil or callback(cid)) then
  271. if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
  272.  
  273. end
  274. end
  275. end
  276.  
  277. -- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
  278. function NpcHandler:onCreatureDisappear(cid)
  279. local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
  280. if(callback == nil or callback(cid)) then
  281. if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
  282. if(self.focus == cid) then
  283. self:unGreet()
  284. end
  285. end
  286. end
  287. end
  288.  
  289. -- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
  290. function NpcHandler:onCreatureSay(cid, msgtype, msg)
  291. local callback = self:getCallback(CALLBACK_CREATURE_SAY)
  292. if(callback == nil or callback(cid, msgtype, msg)) then
  293. if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, msgtype, msg)) then
  294. if(not self:isInRange(cid)) then
  295. return
  296. end
  297. if(self.keywordHandler ~= nil) then
  298. local ret = self.keywordHandler:processMessage(cid, msg)
  299. if(not ret) then
  300. local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
  301. if(callback ~= nil and callback(cid, msgtype, msg)) then
  302. self.talkStart = os.time()
  303. end
  304. else
  305. self.talkStart = os.time()
  306. end
  307. end
  308. end
  309. end
  310. end
  311.  
  312. -- Handles onThink events. If you with to handle this yourself, please use the CALLBACK_ONTHINK callback.
  313. function NpcHandler:onThink()
  314. local callback = self:getCallback(CALLBACK_ONTHINK)
  315. if(callback == nil or callback()) then
  316.  
  317. if(NPCHANDLER_TALKDELAY == TALKDELAY_ONTHINK and self.talkDelay.time ~= nil and self.talkDelay.message ~= nil and os.time() >= self.talkDelay.time) then
  318. selfSay(self.talkDelay.message)
  319. self.talkDelay.time = nil
  320. self.talkDelay.message = nil
  321. end
  322.  
  323. if(self:processModuleCallback(CALLBACK_ONTHINK)) then
  324. if(self.focus ~= 0) then
  325. if(not self:isInRange(self.focus)) then
  326. self:onWalkAway(self.focus)
  327. elseif(os.time()-self.talkStart > self.idleTime) then
  328. self:unGreet()
  329. else
  330. self:updateFocus()
  331. end
  332. end
  333. end
  334. end
  335. end
  336.  
  337. -- Tries to greet the player iwth the given cid. This function does not override queue order, current focus etc.
  338. function NpcHandler:onGreet(cid)
  339. if(self:isInRange(cid)) then
  340. if(self.focus == 0) then
  341. self:greet(cid)
  342. elseif(cid == self.focus) then
  343. local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)
  344. local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
  345. msg = self:parseMessage(msg, parseInfo)
  346. self:say(msg)
  347. else
  348. if(not self.queue:isInQueue(cid)) then
  349. self.queue:push(cid)
  350. end
  351. local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)
  352. local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }
  353. msg = self:parseMessage(msg, parseInfo)
  354. self:say(msg)
  355. end
  356. end
  357. end
  358.  
  359. -- Simply calls the underlying unGreet function.
  360. function NpcHandler:onFarewell()
  361. self:unGreet()
  362. end
  363.  
  364. -- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
  365. function NpcHandler:onWalkAway(cid)
  366. if(cid == self.focus) then
  367. local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
  368. if(callback == nil or callback()) then
  369. if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
  370. if(self.queue == nil or not self.queue:greetNext()) then
  371. local msg = self:getMessage(MESSAGE_WALKAWAY)
  372. local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(self.focus) }
  373. msg = self:parseMessage(msg, parseInfo)
  374. self:say(msg)
  375. self:releaseFocus()
  376. end
  377. end
  378. end
  379. end
  380. end
  381.  
  382. -- Returns true if cid is within the talkRadius of this npc.
  383. function NpcHandler:isInRange(cid)
  384. local playerPos = getPlayerPosition(cid)
  385. if playerPos == LUA_ERROR or playerPos == LUA_NO_ERROR then
  386. return false
  387. end
  388.  
  389. local sx = selfGetPosition().x
  390. local sy = selfGetPosition().y
  391. local sz = selfGetPosition().z
  392.  
  393. local dx = math.abs(sx-playerPos.x)
  394. local dy = math.abs(sy-playerPos.y)
  395. local dz = math.abs(sz-playerPos.z)
  396.  
  397. local dist = (dx^2 + dy^2)^0.5
  398.  
  399. return (dist <= self.talkRadius and dz == 0)
  400. end
  401.  
  402. -- Resets the npc into it's initial state (in regard of the keyrodhandler).
  403. -- All modules are also receiving a reset call through their callbackOnModuleReset function.
  404. function NpcHandler:resetNpc()
  405. if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
  406. self.keywordHandler:reset()
  407. end
  408. end
  409.  
  410.  
  411. -- Makes the npc represented by this instance of NpcHandler say something.
  412. -- This implements the currently set type of talkdelay.
  413. -- shallDelay is a boolean value. If it is false, the message is not delayed. Default value is true.
  414. function NpcHandler:say(message, shallDelay)
  415. if(shallDelay == nil) then
  416. shallDelay = true
  417. end
  418. if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or shallDelay == false) then
  419. selfSay(message)
  420. return
  421. end
  422. self.talkDelay.message = message
  423. self.talkDelay.time = os.time()+self.talkDelayTime
  424. end
  425.  
  426.  
  427. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement