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

For some reason attack speed didn't change

Lopaskurwa

Active Member
Joined
Oct 6, 2017
Messages
873
Solutions
2
Reaction score
49
Hi
so today i tried to balance vocation and first step was attack speed orginal was 2000 i changed to attackspeed="1200" with that much attackspeed i should attack really fast and i applied club fighting 175 and i attack really, really slow its like nothing changed from original. If u need some kind of code just say because now i have no idea where to check or what to send.
 
Solution
First, I want to tell you that your post is hard to understand, and you don't give us accurate information.

But, no worries, I will help you anyway.

Your issue is simple, in TFS Source -> player.cpp, the function doAttacking does not schedule the "next attack" correctly, therefor it waits too long if you have a low attackspeed.
Code:
void Player::doAttacking(uint32_t)

If you want my help, I will need your to copy and paste the entire "doAttacking" function from your player.cpp, and I will review it and tell you how to fix it.
 
First, I want to tell you that your post is hard to understand, and you don't give us accurate information.

But, no worries, I will help you anyway.

Your issue is simple, in TFS Source -> player.cpp, the function doAttacking does not schedule the "next attack" correctly, therefor it waits too long if you have a low attackspeed.
Code:
void Player::doAttacking(uint32_t)

If you want my help, I will need your to copy and paste the entire "doAttacking" function from your player.cpp, and I will review it and tell you how to fix it.
Appreciate your answer :) Well problem is pretty simple my attack speed is slow when i stand but when i move or target moves my attack speed goes faster. So i need to make my attack speed hit faster in every situation
Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                uint32_t delay = getNextActionTime();
                SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::checkCreatureAttack,
                                      &g_game, getID()));
                setNextActionTask(task);
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        g_scheduler.addEvent(createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, getAttackSpeed()), std::bind(&Game::checkCreatureAttack, &g_game, getID())));
        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}
 
Awesome! Thank you for posting this code, I will now explain what is happening.
Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }
You can ignore the above code, it is working as intended.
It sets your defaults if you have never attacked before, and cancels your attack if you are pacified.

Code:
    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        if (weapon) {
This code checks if you can attack, then grabs your weapon (to make sure you aren't using fist).
This is working as intended, so we can ignore it as well.

Code:
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
This code checks if your weapon has interruptSwing. Almost no one uses this, I think it is off by default. (Basically says you can "swing" your weapon and miss, resetting your attackspeed if you are far away from your target)
This is working as intended, so we can ignore it as well.

Code:
            } else if (!canDoAction()) {
                uint32_t delay = getNextActionTime();
                SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::checkCreatureAttack,
                                      &g_game, getID()));
                setNextActionTask(task);
This code can be optimized a bit, to make things easier to see what is going on, BUT I think this is also working as intended.
This checks if you can attack, if you cannot it tries again at "getNextActionTime()" (So it waits a small amount of time before attempting to attack again if the attack fails for some reason)
Technically we can skip this too.

Code:
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
This code does the attack if you have a weapon, working as intended so we can skip it.

Code:
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }
This code does the attack if you do not have a weapon (using fist), working as intended so we can skip it.

In short, I believe you only need to make sure the below scheduler is working correctly.
Code:
        g_scheduler.addEvent(createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, getAttackSpeed()), std::bind(&Game::checkCreatureAttack, &g_game, getID())));
The above schedules your next attack attempt.
Now if you look it has the following:
std::max<uint32_t>(SCHEDULER_MINTICKS, getAttackSpeed())

std::max will grab which ever number is HIGHER.
So if your getAttackSpeed() is 2000, but SCHEDULER_MINTICKS is 3000. Then you will attack every 3000 milliseconds.

The current version of TFS has SCHEDULER_MINTICKS set to 50. I would first check and make sure this is set to 50 as well in your source.
(You can find this in scheduler.h)
Code:
static constexpr int32_t SCHEDULER_MINTICKS = 50;
This will mean the lowest possible attackspeed would be every 50 milliseconds. (20 times per second) Which should be good enough for almost any server.

To test this, please update your source, re-compile with the new changes, restart your server, create a new character and see if your attackspeed is working.

If SCHEDULER_MINTICKS is already set to 50, let me know.
 
Last edited:
Awesome! Thank you for posting this code, I will now explain what is happening.
Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }
You can ignore the above code, it is working as intended.
It sets your defaults if you have never attacked before, and cancels your attack if you are pacified.

