void cOcelot::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (!IsTicking()) { // The base class tick destroyed us return; } if (!IsTame() && !IsBaby()) { if (m_CheckPlayerTickCount == 23) { m_World->DoWithNearestPlayer(GetPosition(), 10, [&](cPlayer & a_Player) -> bool { cItems Items; GetBreedingItems(Items); if (Items.ContainsType(a_Player.GetEquippedItem().m_ItemType)) { if (!IsBegging()) { SetIsBegging(true); m_World->BroadcastEntityMetadata(*this); } MoveToPosition(a_Player.GetPosition()); } else { if (IsBegging()) { SetIsBegging(false); m_World->BroadcastEntityMetadata(*this); } } return true; }, true); m_CheckPlayerTickCount = 0; } else { m_CheckPlayerTickCount++; } } if (IsTame() && !IsSitting()) { TickFollowPlayer(); } else if (IsSitting()) { StopMovingToPosition(); } m_World->BroadcastEntityMetadata(*this); }
int32 Client::CalcManaRegen() { uint8 clevel = GetLevel(); int32 regen = 0; //this should be changed so we dont med while camping, etc... if (IsSitting() || (GetHorseId() != 0)) { BuffFadeBySitModifier(); if(HasSkill(SkillMeditate)) { this->medding = true; regen = (((GetSkill(SkillMeditate) / 10) + (clevel - (clevel / 4))) / 4) + 4; regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; CheckIncreaseSkill(SkillMeditate, nullptr, -5); } else regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; } else { this->medding = false; regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; } //AAs regen += aabonuses.ManaRegen; return (regen * RuleI(Character, ManaRegenMultiplier) / 100); }
void cWolf::TickFollowPlayer() { class cCallback : public cPlayerListCallback { virtual bool Item(cPlayer * a_Player) override { OwnerPos = a_Player->GetPosition(); return false; } public: Vector3f OwnerPos; } Callback; if (m_World->DoWithPlayer(m_OwnerName, Callback)) { // The player is present in the world, follow them: double Distance = (Callback.OwnerPos - GetPosition()).Length(); if (Distance < 3) { m_bMovingToDestination = false; } else if ((Distance > 30) && (!IsSitting())) { TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); } else { m_Destination = Callback.OwnerPos; } } }
void cWolf::Tick(float a_Dt, cChunk & a_Chunk) { if (!IsAngry()) { cMonster::Tick(a_Dt, a_Chunk); } else { super::Tick(a_Dt, a_Chunk); } if (IsSitting()) { m_bMovingToDestination = false; } cPlayer * a_Closest_Player = FindClosestPlayer(); if (a_Closest_Player != NULL) { switch (a_Closest_Player->GetEquippedItem().m_ItemType) { case E_ITEM_BONE: case E_ITEM_RAW_BEEF: case E_ITEM_STEAK: case E_ITEM_RAW_CHICKEN: case E_ITEM_COOKED_CHICKEN: case E_ITEM_ROTTEN_FLESH: { if (!IsBegging()) { SetIsBegging(true); m_World->BroadcastEntityMetadata(*this); } Vector3f a_NewDestination = a_Closest_Player->GetPosition(); a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet. m_Destination = Vector3f(a_NewDestination); m_bMovingToDestination = false; break; } default: { if (IsBegging()) { SetIsBegging(false); m_World->BroadcastEntityMetadata(*this); } } } } if (IsTame()) { TickFollowPlayer(); } }
void cOcelot::OnRightClicked(cPlayer & a_Player) { if (!IsTame()) { if ( IsBegging() && ((a_Player.GetPosition() - GetPosition()).Length() <= 3) ) { cItems Items; GetBreedingItems(Items); if (Items.ContainsType(a_Player.GetEquippedItem().m_ItemType)) { if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); } auto & Random = GetRandomProvider(); if (Random.RandBool(1.0 / 3.0)) { // Taming succeeded SetIsBegging(false); SetMaxHealth(20); SetIsTame(true); SetOwner(a_Player.GetName(), a_Player.GetUUID()); SetCatType(static_cast<eCatType>(Random.RandInt<int>(1, 3))); m_World->BroadcastEntityStatus(*this, esWolfTamed); m_World->BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); } else { // Taming failed m_World->BroadcastEntityStatus(*this, esWolfTaming); m_World->BroadcastParticleEffect("smoke", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); } } } else { super::OnRightClicked(a_Player); } } else if (a_Player.GetUUID() == m_OwnerUUID) { super::OnRightClicked(a_Player); SetIsSitting(!IsSitting()); } m_World->BroadcastEntityMetadata(*this); }
void cWolf::OnRightClicked(cPlayer & a_Player) { if (!IsTame() && !IsAngry()) { if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE) { if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); } if (m_World->GetTickRandomNumber(7) == 0) { SetMaxHealth(20); SetIsTame(true); SetOwner(a_Player.GetName()); m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED); } else { m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING); } } } else if (IsTame()) { if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? { if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) { SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage); if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); } } else if (IsSitting()) { SetIsSitting(false); } else { SetIsSitting(true); } } } m_World->BroadcastEntityMetadata(*this); }
int32 Client::CalcBaseManaRegen() { uint8 clevel = GetLevel(); int32 regen = 0; if (IsSitting() || (GetHorseId() != 0)) { if(HasSkill(SkillMeditate)) regen = (((GetSkill(SkillMeditate) / 10) + (clevel - (clevel / 4))) / 4) + 4; else regen = 2; } else { regen = 2; } return regen; }
void cWolf::ReceiveNearbyFightInfo(const cUUID & a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved) { if ( (a_Opponent == nullptr) || IsSitting() || (!IsTame()) || (!a_Opponent->IsPawn()) || (a_PlayerID != m_OwnerUUID) ) { return; } // If we already have a target if (GetTarget() != nullptr) { // If a wolf is asking for help and we already have a target, do nothing if (!a_IsPlayerInvolved) { return; } // If a player is asking for help and we already have a target, // there's a 50% chance of helping and a 50% chance of doing nothing // This helps spread a wolf pack's targets over several mobs else if (GetRandomProvider().RandBool()) { return; } } if (a_Opponent->IsPlayer() && static_cast<cPlayer *>(a_Opponent)->GetUUID() == m_OwnerUUID) { return; // Our owner has hurt himself, avoid attacking them. } if (a_Opponent->IsMob() && static_cast<cMonster *>(a_Opponent)->GetMobType() == mtWolf) { cWolf * Wolf = static_cast<cWolf *>(a_Opponent); if (Wolf->GetOwnerUUID() == GetOwnerUUID()) { return; // Our owner attacked one of their wolves. Abort attacking wolf. } } SetTarget(a_Opponent); }
void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (!IsAngry()) { cMonster::Tick(a_Dt, a_Chunk); if (m_NotificationCooldown > 0) { m_NotificationCooldown -= 1; } } else { super::Tick(a_Dt, a_Chunk); } if (!IsTicking()) { // The base class tick destroyed us return; } if (GetTarget() == nullptr) { m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool { switch (a_Player.GetEquippedItem().m_ItemType) { case E_ITEM_BONE: case E_ITEM_RAW_BEEF: case E_ITEM_STEAK: case E_ITEM_RAW_CHICKEN: case E_ITEM_COOKED_CHICKEN: case E_ITEM_ROTTEN_FLESH: case E_ITEM_RAW_PORKCHOP: case E_ITEM_COOKED_PORKCHOP: { if (!IsBegging()) { SetIsBegging(true); m_World->BroadcastEntityMetadata(*this); } m_FinalDestination = a_Player.GetPosition(); // So that we will look at a player holding food // Don't move to the player if the wolf is sitting. if (!IsSitting()) { MoveToPosition(a_Player.GetPosition()); } break; } default: { if (IsBegging()) { SetIsBegging(false); m_World->BroadcastEntityMetadata(*this); } } } return true; }); } else { if (IsSitting()) { SetTarget(nullptr); } else { MoveToPosition(GetTarget()->GetPosition()); if (TargetIsInRange()) { Attack(a_Dt); } } } if (IsTame() && !IsSitting()) { TickFollowPlayer(); } else if (IsSitting()) { StopMovingToPosition(); } }
void cWolf::OnRightClicked(cPlayer & a_Player) { const cItem & EquippedItem = a_Player.GetEquippedItem(); const int EquippedItemType = EquippedItem.m_ItemType; if (!IsTame() && !IsAngry()) { // If the player is holding a bone, try to tame the wolf: if (EquippedItemType == E_ITEM_BONE) { if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); } if (GetRandomProvider().RandBool(0.125)) { // Taming succeeded SetMaxHealth(20); SetIsTame(true); SetOwner(a_Player.GetName(), a_Player.GetUUID()); m_World->BroadcastEntityStatus(*this, esWolfTamed); m_World->BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); } else { // Taming failed m_World->BroadcastEntityStatus(*this, esWolfTaming); m_World->BroadcastParticleEffect("smoke", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); } } } else if (IsTame()) { // Feed the wolf, restoring its health, or dye its collar: switch (EquippedItemType) { case E_ITEM_RAW_BEEF: case E_ITEM_STEAK: case E_ITEM_RAW_PORKCHOP: case E_ITEM_COOKED_PORKCHOP: case E_ITEM_RAW_CHICKEN: case E_ITEM_COOKED_CHICKEN: case E_ITEM_ROTTEN_FLESH: { if (m_Health < m_MaxHealth) { Heal(ItemHandler(EquippedItemType)->GetFoodInfo(&EquippedItem).FoodLevel); if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); } } break; } case E_ITEM_DYE: { if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog? { SetCollarColor(EquippedItem.m_ItemDamage); if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); } } break; } default: { if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog? { SetIsSitting(!IsSitting()); } } } } m_World->BroadcastEntityMetadata(*this); }
void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (!IsAngry()) { cMonster::Tick(a_Dt, a_Chunk); } else { super::Tick(a_Dt, a_Chunk); } cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance)); if (a_Closest_Player != nullptr) { switch (a_Closest_Player->GetEquippedItem().m_ItemType) { case E_ITEM_BONE: case E_ITEM_RAW_BEEF: case E_ITEM_STEAK: case E_ITEM_RAW_CHICKEN: case E_ITEM_COOKED_CHICKEN: case E_ITEM_ROTTEN_FLESH: case E_ITEM_RAW_PORKCHOP: case E_ITEM_COOKED_PORKCHOP: { if (!IsBegging()) { SetIsBegging(true); m_World->BroadcastEntityMetadata(*this); } m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food // Don't move to the player if the wolf is sitting. if (!IsSitting()) { MoveToPosition(a_Closest_Player->GetPosition()); } break; } default: { if (IsBegging()) { SetIsBegging(false); m_World->BroadcastEntityMetadata(*this); } } } } if (IsTame() && !IsSitting()) { TickFollowPlayer(); } else if (IsSitting()) { StopMovingToPosition(); } }
int32 Client::LevelRegen() { bool sitting = IsSitting(); bool feigned = GetFeigned(); int level = GetLevel(); bool bonus = GetRaceBitmask(GetBaseRace()) & RuleI(Character, BaseHPRegenBonusRaces); uint8 multiplier1 = bonus ? 2 : 1; int32 hp = 0; //these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities if (level < 51) { if (sitting) { if (level < 20) hp += 2 * multiplier1; else if (level < 50) hp += 3 * multiplier1; else //level == 50 hp += 4 * multiplier1; } else //feigned or standing hp += 1 * multiplier1; } //there may be an easier way to calculate this next part, but I don't know what it is else { //level >= 51 int32 tmp = 0; float multiplier2 = 1; if (level < 56) { tmp = 2; if (bonus) multiplier2 = 3; } else if (level < 60) { tmp = 3; if (bonus) multiplier2 = 3.34; } else if (level < 61) { tmp = 4; if (bonus) multiplier2 = 3; } else if (level < 63) { tmp = 5; if (bonus) multiplier2 = 2.8; } else if (level < 65) { tmp = 6; if (bonus) multiplier2 = 2.67; } else { //level >= 65 tmp = 7; if (bonus) multiplier2 = 2.58; } hp += int32(float(tmp) * multiplier2); if (sitting) hp += 3 * multiplier1; else if (feigned) hp += 1 * multiplier1; } return hp; }
void Client::ActivateAA(aaID activate){ if (activate < 0 || activate >= aaHighestID) return; if (IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned()) return; int AATimerID = GetAATimerID(activate); SendAA_Struct* aa2 = nullptr; aaID aaid = activate; uint8 activate_val = GetAA(activate); //this wasn't taking into acct multi tiered act talents before... if (activate_val == 0){ aa2 = zone->FindAA(activate); if (!aa2){ int i; int a; for (i = 1; i<MAX_AA_ACTION_RANKS; i++){ a = activate - i; if (a <= 0) break; aa2 = zone->FindAA(a); if (aa2 != nullptr) break; } } if (aa2){ aaid = (aaID)aa2->id; activate_val = GetAA(aa2->id); } } if (activate_val == 0){ return; } if (aa2) { if (aa2->account_time_required) { if ((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required) { return; } } } if (!p_timers.Expired(&database, AATimerID + pTimerAAStart)) { uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart); uint32 aaremain_hr = aaremain / (60 * 60); uint32 aaremain_min = (aaremain / 60) % 60; uint32 aaremain_sec = aaremain % 60; if (aa2) { if (aaremain_hr >= 1) //1 hour or more Message(CC_Red, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds", aa2->name, aaremain_hr, aaremain_min, aaremain_sec); else //less than an hour Message(CC_Red, "You can use the ability %s again in %u minute(s) %u seconds", aa2->name, aaremain_min, aaremain_sec); } else { if (aaremain_hr >= 1) //1 hour or more Message(CC_Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", aaremain_hr, aaremain_min, aaremain_sec); else //less than an hour Message(CC_Red, "You can use this ability again in %u minute(s) %u seconds", aaremain_min, aaremain_sec); } return; } if (activate_val > MAX_AA_ACTION_RANKS) activate_val = MAX_AA_ACTION_RANKS; activate_val--; //to get array index. //get our current node, now that the indices are well bounded const AA_DBAction *caa = &AA_Actions[aaid][activate_val]; if ((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){ Message(CC_Red, "Ability recovery time not yet met."); return; } //everything should be configured out now uint16 target_id = 0; //figure out our target switch (caa->target) { case aaTargetUser: case aaTargetGroup: target_id = GetID(); break; case aaTargetCurrent: case aaTargetCurrentGroup: if (GetTarget() == nullptr) { Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! p_timers.Clear(&database, AATimerID + pTimerAAStart); return; } target_id = GetTarget()->GetID(); break; case aaTargetPet: if (GetPet() == nullptr) { Message(0, "A pet is required for this skill."); return; } target_id = GetPetID(); break; } //handle non-spell action if (caa->action != aaActionNone) { if (caa->mana_cost > 0) { if (GetMana() < caa->mana_cost) { Message_StringID(CC_Red, INSUFFICIENT_MANA); return; } SetMana(GetMana() - caa->mana_cost); } if (caa->reuse_time > 0) { uint32 timer_base = CalcAAReuseTimer(caa); if (activate == aaImprovedHarmTouch || activate == aaLeechTouch) { p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); } p_timers.Start(AATimerID + pTimerAAStart, timer_base); SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr))); } HandleAAAction(aaid); } //cast the spell, if we have one if (caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) { if (caa->reuse_time > 0) { uint32 timer_base = CalcAAReuseTimer(caa); SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr))); p_timers.Start(AATimerID + pTimerAAStart, timer_base); if (activate == aaImprovedHarmTouch || activate == aaLeechTouch) { p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); } // Bards can cast instant cast AAs while they are casting another song if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { if (!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) { //Reset on failed cast SendAATimer(activate, 0, 0xFFFFFF); Message_StringID(CC_Yellow, ABILITY_FAILED); p_timers.Clear(&database, AATimerID + pTimerAAStart); return; } } else { if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { //Reset on failed cast SendAATimer(activate, 0, 0xFFFFFF); Message_StringID(CC_Yellow, ABILITY_FAILED); p_timers.Clear(&database, AATimerID + pTimerAAStart); return; } } } else { if (!CastSpell(caa->spell_id, target_id)) return; } } }
int32 Client::CalcManaRegen(bool bCombat) { int regen = 0; auto level = GetLevel(); // so the new formulas break down with older skill caps where you don't have the skill until 4 or 8 // so for servers that want to use the old skill progression they can set this rule so they // will get at least 1 for standing and 2 for sitting. bool old = RuleB(Character, OldMinMana); if (!IsStarved()) { // client does some base regen for shrouds here if (IsSitting() || CanMedOnHorse()) { // kind of weird to do it here w/e // client does some base medding regen for shrouds here if (GetClass() != BARD) { auto skill = GetSkill(EQEmu::skills::SkillMeditate); if (skill > 0) { regen++; if (skill > 1) regen++; if (skill >= 15) regen += skill / 15; } } if (old) regen = std::max(regen, 2); } else if (old) { regen = std::max(regen, 1); } } if (level > 61) { regen++; if (level > 63) regen++; } regen += aabonuses.ManaRegen; // add in + 1 bonus for SE_CompleteHeal, but we don't do anything for it yet? int item_bonus = itembonuses.ManaRegen; // this is capped already int heroic_bonus = 0; switch (GetCasterClass()) { case 'W': heroic_bonus = GetHeroicWIS(); break; default: heroic_bonus = GetHeroicINT(); break; } item_bonus += heroic_bonus / 25; regen += item_bonus; if (level <= 70 && regen > 65) regen = 65; regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f; if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { auto max_mana = GetMaxMana(); int fast_regen = 6 * (max_mana / zone->newzone_data.FastRegenMana); if (regen < fast_regen) // weird, but what the client is doing regen = fast_regen; } regen += spellbonuses.ManaRegen; // TODO: live does this in buff tick return (regen * RuleI(Character, ManaRegenMultiplier) / 100); }
int32 Client::CalcHPRegen(bool bCombat) { int item_regen = itembonuses.HPRegen; // worn spells and +regen, already capped item_regen += GetHeroicSTA() / 20; item_regen += aabonuses.HPRegen; int base = 0; auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) base = static_cast<int>(base_data->hp_regen); auto level = GetLevel(); bool skip_innate = false; if (IsSitting()) { if (level >= 50) { base++; if (level >= 65) base++; } if ((Timer::GetCurrentTime() - tmSitting) > 60000) { if (!IsAffectedByBuffByGlobalGroup(GlobalGroup::Lich)) { auto tic_diff = std::min((Timer::GetCurrentTime() - tmSitting) / 60000, static_cast<uint32>(9)); if (tic_diff != 1) { // starts at 2 mins int tic_bonus = tic_diff * 1.5 * base; if (m_pp.InnateSkills[InnateRegen] != InnateDisabled) tic_bonus = tic_bonus * 1.2; base = tic_bonus; skip_innate = true; } else if (m_pp.InnateSkills[InnateRegen] == InnateDisabled) { // no innate regen gets first tick int tic_bonus = base * 1.5; base = tic_bonus; } } } } if (!skip_innate && m_pp.InnateSkills[InnateRegen] != InnateDisabled) { if (level >= 50) { ++base; if (level >= 55) { ++base; } } base *= 2; } if (IsStarved()) base = 0; base += GroupLeadershipAAHealthRegeneration(); // some IsKnockedOut that sets to -1 base = base * 100.0f * AreaHPRegen * 0.01f + 0.5f; // another check for IsClient && !(base + item_regen) && Cur_HP <= 0 do --base; do later if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { auto max_hp = GetMaxHP(); int fast_regen = 6 * (max_hp / zone->newzone_data.FastRegenHP); if (base < fast_regen) // weird, but what the client is doing base = fast_regen; } int regen = base + item_regen + spellbonuses.HPRegen; // TODO: client does this in buff tick return (regen * RuleI(Character, HPRegenMultiplier) / 100); }
int32 Client::CalcEnduranceRegen(bool bCombat) { int base = 0; if (!IsStarved()) { auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { base = static_cast<int>(base_data->end_regen); if (!auto_attack && base > 0) base += base / 2; } } // so when we are mounted, our local client SpeedRun is always 0, so this is always false, but the packets we process it to our own shit :P bool is_running = runmode && animation != 0 && GetHorseId() == 0; // TODO: animation is really what MQ2 calls SpeedRun int weight_limit = GetSTR(); auto level = GetLevel(); if (GetClass() == MONK) { if (level > 99) weight_limit = 58; else if (level > 94) weight_limit = 57; else if (level > 89) weight_limit = 56; else if (level > 84) weight_limit = 55; else if (level > 79) weight_limit = 54; else if (level > 64) weight_limit = 53; else if (level > 63) weight_limit = 50; else if (level > 61) weight_limit = 47; else if (level > 59) weight_limit = 45; else if (level > 54) weight_limit = 40; else if (level > 50) weight_limit = 38; else if (level > 44) weight_limit = 36; else if (level > 29) weight_limit = 34; else if (level > 14) weight_limit = 32; else weight_limit = 30; } bool encumbered = (CalcCurrentWeight() / 10) >= weight_limit; if (is_running) base += level / -15; if (encumbered) base += level / -15; auto item_bonus = GetHeroicAGI() + GetHeroicDEX() + GetHeroicSTA() + GetHeroicSTR(); item_bonus = item_bonus / 4 / 50; item_bonus += itembonuses.EnduranceRegen; // this is capped already base += item_bonus; base = base * AreaEndRegen + 0.5f; auto aa_regen = aabonuses.EnduranceRegen; int regen = base; if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { auto max_end = GetMaxEndurance(); int fast_regen = 6 * (max_end / zone->newzone_data.FastRegenEndurance); if (aa_regen < fast_regen) // weird, but what the client is doing aa_regen = fast_regen; } regen += aa_regen; regen += spellbonuses.EnduranceRegen; // TODO: client does this in buff tick return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100); }