• 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++ Party shared experience issue

Joined
Sep 24, 2023
Messages
36
Solutions
1
Reaction score
8
Hello everyone,

First of all, I have some programming experience and familiarity with it. However, this is my first encounter with Tibia's code, and it's also a different programming language than the one I'm used to.

I'm facing an issue in my C++ code related to calculating shared experience in an online game. I have a party system where players can hunt together and share the experience gained from creature kills. However, I'm having difficulties ensuring that the experience is evenly distributed among all party members when one player deals the majority of the damage.

Here's a summary of my problem:

  • When a party is hunting, and only one of the players can deal around 80% to 90% of the creature's damage, while the other party members deal the remaining damage, the experience is being distributed based on the damage dealt by the attacker, rather than evenly among all party members.
  • If only the party leader deals 100% of the damage, the experience is incorrectly share.
    1695515234563.png

  • If only one party member deals 100% of the damage, the experience is share incorrectly.1695514933273.png

  • Here, the player "Teste02" dealt approximately 80% to 90% of the creature's health, while the player "Teste01" dealt the remaining damage.
    1695515095004.png

  • If each member, including the party leader, deals around 50% of the creature's health, the experience is divided correctly.
    1695515184814.png

  • I need the experience to be shared equally among all party members, regardless of who dealt the most damage. Since my server version is 7.2 to 7.6, and the vocations deal higher damage than usual, not all players will be able to deal a high percentage of damage to the creatures they are hunting when they are in a party.
I've made some modifications to my code, but I haven't been able to find the correct solution. Could someone help me understand how I can adjust the logic for shared experience calculation so that it's evenly distributed among all party members?
Here's the relevant code snippet where I'm facing this issue.

With my experience, I've managed to identify some relevant functions, but I must admit that I'm confused. Here they are:

getDamageRatio Function
C++:
double Creature::getDamageRatio(Creature* attacker) const
{
    uint32_t totalDamage = 0;
    uint32_t attackerDamage = 0;

    for (const auto& it : damageMap) {
        const CountBlock_t& cb = it.second;
        totalDamage += cb.total;
        if (it.first == attacker->getID()) {
            attackerDamage += cb.total;
        }
    }

    if (totalDamage == 0) {
        return 0;
    }

    return (static_cast<double>(attackerDamage) / totalDamage);
}


onDeath Function
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* attackerPlayer = attacker->getPlayer()) {
                    attackerPlayer->removeAttacked(getPlayer());

                    Party* party = attackerPlayer->getParty();
                    if (party && party->getLeader() && party->isSharedExperienceActive() &&
                        party->isSharedExperienceEnabled()) {
                        attacker = party->getLeader();
                    }
                }

                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 || mostDamageCreatureMaster != lastHitCreatureMaster)) {
                mostDamageUnjustified = mostDamageCreature->onKilledCreature(this, false);
            }
        }
    }

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

    if (master) {
        setMaster(nullptr);
    }

    if (droppedCorpse) {
        g_game.removeCreature(this, false);
    }
}

I've tried to change it and reformulate the logic a bit to what I need, but without any success.

I'd be grateful if anyone could help!
 
Last edited:
If the leader is attacking a monster and the other group is not attacking, the shared experience will only go to the leader. So if you want to solve the problem, when the leader is attacking and the other group is not attacking, the shared experience will be divided between the two.

It is important to inform your version and engine. So, take a look here.
 
Se o líder está atacando um monstro e o outro grupo não está atacando, a experiência compartilhada só irá para o líder. Então, se você quer resolver o problema, quando o líder está atacando e o outro grupo não está atacando, a experiência compartilhada será dividida entre os dois.

É importante informar sua versão e motor. Então, dê uma olhada aqui.
Hello Matheus, I followed the instructions recommended in these topics that you gave me, but the error remains, at first, in the topics it is something referring to the "combat activity" of the party members, but my problem is not this, my problem is in relation to the damage that each member gives to the monsters.

As you can see in the screenshots, if the member has the fight condition and the leader kills the monster completely, the member ends up receiving less experience than the leader because he didn't deal any damage to the monster.

Therefore, I want both the leader and the member to receive the same amount of experience.

This is because my server is from version 7.2 to 7.7 and the damage is higher than usual.

Server with TFS 1.2, sabrehvanth, Nostalrius base.
 
Hola Matheus, seguí las instrucciones recomendadas en estos temas que me diste, pero el error persiste, al principio en los temas es algo referente a la "actividad de combate" de los miembros del grupo, pero mi problema no es este, mi El problema está en relación al daño que cada miembro le da a los monstruos.

Como puedes ver en las capturas de pantalla, si el miembro tiene la condición de pelea y el líder mata al monstruo por completo, el miembro termina recibiendo menos experiencia que el líder porque no le causó ningún daño al monstruo.

Por lo tanto, quiero que tanto el líder como el miembro reciban la misma cantidad de experiencia.

Esto se debe a que mi servidor es de la versión 7.2 a 7.7 y el daño es mayor de lo habitual.

Servidor con TFS 1.2, sabrehvanth, base Nostalrius.
in party.cpp check tis function void Party::shareExperience(double experience, Creature* target, bool multiplied)
 
in party.cpp check tis function void Party::shareExperience(double experience, Creature* target, bool multiplied)
Hello, my Party::shareExperience function

C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = static_cast<uint64_t>(std::ceil((static_cast<double>(experience) * (extraExpRate + 1)) / (memberList.size() + 1)));
    for (Player* member : memberList) {
        member->onGainSharedExperience(shareExperience);
    }
    leader->onGainSharedExperience(shareExperience);
}
 
Last edited:
Hello, my Party::shareExperience function

C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = static_cast<uint64_t>(std::ceil((static_cast<double>(experience) * (extraExpRate + 1)) / (memberList.size() + 1)));
    for (Player* member : memberList) {
        member->onGainSharedExperience(shareExperience);
    }
    leader->onGainSharedExperience(shareExperience);
}
In this function you have to modify how you want to share the experience. For example, if the party is made up of 2 players, they get 50% of the monster's exp, if the party is made up of 3 players, they get 75% of the party's exp, and if the party is more than 4 then each get 100% of the monster exp, as an example.
 
C++:
void Party::shareExperience(uint64_t experience)
{
    if (memberList.empty()) {
        return;
    }
    
    uint64_t individualShare = static_cast<uint64_t>(std::ceil(static_cast<double>(experience) * (extraExpRate + 1) / memberList.size()));

    for (Player* member : memberList) {
        member->onGainSharedExperience(individualShare);
    }
    leader->onGainSharedExperience(individualShare);
}

Do a test using > /memberList.size()));
There is possibly an additional member, and this exp is going to the PT leader (?).
385 (Leader) / 210 (Member); As shown in your example.


It is also possible to test by adding information about the size of the group, and giving bonus exp for each additional member (global style). See below:

C++:
void Party::shareExperience(uint64_t experience)
{
    if (memberList.empty()) {
        return;
    }

    // Determines the amount of experience each member should receive based on group size.
    double groupSizeFactor = 1.0;

    if (memberList.size() > 2 && memberList.size() <= 6) {
        // Adjusts the factor based on the group size (minimum 2 members).
        groupSizeFactor = 1.0 + 0.1 * (memberList.size() - 2); // Each extra member besides the leader receives 10% more.
    }

    uint64_t individualShare = static_cast<uint64_t>(std::ceil(static_cast<double>(experience) * groupSizeFactor / memberList.size()));

    for (Player* member : memberList) {
        member->onGainSharedExperience(individualShare);
    }
    leader->onGainSharedExperience(individualShare);
}

-
Also ensure that the memberList contains all PT members and each player's onGainSharedExperience function is implemented correctly to handle the shared experience.
 
In this function you have to modify how you want to share the experience. For example, if the party is made up of 2 players, they get 50% of the monster's exp, if the party is made up of 3 players, they get 75% of the party's exp, and if the party is more than 4 then each get 100% of the monster exp, as an example.
Well, here we go. I think something was missing from the explanation.
  1. The split experience is CORRECT when
    1. All members attack and kill the monster together.
      1695584381827.png
  2. The split experience is INCORRECT when
    1. Only one member kills 100% of the monster.
      1695584682561.png

    2. Only leader kills 100% of the monster.
      1695584603700.png

    3. When 1 member kills about 80~90% of the monster alone and another member finishes killing the monster.
      Look at the image below, the member has earned 280 and the leader has only received 105 experience.
      1695584682561.png

    4. When the leader kills about 80~90% of the monster alone and another member finishes killing the monster.
      Look at the image below, the leader has earned 280 and the member has only received 105 experience.
      1695584603700.png

Therefore, in the INCORRECT cases, I would like the experience to be divided as it is when all party members take a similar life from the monster. Which was the case presented in item 1.1. where the experience is divided CORRECTLY.
Post automatically merged:

C++:
void Party::shareExperience(uint64_t experience)
{
    if (memberList.empty()) {
        return;
    }
 
    uint64_t individualShare = static_cast<uint64_t>(std::ceil(static_cast<double>(experience) * (extraExpRate + 1) / memberList.size()));

    for (Player* member : memberList) {
        member->onGainSharedExperience(individualShare);
    }
    leader->onGainSharedExperience(individualShare);
}

Do a test using > /memberList.size()));
There is possibly an additional member, and this exp is going to the PT leader (?).
385 (Leader) / 210 (Member); As shown in your example.


It is also possible to test by adding information about the size of the group, and giving bonus exp for each additional member (global style). See below:

C++:
void Party::shareExperience(uint64_t experience)
{
    if (memberList.empty()) {
        return;
    }

    // Determines the amount of experience each member should receive based on group size.
    double groupSizeFactor = 1.0;

    if (memberList.size() > 2 && memberList.size() <= 6) {
        // Adjusts the factor based on the group size (minimum 2 members).
        groupSizeFactor = 1.0 + 0.1 * (memberList.size() - 2); // Each extra member besides the leader receives 10% more.
    }

    uint64_t individualShare = static_cast<uint64_t>(std::ceil(static_cast<double>(experience) * groupSizeFactor / memberList.size()));

    for (Player* member : memberList) {
        member->onGainSharedExperience(individualShare);
    }
    leader->onGainSharedExperience(individualShare);
}

-
Hello my dear friend!

Let's go! This my function.
C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = static_cast<uint64_t>(std::ceil((static_cast<double>(experience) * (extraExpRate + 1)) / (memberList.size() + 1)));
    for (Player* member : memberList) {
        member->onGainSharedExperience(shareExperience);
    }
    leader->onGainSharedExperience(shareExperience);
}

And note that we have a variable called extraExpRate, which is calculated as follows. This function is called whenever there is any action in the party. Join, exclude, share, revoke etc..
C++:
void Party::updateVocationsList()
{
    std::set<uint32_t> vocationIds;

    uint32_t vocationId = leader->getVocation()->getFromVocation();
    if (vocationId != VOCATION_NONE) {
        vocationIds.insert(vocationId);
    }

    for (const Player* member : memberList) {
        vocationId = member->getVocation()->getFromVocation();
        if (vocationId != VOCATION_NONE) {
            vocationIds.insert(vocationId);
        }
    }

    size_t size = vocationIds.size();
    if (size > 1) {
        extraExpRate = static_cast<float>(size * (10 + (size - 1) * 5)) / 100.f;
    } else {
        extraExpRate = 0.20f;
    }
}

In the list of members memberList, +1 is added for all the logic of the party, it doesn't include the leader as a member, because as already said, he is the leader. But nothing stopped me from removing it and carrying out the test as you said. However, the experiment didn't turn out correctly either.

Other relevants functions
C++:
void Player::onGainSharedExperience(uint64_t gainExp)
{
    gainExperience(gainExp);
}

void Player::gainExperience(uint64_t gainExp)
{
  if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0) {
    return;
  }
  addExperience(gainExp, true);
}

void Player::addExperience(uint64_t exp, bool sendText/* = false*/, bool applyStages/* = true*/)
{
  uint64_t currLevelExp = Player::getExpForLevel(level);
  uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  if (currLevelExp >= nextLevelExp) {
    //player has reached max level
    levelPercent = 0;
    sendStats();
    return;
  }
  /* if (getSoul() < getVocation()->getSoulMax() && exp >= level) {
    Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000, 0);
    condition->setParam(CONDITION_PARAM_SOULGAIN, 1);
    condition->setParam(CONDITION_PARAM_SOULTICKS, vocation->getSoulGainTicks() * 1000);
    addCondition(condition);
  } */
 
  if (applyStages) {
    exp *= g_game.getExperienceStage(level);
  }
  if (exp == 0) {
    return;
  }
  if(isPremium()){
    exp += (exp*10)/100;
  }
  experience += exp;
  if (sendText) {
    g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(exp));
  }
  uint32_t prevLevel = level;
  while (experience >= nextLevelExp) {
    ++level;
    healthMax += vocation->getHPGain();
    health += vocation->getHPGain();
    manaMax += vocation->getManaGain();
    mana += vocation->getManaGain();
    capacity += vocation->getCapGain();
    currLevelExp = nextLevelExp;
    nextLevelExp = Player::getExpForLevel(level + 1);
    if (currLevelExp >= nextLevelExp) {
      //player has reached max level
      break;
    }
  }
  if (prevLevel != level) {
    updateBaseSpeed();
    setBaseSpeed(getBaseSpeed());
    g_game.changeSpeed(this, 0);
    g_game.addCreatureHealth(this);
    if (party) {
      party->updateSharedExperience();
    }
    g_creatureEvents->playerAdvance(this, SKILL_LEVEL, prevLevel, level);
    std::ostringstream ss;
    ss << "You advanced from Level " << prevLevel << " to Level " << level << '.';
    sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  }
  if (nextLevelExp > currLevelExp) {
    levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  } else {
    levelPercent = 0;
  }
  sendStats();
}

I think the problem is with the damage the players deal to the monsters. See my examples in the comment above.
 
Last edited:
Well, here we go. I think something was missing from the explanation.
  1. The split experience is CORRECT when
    1. All members attack and kill the monster together.
      View attachment 78748
  2. The split experience is INCORRECT when
    1. Only one member kills 100% of the monster.
      View attachment 78753

    2. Only leader kills 100% of the monster.
      View attachment 78752

    3. When 1 member kills about 80~90% of the monster alone and another member finishes killing the monster.
      Look at the image below, the member has earned 280 and the leader has only received 105 experience.
      View attachment 78753

    4. When the leader kills about 80~90% of the monster alone and another member finishes killing the monster.
      Look at the image below, the leader has earned 280 and the member has only received 105 experience.
      View attachment 78752

Therefore, in the INCORRECT cases, I would like the experience to be divided as it is when all party members take a similar life from the monster. Which was the case presented in item 1.1. where the experience is divided CORRECTLY.
Post automatically merged:


Hello my dear friend!

Let's go! This my function.
C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = static_cast<uint64_t>(std::ceil((static_cast<double>(experience) * (extraExpRate + 1)) / (memberList.size() + 1)));
    for (Player* member : memberList) {
        member->onGainSharedExperience(shareExperience);
    }
    leader->onGainSharedExperience(shareExperience);
}

