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

Wrong behaviour when walking into stacked items (like 2 or more parcels)

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
Hi, i'm using Otclient 7.72 (edubart) and i have this problem:

When i autowalk or use keyboard to move into 2 or more parcels or walkeable stacked items, i have a loop trying to prewalking to the item and receiving the message "Sorry, not possible" again and again, instead of just stay in my position and get the message "Sorry, not possible".

I just checked game.cpp and tried a couple of things (adding checks to elevations items before prewalk), without success. I also looked into Kondra's client walkeable.lua, but couldn't think of a way to make it work like this.

Here is the game.cpp that contains the lines i think should be edited to make it work like the tibia client:


Lua:
bool Game::walk(Otc::Direction direction, bool dash)
{
    if(!canPerformGameAction())
        return false;

    // must cancel follow before any new walk
    if(isFollowing())
        cancelFollow();

    // must cancel auto walking, and wait next try
    if(m_localPlayer->isAutoWalking() || m_localPlayer->isServerWalking()) {
        m_protocolGame->sendStop();
        if(m_localPlayer->isAutoWalking())
            m_localPlayer->stopAutoWalk();
        return false;
    }

    if(dash) {
        if(m_localPlayer->isWalking() && m_dashTimer.ticksElapsed() < std::max<int>(m_localPlayer->getStepDuration(false, direction) - m_ping, 30))
            return false;
    }
    else {
        // check we can walk and add new walk event if false
        if(!m_localPlayer->canWalk(direction)) {
            if(m_lastWalkDir != direction) {
                // must add a new walk event
                float ticks = m_localPlayer->getStepTicksLeft();
                if(ticks <= 0) { ticks = 1; }

                if(m_walkEvent) {
                    m_walkEvent->cancel();
                    m_walkEvent = nullptr;
                }
                m_walkEvent = g_dispatcher.scheduleEvent([=] { walk(direction, false); }, ticks);
            }
            return false;
        }
    }
    Position toPos = m_localPlayer->getPosition().translatedToDirection(direction);
    TilePtr toTile = g_map.getTile(toPos);
    // only do prewalks to walkable tiles (like grounds and not walls)
        if(toTile && toTile->isWalkable()) { 
        m_localPlayer->preWalk(direction);
    // check walk to another floor (e.g: when above 3 parcels)
    } else {
        // check if can walk to a lower floor
        auto canChangeFloorDown = [&]() -> bool {
            Position pos = toPos;
            if(!pos.down())
                return false;
            TilePtr toTile = g_map.getTile(pos);
            if(toTile && toTile->hasElevation(3))
                return true;
            return false;
        };

        // check if can walk to a higher floor
        auto canChangeFloorUp = [&]() -> bool {
            TilePtr fromTile = m_localPlayer->getTile();
            if(!fromTile || !fromTile->hasElevation(3))
                return false;
            Position pos = toPos;
            if(!pos.up())
                return false;
            TilePtr toTile = g_map.getTile(pos);
            if(!toTile || !toTile->isWalkable())
                return false;
            return true;
        };

        if(canChangeFloorDown() || canChangeFloorUp() ||
            (!toTile || toTile->isEmpty())) {
            m_localPlayer->lockWalk();
        } else
            return false;
    }

    m_localPlayer->stopAutoWalk();

    g_lua.callGlobalField("g_game", "onWalk", direction, dash);

    forceWalk(direction);
    if(dash)
      m_dashTimer.restart();

    m_lastWalkDir = direction;
    return true;
}

I tried editing this line:

