Advertisement
Guest User

npchandler

a guest
Nov 2nd, 2014
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.33 KB | None | 0 0
  1. -- Advanced NPC System (Created by Jiddo),
  2. -- Modified by TheForgottenServer Team.
  3.  
  4. if(NpcHandler == nil) then
  5. -- Constant talkdelay behaviors.
  6. TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
  7. TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
  8. TALKDELAY_EVENT = 2 -- Not yet implemented
  9.  
  10. -- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
  11. NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
  12.  
  13. -- Constant conversation behaviors.
  14. CONVERSATION_DEFAULT = 0 -- Conversation through default window, like it was before 8.2 update.
  15. CONVERSATION_PRIVATE = 1 -- Conversation through NPCs chat window, as of 8.2 update. (Default)
  16. --Small Note: Private conversations also means the NPC will use multi-focus system.
  17.  
  18. -- Currently applied conversation behavior. CONVERSATION_PRIVATE is default.
  19. NPCHANDLER_CONVBEHAVIOR = CONVERSATION_PRIVATE
  20.  
  21. -- Constant indexes for defining default messages.
  22. MESSAGE_GREET = 1 -- When the player greets the npc.
  23. MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
  24. MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
  25. MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.
  26. MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.
  27. MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.
  28. MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.
  29. MESSAGE_SOLD = 8 -- When the player sold something through the shop window.
  30. MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.
  31. MESSAGE_MISSINGITEM = 10 -- When the player is trying to sell an item he does not have.
  32. MESSAGE_NEEDMONEY = 11 -- Same as above, used for shop window.
  33. MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.
  34. MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item
  35. MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space
  36. MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.
  37. MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.
  38. MESSAGE_DECLINE = 17 -- When the player says no to something.
  39. MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player
  40. MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any
  41. MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window
  42. MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.
  43. MESSAGE_PLACEDINQUEUE = 22 -- When the player has been placed in the costumer queue.
  44.  
  45. -- Constant indexes for callback functions. These are also used for module callback ids.
  46. CALLBACK_CREATURE_APPEAR = 1
  47. CALLBACK_CREATURE_DISAPPEAR = 2
  48. CALLBACK_CREATURE_SAY = 3
  49. CALLBACK_ONTHINK = 4
  50. CALLBACK_GREET = 5
  51. CALLBACK_FAREWELL = 6
  52. CALLBACK_MESSAGE_DEFAULT = 7
  53. CALLBACK_PLAYER_ENDTRADE = 8
  54. CALLBACK_PLAYER_CLOSECHANNEL = 9
  55. CALLBACK_ONBUY = 10
  56. CALLBACK_ONSELL = 11
  57.  
  58. -- Addidional module callback ids
  59. CALLBACK_MODULE_INIT = 12
  60. CALLBACK_MODULE_RESET = 13
  61.  
  62. -- Constant strings defining the keywords to replace in the default messages.
  63. TAG_PLAYERNAME = '|PLAYERNAME|'
  64. TAG_ITEMCOUNT = '|ITEMCOUNT|'
  65. TAG_TOTALCOST = '|TOTALCOST|'
  66. TAG_ITEMNAME = '|ITEMNAME|'
  67. TAG_QUEUESIZE = '|QUEUESIZE|'
  68.  
  69. NpcHandler = {
  70. keywordHandler = nil,
  71. focuses = nil,
  72. talkStart = nil,
  73. idleTime = 300,
  74. talkRadius = 3,
  75. talkDelayTime = 350, -- Seconds to delay outgoing messages.
  76. queue = nil,
  77. talkDelay = nil,
  78. callbackFunctions = nil,
  79. modules = nil,
  80. shopItems = nil, -- They must be here since ShopModule uses "static" functions
  81. messages = {
  82. -- These are the default replies of all npcs. They can/should be changed individually for each npc.
  83. [MESSAGE_GREET] = 'Welcome, |PLAYERNAME|! I have been expecting you.',
  84. [MESSAGE_FAREWELL] = 'Good bye, |PLAYERNAME|!',
  85. [MESSAGE_BUY] = 'Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
  86. [MESSAGE_ONBUY] = 'It was a pleasure doing business with you.',
  87. [MESSAGE_BOUGHT] = 'Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
  88. [MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?',
  89. [MESSAGE_ONSELL] = 'Thank you for this |ITEMNAME|, |PLAYERNAME|.',
  90. [MESSAGE_SOLD] = 'Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.',
  91. [MESSAGE_MISSINGMONEY] = 'Sorry, you don\'t have enough money.',
  92. [MESSAGE_MISSINGITEM] = 'You don\'t even have that item, |PLAYERNAME|!',
  93. [MESSAGE_NEEDMONEY] = 'You do not have enough money.',
  94. [MESSAGE_NEEDITEM] = 'You do not have this object.',
  95. [MESSAGE_NEEDSPACE] = 'You do not have enough capacity.',
  96. [MESSAGE_NEEDMORESPACE] = 'You do not have enough capacity for all items.',
  97. [MESSAGE_IDLETIMEOUT] = 'Next, please!',
  98. [MESSAGE_WALKAWAY] = 'How rude!',
  99. [MESSAGE_DECLINE] = 'Not good enough, is it... ?',
  100. [MESSAGE_SENDTRADE] = 'Here\'s my offer, |PLAYERNAME|. Don\'t you like it?',
  101. [MESSAGE_NOSHOP] = 'Sorry, I\'m not offering anything.',
  102. [MESSAGE_ONCLOSESHOP] = 'Thank you, come back when you want something more.',
  103. [MESSAGE_ALREADYFOCUSED]= '|PLAYERNAME|! I am already talking to you...',
  104. [MESSAGE_PLACEDINQUEUE] = '|PLAYERNAME|, please wait for your turn. There are |QUEUESIZE| customers before you.'
  105. }
  106. }
  107.  
  108. -- Creates a new NpcHandler with an empty callbackFunction stack.
  109. function NpcHandler:new(keywordHandler)
  110. local obj = {}
  111. obj.messages = {}
  112.  
  113. obj.keywordHandler = keywordHandler
  114. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  115. obj.focuses = {}
  116. obj.talkStart = {}
  117. else
  118. obj.queue = Queue:new(obj)
  119. obj.focuses = 0
  120. obj.talkStart = 0
  121. end
  122.  
  123. obj.callbackFunctions = {}
  124. obj.modules = {}
  125. obj.talkDelay = {}
  126. obj.shopItems = {}
  127.  
  128. setmetatable(obj.messages, self.messages)
  129. self.messages.__index = self.messages
  130.  
  131. setmetatable(obj, self)
  132. self.__index = self
  133. return obj
  134. end
  135.  
  136. -- Re-defines the maximum idle time allowed for a player when talking to this npc.
  137. function NpcHandler:setMaxIdleTime(newTime)
  138. self.idleTime = newTime
  139. end
  140.  
  141. -- Attackes a new keyword handler to this npchandler
  142. function NpcHandler:setKeywordHandler(newHandler)
  143. self.keywordHandler = newHandler
  144. end
  145.  
  146. -- Function used to change the focus of this npc.
  147. function NpcHandler:addFocus(newFocus)
  148. if(not isCreature(newFocus)) then
  149. return
  150. end
  151.  
  152. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  153. if(self:isFocused(newFocus, true)) then
  154. return
  155. end
  156.  
  157. table.insert(self.focuses, newFocus)
  158. else
  159. self.focuses = newFocus
  160. end
  161.  
  162. self:updateFocus(true)
  163. end
  164. NpcHandler.changeFocus = NpcHandler.addFocus -- "changeFocus" looks better for CONVERSATION_DEFAULT
  165.  
  166. -- Function used to verify if npc is focused to certain player
  167. function NpcHandler:isFocused(focus, creatureCheck)
  168. local creatureCheck = creatureCheck or false
  169. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  170. for k, v in pairs(self.focuses) do
  171. if(v == focus) then
  172. if(creatureCheck or isCreature(v)) then
  173. return true
  174. end
  175.  
  176. self:unsetFocus(focus, k)
  177. return false
  178. end
  179. end
  180.  
  181. return false
  182. end
  183.  
  184. if(creatureCheck or isCreature(self.focuses)) then
  185. return self.focuses == focus
  186. end
  187.  
  188. self:changeFocus(0)
  189. return false
  190. end
  191.  
  192. -- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
  193. -- Should also be called whenever a new player is focused.
  194. function NpcHandler:updateFocus(creatureCheck)
  195. local creatureCheck = creatureCheck or false
  196. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  197. for _, focus in pairs(self.focuses) do
  198. if(creatureCheck or isCreature(focus)) then
  199. doNpcSetCreatureFocus(focus)
  200. return
  201. end
  202. end
  203. elseif(creatureCheck or isCreature(self.focuses)) then
  204. doNpcSetCreatureFocus(self.focuses)
  205. return
  206. end
  207.  
  208. doNpcSetCreatureFocus(0)
  209. end
  210.  
  211. -- Used when the npc should un-focus the player.
  212. function NpcHandler:releaseFocus(focus)
  213. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  214. if(not self:isFocused(focus)) then
  215. return
  216. end
  217.  
  218. local pos = nil
  219. for k, v in pairs(self.focuses) do
  220. if(v == focus) then
  221. pos = k
  222. end
  223. end
  224.  
  225. if(pos ~= nil) then
  226. closeShopWindow(focus)
  227. self:unsetFocus(focus, pos)
  228. end
  229. elseif(self.focuses == focus) then
  230. if(isCreature(focus)) then
  231. closeShopWindow(focus)
  232. end
  233.  
  234. self:changeFocus(0)
  235. end
  236. end
  237.  
  238. -- Internal un-focusing function, beware using!
  239. function NpcHandler:unsetFocus(focus, pos)
  240. if(type(self.focuses) ~= "table" or pos == nil or self.focuses[pos] == nil) then
  241. return
  242. end
  243.  
  244. table.remove(self.focuses, pos)
  245. self.talkStart[focus] = nil
  246. self:updateFocus()
  247. end
  248.  
  249. -- Returns the callback function with the specified id or nil if no such callback function exists.
  250. function NpcHandler:getCallback(id)
  251. local ret = nil
  252. if(self.callbackFunctions ~= nil) then
  253. ret = self.callbackFunctions[id]
  254. end
  255.  
  256. return ret
  257. end
  258.  
  259. -- Changes the callback function for the given id to callback.
  260. function NpcHandler:setCallback(id, callback)
  261. if(self.callbackFunctions ~= nil) then
  262. self.callbackFunctions[id] = callback
  263. end
  264. end
  265.  
  266. -- Adds a module to this npchandler and inits it.
  267. function NpcHandler:addModule(module)
  268. if(self.modules == nil or module == nil) then
  269. return false
  270. end
  271.  
  272. module:init(self)
  273. if(module.parseParameters ~= nil) then
  274. module:parseParameters()
  275. end
  276.  
  277. table.insert(self.modules, module)
  278. return true
  279. end
  280.  
  281. -- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
  282. function NpcHandler:processModuleCallback(id, ...)
  283. local ret = true
  284. for _, module in pairs(self.modules) do
  285. local tmpRet = true
  286. if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
  287. tmpRet = module:callbackOnCreatureAppear(...)
  288. elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
  289. tmpRet = module:callbackOnCreatureDisappear(...)
  290. elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
  291. tmpRet = module:callbackOnCreatureSay(...)
  292. elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then
  293. tmpRet = module:callbackOnPlayerEndTrade(...)
  294. elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then
  295. tmpRet = module:callbackOnPlayerCloseChannel(...)
  296. elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then
  297. tmpRet = module:callbackOnBuy(...)
  298. elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then
  299. tmpRet = module:callbackOnSell(...)
  300. elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
  301. tmpRet = module:callbackOnThink(...)
  302. elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
  303. tmpRet = module:callbackOnGreet(...)
  304. elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
  305. tmpRet = module:callbackOnFarewell(...)
  306. elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
  307. tmpRet = module:callbackOnMessageDefault(...)
  308. elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
  309. tmpRet = module:callbackOnModuleReset(...)
  310. end
  311.  
  312. if(not tmpRet) then
  313. ret = false
  314. break
  315. end
  316. end
  317.  
  318. return ret
  319. end
  320.  
  321. -- Returns the message represented by id.
  322. function NpcHandler:getMessage(id)
  323. local ret = nil
  324. if(self.messages ~= nil) then
  325. ret = self.messages[id]
  326. end
  327.  
  328. return ret
  329. end
  330.  
  331. -- Changes the default response message with the specified id to newMessage.
  332. function NpcHandler:setMessage(id, newMessage)
  333. if(self.messages ~= nil) then
  334. self.messages[id] = newMessage
  335. end
  336. end
  337.  
  338. -- Translates all message tags found in msg using parseInfo
  339. function NpcHandler:parseMessage(msg, parseInfo)
  340. for search, replace in pairs(parseInfo) do
  341. if(replace ~= nil) then
  342. msg = msg:gsub(search, replace)
  343. end
  344. end
  345.  
  346. return msg
  347. end
  348.  
  349. -- Makes sure the npc un-focuses the currently focused player
  350. function NpcHandler:unGreet(cid)
  351. if(not self:isFocused(cid)) then
  352. return
  353. end
  354.  
  355. local callback = self:getCallback(CALLBACK_FAREWELL)
  356. if(callback == nil or callback(cid)) then
  357. if(self:processModuleCallback(CALLBACK_FAREWELL)) then
  358. if(self.queue == nil or not self.queue:greetNext()) then
  359. local msg = self:getMessage(MESSAGE_FAREWELL)
  360. msg = self:parseMessage(msg, { [TAG_PLAYERNAME] = getPlayerName(cid) or -1 })
  361.  
  362. self:resetNpc(cid)
  363. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  364. self:say(msg, cid, 0, true)
  365. msg = msg:gsub('{', ''):gsub('}', '')
  366. local ghost, position = isPlayerGhost(cid), getThingPosition(getNpcId())
  367.  
  368. local spectators, nid = getSpectators(position, 7, 7), getNpcId()
  369. for _, pid in ipairs(spectators) do
  370. if(isPlayer(pid) and pid ~= cid) then
  371. if(NPCHANDLER_TALKDELAY ~= TALKDELAY_NONE) then
  372. addEvent(doCreatureSay, self.talkDelayTime, nid, msg, TALKTYPE_SAY, ghost, pid, position)
  373. else
  374. doCreatureSay(nid, msg, TALKTYPE_SAY, ghost, pid, position)
  375. end
  376. end
  377. end
  378. else
  379. self:say(msg)
  380. end
  381.  
  382. self:releaseFocus(cid)
  383. end
  384. end
  385. end
  386. end
  387.  
  388. -- Greets a new player.
  389. function NpcHandler:greet(cid)
  390. local callback = self:getCallback(CALLBACK_GREET)
  391. if(callback == nil or callback(cid)) then
  392. if(self:processModuleCallback(CALLBACK_GREET, cid)) then
  393. local msg = self:getMessage(MESSAGE_GREET)
  394. msg = self:parseMessage(msg, { [TAG_PLAYERNAME] = getCreatureName(cid) })
  395.  
  396. self:addFocus(cid)
  397. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  398. self:say(msg, cid, 0, true)
  399. msg = msg:gsub('{', ''):gsub('}', '')
  400. local ghost, position = isPlayerGhost(cid), getThingPosition(getNpcId())
  401.  
  402. local spectators, nid = getSpectators(position, 7, 7), getNpcId()
  403. for _, pid in ipairs(spectators) do
  404. if(isPlayer(pid) and pid ~= cid) then
  405. if(NPCHANDLER_TALKDELAY ~= TALKDELAY_NONE) then
  406. addEvent(doCreatureSay, self.talkDelayTime, nid, msg, TALKTYPE_SAY, ghost, pid, position)
  407. else
  408. doCreatureSay(nid, msg, TALKTYPE_SAY, ghost, pid, position)
  409. end
  410. end
  411. end
  412. else
  413. self:say(msg)
  414. end
  415. end
  416. end
  417. end
  418.  
  419. -- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
  420. function NpcHandler:onCreatureAppear(cid)
  421. local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
  422. if(callback == nil or callback(cid)) then
  423. if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
  424. --
  425. end
  426. end
  427. end
  428.  
  429. -- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
  430. function NpcHandler:onCreatureDisappear(cid)
  431. local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
  432. if(callback == nil or callback(cid)) then
  433. if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
  434. if(self:isFocused(cid)) then
  435. self:unGreet(cid)
  436. end
  437. end
  438. end
  439. end
  440.  
  441. -- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
  442. function NpcHandler:onCreatureSay(cid, class, msg)
  443. local callback = self:getCallback(CALLBACK_CREATURE_SAY)
  444. if(callback == nil or callback(cid, class, msg)) then
  445. if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, class, msg)) then
  446. if(not self:isInRange(cid)) then
  447. return
  448. end
  449.  
  450. if(self.keywordHandler ~= nil) then
  451. if((self:isFocused(cid) and (class == TALKTYPE_PRIVATE_PN or NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT)) or not self:isFocused(cid)) then
  452. local ret = self.keywordHandler:processMessage(cid, msg)
  453. if(not ret) then
  454. local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
  455. if(callback ~= nil and callback(cid, class, msg)) then
  456. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  457. self.talkStart[cid] = os.time()
  458. else
  459. self.talkStart = os.time()
  460. end
  461. end
  462. elseif(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  463. self.talkStart[cid] = os.time()
  464. else
  465. self.talkStart = os.time()
  466. end
  467. end
  468. end
  469. end
  470. end
  471. end
  472.  
  473. -- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.
  474. function NpcHandler:onPlayerEndTrade(cid)
  475. local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)
  476. if(callback == nil or callback(cid)) then
  477. if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid)) then
  478. if(self:isFocused(cid)) then
  479. local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
  480. local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)
  481. self:say(msg, cid)
  482. end
  483. end
  484. end
  485. end
  486.  
  487. -- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.
  488. function NpcHandler:onPlayerCloseChannel(cid)
  489. local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)
  490. if(callback == nil or callback(cid)) then
  491. if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid)) then
  492. if(self:isFocused(cid)) then
  493. self:unGreet(cid)
  494. end
  495. end
  496. end
  497. end
  498.  
  499. -- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.
  500. function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
  501. local callback = self:getCallback(CALLBACK_ONBUY)
  502. if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
  503. if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
  504. --
  505. end
  506. end
  507. end
  508.  
  509. -- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.
  510. function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
  511. local callback = self:getCallback(CALLBACK_ONSELL)
  512. if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
  513. if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
  514. --
  515. end
  516. end
  517. end
  518.  
  519. -- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.
  520. function NpcHandler:onThink()
  521. local callback = self:getCallback(CALLBACK_ONTHINK)
  522. if(callback == nil or callback()) then
  523. for i, speech in pairs(self.talkDelay) do
  524. if((speech.cid == nil or speech.cid == 0) and speech.time ~= nil and speech.message ~= nil) then
  525. if(os.mtime() >= speech.time) then
  526. selfSay(speech.message)
  527. self.talkDelay[i] = nil
  528. end
  529. elseif(isCreature(speech.cid) and speech.start ~= nil and speech.time ~= nil and speech.message ~= nil) then
  530. if(os.mtime() >= speech.time) then
  531. local talkStart = (NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT and self.talkStart[speech.cid] or self.talkStart)
  532. if(speech.force or (self:isFocused(speech.cid) and talkStart == speech.start)) then
  533. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  534. selfSay(speech.message, speech.cid)
  535. else
  536. selfSay(speech.message)
  537. end
  538. end
  539.  
  540. self.talkDelay[i] = nil
  541. end
  542. else
  543. self.talkDelay[i] = nil
  544. end
  545. end
  546.  
  547. if(self:processModuleCallback(CALLBACK_ONTHINK)) then
  548. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  549. for _, focus in pairs(self.focuses) do
  550. if(focus ~= nil) then
  551. if(not self:isInRange(focus)) then
  552. self:onWalkAway(focus)
  553. elseif((os.time() - self.talkStart[focus]) > self.idleTime) then
  554. self:unGreet(focus)
  555. else
  556. self:updateFocus()
  557. end
  558. end
  559. end
  560. elseif(self.focuses ~= 0) then
  561. if(not self:isInRange(self.focuses)) then
  562. self:onWalkAway(self.focuses)
  563. elseif((os.time() - self.talkStart) > self.idleTime) then
  564. self:unGreet(self.focuses)
  565. else
  566. self:updateFocus()
  567. end
  568. end
  569. end
  570. end
  571. end
  572.  
  573. -- Tries to greet the player with the given cid.
  574. function NpcHandler:onGreet(cid)
  575. if(self:isInRange(cid)) then
  576. if(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE) then
  577. if(not self:isFocused(cid)) then
  578. self:greet(cid)
  579. return
  580. end
  581. elseif(NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT) then
  582. if(self.focuses == 0) then
  583. self:greet(cid)
  584. elseif(self.focuses == cid) then
  585. local msg = self:getMessage(MESSAGE_ALREADYFOCUSED)
  586. local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
  587. msg = self:parseMessage(msg, parseInfo)
  588. self:say(msg)
  589. else
  590. if(not self.queue:isInQueue(cid)) then
  591. self.queue:push(cid)
  592. end
  593.  
  594. local msg = self:getMessage(MESSAGE_PLACEDINQUEUE)
  595. local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid), [TAG_QUEUESIZE] = self.queue:getSize() }
  596. msg = self:parseMessage(msg, parseInfo)
  597. self:say(msg)
  598. end
  599. end
  600. end
  601. end
  602.  
  603. -- Simply calls the underlying unGreet function.
  604. function NpcHandler:onFarewell(cid)
  605. self:unGreet(cid)
  606. end
  607.  
  608. -- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
  609. function NpcHandler:onWalkAway(cid)
  610. if(self:isFocused(cid)) then
  611. local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
  612. if(callback == nil or callback(cid)) then
  613. if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
  614. if(self.queue == nil or not self.queue:greetNext()) then
  615. local msg = self:getMessage(MESSAGE_WALKAWAY)
  616. self:resetNpc(cid)
  617. self:say(self:parseMessage(msg, { [TAG_PLAYERNAME] = getPlayerName(cid) or -1 }))
  618. self:releaseFocus(cid)
  619. end
  620. end
  621. end
  622. end
  623. end
  624.  
  625. -- Returns true if cid is within the talkRadius of this npc.
  626. function NpcHandler:isInRange(cid)
  627. if not isPlayer(cid) then
  628. return false
  629. end
  630.  
  631. local distance = getNpcDistanceTo(cid) or -1
  632. return distance ~= -1 and distance <= self.talkRadius
  633. end
  634.  
  635. -- Resets the npc into it's initial state (in regard of the keyrodhandler).
  636. -- All modules are also receiving a reset call through their callbackOnModuleReset function.
  637. function NpcHandler:resetNpc(cid)
  638. if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
  639. self.keywordHandler:reset(cid)
  640. end
  641. end
  642.  
  643. -- Makes the npc represented by this instance of NpcHandler say something.
  644. -- This implements the currently set type of talkdelay.
  645. function NpcHandler:say(message, focus, delay, force)
  646. local delay = delay or 0
  647. if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or delay <= 0) then
  648. if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then
  649. selfSay(message, focus)
  650. else
  651. selfSay(message)
  652. end
  653.  
  654. return
  655. end
  656.  
  657. -- TODO: Add an event handling method for delayed messages
  658. table.insert(self.talkDelay, {
  659. id = getNpcId(),
  660. cid = focus,
  661. message = message,
  662. time = os.mtime() + (delay and delay or self.talkDelayTime),
  663. start = os.time(),
  664. force = force or false
  665. })
  666. end
  667. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement