• 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!
  • 2026 staff recruitment is open! Check it out and consider applying!

TFS 0.X Don't delete players on death

Izaack

Member
Joined
Jun 18, 2012
Messages
89
Solutions
2
Reaction score
23
Location
México
Hello Otland, I'm modifying my Otxserv 2 sources and I'm implementing a way for you not to close the session when you die. I've already achieved that. What I can't achieve is clearing the damage that the player does before dying, which means that after dying the player continues to gain monsters, experience, and loot if he was the one who inflicted the most damage. I want him to lose all of that when he dies. I'm sharing my OnDeath with you. I hope you can help me. Thank you for your attention.
C++:
bool Player::onDeath()
{
    Item *preventLoss = NULL, *preventDrop = NULL;
    if(getZone() == ZONE_HARDCORE)
    {
        setDropLoot(LOOT_DROP_NONE);
        setLossSkill(false);
    }
    else if(skull < SKULL_RED)
    {
        Item* item = NULL;
        for(int32_t i = SLOT_FIRST; ((!preventDrop || !preventLoss) && i < SLOT_LAST); ++i)
        {
            if(!(item = getInventoryItem((slots_t)i)) || item->isRemoved() ||
                (g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)i)))
                continue;

            const ItemType& it = Item::items[item->getID()];
            if(!it.hasAbilities())
                continue;

            if(lootDrop == LOOT_DROP_FULL && it.abilities->preventDrop)
            {
                setDropLoot(LOOT_DROP_PREVENT);
                preventDrop = item;
            }

            if(skillLoss && !preventLoss && it.abilities->preventLoss)
            {
                preventLoss = item;
                if(preventLoss != preventDrop && it.abilities->preventDrop)
                    preventDrop = item;
            }
        }
    }

    if(!Creature::onDeath())
    {
        if(preventDrop)
            setDropLoot(LOOT_DROP_FULL);

        return false;
    }

    uint32_t totalDamage = 0, pvpDamage = 0, opponents = 0;
    for(CountMap::iterator it = damageMap.begin(); it != damageMap.end(); ++it)
    {
        if(((OTSYS_TIME() - it->second.ticks) / 1000) > g_config.getNumber(
            ConfigManager::FAIRFIGHT_TIMERANGE))
            continue;

        totalDamage += it->second.total;
        if(Creature* creature = g_game.getCreatureByID(it->first))
        {
            Player* player = creature->getPlayer();
            if(!player)
                player = creature->getPlayerMaster();

            if(!player)
                continue;

            opponents += player->getLevel();
            pvpDamage += it->second.total;
        }
    }

    bool usePVPBlessing = false;
    if(preventLoss)
    {
        setLossSkill(false);
        g_game.transformItem(preventLoss, preventLoss->getID(), std::max(0, (int32_t)preventLoss->getCharges() - 1));
    }
    else if(pvpBlessing && (int32_t)std::floor((100. * pvpDamage) / std::max(
        1U, totalDamage)) >= g_config.getNumber(ConfigManager::PVP_BLESSING_THRESHOLD))
        usePVPBlessing = true;

    if(preventDrop && preventDrop != preventLoss && !usePVPBlessing)
        g_game.transformItem(preventDrop, preventDrop->getID(), std::max(0, (int32_t)preventDrop->getCharges() - 1));

    removeConditions(CONDITIONEND_DEATH);
    if(skillLoss)
    {
        double reduction = 1.;
        if(g_config.getBool(ConfigManager::FAIRFIGHT_REDUCTION) && opponents > level)
            reduction -= (double)level / opponents;

        uint64_t lossExperience = (uint64_t)std::floor(reduction * getLostExperience()), currExperience = experience;
        removeExperience(lossExperience, false);
        double percent = 1. - ((double)(currExperience - lossExperience) / std::max((uint64_t)1, currExperience));

        // magic level loss
        uint64_t sumMana = 0, lostMana = 0;
        for(uint32_t i = 1; i <= magLevel; ++i)
            sumMana += vocation->getReqMana(i);

        sumMana += manaSpent;
        lostMana = (uint64_t)std::ceil((percent * lossPercent[LOSS_MANA] / 100.) * sumMana);
        while(lostMana > manaSpent && magLevel > 0)
        {
            lostMana -= manaSpent;
            manaSpent = vocation->getReqMana(magLevel);
            magLevel--;
        }

        manaSpent -= lostMana;
        uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
        if(nextReqMana > vocation->getReqMana(magLevel))
            magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
        else
            magLevelPercent = 0;

        // skill loss
        uint64_t lostSkillTries, sumSkillTries;
        for(int16_t i = 0; i < 7; ++i) // for each skill
        {
            lostSkillTries = sumSkillTries = 0;
            for(uint32_t c = 11; c <= skills[i][SKILL_LEVEL]; ++c) // sum up all required tries for all skill levels
                sumSkillTries += vocation->getReqSkillTries(i, c);

            sumSkillTries += skills[i][SKILL_TRIES];
            lostSkillTries = (uint64_t)std::ceil((percent * lossPercent[LOSS_SKILLS] / 100.) * sumSkillTries);
            while(lostSkillTries > skills[i][SKILL_TRIES] && skills[i][SKILL_LEVEL] > 10)
            {
                lostSkillTries -= skills[i][SKILL_TRIES];
                skills[i][SKILL_TRIES] = vocation->getReqSkillTries(i, skills[i][SKILL_LEVEL]);
                skills[i][SKILL_LEVEL]--;
            }

            skills[i][SKILL_TRIES] -= lostSkillTries;
        }

        if(usePVPBlessing)
            pvpBlessing = false;
        else
            blessings = 0;

        loginPosition = masterPosition;
        if(vocationId > VOCATION_NONE && g_config.getBool(ConfigManager::ROOK_SYSTEM) &&
            level <= (uint32_t)g_config.getNumber(ConfigManager::ROOK_LEVELTO))
        {
            if(Town* rook = Towns::getInstance()->getTown(g_config.getNumber(ConfigManager::ROOK_TOWN)))
            {
                level = 1;
                soulMax = soul = 100;
                capacity = 400;
                stamina = STAMINA_MAX;
                health = healthMax = 150;
                loginPosition = masterPosition = rook->getPosition();
                experience = magLevel = manaSpent = mana = manaMax = balance = marriage = 0;
                promotionLevel = defaultOutfit.lookAddons = 0;

                setTown(rook->getID());
                setVocation(0);
                leaveGuild();

                storageMap.clear();
                for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
                {
                    skills[i][SKILL_LEVEL] = 10;
                    skills[i][SKILL_TRIES] = 0;
                }

                for(uint32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
                {
                    if(inventory[i])
                        g_game.internalRemoveItem(NULL, inventory[i]);
                }
            }
        }
        health = healthMax;
        mana = manaMax;
 
 removeConditions(CONDITIONEND_DEATH);
removeCondition(CONDITION_FIRE);
removeCondition(CONDITION_POISON);
removeCondition(CONDITION_ENERGY);
removeCondition(CONDITION_BLEEDING);
removeCondition(CONDITION_DROWN);
removeCondition(CONDITION_PARALYZE);
removeCondition(CONDITION_INVISIBLE);
removeCondition(CONDITION_HASTE);
removeCondition(CONDITION_CURSED);
 
        sendStats();
        sendIcons();
        sendSkills();
        onIdleStatus();
       
// Mostrar en consola que murió
std::cout << getName() << " has died." << std::endl;

// Detener follow si lo había
setFollowCreature(nullptr);

// Cerrar tienda si estaba abierta
closeShopWindow();

// Salir de party si pertenece a una
if (getParty()) {
    getParty()->leave(this);
}
clearPartyInvitations();

// Quitar target visualmente
setAttackedCreature(nullptr);
sendCancelTarget();

// Mostrar vida falsa (puedes ajustar esto si haces revive)
g_game.addCreatureHealth(this);

// Teleportar al templo
g_game.internalTeleport(this, masterPosition, true);
 
        /* g_creatureEvents->playerLogout(this, true);
        g_game.removeCreature(this, false);
        sendReLoginWindow(); */
    }
    else
    {
        setLossSkill(true);
        if(preventLoss)
        {
            loginPosition = masterPosition;
            g_creatureEvents->playerLogout(this, true);

            g_game.removeCreature(this, false);
            sendReLoginWindow();
        }
    }

    return true;
}

