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

Final thread for fixing OTClient dashing animation issue for TFS 1.3

Togu

Advanced OT User
Joined
Jun 22, 2018
Messages
308
Solutions
1
Reaction score
178
Location
Brazil
I made a super slow motion to show the exactly dashing issue:
final - Streamable (https://streamable.com/2xek1) troll going from up to down and comparing with real gif and sprites
test - Streamable (https://streamable.com/wmv6e) troll going from right to left
Split animated GIF image in frames (free online tool) (https://ezgif.com/split/ezgif-1-1c80fb2cf982.gif) gif and sprites troll going from up to down

More information you can find here:
https://github.com/edubart/otclient/issues/937
https://otland.net/threads/looking-...nimations-walking-frames-for-otclient.260611/
https://otland.net/threads/animations-lets-give-it-a-try.262781/
otland/otclient (https://github.com/otland/otclient/compare/master...otland:WalkAnimation?expand=1) Ninja's solution for local player walking animation (I changed it a little to my client)

I fixed the local player walking animation issue using Ninja's solution and fixed the pushing diagonal laggy issue (https://i.gyazo.com/412d93e4a799cdc93fd098c04001d7e2.mp4) setting the factor from getStepDuration() (in creature.cpp in client sources) to 1.

But this dashing issue is not so easy to solve, I need tips and help from the community and I guess this is wanted from everybody.

My guess is that creatures that aren't the local player are being drawn with wrong position or wrong speed and then when the creature reach the next tile position on server side the creature position on client side is refreshed (you can see that Troll's name and health bar teleports too, so its not a problem on skipping frames, its skipping positions) without the sprite reaching it.

I've seen that the information sent from server to client is just the oldPos and the newPos, on client the sprites are rendered using the oldPos + newPos + gettingStepDuration() + totalPixelsWalked (something like that) and I verified the getStepDuration() formula from client sources and server sources and they look almost the same thing... I'm kind of lost here

@Ninja @Gesior.pl @Evolunia @Dual Core

Edit:
Created an official issue on otland/otclient github:
[10.98] Creatures on screen appears to be dashing/teleporting to next tile when walking · Issue #21 · otland/otclient (https://github.com/otland/otclient/issues/21)
 
Last edited:
I think I found the bug.

creature.cpp:

I changed the return value of this function to 80% of the original value.
The factor is commented to solve the pushing diagonal laggy issue.
This line interval = std::ceil(interval / (50 * 1.f)) * 50; uses 50 cause I use TFS 1.3.
Code:
int Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir)
{
    int speed = m_speed;
    if(speed < 1)
        return 0;

    if(g_game.getFeature(Otc::GameNewSpeedLaw))
        speed *= 2;

    int groundSpeed = 0;
    Position tilePos;

    if(dir == Otc::InvalidDirection)
        tilePos = m_lastStepToPosition;
    else
        tilePos = m_position.translatedToDirection(dir);

    if(!tilePos.isValid())
        tilePos = m_position;
    const TilePtr& tile = g_map.getTile(tilePos);
    if(tile) {
        groundSpeed = tile->getGroundSpeed();
        if(groundSpeed == 0)
            groundSpeed = 150;
    }

    int interval = 1000;
    if(groundSpeed > 0 && speed > 0)
        interval = 1000 * groundSpeed;

    if(g_game.getFeature(Otc::GameNewSpeedLaw) && hasSpeedFormula()) {
        int formulatedSpeed = 1;
        if(speed > -m_speedFormula[Otc::SpeedFormulaB]) {
            formulatedSpeed = std::max<int>(1, (int)floor((m_speedFormula[Otc::SpeedFormulaA] * log((speed / 2)
                 + m_speedFormula[Otc::SpeedFormulaB]) + m_speedFormula[Otc::SpeedFormulaC]) + 0.5));
        }
        interval = std::floor(interval / (double)formulatedSpeed);
    }
    else
        interval /= speed;

    if(g_game.getClientVersion() >= 900)
        interval = std::ceil(interval / (50 * 1.f)) * 50;

    //float factor = 3;
    //if(g_game.getClientVersion() <= 810)
    //    factor = 2;

    //interval = std::max<int>(interval, g_game.getServerBeat());

    //if(!ignoreDiagonal && (m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
    //   m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast))
    //    interval *= factor;

    
    if (this->isLocalPlayer()) {
        return interval;
    }

    return (int)interval*0.8f;
}

My updateWalkAnimation function based on ninjalulz fix for walking and idle animations
Code:
void Creature::updateWalkAnimation(int totalPixelsWalked)
{
    // update outfit animation
    if(m_outfit.getCategory() != ThingCategoryCreature)
        return;

    int footAnimPhases = getAnimationPhases() - 1; //returns 8
    float footDelay = getStepDuration(true) / footAnimPhases; //varies with the g round

    // since mount is a different outfit we need to get the mount animation phases
    if(m_outfit.getMount() != 0) {
        ThingType *type = g_things.rawGetThingType(m_outfit.getMount(), m_outfit.getCategory());
        footAnimPhases = type->getAnimationPhases() - 1;
        footDelay = getStepDuration(true) / footAnimPhases;
    }

    if (totalPixelsWalked >= 32 && !m_walkFinishAnimEvent) {
        m_footStep = 0;

        auto self = static_self_cast<Creature>();
        m_walkFinishAnimEvent = g_dispatcher.scheduleEvent([self] {
            if (!self->m_walking || self->m_walkTimer.ticksElapsed() >= self->getStepDuration(true)) {
                self->m_walkAnimationPhase = 0;
            }
            self->m_walkFinishAnimEvent = nullptr;
        }, std::min<int>(footDelay, 200));
    }

    if (footAnimPhases == 0) {
        m_walkAnimationPhase = 0;
    }
    else if(m_footStepDrawn && m_footTimer.ticksElapsed() >= footDelay && totalPixelsWalked < 32) {
        m_footStep++;
        m_walkAnimationPhase = (m_footStep % footAnimPhases);
        m_footStepDrawn = false;
        m_footTimer.restart();
    }
    else if(m_walkAnimationPhase == 0 && totalPixelsWalked < 32) {
        m_walkAnimationPhase = (m_footStep % footAnimPhases);
    }
}

Videos
https://streamable.com/2gq1h
https://streamable.com/j679m

Conclusion
Client creature speed values are treated different from server creature speed values or the getStepDuration is wrong on client or server sources.
 
Last edited:
I think I found the bug.

creature.cpp:

I changed the return value of this function to 80% of the original value.
The factor is commented to solve the pushing diagonal laggy issue.
This line interval = std::ceil(interval / (50 * 1.f)) * 50; uses 50 cause I use TFS 1.3.
Code:
int Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir)
{
    int speed = m_speed;
    if(speed < 1)
        return 0;

    if(g_game.getFeature(Otc::GameNewSpeedLaw))
        speed *= 2;

    int groundSpeed = 0;
    Position tilePos;

    if(dir == Otc::InvalidDirection)
        tilePos = m_lastStepToPosition;
    else
        tilePos = m_position.translatedToDirection(dir);

    if(!tilePos.isValid())
        tilePos = m_position;
    const TilePtr& tile = g_map.getTile(tilePos);
    if(tile) {
        groundSpeed = tile->getGroundSpeed();
        if(groundSpeed == 0)
            groundSpeed = 150;
    }

    int interval = 1000;
    if(groundSpeed > 0 && speed > 0)
        interval = 1000 * groundSpeed;

    if(g_game.getFeature(Otc::GameNewSpeedLaw) && hasSpeedFormula()) {
        int formulatedSpeed = 1;
        if(speed > -m_speedFormula[Otc::SpeedFormulaB]) {
            formulatedSpeed = std::max<int>(1, (int)floor((m_speedFormula[Otc::SpeedFormulaA] * log((speed / 2)
                 + m_speedFormula[Otc::SpeedFormulaB]) + m_speedFormula[Otc::SpeedFormulaC]) + 0.5));
        }
        interval = std::floor(interval / (double)formulatedSpeed);
    }
    else
        interval /= speed;

    if(g_game.getClientVersion() >= 900)
        interval = std::ceil(interval / (50 * 1.f)) * 50;

    //float factor = 3;
    //if(g_game.getClientVersion() <= 810)
    //    factor = 2;

    //interval = std::max<int>(interval, g_game.getServerBeat());

    //if(!ignoreDiagonal && (m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
    //   m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast))
    //    interval *= factor;

   
    if (this->isLocalPlayer()) {
        return interval;
    }

    return (int)interval*0.8f;
}

My updateWalkAnimation function based on ninjalulz fix for walking and idle animations
Code:
void Creature::updateWalkAnimation(int totalPixelsWalked)
{
    // update outfit animation
    if(m_outfit.getCategory() != ThingCategoryCreature)
        return;

    int footAnimPhases = getAnimationPhases() - 1; //returns 8
    float footDelay = getStepDuration(true) / footAnimPhases; //varies with the g round

    // since mount is a different outfit we need to get the mount animation phases
    if(m_outfit.getMount() != 0) {
        ThingType *type = g_things.rawGetThingType(m_outfit.getMount(), m_outfit.getCategory());
        footAnimPhases = type->getAnimationPhases() - 1;
        footDelay = getStepDuration(true) / footAnimPhases;
    }

    if (totalPixelsWalked >= 32 && !m_walkFinishAnimEvent) {
        m_footStep = 0;

        auto self = static_self_cast<Creature>();
        m_walkFinishAnimEvent = g_dispatcher.scheduleEvent([self] {
            if (!self->m_walking || self->m_walkTimer.ticksElapsed() >= self->getStepDuration(true)) {
                self->m_walkAnimationPhase = 0;
            }
            self->m_walkFinishAnimEvent = nullptr;
        }, std::min<int>(footDelay, 200));
    }

    if (footAnimPhases == 0) {
        m_walkAnimationPhase = 0;
    }
    else if(m_footStepDrawn && m_footTimer.ticksElapsed() >= footDelay && totalPixelsWalked < 32) {
        m_footStep++;
        m_walkAnimationPhase = (m_footStep % footAnimPhases);
        m_footStepDrawn = false;
        m_footTimer.restart();
    }
    else if(m_walkAnimationPhase == 0 && totalPixelsWalked < 32) {
        m_walkAnimationPhase = (m_footStep % footAnimPhases);
    }
}

Videos
https://streamable.com/2gq1h
https://streamable.com/j679m

Conclusion
Client creature speed values are treated different from server creature speed values or the getStepDuration is wrong on client or server sources.
Glad to see you're working on it bro. If I was better at c++ I'd be glad to help. Good luck!
 
@Olddies @Apollos @BahamutxD
Thanks for the feedback, you can help me telling if you think it is better now! Did you see the video?

I've made 2 pull requests on otland/otclient repository, one for fixing the lag when pushing diagonal and other for fixing the dashing/teleporting in case anybody wants to see the changes there and test for yourself.

 
@Olddies @Apollos @BahamutxD
Thanks for the feedback, you can help me telling if you think it is better now! Did you see the video?

I've made 2 pull requests on otland/otclient repository, one for fixing the lag when pushing diagonal and other for fixing the dashing/teleporting in case anybody wants to see the changes there and test for yourself.

You had a build error in your pull request, fixed by adding parentheses at the end of isLocalPlayer.
 
Been gone from OTs for a while, I'm back with a near complete fix to the horrible OTC walking. At least with the new speed law, so... 9.81+
 
I'm gonna try your fix later with a clean tfs and otclient! I'm glad that this is solved now.
 
Been gone from OTs for a while, I'm back with a near complete fix to the horrible OTC walking. At least with the new speed law, so... 9.81+
The walking animation for most outfits is much smoother and this is a definite step in the right direction. Although, there are some issues with outfits with idle animation, diagonal movement, and also if you are mounted. I know you don't claim to fix mounts but I thought you may want to know.

35075 35076
 
Last edited:
The walking animation for most outfits is much smoother and this is a definite step in the right direction. Although, there are some issues with outfits with idle animation, diagonal movement, and also if you are mounted. I know you don't claim to fix mounts but I thought you may want to know.

35075
35076

Idle animations are a completely different issue that has to do with .dat groups.
It looks like you're using the half fix posted by ninja? I suspect that could be causing the problems you experience. Try without it.

I don't have any problems with diagonal movement or mounts, but I'm also not using the latest OTC
 
Last edited:
Idle animations are a completely different issue that has to do with .dat groups.
It looks like you're using the half fix posted by ninja's? I suspect that could be causing the problems you experience. Try without it.

I don't have any problems with diagonal movement or mounts, but I'm also not using the latest OTC
Yep, you're right, my mistake. It's still buggy with the walking animation for outfits that have an idle animation. Otherwise it seems to be working really well, great job. Sorry for the confusion.

EDIT: I combined some parts of ninja's commit with yours to include idle animations and fix the missing animation bug that come with that fix (when you combine an idle animated outfit and a non-idle animated mount and vice versa) and got it working. Although the only thing that I cannot fix is the walking animationPhases (im assuming it's something to do with this) for idle animating outfits. If anyone can help figure it out that would be awesome.