Code:
    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        if (weapon) {
This code checks if you can attack, then grabs your weapon (to make sure you aren't using fist).
This is working as intended, so we can ignore it as well.

Code:
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
This code checks if your weapon has interruptSwing. Almost no one uses this, I think it is off by default. (Basically says you can "swing" your weapon and miss, resetting your attackspeed if you are far away from your target)
This is working as intended, so we can ignore it as well.

Code:
            } else if (!canDoAction()) {
                uint32_t delay = getNextActionTime();
                SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::checkCreatureAttack,
                                      &g_game, getID()));
                setNextActionTask(task);
This code can be optimized a bit, to make things easier to see what is going on, BUT I think this is also working as intended.
This checks if you can attack, if you cannot it tries again at "getNextActionTime()" (So it waits a small amount of time before attempting to attack again if the attack fails for some reason)
Technically we can skip this too.

Code:
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
This code does the attack if you have a weapon, working as intended so we can skip it.

Code:
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }
This code does the attack if you do not have a weapon (using fist), working as intended so we can skip it.

In short, I believe you only need to make sure the below scheduler is working correctly.
Code:
        g_scheduler.addEvent(createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, getAttackSpeed()), std::bind(&Game::checkCreatureAttack, &g_game, getID())));
The above schedules your next attack attempt.
Now if you look it has the following:
std::max<uint32_t>(SCHEDULER_MINTICKS, getAttackSpeed())

std::max will grab which ever number is HIGHER.
So if your getAttackSpeed() is 2000, but SCHEDULER_MINTICKS is 3000. Then you will attack every 3000 milliseconds.

The current version of TFS has SCHEDULER_MINTICKS set to 50. I would first check and make sure this is set to 50 as well in your source.
(You can find this in scheduler.h)
Code:
static constexpr int32_t SCHEDULER_MINTICKS = 50;
This will mean the lowest possible attackspeed would be every 50 milliseconds. (20 times per second) Which should be good enough for almost any server.

To test this, please update your source, re-compile with the new changes, restart your server, create a new character and see if your attackspeed is working.

If SCHEDULER_MINTICKS is already set to 50, let me know.
Awesome reply :eek: So yea in scheduler.h my minitick is already 50
Code:
#define SCHEDULER_MINTICKS 50
So i attack fast only if i move but if i stand still my attack speed is really slow.
 
Last edited:
Try using this edited version of doAttacking. (It is the newest TFS 1.X version with extra functionality removed)

Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        g_scheduler.addEvent(task);

        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}
 
Last edited:
Try using this edited version of doAttacking. (It is the newest TFS 1.X version with extra functionality removed)

Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        if (!classicSpeed) {
            setNextActionTask(task);
        } else {
            g_scheduler.addEvent(task);
        }

        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}
Nop still same. Tried to create new voc but it's still same. Only attack fast if i move or target moves.
 
Try this one more time, if it doesn't work let me know. (I changed one thing)

Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        g_scheduler.addEvent(task);

        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}
 
Try this one more time, if it doesn't work let me know. (I changed one thing)

Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        g_scheduler.addEvent(task);

        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}
Still same.
P.S Didn't tried to create new voc
 
You do not need to create a new vocation.

I think I was reading the function wrong (I thought TFS 1.x has this working right but maybe not)

Try this:
Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }



        if (result) {
            lastAttack = OTSYS_TIME();
        } else {
            delay = SCHEDULER_MINTICKS;
        }
        SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        g_scheduler.addEvent(task);
    }
}
 
I don't think the problem has to do with the function, rather that the function is called only when the creature in question "thinks" as can be seen in game.cpp
Code:
creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
the onAttacking function in creature.cpp in turn calls the doAttacking function
Code:
if (g_game.isSightClear(getPosition(), attackedCreature->getPosition(), true)) {
    doAttacking(interval);
}
 
You do not need to create a new vocation.

I think I was reading the function wrong (I thought TFS 1.x has this working right but maybe not)

Try this:
Code:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }
Still same


        if (result) {
            lastAttack = OTSYS_TIME();
        } else {
            delay = SCHEDULER_MINTICKS;
        }
        SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        g_scheduler.addEvent(task);
    }
}
Still same.
 
No dude :D read my messages more closely. Items have nothing to do with attack speed.

Weapons.xml "can" have things to do with how attackspeed works. Don't be so fast to be rude to people, he was trying to help.
As for your issue, I am at a loss, honestly it should be working even with the earlier code and maybe even your original code.

The scheduler either does 50 ms or your attackspeed, which means you should be attacking super-fast even standing still.
Are you sure after you compile, you are copying the new TFS.exe over, and running the new server file?

What are you using to compile? Are you running your server on Windows or Linux?
I can only think you are doing something wrong, because if you are following the steps correctly it should be working.
 
Back
Top