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

Solved [TFS 1.2] Transfering items from opened container to player

guiismiti

Well-Known Member
Joined
May 19, 2014
Messages
315
Solutions
3
Reaction score
68
Hi again,

This should be simple, but I've been stuck for a while now.
Like I said in another post a few days ago, I'm using depot 34 to send loots, for practicality - I'm too lazy to loot dead stuff every single time I kill it :)
Since I'm even more lazy than that, I created a talkaction "!loot" - if you stand in front of a loot depot, it will pick all stuff you can carry (limited by space or cap).
What it should do:
  • Scan the loot depot and create a table {{id = item id, count = item count}};
  • Add the items in the table to the player, and remove it from depot as soon as they are created in the table.

So, I have used the search function and I found this for scanning the depot
https://otland.net/threads/function-get-all-items-by-id-get-all-items-in-slot-by-id.171981/

And this for adding the items in backpacks
https://otland.net/threads/addcontaineritems-container-items.166104/

I have also taken a look at doNpcSellItem in npc.lua, but that's not what I'm using.

I adjusted the scan function to match my needs (code below).

Here is the code
Code:
function onSay(player, words, param)
    local lookPos = player:getPosition()
    lookPos:getNextPosition(player:getDirection())
    local depotItem = lookPos:getTile():getItemByType(ITEM_TYPE_DEPOT)
  
    if depotItem ~= nil then
        local depot = player:getDepotChest(34, true)
      
        if not depot then
            return true
        end
      
        local items = getDepotItems(depot)
      
        local container = doCreateItemEx(1988, 1)
        container = addContainerItems(getThing(container), items)
        doPlayerAddItemEx(player, container, true)
      
        for i = 1, #items do
            print(items[i].id, " count ", items[i].count)
        end
    else
        player:say('Stand in front of an opened loot depot.', TALKTYPE_MONSTER_SAY, false, player)
    end
  
    return true
end

function getDepotItems(container)
    local containers = {} --a table for this function only, stores the containers inside the investigated container
    local items = {} --a table that stores all the items, both from the investigated container and the containers inside it
    local itcount = 1
  
    local sitem = container
  
    if sitem.uid > 0 then
        if isContainer(sitem.uid) then
            table.insert(containers, sitem.uid)
        elseif not(id) or id == sitem.itemid then
            if sitem.type > 0 then
                itcount = sitem.type
            else
                itcount = 1
            end
          
            table.insert(items, {id = sitem.itemid, count = itcount})
        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(id) or id == tmp.itemid then
                if tmp.type > 0 then
                    itcount = tmp.type
                else
                    itcount = 1
                end
              
                table.insert(items, {id = tmp.itemid, count = itcount})
            end
        end
      
        table.remove(containers, 1)
    end
  
    return items --returns all the items + count from the investigated container except for containers. Includes the items inside these containers
end