I am using this distribution GitHub - mattyx14/otxserver at otxserv2 (https://github.com/mattyx14/otxserver/tree/otxserv2)
Post automatically merged:

Any?
 
Last edited:
Generate new playerID delete the player pointer and create it again or make a new way to change current playerID (cid), anyway the server will have to reset the session, since it uses playerID on client side so it will be unknown player over a known session which will eventually require reset or will be done by applying the new info when sendAddCreature is called from serverSide
 
Generate new playerID delete the player pointer and create it again or make a new way to change current playerID (cid), anyway the server will have to reset the session, since it uses playerID on client side so it will be unknown player over a known session which will eventually require reset or will be done by applying the new info when sendAddCreature is called from serverSide
This will solve it, but its a roundabout trick.
Ask yourself why clearing cid even solve it? Because its used in collections that use it as an index to store the data about e.g. damage dealt in this case. So all he gotta do is clear that. I dont wanna browse srces on my phone tho so wont be able to say which ones


Nvm, death without changing cid will bring other problems such as spells on timer still affecting such player, so must do it anyway.
 
Last edited:
Hello Otland, I'm modifying my Otxserv 2 sources and I'm implementing a way for you not to close the session when you die. I've already achieved that. What I can't achieve is clearing the damage that the player does before dying, which means that after dying the player continues to gain monsters, experience, and loot if he was the one who inflicted the most damage. I want him to lose all of that when he dies. I'm sharing my OnDeath with you. I hope you can help me. Thank you for your attention.
C++:
bool Player::onDeath()
{
    Item *preventLoss = NULL, *preventDrop = NULL;
    if(getZone() == ZONE_HARDCORE)
    {
        setDropLoot(LOOT_DROP_NONE);
        setLossSkill(false);
    }
    else if(skull < SKULL_RED)
    {
        Item* item = NULL;
        for(int32_t i = SLOT_FIRST; ((!preventDrop || !preventLoss) && i < SLOT_LAST); ++i)
        {
            if(!(item = getInventoryItem((slots_t)i)) || item->isRemoved() ||
                (g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)i)))
                continue;

            const ItemType& it = Item::items[item->getID()];
            if(!it.hasAbilities())
                continue;

            if(lootDrop == LOOT_DROP_FULL && it.abilities->preventDrop)
            {
                setDropLoot(LOOT_DROP_PREVENT);
                preventDrop = item;
            }

            if(skillLoss && !preventLoss && it.abilities->preventLoss)
            {
                preventLoss = item;
                if(preventLoss != preventDrop && it.abilities->preventDrop)
                    preventDrop = item;
            }
        }
    }

    if(!Creature::onDeath())
    {
        if(preventDrop)
            setDropLoot(LOOT_DROP_FULL);

        return false;
    }

    uint32_t totalDamage = 0, pvpDamage = 0, opponents = 0;
    for(CountMap::iterator it = damageMap.begin(); it != damageMap.end(); ++it)
    {
        if(((OTSYS_TIME() - it->second.ticks) / 1000) > g_config.getNumber(
            ConfigManager::FAIRFIGHT_TIMERANGE))
            continue;

        totalDamage += it->second.total;
        if(Creature* creature = g_game.getCreatureByID(it->first))
        {
            Player* player = creature->getPlayer();
            if(!player)
                player = creature->getPlayerMaster();

            if(!player)
                continue;

            opponents += player->getLevel();
            pvpDamage += it->second.total;
        }
    }

    bool usePVPBlessing = false;
    if(preventLoss)
    {
        setLossSkill(false);
        g_game.transformItem(preventLoss, preventLoss->getID(), std::max(0, (int32_t)preventLoss->getCharges() - 1));
    }
    else if(pvpBlessing && (int32_t)std::floor((100. * pvpDamage) / std::max(
        1U, totalDamage)) >= g_config.getNumber(ConfigManager::PVP_BLESSING_THRESHOLD))
        usePVPBlessing = true;

    if(preventDrop && preventDrop != preventLoss && !usePVPBlessing)
        g_game.transformItem(preventDrop, preventDrop->getID(), std::max(0, (int32_t)preventDrop->getCharges() - 1));

    removeConditions(CONDITIONEND_DEATH);
    if(skillLoss)
    {
        double reduction = 1.;
        if(g_config.getBool(ConfigManager::FAIRFIGHT_REDUCTION) && opponents > level)
            reduction -= (double)level / opponents;

        uint64_t lossExperience = (uint64_t)std::floor(reduction * getLostExperience()), currExperience = experience;
        removeExperience(lossExperience, false);
        double percent = 1. - ((double)(currExperience - lossExperience) / std::max((uint64_t)1, currExperience));

        // magic level loss
        uint64_t sumMana = 0, lostMana = 0;
        for(uint32_t i = 1; i <= magLevel; ++i)
            sumMana += vocation->getReqMana(i);

        sumMana += manaSpent;
        lostMana = (uint64_t)std::ceil((percent * lossPercent[LOSS_MANA] / 100.) * sumMana);
        while(lostMana > manaSpent && magLevel > 0)
        {
            lostMana -= manaSpent;
            manaSpent = vocation->getReqMana(magLevel);
            magLevel--;
        }

        manaSpent -= lostMana;
        uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
        if(nextReqMana > vocation->getReqMana(magLevel))
            magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
        else
            magLevelPercent = 0;

        // skill loss
        uint64_t lostSkillTries, sumSkillTries;
        for(int16_t i = 0; i < 7; ++i) // for each skill
        {
            lostSkillTries = sumSkillTries = 0;
            for(uint32_t c = 11; c <= skills[i][SKILL_LEVEL]; ++c) // sum up all required tries for all skill levels
                sumSkillTries += vocation->getReqSkillTries(i, c);

            sumSkillTries += skills[i][SKILL_TRIES];
            lostSkillTries = (uint64_t)std::ceil((percent * lossPercent[LOSS_SKILLS] / 100.) * sumSkillTries);
            while(lostSkillTries > skills[i][SKILL_TRIES] && skills[i][SKILL_LEVEL] > 10)
            {
                lostSkillTries -= skills[i][SKILL_TRIES];
                skills[i][SKILL_TRIES] = vocation->getReqSkillTries(i, skills[i][SKILL_LEVEL]);
                skills[i][SKILL_LEVEL]--;
            }

            skills[i][SKILL_TRIES] -= lostSkillTries;
        }

        if(usePVPBlessing)
            pvpBlessing = false;
        else
            blessings = 0;

        loginPosition = masterPosition;
        if(vocationId > VOCATION_NONE && g_config.getBool(ConfigManager::ROOK_SYSTEM) &&
            level <= (uint32_t)g_config.getNumber(ConfigManager::ROOK_LEVELTO))
        {
            if(Town* rook = Towns::getInstance()->getTown(g_config.getNumber(ConfigManager::ROOK_TOWN)))
            {
                level = 1;
                soulMax = soul = 100;
                capacity = 400;
                stamina = STAMINA_MAX;
                health = healthMax = 150;
                loginPosition = masterPosition = rook->getPosition();
                experience = magLevel = manaSpent = mana = manaMax = balance = marriage = 0;
                promotionLevel = defaultOutfit.lookAddons = 0;

                setTown(rook->getID());
                setVocation(0);
                leaveGuild();

                storageMap.clear();
                for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
                {
                    skills[i][SKILL_LEVEL] = 10;
                    skills[i][SKILL_TRIES] = 0;
                }

                for(uint32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
                {
                    if(inventory[i])
                        g_game.internalRemoveItem(NULL, inventory[i]);
                }
            }
        }
        health = healthMax;
        mana = manaMax;
 
 removeConditions(CONDITIONEND_DEATH);
removeCondition(CONDITION_FIRE);
removeCondition(CONDITION_POISON);
removeCondition(CONDITION_ENERGY);
removeCondition(CONDITION_BLEEDING);
removeCondition(CONDITION_DROWN);
removeCondition(CONDITION_PARALYZE);
removeCondition(CONDITION_INVISIBLE);
removeCondition(CONDITION_HASTE);
removeCondition(CONDITION_CURSED);
 
        sendStats();
        sendIcons();
        sendSkills();
        onIdleStatus();
  
// Mostrar en consola que murió
std::cout << getName() << " has died." << std::endl;

// Detener follow si lo había
setFollowCreature(nullptr);

// Cerrar tienda si estaba abierta
closeShopWindow();

// Salir de party si pertenece a una
if (getParty()) {
    getParty()->leave(this);
}
clearPartyInvitations();

// Quitar target visualmente
setAttackedCreature(nullptr);
sendCancelTarget();

// Mostrar vida falsa (puedes ajustar esto si haces revive)
g_game.addCreatureHealth(this);

// Teleportar al templo
g_game.internalTeleport(this, masterPosition, true);
 
        /* g_creatureEvents->playerLogout(this, true);
        g_game.removeCreature(this, false);
        sendReLoginWindow(); */
    }
    else
    {
        setLossSkill(true);
        if(preventLoss)
        {
            loginPosition = masterPosition;
            g_creatureEvents->playerLogout(this, true);

            g_game.removeCreature(this, false);
            sendReLoginWindow();
        }
    }

    return true;
}

