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

Action [TFS 1.x] Item 'loot seller'

Gesior.pl

Mega Noob&LOL 2012
Senator
Joined
Sep 18, 2007
Messages
2,955
Solutions
98
Reaction score
3,351
Location
Poland
GitHub
gesior
TFS 1.x version of Action - item 'item seller'/'fast loot' (use npc items list config!) (https://otland.net/threads/item-item-seller-fast-loot-use-npc-items-list-config.51076/)
How it works?
Player uses 'loot seller item' (ex. some rune - item that player can 'Use with...') on other item and if that other item is sellable, script removes it and gives player money.
It's easy to configure, as you can copy item lists from NPC. Items configuration is at bottom of script.

New features in 1.x version:
  • support for items list from .xml file
  • limit range to 1 SQM
  • block possibility to sell item in house, otherwise someone could sell item that lays in house doors
  • block possibility to sell items with actionID or uniqueID
data/actions.xml:
XML:
<action actionid="36282" event="script" value="other/item_seller.lua"/>
data/actions/scripts/other/item_seller.lua:
Lua:
-- rest of config (item prices) is under function, paste there your items list from npc
local config = {
    price_percent = 90, -- how many % of shop price player receive when sell by 'item seller'
    cash_to_bank = true -- send money to bank, not add to player BP
}

local shopItems = {}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if toPosition.x ~= 65535 and getDistanceBetween(player:getPosition(), toPosition) > 1 then
        player:sendTextMessage(MESSAGE_INFO_DESCR, 'This item is too far away.')
        return true
    end

    local targetTile = Tile(toPosition)
    if targetTile then
        -- this is to prevent selling item that lays in house doors
        local targetHouse = targetTile:getHouse()
        if targetHouse then
            -- this blocks only selling items laying on house floor
            -- if player open BP that lays on house floor, he can sell items inside it
            player:sendTextMessage(MESSAGE_INFO_DESCR, 'You cannot sell items in house.')
            return true
        end
    end

    local itemEx = Item(target.uid)
    if not itemEx then
        player:sendTextMessage(MESSAGE_INFO_DESCR, 'This is not an item.')
        return true
    end

    if itemEx:getUniqueId() < 65535 or itemEx:getActionId() > 0 then
        player:sendTextMessage(MESSAGE_INFO_DESCR, 'You cannot sell quest item.')
        return true
    end

    if not shopItems[itemEx.itemid] then
        player:sendTextMessage(MESSAGE_INFO_DESCR, 'This is not sellable item.')
        return true
    end

    local itemCount = 1
    local itemType = ItemType(itemEx.itemid)
    if itemType:isStackable() then
        itemCount = itemEx.type
    end

    local itemName = itemEx:getName()
    local itemValue = math.ceil(shopItems[itemEx.itemid] * itemCount / 100 * config.price_percent)
    if itemValue > 0 then
        itemEx:getPosition():sendMagicEffect(CONST_ME_GIFT_WRAPS)
        itemEx:remove()

        local message = 'You sold ' .. itemCount .. ' ' .. itemName .. ' for ' .. itemValue .. ' gold coins.'
        if config.cash_to_bank then
            player:setBankBalance(player:getBankBalance() + itemValue)
            message = message .. ' Money was added to your bank account.'
        else
            player:addMoney(itemValue)
        end
        player:sendTextMessage(MESSAGE_INFO_DESCR, message)
    else
        player:sendTextMessage(MESSAGE_INFO_DESCR, itemName .. ' is worthless.')
    end

    return true
end

local shopModule = {}
function shopModule:addBuyableItemContainer()
end
function shopModule:addBuyableItem()
end
function shopModule:addSellableItem(names, itemid, cost, realName)
    shopItems[itemid] = cost
end

function shopModule:parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        local i = 1
        local itemid = -1
        local cost = 0
        for temp in string.gmatch(item, "[^,]+") do
            if i == 2 then
                itemid = tonumber(temp)
            elseif i == 3 then
                cost = tonumber(temp)
            end
            i = i + 1
        end

        shopItems[itemid] = cost
    end
end

-- here paste list of items from NPC lua file
shopModule:addSellableItem({ 'poison arrow' }, 2545, 5, 'poison arrow')
shopModule:addSellableItem({ 'hota' }, 2342, 500, 'helmet of the ancients')

-- here paste list from .xml file
shopModule:parseList('crossbow,2455,150;bow,2456,130')
shopModule:parseList('knight armor, 2476, 10000')
 
Last edited:
Little optimization on parse func
Lua:
function shopModule:parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("(%d+).(%d+)$", function(itemid, cost)
            shopItems[itemid] = cost
        end)
    end
end

Nice script : )
 
