• 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+ getTopTopItem ignores corpses

overdriven

Active Member
Joined
Mar 10, 2020
Messages
70
Solutions
1
Reaction score
42
If I have a corpse and a pool of blood below it, getTopTopItem will get the pool of blood instead corpse.

Lua:
player:say(tostring(tile:getTopTopItem():getType():getId()), TALKTYPE_MONSTER_SAY) -- returns 2016 (splash item)

I can move the corpse elsewhere, put it back on top of blood and getTopTopItem will still return pool of blood completely ignoring the corpse.

Why is it not returning the real item on top - the corpse?

Btw. what's the reason behind this silly function name "get Top Top"?

I'm using TFS 1.5.
Post automatically merged:

I see now that getTopDownItem will get the corpse, but I have no idea what "TopDown" and "TopTop" means, it's counter-intuitive to be honest, I wish docs would explain what's the difference between these 2 methods.
 
Last edited:
If I have a corpse and a pool of blood below it, getTopTopItem will get the pool of blood instead corpse.

Lua:
player:say(tostring(tile:getTopTopItem():getType():getId()), TALKTYPE_MONSTER_SAY) -- returns 2016 (splash item)

I can move the corpse elsewhere, put it back on top of blood and getTopTopItem will still return pool of blood completely ignoring the corpse.

Why is it not returning the real item on top - the corpse?

Btw. what's the reason behind this silly function name "get Top Top"?

I'm using TFS 1.5.
Post automatically merged:

I see now that getTopDownItem will get the corpse, but I have no idea what "TopDown" and "TopTop" means, it's counter-intuitive to be honest, I wish docs would explain what's the difference between these 2 methods.
As Gesior said before:

  • TopTop - top item of top items, it's top from alwaysOnTop attribute from Tibia Client
  • TopDown - top item of down items, items that are not alwaysOnTop, so they go down (below top items) and disappear when you add more items to tile
 
How to get the current item? For example when a player moves an item covered by other item (for older tibia versions).
We should have a function like:
C++:
        iterator getCurrentItem() {
            return func();
        }
        const_iterator getCurrentItem() const {
            return func();
        }
So I could change in stackpos_move the function getTopDownItem to getCurrentItem:
C++:
case STACKPOS_MOVE: {
                //Item* item = tile->getTopDownItem();
                Item* item = tile->getCurrentItem();
                if (item && item->isMoveable()) {
                    thing = item;
                } else {
                    thing = tile->getTopVisibleCreature(player);
                }
                break;
            }
This happens when clientID != spriteID (when item is covered by an another one):
C++:
if (item->getClientID() != spriteId) {
        //player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
        //return;
    }
The GIF below shows that the Demon Shield has been moved instead of the Giant Sword, because it always moves the first thing on top from function getTopDownItem:
up

Why laughing instead of atleast just giving me a hint? @Nekiro :(
 
Last edited:
How to get the current item? For example when a player moves an item covered by other item (for older tibia versions).
We should have a function like:
C++:
        iterator getCurrentItem() {
            return func();
        }
        const_iterator getCurrentItem() const {
            return func();
        }
So I could change in stackpos_move the function getTopDownItem to getCurrentItem:
C++:
case STACKPOS_MOVE: {
                //Item* item = tile->getTopDownItem();
                Item* item = tile->getCurrentItem();
                if (item && item->isMoveable()) {
                    thing = item;
                } else {
                    thing = tile->getTopVisibleCreature(player);
                }
                break;
            }
This happens when clientID != spriteID (when item is covered by an another one):
C++:
if (item->getClientID() != spriteId) {
        //player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
        //return;
    }
The GIF below shows that the Demon Shield has been moved instead of the Giant Sword, because it always moves the first thing on top from function getTopDownItem:
up
 
@btc Be clear with what you want to achieve.
As I can see on GIF you're trying to move sword before shield get's placed on top of it.

So you're trying to make it work like on 7.x retro protocols. You've commented out logic that is responsible for checking if item that's placed on such stackpos at tile is actually same item as player moved. I don't like that as it's simply moving logic to client and make it source of truth (which is ... ehmm.. not best idea as someone can modify it and sent whatever throw opcode's he likes). But it can be also seen as cool feature which alow you to get your item as long as you "hold it in your hands", debatable.

