• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Lua NPC crash on farewell

swashed

Member
Joined
Jul 9, 2023
Messages
73
Reaction score
8
Hi,

I’m using a script I found here for an NPC that handles tasks. Everything works great except for when the player says “bye” or walks away.

On the original client, the client crashes.

On OTClient, it doesn’t crash, but the NPC “blinks” like he’s reloading right there on the spot.

I’m hoping somebody out here has seen this before.
Post automatically merged:

1714996820298.png
Post automatically merged:

This is the script I'm using:


I'm linking the thread because it's a few files being used.
Post automatically merged:

Lua:
dofile(getDataDir() .. 'npc/scripts/lib/greeting.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
print("Loaded npc modules")
local choose = {}
local cancel = {}
local available = {}
print("loaded some variables")
function creatureSayCallback(cid, type, msg)
print("creaturesaycallback")
if(npcHandler.focus ~= cid) then
        print("focus lost")
        return false
    end

local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE and 0 or cid
print("talkuser")
--Player asks about task
if isInArray({"tasks", "task", "mission"}, msg:lower()) then
print("TRACE 0001")
local can = getTasksByPlayer(cid)
if #can > 0 then
local text = ""
local sep = ", "
table.sort(can, (function(a, b) return (a < b) end))
local t = 0
for _, id in ipairs(can) do
t = t + 1
if t == #can - 1 then
sep = " and "
elseif t == #can then
sep = "."
end
text = text .. "{" .. (tasks[id].name or tasks[id].raceName) .. "}" .. sep
end
selfSay("The current task" .. (#can > 1 and "s" or "") .. " that you can choose " .. (#can > 1 and "are" or "is") .. " " .. text)
talk_state = 0
else
selfSay("I don't have any task for you right now.")
end
elseif msg ~= "" and canStartTask(cid, msg) then
if #getPlayerStartedTasks(cid) >= tasksByPlayer then
selfSay("Sorry, but you already started " .. tasksByPlayer .. " tasks.")
return true
end

--Set new task
local task = getTaskByName(msg)
if task and getPlayerStorageValue(cid, QUESTSTORAGE_BASE + task) > 0 then
return false
end
selfSay("In this task you must defeat " .. tasks[task].killsRequired .. " " .. tasks[task].raceName .. ". Are you sure that you want to start this task?")
choose[cid] = task
talk_state = 1
elseif msg:lower() == "yes" and talk_state == 1 then
setPlayerStorageValue(cid, QUESTSTORAGE_BASE + choose[cid], 1)
selfSay("Excellent! You can check the status of your task by saying reward to me, or if you want to leave your task.")
choose[cid] = nil
talk_state = 0
elseif msg:lower() == "reward" then
local started = getPlayerStartedTasks(cid)
local finishedAtLeastOne = false
local finished = 0
if started and #started > 0 then
for _, id in ipairs(started) do

--Give player their reward for completing task
if getPlayerStorageValue(cid, KILLSSTORAGE_BASE + id) >= tasks[id].killsRequired then
for _, reward in ipairs(tasks[id].rewards) do
print("Rewards loaded")
local deny = false
if reward.storage then
if getPlayerStorageValue(cid, reward.storage[1]) >= reward.storage[2] then
deny = true
end
end

--Reward types
if isInArray({REWARD_MONEY, "money"}, reward.type:lower()) and not deny then
    doPlayerAddMoney(cid, reward.value[1])
elseif isInArray({REWARD_EXP, "exp", "experience"}, reward.type:lower()) and not deny then
    doPlayerAddExp(cid, reward.value[1])
    doPlayerSendDefaultCancel(cid, "You gained " .. reward.value[1] .. " experience points.")
elseif isInArray({REWARD_ACHIEVEMENT, "achievement", "ach"}, reward.type:lower()) and not deny then

if doPlayerAddAchievement then
    doPlayerAddAchievement(cid, reward.value[1], true)
end
elseif isInArray({REWARD_STORAGE, "storage", "stor"}, reward.type:lower()) and not deny then
    setPlayerStorageValue(cid, reward.value[1], reward.value[2])
elseif isInArray({REWARD_POINT, "points", "point"}, reward.type:lower()) and not deny then
    setPlayerStorageValue(cid, POINTSSTORAGE, getPlayerStorageValue(cid, POINTSSTORAGE) + reward.value[1])
elseif isInArray({REWARD_ITEM, "item", "items", "object"}, reward.type:lower()) and not deny then
    doPlayerAddItem(cid, reward.value[1], reward.value[2])
end

if reward.storage then
    setPlayerStorageValue(cid, reward.storage[1], reward.storage[2])
end
end

if tasks[id].norepeatable then
    setPlayerStorageValue(cid, QUESTSTORAGE_BASE + id, 2)
else
    setPlayerStorageValue(cid, QUESTSTORAGE_BASE + id, 0)
end

setPlayerStorageValue(cid, KILLSSTORAGE_BASE + id, 0)
if getPlayerStorageValue(cid, REPEATSTORAGE_BASE + id) < 1 then
    setPlayerStorageValue(cid, REPEATSTORAGE_BASE + id, 0)
end

setPlayerStorageValue(cid, REPEATSTORAGE_BASE + id, getPlayerStorageValue(cid, REPEATSTORAGE_BASE + id) + 1)
finishedAtLeastOne = true
finished = finished + 1
end
end

if not finishedAtLeastOne then
    selfSay("You haven't completed any task yet.")
else
    selfSay("Awesome! you finished " .. (finished > 1 and "various" or "a") .. " task" .. (finished > 1 and "s" or "") .. ". Talk to me again if you want to start a task.")
end
else
    selfSay("You haven't started any task yet.")
end
elseif msg:lower() == "started" then
local started = getPlayerStartedTasks(cid)
if started and #started > 0 then
    local text = ""
    local sep = ", "
    table.sort(started, (function(a, b) return (a < b) end))
    local t = 0
    for _, id in ipairs(started) do
        t = t + 1
    if t == #started - 1 then
        sep = " and "
    elseif t == #started then
        sep = "."
    end
        text = text .. "{" .. (tasks[id].name or tasks[id].raceName) .. "}" .. sep
    end

    selfSay("The current task" .. (#started > 1 and "s" or "") .. " that you started " .. (#started > 1 and "are" or "is") .. " " .. text)
    else
        selfSay("You haven't started any task yet.")
end

--Leave task
elseif msg:lower() == "leave" then
local started = getPlayerStartedTasks(cid)
if started and #started > 0 then
selfSay("Cancelling a task will remove all progress. Are you sure you want to leave your current task?")
talk_state = 2
else
selfSay("You haven't started any task yet.")
end
--get player to name the task they want to cancel
elseif getTaskByName(msg) and talk_state == 2 and isInArray(getPlayerStartedTasks(cid), getTaskByName(msg)) then
local task = getTaskByName(msg)
if getPlayerStorageValue(cid, KILLSSTORAGE_BASE + task) > 0 then
selfSay("You currently killed " .. getPlayerStorageValue(cid, KILLSSTORAGE_BASE + task) .. "/" .. tasks[task].killsRequired .. " " .. tasks[task].raceName .. ". Cancelling this task lose all progress. Are you sure you want to cancel this task?")
else
selfSay("Are you sure you want to cancel this task?")
end
talk_state = 3
cancel[cid] = task
elseif msg:lower() == "yes" and talk_state == 3 then
setPlayerStorageValue(cid, QUESTSTORAGE_BASE + cancel[cid], -1)
setPlayerStorageValue(cid, KILLSSTORAGE_BASE + cancel[cid], -1)
selfSay("You have cancelled the task " .. (tasks[cancel[cid]].name or tasks[cancel[cid]].raceName) .. ".")
talk_state = 0

--Ask about point gained
elseif isInArray({"points", "rank"}, msg:lower()) then
selfSay("At this time, you have " .. getPlayerStorageValue(cid, POINTSSTORAGE) .. " Paw & Fur points. You " .. (getPlayerRank(cid) == 5 and "are an Elite Hunter" or getPlayerRank(cid) == 4 and "are a Trophy Hunter" or getPlayerRank(cid) == 3 and "are a Big Game Hunter" or getPlayerRank(cid) == 2 and "are a Ranger" or getPlayerRank(cid) == 1 and "are a Huntsman" or "haven't been ranked yet") .. ".")
talk_state = 0
end
print("TRACE 0002 - end")
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
Post automatically merged:

would ungreet fix this
 
Last edited:
I put debug messages on walk away AND saying bye. both seem to work for this npc. there is something about this npc that is resetting
Post automatically merged:

blink.gif
Post automatically merged:

1715026091592.png

Could the function be using storages that i have in use? Would that cause a problem for the npc to load even
 
Last edited:
I don't think there's any storage involved in your error. because bye message is related to libs. Is there a chance that you could attach npc/scripts/lib/greeting.lua? Because somewhere there should be the data for npcHandler, and that somewhere generally is the lib that you're using for your NPCs.

I have never used OThire, but as I see there's a "bye" trigger in OTHire/data/npc/scripts/lib/npcsystem/npcsystem.lua at master · Ezzz-dev/OTHire (https://github.com/Ezzz-dev/OTHire/blob/master/data/npc/scripts/lib/npcsystem/npcsystem.lua#L24)

And one here

Start checking there. Another idea is to get the lines from FOCUS_FAREWELLWORDS or MESSAGE_FAREWELL from another NPC and replicate them. Pheraps you also have two library functions attached on the same NPC. So that's why when you say bye, it trigger those two (I don't know if this is the error, just giving you some ideas). Cheers and hope you can fix it! Regards :)
 
I don't think there's any storage involved in your error. because bye message is related to libs. Is there a chance that you could attach npc/scripts/lib/greeting.lua? Because somewhere there should be the data for npcHandler, and that somewhere generally is the lib that you're using for your NPCs.

I have never used OThire, but as I see there's a "bye" trigger in OTHire/data/npc/scripts/lib/npcsystem/npcsystem.lua at master · Ezzz-dev/OTHire (https://github.com/Ezzz-dev/OTHire/blob/master/data/npc/scripts/lib/npcsystem/npcsystem.lua#L24)

And one here

Start checking there. Another idea is to get the lines from FOCUS_FAREWELLWORDS or MESSAGE_FAREWELL from another NPC and replicate them. Pheraps you also have two library functions attached on the same NPC. So that's why when you say bye, it trigger those two (I don't know if this is the error, just giving you some ideas). Cheers and hope you can fix it! Regards :)

This NPC has access="3" in the spawn.xml. I summoned 1 and he works perfectly. Even with saying bye. I summoned another one and he is glitchy. I summoned a 3rd one who is glitchy. The first one I summoned still works. Maybe the access of 3 creates a conflict? I'd just test it but I don't want to reboot server
Post automatically merged:

greeting.lua

Lua:
function FocusModule:init(handler)
    FOCUS_GREETSWORDS = {'hi', 'hello'}
    FOCUS_FAREWELLSWORDS = {'bye', 'farewell'}
    self.npcHandler = handler
    for i, word in pairs(FOCUS_GREETSWORDS) do
        local obj = {}
        table.insert(obj, word)
        obj.callback = FOCUS_GREETSWORDS.callback or FocusModule.messageMatcher
        handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self})
    end

    for i, word in pairs(FOCUS_FAREWELLSWORDS) do
        local obj = {}
        table.insert(obj, word)
        obj.callback = FOCUS_FAREWELLSWORDS.callback or FocusModule.messageMatcher
        handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self})
    end

    return true