And note that we have a variable called extraExpRate, which is calculated as follows. This function is called whenever there is any action in the party. Join, exclude, share, revoke etc..
C++:
void Party::updateVocationsList()
{
    std::set<uint32_t> vocationIds;

    uint32_t vocationId = leader->getVocation()->getFromVocation();
    if (vocationId != VOCATION_NONE) {
        vocationIds.insert(vocationId);
    }

    for (const Player* member : memberList) {
        vocationId = member->getVocation()->getFromVocation();
        if (vocationId != VOCATION_NONE) {
            vocationIds.insert(vocationId);
        }
    }

    size_t size = vocationIds.size();
    if (size > 1) {
        extraExpRate = static_cast<float>(size * (10 + (size - 1) * 5)) / 100.f;
    } else {
        extraExpRate = 0.20f;
    }
}

In the list of members memberList, +1 is added for all the logic of the party, it doesn't include the leader as a member, because as already said, he is the leader. But nothing stopped me from removing it and carrying out the test as you said. However, the experiment didn't turn out correctly either.

Other relevants functions
C++:
void Player::onGainSharedExperience(uint64_t gainExp)
{
    gainExperience(gainExp);
}

void Player::gainExperience(uint64_t gainExp)
{
  if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0) {
    return;
  }
  addExperience(gainExp, true);
}

void Player::addExperience(uint64_t exp, bool sendText/* = false*/, bool applyStages/* = true*/)
{
  uint64_t currLevelExp = Player::getExpForLevel(level);
  uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  if (currLevelExp >= nextLevelExp) {
    //player has reached max level
    levelPercent = 0;
    sendStats();
    return;
  }
  /* if (getSoul() < getVocation()->getSoulMax() && exp >= level) {
    Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000, 0);
    condition->setParam(CONDITION_PARAM_SOULGAIN, 1);
    condition->setParam(CONDITION_PARAM_SOULTICKS, vocation->getSoulGainTicks() * 1000);
    addCondition(condition);
  } */
 
  if (applyStages) {
    exp *= g_game.getExperienceStage(level);
  }
  if (exp == 0) {
    return;
  }
  if(isPremium()){
    exp += (exp*10)/100;
  }
  experience += exp;
  if (sendText) {
    g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(exp));
  }
  uint32_t prevLevel = level;
  while (experience >= nextLevelExp) {
    ++level;
    healthMax += vocation->getHPGain();
    health += vocation->getHPGain();
    manaMax += vocation->getManaGain();
    mana += vocation->getManaGain();
    capacity += vocation->getCapGain();
    currLevelExp = nextLevelExp;
    nextLevelExp = Player::getExpForLevel(level + 1);
    if (currLevelExp >= nextLevelExp) {
      //player has reached max level
      break;
    }
  }
  if (prevLevel != level) {
    updateBaseSpeed();
    setBaseSpeed(getBaseSpeed());
    g_game.changeSpeed(this, 0);
    g_game.addCreatureHealth(this);
    if (party) {
      party->updateSharedExperience();
    }
    g_creatureEvents->playerAdvance(this, SKILL_LEVEL, prevLevel, level);
    std::ostringstream ss;
    ss << "You advanced from Level " << prevLevel << " to Level " << level << '.';
    sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  }
  if (nextLevelExp > currLevelExp) {
    levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  } else {
    levelPercent = 0;
  }
  sendStats();
}

I think the problem is with the damage the players deal to the monsters. See my examples in the comment above.
this is my code check this script

C++:
void Party::shareExperience(double experience, Creature* target, bool multiplied)
{
    double shareExperience = experience;
    if(memberList.size() == 1){
    shareExperience = (experience*0.5);}
    
    if(memberList.size() == 2){
    shareExperience = (experience*0.75);}
    
    if(memberList.size() >= 3){
    shareExperience = (experience*1.0);}
    
 
    double tmpExperience = shareExperience;

    leader->onGainSharedExperience(tmpExperience, target, multiplied);
    for(PlayerVector::iterator it = memberList.begin(); it != memberList.end(); ++it)
    {
        tmpExperience = shareExperience;
        (*it)->onGainSharedExperience(tmpExperience, target, multiplied);
    }
}
 
