-- Advanced NPC System (Created by Jiddo), -- Modified by Talaturen. if(Modules == nil) then -- default words for greeting and ungreeting the npc. Should be a talbe containing all such words. FOCUS_GREETWORDS = {'hi', 'hello', 'hey'} FOCUS_FAREWELLWORDS = {'bye', 'farewell', 'cya'} -- The word for accepting/declining an offer. CAN ONLY CONTAIN ONE FIELD! Should be a teble with a single string value. SHOP_YESWORD = {'yes'} SHOP_NOWORD = {'no'} -- Pattern used to get the amount of an item a player wants to buy/sell. PATTERN_COUNT = '%d+' -- Constants used to separate buying from selling. SHOPMODULE_SELL_ITEM = 1 SHOPMODULE_BUY_ITEM = 2 SHOPMODULE_BUY_CONTAINER = 3 Modules = { parseableModules = {} } StdModule = {} -- These callback function must be called with parameters.npcHandler = npcHandler in the parameters table or they will not work correctly. -- Notice: The members of StdModule have not yet been tested. If you find any bugs, please report them to me. -- Usage: -- keywordHandler:addKeyword({'offer'}, StdModule.say, {npcHandler = npcHandler, text = 'I sell many powerful melee weapons.'}) function StdModule.say(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then error('StdModule.say called without any npcHandler instance.') end if(cid ~= npcHandler.focus and (parameters.onlyFocus == nil or parameters.onlyFocus == true)) then return false end local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid), } msgout = npcHandler:parseMessage(parameters.text or parameters.message, parseInfo) selfSay(msgout) if(parameters.reset == true) then npcHandler:resetNpc() elseif(parameters.moveup ~= nil and type(parameters.moveup) == 'number') then npcHandler.keywordHandler:moveUp(parameters.moveup) end return true end --dodawanie kontenera function StdModule.buyContainer(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then error('StdModule.buyContainer called without any npcHandler instance.') end if(cid ~= npcHandler.focus) then return false end if(doPlayerRemoveMoney(cid, parameters.cost) ~= TRUE) then selfSay('Nie masz dosc pieniedzy!') else local containerId = doPlayerAddItem(cid, parameters.containerId, 1) if isContainer(containerId) == TRUE then while getContainerSize(containerId) ~= 20 do doAddContainerItem(containerId, parameters.runeId, parameters.runeCharges) end end return true end end --Usage: -- 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?'}) -- node1:addChildKeyword({'yes'}, StdModule.promotePlayer, {npcHandler = npcHandler, cost = 20000, level = 20}, text = 'Congratulations! You are now promoted.') -- node1:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, text = 'Allright then. Come back when you are ready.'}, reset = true) function StdModule.promotePlayer(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then error('StdModule.promotePlayer called without any npcHandler instance.') end if(cid ~= npcHandler.focus) then return false end local promotedVoc = getPromotedVocation(getPlayerVocation(cid)) if(promotedVoc == getPlayerVocation(cid) or promotedVoc == 0) then selfSay('Masz juz promocje!') elseif(getPlayerLevel(cid) < parameters.level) then selfSay('Sorry, ale mozesz otrzymac promocje dopiero na ' .. parameters.level .. ' levelu.') elseif(doPlayerRemoveMoney(cid, parameters.cost) ~= TRUE) then selfSay('Nie masz dosc pieniedzy!') else doPlayerSetVocation(cid, promotedVoc) selfSay(parameters.text) end npcHandler:resetNpc() return true end function StdModule.learnSpell(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then error('StdModule.buySpell called without any npcHandler instance.') end if(cid ~= npcHandler.focus) then return false end if getPlayerLearnedInstantSpell(cid, parameters.spellName) == TRUE then selfSay('Znasz juz ten czar.') elseif getPlayerLevel(cid) < parameters.level then selfSay('Musisz miec level ' .. parameters.level .. ' lub wyzszy, zeby moc sie nauczyc czaru ' .. parameters.spellName .. '.') elseif parameters.premium == TRUE and getPlayerPremiumDays(cid) == 0 then selfSay('Musisz miec platne konto, zeby moc sie nauczyc tego czaru ' .. parameters.spellName .. '.') elseif getPlayerVocation(cid) ~= parameters.vocation and getPlayerVocation(cid) ~= parameters.vocation + 4 and vocation ~= 9 then selfSay('Ten czar nie jest dla twojej profesji.') elseif doPlayerRemoveMoney(cid, parameters.price) == FALSE then selfSay('Nie masz dosc pieniedzy, ten czar kosztuje ' .. parameters.price .. ' GP.') else selfSay('Nauczyles sie ' .. parameters.spellName .. '.') playerLearnInstantSpell(cid, parameters.spellName) end npcHandler:resetNpc() return true end function StdModule.travel(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then error('StdModule.travel called without any npcHandler instance.') end if(cid ~= npcHandler.focus) then return false end if (getPlayerPzLock(cid) == 0) then if(isPlayerPremiumCallback == nil or isPlayerPremiumCallback(cid) == true or parameters.premium == false) then if(parameters.level ~= nil and getPlayerLevel(cid) < parameters.level) then selfSay('Musisz miec level ' .. parameters.level .. ' zanim tam sie znajdziesz.') elseif(doPlayerRemoveMoney(cid, parameters.cost) ~= TRUE) then selfSay('Nie masz dosc pieniedzy!') else doTeleportThing(cid, parameters.destination, 0) doSendMagicEffect(parameters.destination, 10) end else selfSay('Tylko osoby z platnym kontem moga plywac ze mna.') end else selfSay('Nie plywam z osobami tuz po walce. Jeszcze bys mi uszkodzil statek!') end npcHandler:resetNpc() return true end FocusModule = { npcHandler = nil } -- Creates a new instance of FocusModule without an associated NpcHandler. function FocusModule:new() local obj = {} setmetatable(obj, self) self.__index = self return obj end -- Inits the module and associates handler to it. function FocusModule:init(handler) self.npcHandler = handler for i, word in pairs(FOCUS_GREETWORDS) do local obj = {} table.insert(obj, word) obj.callback = FOCUS_GREETWORDS.callback or FocusModule.messageMatcher handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self}) end for i, word in pairs(FOCUS_FAREWELLWORDS) do local obj = {} table.insert(obj, word) obj.callback = FOCUS_FAREWELLWORDS.callback or FocusModule.messageMatcher handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self}) end return true end -- Greeting callback function. function FocusModule.onGreet(cid, message, keywords, parameters) parameters.module.npcHandler:onGreet(cid) return true end -- UnGreeting callback function. function FocusModule.onFarewell(cid, message, keywords, parameters) if(parameters.module.npcHandler.focus == cid) then parameters.module.npcHandler:onFarewell() return true else return false end end -- Custom message matching callback function for greeting messages. function FocusModule.messageMatcher(keywords, message) for i, word in pairs(keywords) do if(type(word) == 'string') then if string.find(message, word) and not string.find(message, '[%w+]' .. word) and not string.find(message, word .. '[%w+]') then return true end end end return false end KeywordModule = { npcHandler = nil } -- Add it to the parseable module list. Modules.parseableModules['module_keywords'] = KeywordModule function KeywordModule:new() local obj = {} setmetatable(obj, self) self.__index = self return obj end function KeywordModule:init(handler) self.npcHandler = handler return true end -- Parses all known parameters. function KeywordModule:parseParameters() local ret = NpcSystem.getParameter('keywords') if(ret ~= nil) then self:parseKeywords(ret) end end function KeywordModule:parseKeywords(data) local n = 1 for keys in string.gmatch(data, '[^;]+') do local i = 1 local keywords = {} for temp in string.gmatch(keys, '[^,]+') do table.insert(keywords, temp) i = i+1 end if(i ~= 1) then local reply = NpcSystem.getParameter('keyword_reply' .. n) if(reply ~= nil) then self:addKeyword(keywords, reply) else print('[Warning] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...') end else print('[Warning] NpcSystem:', 'No keywords found for keyword set #' .. n .. '. Skipping...') end n = n+1 end end function KeywordModule:addKeyword(keywords, reply) self.npcHandler.keywordHandler:addKeyword(keywords, StdModule.say, {npcHandler = self.npcHandler, onlyFocus = true, text = reply, reset = true}) end TravelModule = { npcHandler = nil, destinations = nil, yesNode = nil, noNode = nil, } -- Add it to the parseable module list. Modules.parseableModules['module_travel'] = TravelModule function TravelModule:new() local obj = {} setmetatable(obj, self) self.__index = self return obj end function TravelModule:init(handler) self.npcHandler = handler self.yesNode = KeywordNode:new(SHOP_YESWORD, TravelModule.onConfirm, {module = self}) self.noNode = KeywordNode:new(SHOP_NOWORD, TravelModule.onDecline, {module = self}) self.destinations = {} return true end -- Parses all known parameters. function TravelModule:parseParameters() local ret = NpcSystem.getParameter('travel_destinations') if(ret ~= nil) then self:parseDestinations(ret) self.npcHandler.keywordHandler:addKeyword({'destination'}, TravelModule.listDestinations, {module = self}) self.npcHandler.keywordHandler:addKeyword({'where'}, TravelModule.listDestinations, {module = self}) self.npcHandler.keywordHandler:addKeyword({'travel'}, TravelModule.listDestinations, {module = self}) end end function TravelModule:parseDestinations(data) for destination in string.gmatch(data, '[^;]+') do local i = 1 local name = nil local x = nil local y = nil local z = nil local cost = nil local premium = false for temp in string.gmatch(destination, '[^,]+') do if(i == 1) then name = temp elseif(i == 2) then x = tonumber(temp) elseif(i == 3) then y = tonumber(temp) elseif(i == 4) then z = tonumber(temp) elseif(i == 5) then cost = tonumber(temp) elseif(i == 6) then premium = temp == 'true' else print('[Warning] NpcSystem:', 'Unknown parameter found in travel destination parameter.', temp, destination) end i = i+1 end if(name ~= nil and x ~= nil and y ~= nil and z ~= nil and cost ~= nil) then self:addDestination(name, {x=x, y=y, z=z}, cost, premium) else print('[Warning] NpcSystem:', 'Parameter(s) missing for travel destination:', name, x, y, z, cost, premium) end end end function TravelModule:addDestination(name, position, price, premium) table.insert(self.destinations, name) local parameters = { cost = price, destination = position, premium = premium, module = self } local keywords = {} table.insert(keywords, name) local keywords2 = {} table.insert(keywords2, 'bring me to ' .. name) local node = self.npcHandler.keywordHandler:addKeyword(keywords, TravelModule.travel, parameters) self.npcHandler.keywordHandler:addKeyword(keywords2, TravelModule.bringMeTo, parameters) node:addChildKeywordNode(self.yesNode) node:addChildKeywordNode(self.noNode) end function TravelModule.travel(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local npcHandler = module.npcHandler local cost = parameters.cost local destination = parameters.destination local premium = parameters.premium module.npcHandler:say('Chcesz poplynac do ' .. keywords[1] .. ' za ' .. cost .. ' GP?') return true end function TravelModule.onConfirm(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local npcHandler = module.npcHandler local parentParameters = node:getParent():getParameters() local cost = parentParameters.cost local destination = parentParameters.destination local premium = parentParameters.premium if(isPlayerPremiumCallback == nil or isPlayerPremiumCallback(cid) == true or parameters.premium ~= true) then if(doPlayerRemoveMoney(cid, cost) ~= TRUE) then npcHandler:say('Nie masz dosc pieniedzy!') else npcHandler:say('Dziekuje za tranzakcje.', false) npcHandler:releaseFocus() doTeleportThing(cid, destination, 0) doSendMagicEffect(destination, 10) end else npcHandler:say('Tylko gracze z platnym kontem moga tam plynac.') end npcHandler:resetNpc() return true end -- onDecliune keyword callback function. Generally called when the player sais 'no' after wanting to buy an item. function TravelModule.onDecline(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local parentParameters = node:getParent():getParameters() local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid), } local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), parseInfo) module.npcHandler:say(msg) module.npcHandler:resetNpc() return true end function TravelModule.bringMeTo(cid, message, keywords, parameters, node) local module = parameters.module if(cid == module.npcHandler.focus) then return false end local cost = parameters.cost local destination = parameters.destination local premium = parameters.premium if(isPlayerPremiumCallback == nil or isPlayerPremiumCallback(cid) == true or parameters.premium ~= true) then if(doPlayerRemoveMoney(cid, cost) == TRUE) then doTeleportThing(cid, destination, 0) doSendMagicEffect(destination, 10) end end return true end function TravelModule.listDestinations(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local msg = 'Moge ciebie zawiezc do ' --local i = 1 local maxn = table.maxn(module.destinations) for i,destination in pairs(module.destinations) do msg = msg .. destination if(i == maxn-1) then msg = msg .. ' i ' elseif(i == maxn) then msg = msg .. '.' else msg = msg .. ', ' end i = i+1 end module.npcHandler:say(msg) module.npcHandler:resetNpc() return true end ShopModule = { yesNode = nil, noNode = nil, npcHandler = nil, noText = '', maxCount = 500, amount = 0 } -- Add it to the parseable module list. Modules.parseableModules['module_shop'] = ShopModule -- Creates a new instance of ShopModule function ShopModule:new() local obj = {} setmetatable(obj, self) self.__index = self return obj end -- Parses all known parameters. function ShopModule:parseParameters() local ret = NpcSystem.getParameter('shop_sellable') if(ret ~= nil) then self:parseSellable(ret) end local ret = NpcSystem.getParameter('shop_buyable') if(ret ~= nil) then self:parseBuyable(ret) end end -- Parse a string contaning a set of buyable items. function ShopModule:parseBuyable(data) for item in string.gmatch(data, '[^;]+') do local i = 1 local name = nil local itemid = nil local cost = nil local charges = nil for temp in string.gmatch(item, '[^,]+') do if(i == 1) then name = temp elseif(i == 2) then itemid = tonumber(temp) elseif(i == 3) then cost = tonumber(temp) elseif(i == 4) then charges = tonumber(temp) else print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item) end i = i+1 end if(name ~= nil and itemid ~= nil and cost ~= nil) then if((isItemRune(itemid) == TRUE or isItemFluidContainer(itemid) == TRUE) and charges == nil) then print('[Warning] NpcSystem:', 'Charges missing for parameter item:' , item) else local names = {} table.insert(names, name) self:addBuyableItem(names, itemid, cost, charges) end else print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost) end end end -- Parse a string contaning a set of sellable items. function ShopModule:parseSellable(data) for item in string.gmatch(data, '[^;]+') do local i = 1 local name = nil local itemid = nil local cost = nil for temp in string.gmatch(item, '[^,]+') do if(i == 1) then name = temp elseif(i == 2) then itemid = tonumber(temp) elseif(i == 3) then cost = tonumber(temp) else print('[Warning] NpcSystem:', 'Unknown parameter found in sellable items parameter.', temp, item) end i = i+1 end if(name ~= nil and itemid ~= nil and cost ~= nil) then local names = {} table.insert(names, name) self:addSellableItem(names, itemid, cost) else print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost) end end end -- Initializes the module and associates handler to it. function ShopModule:init(handler) self.npcHandler = handler self.yesNode = KeywordNode:new(SHOP_YESWORD, ShopModule.onConfirm, {module = self}) self.noNode = KeywordNode:new(SHOP_NOWORD, ShopModule.onDecline, {module = self}) self.noText = handler:getMessage(MESSAGE_DECLINE) return true end -- Resets the module-specific variables. function ShopModule:reset() self.amount = 0 end -- Function used to match a number value from a string. function ShopModule:getCount(message) local ret = 1 local b, e = string.find(message, PATTERN_COUNT) if b ~= nil and e ~= nil then ret = tonumber(string.sub(message, b, e)) end if(ret <= 0) then ret = 1 elseif(ret > self.maxCount) then ret = self.maxCount end return ret end function ShopModule:buyContainer(names, container, itemid, cost, charges, realname) for i, name in pairs(names) do local parameters = { container = container, itemid = itemid, cost = cost, charges = charges, eventType = SHOPMODULE_BUY_CONTAINER, module = self } if(realname ~= nil) then parameters.realname = realname end if(isItemRune(itemid) == TRUE or isItemFluidContainer(itemid) == TRUE) then parameters.charges = charges end keywords = {} table.insert(keywords, name) local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters) node:addChildKeywordNode(self.yesNode) node:addChildKeywordNode(self.noNode) end end -- Adds a new buyable item. -- names = A table containing one or more strings of alternative names to this item. -- itemid = the itemid of the buyable item -- cost = the price of one single item with item id itemid ^^ -- charges - The charges of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer and no realname is needed. Default value is nil. -- 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 (keywords[1]/names will be used) function ShopModule:addBuyableItem(names, itemid, cost, charges, realname) for i, name in pairs(names) do local parameters = { itemid = itemid, cost = cost, eventType = SHOPMODULE_BUY_ITEM, module = self } if(realname ~= nil) then parameters.realname = realname end if(isItemRune(itemid) == TRUE or isItemFluidContainer(itemid) == TRUE) then parameters.charges = charges end keywords = {} table.insert(keywords, name) local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters) node:addChildKeywordNode(self.yesNode) node:addChildKeywordNode(self.noNode) end end -- Adds a new sellable item. -- names = A table containing one or more strings of alternative names to this item. -- itemid = the itemid of the buyable item -- cost = the price of one single item with item id itemid ^^ -- 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 (keywords[2]/names will be used) function ShopModule:addSellableItem(names, itemid, cost, realname) for i, name in pairs(names) do local parameters = { itemid = itemid, cost = cost, eventType = SHOPMODULE_SELL_ITEM, module = self } if(realname ~= nil) then parameters.realname = realname end keywords = {} table.insert(keywords, 'sell') table.insert(keywords, name) local node = self.npcHandler.keywordHandler:addKeyword(keywords, ShopModule.tradeItem, parameters) node:addChildKeywordNode(self.yesNode) node:addChildKeywordNode(self.noNode) end end -- onModuleReset callback function. Calls ShopModule:reset() function ShopModule:callbackOnModuleReset() self:reset() return true end -- tradeItem callback function. Makes the npc say the message defined by MESSAGE_BUY or MESSAGE_SELL function ShopModule.tradeItem(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local count = module:getCount(message) module.amount = count local tmpName = nil if(parameters.eventType == SHOPMODULE_SELL_ITEM) then tmpName = node:getKeywords()[2] elseif(parameters.eventType == SHOPMODULE_BUY_CONTAINER) then tmpName = node:getKeywords()[3] elseif(parameters.eventType == SHOPMODULE_BUY_ITEM) then tmpName = node:getKeywords()[1] end local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid), [TAG_ITEMCOUNT] = module.amount, [TAG_TOTALCOST] = parameters.cost*module.amount, [TAG_ITEMNAME] = parameters.realname or tmpName } if(parameters.eventType == SHOPMODULE_SELL_ITEM) then local msg = module.npcHandler:getMessage(MESSAGE_SELL) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) elseif(parameters.eventType == SHOPMODULE_BUY_CONTAINER) then local msg = module.npcHandler:getMessage(MESSAGE_BUY) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) elseif(parameters.eventType == SHOPMODULE_BUY_ITEM) then local msg = module.npcHandler:getMessage(MESSAGE_BUY) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) end return true end -- onConfirm keyword callback function. Sells/buys the actual item. function ShopModule.onConfirm(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local parentParameters = node:getParent():getParameters() local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid), [TAG_ITEMCOUNT] = module.amount, [TAG_TOTALCOST] = parentParameters.cost*module.amount, [TAG_ITEMNAME] = parentParameters.realname or node:getParent():getKeywords()[1] } if(parentParameters.eventType == SHOPMODULE_SELL_ITEM) then local ret = doPlayerSellItem(cid, parentParameters.itemid, module.amount, parentParameters.cost*module.amount) if(ret == LUA_NO_ERROR) then local msg = module.npcHandler:getMessage(MESSAGE_ONSELL) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) else local msg = module.npcHandler:getMessage(MESSAGE_NOTHAVEITEM) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) end elseif(parentParameters.eventType == SHOPMODULE_BUY_CONTAINER) then local ret = doPlayerbuyContainer(cid, parentParameters.container, parentParameters.itemid, parentParameters.charges, parentParameters.cost*module.amount, module.amount) if(ret == LUA_NO_ERROR) then local msg = module.npcHandler:getMessage(MESSAGE_ONBUY) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) else local msg = module.npcHandler:getMessage(MESSAGE_NEEDMOREMONEY) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) end elseif(parentParameters.eventType == SHOPMODULE_BUY_ITEM) then local ret = doPlayerBuyItem(cid, parentParameters.itemid, module.amount, parentParameters.cost*module.amount, parentParameters.charges) if(ret == LUA_NO_ERROR) then if parentParameters.itemid == ITEM_PARCEL then doPlayerBuyItem(cid, ITEM_LABEL, module.amount, 0, parentParameters.charges) end local msg = module.npcHandler:getMessage(MESSAGE_ONBUY) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) else local msg = module.npcHandler:getMessage(MESSAGE_NEEDMOREMONEY) msg = module.npcHandler:parseMessage(msg, parseInfo) selfSay(msg) end end module.npcHandler:resetNpc() return true end -- onDecliune keyword callback function. Generally called when the player sais 'no' after wanting to buy an item. function ShopModule.onDecline(cid, message, keywords, parameters, node) local module = parameters.module if(cid ~= module.npcHandler.focus) then return false end local parentParameters = node:getParent():getParameters() local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid), [TAG_ITEMCOUNT] = module.amount, [TAG_TOTALCOST] = parentParameters.cost*module.amount, [TAG_ITEMNAME] = parentParameters.realname or node:getParent():getKeywords()[1] } local msg = module.npcHandler:parseMessage(module.noText, parseInfo) selfSay(msg) module.npcHandler:resetNpc() return true end end