end
Post automatically merged:

first npc spawned working. others not

blink2.gif
 
This NPC has access="3" in the spawn.xml. I summoned 1 and he works perfectly. Even with saying bye. I summoned another one and he is glitchy. I summoned a 3rd one who is glitchy. The first one I summoned still works. Maybe the access of 3 creates a conflict? I'd just test it but I don't want to reboot server
Post automatically merged:

greeting.lua

Lua:
function FocusModule:init(handler)
    FOCUS_GREETSWORDS = {'hi', 'hello'}
    FOCUS_FAREWELLSWORDS = {'bye', 'farewell'}
    self.npcHandler = handler
    for i, word in pairs(FOCUS_GREETSWORDS) do
        local obj = {}
        table.insert(obj, word)
        obj.callback = FOCUS_GREETSWORDS.callback or FocusModule.messageMatcher
        handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self})
    end

    for i, word in pairs(FOCUS_FAREWELLSWORDS) do
        local obj = {}
        table.insert(obj, word)
        obj.callback = FOCUS_FAREWELLSWORDS.callback or FocusModule.messageMatcher
        handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self})
    end

    return true
end
Post automatically merged:

first npc spawned working. others not

View attachment 84427
Well again, I haven't used OThire. But I guess that spawn.xml loads npcs based on their filenames right? So why not trying to copy and paste the file, call it TaskManager2.xml or something and spawn it separately? It's not a solution at all, but for the moment is something I would trigger just to see what happens.