Here's what I did:
 
Last edited:
The walking animation for most outfits is much smoother and this is a definite step in the right direction. Although, there are some issues with outfits with idle animation, diagonal movement, and also if you are mounted. I know you don't claim to fix mounts but I thought you may want to know.

View attachment 35075 View attachment 35076
What about a fix for older protocols? in older versiona diagonal movement is stiill bugged
 
I don't have that lag:
(I don't know if the GIF is accurate to see the difference)
View attachment 35525
im using latest otc, but i noticed that the jump of the screen only occurss when i was with dash walk + smartwalk turned on at the same time
if i have dash disabled and smooth walking turned on its works good... but a bit foced for my liking anyway
and with everything disabled its also works good but a bit, just a bit forced on the presskey to walk too

PS
im using DX9 , maybe OPENGL would work better? anybody could tell me the prepocessor definitions in order to do this?
 
Last edited:
im using latest otc, but i noticed that the jump of the screen only occurss when i was with dash walk + smartwalk turned on at the same time
if i have dash disabled and smooth walking turned on its works good... but a bit foced for my liking anyway
and with everything disabled its also works good but a bit, just a bit forced on the presskey to walk too

PS
im using DX9 , maybe OPENGL would work better? anybody could tell me the prepocessor definitions in order to do this?
Im using OGL btw.
I think the lag comes when you activate both smart+dash walking.
What you need to compile on DX9? I can try to reproduce it.
 
Back
Top