• 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+ Add rings from quest rewards to bag instead of ring slot

Dawg

Member
Joined
Mar 23, 2014
Messages
180
Reaction score
22
I'm trying to figure out how to make it so that when a player receives a ring from a reward chest, the ring goes straight to the backpack even if the player's ring slot is open. With TFS 1.2, if the player has no ring on, and then they open a quest chest that gives a ring, the ring goes straight to the player's ring slot and starts the expire timer.

Obviously this is not ideal, and besides, in RL this does not happen. See here:
A guy has no ring on and he does the banshee quest. When he opens the top right chest and gets the stealth ring, it goes to his bag, not his ring slot.

I also try some other distros, like nostalrius, and it also has the same problem. The game tries to dress the ring in your ring slot.

I see that the quest system adds the item with playerAddItemEx, and i see in the source code (luascript.cpp) that playerAddItemEx goes through all slots, but I don't understand how to exclude rings from this.

I searched and i have not seen anyone else reporting this issue, not sure if it's easy to fix and I'm just missing it or if the solution is already out there somwhere, or if I need to be using another function. thanks in advance!
 
I was also trying to figure this out, since I am building a unique ring system, I want gained rings from chest or NPC given to backpack instead of a slot.
Since I also have a binding script, which binds Rings on equip, and also unable to remove it from eq upon equip.

With using TFS 1.5, the ring goes directly into ring slot, without binding, without able to unequip....
Had to work around with scripting but not very permanent solution in the end :/
 
Yes, I was surprised that this problem exists when TFS has existed for so many years, hopefully someone with more knowledge will point us in the right direction. I know there are more people than just us who would like to see this problem fixed.
 
that is actually a really simple "issue" that can be solved easily by using the optional parameters of the addItem/addItemEx commands:

Lua:
// player:addItemEx(item[, canDropOnMap = false[, index = INDEX_WHEREEVER[, flags = 0]]])
// player:addItemEx(item[, canDropOnMap = true[, slot = CONST_SLOT_WHEREEVER]])
// player:addItem(itemId[, count = 1[, canDropOnMap = true[, subType = 1[, slot = CONST_SLOT_WHEREEVER]]]])
 
I've never tried, but I think @Evil Puncker is saying we can use CONST_SLOT_BACKPACK to force it into the backpack slot, and it won't go into the ring slot then?
 
I had actually already tried forcing it into the backpack slot by messing with the chests.lua script, checking to see if the item was ring and then forcing it into backpack like @Evil Puncker said, but I got this error whenever I tried to do that:


Lua:
Lua Script Error: [Action Interface]
data/actions/scripts/misc/chests.lua:onUse
luaPlayerAddItemEx(). Item already has a parent
stack traceback:
    [C]: in function 'addItemEx'
    data/actions/scripts/misc/chests.lua:58: in function <data/actions/scripts/misc/chests.lua:1>

Good to know I was sort of on the right track, but maybe I was getting the item attributes wrong? I was using ItemType:getSlotPosition(itemId)
 
I had actually already tried forcing it into the backpack slot by messing with the chests.lua script, checking to see if the item was ring and then forcing it into backpack like @Evil Puncker said, but I got this error whenever I tried to do that:


Lua:
Lua Script Error: [Action Interface]
data/actions/scripts/misc/chests.lua:onUse
luaPlayerAddItemEx(). Item already has a parent
stack traceback:
    [C]: in function 'addItemEx'
    data/actions/scripts/misc/chests.lua:58: in function <data/actions/scripts/misc/chests.lua:1>

Good to know I was sort of on the right track, but maybe I was getting the item attributes wrong? I was using ItemType:getSlotPosition(itemId)
Something like that
Lua:
player:addItemEx(item, false, CONST_SLOT_BACKPACK)
 