this is my code check this script

C++:
void Party::shareExperience(double experience, Creature* target, bool multiplied)
{
    double shareExperience = experience;
    if(memberList.size() == 1){
    shareExperience = (experience*0.5);}
  
    if(memberList.size() == 2){
    shareExperience = (experience*0.75);}
  
    if(memberList.size() >= 3){
    shareExperience = (experience*1.0);}
  
 
    double tmpExperience = shareExperience;

    leader->onGainSharedExperience(tmpExperience, target, multiplied);
    for(PlayerVector::iterator it = memberList.begin(); it != memberList.end(); ++it)
    {
        tmpExperience = shareExperience;
        (*it)->onGainSharedExperience(tmpExperience, target, multiplied);
    }
}

Hello, with this code.
  1. Both attacked
    1695592891715.png

  2. Only the leader attacked
    1695592969295.png

  3. Only the member attacked
    1695593042697.png
Even if you force it to be equal, the same thing happens.
C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = experience;
    
    shareExperience = (experience*1.0);
    
 
    uint64_t tmpExperience = shareExperience;

    leader->onGainSharedExperience(tmpExperience);
    for(PlayerVector::iterator it = memberList.begin(); it != memberList.end(); ++it)
    {
        tmpExperience = shareExperience;
        (*it)->onGainSharedExperience(tmpExperience);
    }
}

Look
  • Both attacked
    1695593866003.png

  • First image, member only 100% attacked. Second, leader 100% attacked.
    1695594036773.png

But the big fact is. Why is it that when only 1 player in the party kills the monster alone, the others get a different amount of experience?
 
Last edited:
I already understood what you want to do, but you have to modify the party then, the shared requires that everyone hits it at least 1, to activate the shared exp, what you ask could cause you too many problems since it could leave the player earning free exp without not even participate in the hunt.
 
If you don't want only the leader to gain experience when the leader attacks a monster and a member doesn't attack, and if the leader attacks and a member doesn't attack, the exp will be split between the two. We can test this. I found your source and checked it out here.

game.cpp.
look for this line:

Lua:
void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    Party* party = player->getParty();
    if (!party || player->hasCondition(CONDITION_INFIGHT)) {
        return;
    }

    party->setSharedExperience(player, sharedExpActive);
}

and replace all.
C++:
void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    Party* party = player->getParty();
        return;
    }

    party->setSharedExperience(player, sharedExpActive);
}

Just test to see if the experience is divided correctly.
Post automatically merged:

I already understood what you want to do, but you have to modify the party then, the shared requires that everyone hits it at least 1, to activate the shared exp, what you ask could cause you too many problems since it could leave the player earning free exp without not even participate in the hunt.
If the leader activates the 'ENABLED SHARED' option, the experience will be divided, depending on the leader. When they go hunting, he can activate it or not. I did a test and removed this line, and it worked. There are people who want to share the experience without participating. He can activate it and share the experience. If the leader doesn't want to share the experience, he doesn't activate this option, you know? Without activating it, it is not possible to share the experience.
 
I already understood what you want to do, but you have to modify the party then, the shared requires that everyone hits it at least 1, to activate the shared exp, what you ask could cause you too many problems since it could leave the player earning free exp without not even participate in the hunt.
I understand your concern and I'm aware of this scenario. But it's not my focus at the moment, because even if a member or leader helps to kill only 20% of the monster, they won't receive the experience divided correctly. As for the AFK up, there are other validations that will make the process of not letting a person receive experience if they don't attack/movement.


If you don't want only the leader to gain experience when the leader attacks a monster and a member doesn't attack, and if the leader attacks and a member doesn't attack, the exp will be split between the two. We can test this. I found your source and checked it out here.

game.cpp.
look for this line:

