• 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!

TFS 1.X+ Problem NPC - Argument #5 is unsafe

Svira

Banned User
Joined
Jan 27, 2008
Messages
361
Solutions
13
Reaction score
105
Hello, I have a problem with an NPC - Argument #5 is unsafe, I found some errors on the forum but I cannot solve the problem in my code. Will there be a soul who will tell me where the mistake is and how to fix it?

error:
Code:
Lua Script Error: [Npc interface]
(Unknown scriptfile)
luaAddEvent(). Argument #5 is unsafe
stack traceback:
    [C]: in function 'addEvent'
    data/npc/lib/npcsystem/npchandler.lua:648: in function 'say'
    data/npc/scripts/local_trader.lua:79: in function <data/npc/scripts/local_trader.lua:75>

NPC:
LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

local moeda = 15515 -- ID waluty
local itemsForSale = {
    [27063] = {price = 400},
    [18394] = {price = 180},
    [24809] = {price = 600},
    [16007] = {price = 150},
    [2197] = {price = 4},
    [2164] = {price = 3},
    [5919] = {price = 1000},
    [5015] = {price = 800},
    [5804] = {price = 1000},
    [5809] = {price = 1500},
    [6099] = {price = 800},
    [6102] = {price = 300},
    [6101] = {price = 200},
    [6100] = {price = 200},
    [11754] = {price = 100},
    [11144] = {price = 100},
    [10063] = {price = 100},
    [6579] = {price = 100},
    [3954] = {price = 100},
    [2108] = {price = 100},
    [9019] = {price = 100},
    [6512] = {price = 100},
    [11256] = {price = 100},
    [5903] = {price = 8000}
}

local backpacksForSale = {
    {name = "BP of Supreme Health Potion", bpId = 2000, itemId = 26031, count = 2000, price = 5},
    {name = "BP of Ultimate Mana Potion", bpId = 2001, itemId = 26029, count = 2000, price = 4},
    {name = "BP of Great Spirit Potion", bpId = 10519, itemId = 8472, count = 2000, price = 3},
    {name = "BP of Sudden Death Rune", bpId = 2003, itemId = 2268, count = 2000, price = 4},
    {name = "BP of Infernal Bolt", bpId = 2000, itemId = 6529, count = 2000, price = 22}
}

local function createBackpack(player, bpId, itemId, count)
    local backpack = player:addItem(bpId, 1)
    if backpack then
        for i = 1, count do
            backpack:addItem(itemId, 1)
        end
        return true
    end
    return false
end

local function greetCallback(cid)
    npcHandler:say("Hero, I have goods for {trade} and special {supplies} for bars of gold. Are you interested?", cid)
    npcHandler.topic[cid] = 0
    npcHandler:addFocus(cid)
    return true
end

local function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end

    local player = Player(cid)
    if not player then return false end

    local lowerMsg = msg:lower()
    npcHandler.topic[cid] = npcHandler.topic[cid] or 0

    if lowerMsg == "trade" then
        local shopWindow = {}
        for itemId, details in pairs(itemsForSale) do
            table.insert(shopWindow, {id = itemId, subType = 0, buy = details.price, sell = 0, name = getItemName(itemId)})
        end
        openShopWindow(cid, shopWindow, function(cid, itemId, subType, amount, ignoreCap, inBackpacks)
            local itemDetails = itemsForSale[itemId]
            if itemDetails and player:removeItem(moeda, itemDetails.price) then
                player:addItem(itemId, 1)
                npcHandler:say("Here you go, hero. Spend it wisely!", cid)
            else
                npcHandler:say("You don't have enough " .. getItemName(moeda) .. ".", cid)
            end
        end, function() end)
        npcHandler:say("Take a look at my goods, hero.", cid)
    elseif lowerMsg == "supplies" then
        local suppliesText = "Here are the special supplies I can offer:\n"
        for i, bp in ipairs(backpacksForSale) do
            suppliesText = suppliesText .. string.format("%d) %s for %d Bars of Gold\n", i, bp.name, bp.price)
        end
        npcHandler:say(suppliesText .. "Type the number to purchase.", cid)
        npcHandler.topic[cid] = 1
    elseif tonumber(lowerMsg) and npcHandler.topic[cid] == 1 then
        local choice = tonumber(lowerMsg)
        local backpack = backpacksForSale[choice]
        if backpack and player:removeItem(moeda, backpack.price) then
            if createBackpack(player, backpack.bpId, backpack.itemId, backpack.count) then
                npcHandler:say("Here is your " .. backpack.name .. ", hero. Use it wisely!", cid)
            else
                npcHandler:say("I couldn't prepare your backpack. Try again.", cid)
            end
        else
            npcHandler:say("You don't have enough Bars of Gold for this supply.", cid)
        end
        npcHandler.topic[cid] = 0
    else
        npcHandler:say("Say {trade} for items or {supplies} for special backpacks.", cid)
    end

    return true