I am using this distribution GitHub - mattyx14/otxserver at otxserv2 (https://github.com/mattyx14/otxserver/tree/otxserv2)
Post automatically merged:

Any?
Hey man,
So I checked out your onDeath() try this approach..
(in creature.h):
C++:
void removeDamagePoints(uint32_t attackerId);
Then in creature.cpp,
C++:
void Creature::removeDamagePoints(uint32_t attackerId)
{
    damageMap.erase(attackerId);
}
Just wipes the attacker’s damage from the map, no fuss.

Finally, inside Player::onDeath(), right after teleporting the player...
C++:
for(AutoList<Creature>::iterator it = g_game.autoList.begin(); it != g_game.autoList.end(); ++it)
{
    Creature* creature = it->second;
    if(creature && !creature->isRemoved() && creature != this)
    {
        creature->removeDamagePoints(this->getID());
    }
}
So yeah, with this setup, once the player dies, the server loops through every creature and clears any damage that player had dealt
Should totally stop them from snagging XP or loot from monsters they hit before dying Ig👍

Reminder: you need 2 remove player's damage contribution from the creature's damage map so it should be working..
 
Last edited:
This will solve it, but its a roundabout trick.
Ask yourself why clearing cid even solve it? Because its used in collections that use it as an index to store the data about e.g. damage dealt in this case. So all he gotta do is clear that. I dont wanna browse srces on my phone tho so wont be able to say which ones


Nvm, death without changing cid will bring other problems such as spells on timer still affecting such player, so must do it anyway.
Thank you for taking from your time to proof my statement is correct, i cannot waste time by explaining all the reasons deleting a player on death is crucial but i can guide you towards completion of your idea without interrupting anything else in TFS
 

Similar threads

Back
Top