• 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

Veteran OT User
Joined
Oct 18, 2020
Messages
401
Solutions
13
Reaction score
254
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.
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:
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:
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;
}
 
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.
 
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:
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
 
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:
 
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.

 
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).
 
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.
 
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?
 
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
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.

Looks like the latest commit in that repo is from 2016, probably better to use newest OTC sources instead and add code from the repo you're currently using when needed, or I guess you could copy code from OTC to your own repo when needed, just a tip.

Also the looping issue you're talking about in the beginning is or can be a separate issue, one of hundreds of bugs that hasn't been fixed even in the latest Edubart client, I had to rewrite a lot of stuff to get a smooth autowalking system myself.
A tip on how to improve autowalking is to figure out how to get all of these 3 things working within the same system (auto walk interruption by user, no infinite walk loop, and being able to traverse dense areas of moving creatures without interruption).
(Any fps loss here is caused by logging still being enabled since I don't consider autowalking in my own client fully finished yet)
 
Back
Top