function addContainerItems(container, items)
    local items_mod = {}
  
    for _, it in ipairs(items) do
        if(it.count > 100) then
            local c = it.count
          
            while(c > 100) do
                table.insert(items_mod, {id = it.id, count = 100})
                c = c - 100
            end
          
            if(c > 0) then
                table.insert(items_mod, {id = it.id, count = c})
            end
        else
            table.insert(items_mod, {id = it.id, count = 1})
        end
    end
  
    local free = getContainerCap(container.uid) - (getContainerSize(container.uid))
    local count = math.ceil(#items_mod/ free)
    local main_bp = container.uid
    local insert_bp = main_bp
    local counter = 1
  
    for c, it in ipairs(items_mod) do
        local _c = (it.count > 100 and 100 or it.count) or 1
      
        if count > 1 then
            if (counter < free) then
                doAddContainerItem(insert_bp, it.id, _c)
            else
                insert_bp = doAddContainerItem(insert_bp, container.itemid, 1)
                count = (#items_mod) - (free - 1)
                free = getContainerCap(insert_bp)
                count = math.ceil(count/ free)
                doAddContainerItem(insert_bp, it.id, _c)
                counter = 1
            end
          
            counter = counter + 1
        else
      
        doAddContainerItem(insert_bp, it.id, _c)
        end
    end
  
    return main_bp
end

Here are the problems I have so far (I'm stuck...)
  • There are functions that aren't being recognized (maybe because this is a talkaction script and the other scripts are not). removeItem(id, amount) and isItemStackable are not being recognized. I don't need isItemStackable anymore, I worked it around;
  • I'm not sure removeItem is the right function to remove an item from the depot after it's been added to the player, so, the items are not being removed yet. I need to know which function to use.
  • When using the talkaction, the player gets a backpack with 1x of all the items listed - the count of the stackable items is not being considered.
Can anybody help me with that?
Thanks in advance.
 
doRemoveItem, not removeItem also depends on the source used

I added a new parameter to addContainerItems, the depot.
So, after adding the item to the container, it should be removing it from the depot. Here is the part of the function that has been changed.
Code:
        if count > 1 then
            if (counter < free) then
                doAddContainerItem(insert_bp, it.id, _c)
                removeditem = depot:getItemById(it.id)
                doRemoveItem(removeditem, _c)
            else
                insert_bp = doAddContainerItem(insert_bp, container.itemid, 1)
                count = (#items_mod) - (free - 1)
                free = getContainerCap(insert_bp)
                count = math.ceil(count/ free)
                doAddContainerItem(insert_bp, it.id, _c)
                removeditem = depot:getItemById(it.id)
                doRemoveItem(removeditem, _c)
                counter = 1
            end
          
            counter = counter + 1
        else
      
        doAddContainerItem(insert_bp, it.id, _c)
        removeditem = depot:getItemById(it.id)
        doRemoveItem(removeditem, _c)
        end

I added this right after the item is added to the container
Code:
                removeditem = depot:getItemById(it.id)
                doRemoveItem(removeditem, _c)

But getItemById is returning nil...

I'm thinking of just adding a lever between the depot chests and implement this thing as an action, not a talkaction.
 
I forgot to mention that I'm using TFS 1.2 (it's in the title now)
Those functions that you're trying to use are not for TFS 1.2.

Edit: Try this:

Code:
function Container.getItems(self)
    local ret = {}
    local size = self:getSize()
    for i = 0, size-1 do
        local item = self:getItem(i)
        table.insert(ret, item)
    end
    return ret
end

function onSay(player, words, param)
    local lookPos = player:getPosition()
    lookPos:getNextPosition(player:getDirection())
    local depotItem = Tile(lookPos):getItemByType(ITEM_TYPE_DEPOT)

    if depotItem ~= nil then
        local depot = player:getDepotChest(34, true)
        local items = depot:getItems()
        local itemstoremove = {}
        if #items > 0 then
            local container = Game.createItem(1988)
            for slot = 1, container:getCapacity() do
                if not items[slot] then
                    break
                else
                    local cloneditem = items[slot]:clone()
                    local add = container:addItemEx(cloneditem)
                    if add == RETURNVALUE_NOERROR then
                        table.insert(itemstoremove, items[slot])
                    end
                end
            end
            local add = player:addItemEx(container, false)
            if add == RETURNVALUE_NOERROR then
                for i = 1, #itemstoremove do
                    itemstoremove[i]:remove()
                end
            else
                player:say(Game.getReturnMessage(add), TALKTYPE_MONSTER_SAY, false, player)
            end
        else
            player:say("Your depot is empty.", TALKTYPE_MONSTER_SAY, false, player)
        end
    else
        player:say('Stand in front of an opened loot depot.', TALKTYPE_MONSTER_SAY, false, player)
    end
    return false
end
 
Last edited:
Those functions that you're trying to use are not for TFS 1.2.

I'm using actions now, it's more practical.

So, I'm still editing the function, and it will
  • Scan the depot using the same function I posted, adding all items + count to a table;
  • Add items the player can carry to a backpack and give it to the player;
  • Scan the backpack and delete those same items from the depot.

What I still need
  • The items to be added to the backpack, with the right count;
Can anybody help?

By the way, it looks like the problem I was having with the count of the items being added to the backpack was caused by the first loop in addContainerItems
 
I'm using actions now, it's more practical.

So, I'm still editing the function, and it will
  • Scan the depot using the same function I posted, adding all items + count to a table;
  • Add items the player can carry to a backpack and give it to the player;
  • Scan the backpack and delete those same items from the depot.

What I still need
  • The items to be added to the backpack, with the right count;
  • A function to remove an item from a container, compatible with TFS 1.2.

Can anybody help?
Check the code that I put in the previous post its working exactly as you wanted. Anyway if you want it to be an action just change it to onUse, it will work as well.
 
It is working, and it's very simple.

The only detail (I'm trying to fix now) is that, if the weight of everything inside the depot > player cap, it won't pick anything.
I'll just get the player cap after using the lever and, in the items table, will use a variable for the cumulative weight of the stuff + backpack and stop the loop when it exceeds the player's cap.
 
It is working, and it's very simple.

The only detail (I'm trying to fix now) is that, if the weight of everything inside the depot > player cap, it won't pick anything.
I'll just get the player cap after using the lever and, in the items table, will use a variable for the cumulative weight of the stuff + backpack and stop the loop when it exceeds the player's cap.
Code:
function Container.getItems(self)
    local ret = {}
    local size = self:getSize()
    for i = 0, size-1 do
        local item = self:getItem(i)
        table.insert(ret, item)
    end
    return ret
end

function onSay(player, words, param)
    local lookPos = player:getPosition()
    lookPos:getNextPosition(player:getDirection())
    local depotItem = Tile(lookPos):getItemByType(ITEM_TYPE_DEPOT)

    if depotItem ~= nil then
        local depot = player:getDepotChest(34, true)
        local items = depot:getItems()
        local itemstoremove = {}
        if #items > 0 then
            local playercap = player:getFreeCapacity()
            local totalcap = 0
            local container = Game.createItem(1988)
            local maincontainer = container
            for slot = 1, #items do
                if not items[slot] then
                    break
                else
                    if items[slot]:getWeight()+totalcap >  playercap then
                        break
                    end
                    totalcap = totalcap + items[slot]:getWeight()
                    if container:getCapacity() == container:getSize()+1 then
                        container = container:addItem(1988)
                    end
                    local cloneditem = items[slot]:clone()
                    local add = container:addItemEx(cloneditem)
                    if add == RETURNVALUE_NOERROR then
                        table.insert(itemstoremove, items[slot])
                    end
                end
            end
            local add = player:addItemEx(maincontainer, false)
            if add == RETURNVALUE_NOERROR then
                for i = 1, #itemstoremove do
                    itemstoremove[i]:remove()
                end
            else
                player:say(Game.getReturnMessage(add), TALKTYPE_MONSTER_SAY, false, player)
            end
        else
            player:say("Your depot is empty.", TALKTYPE_MONSTER_SAY, false, player)
        end
    else
        player:say('Stand in front of an opened loot depot.', TALKTYPE_MONSTER_SAY, false, player)
    end
    return false
end

Also now it adds multiple backpacks if there are more than 20 items.

Obs: This is not calculating the weight of the backpacks so its easier for you if you make the backpack 1988 weightless
 
I'm getting "the depot is empty" with the new code.

But I edited the old one, it's pretty good. Added the transformation for the lever, blue sparks on player if successfully used, and the cap thing I made worked.

Thanks :)
 
I'm getting "the depot is empty" with the new code.

But I edited the old one, it's pretty good. Added the transformation for the lever, blue sparks on player if successfully used, and the cap thing I made worked.

Thanks :)
Share the solution so that it may help others with similar problems.
 
I'm getting "the depot is empty" with the new code.

But I edited the old one, it's pretty good. Added the transformation for the lever, blue sparks on player if successfully used, and the cap thing I made worked.

Thanks :)
Check the depot id if its 34 I pasted with id 1 and after edited it to 34, also I've added the backpack weight so now it should work properly:

Code:
function Container.getItems(self)
    local ret = {}
    local size = self:getSize()
    for i = 0, size-1 do
        local item = self:getItem(i)
        table.insert(ret, item)
    end
    return ret
end

function onSay(player, words, param)
    local lookPos = player:getPosition()
    lookPos:getNextPosition(player:getDirection())
    local depotItem = Tile(lookPos):getItemByType(ITEM_TYPE_DEPOT)

    if depotItem ~= nil then
        local depot = player:getDepotChest(34, true)
        local items = depot:getItems()
        local itemstoremove = {}
        if #items > 0 then
            local playercap = player:getFreeCapacity()
            local totalcap = 0
            local container = Game.createItem(1988)
            local backpackweight = container:getWeight()
            local maincontainer = container

            totalcap = totalcap+backpackweight
            for slot = 1, #items do
                if not items[slot] then
                    break
                else
                    if items[slot]:getWeight()+totalcap >  playercap then
                        break
                    end
                    totalcap = totalcap + items[slot]:getWeight()
                    if container:getCapacity() == container:getSize()+1 then
                        if backpackweight+totalcap > playercap then
                            break
                        end
                        container = container:addItem(1988)
                    end
                    local cloneditem = items[slot]:clone()
                    local add = container:addItemEx(cloneditem)
                    if add == RETURNVALUE_NOERROR then
                        table.insert(itemstoremove, items[slot])
                    end
                end
            end
            if #itemstoremove > 0 then
                local add = player:addItemEx(maincontainer, false)
                if add == RETURNVALUE_NOERROR then
                    for i = 1, #itemstoremove do
                        itemstoremove[i]:remove()
                    end
                else
                    player:say(Game.getReturnMessage(add), TALKTYPE_MONSTER_SAY, false, player)
                end
            else
                player:say(Game.getReturnMessage(RETURNVALUE_NOTENOUGHCAPACITY), TALKTYPE_MONSTER_SAY, false, player)
            end
        else
            player:say("Your depot is empty.", TALKTYPE_MONSTER_SAY, false, player)
        end
    else
        player:say('Stand in front of an opened loot depot.', TALKTYPE_MONSTER_SAY, false, player)
    end
    return false
end
 
Just a quick fix - the last code is missing this
Code:
totalcap = totalcap+backpackweight
just after this
Code:
container = container:addItem(1988)
 
Back
Top