I think this was a bug in Avesta too.
Hmm so if there's at least two different distributions with this issue. How about doing this same excersise I wrote here cannot open data/npc/lib/npcsystem/main.lua: No such file or directory (https://otland.net/threads/cannot-open-data-npc-lib-npcsystem-main-lua-no-such-file-or-directory.288923/#post-2752242) and see how the keyword handlers are set up? You can also see on source code if there's something wrong with npcs but I think that's too much for this ocassion. Tell me what you guys can find from it. Regards!
 
Last edited:
Well again, I haven't used OThire. But I guess that spawn.xml loads npcs based on their filenames right? So why not trying to copy and paste the file, call it TaskManager2.xml or something and spawn it separately? It's not a solution at all, but for the moment is something I would trigger just to see what happens.


Hmm so if there's at least two different distributions with this issue. How about doing this same excersise I wrote here cannot open data/npc/lib/npcsystem/main.lua: No such file or directory (https://otland.net/threads/cannot-open-data-npc-lib-npcsystem-main-lua-no-such-file-or-directory.288923/#post-2752242) and see how the modules are keyword handlers are set up? You can also see on source code if there's something wrong with npcs but I think that's too much for this ocassion. Tell me what you guys can find from it. Regards!
Yeah I might try making separate npc to test. Sometimes having just 1 will cause the issue. It’s annoying that it isn’t happening every time. It just works perfectly sometimes.

