Code:
[09/09/2011 12:56:53] Lua Script Error: [Npc interface]
[09/09/2011 12:56:53] data/npc/scripts/Arkuliuss.lua:onThink
[09/09/2011 12:56:53] data/npc/lib/npcsystem/npchandler.lua:596: table index is nil
[09/09/2011 12:56:53] stack traceback:
[09/09/2011 12:56:53] [C]: in function '__newindex'
[09/09/2011 12:56:53] data/npc/lib/npcsystem/npchandler.lua:596: in function 'say'
[09/09/2011 12:56:53] data/npc/scripts/Arkuliuss.lua:30: in function 'callback'
[09/09/2011 12:56:53] data/npc/lib/npcsystem/npchandler.lua:462: in function 'onThink'
[09/09/2011 12:56:53] data/npc/scripts/Arkuliuss.lua:25: in function <data/npc/scripts/Arkuliuss.lua:25>
Arkuliuss.lua
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local Topic = {}
local storageMain = 65100
local storageKill = 65101
local greetMsg = {
"...if the expected constant is higher than... Hmmm, who are you?? What do you want?",
"...then I could transform a spell to bend... How can anyone expect me to work under these conditions?? What do you want?",
"...if my calculations are correct, I will be able to revive... Arrgghh!! What do you want?"
}
local thinkMsg = {
"Oh my! Alverus!! Did you really...?!?! I have to recalculate it to make sure that I made no mistake.",
"...the minimum square deviation could cause a dislocation, in a matter of fact...",
"...it could be possible to bring the sphere to a destination where...",
"...the arithmetical paradox has the same value in a metaphysical way, then...",
"<mumbles>",
"Yes, that's it! The elementary particle are corresponding to the... the ... UNBELIEVEABLE!!! "
}
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
function thinkCallback(cid)
local rand = math.random(250)
if thinkMsg[rand] then
npcHandler:say(thinkMsg[rand])
end
return true
end
function greetCallback(cid)
npcHandler:setMessage(MESSAGE_GREET, greetMsg[math.random(#greetMsg)])
Topic[cid] = 0
return true
end
function creatureSayCallback(cid, type, msg)
if(not npcHandler:isFocused(cid)) then
return false
elseif msgcontains(msg, "job") then
npcHandler:say("How dare you asking me this?!? I'm Arkulius - Master of Elements, the HEADMASTER of this academy!!", cid)
Topic[cid] = 0
elseif msgcontains(msg, "name") then
npcHandler:say("I'm Arkulius - Master of Elements, the headmaster of this academy.", cid)
Topic[cid] = 0
elseif msgcontains(msg, "help") then
npcHandler:say("I have better things to do than helping you. See that ice statue over there? My dear friend Alverus needs to be revived!", cid)
Topic[cid] = 0
elseif msgcontains(msg, "time") then
npcHandler:say("Time is an illusion and completely irrelevant to me.", cid)
Topic[cid] = 0
elseif msgcontains(msg, "weapon") then
npcHandler:say("Weapons are for those people who aren't able to use their heads or better what's INSIDE their heads. No offence <coughs>.", cid) -- < Knight; FIXME !!!
Topic[cid] = 0
elseif msgcontains(msg, "pits") and msgcontains(msg, "of") and msgcontains(msg, "inferno") then
npcHandler:say("Yeye, I believe you almost feel like home among all those brainless creatures!", cid)
Topic[cid] = 0
elseif msgcontains(msg, "alverus") then
npcHandler:say({"It happened while he carried out an experiment concerning the creation of the elemental {shrines}. I still get goose bumps just by thinking of it. ...", "You need to know about the process of creating an elemental shrine to understand it completely, but I don't want to go into detail now. ...", "Anyway, his spell had a different outcome than he had planned. He accidentally created an Ice Overlord, pure living elemental ice, who froze him in a blink of an eye."}, cid)
Topic[cid] = 0
elseif msgcontains(msg, "shrine") then
npcHandler:say({"The creation of the elemental shrines is a really complex matter. They are actually nodes, locations where the matching elemental sphere is very close. ...", "The shrine itself is like a portal between our world and the elemental {sphere} and enables us to use the elemental energy emerging from it."}, cid)
Topic[cid] = 0
elseif msgcontains(msg, "sphere") and getPlayerLevel(cid) >= 80 then
npcHandler:say({"There are four spheres we know of: ice, fire, earth and energy. ....<mumbles> Hmmm, should I ask or not?....The heck with it! Now that you know about the spheres ...", "I found a way to visit them. It's VERY dangerous and there is a decent chance that you won't come back BUT if you succeed you'll write history!!! Ask me about that {mission} if you're interested."}, cid)
Topic[cid] = 0
elseif msgcontains(msg, "mission") or msgcontains(msg, "quest") then
local v = getPlayerStorageValue(cid, storageMain)
if v < 1 then
if getPlayerLevel(cid) >= 80 then
if isSorcerer(cid) then
npcHandler:say({"Okay, listen closely: First of all, you need to gather 20 enchanted rubies in order to go to the fire sphere. Deep under the academy, one floor below the elemental shrines, there is a machine. Put the gems in there and activate it. ...", "Once you got there, find a way to gather elemental fire in any form. You will face fire elementals, that's for sure, but I don't know how the fire is stored. ...", "Anyway, there should be a way to use that elemental fire and strengthen one of the elementals. If my calculations are right, you will create a Fire Overlord who hopefully will consist of some sort of 'concentrated' fire or something similar. ...", "THAT'S what we need!! Are you in on it?"}, cid)
elseif isDruid(cid) then
npcHandler:say({"Okay, listen closely: First of all, you need to gather 20 enchanted emeralds in order to go to the earth sphere. Deep under the academy, one floor below the elemental shrines, there is a machine. Put the gems in there and activate it. ...", "Once you got there, find a way to gather elemental earth in any form. You will face earth elementals, that's for sure, but I don't know how the earth is stored. ...", "Anyway, there should be a way to use that elemental earth and strengthen one of the elementals. If my calculations are right, you will create an Earth Overlord who hopefully will consist of some sort of 'concentrated' earth or something similar. ...", "THAT'S what we need!! Are you in on it?"}, cid)
elseif isPaladin(cid) then
npcHandler:say({"Okay, listen closely: First of all, you need to gather 20 enchanted sapphires in order to go to the ice sphere. Deep under the academy, one floor below the elemental shrines, there is a machine. Put the gems in there and activate it. ...", "Once you got there, find a way to gather elemental ice in any form. You will face ice elementals, that's for sure, but I don't know how the ice is stored. ...", "Anyway, there should be a way to use that elemental ice and strengthen one of the elementals. If my calculations are right, you will create an Ice Overlord who hopefully will consist of some sort of 'concentrated' ice or something similar. ...", "THAT'S what we need!! Are you in on it?"}, cid)
elseif isKnight(cid) then
npcHandler:say({"Okay, listen closely: First of all, you need to gather 20 enchanted amethysts in order to go to the energy sphere. Deep under the academy, one floor below the elemental shrines, there is a machine. Put the gems in there and activate it. ...", "Once you got there, find a way to gather elemental energy in any form. You will face energy elementals, that's for sure, but I don't know how the energy is stored. ...", "Anyway, there should be a way to use that energy and strengthen one of the elementals. If my calculations are right, you will create an Energy Overlord who hopefully will consist of some sort of 'concentrated' energy. ...", "THAT'S what we need!! Are you in on it?"}, cid)
end
else
npcHandler:say("I'm sorry this task is highly dangerous and I need experienced people for it.", cid)
Topic[cid] = 0
return
end
Topic[cid] = 1
elseif v == 1 then
if --[[getPlayerStorageValue(cid, storageKill) == 1 and]] getPlayerItemCount(cid, isSorcerer(cid) and 8304 or isDruid(cid) and 8305 or isPaladin(cid) and 8300 or isKnight(cid) and 8306) > 0 then
setPlayerStorageValue(cid, storageMain, 2)
npcHandler:say({"Impressive!! Let me take a look.......Ahh, " .. (isSorcerer(cid) and "an ETERNAL FLAME! Now you need to find a knight, a druid, and a paladin who also completed this first task. ..." or isDruid(cid) and "MOTHER SOIL! Now you need to find a knight, a paladin, and a sorcerer who also completed this first task. ..." or isPaladin(cid) and "a FLAWLESS ICE CRYSTAL! Now you need to find a knight, a druid, and a sorcerer who also completed this first task. ..." or isKnight(cid) and "PURE ENERGY! Now you need to find a druid, a paladin, and a sorcerer who also completed this first task. ..."), "Go down in the cellar again. I prepared a room under the academy where it should be safe. Your task is to charge the machines with the elemental substances and summon the LORD OF THE ELEMENTS. ...", "When you use an obsidian knife on it's corpse you hopefully get some of the precious neutral matter. It's the only way to revive my dear friend Alverus!!"}, cid)
else
npcHandler:say("You need some kind of pure elemental soil from the " .. (isSorcerer(cid) and "Fire" or isDruid(cid) and "Earth" or isPaladin(cid) and "Ice" or isKnight(cid) and "Energy") .. " Overlord. Come back when you've got it.", cid)
end
Topic[cid] = 0
elseif v == 2 then
if --[[getPlayerStorageValue(cid, storageKill) == 2 and]] doPlayerRemoveItem(cid, 8310, 1) == TRUE then
npcHandler:say("AMAZING!! I'm going to start immediately with the research. If it turns out the way I expect it, Alverus will be revived soon!! Here, take this as a reward and try to collect more of this substance. I'll make you a good offer, I promise. ", cid)
doPlayerAddItem(cid, isSorcerer(cid) and 8867 or isDruid(cid) and 8869 or isPaladin(cid) and 8853 or isKnight(cid) and 8883, 1)
setPlayerStorageValue(cid, storageMain, 3)
end
end
elseif Topic[cid] == 1 and msgcontains(msg, "yes") then
setPlayerStorageValue(cid, storageMain, 1)
npcHandler:say("Good, don't waste time! Come back here when you have the elemental object!", cid)
Topic[cid] = 0
elseif msgcontains(msg, "trade") and getPlayerStorageValue(cid, 65100) == 3 then
local items = {{name="neutral matter", id=8310, sell=5000, subType = 1}}
openShopWindow(cid, items,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
customCallbackOnBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks, items)
end,
function(cid, itemid, subType, amount, ignoreCap, inBackpacks)
customCallbackOnSell(cid, itemid, subType, amount, ignoreCap, inBackpacks, items)
end
)
npcHandler:say("I only buy neutral matter. You'll get 5,000 gold for each.", cid)
Topic[cid] = 0
end
return TRUE
end
npcHandler:setMessage(MESSAGE_WALKAWAY, "Good bye and please stay away, okay?")
npcHandler:setMessage(MESSAGE_FAREWELL, "At last! Good things come to those who wait.")
npcHandler:setCallback(CALLBACK_ONTHINK, thinkCallback)
npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
Npchandler.lua
Code:
-- Advanced NPC System (Created by Jiddo),
-- Modified by Cykotitan and Talaturen.
if(NpcHandler == nil) then
-- Constant talkdelay behaviors.
TALKDELAY_NONE = 0 -- No talkdelay. Npc will reply immedeatly.
TALKDELAY_ONTHINK = 1 -- Talkdelay handled through the onThink callback function. (Default)
TALKDELAY_EVENT = 2 -- Not yet implemented
-- Currently applied talkdelay behavior. TALKDELAY_ONTHINK is default.
NPCHANDLER_TALKDELAY = TALKDELAY_ONTHINK
-- Constant indexes for defining default messages.
MESSAGE_GREET = 1 -- When the player greets the npc.
MESSAGE_FAREWELL = 2 -- When the player unGreets the npc.
MESSAGE_BUY = 3 -- When the npc asks the player if he wants to buy something.
MESSAGE_ONBUY = 4 -- When the player successfully buys something via talk.
MESSAGE_BOUGHT = 5 -- When the player bought something through the shop window.
MESSAGE_SELL = 6 -- When the npc asks the player if he wants to sell something.
MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk.
MESSAGE_SOLD = 8 -- When the player sold something through the shop window.
MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money.
MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window.
MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have.
MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window.
MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item
MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space
MESSAGE_IDLETIMEOUT = 15 -- When the player has been idle for longer then idleTime allows.
MESSAGE_WALKAWAY = 16 -- When the player walks out of the talkRadius of the npc.
MESSAGE_DECLINE = 17 -- When the player says no to something.
MESSAGE_SENDTRADE = 18 -- When the npc sends the trade window to the player
MESSAGE_NOSHOP = 19 -- When the npc's shop is requested but he doesn't have any
MESSAGE_ONCLOSESHOP = 20 -- When the player closes the npc's shop window
MESSAGE_ALREADYFOCUSED = 21 -- When the player already has the focus of this npc.
MESSAGE_WALKAWAY_MALE = 22 -- When a male player walks out of the talkRadius of the npc.
MESSAGE_WALKAWAY_FEMALE = 23 -- When a female player walks out of the talkRadius of the npc.
-- Constant indexes for callback functions. These are also used for module callback ids.
CALLBACK_CREATURE_APPEAR = 1
CALLBACK_CREATURE_DISAPPEAR = 2
CALLBACK_CREATURE_SAY = 3
CALLBACK_ONTHINK = 4
CALLBACK_GREET = 5
CALLBACK_FAREWELL = 6
CALLBACK_MESSAGE_DEFAULT = 7
CALLBACK_PLAYER_ENDTRADE = 8
CALLBACK_PLAYER_CLOSECHANNEL = 9
CALLBACK_ONBUY = 10
CALLBACK_ONSELL = 11
CALLBACK_ONADDFOCUS = 18
CALLBACK_ONRELEASEFOCUS = 19
CALLBACK_ONTRADEREQUEST = 20
-- Addidional module callback ids
CALLBACK_MODULE_INIT = 12
CALLBACK_MODULE_RESET = 13
-- Constant strings defining the keywords to replace in the default messages.
TAG_PLAYERNAME = "|PLAYERNAME|"
TAG_ITEMCOUNT = "|ITEMCOUNT|"
TAG_TOTALCOST = "|TOTALCOST|"
TAG_ITEMNAME = "|ITEMNAME|"
NpcHandler = {
keywordHandler = nil,
focuses = nil,
talkStart = nil,
idleTime = 86400,
talkRadius = 3,
talkDelayTime = 1, -- Seconds to delay outgoing messages.
talkDelay = nil,
callbackFunctions = nil,
modules = nil,
shopItems = nil, -- They must be here since ShopModule uses 'static' functions
eventSay = nil,
eventDelayedSay = nil,
topic = nil,
messages = {
-- These are the default replies of all npcs. They can/should be changed individually for each npc.
[MESSAGE_GREET] = "Greetings, |PLAYERNAME|.",
[MESSAGE_FAREWELL] = "Good bye, |PLAYERNAME|.",
[MESSAGE_BUY] = "Do you want to buy |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?",
[MESSAGE_ONBUY] = "Here you are.",
[MESSAGE_BOUGHT] = "Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.",
[MESSAGE_SELL] = "Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?",
[MESSAGE_ONSELL] = "Here you are, |TOTALCOST| gold.",
[MESSAGE_SOLD] = "Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.",
[MESSAGE_MISSINGMONEY] = "You don't have enough money.",
[MESSAGE_NEEDMONEY] = "You don't have enough money.",
[MESSAGE_MISSINGITEM] = "You don't have so many.",
[MESSAGE_NEEDITEM] = "You do not have this object.",
[MESSAGE_NEEDSPACE] = "You do not have enough capacity.",
[MESSAGE_NEEDMORESPACE] = "You do not have enough capacity for all items.",
[MESSAGE_IDLETIMEOUT] = "Good bye.",
[MESSAGE_WALKAWAY] = "Good bye.",
[MESSAGE_DECLINE] = "Then not.",
[MESSAGE_SENDTRADE] = "Of course, just browse through my wares.",
[MESSAGE_NOSHOP] = "Sorry, I'm not offering anything.",
[MESSAGE_ONCLOSESHOP] = "Thank you, come back whenever you're in need of something else.",
[MESSAGE_ALREADYFOCUSED]= "|PLAYERNAME|, I am already talking to you.",
[MESSAGE_WALKAWAY_MALE] = "Good bye.",
[MESSAGE_WALKAWAY_FEMALE] = "Good bye."
}
}
-- Creates a new NpcHandler with an empty callbackFunction stack.
function NpcHandler:new(keywordHandler)
local obj = {}
obj.callbackFunctions = {}
obj.modules = {}
obj.eventSay = {}
obj.eventDelayedSay = {}
obj.topic = {}
obj.focuses = {}
obj.talkStart = {}
obj.talkDelay = {}
obj.keywordHandler = keywordHandler
obj.messages = {}
obj.shopItems = {}
setmetatable(obj.messages, self.messages)
self.messages.__index = self.messages
setmetatable(obj, self)
self.__index = self
return obj
end
-- Re-defines the maximum idle time allowed for a player when talking to this npc.
function NpcHandler:setMaxIdleTime(newTime)
self.idleTime = newTime
end
-- Attackes a new keyword handler to this npchandler
function NpcHandler:setKeywordHandler(newHandler)
self.keywordHandler = newHandler
end
-- Function used to change the focus of this npc.
function NpcHandler:addFocus(newFocus)
if(self:isFocused(newFocus)) then
return
end
table.insert(self.focuses, newFocus)
self.topic[newFocus] = 0
local callback = self:getCallback(CALLBACK_ONADDFOCUS)
if(callback == nil or callback(newFocus)) then
self:processModuleCallback(CALLBACK_ONADDFOCUS, newFocus)
end
self:updateFocus()
end
-- Function used to verify if npc is focused to certain player
function NpcHandler:isFocused(focus)
for k,v in pairs(self.focuses) do
if v == focus then
return true
end
end
return false
end
-- This function should be called on each onThink and makes sure the npc faces the player it is talking to.
-- Should also be called whenever a new player is focused.
function NpcHandler:updateFocus()
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
doNpcSetCreatureFocus(focus)
return
end
end
doNpcSetCreatureFocus(0)
end
-- Used when the npc should un-focus the player.
function NpcHandler:releaseFocus(focus)
if(shop_cost[focus] ~= nil) then
table.remove(shop_amount, focus)
table.remove(shop_cost, focus)
table.remove(shop_rlname, focus)
table.remove(shop_itemid, focus)
table.remove(shop_container, focus)
table.remove(shop_npcuid, focus)
table.remove(shop_eventtype, focus)
table.remove(shop_subtype, focus)
table.remove(shop_destination, focus)
table.remove(shop_premium, focus)
end
if self.eventDelayedSay[focus] then
self:cancelNPCTalk(self.eventDelayedSay[focus])
end
if(not self:isFocused(focus)) then
return
end
local pos = nil
for k,v in pairs(self.focuses) do
if v == focus then
pos = k
end
end
table.remove(self.focuses, pos)
self.eventSay[focus] = nil
self.eventDelayedSay[focus] = nil
self.talkStart[focus] = nil
self.topic[focus] = nil
local callback = self:getCallback(CALLBACK_ONRELEASEFOCUS)
if(callback == nil or callback(focus)) then
self:processModuleCallback(CALLBACK_ONRELEASEFOCUS, focus)
end
if isPlayer(focus) == TRUE then
closeShopWindow(focus) --Even if it can not exist, we need to prevent it.
self:updateFocus()
end
end
-- Returns the callback function with the specified id or nil if no such callback function exists.
function NpcHandler:getCallback(id)
local ret = nil
if(self.callbackFunctions ~= nil) then
ret = self.callbackFunctions[id]
end
return ret
end
-- Changes the callback function for the given id to callback.
function NpcHandler:setCallback(id, callback)
if(self.callbackFunctions ~= nil) then
self.callbackFunctions[id] = callback
end
end
-- Adds a module to this npchandler and inits it.
function NpcHandler:addModule(module)
if(self.modules ~= nil) then
table.insert(self.modules, module)
module:init(self)
end
end
-- Calls the callback function represented by id for all modules added to this npchandler with the given arguments.
function NpcHandler:processModuleCallback(id, ...)
local ret = true
for i, module in pairs(self.modules) do
local tmpRet = true
if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then
tmpRet = module:callbackOnCreatureAppear(...)
elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then
tmpRet = module:callbackOnCreatureDisappear(...)
elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then
tmpRet = module:callbackOnCreatureSay(...)
elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then
tmpRet = module:callbackOnPlayerEndTrade(...)
elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then
tmpRet = module:callbackOnPlayerCloseChannel(...)
elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then
tmpRet = module:callbackOnBuy(...)
elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then
tmpRet = module:callbackOnSell(...)
elseif(id == CALLBACK_ONTRADEREQUEST and module.callbackOnTradeRequest ~= nil) then
tmpRet = module:callbackOnTradeRequest(...)
elseif(id == CALLBACK_ONADDFOCUS and module.callbackOnAddFocus ~= nil) then
tmpRet = module:callbackOnAddFocus(...)
elseif(id == CALLBACK_ONRELEASEFOCUS and module.callbackOnReleaseFocus ~= nil) then
tmpRet = module:callbackOnReleaseFocus(...)
elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then
tmpRet = module:callbackOnThink(...)
elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then
tmpRet = module:callbackOnGreet(...)
elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then
tmpRet = module:callbackOnFarewell(...)
elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then
tmpRet = module:callbackOnMessageDefault(...)
elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then
tmpRet = module:callbackOnModuleReset(...)
end
if(not tmpRet) then
ret = false
break
end
end
return ret
end
-- Returns the message represented by id.
function NpcHandler:getMessage(id)
local ret = nil
if(self.messages ~= nil) then
ret = self.messages[id]
end
return ret
end
-- Changes the default response message with the specified id to newMessage.
function NpcHandler:setMessage(id, newMessage)
if(self.messages ~= nil) then
self.messages[id] = newMessage
end
end
-- Translates all message tags found in msg using parseInfo
function NpcHandler:parseMessage(msg, parseInfo)
local ret = msg
for search, replace in pairs(parseInfo) do
ret = string.gsub(ret, search, replace)
end
return ret
end
-- Makes sure the npc un-focuses the currently focused player
function NpcHandler:unGreet(cid)
if(not self:isFocused(cid)) then
return
end
local callback = self:getCallback(CALLBACK_FAREWELL)
if(callback == nil or callback()) then
if(self:processModuleCallback(CALLBACK_FAREWELL)) then
local msg = self:getMessage(MESSAGE_FAREWELL)
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid, true)
self:releaseFocus(cid)
end
end
end
-- Greets a new player.
function NpcHandler:greet(cid)
if(cid ~= 0) then
local callback = self:getCallback(CALLBACK_GREET)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_GREET, cid)) then
local msg = self:getMessage(MESSAGE_GREET)
local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) }
msg = self:parseMessage(msg, parseInfo)
self:say(msg, cid, true)
else
return
end
else
return
end
end
self:addFocus(cid)
end
-- Handles onCreatureAppear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_APPEAR callback.
function NpcHandler:onCreatureAppear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_APPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_APPEAR, cid)) then
--
end
end
end
-- Handles onCreatureDisappear events. If you with to handle this yourself, please use the CALLBACK_CREATURE_DISAPPEAR callback.
function NpcHandler:onCreatureDisappear(cid)
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onCreatureSay events. If you with to handle this yourself, please use the CALLBACK_CREATURE_SAY callback.
function NpcHandler:onCreatureSay(cid, msgtype, msg)
local callback = self:getCallback(CALLBACK_CREATURE_SAY)
if(callback == nil or callback(cid, msgtype, msg)) then
if(self:processModuleCallback(CALLBACK_CREATURE_SAY, cid, msgtype, msg)) then
if(not self:isInRange(cid)) then
return
end
if(self.keywordHandler ~= nil) then
if(self:isFocused(cid) and (msgtype == TALKTYPE_PRIVATE_PN) or (not self:isFocused(cid))) then
local ret = self.keywordHandler:processMessage(cid, msg)
if(not ret) then
local callback = self:getCallback(CALLBACK_MESSAGE_DEFAULT)
if(callback ~= nil and callback(cid, msgtype, msg)) then
self.talkStart[cid] = os.time()
end
else
self.talkStart[cid] = os.time()
end
end
end
end
end
end
-- Handles onPlayerEndTrade events. If you wish to handle this yourself, use the CALLBACK_PLAYER_ENDTRADE callback.
function NpcHandler:onPlayerEndTrade(cid)
local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid, msgtype, msg)) then
if(self:isFocused(cid)) then
local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo)
self:say(msg, cid)
end
end
end
end
-- Handles onPlayerCloseChannel events. If you wish to handle this yourself, use the CALLBACK_PLAYER_CLOSECHANNEL callback.
function NpcHandler:onPlayerCloseChannel(cid)
local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid, msgtype, msg)) then
if(self:isFocused(cid)) then
self:unGreet(cid)
end
end
end
end
-- Handles onBuy events. If you wish to handle this yourself, use the CALLBACK_ONBUY callback.
function NpcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONBUY)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONBUY, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onSell events. If you wish to handle this yourself, use the CALLBACK_ONSELL callback.
function NpcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks)
local callback = self:getCallback(CALLBACK_ONSELL)
if(callback == nil or callback(cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
if(self:processModuleCallback(CALLBACK_ONSELL, cid, itemid, subType, amount, ignoreCap, inBackpacks)) then
--
end
end
end
-- Handles onTradeRequest events. If you wish to handle this yourself, use the CALLBACK_ONTRADEREQUEST callback.
function NpcHandler:onTradeRequest(cid)
local callback = self:getCallback(CALLBACK_ONTRADEREQUEST)
if(callback == nil or callback(cid)) then
if(self:processModuleCallback(CALLBACK_ONTRADEREQUEST, cid)) then
return true
end
end
return false
end
-- Handles onThink events. If you wish to handle this yourself, please use the CALLBACK_ONTHINK callback.
function NpcHandler:onThink()
local callback = self:getCallback(CALLBACK_ONTHINK)
if(callback == nil or callback()) then
if(NPCHANDLER_TALKDELAY == TALKDELAY_ONTHINK) then
for cid, talkDelay in pairs(self.talkDelay) do
if(talkDelay.time ~= nil and talkDelay.message ~= nil and os.time() >= talkDelay.time) then
selfSay(talkDelay.message, cid, talkDelay.publicize and TRUE or FALSE)
self.talkDelay[cid] = nil
end
end
end
if(self:processModuleCallback(CALLBACK_ONTHINK)) then
for pos, focus in pairs(self.focuses) do
if(focus ~= nil) then
if(not self:isInRange(focus)) then
self:onWalkAway(focus)
elseif(self.talkStart[focus] ~= nil and (os.time() - self.talkStart[focus]) > self.idleTime) then
self:unGreet(focus)
else
self:updateFocus()
end
end
end
end
end
end
-- Tries to greet the player with the given cid.
function NpcHandler:onGreet(cid)
if(self:isInRange(cid)) then
if(not self:isFocused(cid)) then
self:greet(cid)
return
end
end
end
-- Simply calls the underlying unGreet function.
function NpcHandler:onFarewell(cid)
self:unGreet(cid)
end
-- Should be called on this npc's focus if the distance to focus is greater then talkRadius.
function NpcHandler:onWalkAway(cid)
if(self:isFocused(cid)) then
local callback = self:getCallback(CALLBACK_CREATURE_DISAPPEAR)
if(callback == nil or callback()) then
if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then
local msg = self:getMessage(MESSAGE_WALKAWAY)
local playerName = getPlayerName(cid)
if not playerName then
playerName = -1
end
local parseInfo = { [TAG_PLAYERNAME] = playerName }
local message = self:parseMessage(msg, parseInfo)
local msg_male = self:getMessage(MESSAGE_WALKAWAY_MALE)
local message_male = self:parseMessage(msg_male, parseInfo)
local msg_female = self:getMessage(MESSAGE_WALKAWAY_FEMALE)
local message_female = self:parseMessage(msg_female, parseInfo)
if message_female ~= message_male then
if getPlayerSex(cid) == 0 then
selfSay(message_female)
else
selfSay(message_male)
end
elseif message ~= "" then
selfSay(message)
end
self:releaseFocus(cid)
end
end
end
end
-- Returns true if cid is within the talkRadius of this npc.
function NpcHandler:isInRange(cid)
local distance = isPlayer(cid) == TRUE and getDistanceTo(cid) or -1
if distance == -1 then
return false
end
return (distance <= self.talkRadius)
end
-- Resets the npc into its initial state (in regard of the keywordhandler).
-- All modules are also receiving a reset call through their callbackOnModuleReset function.
function NpcHandler:resetNpc()
if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then
self.keywordHandler:reset()
end
end
function NpcHandler:cancelNPCTalk(events)
for aux = 1, #events do
stopEvent(events[aux].event)
end
events = nil
end
function NpcHandler:doNPCTalkALot(msgs, interval, pcid)
if self.eventDelayedSay[pcid] then
self:cancelNPCTalk(self.eventDelayedSay[pcid])
end
self.eventDelayedSay[pcid] = {}
local ret = {}
for aux = 1, #msgs do
self.eventDelayedSay[pcid][aux] = {}
doCreatureSayWithDelay(getNpcCid(), msgs[aux], TALKTYPE_PRIVATE_NP, ((aux-1) * (interval or 10000)) + 1000, self.eventDelayedSay[pcid][aux], pcid)
table.insert(ret, self.eventDelayedSay[pcid][aux])
end
return(ret)
end
-- Makes the npc represented by this instance of NpcHandler say something.
-- This implements the currently set type of talkdelay.
-- shallDelay is a boolean value. If it is false, the message is not delayed. Default value is true.
function NpcHandler:say(message, focus, publicize, shallDelay, delay)
if(type(message) == "table") then
return self:doNPCTalkALot(message, delay or 10000, focus)
end
if self.eventDelayedSay[focus] then
self:cancelNPCTalk(self.eventDelayedSay[focus])
end
local shallDelay = not shallDelay and true or shallDelay
if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or shallDelay == false) then
selfSay(message, focus, publicize and TRUE or FALSE)
return
end
stopEvent(self.eventSay[focus])
self.eventSay[focus] = addEvent(function(x) doCreatureSay(x[1], x[2], TALKTYPE_PRIVATE_NP, false, x[3], getCreaturePosition(x[1])) end, self.talkDelayTime * 1000, {getNpcCid(), message, focus})
end
end