Lua:
void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    Party* party = player->getParty();
    if (!party || player->hasCondition(CONDITION_INFIGHT)) {
        return;
    }

    party->setSharedExperience(player, sharedExpActive);
}

and replace all.
C++:
void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    Party* party = player->getParty();
        return;
    }

    party->setSharedExperience(player, sharedExpActive);
}

Just test to see if the experience is divided correctly.
Post automatically merged:


If the leader activates the 'ENABLED SHARED' option, the experience will be divided, depending on the leader. When they go hunting, he can activate it or not. I did a test and removed this line, and it worked. There are people who want to share the experience without participating. He can activate it and share the experience. If the leader doesn't want to share the experience, he doesn't activate this option, you know? Without activating it, it is not possible to share the experience.

Hello, I saw about this change in another related topic. I even made the adjustment and my function is as follows.
C++:
void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
{
    Player* player = getPlayerByID(playerId);
    if (!player) {
        return;
    }

    Party* party = player->getParty();
    if (!party) {
        return;
    }

    party->setSharedExperience(player, sharedExpActive);
}

Hello, I saw about this change in another related topic. I even made the adjustment and my function is as follows.aHowever, even having done this, the experience is divided. So far so good!

But here's the scenario.

If only the leader attacks and kills the monster alone, the experience for the leader goes "full" and the experience for the member goes broken, making the experience gained by the leader higher than that of the member, and vice versa if the scenario were different.

An example of this is in the following answer.
With this answer below, the correct scenario would be. When only the leader kills the monster, the member should receive the experience split equal to what the leader receives. Just like when both (leader/member) attack together, scenario 1.
Hello, with this code.
  1. Both attacked
    View attachment 78763

  2. Only the leader attacked
    View attachment 78764

  3. Only the member attacked
    View attachment 78765
Even if you force it to be equal, the same thing happens.
C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = experience;
   
    shareExperience = (experience*1.0);
   
 
    uint64_t tmpExperience = shareExperience;

    leader->onGainSharedExperience(tmpExperience);
    for(PlayerVector::iterator it = memberList.begin(); it != memberList.end(); ++it)
    {
        tmpExperience = shareExperience;
        (*it)->onGainSharedExperience(tmpExperience);
    }
}

Look

But the big fact is. Why is it that when only 1 player in the party kills the monster alone, the others get a different amount of experience?
 
have you checked lua codes in onShareExperience?
There is only lua file related share.

File experienceshare.lua
Lua:
function onSay(player, words, param)
    local party = player:getParty()
    if not party then
        player:sendCancelMessage("You are not part of a party.")
        return false
    end
    
    if party:getLeader() ~= player then
        player:sendCancelMessage("You are not the leader of the party.")
        return false
    end
    
    if party:isSharedExperienceActive() then
        if player:getCondition(CONDITION_INFIGHT) then
            player:sendCancelMessage("You are in fight. Experience sharing not disabled.")
        else
            party:setSharedExperience(false)
        end
    else
        if player:getCondition(CONDITION_INFIGHT) then
            player:sendCancelMessage("You are in fight. Experience sharing not enabled.")
        else
            party:setSharedExperience(true)
        end
    end
        
    return false
end

In luascript.cpp
C++:
registerMethod("Party", "shareExperience", LuaScriptInterface::luaPartyShareExperience);

int LuaScriptInterface::luaPartyShareExperience(lua_State* L)
{
   // party:shareExperience(experience)
   uint64_t experience = getNumber<uint64_t>(L, 2);
   Party* party = getUserdata<Party>(L, 1);
   if (party) {
      party->shareExperience(experience);
      pushBoolean(L, true);
   } else {
      lua_pushnil(L);
   }
   return 1;
}

In luascript.h
C++:
static int luaPartyShareExperience(lua_State* L);
 