I did actually check all the other npc and used them as a schema for this npc and it has everything the same. I’ve even played around with the dofile imports.

The script works when it’s working so I don’t think there’s any mistakes in there, and the npchandler stuff also looks good. Just something about initial loading of npc and I think it resets or reloads when you say bye
 
Yeah I might try making separate npc to test. Sometimes having just 1 will cause the issue. It’s annoying that it isn’t happening every time. It just works perfectly sometimes.

I did actually check all the other npc and used them as a schema for this npc and it has everything the same. I’ve even played around with the dofile imports.

The script works when it’s working so I don’t think there’s any mistakes in there, and the npchandler stuff also looks good. Just something about initial loading of npc and I think it resets or reloads when you say bye
That exactly what you should do, probably you're missing something. But I wrote everything so you can start troubleshooting and that will help on moving forward. You can still look up for more clues, for example, here in sources there's -mostly- the functions of npcs that has to do something with source code.


So if you think is something about the initial loading of the npcs you can also check for load function here

You should also check for isLoaded

You can compare those to other source code to see if you can find something. Remember to back-up everything if you're going to edit source. Regards!
 
Ok so I've come to learn that this has NOTHING to do with the task script. This problem will still occur when I manually summon ANY npc.

blink2.gif
Post automatically merged:

I spawned in the NPC by putting him into spawn.xml and reloaded the server (can i reload the spawn file without restart??) and this seems to have fixed the issue. I knew it was going to be something stupidly simple >.>

blink3.gif
 
Last edited:
I spawned in the NPC by putting him into spawn.xml and reloaded the server (can i reload the spawn file without restart??) and this seems to have fixed the issue. I knew it was going to be something stupidly simple >.>

View attachment 84454
All the map related files are loaded on startup. Otherwise there would be unnecessary memory resourses just by loading changes on real-time (and lots of other reasons for not doing it). If you wish to take a look how the maps are loaded by sources just head to OTHire/source/map.cpp at master · Ezzz-dev/OTHire (https://github.com/Ezzz-dev/OTHire/blob/master/source/map.cpp#L55)

For spawns is specifically here:
C++:
if(!loader->loadSpawns(this)){
If you see in the bool of loadSpawns, there's how the file is loaded.
C++:
    /** Load the spawns
      * \param map pointer to the Map class
      * \return Returns true if the spawns were loaded successfully
    */
    bool loadSpawns(Map* map)
    {
        if(map->spawnfile.empty()){
            return true;
        }
        
        return Spawns::getInstance()->loadFromXml(map->spawnfile);
    }

It literally says that returns true only if the spawns were loaded successfully. And if you dig more, and go for Spawns::getInstance, you see how it is loaded in startUp.

C++:
Spawns::getInstance()->startup();
 
Back
Top Bottom