• 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+ EXHAUST issue on Monsters

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
987
Solutions
14
Reaction score
532
I'm putting together a stun function but the exhaust portion doesn't quite work properly on monsters.

Lua:
local exhaustheal = createConditionObject(CONDITION_EXHAUST)
setConditionParam(exhaustheal, CONDITION_PARAM_SUBID, EXHAUST_HEALING)
setConditionParam(exhaustheal, CONDITION_PARAM_TICKS, 10000)

local exhaustcombat = createConditionObject(CONDITION_EXHAUST)
setConditionParam(exhaustcombat, CONDITION_PARAM_SUBID, EXHAUST_COMBAT)
setConditionParam(exhaustcombat, CONDITION_PARAM_TICKS, 10000)

creature:addCondition(exhaustcombat, true)
creature:addCondition(exhaustheal, true)

They still continue to heal/cast spells.
No console errors.

Are you unable to Exhaust monsters?
 
Solution
Lost my source files so I had to re-download and old revision I chucked on github and re-add the onSpawn and Loot customizations.
But I did it.

Re-compiled, tested.
So...

C++:
if (hasCondition(CONDITION_EXHAUST)) {

    return false;

}

Is not a valid CONST.

CONDITION_EXHAUST_COMBAT
CONDITION_EXHAUST_HEALING is.

C++:
if (hasCondition(CONDITION_EXHAUST_HEALING)) {

    return false;

}

Either way, those conditions don't seem to apply on monsters properly(?)
So I've settled for CONDITION_PACIFIED instead, its probably more appropriate anyway.

I've added the check to the following monster functions:

Stop monster from changing look direction (because its stunned)
C++:
void Monster::doAttacking(uint32_t interval)
{
    if...
You might need to edit this to check for exhaust condition:
C++:
bool Monster::canUseSpell(const Position& pos, const Position& targetPos,
                          const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks)
{
    inRange = true;

    if (sb.isMelee && isFleeing()) {
        return false;
    }

    if (extraMeleeAttack) {
        lastMeleeAttack = OTSYS_TIME();
    } else if (sb.isMelee && (OTSYS_TIME() - lastMeleeAttack) < 1500) {
        return false;
    }

    if (!sb.isMelee || !extraMeleeAttack) {
        if (sb.speed > attackTicks) {
            resetTicks = false;
            return false;
        }

        if (attackTicks % sb.speed >= interval) {
            //already used this spell for this round
            return false;
        }
    }

    if (sb.range != 0 && std::max<uint32_t>(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)) > sb.range) {
        inRange = false;
        return false;
    }
    return true;
}
 
Damn
My cpp is garbage too.

Something like:
C++:
if (!creature->hasFlag(PlayerFlag_HasNoExhaustion)) {
        return false;
}

C++:
bool Monster::canUseSpell(const Position& pos, const Position& targetPos,
                          const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks)
{
    inRange = true;


    if (!creature->hasFlag(PlayerFlag_HasNoExhaustion)) {
        return false;
    }
  
    if (creature->getMonster()
    if (sb.isMelee && isFleeing()) {
        return false;
    }

    if (extraMeleeAttack) {
        lastMeleeAttack = OTSYS_TIME();
    } else if (sb.isMelee && (OTSYS_TIME() - lastMeleeAttack) < 1500) {
        return false;
    }

    if (!sb.isMelee || !extraMeleeAttack) {
        if (sb.speed > attackTicks) {
            resetTicks = false;
            return false;
        }

        if (attackTicks % sb.speed >= interval) {
            //already used this spell for this round
            return false;
        }
    }

    if (sb.range != 0 && std::max<uint32_t>(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)) > sb.range) {
        inRange = false;
        return false;
    }
    return true;
}

EDIT: Looks like ->hasFlag doesn't even exist for Monsters.
What a can of worms O LAWD
 
Last edited:
This is interesting, should it be in the original sources? Or if you consider a custom option that does not exist in real tibia?

I mean it would be great to have as a built-in function.
I thought player/monsters were a subdivision of creatures so you can manipulate their actions in the same way, this seems like a scenario where it doesn't, so yes? I guess?

Need to re-download VC, re-compile and test some edge-cases though, like:

What if runes or other spells add exhaust condition to creatures? or does it specifically target players?
If any creature - this line of code could cause abuses were players find a weird way to exhaust a creature and preventing it from using spells.