What you need is to rework the logic a bit to look for a item with specified spriteId and make it your item (maybe there's some better way but simply try it first and see)
 
@btc Be clear with what you want to achieve.
As I can see on GIF you're trying to move sword before shield get's placed on top of it.

So you're trying to make it work like on 7.x retro protocols. You've commented out logic that is responsible for checking if item that's placed on such stackpos at tile is actually same item as player moved. I don't like that as it's simply moving logic to client and make it source of truth (which is ... ehmm.. not best idea as someone can modify it and sent whatever throw opcode's he likes). But it can be also seen as cool feature which alow you to get your item as long as you "hold it in your hands", debatable.

What you need is to rework the logic a bit to look for a item with specified spriteId and make it your item (maybe there's some better way but simply try it first and see)
What about reworking the logic for checking the item with specified stackpos and then compare it to the itemCount starting from the bottom where 0 is ground and 1 is first item on the ground? So if stackpos 1 == itemCount 1 then move item with itemCount 1
 
@btc Well maybe something like this, I didn't tested it troughly (just POC):
C++:
Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index, uint32_t spriteId, stackPosType_t type) const
{
    ...
        switch (type) {
            case STACKPOS_MOVE: {
                const TileItemVector* items = tile->getItemList();
                auto movedItem = std::find_if(items->begin(), items->end(), [&spriteId](const Item* item) {
                    return item->getClientID() == spriteId;
                });
                Item* item = movedItem != items->end() ? *movedItem : nullptr;
                ...
                break;
            }
        }
    ...
}

Was it supposed to work like that (create mace under player while holding jacket and throwing jacket away)?:

Animation.gif
 
Last edited:
@btc Well maybe something like this, I didn't tested it troughly (just POC):
C++:
Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index, uint32_t spriteId, stackPosType_t type) const
{
    ...
        switch (type) {
            case STACKPOS_MOVE: {
                const TileItemVector* items = tile->getItemList();
                auto movedItem = std::find_if(items->begin(), items->end(), [&spriteId](const Item* item) {
                    return item->getClientID() == spriteId;
                });
                Item* item = movedItem != items->end() ? *movedItem : nullptr;
                ...
                break;
            }
        }
    ...
}

Was it supposed to work like that (create mace under player while holding jacket and throwing jacket away)?:

View attachment 73985
Your code searches for an Item in the items container that has a specific client ID (spriteId). If it finds a matching item, it returns an iterator that points to that item. If it does not find a matching item, it returns the items->end() iterator. This is how it is supposed to work, yes.
However it is not working, because in "playerMoveThing" function of the "Game" class:
C++:
void Game::playerMoveThing(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
The function "internalGetThing" returns:
C++:
Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
    if (!thing) {
        player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
        return;
    }
 
@btc
You pass zero as spriteId so it cannot work.
Change that to:
C++:
Thing* thing = internalGetThing(player, fromPos, fromIndex, spriteId, STACKPOS_MOVE);
 
@btc
You pass zero as spriteId so it cannot work.
Change that to:
C++:
Thing* thing = internalGetThing(player, fromPos, fromIndex, spriteId, STACKPOS_MOVE);
Thank You. The code now works as intended. I didn't notice that. Thank You very much, BIG PLUS for You! To be honest that was the hardest script to script in my OTS and you have made me very happy! :)
 
@btc I didn't check all edge cases so need to be tested to confirm it's good approach :p
Make sure to change every call to internalGetThing (only ones with type STACKPOS_MOVE)
 
@btc I didn't check all edge cases so need to be tested to confirm it's good approach :p
Make sure to change every call to internalGetThing (only ones with type STACKPOS_MOVE)
Yes, I have made some tasks aswell "createSchedulerTask" to give moves delays, like 200 mls and STACKPOS_USE has now implemented this aswell in case you want to use a rune or rotate a chair but an item lies on top of it. :) Thank, dude!
 

Attachments

Back
Top