so, check onGainExperience event (lua), probably some maths there for party bonus
@Rodrigo Tolomeo
Oh, thanks man!
I've discovered something, there's an onGainExperience function in creature.cpp, there it divides the experience gained by 2. If you look at the screenshots of the last changes I made to the code, when 1 player doesn't attack and he's in a party with shared experience, he ends up receiving the experience of the monster /2 anyway. Maybe it's something along these lines that's affecting the division of experience gained.

But I'm sad, because I haven't had time to check if this is really the case.
Hello, with this code.
  1. Both attacked
    View attachment 78763

  2. Only the leader attacked
    View attachment 78764

  3. Only the member attacked
    View attachment 78765
Even if you force it to be equal, the same thing happens.
C++:
void Party::shareExperience(uint64_t experience)
{
    uint64_t shareExperience = experience;
   
    shareExperience = (experience*1.0);
   
 
    uint64_t tmpExperience = shareExperience;

    leader->onGainSharedExperience(tmpExperience);
    for(PlayerVector::iterator it = memberList.begin(); it != memberList.end(); ++it)
    {
        tmpExperience = shareExperience;
        (*it)->onGainSharedExperience(tmpExperience);
    }
}

Look

But the big fact is. Why is it that when only 1 player in the party kills the monster alone, the others get a different amount of experience?
Take a look at this case. The dragon for the player alone, without a party, gives 350 experience, in a party with shared experience, when only one kills the dragon, it keeps giving 350 experience and the other player 175.

350/2: 175.

I think I'm on the right track!!!
 
Oh, thanks man!
I've discovered something, there's an onGainExperience function in creature.cpp, there it divides the experience gained by 2. If you look at the screenshots of the last changes I made to the code, when 1 player doesn't attack and he's in a party with shared experience, he ends up receiving the experience of the monster /2 anyway. Maybe it's something along these lines that's affecting the division of experience gained.

But I'm sad, because I haven't had time to check if this is really the case.

Take a look at this case. The dragon for the player alone, without a party, gives 350 experience, in a party with shared experience, when only one kills the dragon, it keeps giving 350 experience and the other player 175.

350/2: 175.

I think I'm on the right track!!!
show this function...
 
show this function...
Hello,

In creature.cpp fily, have a function
Lua:
void Creature::onGainExperience(uint64_t gainExp, Creature* target)
{
    if (gainExp == 0 || !master) {
        return;
    }

    gainExp /= 2;
    master->onGainExperience(gainExp, target);

    g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(gainExp));
}


In player.cpp, have a similar function but it's diferent
Lua:
void Player::onGainExperience(uint64_t gainExp, Creature* target)
{
    if (hasFlag(PlayerFlag_NotGainExperience)) {
        return;
    }

    if (target && !target->getPlayer() && party && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
        party->shareExperience(gainExp);
        //We will get a share of the experience through the sharing mechanism
        return;
    }

    Creature::onGainExperience(gainExp, target);
    gainExperience(gainExp);
}
 
Hello,

In creature.cpp fily, have a function
Lua:
void Creature::onGainExperience(uint64_t gainExp, Creature* target)
{
    if (gainExp == 0 || !master) {
        return;
    }

    gainExp /= 2;
    master->onGainExperience(gainExp, target);

    g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(gainExp));
}


In player.cpp, have a similar function but it's diferent
Lua:
void Player::onGainExperience(uint64_t gainExp, Creature* target)
{
    if (hasFlag(PlayerFlag_NotGainExperience)) {
        return;
    }

    if (target && !target->getPlayer() && party && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
        party->shareExperience(gainExp);
        //We will get a share of the experience through the sharing mechanism
        return;
    }

    Creature::onGainExperience(gainExp, target);
    gainExperience(gainExp);
}
change
void Party::shareExperience(uint64_t experience)
after
(*it)->onGainSharedExperience(tmpExperience);
add
std::cout << "Share Experience: " << tmpExperience << " Player: " << (*it)->getName() << std::endl;

and see if it's printing the leader name too
 
Back
Top