Code:
// player:addItem(itemId[, count = 1[, canDropOnMap = true[, subType = 1[, slot = CONST_SLOT_WHEREEVER]]]])
Lua:
if player:addItem(2160, 1, true, 1, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
    --handle exception
end


C++:
//Game::internalPlayerAddItem
if (ret != RETURNVALUE_NOERROR && dropOnMap) {
    ret = internalAddItem(player->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT);
}
Best to pass true to canDropOnMap, as it will place it underneath the player if their backpack is full/no cap. But you can still handle the exception if neither backpack or tile is successful on the query add
 
Last edited:
Is there a cleaner way to do that without having to manually create a table with all ring item IDs? When I try to print the value for ItemType:getSlotPosition(life_ring_ID) for example, I get a print of 560. I understand that this is from the SLOTP_HEAD, SLOTP_RING, etc. bit enums, but I get the same value (560) for other items as well, so I must be doing something wrong with the ItemType function?
 
Is there a cleaner way to do that without having to manually create a table with all ring item IDs? When I try to print the value for ItemType:getSlotPosition(life_ring_ID) for example, I get a print of 560. I understand that this is from the SLOTP_HEAD, SLOTP_RING, etc. bit enums, but I get the same value (560) for other items as well, so I must be doing something wrong with the ItemType function?

Lua:
local itemtype = item:getType() --or local itemtype = ItemType(itemid)
if itemtype and itemtype:getSlotPosition() == SLOTP_RING then
    if player:addItem(2160, 1, true, 1, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
        --handle exception
    end
else
    if player:addItem(2160, 1) ~= RETURNVALUE_NOERROR then
        --handle exception
    end
end
or(same thing but better)
Lua:
local itemtype = item:getType()
local slot = itemtype:getSlotPosition() == SLOTP_RING and CONST_SLOT_BACKPACK or CONST_SLOT_WHEREEVER
if player:addItem(2160, 1, true, 1, slot) ~= RETURNVALUE_NOERROR then
    --handle exception
end
 
Last edited:
Well I'm not sure what the issue is but the conditional
Lua:
if itemtype and itemtype:getSlotPosition() == SLOTP_RING then
must not work with my distro or im doing something wrong. I added a print immediately after
Code:
if itemtype and itemtype:getSlotPosition() == SLOTP_RING then
        print("its a ring")
but that print never shows. I tried a different approach, and at least im getting into the conditional:
Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
        print("its a decayable")
There are other decayable things besides rings (like soft boots) so I figure might as well use that attribute. Anyway that works, but then something else goes wrong, here is the code:

Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
    print("its a decayable")
    if player:addItem(reward:clone(), 1, true, 1, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
        print("there was an error")
    end
else
    player:addItemEx(reward:clone(), true)
end
Here are the prints when I open a chest with a stealth ring:

Code:
its a decayable
there was an error
is there a way to check the returnvalue in lua?
~~~~~~~~EDIT~~~~~~~~~~~
I was using addItem instead of addItemEx, that fixed the lack of returnvalue_noerror, but something still isn't right.

Code with more prints:

Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
    print("its a decayable")
    if player:addItemEx(reward:clone(), 1, true, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
        print("there was an error")
    else
        print("its adding the item to the backpack")
    end
else
    print("its adding the item the old way")
    player:addItemEx(reward:clone(), true)
end
When opening a chest with the ring, I get these prints:

Code:
its a decayable
its adding the item to the backpack

That looks good, but the ring is still going right into the ring slot. And yes my character has an empty backpack in the backpack slot.
 
Last edited:
Well I'm not sure what the issue is but the conditional
Lua:
if itemtype and itemtype:getSlotPosition() == SLOTP_RING then
must not work with my distro or im doing something wrong. I added a print immediately after
Code:
if itemtype and itemtype:getSlotPosition() == SLOTP_RING then
        print("its a ring")
but that print never shows. I tried a different approach, and at least im getting into the conditional:
Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
        print("its a decayable")
There are other decayable things besides rings (like soft boots) so I figure might as well use that attribute. Anyway that works, but then something else goes wrong, here is the code:

Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
    print("its a decayable")
    if player:addItem(reward:clone(), 1, true, 1, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
        print("there was an error")
    end
else
    player:addItemEx(reward:clone(), true)
end
Here are the prints when I open a chest with a stealth ring:

Code:
its a decayable
there was an error
is there a way to check the returnvalue in lua?
~~~~~~~~EDIT~~~~~~~~~~~
I was using addItem instead of addItemEx, that fixed the lack of returnvalue_noerror, but something still isn't right.

Code with more prints:

Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
    print("its a decayable")
    if player:addItemEx(reward:clone(), 1, true, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
        print("there was an error")
    else
        print("its adding the item to the backpack")
    end
else
    print("its adding the item the old way")
    player:addItemEx(reward:clone(), true)
end
When opening a chest with the ring, I get these prints:

Code:
its a decayable
its adding the item to the backpack

That looks good, but the ring is still going right into the ring slot. And yes my character has an empty backpack in the backpack slot.
Lua:
if player:addItemEx(reward:clone(), 1, true, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
This isnt working because addItemEx takes the parameters (item, canDropOnMap, slot), so its firing the else.

Overall, could be a flag issue.

If you print the below code, what is printed?
Lua:
player:addItem(2160, 1, true, 1, CONST_SLOT_BACKPACK)
 
Printing that code yields:
Code:
userdata: 0x41362f08

Also, I finally got the desired behavior with this:

Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
        print("its a decayable")
        player:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK)
        print("its adding the item to the backpack")
else
    print("its adding the item the old way")
    player:addItemEx(reward:clone(), true)
end
It works fine, but it doesnt have any error handling
 
nice, to be honest this is better than just checking for a ring anyway, as this will cover amulets too. However, you should still check the return value of the addItemEx and handle what happens if it fails.
 
nice, to be honest this is better than just checking for a ring anyway, as this will cover amulets too. However, you should still check the return value of the addItemEx and handle what happens if it fails.
Also covers soft boots, etc.

For fail handling, something like this? Just send a warning message or should i be doing something else? Thanks so much for the help.

Lua:
if itemtype and itemtype:getTransformEquipId() ~= nil then
       print("its a decayable")
       if player:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK) ~= RETURNVALUE_NOERROR then
               player:sendTextMessage(MESSAGE_INFO_DESCR, "The chest is bugged.")
        end
else
    print("its adding the item the old way")
    player:addItemEx(reward:clone(), true)
end
 
This is better and does the same thing
Lua:
local slotPosition = (itemtype and itemtype:getTransformEquipId() ~= nil) and CONST_SLOT_BACKPACK or CONST_SLOT_WHEREEVER
if player:addItemEx(reward:clone(), true, slotPosition) ~= RETURNVALUE_NOERROR then
    player:sendTextMessage(MESSAGE_INFO_DESCR, "The chest is bugged.")
end
 
A couple of things i discovered:
1) nil actually does not work here, you have to use 0. I tested with prints when getting a quest chest for a non-expirable, and it actually prints 0, not nil.
2) This doesn't take into account searching recursively for empty backpacks. For example: if I have a backpack and 19 of the slots are full of gold coins or whatever, and the 20th slot has an empty backpack in it, and I open a quest chest with a ring, it will not add the ring to the empty backpack inside my main backpack, it will just drop it on the floor.

We're certainly making progress, but we need to do some sort of lua function to search slot_backpack for the next empty space? I don't know how to do that unfortunately.
 
Last edited:
A couple of things i discovered:
1) nil actually does not work here, you have to use 0. I tested with prints when getting a quest chest for a non-expirable, and it actually prints 0, not nil.
2) This doesn't take into account searching recursively for empty backpacks. For example: if I have a backpack and 19 of the slots are full of gold coins or whatever, and the 20th slot has an empty backpack in it, and I open a quest chest with a ring, it will not add the ring to the empty backpack inside my main backpack, it will just drop it on the floor.

We're certainly making progress, but we need to do some sort of lua function to search slot_backpack for the next empty space? I don't know how to do that unfortunately.
try including the recursive flag:
Lua:
if player:addItemEx(reward:clone(), true, slotPosition, FLAG_RECURSIVE) ~= RETURNVALUE_NOERROR then

or perhaps best to do something like this: (lets call recursive on the container instead)
Lua:
local added = false
local useBackpack = (itemtype and itemtype:getTransformEquipId() > 0) and true or false
if useBackpack then
    local backpack = player:getSlotItem(CONST_SLOT_BACKPACK)
    if backpack and backpack:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK, FLAG_RECURSIVE) == RETURNVALUE_NOERROR then
        added = true
    end
