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

C++ Remove summon from experience map

zxmatzx

Advanced OT User
Joined
Dec 1, 2010
Messages
311
Solutions
27
Reaction score
154
Location
Brazil
GitHub
Mateuso8
Hi,
I need to change summon from experience map to master, then the master will receive the experience that summon would recive. I already blocked summon to recive experience, but if summon and player damage the creature, when the experience map is constructed, the experience wouldn't all to player, because some part is owned by summon.

The problem is:
When Player kill a rotworm alone, gain 40 experience points.
When Summon kill a rotworm alone, the player gain 40 experience points.
When Player(18 damage) and Summon(47 damage) kill a rotworm, player only gain 39 experience point.

I don't sure that the problem is in the experience map, just a guess.

Using TFS 1.2

In creature.cpp:

C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Creature*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
                
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end()) {
                    experienceMap[attacker] = gainExp;
                } else {
                    tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
I compiled with this change, and don't is working, i don't know much about C++, maybe i forgot something or just my approach isn't right. Any help will be welcome.
Thanks.
 
Solution
@itachi_
xD

@zxmatzx
From what you writed in first post it clearly isn't experienceMap issue but damageMap issue.
Change your Creature::addDamagePoints to:
Code:
void Creature::addDamagePoints(Creature* attacker, int32_t damagePoints)
{
    if (damagePoints <= 0) {
        return;
    }

    uint32_t attackerId = (attacker->isSummon() ? attacker->getMaster()->getID() : attacker->id);

    auto it = damageMap.find(attackerId);
    if (it == damageMap.end()) {
        CountBlock_t cb;
        cb.ticks = OTSYS_TIME();
        cb.total = damagePoints;
        damageMap[attackerId] = cb;
    } else {
        it->second.total += damagePoints;
        it->second.ticks = OTSYS_TIME();
    }

    lastHitCreatureId = attackerId;
}
and...
Hi,
I need to change summon from experience map to master, then the master will receive the experience that summon would recive. I already blocked summon to recive experience, but if summon and player damage the creature, when the experience map is constructed, the experience wouldn't all to player, because some part is owned by summon.

The problem is:
When Player kill a rotworm alone, gain 40 experience points.
When Summon kill a rotworm alone, the player gain 40 experience points.
When Player(18 damage) and Summon(47 damage) kill a rotworm, player only gain 39 experience point.

I don't sure that the problem is in the experience map, just a guess.

Using TFS 1.2

In creature.cpp:

C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Creature*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
               
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end()) {
                    experienceMap[attacker] = gainExp;
                } else {
                    tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
I compiled with this change, and don't is working, i don't know much about C++, maybe i forgot something or just my approach isn't right. Any help will be welcome.
Thanks.
could u try this
C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Creature*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
                
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end()) {
               //     experienceMap[attacker] = gainExp;
                } else {
                 //   tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
 
could u try this
C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Creature*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
               
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end()) {
               //     experienceMap[attacker] = gainExp;
                } else {
                 //   tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
Thanks for answer, by commenting this lines the experience map will not be filled. No one recieve exp.
This code block check if the attacker have a index in the experience map, if don't have, give the last index in map to attacker and set the value as gainExp, if the attacker already exist in the map, just incress the value.
Again, thanks for answer, but not worked.
 
Throwing a comment in it is not a solution!
C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Player*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
                
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end() && (Player* player = attacker->getPlayer())) {
                    experienceMap[player] = gainExp;
                } else {
                    tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
 
Throwing a comment in it is not a solution!
C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Player*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
               
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end() && (Player* player = attacker->getPlayer())) {
                    experienceMap[player] = gainExp;
                } else {
                    tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
Thanks for answer. I can't test now, tomorow i give you feedback if worked.
 
@itachi_
xD

@zxmatzx
From what you writed in first post it clearly isn't experienceMap issue but damageMap issue.
Change your Creature::addDamagePoints to:
Code:
void Creature::addDamagePoints(Creature* attacker, int32_t damagePoints)
{
    if (damagePoints <= 0) {
        return;
    }

    uint32_t attackerId = (attacker->isSummon() ? attacker->getMaster()->getID() : attacker->id);

    auto it = damageMap.find(attackerId);
    if (it == damageMap.end()) {
        CountBlock_t cb;
        cb.ticks = OTSYS_TIME();
        cb.total = damagePoints;
        damageMap[attackerId] = cb;
    } else {
        it->second.total += damagePoints;
        it->second.ticks = OTSYS_TIME();
    }

    lastHitCreatureId = attackerId;
}
and it should work correctly because all the damage monster took due to summon attack will be given to summon master, so there shouldn't be any rounding issue when calculating experience.
 
Solution
@itachi_
xD

@zxmatzx
From what you writed in first post it clearly isn't experienceMap issue but damageMap issue.
Change your Creature::addDamagePoints to:
Code:
void Creature::addDamagePoints(Creature* attacker, int32_t damagePoints)
{
    if (damagePoints <= 0) {
        return;
    }

    uint32_t attackerId = (attacker->isSummon() ? attacker->getMaster()->getID() : attacker->id);

    auto it = damageMap.find(attackerId);
    if (it == damageMap.end()) {
        CountBlock_t cb;
        cb.ticks = OTSYS_TIME();
        cb.total = damagePoints;
        damageMap[attackerId] = cb;
    } else {
        it->second.total += damagePoints;
        it->second.ticks = OTSYS_TIME();
    }

    lastHitCreatureId = attackerId;
}
and it should work correctly because all the damage monster took due to summon attack will be given to summon master, so there shouldn't be any rounding issue when calculating experience.
Hello,
I thought about do this, but if i need to access the damageMap, i will not have the real data about damage. Because of this, i tryed to change the experience map to read the attacker and identify if is a player summon and change the attacker to master. I think doing your change should work, because experience map constructor will only read Player data, but i don't want do by this way, realy thanks for your answer, was very helpfull and i learned with. If i have no choice will use this. I don't tested Delusion's code yet, but i think will fit my necessity. Thanks all for help.
 
-Sorry, i forgot to edit my previous post
Throwing a comment in it is not a solution!
C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Player*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
              
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker);
                if (tmpIt == experienceMap.end() && (Player* player = attacker->getPlayer())) {
                    experienceMap[player] = gainExp;
                } else {
                    tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
Hi,
To compile your code i have to change some parts, here is the code after i change:

C++:
void Creature::onDeath()
{
    bool lastHitUnjustified = false;
    bool mostDamageUnjustified = false;
    Creature* lastHitCreature = g_game.getCreatureByID(lastHitCreatureId);
    Creature* lastHitCreatureMaster;
    if (lastHitCreature) {
        lastHitUnjustified = lastHitCreature->onKilledCreature(this);
        lastHitCreatureMaster = lastHitCreature->getMaster();
    } else {
        lastHitCreatureMaster = nullptr;
    }

    Creature* mostDamageCreature = nullptr;

    const int64_t timeNow = OTSYS_TIME();
    const uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
    int32_t mostDamage = 0;
    std::map<Player*, uint64_t> experienceMap;
    for (const auto& it : damageMap) {
        if (Creature* attacker = g_game.getCreatureByID(it.first)) {
            CountBlock_t cb = it.second;
            if ((cb.total > mostDamage && (timeNow - cb.ticks <= inFightTicks))) {
                mostDamage = cb.total;
                mostDamageCreature = attacker;
            }

            if (attacker != this) {
                uint64_t gainExp = getGainedExperience(attacker);
                if (Player* player = attacker->getPlayer()) {
                    Party* party = player->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }
               
                //Check if attacker is summon
                if (attacker->isSummon()) {
                    //Check if is a player summon, then change the attacker for master
                    if (Player* attackerMaster = attacker->getMaster()->getPlayer()) {
                        attacker = attackerMaster;
                    }
                }

                auto tmpIt = experienceMap.find(attacker->getPlayer());
                if (tmpIt == experienceMap.end()) {
                    if (Player* player = attacker->getPlayer())
                    {
                        experienceMap[player] = gainExp;
                    }
                } else {
                    tmpIt->second += gainExp;
                }
            }
        }
    }

    for (const auto& it : experienceMap) {
        it.first->onGainExperience(it.second, this);
    }

    if (mostDamageCreature) {
        if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) {
            Creature* mostDamageCreatureMaster = mostDamageCreature->getMaster();
            if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

    bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    death(lastHitCreature);

    if (master) {
        master->removeSummon(this);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}
I changed line 45 that was returning a error to getPlayer(), because attacker is a Creature type and can't be find in map because u changed the type of map for Player type. And splited the IF in line 46 because the compiler returned that was missing a ).

After test, the error is the same that i reported:
When Player kill a rotworm alone, gain 40 experience points.
When Summon kill a rotworm alone, the player gain 40 experience points.
When Player(1 damage) and Summon(64 damage) kill a rotworm, player only gain 39 experience point.

Thanks for your help.
 
It is your choice to not use the solution with damageMap(I can't even see one reason to need damageMap for summons since theirs damage later get converted as yours), the problem is that experience is using uint64_t not double so it has rounding problem in case of summons.

A little example with your "test":
monster experience: 40
total damage: 65
summon damage: 1
player damage: 64
summon ratio: 0.0153846153846154
player ratio: 0.9846153846153846
summon exp gain: summon ratio*monster experience = 0
player exp gain: player ratio*monster experience = 39
summon exp gain+player exp gain = 0+39 = 39
so there's your rounding uint64_t problem, you can either change damageMap or use double as internal data type for experience there aren't any other way.

Edit:
Excellent explanation! But if i get the summon ratio and add with player ratio will result in 1.0(all exp) that is what im trying to do, if i couldn't do that, i will definitly use the damageMap solution.
but this is what changing summon damageMap to their master will do, it will make damage ratio to merge xD
 
Last edited:
It is your choice to not use the solution with damageMap(I can't even see one reason to need damageMap for summons since theirs damage later get converted as yours), the problem is that experience is using uint64_t not double so it has rounding problem in case of summons.

A little example with your "test":
monster experience: 40
total damage: 65
summon damage: 1
player damage: 64
summon ratio: 0.0153846153846154
player ratio: 0.9846153846153846
summon exp gain: summon ratio*monster experience = 0
player exp gain: player ratio*monster experience = 39
summon exp gain+player exp gain = 0+39 = 39
so there's your rounding uint64_t problem, you can either change damageMap or use double as internal data type for experience there aren't any other way.
Excellent explanation! But if i get the summon ratio and add with player ratio will result in 1.0(all exp) that is what im trying to do, if i couldn't do that, i will definitly use the damageMap solution.


--Edit
@itachi_
xD

@zxmatzx
From what you writed in first post it clearly isn't experienceMap issue but damageMap issue.
Change your Creature::addDamagePoints to:
Code:
void Creature::addDamagePoints(Creature* attacker, int32_t damagePoints)
{
    if (damagePoints <= 0) {
        return;
    }

    uint32_t attackerId = (attacker->isSummon() ? attacker->getMaster()->getID() : attacker->id);

    auto it = damageMap.find(attackerId);
    if (it == damageMap.end()) {
        CountBlock_t cb;
        cb.ticks = OTSYS_TIME();
        cb.total = damagePoints;
        damageMap[attackerId] = cb;
    } else {
        it->second.total += damagePoints;
        it->second.ticks = OTSYS_TIME();
    }

    lastHitCreatureId = attackerId;
}
and it should work correctly because all the damage monster took due to summon attack will be given to summon master, so there shouldn't be any rounding issue when calculating experience.
After some tests, this solution worked well. Thanks for help!
 
Last edited:
@itachi_
xD

@zxmatzx
From what you writed in first post it clearly isn't experienceMap issue but damageMap issue.
Change your Creature::addDamagePoints to:
Code:
void Creature::addDamagePoints(Creature* attacker, int32_t damagePoints)
{
    if (damagePoints <= 0) {
        return;
    }

    uint32_t attackerId = (attacker->isSummon() ? attacker->getMaster()->getID() : attacker->id);

    auto it = damageMap.find(attackerId);
    if (it == damageMap.end()) {
        CountBlock_t cb;
        cb.ticks = OTSYS_TIME();
        cb.total = damagePoints;
        damageMap[attackerId] = cb;
    } else {
        it->second.total += damagePoints;
        it->second.ticks = OTSYS_TIME();
    }

    lastHitCreatureId = attackerId;
}
and it should work correctly because all the damage monster took due to summon attack will be given to summon master, so there shouldn't be any rounding issue when calculating experience.
I know its old thread but I just wanted to thank you for this code! as someone making a server with small c++ experience you're a life saver!
 
Back
Top