• 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+ onHealthChange only detects healing (sabrehaven svn)

elnelson

Lunaria World Dev
Joined
Jun 20, 2009
Messages
591
Solutions
2
Reaction score
63
Location
México
Hello, im using this files [7.4, 7.8, 7.92, 8.0] Sabrehaven.com based on Nostalrius 7.7 fork (https://otland.net/threads/7-4-7-8-7-92-8-0-sabrehaven-com-based-on-nostalrius-7-7-fork.283603/) on 8.0 branch but seems like onHealthChange doesnt work on negative values (only work when healing)

im trying to get the values using prints but i as i said before it doesnt work on damage.


LUA:
function onHealthChange(creature, attacker, value, type, min, max, origin)
    -- Imprimir todas las variables recibidas
    print("=== onHealthChange Called ===")
    print("Creature:", creature and creature:getName() or "nil")
    print("Attacker:", attacker and attacker:getName() or "nil")
    print("Value (Damage/Heal):", value)
    print("Type:", type)
    print("Min:", min)
    print("Max:", max)
    print("Origin:", origin)
  
    -- Identificar el tipo de daño o curación
    if type == COMBAT_HEALING then
        print("Effect: Healing")
    else
        print("Effect: Damage")
    end

    -- Identificar el origen del daño
    if origin == ORIGIN_MELEE then
        print("Damage Origin: Melee Attack")
    elseif origin == ORIGIN_RANGED then
        print("Damage Origin: Ranged Attack")
    elseif origin == ORIGIN_SPELL then
        print("Damage Origin: Spell")
    elseif origin == ORIGIN_CONDITION then
        print("Damage Origin: Condition (e.g., poison, fire)")
    elseif origin == ORIGIN_UNKNOWN then
        print("Damage Origin: Unknown")
    else
        print("Damage Origin: Other (" .. tostring(origin) .. ")")
    end

    -- Retornar los valores sin modificar (puedes ajustarlo si necesitas)
    return value, type, min, max
end

the original creatureevent.cpp is this:


C++:
void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage)
{
    //onHealthChange(creature, attacker, value, type, min, max, origin)
    if (!scriptInterface->reserveScriptEnv()) {
        std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl;
        return;
    }

    ScriptEnvironment* env = scriptInterface->getScriptEnv();
    env->setScriptId(scriptId, scriptInterface);

    lua_State* L = scriptInterface->getLuaState();
    scriptInterface->pushFunction(scriptId);

    LuaScriptInterface::pushUserdata(L, creature);
    LuaScriptInterface::setCreatureMetatable(L, -1, creature);
    if (attacker) {
        LuaScriptInterface::pushUserdata(L, attacker);
        LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
    }
    else {
        lua_pushnil(L);
    }

    LuaScriptInterface::pushCombatDamage(L, damage);

    if (scriptInterface->protectedCall(L, 7, 4) != 0) {
        LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
    }
    else {
        damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
        damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
        damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
        damage.max = LuaScriptInterface::getNumber<CombatType_t>(L, -1);

        lua_pop(L, 4);
        if (damage.type != COMBAT_HEALING) {
            damage.value = -damage.value;
        }
    }

    scriptInterface->resetScriptEnv();
}

i asked to god gpt to fix it, but it keep failing, any ideas how to solve this its driving me crazy u.u

note: i've already registered event on login and also tried on creature but keeps failing :S
 
Solution
this bug its from game.cpp

function:
C++:
bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage, bool sendEffect)

add under:
C++:
int32_t realDamage = damage.value;
if (realDamage == 0) {
   return true;
}


this:
C++:
 if (damage.origin != ORIGIN_NONE) {
       const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
            if (!events.empty()) {
                for (CreatureEvent* creatureEvent : events) {
                    creatureEvent->executeHealthChange(target, attacker, damage);
                }
                damage.origin = ORIGIN_NONE;
                return combatChangeHealth(attacker, target, damage);
            }
 }
this bug its from game.cpp

function:
C++:
bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage, bool sendEffect)

add under:
C++:
int32_t realDamage = damage.value;
if (realDamage == 0) {
   return true;
}


this:
C++:
 if (damage.origin != ORIGIN_NONE) {
       const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
            if (!events.empty()) {
                for (CreatureEvent* creatureEvent : events) {
                    creatureEvent->executeHealthChange(target, attacker, damage);
                }
                damage.origin = ORIGIN_NONE;
                return combatChangeHealth(attacker, target, damage);
            }
 }
 
Solution
this bug its from game.cpp

function:
C++:
bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage, bool sendEffect)

add under:
C++:
int32_t realDamage = damage.value;
if (realDamage == 0) {
   return true;
}