end

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

npcHandler:setMessage(MESSAGE_WALKAWAY, "Come by again, I like bars of gold...")
npcHandler:setMessage(MESSAGE_FAREWELL, "Ehh, I'll still get my money's worth!")
npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

I know that the source of the problem is addevent, but if I try to go in a different direction, the trade window does not open and the console does not show any errors.


Here I have code that does not generate errors, but how to transfer these messages to NPC CHannel?

LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

local moeda = 15515 -- ID waluty
local itemsForSale = {
    [27063] = {price = 400},
    [18394] = {price = 180},
    [24809] = {price = 600},
    [16007] = {price = 150},
    [2197] = {price = 4},
    [2164] = {price = 3},
    [5919] = {price = 1000},
    [5015] = {price = 800},
    [5804] = {price = 1000},
    [5809] = {price = 1500},
    [6099] = {price = 800},
    [6102] = {price = 300},
    [6101] = {price = 200},
    [6100] = {price = 200},
    [11754] = {price = 100},
    [11144] = {price = 100},
    [10063] = {price = 100},
    [6579] = {price = 100},
    [3954] = {price = 100},
    [2108] = {price = 100},
    [9019] = {price = 100},
    [6512] = {price = 100},
    [11256] = {price = 100},
    [5903] = {price = 8000}
}

local backpacksForSale = {
    {name = "BP of Supreme Health Potion", bpId = 2000, itemId = 26031, count = 2000, price = 5},
    {name = "BP of Ultimate Mana Potion", bpId = 2001, itemId = 26029, count = 2000, price = 4},
    {name = "BP of Great Spirit Potion", bpId = 10519, itemId = 8472, count = 2000, price = 3},
    {name = "BP of Sudden Death Rune", bpId = 2003, itemId = 2268, count = 2000, price = 4},
    {name = "BP of Infernal Bolt", bpId = 2000, itemId = 6529, count = 2000, price = 22}
}

local function createBackpack(player, bpId, itemId, count)
    local backpack = player:addItem(bpId, 1)
    if backpack then
        for i = 1, count do
            backpack:addItem(itemId, 1)
        end
        return true
    end
    return false
end

local function greetCallback(cid)
    npcHandler:setMessage(
        MESSAGE_GREET,
        "Hero, I have goods for {trade} and special {supplies} for bars of gold. Are you interested?"
    )
    npcHandler.topic[cid] = 0 -- Inicjalizacja tematu rozmowy
    npcHandler:addFocus(cid)
    return true
end

local function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end

    local player = Player(cid)
    if not player then return false end -- Upewniamy się, że gracz istnieje

    local lowerMsg = msg:lower()
    npcHandler.topic[cid] = npcHandler.topic[cid] or 0 -- Zabezpieczenie dla topic

    if lowerMsg == "trade" then
        local shopWindow = {}
        for itemId, details in pairs(itemsForSale) do
            table.insert(shopWindow, {id = itemId, subType = 0, buy = details.price, sell = 0, name = getItemName(itemId)})
        end
        openShopWindow(cid, shopWindow, function(cid, itemId, subType, amount, ignoreCap, inBackpacks)
            local itemDetails = itemsForSale[itemId]
            if itemDetails and player:removeItem(moeda, itemDetails.price) then
                player:addItem(itemId, 1)
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Here you go, hero. Spend it wisely!")
            else
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_RED, "You don't have enough " .. getItemName(moeda) .. ".")
            end
        end, function() end)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Take a look at my goods, hero.")
    elseif lowerMsg == "supplies" then
        local suppliesText = "Here are the special supplies I can offer:\n"
        for i, bp in ipairs(backpacksForSale) do
            suppliesText = suppliesText .. string.format("%d) %s for %d Bars of Gold\n", i, bp.name, bp.price)
        end
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, suppliesText .. "Type the number to purchase.")
        npcHandler.topic[cid] = 1
    elseif tonumber(lowerMsg) and npcHandler.topic[cid] == 1 then
        local choice = tonumber(lowerMsg)
        local backpack = backpacksForSale[choice]
        if backpack and player:removeItem(moeda, backpack.price) then
            if createBackpack(player, backpack.bpId, backpack.itemId, backpack.count) then
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Here is your " .. backpack.name .. ", hero. Use it wisely!")
            else
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_RED, "I couldn't prepare your backpack. Try again.")
            end
        else
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_RED, "You don't have enough Bars of Gold for this supply.")
        end
        npcHandler.topic[cid] = 0
    else
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Say {trade} for items or {supplies} for special backpacks.")
    end

    return true
