diff --git a/common/ruletypes.h b/common/ruletypes.h index 96f3e9162..9edd9e427 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -242,6 +242,9 @@ RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a mer RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse") RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor") RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat") +RULE_BOOL(Mercs, MercsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.") +RULE_INT(Mercs, MercsHasteCap, 100, "Haste cap for non-v3(over haste) haste") +RULE_INT(Mercs, MercsHastev3Cap, 25, "Haste cap for v3(over haste) haste") RULE_CATEGORY_END() RULE_CATEGORY(Guild) @@ -668,6 +671,9 @@ RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if e RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them") RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732") RULE_BOOL(NPC, DisableLastNames, false, "Enable to disable NPC Last Names") +RULE_BOOL(NPC, NPCIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.") +RULE_INT(NPC, NPCHasteCap, 150, "Haste cap for non-v3(over haste) haste") +RULE_INT(NPC, NPCHastev3Cap, 25, "Haste cap for v3(over haste) haste") RULE_CATEGORY_END() RULE_CATEGORY(Aggro) @@ -759,6 +765,9 @@ RULE_BOOL(Bots, CazicTouchBotsOwner, true, "Default True. Cazic Touch/DT will hi RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.") RULE_BOOL(Bots, BotsCanClickItems, true, "Enables the ability for bots to click items they have equipped. Default TRUE") RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to click Mage Epic 1.0. Default TRUE") +RULE_BOOL(Bots, BotsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.") +RULE_INT(Bots, BotsHasteCap, 100, "Haste cap for non-v3(over haste) haste") +RULE_INT(Bots, BotsHastev3Cap, 25, "Haste cap for v3(over haste) haste") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/mob.cpp b/zone/mob.cpp index 643b0a59a..c9a8e0292 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2415,7 +2415,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window) DialogueWindow::TableCell( fmt::format( "{} ({})", - Strings::Commify(GetHaste()), + IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()), Strings::Commify(RuleI(Character, HasteCap)) ) ) @@ -2682,12 +2682,12 @@ void Mob::SendStatsWindow(Client* c, bool use_window) ).c_str() ); - if (GetHaste()) { + if ((IsClient() && CastToClient()->GetHaste()) || (!IsClient() && GetHaste())) { c->Message( Chat::White, fmt::format( "Haste: {}/{} (Item: {} + Spell: {} + Over: {})", - Strings::Commify(GetHaste()), + IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()), Strings::Commify(RuleI(Character, HasteCap)), Strings::Commify(itembonuses.haste), Strings::Commify(spellbonuses.haste + spellbonuses.hastetype2), @@ -3217,6 +3217,14 @@ void Mob::ShowStats(Client* c) ).c_str() ); + c->Message( + Chat::White, + fmt::format( + "Combat Stats | Haste: {}", + GetHaste() + ).c_str() + ); + // Stats c->Message( Chat::White, @@ -5431,10 +5439,19 @@ int Mob::GetHaste() h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2; // 26+ no cap, 1-25 10 - if (level > 25 || (IsClient() && RuleB(Character, IgnoreLevelBasedHasteCaps))) // 26+ + if ( + level > 25 || + ( + (IsNPC() && RuleB(NPC, NPCIgnoreLevelBasedHasteCaps)) || + (IsBot() && RuleB(Bots, BotsIgnoreLevelBasedHasteCaps)) || + (IsMerc() && RuleB(Mercs, MercsIgnoreLevelBasedHasteCaps)) + ) + ) { h += itembonuses.haste; - else // 1-25 + } + else { // 1-25 h += itembonuses.haste > 10 ? 10 : itembonuses.haste; + } // mobs are different! Mob *owner = nullptr; @@ -5446,23 +5463,36 @@ int Mob::GetHaste() cap = 10 + level; cap += std::max(0, owner->GetLevel() - 39) + std::max(0, owner->GetLevel() - 60); } else { - cap = 150; + cap = (IsNPC() ? RuleI(NPC, NPCHasteCap) : IsBot() ? RuleI(Bots, BotsHasteCap) : IsMerc() ? RuleI(Mercs, MercsHasteCap) : 150); } if(h > cap) h = cap; // 51+ 25 (despite there being higher spells...), 1-50 10 - if (level > 50 || (IsClient() && RuleB(Character, IgnoreLevelBasedHasteCaps))) { // 51+ - cap = RuleI(Character, Hastev3Cap); - if (spellbonuses.hastetype3 > cap) { - h += cap; - } else { - h += spellbonuses.hastetype3; + if ( + (IsNPC() && !RuleB(NPC, NPCIgnoreLevelBasedHasteCaps)) || + (IsBot() && !RuleB(Bots, BotsIgnoreLevelBasedHasteCaps)) || + (IsMerc() && !RuleB(Mercs, MercsIgnoreLevelBasedHasteCaps)) + ) { + if (level > 50) { // 51+ + cap = (IsNPC() ? RuleI(NPC, NPCHastev3Cap) : IsBot() ? RuleI(Bots, BotsHastev3Cap) : IsMerc() ? RuleI(Mercs, MercsHastev3Cap) : RuleI(Character, Hastev3Cap)); + + if (spellbonuses.hastetype3 > cap) { + h += cap; + } + else { + h += spellbonuses.hastetype3; + } + } + else { // 1-50 + h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; } - } else { // 1-50 - h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; } + else { + h += spellbonuses.hastetype3; + } + h += extra_haste; //GM granted haste. return 100 + h; diff --git a/zone/mob_info.cpp b/zone/mob_info.cpp index e8c5bb7d3..1e9de2dd9 100644 --- a/zone/mob_info.cpp +++ b/zone/mob_info.cpp @@ -234,6 +234,15 @@ inline std::string GetMobAttributeByString(Mob *mob, const std::string &attribut return std::to_string(mob->GetMitigationAC()); } + if (attribute == "haste") { + if (mob->IsClient()) { + return Strings::Commify(std::to_string(mob->CastToClient()->GetHaste())); + } + else { + return Strings::Commify(std::to_string(mob->GetHaste())); + } + } + if (mob->IsNPC()) { NPC *npc = mob->CastToNPC(); @@ -700,6 +709,7 @@ void Mob::DisplayInfo(Mob *mob) "total_defense", "offense", "mitigation_ac", + "haste", }; window_text += WriteDisplayInfoSection(mob, "Calculations", calculations, 1, true);