this:
C++:
bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage)
{
    const Position& targetPos = target->getPosition();
    if (damage.value > 0) {
        if (target->getHealth() <= 0) {
            return false;
        }
        if (damage.origin != ORIGIN_NONE) {
            const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
            if (!events.empty()) {
                for (CreatureEvent* creatureEvent : events) {
                    creatureEvent->executeHealthChange(target, attacker, damage);
                }
                damage.origin = ORIGIN_NONE;
                return combatChangeHealth(attacker, target, damage);
            }
        }
        int32_t realHealthChange = target->getHealth();
        target->gainHealth(attacker, damage.value);
        realHealthChange = target->getHealth() - realHealthChange;
        if (realHealthChange > 0 && !target->isInGhostMode()) {
            addMagicEffect(targetPos, CONST_ME_MAGIC_BLUE);
        }
    }
    else {
        if (Monster* monster = target->getMonster()) {
            // makes monsters aggressive when damaged
            // basically stands for UNDERATTACK stance under CipSoft servers
            // the attacker must be valid everytime (avoid field ticks damage to trigger condition)
            if (!monster->hasCondition(CONDITION_AGGRESSIVE) && attacker) {
                Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_AGGRESSIVE, 3000);
                monster->addCondition(condition, true);
            }
        }
        if (!target->isAttackable()) {
            if (!target->isInGhostMode()) {
                addMagicEffect(targetPos, CONST_ME_POFF);
            }
            return true;
        }
        Player* attackerPlayer;
        if (attacker) {
            attackerPlayer = attacker->getPlayer();
        }
        else {
            attackerPlayer = nullptr;
        }
        damage.value = std::abs(damage.value);
        int32_t healthChange = damage.value;
        if (healthChange == 0) {
            return true;
        }
        Player* targetPlayer = target->getPlayer();
        SpectatorVec spectators;
        if (target->hasCondition(CONDITION_MANASHIELD) && damage.type != COMBAT_UNDEFINEDDAMAGE) {
            int32_t manaDamage = std::min<int32_t>(targetPlayer->getMana(), healthChange);
            if (manaDamage != 0) {
                if (damage.origin != ORIGIN_NONE) {
                    const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
                    if (!events.empty()) {
                        for (CreatureEvent* creatureEvent : events) {
                            creatureEvent->executeManaChange(target, attacker, damage);
                        }
                        healthChange = damage.value;
                        if (healthChange == 0) {
                            return true;
                        }
                        manaDamage = std::min<int32_t>(targetPlayer->getMana(), healthChange);
                    }
                }
                targetPlayer->drainMana(attacker, manaDamage);
                map.getSpectators(spectators, targetPos, true, true);
                addMagicEffect(spectators, targetPos, CONST_ME_LOSEENERGY);
                std::string damageString = std::to_string(manaDamage);
                if (targetPlayer) {
                    std::stringstream ss;
                    if (!attacker) {
                        ss << "You lose " << damageString << " mana.";
                    }
                    else if (targetPlayer == attackerPlayer) {
                        ss << "You lose " << damageString << " mana due to your own attack.";
                    }
                    else {
                        ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.';
                    }
                    targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str());
                }
                for (Creature* spectator : spectators) {
                    Player* tmpPlayer = spectator->getPlayer();
                    tmpPlayer->sendAnimatedText(targetPos, TEXTCOLOR_BLUE, damageString);
                }
                damage.value -= manaDamage;
                if (damage.value < 0) {
                    damage.value = 0;
                }
            }
        }
        int32_t realDamage = damage.value;
        if (realDamage == 0) {
            return true;
        }
        int32_t targetHealth = target->getHealth();
        if (damage.value >= targetHealth) {
            damage.value = targetHealth;
        }
        realDamage = damage.value;
        if (realDamage == 0) {
            return true;
        }
        if (spectators.empty()) {
            map.getSpectators(spectators, targetPos, true, true);
        }
        TextColor_t color = TEXTCOLOR_NONE;
        uint8_t hitEffect;
        if (damage.value) {
            combatGetTypeInfo(damage.type, target, color, hitEffect);
            if (hitEffect != CONST_ME_NONE) {
                addMagicEffect(spectators, targetPos, hitEffect);
            }
        }
        if (color != TEXTCOLOR_NONE) {
            std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint");
            if (targetPlayer) {
                std::stringstream ss;
                if (!attacker) {
                    ss << "You lose " << damageString << ".";
                }
                else if (targetPlayer == attackerPlayer) {
                    ss << "You lose " << damageString << " due to your own attack.";
                }
                else {
                    ss << "You lose " << damageString << " due to an attack by " << attacker->getNameDescription() << '.';
                }
                targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str());
            }
            std::string realDamageStr = std::to_string(realDamage);
            for (Creature* spectator : spectators) {
                Player* tmpPlayer = spectator->getPlayer();
                tmpPlayer->sendAnimatedText(targetPos, color, realDamageStr);
            }
        }
        if (realDamage >= targetHealth) {
            for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) {
                if (!creatureEvent->executeOnPrepareDeath(target, attacker)) {
                    return false;
                }
            }
        }
        target->drainHealth(attacker, realDamage);
        addCreatureHealth(spectators, target);
    }
    return true;
}
[/QUOTE]

}



edit: I FOUND THE SOLUTION :D:
before:
Code:
damage.value = std::abs(damage.value);
i added this patch:

C++:
if (damage.origin != ORIGIN_NONE) {
    const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
    if (!events.empty()) {
        for (CreatureEvent* creatureEvent : events) {
            creatureEvent->executeHealthChange(target, attacker, damage);
        }
    }
}
and voalá:

1739242560747.webp
 
Last edited:
Back
Top