Lyky
Well-Known Member
- Joined
- May 27, 2014
- Messages
- 291
- Solutions
- 8
- Reaction score
- 89
Hi,
I have the simplest bug, but with this npc system i'm not sure how to fix it (looks like parsing issue); The code look ok - but i may have missed something.
NPC Script
If needed added npc.lua, and npcsystem.lua
NPC.lua
npcsystem.lua
keywordhandler.lua (note line 46)
I have the simplest bug, but with this npc system i'm not sure how to fix it (looks like parsing issue); The code look ok - but i may have missed something.
NPC Script
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end
function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end
function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end
function onThink() npcHandler:onThink() end
keywordHandler:addKeyword({'job'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "I am the humble supplier for distance fighting weapons of the Ironhouse, owned by Abran Ironeye."})
keywordHandler:addKeyword({'fletcher'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "I am the humble supplier for distance fighting weapons of the Ironhouse, owned by Abran Ironeye."})
keywordHandler:addKeyword({'name'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "People call me Xed, but my full name is Xedem."})
keywordHandler:addKeyword({'time'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "I don't know, maybe what you really need is a watch."})
keywordHandler:addKeyword({'hurt'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Go to a priest. I am sure they will fix you up."})
keywordHandler:addKeyword({'Abran Ironeye'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "He is the owner of this market, although - just between you and me - I'm not so sure he's honest."})
keywordHandler:addKeyword({'honest'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Well, I overheard the boss discussing some shady deals with a man in a black cloak."})
keywordHandler:addKeyword({'shady deals'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Something about a sword only great warlords can use and a rare distance fighting item."})
keywordHandler:addKeyword({'rare distance'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Yes, but I believe this is nothing but lies seeing that there are only a few distance fighting weapons."})
keywordHandler:addKeyword({'amazon'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "They are a band or tribe of strange women that have nothing in common with civilized men like me."})
keywordHandler:addKeyword({'general'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "You must be talking of the great general Benjamin. He saved the kingdom from ferumbras you know."})
keywordHandler:addKeyword({'army'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "We supply the archers of the army with distance fighting weapons."})
keywordHandler:addKeyword({'ferumbras'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "I heard rumours somewhere that his father was called Hugo."})
keywordHandler:addKeyword({'xed'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Yeah, nice name, eh?"})
keywordHandler:addKeyword({'excalibug'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "I think that was the sword they were talking about. Said something about a man in Edron that could get it for him."})
keywordHandler:addKeyword({'new'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Some people say Ferumbras isn't really dead. Crazy kids!"})
keywordHandler:addKeyword({'help'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "I sell items of the distance type."})
keywordHandler:addKeyword({'monster'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Yeah, these awful beasts. They live in the swamps near the city and in dark dungeons."})
keywordHandler:addKeyword({'dungeon'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Oh, they are all over. You never see more of them than in Kaz, though."})
keywordHandler:addKeyword({'kaz'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = "Oh, that's short for Kazordoon."})
local shopModule = ShopModule:new()
npcHandler:addModule(shopModule)
shopModule:addBuyableItem({'arrow','arrows'}, 2544, 3, 'arrows')
shopModule:addBuyableItem({'power bolt','power bolts'}, 2547, 100, 'power bolts')
shopModule:addBuyableItem({'bow'}, 2456, 400, 'bow')
shopModule:addSellableItem({'bow'}, 2456, 100, 'bow')
shopModule:addBuyableItem({'bolt', 'bolts'}, 2543, 4, 'bolts')
shopModule:addBuyableItem({'crossbow', 'xbow'}, 2455, 500, 'crossbow')
shopModule:addSellableItem({'crossbow', 'xbow'}, 2455, 500, 'crossbow')
shopModule:addBuyableItem({'spear', 'spears'}, 2389, 9, 'spears')
npcHandler:addModule(FocusModule:new())
If needed added npc.lua, and npcsystem.lua
NPC.lua
Lua:
-- Including the Advanced NPC System
dofile('data/npc/lib/configuration.lua')
dofile('data/npc/lib/npcsystem/npcsystem.lua')
dofile('data/npc/lib/npcsystem/customModules.lua')
function msgcontains(message, keyword)
local message, keyword = message:lower(), keyword:lower()
if message == keyword then
return true
end
return message:find(keyword) and not message:find('(%w+)' .. keyword)
end
function doNpcSellItem(cid, itemId, amount, subType, ignoreCap, inBackpacks, backpack)
local amount = amount or 1
local subType = subType or 0
local item = 0
local player = Player(cid)
if ItemType(itemId):isStackable() then
local stuff
if inBackpacks then
stuff = Game.createItem(backpack, 1)
item = stuff:addItem(itemId, math.min(100, amount))
else
stuff = Game.createItem(itemId, math.min(100, amount))
end
return player:addItemEx(stuff, ignoreCap) ~= RETURNVALUE_NOERROR and 0 or amount, 0
end
local a = 0
if inBackpacks then
local container, itemType, b = Game.createItem(backpack, 1), ItemType(backpack), 1
for i = 1, amount do
local item = container:addItem(itemId, subType)
if isInArray({(itemType:getCapacity() * b), amount}, i) then
if player:addItemEx(container, ignoreCap) ~= RETURNVALUE_NOERROR then
b = b - 1
break
end
a = i
if amount > i then
container = Game.createItem(backpack, 1)
b = b + 1
end
end
end
return a, b
end
for i = 1, amount do -- normal method for non-stackable items
local item = Game.createItem(itemId, subType)
if player:addItemEx(item, ignoreCap) ~= RETURNVALUE_NOERROR then
break
end
a = i
end
return a, 0
end
local func = function(cid, text, type, e, pcid)
local npc = Npc(cid)
if not npc then
return
end
local player = Player(pcid)
if player then
npc:say(text, type, false, player, npc:getPosition())
e.done = true
end
end
function doCreatureSayWithDelay(cid, text, type, delay, e, pcid)
if Player(pcid) then
e.done = false
e.event = addEvent(func, delay < 1 and 1000 or delay, cid, text, type, e, pcid)
end
end
function doPlayerTakeItem(cid, itemid, count)
local player = Player(cid)
if player:getItemCount(itemid) < count then
return false
end
while count > 0 do
local tempcount = 0
if ItemType(itemid):isStackable() then
tempcount = math.min (100, count)
else
tempcount = 1
end
local ret = player:removeItem(itemid, tempcount)
if ret then
count = count - tempcount
else
return false
end
end
if count ~= 0 then
return false
end
return true
end
function doPlayerSellItem(cid, itemid, count, cost)
local player = Player(cid)
if doPlayerTakeItem(cid, itemid, count) then
if not player:addMoney(cost) then
error('Could not add money to ' .. player:getName() .. '(' .. cost .. 'gp)')
end
return true
end
return false
end
function doPlayerBuyItemContainer(cid, containerid, itemid, count, cost, charges)
local player = Player(cid)
if not player:removeMoney(cost) then
return false
end
for i = 1, count do
local container = Game.createItem(containerid, 1)
for x = 1, ItemType(containerid):getCapacity() do
container:addItem(itemid, charges)
end
if player:addItemEx(container, true) ~= RETURNVALUE_NOERROR then
return false
end
end
return true
end
function getCount(string)
local b, e = string:find("%d+")
return b and e and tonumber(string:sub(b, e)) or -1
end
npcsystem.lua
Lua:
-- Advanced NPC System by Jiddo
shop_amount = {}
shop_cost = {}
shop_rlname = {}
shop_itemid = {}
shop_container = {}
shop_npcuid = {}
shop_eventtype = {}
shop_subtype = {}
shop_destination = {}
shop_premium = {}
npcs_loaded_shop = {}
npcs_loaded_travel = {}
if NpcSystem == nil then
-- Loads the underlying classes of the npcsystem.
dofile('data/npc/lib/npcsystem/keywordhandler.lua')
dofile('data/npc/lib/npcsystem/queue.lua')
dofile('data/npc/lib/npcsystem/npchandler.lua')
dofile('data/npc/lib/npcsystem/modules.lua')
-- Global npc constants:
-- Greeting and unGreeting keywords. For more information look at the top of modules.lua
FOCUS_GREETWORDS = {'hi', 'hello'}
FOCUS_FAREWELLWORDS = {'bye', 'farewell'}
-- The word for requesting trade window. For more information look at the top of modules.lua
SHOP_TRADEREQUEST = {'offer', 'trade'}
-- The word for accepting/declining an offer. CAN ONLY CONTAIN ONE FIELD! For more information look at the top of modules.lua
SHOP_YESWORD = {'yes'}
SHOP_NOWORD = {'no'}
-- Pattern used to get the amount of an item a player wants to buy/sell.
PATTERN_COUNT = '%d+'
-- Talkdelay behavior. For more information, look at the top of npchandler.lua.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
-- Constant strings defining the keywords to replace in the default messages.
-- For more information, look at the top of npchandler.lua...
TAG_PLAYERNAME = '|PLAYERNAME|'
TAG_ITEMCOUNT = '|ITEMCOUNT|'
TAG_TOTALCOST = '|TOTALCOST|'
TAG_ITEMNAME = '|ITEMNAME|'
TAG_QUEUESIZE = '|QUEUESIZE|'
TAG_TIME = '|TIME|'
TAG_BLESSCOST = '|BLESSCOST|'
TAG_TRAVELCOST = '|TRAVELCOST|'
NpcSystem = {}
-- Gets an npcparameter with the specified key. Returns nil if no such parameter is found.
function NpcSystem.getParameter(key)
local ret = getNpcParameter(tostring(key))
if (type(ret) == 'number' and ret == 0) or ret == nil then
return nil
else
return ret
end
end
-- Parses all known parameters for the npc. Also parses parseable modules.
function NpcSystem.parseParameters(npcHandler)
local ret = NpcSystem.getParameter('idletime')
if ret ~= nil then
npcHandler.idleTime = tonumber(ret)
end
local ret = NpcSystem.getParameter('talkradius')
if ret ~= nil then
npcHandler.talkRadius = tonumber(ret)
end
local ret = NpcSystem.getParameter('message_greet')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_GREET, ret)
end
local ret = NpcSystem.getParameter('message_farewell')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_FAREWELL, ret)
end
local ret = NpcSystem.getParameter('message_decline')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_DECLINE, ret)
end
local ret = NpcSystem.getParameter('message_needmorespace')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_NEEDMORESPACE, ret)
end
local ret = NpcSystem.getParameter('message_needspace')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_NEEDSPACE, ret)
end
local ret = NpcSystem.getParameter('message_sendtrade')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_SENDTRADE, ret)
end
local ret = NpcSystem.getParameter('message_noshop')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_NOSHOP, ret)
end
local ret = NpcSystem.getParameter('message_oncloseshop')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_ONCLOSESHOP, ret)
end
local ret = NpcSystem.getParameter('message_onbuy')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_ONBUY, ret)
end
local ret = NpcSystem.getParameter('message_onsell')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_ONSELL, ret)
end
local ret = NpcSystem.getParameter('message_missingmoney')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_MISSINGMONEY, ret)
end
local ret = NpcSystem.getParameter('message_needmoney')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_NEEDMONEY, ret)
end
local ret = NpcSystem.getParameter('message_missingitem')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_MISSINGITEM, ret)
end
local ret = NpcSystem.getParameter('message_needitem')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_NEEDITEM, ret)
end
local ret = NpcSystem.getParameter('message_idletimeout')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_IDLETIMEOUT, ret)
end
local ret = NpcSystem.getParameter('message_walkaway')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_WALKAWAY, ret)
end
local ret = NpcSystem.getParameter('message_alreadyfocused')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_ALREADYFOCUSED, ret)
end
local ret = NpcSystem.getParameter('message_placedinqueue')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_PLACEDINQUEUE, ret)
end
local ret = NpcSystem.getParameter('message_buy')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_BUY, ret)
end
local ret = NpcSystem.getParameter('message_sell')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_SELL, ret)
end
local ret = NpcSystem.getParameter('message_bought')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_BOUGHT, ret)
end
local ret = NpcSystem.getParameter('message_sold')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_SOLD, ret)
end
local ret = NpcSystem.getParameter('message_walkaway_male')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_WALKAWAY_MALE, ret)
end
local ret = NpcSystem.getParameter('message_walkaway_female')
if ret ~= nil then
npcHandler:setMessage(MESSAGE_WALKAWAY_FEMALE, ret)
end
-- Parse modules.
for parameter, module in pairs(Modules.parseableModules) do
local ret = NpcSystem.getParameter(parameter)
if ret ~= nil then
local number = tonumber(ret)
if number ~= 0 and module.parseParameters ~= nil then
local instance = module:new()
npcHandler:addModule(instance)
instance:parseParameters()
end
end
end
end
end
keywordhandler.lua (note line 46)
Lua:
-- Advanced NPC System by Jiddo
if KeywordHandler == nil then
KeywordNode = {
keywords = nil,
callback = nil,
parameters = nil,
children = nil,
parent = nil,
condition = nil,
action = nil
}
-- Created a new keywordnode with the given keywords, callback function and parameters and without any childNodes.
function KeywordNode:new(keys, func, param, condition, action)
local obj = {}
obj.keywords = keys
obj.callback = func
obj.parameters = param
obj.children = {}
obj.condition = condition
obj.action = action
setmetatable(obj, self)
self.__index = self
return obj
end
-- Calls the underlying callback function if it is not nil.
function KeywordNode:processMessage(cid, message)
return (self.callback == nil or self.callback(cid, message, self.keywords, self.parameters, self))
end
function KeywordNode:processAction(cid)
if not self.action then
return
end
local player = Player(cid)
if not player then
return
end
self.action(player, self.parameters.npcHandler)
end
-- Returns true if message contains all patterns/strings found in keywords.
function KeywordNode:checkMessage(cid, message)
if self.keywords.callback ~= nil then
local ret, data = self.keywords.callback(self.keywords, message)
if not ret then
return false
end
if self.condition and not self.condition(Player(cid), data) then
return false
end
return true
end
local data = {}
local last = 0
for i = 1, #self.keywords do
local keyword = self.keywords[i]
if type(keyword) == 'string' then
local a, b = string.find(message, keyword)
if not a or not b or a < last then
return false
end
if keyword:sub(1, 1) == '%' then
data[#data + 1] = tonumber(message:sub(a, b)) or nil
end
last = a
end
end
if self.condition and not self.condition(Player(cid), data) then
return false
end
return true
end
-- Returns the parent of this node or nil if no such node exists.
function KeywordNode:getParent()
return self.parent
end
-- Returns an array of the callback function parameters assosiated with this node.
function KeywordNode:getParameters()
return self.parameters
end
-- Returns an array of the triggering keywords assosiated with this node.
function KeywordNode:getKeywords()
return self.keywords
end
-- Adds a childNode to this node. Creates the childNode based on the parameters (k = keywords, c = callback, p = parameters)
function KeywordNode:addChildKeyword(keywords, callback, parameters, condition, action)
local new = KeywordNode:new(keywords, callback, parameters, condition, action)
return self:addChildKeywordNode(new)
end
function KeywordNode:addAliasKeyword(keywords)
if #self.children == 0 then
print('KeywordNode:addAliasKeyword no previous node found')
return false
end
local prevNode = self.children[#self.children]
local new = KeywordNode:new(keywords, prevNode.callback, prevNode.parameters, prevNode.condition, prevNode.action)
for i = 1, #prevNode.children do
new:addChildKeywordNode(prevNode.children[i])
end
return self:addChildKeywordNode(new)
end
-- Adds a pre-created childNode to this node. Should be used for example if several nodes should have a common child.
function KeywordNode:addChildKeywordNode(childNode)
self.children[#self.children + 1] = childNode
childNode.parent = self
return childNode
end
KeywordHandler = {
root = nil,
lastNode = nil
}
-- Creates a new keywordhandler with an empty rootnode.
function KeywordHandler:new()
local obj = {}
obj.root = KeywordNode:new(nil, nil, nil)
obj.lastNode = {}
setmetatable(obj, self)
self.__index = self
return obj
end
-- Resets the lastNode field, and this resetting the current position in the node hierarchy to root.
function KeywordHandler:reset(cid)
if self.lastNode[cid] then
self.lastNode[cid] = nil
end
end
-- Makes sure the correct childNode of lastNode gets a chance to process the message.
function KeywordHandler:processMessage(cid, message)
local node = self:getLastNode(cid)
if node == nil then
error('No root node found.')
return false
end
local ret = self:processNodeMessage(node, cid, message)
if ret then
return true
end
if node:getParent() then
node = node:getParent() -- Search through the parent.
local ret = self:processNodeMessage(node, cid, message)
if ret then
return true
end
end
if node ~= self:getRoot() then
node = self:getRoot() -- Search through the root.
local ret = self:processNodeMessage(node, cid, message)
if ret then
return true
end
end
return false
end
-- Tries to process the given message using the node parameter's children and calls the node's callback function if found.
-- Returns the childNode which processed the message or nil if no such node was found.
function KeywordHandler:processNodeMessage(node, cid, message)
local messageLower = message:lower()
for i = 1, #node.children do
local childNode = node.children[i]
if childNode:checkMessage(cid, messageLower) then
local oldLast = self.lastNode[cid]
self.lastNode[cid] = childNode
childNode.parent = node -- Make sure node is the parent of childNode (as one node can be parent to several nodes).
if childNode:processMessage(cid, message) then
childNode:processAction(cid)
return true
end
self.lastNode[cid] = oldLast
end
end
return false
end
-- Returns the root keywordnode
function KeywordHandler:getRoot()
return self.root
end
-- Returns the last processed keywordnode or root if no last node is found.
function KeywordHandler:getLastNode(cid)
return self.lastNode[cid] or self:getRoot()
end
-- Adds a new keyword to the root keywordnode. Returns the new node.
function KeywordHandler:addKeyword(keys, callback, parameters, condition, action)
return self:getRoot():addChildKeyword(keys, callback, parameters, condition, action)
end
-- Adds an alias keyword for the previous node.
function KeywordHandler:addAliasKeyword(keys)
return self:getRoot():addAliasKeyword(keys)
end
-- Moves the current position in the keyword hierarchy steps upwards. Steps defalut value = 1.
function KeywordHandler:moveUp(cid, steps)
if steps == nil or type(steps) ~= "number" then
steps = 1
end
for i = 1, steps do
if self.lastNode[cid] == nil then
return nil
end
self.lastNode[cid] = self.lastNode[cid]:getParent() or self:getRoot()
end
return self.lastNode[cid]
end
end
Last edited: