• 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+ NPC trade window with filter based on item attribute

VitoxMaster

Active Member
Joined
Feb 6, 2023
Messages
79
Solutions
2
Reaction score
38
Hi everyone!


I was wondering if there’s an easy way to implement the following mechanism:


When opening the trade window with an NPC, I’d like to apply a filter that hides items in my backpack that have specific customAttributes from being displayed as sellable. In other words, I have an item upgrade system, and I want to prevent accidentally selling upgraded gear for just a few gold coins.


Is there a LUA-based solution for this requirement? I know that I could use the onSell callback (from NPC Module) to block selling such items, but I’d prefer to hide them from the trade window entirely.

Real-case scenario:​

1742250445761.webp
I have three Underworld Rods in my backpack. When I open the trade window with an NPC, I see all three available for sale. However, if one of the rods has a customAttribute('x'), I want the trade window to display only the two unmodified rods.


I'm working on TFS 1.5 Nekiro downgrade 8.6.
Thanks in advance for any help!
 
Ended up using function below in callbackOnSell.
LUA:
function getPlayerItemsById(cid, itemId)
    local containers = {}
    local items = {}
  
    local sitem = getPlayerSlotItem(cid, CONST_SLOT_BACKPACK)
    if sitem.uid > 0 then
        if isContainer(sitem.uid) then
            table.insert(containers, sitem.uid)
        elseif not(itemId) or itemId == sitem.itemid then
            table.insert(items, sitem)
        end
    end

    while #containers > 0 do
        for k = (getContainerSize(containers[1]) - 1), 0, -1 do
            local tmp = getContainerItem(containers[1], k)
            if isContainer(tmp.uid) then
                table.insert(containers, tmp.uid)
            elseif not(itemId) or itemId == tmp.itemid then
                table.insert(items, tmp)
            end
        end
        table.remove(containers, 1)
    end
    return items
end

Code snippet. I was looking for items without customAttribute "level" on item. Using my custom functions to check if item is upgradeable, and what's its level.
LUA:
function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreEquipped, _)
[...]
if(isInArray(NpcsNotBuyingUpgradedItems, Npc():getName())) then
            if (isObjectUpgradeable(itemid)) then
                local itemSoldCount = 0
                local itemLeveledCount = 0
                local totalCost = 0
                local msg = ""
                local playerItems = getPlayerItemsById(cid, itemid)
                for i, item in pairs(playerItems) do
                    if(itemSoldCount >= amount) then
                        break
                    end
                    local itemLevel = getObjLevel(Item(item.uid))
                    if(itemLevel == 0) then
                        itemSoldCount = itemSoldCount + 1
                        totalCost = totalCost + shopItem.sell
                        doRemoveItem(item.uid, 1)
                    else
                        itemLeveledCount = itemLeveledCount + 1
                    end
                end [...]


Leaving it, maybe someone will find it useful. Now my NPC informs what couldnt be sold.
Final result - no more selling my upgraded stuff:
1743366751461.webp
 
Amazing bro, One question, was it difficult for you to install the trading system in the source code? I have the same TFS but version 8.00 and I don't know how to implement it ;(
 
Hi there,
I skipped C++ part and focused on callbacks in Lua. I'm not sure how it is done in 8.0 but probably you will have to find method called in shop module during sell event. Here is my full solution where I can't sell items which have custom improvements (getObjLevel function returns level of improvement).


LUA:
if(isInArray(NpcsNotBuyingUpgradedItems, Npc():getName())) then
            if (isObjectUpgradeable(itemid)) then
                local itemSoldCount = 0
                local itemLeveledCount = 0
                local totalCost = 0
                local msg = ""
                local playerItems = getPlayerItemsById(cid, itemid)
                for i, item in pairs(playerItems) do
                    if(itemSoldCount >= amount) then
                        break
                    end
                    local itemLevel = getObjLevel(Item(item.uid))
                    if(itemLevel == 0) then
                        itemSoldCount = itemSoldCount + 1
                        totalCost = totalCost + shopItem.sell
                        doRemoveItem(item.uid, 1)
                    else
                        itemLeveledCount = itemLeveledCount + 1
                    end
                end
                if(itemSoldCount > 0) then
                    parseInfo[TAG_ITEMCOUNT] = itemSoldCount
                    parseInfo[TAG_TOTALCOST] = totalCost
                    msg = self.npcHandler:getMessage(MESSAGE_SOLD)
                    msg = self.npcHandler:parseMessage(msg, parseInfo)
                    msg = msg .. " "
                end
                if (itemLeveledCount == 1 ) then
                    msg = msg .. string.format("You have %d non-sellable, upgraded item - %s.", itemLeveledCount, shopItem.name)
                elseif (itemLeveledCount > 1 ) then
                    msg = msg .. string.format("You have %d non-sellable, upgraded items - %s.", itemLeveledCount, shopItem.name)
                end
                player:addMoney(totalCost)
                msg = msg .. self:handleNpcMoneyFlow(cid, totalCost, "sell")
                player:sendTextMessage(MESSAGE_INFO_DESCR, msg)
                self.npcHandler.talkStart[cid] = os.time()
                return true
            end
        end

Upper part of code is right below:
LUA:
    -- Callback onSell() function. If you wish, you can change certain Npc to use your onSell().
    function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreEquipped, _)
        local shopItem = self:getShopItem(itemid, subType)

        if not shopItem then
            error("[ShopModule.onSell] items[itemid] == nil")
            return false
        end

        if shopItem.sell == -1 then
            error("[ShopModule.onSell] attempt to sell a non-sellable item")
            return false
        end

        local player = Player(cid)
        local parseInfo = {
            [TAG_PLAYERNAME] = player:getName(),
            [TAG_ITEMCOUNT] = amount,
            [TAG_TOTALCOST] = amount * shopItem.sell,
            [TAG_ITEMNAME] = shopItem.name
        }

        if not ItemType(itemid):isFluidContainer() then
            subType = -1
        end
 
Back
Top