end

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

npcHandler:setMessage(MESSAGE_WALKAWAY, "Come by again, I like bars of gold...")
npcHandler:setMessage(MESSAGE_FAREWELL, "Ehh, I'll still get my money's worth!")
npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
 
Last edited:
Solution
I know that the source of the problem is addevent
Problem is that you are inside onBuy callback in 79 line of code, function that will be called, when player clicks in tibia client some action (Buy/Sell) in Trade Window.
Your code:
LUA:
        openShopWindow(cid, shopWindow, function(cid, itemId, subType, amount, ignoreCap, inBackpacks)
            local itemDetails = itemsForSale[itemId]
            if itemDetails and player:removeItem(moeda, itemDetails.price) then
                player:addItem(itemId, 1)
                npcHandler:say("Here you go, hero. Spend it wisely!", cid)
            else
                npcHandler:say("You don't have enough " .. getItemName(moeda) .. ".", cid)
            end
        end, function()...
I know that the source of the problem is addevent
Problem is that you are inside onBuy callback in 79 line of code, function that will be called, when player clicks in tibia client some action (Buy/Sell) in Trade Window.
Your code:
LUA:
        openShopWindow(cid, shopWindow, function(cid, itemId, subType, amount, ignoreCap, inBackpacks)
            local itemDetails = itemsForSale[itemId]
            if itemDetails and player:removeItem(moeda, itemDetails.price) then
                player:addItem(itemId, 1)
                npcHandler:say("Here you go, hero. Spend it wisely!", cid)
            else
                npcHandler:say("You don't have enough " .. getItemName(moeda) .. ".", cid)
            end
        end, function() end)
TFS 1.4 C++ code docs:
LUA:
npc:openShopWindow(cid, items, buyCallback, sellCallback)
3rd parameter is buyCallback and in your case it's:
LUA:
        function(cid, itemId, subType, amount, ignoreCap, inBackpacks)
            local itemDetails = itemsForSale[itemId]
            if itemDetails and player:removeItem(moeda, itemDetails.price) then
                player:addItem(itemId, 1)
                npcHandler:say("Here you go, hero. Spend it wisely!", cid)
            else
                npcHandler:say("You don't have enough " .. getItemName(moeda) .. ".", cid)
            end
        end
and it's old NPC code for TFS 0.3/0.4 (parameter cid as first parameter). On TFS 1.4 it is:
LUA:
onBuy(player, itemid, count, amount, ignore, inbackpacks)
so first parameter is player (object Player), not cid (creature ID = number with ID of creature).

There are 2 problems:

To fix these 2 problems, you should replace onBuy callback code with:
LUA:
        function(player, itemId, subType, amount, ignoreCap, inBackpacks)
            local itemDetails = itemsForSale[itemId]
            if itemDetails and player:removeItem(moeda, itemDetails.price) then
                player:addItem(itemId, 1)
                npcHandler:say("Here you go, hero. Spend it wisely!", player:getId())
            else
                npcHandler:say("You don't have enough " .. getItemName(moeda) .. ".", player:getId())
            end
        end
So we set first variable name to player, not cid, as it's Player object passed from C++ to Lua, not some "cid" number and then to get player ID we use player:getId(), not cid.

OFFTOP:
In this RARE scenario, you can just replace cid variable with player in first parameter of function. It will work and it will be save, but in any other script, you will also have to replace cid with player:getId(), so I posted this as official solution. To do not recommend bad practices that would crash OTS or execute bugged code in 99% of other cases.
 
Solution
Back
Top