end

if not added and player:addItemEx(reward:clone(), true) ~= RETURNVALUE_NOERROR then
    player:sendTextMessage(MESSAGE_INFO_DESCR, "The chest is bugged.")
end
 
Last edited:
Wow nice that recursive flag is certainly helpful, ive been struggling to do the same thing with lua. Again, making progress.

With this code you shared, it successfully opens backpacks within backpacks and adds the item (yay!). However, if the player didn't have a backpack (added = false) it defaulted to the default of CONST_SLOT_WHEREEVER and put the ring on the slot.

I made these changes, im sure its not the most nice-looking or efficient code, but it got rid of that first problem:

Lua:
local added = false
local useBackpack = (itemtype and itemtype:getTransformEquipId() > 0) and true or false
if useBackpack then
    print("expiring item, its supposed to only go into the backpack")
    local backpack = player:getSlotItem(CONST_SLOT_BACKPACK)
    if backpack and backpack:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK, FLAG_RECURSIVE) == RETURNVALUE_NOERROR then
        print("added to the backpack, first empty slot")
        added = true
    elseif player:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK) == RETURNVALUE_NOERROR then
        print("player doesnt have a backpack, added to the floor")
        print(added)
    end
else
    print("not an expiring item, can send to whereever or ground")
    player:addItemEx(reward:clone(), true)