C++:
if (creature->hasCondition(CONDITION_EXHAUST)) {
    return false;
}

My ignorance of the TFS codebase is bad, and I should feel bad.
 
Last edited:
My bad, my code snippet is completely wrong because there is no creature object within that function, it should be:
C++:
if (hasCondition(CONDITION_EXHAUST)) {
    return false;
}
OR
C++:
if (this->hasCondition(CONDITION_EXHAUST)) {
    return false;
}

This should only be checked when a monster attempts to use a spell they have defined in their XML file, if the monster has any kind of exhaust condition it will not be allowed to cast the spell.
 
My bad, my code snippet is completely wrong because there is no creature object within that function, it should be:
C++:
if (hasCondition(CONDITION_EXHAUST)) {
    return false;
}
OR
C++:
if (this->hasCondition(CONDITION_EXHAUST)) {
    return false;
}

This should only be checked when a monster attempts to use a spell they have defined in their XML file, if the monster has any kind of exhaust condition it will not be allowed to cast the spell.
Why do you have to use this explicitly in this case? the other variables of the object if they have access and hasCondition not? interesting mmm :eek:
 
Lost my source files so I had to re-download and old revision I chucked on github and re-add the onSpawn and Loot customizations.
But I did it.

Re-compiled, tested.
So...

C++:
if (hasCondition(CONDITION_EXHAUST)) {

    return false;

}

Is not a valid CONST.

CONDITION_EXHAUST_COMBAT
CONDITION_EXHAUST_HEALING is.

C++:
if (hasCondition(CONDITION_EXHAUST_HEALING)) {

    return false;

}

Either way, those conditions don't seem to apply on monsters properly(?)
So I've settled for CONDITION_PACIFIED instead, its probably more appropriate anyway.

I've added the check to the following monster functions:

Stop monster from changing look direction (because its stunned)
C++:
void Monster::doAttacking(uint32_t interval)
{
    if (!attackedCreature || (isSummon() && attackedCreature == this)) {
        return;
    }

    bool updateLook = true;
    bool resetTicks = interval != 0;
    attackTicks += interval;

    if (hasCondition(CONDITION_PACIFIED)) {
        updateLook = false;
    }
...
...

Stop monster from attacking
C++:
bool Monster::canUseAttack(const Position& pos, const Creature* target) const
{
    if (isHostile()) {
        const Position& targetPos = target->getPosition();
        uint32_t distance = std::max<uint32_t>(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos));
        for (const spellBlock_t& spellBlock : mType->attackSpells) {
            if (spellBlock.range != 0 && distance <= spellBlock.range) {
                return g_game.isSightClear(pos, targetPos, true);
            }
        }
        return false;
    }
    if (hasCondition(CONDITION_PACIFIED)) {
        return false;
    }
...
...

Stop monster from using offensive spells (possibly redundant due to check above, no harm either way)
C++:
bool Monster::canUseSpell(const Position& pos, const Position& targetPos,
                          const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks)
{
    inRange = true;

    if (hasCondition(CONDITION_PACIFIED)) {
        return false;
    }
...
...

Stop monster from healing/hasting etc
Going to test this one a bit more as its also probably redundant, and reseting the interval ticks will add an unecessary delay between the stun wearing off and them casting their next spell.

But maybe thats thematically a good thing, we'll see.
C++:
void Monster::onThinkDefense(uint32_t interval)
{
    bool resetTicks = true;
    defenseTicks += interval;

    if (hasCondition(CONDITION_PACIFIED)) {
        defenseTicks = 0;
    }
...
...

Stop monster from trying to move
C++:
bool Monster::getNextStep(Direction& dir, uint32_t& flags)
{
    if (isIdle || getHealth() <= 0) {
        //we dont have anyone watching might aswell stop walking
        eventWalk = 0;
        return false;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return false;
    }
...
...

Stop monster from trying to flee (also probably redundant, oh well)
C++:
bool Monster::getDistanceStep(const Position& targetPos, Direction& dir, bool flee /* = false */)
{
    const Position& creaturePos = getPosition();

    if (hasCondition(CONDITION_PACIFIED)) {
        return false;
    }
...
...

Works perfectly, monster literally stops in-place and it functions as a stun.
addCondition + addEvent with repeating CONST_ME_STUN for the duration and you have a stun system.

👍
 
Last edited:
Solution
Back
Top