Guest User

Untitled

a guest
Mar 10th, 2017
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 43.71 KB | None | 0 0
  1. -- Advanced NPC System by Jiddo
  2.  
  3. if Modules == nil then
  4.     -- default words for greeting and ungreeting the npc. Should be a table containing all such words.
  5.     FOCUS_GREETWORDS = {"hi", "hello"}
  6.     FOCUS_FAREWELLWORDS = {"bye", "farewell"}
  7.  
  8.     -- The words for requesting trade window.
  9.     SHOP_TRADEREQUEST = {"trade"}
  10.  
  11.     -- The word for accepting/declining an offer. CAN ONLY CONTAIN ONE FIELD! Should be a table with a single string value.
  12.     SHOP_YESWORD = {"yes"}
  13.     SHOP_NOWORD = {"no"}
  14.  
  15.     -- Pattern used to get the amount of an item a player wants to buy/sell.
  16.     PATTERN_COUNT = "%d+"
  17.  
  18.     -- Constants used to separate buying from selling.
  19.     SHOPMODULE_SELL_ITEM = 1
  20.     SHOPMODULE_BUY_ITEM = 2
  21.     SHOPMODULE_BUY_ITEM_CONTAINER = 3
  22.  
  23.     -- Constants used for shop mode. Notice: addBuyableItemContainer is working on all modes
  24.     SHOPMODULE_MODE_TALK = 1 -- Old system used before client version 8.2: sell/buy item name
  25.     SHOPMODULE_MODE_TRADE = 2 -- Trade window system introduced in client version 8.2
  26.     SHOPMODULE_MODE_BOTH = 3 -- Both working at one time
  27.  
  28.     -- Used shop mode
  29.     SHOPMODULE_MODE = SHOPMODULE_MODE_BOTH
  30.  
  31.     Modules = {
  32.         parseableModules = {}
  33.     }
  34.  
  35.     StdModule = {}
  36.  
  37.     -- These callback function must be called with parameters.npcHandler = npcHandler in the parameters table or they will not work correctly.
  38.     -- Notice: The members of StdModule have not yet been tested. If you find any bugs, please report them to me.
  39.     -- Usage:
  40.         -- keywordHandler:addKeyword({"offer"}, StdModule.say, {npcHandler = npcHandler, text = "I sell many powerful melee weapons."})
  41.     function StdModule.say(cid, message, keywords, parameters, node)
  42.         local npcHandler = parameters.npcHandler
  43.         if npcHandler == nil then
  44.             error("StdModule.say called without any npcHandler instance.")
  45.         end
  46.         local onlyFocus = (parameters.onlyFocus == nil or parameters.onlyFocus == true)
  47.         if not npcHandler:isFocused(cid) and onlyFocus then
  48.             return false
  49.         end
  50.  
  51.         local parseInfo = {[TAG_PLAYERNAME] = Player(cid):getName()}
  52.         npcHandler:say(npcHandler:parseMessage(parameters.text or parameters.message, parseInfo), cid, parameters.publicize and true)
  53.         if parameters.reset then
  54.             npcHandler:resetNpc(cid)
  55.         elseif parameters.moveup ~= nil then
  56.             npcHandler.keywordHandler:moveUp(cid, parameters.moveup)
  57.         end
  58.  
  59.         return true
  60.     end
  61.  
  62.     --Usage:
  63.         -- local node1 = keywordHandler:addKeyword({"promot"}, StdModule.say, {npcHandler = npcHandler, text = "I can promote you for 20000 gold coins. Do you want me to promote you?"})
  64.         -- node1:addChildKeyword({"yes"}, StdModule.promotePlayer, {npcHandler = npcHandler, cost = 20000, level = 20}, text = "Congratulations! You are now promoted.")
  65.         -- node1:addChildKeyword({"no"}, StdModule.say, {npcHandler = npcHandler, text = "Allright then. Come back when you are ready."}, reset = true)
  66.     function StdModule.promotePlayer(cid, message, keywords, parameters, node)
  67.         local npcHandler = parameters.npcHandler
  68.         if npcHandler == nil then
  69.             error("StdModule.promotePlayer called without any npcHandler instance.")
  70.         end
  71.  
  72.         if not npcHandler:isFocused(cid) then
  73.             return false
  74.         end
  75.  
  76.         local player = Player(cid)
  77.         if player:isPremium() or not parameters.premium then
  78.             local promotion = player:getVocation():getPromotion()
  79.             if player:getStorageValue(STORAGEVALUE_PROMOTION) == 1 then
  80.                 npcHandler:say("You are already promoted!", cid)
  81.             elseif player:getLevel() < parameters.level then
  82.                 npcHandler:say("I am sorry, but I can only promote you once you have reached level " .. parameters.level .. ".", cid)
  83.             elseif not player:removeMoney(parameters.cost) then
  84.                 if player:getBankBalance() < parameters.cost then
  85.                     npcHandler:say("You do not have enough money!", cid)
  86.                 else
  87.                     player:setBankBalance(player:getBankBalance() - parameters.cost)
  88.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(parameters.cost, player:getBankBalance()))
  89.                 end
  90.             else
  91.                 npcHandler:say(parameters.text, cid)
  92.                 player:setVocation(promotion)
  93.                 player:setStorageValue(STORAGEVALUE_PROMOTION, 1)
  94.             end
  95.         else
  96.             npcHandler:say("You need a premium account in order to get promoted.", cid)
  97.         end
  98.         npcHandler:resetNpc(cid)
  99.         return true
  100.     end
  101.  
  102.     function StdModule.learnSpell(cid, message, keywords, parameters, node)
  103.         local npcHandler = parameters.npcHandler
  104.         if npcHandler == nil then
  105.             error("StdModule.learnSpell called without any npcHandler instance.")
  106.         end
  107.  
  108.         if not npcHandler:isFocused(cid) then
  109.             return false
  110.         end
  111.  
  112.         local player = Player(cid)
  113.         if player:isPremium() or not parameters.premium then
  114.             if player:hasLearnedSpell(parameters.spellName) then
  115.                 npcHandler:say("You already know this spell.", cid)
  116.             elseif not player:canLearnSpell(parameters.spellName) then
  117.                 npcHandler:say("You cannot learn this spell.", cid)
  118.             elseif not player:removeMoney(parameters.price) then
  119.                 if player:getBankBalance() < parameters.price then
  120.                     npcHandler:say("You do not have enough money, this spell costs " .. parameters.price .. " gold.", cid)
  121.                 else
  122.                     player:setBankBalance(player:getBankBalance() - parameters.price)
  123.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(parameters.price, player:getBankBalance()))
  124.                 end
  125.             else
  126.                 npcHandler:say("You have learned " .. parameters.spellName .. ".", cid)
  127.                 player:learnSpell(parameters.spellName)
  128.             end
  129.         else
  130.             npcHandler:say("You need a premium account in order to buy " .. parameters.spellName .. ".", cid)
  131.         end
  132.         npcHandler:resetNpc(cid)
  133.         return true
  134.     end
  135.  
  136.     function StdModule.bless(cid, message, keywords, parameters, node)
  137.         local npcHandler = parameters.npcHandler
  138.         if npcHandler == nil then
  139.             error("StdModule.bless called without any npcHandler instance.")
  140.         end
  141.  
  142.         if not npcHandler:isFocused(cid) or getWorldType() == WORLD_TYPE_PVP_ENFORCED then
  143.             return false
  144.         end
  145.  
  146.         local player = Player(cid)
  147.         if player:isPremium() or not parameters.premium then
  148.             if player:hasBlessing(parameters.bless) then
  149.                 npcHandler:say("Gods have already blessed you with this blessing!", cid)
  150.             elseif not player:removeMoney(parameters.cost) then
  151.                 if player:getBankBalance() < parameters.cost then
  152.                     npcHandler:say("You don't have enough money for blessing.", cid)
  153.                 else
  154.                     player:setBankBalance(player:getBankBalance() - parameters.cost)
  155.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(parameters.cost, player:getBankBalance()))
  156.                 end
  157.             else
  158.                 player:addBlessing(parameters.bless)
  159.                 npcHandler:say("You have been blessed by one of the five gods!", cid)
  160.             end
  161.         else
  162.             npcHandler:say("You need a premium account in order to be blessed.", cid)
  163.         end
  164.         npcHandler:resetNpc(cid)
  165.         return true
  166.     end
  167.  
  168.     function StdModule.travel(cid, message, keywords, parameters, node)
  169.         local npcHandler = parameters.npcHandler
  170.         if npcHandler == nil then
  171.             error("StdModule.travel called without any npcHandler instance.")
  172.         end
  173.  
  174.         if not npcHandler:isFocused(cid) then
  175.             return false
  176.         end
  177.  
  178.         local player = Player(cid)
  179.         if player:isPremium() or not parameters.premium then
  180.             if player:isPzLocked() then
  181.                 npcHandler:say("First get rid of those blood stains! You are not going to ruin my vehicle!", cid)
  182.             elseif parameters.level and player:getLevel() < parameters.level then
  183.                 npcHandler:say("You must reach level " .. parameters.level .. " before I can let you go there.", cid)
  184.             elseif not player:removeMoney(parameters.cost) then
  185.                 if player:getBankBalance() < parameters.cost then
  186.                     npcHandler:say("You don't have enough money.", cid)
  187.                 else
  188.                     player:setBankBalance(player:getBankBalance() - parameters.cost)
  189.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(parameters.cost, player:getBankBalance()))
  190.                 end
  191.             else
  192.                 npcHandler:say(parameters.msg or "Set the sails!", cid)
  193.                 npcHandler:releaseFocus(cid)
  194.  
  195.                 local destination = Position(parameters.destination)
  196.                 local position = player:getPosition()
  197.                 player:teleportTo(destination)
  198.  
  199.                 position:sendMagicEffect(CONST_ME_TELEPORT)
  200.                 destination:sendMagicEffect(CONST_ME_TELEPORT)
  201.             end
  202.         else
  203.             npcHandler:say("I'm sorry, but you need a premium account in order to travel onboard our ships.", cid)
  204.         end
  205.         npcHandler:resetNpc(cid)
  206.         return true
  207.     end
  208.  
  209.     FocusModule = {
  210.         npcHandler = nil
  211.     }
  212.  
  213.     -- Creates a new instance of FocusModule without an associated NpcHandler.
  214.     function FocusModule:new()
  215.         local obj = {}
  216.         setmetatable(obj, self)
  217.         self.__index = self
  218.         return obj
  219.     end
  220.  
  221.     -- Inits the module and associates handler to it.
  222.     function FocusModule:init(handler)
  223.         self.npcHandler = handler
  224.         for i, word in pairs(FOCUS_GREETWORDS) do
  225.             local obj = {}
  226.             obj[#obj + 1] = word
  227.             obj.callback = FOCUS_GREETWORDS.callback or FocusModule.messageMatcher
  228.             handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self})
  229.         end
  230.  
  231.         for i, word in pairs(FOCUS_FAREWELLWORDS) do
  232.             local obj = {}
  233.             obj[#obj + 1] = word
  234.             obj.callback = FOCUS_FAREWELLWORDS.callback or FocusModule.messageMatcher
  235.             handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self})
  236.         end
  237.  
  238.         return true
  239.     end
  240.  
  241.     -- Greeting callback function.
  242.     function FocusModule.onGreet(cid, message, keywords, parameters)
  243.         parameters.module.npcHandler:onGreet(cid)
  244.         return true
  245.     end
  246.  
  247.     -- UnGreeting callback function.
  248.     function FocusModule.onFarewell(cid, message, keywords, parameters)
  249.         if parameters.module.npcHandler:isFocused(cid) then
  250.             parameters.module.npcHandler:onFarewell(cid)
  251.             return true
  252.         else
  253.             return false
  254.         end
  255.     end
  256.  
  257.     -- Custom message matching callback function for greeting messages.
  258.     function FocusModule.messageMatcher(keywords, message)
  259.         for i, word in pairs(keywords) do
  260.             if type(word) == "string" then
  261.                 if string.find(message, word) and not string.find(message, "[%w+]" .. word) and not string.find(message, word .. "[%w+]") then
  262.                     return true
  263.                 end
  264.             end
  265.         end
  266.         return false
  267.     end
  268.  
  269.     KeywordModule = {
  270.         npcHandler = nil
  271.     }
  272.     -- Add it to the parseable module list.
  273.     Modules.parseableModules["module_keywords"] = KeywordModule
  274.  
  275.     function KeywordModule:new()
  276.         local obj = {}
  277.         setmetatable(obj, self)
  278.         self.__index = self
  279.         return obj
  280.     end
  281.  
  282.     function KeywordModule:init(handler)
  283.         self.npcHandler = handler
  284.         return true
  285.     end
  286.  
  287.     -- Parses all known parameters.
  288.     function KeywordModule:parseParameters()
  289.         local ret = NpcSystem.getParameter("keywords")
  290.         if ret ~= nil then
  291.             self:parseKeywords(ret)
  292.         end
  293.     end
  294.  
  295.     function KeywordModule:parseKeywords(data)
  296.         local n = 1
  297.         for keys in string.gmatch(data, "[^;]+") do
  298.             local i = 1
  299.  
  300.             local keywords = {}
  301.             for temp in string.gmatch(keys, "[^,]+") do
  302.                 keywords[#keywords + 1] = temp
  303.                 i = i + 1
  304.             end
  305.  
  306.             if i ~= 1 then
  307.                 local reply = NpcSystem.getParameter("keyword_reply" .. n)
  308.                 if reply ~= nil then
  309.                     self:addKeyword(keywords, reply)
  310.                 else
  311.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter '" .. "keyword_reply" .. n .. "' missing. Skipping...")
  312.                 end
  313.             else
  314.                 print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "No keywords found for keyword set #" .. n .. ". Skipping...")
  315.             end
  316.  
  317.             n = n + 1
  318.         end
  319.     end
  320.  
  321.     function KeywordModule:addKeyword(keywords, reply)
  322.         self.npcHandler.keywordHandler:addKeyword(keywords, StdModule.say, {npcHandler = self.npcHandler, onlyFocus = true, text = reply, reset = true})
  323.     end
  324.  
  325.     TravelModule = {
  326.         npcHandler = nil,
  327.         destinations = nil,
  328.         yesNode = nil,
  329.         noNode = nil,
  330.     }
  331.  
  332.     -- Add it to the parseable module list.
  333.     Modules.parseableModules["module_travel"] = TravelModule
  334.  
  335.     function TravelModule:new()
  336.         local obj = {}
  337.         setmetatable(obj, self)
  338.         self.__index = self
  339.         return obj
  340.     end
  341.  
  342.     function TravelModule:init(handler)
  343.         self.npcHandler = handler
  344.         self.yesNode = KeywordNode:new(SHOP_YESWORD, TravelModule.onConfirm, {module = self})
  345.         self.noNode = KeywordNode:new(SHOP_NOWORD, TravelModule.onDecline, {module = self})
  346.         self.destinations = {}
  347.         return true
  348.     end
  349.  
  350.     -- Parses all known parameters.
  351.     function TravelModule:parseParameters()
  352.         local ret = NpcSystem.getParameter("travel_destinations")
  353.         if ret ~= nil then
  354.             self:parseDestinations(ret)
  355.  
  356.             self.npcHandler.keywordHandler:addKeyword({"destination"}, TravelModule.listDestinations, {module = self})
  357.             self.npcHandler.keywordHandler:addKeyword({"where"}, TravelModule.listDestinations, {module = self})
  358.             self.npcHandler.keywordHandler:addKeyword({"travel"}, TravelModule.listDestinations, {module = self})
  359.  
  360.         end
  361.     end
  362.  
  363.     function TravelModule:parseDestinations(data)
  364.         for destination in string.gmatch(data, "[^;]+") do
  365.             local i = 1
  366.  
  367.             local name = nil
  368.             local x = nil
  369.             local y = nil
  370.             local z = nil
  371.             local cost = nil
  372.             local premium = false
  373.  
  374.             for temp in string.gmatch(destination, "[^,]+") do
  375.                 if i == 1 then
  376.                     name = temp
  377.                 elseif i == 2 then
  378.                     x = tonumber(temp)
  379.                 elseif i == 3 then
  380.                     y = tonumber(temp)
  381.                 elseif i == 4 then
  382.                     z = tonumber(temp)
  383.                 elseif i == 5 then
  384.                     cost = tonumber(temp)
  385.                 elseif i == 6 then
  386.                     premium = temp == "true"
  387.                 else
  388.                     print("[Warning : " .. getCreatureName(getNpcCid()) .. "] NpcSystem:", "Unknown parameter found in travel destination parameter.", temp, destination)
  389.                 end
  390.                 i = i + 1
  391.             end
  392.  
  393.             if name ~= nil and x ~= nil and y ~= nil and z ~= nil and cost ~= nil then
  394.                 self:addDestination(name, {x=x, y=y, z=z}, cost, premium)
  395.             else
  396.                 print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter(s) missing for travel destination:", name, x, y, z, cost, premium)
  397.             end
  398.         end
  399.     end
  400.  
  401.     function TravelModule:addDestination(name, position, price, premium)
  402.         self.destinations[#self.destinations + 1] = name
  403.  
  404.         local parameters = {
  405.             cost = price,
  406.             destination = position,
  407.             premium = premium,
  408.             module = self
  409.         }
  410.         local keywords = {}
  411.         keywords[#keywords + 1] = name
  412.  
  413.         local keywords2 = {}
  414.         keywords2[#keywords2 + 1] = "bring me to " .. name
  415.         local node = self.npcHandler.keywordHandler:addKeyword(keywords, TravelModule.travel, parameters)
  416.         self.npcHandler.keywordHandler:addKeyword(keywords2, TravelModule.bringMeTo, parameters)
  417.         node:addChildKeywordNode(self.yesNode)
  418.         node:addChildKeywordNode(self.noNode)
  419.  
  420.         if npcs_loaded_travel[getNpcCid()] == nil then
  421.             npcs_loaded_travel[getNpcCid()] = getNpcCid()
  422.             self.npcHandler.keywordHandler:addKeyword({'yes'}, TravelModule.onConfirm, {module = self})
  423.             self.npcHandler.keywordHandler:addKeyword({'no'}, TravelModule.onDecline, {module = self})
  424.         end
  425.     end
  426.  
  427.     function TravelModule.travel(cid, message, keywords, parameters, node)
  428.         local module = parameters.module
  429.         if not module.npcHandler:isFocused(cid) then
  430.             return false
  431.         end
  432.  
  433.         local npcHandler = module.npcHandler
  434.  
  435.         shop_destination[cid] = parameters.destination
  436.         shop_cost[cid] = parameters.cost
  437.         shop_premium[cid] = parameters.premium
  438.         shop_npcuid[cid] = getNpcCid()
  439.  
  440.         local cost = parameters.cost
  441.         local destination = parameters.destination
  442.         local premium = parameters.premium
  443.  
  444.         module.npcHandler:say("Do you want to travel to " .. keywords[1] .. " for " .. cost .. " gold coins?", cid)
  445.         return true
  446.     end
  447.  
  448.  
  449.  function TravelModule.onConfirm(cid, message, keywords, parameters, node)
  450.         local module = parameters.module
  451.         if not module.npcHandler:isFocused(cid) then
  452.             return false
  453.         end
  454.  
  455.         if shop_npcuid[cid] ~= Npc().uid then
  456.             return false
  457.         end
  458.  
  459.         local npcHandler = module.npcHandler
  460.  
  461.         local cost = shop_cost[cid]
  462.         local destination = Position(shop_destination[cid])
  463.  
  464.         local player = Player(cid)
  465.         if player:isPremium() or not shop_premium[cid] then
  466.             if player:isPzLocked() then
  467.                 npcHandler:say("Get out of there with this blood.", cid)
  468.                 npcHandler:resetNpc(cid)
  469.                 return true
  470.             end
  471.  
  472.             if not player:removeMoney(cost) then
  473.                 if player:getBankBalance() < cost then
  474.                     npcHandler:say("You do not have enough money!", cid)
  475.                     npcHandler:resetNpc(cid)
  476.                     return true
  477.                 else
  478.                     player:setBankBalance(player:getBankBalance() - cost)
  479.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(cost, player:getBankBalance()))
  480.                     print("testing")
  481.                 end
  482.             end
  483.  
  484.             npcHandler:say("It was a pleasure doing business with you.", cid)
  485.             npcHandler:releaseFocus(cid)
  486.  
  487.             local position = player:getPosition()
  488.             player:teleportTo(destination)
  489.  
  490.             position:sendMagicEffect(CONST_ME_TELEPORT)
  491.             destination:sendMagicEffect(CONST_ME_TELEPORT)
  492.         else
  493.             npcHandler:say("I can only allow premium players to travel there.", cid)
  494.         end
  495.  
  496.         npcHandler:resetNpc(cid)
  497.         return true
  498.     end
  499.  
  500.     -- onDecline keyword callback function. Generally called when the player sais "no" after wanting to buy an item.
  501.     function TravelModule.onDecline(cid, message, keywords, parameters, node)
  502.         local module = parameters.module
  503.         if not module.npcHandler:isFocused(cid) or shop_npcuid[cid] ~= getNpcCid() then
  504.             return false
  505.         end
  506.         local parentParameters = node:getParent():getParameters()
  507.         local parseInfo = { [TAG_PLAYERNAME] = Player(cid):getName() }
  508.         local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), parseInfo)
  509.         module.npcHandler:say(msg, cid)
  510.         module.npcHandler:resetNpc(cid)
  511.         return true
  512.     end
  513.  
  514.     function TravelModule.bringMeTo(cid, message, keywords, parameters, node)
  515.         local module = parameters.module
  516.         if not module.npcHandler:isFocused(cid) then
  517.             return false
  518.         end
  519.  
  520.         local cost = parameters.cost
  521.         local destination = Position(parameters.destination)
  522.  
  523.         local player = Player(cid)
  524.         if player:isPremium() or not parameters.premium then
  525.             if player:removeMoney(cost) then
  526.                 local position = player:getPosition()
  527.                 player:teleportTo(destination)
  528.  
  529.                 position:sendMagicEffect(CONST_ME_TELEPORT)
  530.                 destination:sendMagicEffect(CONST_ME_TELEPORT)
  531.             else
  532.                 if player:getBankBalance() >= cost then
  533.                     player:setBankBalance(player:getBankBalance() - cost)
  534.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(cost, player:getBankBalance()))
  535.                     local position = player:getPosition()
  536.                     player:teleportTo(destination)
  537.  
  538.                     position:sendMagicEffect(CONST_ME_TELEPORT)
  539.                     destination:sendMagicEffect(CONST_ME_TELEPORT)
  540.                 end
  541.             end
  542.         end
  543.         return true
  544.     end
  545.  
  546.     function TravelModule.listDestinations(cid, message, keywords, parameters, node)
  547.         local module = parameters.module
  548.         if not module.npcHandler:isFocused(cid) then
  549.             return false
  550.         end
  551.  
  552.         local msg = "I can bring you to "
  553.         --local i = 1
  554.         local maxn = #module.destinations
  555.         for i, destination in pairs(module.destinations) do
  556.             msg = msg .. destination
  557.             if i == maxn - 1 then
  558.                 msg = msg .. " and "
  559.             elseif i == maxn then
  560.                 msg = msg .. "."
  561.             else
  562.                 msg = msg .. ", "
  563.             end
  564.             i = i + 1
  565.         end
  566.  
  567.         module.npcHandler:say(msg, cid)
  568.         module.npcHandler:resetNpc(cid)
  569.         return true
  570.     end
  571.  
  572.     ShopModule = {
  573.         npcHandler = nil,
  574.         yesNode = nil,
  575.         noNode = nil,
  576.         noText = "",
  577.         maxCount = 100,
  578.         amount = 0
  579.     }
  580.  
  581.     -- Add it to the parseable module list.
  582.     Modules.parseableModules["module_shop"] = ShopModule
  583.  
  584.     -- Creates a new instance of ShopModule
  585.     function ShopModule:new()
  586.         local obj = {}
  587.         setmetatable(obj, self)
  588.         self.__index = self
  589.         return obj
  590.     end
  591.  
  592.     -- Parses all known parameters.
  593.     function ShopModule:parseParameters()
  594.         local ret = NpcSystem.getParameter("shop_buyable")
  595.         if ret ~= nil then
  596.             self:parseBuyable(ret)
  597.         end
  598.  
  599.         local ret = NpcSystem.getParameter("shop_sellable")
  600.         if ret ~= nil then
  601.             self:parseSellable(ret)
  602.         end
  603.  
  604.         local ret = NpcSystem.getParameter("shop_buyable_containers")
  605.         if ret ~= nil then
  606.             self:parseBuyableContainers(ret)
  607.         end
  608.     end
  609.  
  610.     -- Parse a string contaning a set of buyable items.
  611.     function ShopModule:parseBuyable(data)
  612.         for item in string.gmatch(data, "[^;]+") do
  613.             local i = 1
  614.  
  615.             local name = nil
  616.             local itemid = nil
  617.             local cost = nil
  618.             local subType = nil
  619.             local realName = nil
  620.  
  621.             for temp in string.gmatch(item, "[^,]+") do
  622.                 if i == 1 then
  623.                     name = temp
  624.                 elseif i == 2 then
  625.                     itemid = tonumber(temp)
  626.                 elseif i == 3 then
  627.                     cost = tonumber(temp)
  628.                 elseif i == 4 then
  629.                     subType = tonumber(temp)
  630.                 elseif i == 5 then
  631.                     realName = temp
  632.                 else
  633.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Unknown parameter found in buyable items parameter.", temp, item)
  634.                 end
  635.                 i = i + 1
  636.             end
  637.  
  638.             local it = ItemType(itemid)
  639.             if subType == nil and it:getCharges() ~= 0 then
  640.                 subType = it:getCharges()
  641.             end
  642.  
  643.             if SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE then
  644.                 if itemid ~= nil and cost ~= nil then
  645.                     if subType == nil and it:isFluidContainer() then
  646.                         print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "SubType missing for parameter item:", item)
  647.                     else
  648.                         self:addBuyableItem(nil, itemid, cost, subType, realName)
  649.                     end
  650.                 else
  651.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter(s) missing for item:", itemid, cost)
  652.                 end
  653.             else
  654.                 if name ~= nil and itemid ~= nil and cost ~= nil then
  655.                     if subType == nil and it:isFluidContainer() then
  656.                         print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "SubType missing for parameter item:", item)
  657.                     else
  658.                         local names = {}
  659.                         names[#names + 1] = name
  660.                         self:addBuyableItem(names, itemid, cost, subType, realName)
  661.                     end
  662.                 else
  663.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter(s) missing for item:", name, itemid, cost)
  664.                 end
  665.             end
  666.         end
  667.     end
  668.  
  669.     -- Parse a string contaning a set of sellable items.
  670.     function ShopModule:parseSellable(data)
  671.         for item in string.gmatch(data, "[^;]+") do
  672.             local i = 1
  673.  
  674.             local name = nil
  675.             local itemid = nil
  676.             local cost = nil
  677.             local realName = nil
  678.             local subType = nil
  679.  
  680.             for temp in string.gmatch(item, "[^,]+") do
  681.                 if i == 1 then
  682.                     name = temp
  683.                 elseif i == 2 then
  684.                     itemid = tonumber(temp)
  685.                 elseif i == 3 then
  686.                     cost = tonumber(temp)
  687.                 elseif i == 4 then
  688.                     realName = temp
  689.                 elseif i == 5 then
  690.                     subType = tonumber(temp)
  691.                 else
  692.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Unknown parameter found in sellable items parameter.", temp, item)
  693.                 end
  694.                 i = i + 1
  695.             end
  696.  
  697.             if SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE then
  698.                 if itemid ~= nil and cost ~= nil then
  699.                     self:addSellableItem(nil, itemid, cost, realName, subType)
  700.                 else
  701.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter(s) missing for item:", itemid, cost)
  702.                 end
  703.             else
  704.                 if name ~= nil and itemid ~= nil and cost ~= nil then
  705.                     local names = {}
  706.                     names[#names + 1] = name
  707.                     self:addSellableItem(names, itemid, cost, realName, subType)
  708.                 else
  709.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter(s) missing for item:", name, itemid, cost)
  710.                 end
  711.             end
  712.         end
  713.     end
  714.  
  715.     -- Parse a string contaning a set of buyable items.
  716.     function ShopModule:parseBuyableContainers(data)
  717.         for item in string.gmatch(data, "[^;]+") do
  718.             local i = 1
  719.  
  720.             local name = nil
  721.             local container = nil
  722.             local itemid = nil
  723.             local cost = nil
  724.             local subType = nil
  725.             local realName = nil
  726.  
  727.             for temp in string.gmatch(item, "[^,]+") do
  728.                 if i == 1 then
  729.                     name = temp
  730.                 elseif i == 2 then
  731.                     itemid = tonumber(temp)
  732.                 elseif i == 3 then
  733.                     itemid = tonumber(temp)
  734.                 elseif i == 4 then
  735.                     cost = tonumber(temp)
  736.                 elseif i == 5 then
  737.                     subType = tonumber(temp)
  738.                 elseif i == 6 then
  739.                     realName = temp
  740.                 else
  741.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Unknown parameter found in buyable items parameter.", temp, item)
  742.                 end
  743.                 i = i + 1
  744.             end
  745.  
  746.             if name ~= nil and container ~= nil and itemid ~= nil and cost ~= nil then
  747.                 if subType == nil and ItemType(itemid):isFluidContainer() then
  748.                     print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "SubType missing for parameter item:", item)
  749.                 else
  750.                     local names = {}
  751.                     names[#names + 1] = name
  752.                     self:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
  753.                 end
  754.             else
  755.                 print("[Warning : " .. Npc():getName() .. "] NpcSystem:", "Parameter(s) missing for item:", name, container, itemid, cost)
  756.             end
  757.         end
  758.     end
  759.  
  760.     -- Initializes the module and associates handler to it.
  761.     function ShopModule:init(handler)
  762.         self.npcHandler = handler
  763.         self.yesNode = KeywordNode:new(SHOP_YESWORD, ShopModule.onConfirm, {module = self})
  764.         self.noNode = KeywordNode:new(SHOP_NOWORD, ShopModule.onDecline, {module = self})
  765.         self.noText = handler:getMessage(MESSAGE_DECLINE)
  766.  
  767.         if SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK then
  768.             for i, word in pairs(SHOP_TRADEREQUEST) do
  769.                 local obj = {}
  770.                 obj[#obj + 1] = word
  771.                 obj.callback = SHOP_TRADEREQUEST.callback or ShopModule.messageMatcher
  772.                 handler.keywordHandler:addKeyword(obj, ShopModule.requestTrade, {module = self})
  773.             end
  774.         end
  775.  
  776.         return true
  777.     end
  778.  
  779.     -- Custom message matching callback function for requesting trade messages.
  780.     function ShopModule.messageMatcher(keywords, message)
  781.         for i, word in pairs(keywords) do
  782.             if type(word) == "string" then
  783.                 if string.find(message, word) and not string.find(message, "[%w+]" .. word) and not string.find(message, word .. "[%w+]") then
  784.                     return true
  785.                 end
  786.             end
  787.         end
  788.  
  789.         return false
  790.     end
  791.  
  792.     -- Resets the module-specific variables.
  793.     function ShopModule:reset()
  794.         self.amount = 0
  795.     end
  796.  
  797.     -- Function used to match a number value from a string.
  798.     function ShopModule:getCount(message)
  799.         local ret = 1
  800.         local b, e = string.find(message, PATTERN_COUNT)
  801.         if b ~= nil and e ~= nil then
  802.             ret = tonumber(string.sub(message, b, e))
  803.         end
  804.  
  805.         if ret <= 0 then
  806.             ret = 1
  807.         elseif ret > self.maxCount then
  808.             ret = self.maxCount
  809.         end
  810.  
  811.         return ret
  812.     end
  813.  
  814.     -- Adds a new buyable item.
  815.     --  names = A table containing one or more strings of alternative names to this item. Used only for old buy/sell system.
  816.     --  itemid = The itemid of the buyable item
  817.     --  cost = The price of one single item
  818.     --  subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
  819.     --  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 (getItemName will be used)
  820.     function ShopModule:addBuyableItem(names, itemid, cost, itemSubType, realName)
  821.         if SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK then
  822.             if itemSubType == nil then
  823.                 itemSubType = 1
  824.             end
  825.  
  826.             local shopItem = self:getShopItem(itemid, itemSubType)
  827.             if shopItem == nil then
  828.                 self.npcHandler.shopItems[#self.npcHandler.shopItems + 1] = {id = itemid, buy = cost, sell = -1, subType = itemSubType, name = realName or ItemType(itemid):getName()}
  829.             else
  830.                 shopItem.buy = cost
  831.             end
  832.         end
  833.  
  834.         if names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE then
  835.             for i, name in pairs(names) do
  836.                 local parameters = {
  837.                         itemid = itemid,
  838.                         cost = cost,
  839.                         eventType = SHOPMODULE_BUY_ITEM,
  840.                         module = self,
  841.                         realName = realName or ItemType(itemid):getName(),
  842.                         subType = itemSubType or 1
  843.                     }
  844.  
  845.                 keywords = {}
  846.                 keywords[#keywords + 1] = "buy"
  847.                 keywords[#keywords + 1] = name
  848.                 local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
  849.                 node:addChildKeywordNode(self.yesNode)
  850.                 node:addChildKeywordNode(self.noNode)
  851.             end
  852.         end
  853.  
  854.         if npcs_loaded_shop[getNpcCid()] == nil then
  855.             npcs_loaded_shop[getNpcCid()] = getNpcCid()
  856.             self.npcHandler.keywordHandler:addKeyword({'yes'}, ShopModule.onConfirm, {module = self})
  857.             self.npcHandler.keywordHandler:addKeyword({'no'}, ShopModule.onDecline, {module = self})
  858.         end
  859.     end
  860.  
  861.     function ShopModule:getShopItem(itemId, itemSubType)
  862.         if ItemType(itemId):isFluidContainer() then
  863.             for i = 1, #self.npcHandler.shopItems do
  864.                 local shopItem = self.npcHandler.shopItems[i]
  865.                 if shopItem.id == itemId and shopItem.subType == itemSubType then
  866.                     return shopItem
  867.                 end
  868.             end
  869.         else
  870.             for i = 1, #self.npcHandler.shopItems do
  871.                 local shopItem = self.npcHandler.shopItems[i]
  872.                 if shopItem.id == itemId then
  873.                     return shopItem
  874.                 end
  875.             end
  876.         end
  877.         return nil
  878.     end
  879.  
  880.     -- Adds a new buyable container of items.
  881.     --  names = A table containing one or more strings of alternative names to this item.
  882.     --  container = Backpack, bag or any other itemid of container where bought items will be stored
  883.     --  itemid = The itemid of the buyable item
  884.     --  cost = The price of one single item
  885.     --  subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1.
  886.     --  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 (getItemName will be used)
  887.     function ShopModule:addBuyableItemContainer(names, container, itemid, cost, subType, realName)
  888.         if names ~= nil then
  889.             for i, name in pairs(names) do
  890.                 local parameters = {
  891.                         container = container,
  892.                         itemid = itemid,
  893.                         cost = cost,
  894.                         eventType = SHOPMODULE_BUY_ITEM_CONTAINER,
  895.                         module = self,
  896.                         realName = realName or ItemType(itemid):getName(),
  897.                         subType = subType or 1
  898.                     }
  899.  
  900.                 keywords = {}
  901.                 keywords[#keywords + 1] = "buy"
  902.                 keywords[#keywords + 1] = name
  903.                 local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
  904.                 node:addChildKeywordNode(self.yesNode)
  905.                 node:addChildKeywordNode(self.noNode)
  906.             end
  907.         end
  908.     end
  909.  
  910.     -- Adds a new sellable item.
  911.     --  names = A table containing one or more strings of alternative names to this item. Used only by old buy/sell system.
  912.     --  itemid = The itemid of the sellable item
  913.     --  cost = The price of one single item
  914.     --  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 (getItemName will be used)
  915.     function ShopModule:addSellableItem(names, itemid, cost, realName, itemSubType)
  916.         if SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK then
  917.             if itemSubType == nil then
  918.                 itemSubType = 0
  919.             end
  920.  
  921.             local shopItem = self:getShopItem(itemid, itemSubType)
  922.             if shopItem == nil then
  923.                 self.npcHandler.shopItems[#self.npcHandler.shopItems + 1] = {id = itemid, buy = -1, sell = cost, subType = itemSubType, name = realName or getItemName(itemid)}
  924.             else
  925.                 shopItem.sell = cost
  926.             end
  927.         end
  928.  
  929.         if names ~= nil and SHOPMODULE_MODE ~= SHOPMODULE_MODE_TRADE then
  930.             for i, name in pairs(names) do
  931.                 local parameters = {
  932.                     itemid = itemid,
  933.                     cost = cost,
  934.                     eventType = SHOPMODULE_SELL_ITEM,
  935.                     module = self,
  936.                     realName = realName or getItemName(itemid)
  937.                 }
  938.  
  939.                 keywords = {}
  940.                 keywords[#keywords + 1] = "sell"
  941.                 keywords[#keywords + 1] = name
  942.  
  943.                 local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters)
  944.                 node:addChildKeywordNode(self.yesNode)
  945.                 node:addChildKeywordNode(self.noNode)
  946.             end
  947.         end
  948.     end
  949.  
  950.     -- onModuleReset callback function. Calls ShopModule:reset()
  951.     function ShopModule:callbackOnModuleReset()
  952.         self:reset()
  953.         return true
  954.     end
  955.  
  956.     -- Callback onBuy() function. If you wish, you can change certain Npc to use your onBuy().
  957.     function ShopModule:callbackOnBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
  958.         local player = Player(cid)
  959.         local shopItem = self:getShopItem(itemid, subType)
  960.         if shopItem == nil then
  961.             error("[ShopModule.onBuy] shopItem == nil")
  962.             return false
  963.         end
  964.  
  965.         if shopItem.buy == -1 then
  966.             error("[ShopModule.onSell] attempt to buy a non-buyable item")
  967.             return false
  968.         end
  969.  
  970.         local backpack = 1988
  971.         local totalCost = amount * shopItem.buy
  972.         if inBackpacks then
  973.             totalCost = isItemStackable(itemid) == TRUE and totalCost + 20 or totalCost + (math.max(1, math.floor(amount / getContainerCapById(backpack))) * 20)
  974.         end
  975.  
  976.         local parseInfo = {
  977.             [TAG_PLAYERNAME] = getPlayerName(cid),
  978.             [TAG_ITEMCOUNT] = amount,
  979.             [TAG_TOTALCOST] = totalCost,
  980.             [TAG_ITEMNAME] = shopItem.name
  981.         }
  982.  
  983.         local useBankBalance = false
  984.         if getPlayerMoney(cid) < totalCost then
  985.             if player:getBankBalance() < totalCost then
  986.                 local msg = self.npcHandler:getMessage(MESSAGE_NEEDMONEY)
  987.                 msg = self.npcHandler:parseMessage(msg, parseInfo)
  988.                 doPlayerSendCancel(cid, msg)
  989.                 return false
  990.             else
  991.                 useBankBalance = true
  992.             end
  993.         end
  994.  
  995.         local subType = shopItem.subType or 1
  996.         local a, b = doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, backpack)
  997.         if a < amount then
  998.             local msgId = MESSAGE_NEEDMORESPACE
  999.             if a == 0 then
  1000.                 msgId = MESSAGE_NEEDSPACE
  1001.             end
  1002.  
  1003.             local msg = self.npcHandler:getMessage(msgId)
  1004.             parseInfo[TAG_ITEMCOUNT] = a
  1005.             msg = self.npcHandler:parseMessage(msg, parseInfo)
  1006.             doPlayerSendCancel(cid, msg)
  1007.             self.npcHandler.talkStart[cid] = os.time()
  1008.  
  1009.             if a > 0 then
  1010.                 if useBankBalance then
  1011.                     local cost = ((a * shopItem.buy) + (b * 20))
  1012.                     player:setBankBalance(player:getBankBalance() - cost)
  1013.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(cost, player:getBankBalance()))
  1014.                 else
  1015.                     doPlayerRemoveMoney(cid, ((a * shopItem.buy) + (b * 20)))
  1016.                 end
  1017.                 return true
  1018.             end
  1019.  
  1020.             return false
  1021.         else
  1022.             local msg = self.npcHandler:getMessage(MESSAGE_BOUGHT)
  1023.             msg = self.npcHandler:parseMessage(msg, parseInfo)
  1024.             doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, msg)
  1025.             if useBankBalance then
  1026.                 player:setBankBalance(player:getBankBalance() - totalCost)
  1027.                 player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(totalCost, player:getBankBalance()))
  1028.             else
  1029.                 doPlayerRemoveMoney(cid, totalCost)
  1030.             end
  1031.             self.npcHandler.talkStart[cid] = os.time()
  1032.             return true
  1033.         end
  1034.     end
  1035.  
  1036.     -- Callback onSell() function. If you wish, you can change certain Npc to use your onSell().
  1037.     function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreEquipped, _)
  1038.         local shopItem = self:getShopItem(itemid, subType)
  1039.         if shopItem == nil then
  1040.             error("[ShopModule.onSell] items[itemid] == nil")
  1041.             return false
  1042.         end
  1043.  
  1044.         if shopItem.sell == -1 then
  1045.             error("[ShopModule.onSell] attempt to sell a non-sellable item")
  1046.             return false
  1047.         end
  1048.  
  1049.         local parseInfo = {
  1050.             [TAG_PLAYERNAME] = getPlayerName(cid),
  1051.             [TAG_ITEMCOUNT] = amount,
  1052.             [TAG_TOTALCOST] = amount * shopItem.sell,
  1053.             [TAG_ITEMNAME] = shopItem.name
  1054.         }
  1055.  
  1056.         if not isItemFluidContainer(itemid) then
  1057.             subType = -1
  1058.         end
  1059.  
  1060.         if doPlayerRemoveItem(cid, itemid, amount, subType, ignoreEquipped) then
  1061.             local msg = self.npcHandler:getMessage(MESSAGE_SOLD)
  1062.             msg = self.npcHandler:parseMessage(msg, parseInfo)
  1063.             doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, msg)
  1064.             doPlayerAddMoney(cid, amount * shopItem.sell)
  1065.             self.npcHandler.talkStart[cid] = os.time()
  1066.             return true
  1067.         else
  1068.             local msg = self.npcHandler:getMessage(MESSAGE_NEEDITEM)
  1069.             msg = self.npcHandler:parseMessage(msg, parseInfo)
  1070.             doPlayerSendCancel(cid, msg)
  1071.             self.npcHandler.talkStart[cid] = os.time()
  1072.             return false
  1073.         end
  1074.     end
  1075.  
  1076.     -- Callback for requesting a trade window with the NPC.
  1077.     function ShopModule.requestTrade(cid, message, keywords, parameters, node)
  1078.         local module = parameters.module
  1079.         if not module.npcHandler:isFocused(cid) then
  1080.             return false
  1081.         end
  1082.  
  1083.         if not module.npcHandler:onTradeRequest(cid) then
  1084.             return false
  1085.         end
  1086.  
  1087.         local itemWindow = {}
  1088.         for i = 1, #module.npcHandler.shopItems do
  1089.             itemWindow[#itemWindow + 1] = module.npcHandler.shopItems[i]
  1090.         end
  1091.  
  1092.         if itemWindow[1] == nil then
  1093.             local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
  1094.             local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_NOSHOP), parseInfo)
  1095.             module.npcHandler:say(msg, cid)
  1096.             return true
  1097.         end
  1098.  
  1099.         local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
  1100.         local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_SENDTRADE), parseInfo)
  1101.         openShopWindow(cid, itemWindow,
  1102.             function(cid, itemid, subType, amount, ignoreCap, inBackpacks) module.npcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks) end,
  1103.             function(cid, itemid, subType, amount, ignoreCap, inBackpacks) module.npcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks) end)
  1104.         module.npcHandler:say(msg, cid)
  1105.         return true
  1106.     end
  1107.  
  1108.     -- onConfirm keyword callback function. Sells/buys the actual item.
  1109.     function ShopModule.onConfirm(cid, message, keywords, parameters, node)
  1110.         local player = Player(cid)
  1111.         local module = parameters.module
  1112.         if not module.npcHandler:isFocused(cid) or shop_npcuid[cid] ~= getNpcCid() then
  1113.             return false
  1114.         end
  1115.         shop_npcuid[cid] = 0
  1116.  
  1117.         local parentParameters = node:getParent():getParameters()
  1118.         local parseInfo = {
  1119.             [TAG_PLAYERNAME] = getPlayerName(cid),
  1120.             [TAG_ITEMCOUNT] = shop_amount[cid],
  1121.             [TAG_TOTALCOST] = shop_cost[cid] * shop_amount[cid],
  1122.             [TAG_ITEMNAME] = shop_rlname[cid]
  1123.         }
  1124.  
  1125.         if shop_eventtype[cid] == SHOPMODULE_SELL_ITEM then
  1126.             local ret = doPlayerSellItem(cid, shop_itemid[cid], shop_amount[cid], shop_cost[cid] * shop_amount[cid])
  1127.             if ret == true then
  1128.                 local msg = module.npcHandler:getMessage(MESSAGE_ONSELL)
  1129.                 msg = module.npcHandler:parseMessage(msg, parseInfo)
  1130.                 module.npcHandler:say(msg, cid)
  1131.             else
  1132.                 local msg = module.npcHandler:getMessage(MESSAGE_MISSINGITEM)
  1133.                 msg = module.npcHandler:parseMessage(msg, parseInfo)
  1134.                 module.npcHandler:say(msg, cid)
  1135.             end
  1136.         elseif shop_eventtype[cid] == SHOPMODULE_BUY_ITEM then
  1137.             local cost = shop_cost[cid] * shop_amount[cid]
  1138.             local useBankBalance = false
  1139.             if getPlayerMoney(cid) < cost then
  1140.                 if player:getBankBalance() < cost then
  1141.                     local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
  1142.                     msg = module.npcHandler:parseMessage(msg, parseInfo)
  1143.                     module.npcHandler:say(msg, cid)
  1144.                     return false
  1145.                 else
  1146.                     useBankBalance = true
  1147.                 end
  1148.             end
  1149.  
  1150.             local a, b = doNpcSellItem(cid, shop_itemid[cid], shop_amount[cid], shop_subtype[cid], false, false, 1988)
  1151.             if a < shop_amount[cid] then
  1152.                 local msgId = MESSAGE_NEEDMORESPACE
  1153.                 if a == 0 then
  1154.                     msgId = MESSAGE_NEEDSPACE
  1155.                 end
  1156.  
  1157.                 local msg = module.npcHandler:getMessage(msgId)
  1158.                 msg = module.npcHandler:parseMessage(msg, parseInfo)
  1159.                 module.npcHandler:say(msg, cid)
  1160.                 if a > 0 then
  1161.                     if not useBankBalance then
  1162.                         doPlayerRemoveMoney(cid, a * shop_cost[cid])
  1163.                     else
  1164.                         player:setBankBalance(player:getBankBalance() - a * shop_cost[cid])
  1165.                         player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(a * shop_cost[cid], player:getBankBalance()))
  1166.                     end
  1167.                    
  1168.                     if shop_itemid[cid] == ITEM_PARCEL then
  1169.                         doNpcSellItem(cid, ITEM_LABEL, shop_amount[cid], shop_subtype[cid], true, false, 1988)
  1170.                     end
  1171.                     return true
  1172.                 end
  1173.                 return false
  1174.             else
  1175.                 local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
  1176.                 msg = module.npcHandler:parseMessage(msg, parseInfo)
  1177.                 module.npcHandler:say(msg, cid)
  1178.                
  1179.                 if not useBankBalance then
  1180.                     doPlayerRemoveMoney(cid, cost)
  1181.                 else
  1182.                     player:setBankBalance(player:getBankBalance() - a * shop_cost[cid])
  1183.                     player:sendTextMessage(MESSAGE_INFO_DESCR, ("Bank Account: %d gold has been withdrawn from your account for this purchase. Your account balance is %d gold coins."):format(a * shop_cost[cid], player:getBankBalance()))
  1184.                 end
  1185.                
  1186.                 if shop_itemid[cid] == ITEM_PARCEL then
  1187.                     doNpcSellItem(cid, ITEM_LABEL, shop_amount[cid], shop_subtype[cid], true, false, 1988)
  1188.                 end
  1189.                 return true
  1190.             end
  1191.         elseif shop_eventtype[cid] == SHOPMODULE_BUY_ITEM_CONTAINER then
  1192.             local ret = doPlayerBuyItemContainer(cid, shop_container[cid], shop_itemid[cid], shop_amount[cid], shop_cost[cid] * shop_amount[cid], shop_subtype[cid])
  1193.             if ret == true then
  1194.                 local msg = module.npcHandler:getMessage(MESSAGE_ONBUY)
  1195.                 msg = module.npcHandler:parseMessage(msg, parseInfo)
  1196.                 module.npcHandler:say(msg, cid)
  1197.             else
  1198.                 local msg = module.npcHandler:getMessage(MESSAGE_MISSINGMONEY)
  1199.                 msg = module.npcHandler:parseMessage(msg, parseInfo)
  1200.                 module.npcHandler:say(msg, cid)
  1201.             end
  1202.         end
  1203.  
  1204.         module.npcHandler:resetNpc(cid)
  1205.         return true
  1206.     end
  1207.  
  1208.     -- onDecline keyword callback function. Generally called when the player sais "no" after wanting to buy an item.
  1209.     function ShopModule.onDecline(cid, message, keywords, parameters, node)
  1210.         local module = parameters.module
  1211.         if not module.npcHandler:isFocused(cid) or shop_npcuid[cid] ~= getNpcCid() then
  1212.             return false
  1213.         end
  1214.         shop_npcuid[cid] = 0
  1215.  
  1216.         local parentParameters = node:getParent():getParameters()
  1217.         local parseInfo = {
  1218.             [TAG_PLAYERNAME] = getPlayerName(cid),
  1219.             [TAG_ITEMCOUNT] = shop_amount[cid],
  1220.             [TAG_TOTALCOST] = shop_cost[cid] * shop_amount[cid],
  1221.             [TAG_ITEMNAME] = shop_rlname[cid]
  1222.         }
  1223.  
  1224.         local msg = module.npcHandler:parseMessage(module.noText, parseInfo)
  1225.         module.npcHandler:say(msg, cid)
  1226.         module.npcHandler:resetNpc(cid)
  1227.         return true
  1228.     end
  1229.  
  1230.     -- tradeItem callback function. Makes the npc say the message defined by MESSAGE_BUY or MESSAGE_SELL
  1231.     function ShopModule.tradeItem(cid, message, keywords, parameters, node)
  1232.         local module = parameters.module
  1233.         if not module.npcHandler:isFocused(cid) then
  1234.             return false
  1235.         end
  1236.  
  1237.         if not module.npcHandler:onTradeRequest(cid) then
  1238.             return true
  1239.         end
  1240.  
  1241.         local count = module:getCount(message)
  1242.         module.amount = count
  1243.  
  1244.         shop_amount[cid] = module.amount
  1245.         shop_cost[cid] = parameters.cost
  1246.         shop_rlname[cid] = parameters.realName
  1247.         shop_itemid[cid] = parameters.itemid
  1248.         shop_container[cid] = parameters.container
  1249.         shop_npcuid[cid] = getNpcCid()
  1250.         shop_eventtype[cid] = parameters.eventType
  1251.         shop_subtype[cid] = parameters.subType
  1252.  
  1253.         local parseInfo = {
  1254.             [TAG_PLAYERNAME] = getPlayerName(cid),
  1255.             [TAG_ITEMCOUNT] = shop_amount[cid],
  1256.             [TAG_TOTALCOST] = shop_cost[cid] * shop_amount[cid],
  1257.             [TAG_ITEMNAME] = shop_rlname[cid]
  1258.         }
  1259.  
  1260.         if shop_eventtype[cid] == SHOPMODULE_SELL_ITEM then
  1261.             local msg = module.npcHandler:getMessage(MESSAGE_SELL)
  1262.             msg = module.npcHandler:parseMessage(msg, parseInfo)
  1263.             module.npcHandler:say(msg, cid)
  1264.         elseif shop_eventtype[cid] == SHOPMODULE_BUY_ITEM then
  1265.             local msg = module.npcHandler:getMessage(MESSAGE_BUY)
  1266.             msg = module.npcHandler:parseMessage(msg, parseInfo)
  1267.             module.npcHandler:say(msg, cid)
  1268.         elseif shop_eventtype[cid] == SHOPMODULE_BUY_ITEM_CONTAINER then
  1269.             local msg = module.npcHandler:getMessage(MESSAGE_BUY)
  1270.             msg = module.npcHandler:parseMessage(msg, parseInfo)
  1271.             module.npcHandler:say(msg, cid)
  1272.         end
  1273.         return true
  1274.     end
  1275. end
Add Comment
Please, Sign In to add comment