end
This is even closer to correct, but there is still a problem: If I have a backpack in my backpack slot, and it is all full of 20x items, this code will still force the ring inside, and now the backpack has 21 items in it. Just like how some boss monsters' corpse (like ferumbras) only has 8 spots, but they can drop like 20+ items, you have to take some items out and more are revealed.

Is there a check to prevent this? Tysm!
 
Wow nice that recursive flag is certainly helpful, ive been struggling to do the same thing with lua. Again, making progress.

With this code you shared, it successfully opens backpacks within backpacks and adds the item (yay!). However, if the player didn't have a backpack (added = false) it defaulted to the default of CONST_SLOT_WHEREEVER and put the ring on the slot.

I made these changes, im sure its not the most nice-looking or efficient code, but it got rid of that first problem:

Lua:
local added = false
local useBackpack = (itemtype and itemtype:getTransformEquipId() > 0) and true or false
if useBackpack then
    print("expiring item, its supposed to only go into the backpack")
    local backpack = player:getSlotItem(CONST_SLOT_BACKPACK)
    if backpack and backpack:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK, FLAG_RECURSIVE) == RETURNVALUE_NOERROR then
        print("added to the backpack, first empty slot")
        added = true
    elseif player:addItemEx(reward:clone(), true, CONST_SLOT_BACKPACK) == RETURNVALUE_NOERROR then
        print("player doesnt have a backpack, added to the floor")
        print(added)
    end
else
    print("not an expiring item, can send to whereever or ground")
    player:addItemEx(reward:clone(), true)
end
This is even closer to correct, but there is still a problem: If I have a backpack in my backpack slot, and it is all full of 20x items, this code will still force the ring inside, and now the backpack has 21 items in it. Just like how some boss monsters' corpse (like ferumbras) only has 8 spots, but they can drop like 20+ items, you have to take some items out and more are revealed.

Is there a check to prevent this? Tysm!
yes, you can just get the empty slot count.

Lua:
if container:getEmptySlots() > 0 then
    --has room
end
 
Back
Top