Last edited:
Little optimization on parse func
Lua:
function parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("(%d+),(%d+)$", function(itemid, cost)
            shopItems[itemid] = cost
        end)
    end
end

Nice script : )
Nice. I just copied part of modules.lua from npc/lib.

NPC item lists in public datapacks have different formats. Some authors add space before/after ,.
How to make it handle spaces before and after ,? Like these 4 cases:
Code:
knight armor, 2476,10000
knight armor, 2476, 10000
knight armor, 2476 ,10000
knight armor, 2476 , 10000
I hope there are no items with number at end of name :p

Test code (you can run it on Lua: demo (https://www.lua.org/cgi-bin/demo) ):
Lua:
function parseListOld(data)
    for item in string.gmatch(data, "[^;]+") do
        local i = 1
        local itemid = -1
        local cost = 0
        for temp in string.gmatch(item, "[^,]+") do
            if i == 2 then
                itemid = tonumber(temp)
            elseif i == 3 then
                cost = tonumber(temp)
            end
            i = i + 1
        end
        shopItems[itemid] = cost
    end
end

function parseListNew(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("(%d+),(%d+)$", function(itemid, cost)
            shopItems[itemid] = cost
        end)
    end
end

shopItems = {}
parseListOld('a armor, 2476,10001');
parseListOld('b armor, 2477, 10002');
parseListOld('c armor, 2478 ,10003');
parseListOld('d armor, 2479 ,10004');
print('old')
for k, v in pairs(shopItems) do
    print(k, v)
end

shopItems = {}
parseListNew('a armor, 2476,10001');
parseListNew('b armor, 2477, 10002');
parseListNew('c armor, 2478 ,10003');
parseListNew('d armor, 2479 ,10004');
print('new')
for k, v in pairs(shopItems) do
    print(k, v)
end
 
Last edited:
Ahh you right, a weird way but this could be a workaround
Lua:
function shopModule:parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("[^,]+", "", 1):gsub("(%d+).-(%d+)", function(itemid, cost)
            shopItems[itemid] = cost
        end)
    end
end
 
Ahh you right, a weird way but this could be a workaround
Lua:
function shopModule:parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("[^,]+", "", 1):gsub("(%d+).-(%d+)", function(itemid, cost)
            shopItems[itemid] = cost
        end)
    end
end
Lua:
function shopModule:parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("[^,]+", "", 1):gsub("(%d+).-(%d+)", function(itemid, cost)
            if tonumber(itemid) > 0 and tonumber(cost) > 0 then
                shopItems[tonumber(itemid)] = tonumber(cost)
            end
        end)
    end
end
Added conversion to number. Somehow (%d+) returns string, not number.

Anyway, it still works wrong somehow. WTF.
New edge case "someone typed some string in place of cost":
Lua:
parseListNew('b armor, 2477, b armor');
returns:
Code:
itemid = 247
cost = 7
I must say I'm not a fan of regex. Results are hard to predict and in some cases it's very unefficient.
In simple cases like spliting by ; and , I prefere few loops and simple string split.
 
I think this is the one, instead of looking for numbers we better explode , as you mention and then we capture their content
Lua:
function shopModule:parseList(data)
    for item in string.gmatch(data, "[^;]+") do
        item:gsub("[^,]+", "", 1):gsub("([^,]+).-([^,]+)", function(itemid, cost)
            itemid = tonumber(itemid)
            if itemid then -- only true if itemid is number
                shopItems[itemid] = tonumber(cost) -- if cost is not a number, then new index wont be added to the list
            end
        end)
    end
end
 
Last edited:
Is it possible to change the type of money it gives you for a specific item used as a token?
 
hello havent tinkerd with a distro for a while totally forgot,
but can you fill in where i put the item id wich will be the shop
 
hello havent tinkerd with a distro for a while totally forgot,
but can you fill in where i put the item id wich will be the shop
It will be any item with ActionID set to 36282:
XML:
<action actionid="36282" event="script" value="other/item_seller.lua"/>
You can change it to:
XML:
<action itemid="2400" event="script" value="other/item_seller.lua"/>
to make item ID 2400 (Magic Sword) work as item seller.
 
Hello @Gesior.pl.

I tried to install it on my test server, however, when trying to use it on an item it just says "You cannot use this object". It doesn't give any errors, it just doesn't work.

I'm using TFS 1.5 downgrade from Nekiro to 7.72.

Can you help me?

Thanks!!
 
Hello @Gesior.pl.

I tried to install it on my test server, however, when trying to use it on an item it just says "You cannot use this object". It doesn't give any errors, it just doesn't work.

I'm using TFS 1.5 downgrade from Nekiro to 7.72.

Can you help me?

Thanks!!
Use what item ID on what item ID? "You cannot use this object" is engine error. It may be related to items.otb configuration of items or to invalid implementation of some items (ex. potions) on Nekiro downgrade.
 
Use what item ID on what item ID? "You cannot use this object" is engine error. It may be related to items.otb configuration of items or to invalid implementation of some items (ex. potions) on Nekiro downgrade.
It made perfect sense. I was trying with an item that did not have the correct assignments.

I tested it with a Magic Sword and it worked.

Taking advantage, do you think it would be difficult to put charges on this item?
 
do you think it would be difficult to put charges on this item?
Under:
Lua:
itemEx:remove()
add:
Lua:
local charges = item:getAttribute(ITEM_ATTRIBUTE_CHARGES)
if charges > 1 then
    item:setAttribute(ITEM_ATTRIBUTE_CHARGES, charges - 1)
else
    item:remove()
end
of course item must have charges attribute, if it does not have charges, it will be removed after first use.

Maybe item:remove(1) would work. On old engines it worked like that, removing 1 'count' of item removed 1 'charge' of item or whole item, if there was just 1 charge. I cannot find code responsible for that in TFS 1.x, so I've made version that remove charges attribute or whole item.
 
Under:
Lua:
itemEx:remove()
add:
Lua:
local charges = item:getAttribute(ITEM_ATTRIBUTE_CHARGES)
if charges > 1 then
    item:setAttribute(ITEM_ATTRIBUTE_CHARGES, charges - 1)
else
    item:remove()
end
of course item must have charges attribute, if it does not have charges, it will be removed after first use.

Maybe item:remove(1) would work. On old engines it worked like that, removing 1 'count' of item removed 1 'charge' of item or whole item, if there was just 1 charge. I cannot find code responsible for that in TFS 1.x, so I've made version that remove charges attribute or whole item.

Worked perfectly!

Thank you so much GOD GESIOR!
 
@Gesior.pl

Would it be possible to modify it so that instead of having to 'Use with" on the loot, when using the item, you can scroll through all the items configured inside the character's backpack and sell them automatically?

For example I have with me 10 boh, 5 demon shield and 5 fire axe, would I sell all these 20 items at once?
 
@Gesior.pl

Would it be possible to modify it so that instead of having to 'Use with" on the loot, when using the item, you can scroll through all the items configured inside the character's backpack and sell them automatically?

For example I have with me 10 boh, 5 demon shield and 5 fire axe, would I sell all these 20 items at once?
Like click 1 BoH and it sells all BoHs in same BP? or all in all player's BPs? or sell all sellable items from BP?
For 1 BP - often 20 slots - it's fine, but running it on 'all BPs in BPs' (all player's BPs) would lag OTS, if some player wear ex. 2000 items.
 
Back
Top