Code:
 if(toTile && toTile->isWalkable()) {

into this:

Code:
if(toTile && toTile->isWalkable() && !(toTile->hasElevation(2))) {

Although you don't prewalk into it, you can't walk into stacked parcels that have more than 1 parcel, so i can't figure out a way to fix it.

Help please
 
Last edited:
Solution
it works, now i don't have bugs when going downstairs, but this way i can't go from 4 chairs to 6 chairs i.e, although i was able with my buggy way haha
If you want to be able to walk on any stack from 4 chairs, you can change:
Code:
if(fromTile && (toTile->getElevation() - fromTile->getElevation() <= 1)) {
to
Code:
if(fromTile && ((toTile->getElevation() - fromTile->getElevation() <= 1) || (fromTile->getElevation() > 3))) {
but you should check if it's allowed on the server's side too.

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
913
Solutions
9
Reaction score
1,251
Location
32316,31942,7
YouTube
TibiantisOnline
I think you need to do something like this:
Code:
if(toTile && toTile->isWalkable()) {
   TilePtr fromTile = m_localPlayer->getTile();
   if(fromTile && (toTile->elevation() - fromTile->elevation() <= 1)) {
       m_localPlayer->preWalk(direction);
	} else {
       //higher elevation difference
       //try to walk but don't activate prewalk
       //may need to add code here
	}
} else {
Note that it's a pseudocode, I don't know if there's an elevation() method and I don't guarantee the correctness, but to give you the picture. After you verified the destination tile exists and is walkable, you need to differ two situations: its height allows you to go or not. In both cases you will try to walk (because there might be hidden parcels under the character), but only in the first one you activate preWalk.
 
Last edited:
OP
OP
Terotrificy

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
I think you need to do something like this:
Code:
if(toTile && toTile->isWalkable()) {
   TilePtr fromTile = m_localPlayer->getTile();
   if(fromTile && (toTile->elevation() - fromTile->elevation() <= 1)) {
       m_localPlayer->preWalk(direction);
    } else {
       //higher elevation difference
       //try to walk but don't activate prewalk
       //may need to add code here
    }
} else {
Note that it's a pseudocode, I don't know if there's an elevation() method and I don't guarantee the correctness, but to give you the picture. After you verified the destination tile exists and is walkable, you need to differ two situations: its height allows you to go or not. In both cases you will try to walk (because there might be hidden parcels under the character), but only in the first one you activate preWalk.
Thank you mate <3 It didn't work tho:

Lua:
Error    1    error C2039: 'elevation' : is not a member of 'Tile'    game.cpp    628    1    otclient

I tried using hasElevation() instead, but nothing changed at all.

I just localized the isWalkable function and hasElevation function:


Lua:
bool Tile::isWalkable(bool ignoreCreatures)
{
    if(!getGround())
        return false;

    if(g_game.getClientVersion() <= 740 && hasElevation(2))
        return false;

    for(const ThingPtr& thing : m_things) {
        if(thing->isNotWalkable())
            return false;

        if(!ignoreCreatures) {
            if(thing->isCreature()) {
                CreaturePtr creature = thing->static_self_cast<Creature>();
                if(!creature->isPassable() && creature->canBeSeen())
                    return false;
            }
        }
    }
    return true;
}

Lua:
bool Tile::hasElevation(int elevation)
{
    int count = 0;
    for(const ThingPtr& thing : m_things)
        if(thing->getElevation() > 0)
            count++;
    return count >= elevation;
}


Since i'm using 772 protocol, idk if it's right to work like this, but i tried reverting the "<=" to ">=" and you simply can't jump into 2 or more parcels.
 
Last edited:

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
913
Solutions
9
Reaction score
1,251
Location
32316,31942,7
YouTube
TibiantisOnline
Thank you mate <3 It didn't work tho:

Lua:
Error    1    error C2039: 'elevation' : is not a member of 'Tile'    game.cpp    628    1    otclient
I said it wasn't real method, I came up with it for the sake of explanation, because I'm not familiar with the sources. But there should be an adequate method that returns the actual elevetion value under some name, maybe try "getElevation". In case there's not - you can easily add one. Somehthing like this will do the job:
Code:
int Tile::elevation()
{
    int count = 0;
    for(const ThingPtr& thing : m_things)
        if(thing->getElevation() > 0)
            count++;
    return count;
}
 
OP
OP
Terotrificy

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
Just modified the code to this:


Lua:
osition toPos = m_localPlayer->getPosition().translatedToDirection(direction);
    TilePtr toTile = g_map.getTile(toPos);
    Position inPos = m_localPlayer->getPosition();
    TilePtr frommTile = g_map.getTile(inPos);
    int elevationdiff = toTile->hasElevation() - frommTile->hasElevation();
    // only do prewalks to walkable tiles (like grounds and not walls)
    if(toTile && toTile->isWalkable()) {
        m_localPlayer->preWalk(direction);
        g_lua.callGlobalField("g_game", "onTextMessage", 19, "The elevation difference is: "+std::to_string(elevationdiff));

So now it sends me a msg with the result of the ecuation (tile to go - tile i'm standing before the action).


So far, this is the behaviour:

If i move from ground to ground the result is 0 (0-0 = 0)
If i move from ground to parcel the result is 1 (1 - 0 = 1)
If i move from parcel to ground the result is -1 (0 - 1 = -1)
If i move from parcel to parcel the result is 0 (1-1 = 0)

Until that all makes sense but:
If i move from parcel to 2 parcels the result is 0
If i move from lot of parcels to 1 parcel the result is 0
If i move from lot of parcels to ground, the result is -1

So i guess it's only verifyng if the elevation is different than 0, but not actually checking the elevation level.
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
913
Solutions
9
Reaction score
1,251
Location
32316,31942,7
YouTube
TibiantisOnline
Just modified the code to this:


Lua:
osition toPos = m_localPlayer->getPosition().translatedToDirection(direction);
    TilePtr toTile = g_map.getTile(toPos);
    Position inPos = m_localPlayer->getPosition();
    TilePtr frommTile = g_map.getTile(inPos);
    int elevationdiff = toTile->hasElevation() - frommTile->hasElevation();
    // only do prewalks to walkable tiles (like grounds and not walls)
    if(toTile && toTile->isWalkable()) {
        m_localPlayer->preWalk(direction);
        g_lua.callGlobalField("g_game", "onTextMessage", 19, "The elevation difference is: "+std::to_string(elevationdiff));

So now it sends me a msg with the result of the ecuation (tile to go - tile i'm standing before the action).


So far, this is the behaviour:

If i move from ground to ground the result is 0 (0-0 = 0)
If i move from ground to parcel the result is 1 (1 - 0 = 1)
If i move from parcel to ground the result is -1 (0 - 1 = -1)
If i move from parcel to parcel the result is 0 (1-1 = 0)

Until that all makes sense but:
If i move from parcel to 2 parcels the result is 0
If i move from lot of parcels to 1 parcel the result is 0
If i move from lot of parcels to ground, the result is -1

So i guess it's only verifyng if the elevation is different than 0, but not actually checking the elevation level.

hasElevation returns bool which is only 1 or 0, you need to know the actual difference. Use getElevation if it exists, as I said, otherwise add it.

Edit: I've checked the sources of otc now, there's getElevation method which looks exactly the same as I wrote in the previous post. That being said, you can simply try this:

Code:
if(toTile && toTile->isWalkable()) {
   TilePtr fromTile = m_localPlayer->getTile();
   if(fromTile && (toTile->getElevation() - fromTile->getElevation() <= 1)) {
       m_localPlayer->preWalk(direction);
	} else {
       return true;
	}
} else {

Also, the code of isWalkable method you've pasted here has these lines:
Code:
    if(g_game.getClientVersion() <= 740 && hasElevation(2))
        return false;
They should be removed in my opinion. It's not correct because the rule isn't that we shouldn't be allowed to walk on 2 parcels. It is that we shouldn't be allowed to walk on a stack of parcels that is higher than the one we're standing on by difference equal or greater than 2. And the code we're adding is already supposed to do that.
 
Last edited:
OP
OP
Terotrificy

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
hasElevation returns bool which is only 1 or 0, you need to know the actual difference. Use getElevation if it exists, as I said, otherwise add it.

Edit: I've checked the sources of otc now, there's getElevation method which looks exactly the same as I wrote in the previous post. That being said, you can simply try this:

Code:
if(toTile && toTile->isWalkable()) {
   TilePtr fromTile = m_localPlayer->getTile();
   if(fromTile && (toTile->getElevation() - fromTile->getElevation() <= 1)) {
       m_localPlayer->preWalk(direction);
    } else {
       return true;
    }
} else {

Also, the code of isWalkable method you've pasted here has these lines:
Code:
    if(g_game.getClientVersion() <= 740 && hasElevation(2))
        return false;
They should be removed in my opinion. It's not correct because the rule isn't that we shouldn't be allowed to walk on 2 parcels. It is that we shouldn't be allowed to walk on a stack of parcels that is higher than the one we're standing on by difference equal or greater than 2. And the code we're adding is already supposed to do that.
I'm struggling with adding the function. That function of getElevation exists, but it's only for "things", so i can't call it for "tiles", that's why i can't use that method with Tileptr.


Lua:
Error    1    error C2039: 'getElevation' : is not a member of 'Tile'game.cpp    627    1    otclient
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
913
Solutions
9
Reaction score
1,251
Location
32316,31942,7
YouTube
TibiantisOnline
I'm struggling with adding the function. That function of getElevation exists, but it's only for "things", so i can't call it for "tiles", that's why i can't use that method with Tileptr.


Lua:
Error    1    error C2039: 'getElevation' : is not a member of 'Tile'game.cpp    627    1    otclient
Can you link the sources? getElevation is a member of Tile here:
 
OP
OP
Terotrificy

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
This are the sources i'm using, it doesn't have getElevation() in tiles, just checked it:


Although i managed to add the function as "obtainElevation()", compiling right now and gonna check if it worked O.O

Bumb: It worked, now when i go from 3 chairs to floor, i get a -3.

 
OP
OP
Terotrificy

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
Kinda weird not to have it. But anyway now when you've added it, you can apply these changes from post #6 and you should be fine.
I left the code like this:
Lua:
 Position toPos = m_localPlayer->getPosition().translatedToDirection(direction);
    TilePtr toTile = g_map.getTile(toPos);
    Position inPos = m_localPlayer->getPosition();
    TilePtr frommTile = g_map.getTile(inPos);
    int elevationdiff = toTile->obtainElevation() - frommTile->obtainElevation();
    // only do prewalks to walkable tiles (like grounds and not walls)
    if(toTile && toTile->isWalkable() && (elevationdiff <= 1)) {
        m_localPlayer->preWalk(direction);
        g_lua.callGlobalField("g_game", "onTextMessage", 19, "The elevation difference is: "+std::to_string(elevationdiff));

Now it's working as expected, even if i'm in 3 chairs and want to jump to 5 or more chairs, i just jump because the max elevation is 4, however, now i have client crash when i go down from a roof to 3 parcels... (not when going up, just when going down).
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
913
Solutions
9
Reaction score
1,251
Location
32316,31942,7
YouTube
TibiantisOnline
I left the code like this:
Lua:
 Position toPos = m_localPlayer->getPosition().translatedToDirection(direction);
    TilePtr toTile = g_map.getTile(toPos);
    Position inPos = m_localPlayer->getPosition();
    TilePtr frommTile = g_map.getTile(inPos);
    int elevationdiff = toTile->obtainElevation() - frommTile->obtainElevation();
    // only do prewalks to walkable tiles (like grounds and not walls)
    if(toTile && toTile->isWalkable() && (elevationdiff <= 1)) {
        m_localPlayer->preWalk(direction);
        g_lua.callGlobalField("g_game", "onTextMessage", 19, "The elevation difference is: "+std::to_string(elevationdiff));

Now it's working as expected, even if i'm in 3 chairs and want to jump to 5 or more chairs, i just jump because the max elevation is 4, however, now i have client crash when i go down from a roof to 3 parcels... (not when going up, just when going down).
That's probably because you're calling toTile->obtainElevation() before checking if toTile is true. And in case you're trying to get down the roof - it isn't, because the walk aims on the non-existant square next to it. So the pointer is null and it results in segmentation fault.
 
OP
OP
Terotrificy

Terotrificy

Well-Known Member
Joined
Oct 18, 2020
Messages
247
Solutions
9
Reaction score
81
Location
Santiago, Chile.
That's probably because you're calling toTile->obtainElevation() before checking if toTile is true. And in case you're trying to get down the roof - it isn't, because the walk aims on the non-existant square next to it. So the pointer is null and it results in segmentation fault.
Damn, so close yet so far lol. Do you have any idea how to solve it?
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
913
Solutions
9
Reaction score
1,251
Location
32316,31942,7
YouTube
TibiantisOnline
it works, now i don't have bugs when going downstairs, but this way i can't go from 4 chairs to 6 chairs i.e, although i was able with my buggy way haha
If you want to be able to walk on any stack from 4 chairs, you can change:
Code:
if(fromTile && (toTile->getElevation() - fromTile->getElevation() <= 1)) {
to
Code:
if(fromTile && ((toTile->getElevation() - fromTile->getElevation() <= 1) || (fromTile->getElevation() > 3))) {
but you should check if it's allowed on the server's side too.
 
Solution
Top