From ddbb30498694ba0732e67a24046206cd6a587655 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 11:42:29 +0800 Subject: [PATCH 01/11] Rewrite stats overflow calculation --- src/factory/StatsCollector.cpp | 6 +- src/factory/StatsWeightCalculator.cpp | 129 ++++++++++++++++++-------- src/factory/StatsWeightCalculator.h | 3 +- 3 files changed, 96 insertions(+), 42 deletions(-) diff --git a/src/factory/StatsCollector.cpp b/src/factory/StatsCollector.cpp index 1216b35c3..1f6c6d655 100644 --- a/src/factory/StatsCollector.cpp +++ b/src/factory/StatsCollector.cpp @@ -233,7 +233,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) { if (type_ != CollectorType::SPELL_HEAL) stats[STATS_TYPE_CRIT] += 50; return true; - case 59620: + case 59620: // Berserk if (type_ == CollectorType::MELEE) stats[STATS_TYPE_ATTACK_POWER] += 120; return true; @@ -254,6 +254,10 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) { /// Noticing that heroic item can not be triggered, probably a bug to report to AC if (type_ == CollectorType::SPELL_HEAL) return true; + break; + case 71903: // Shadowmourne + stats[STATS_TYPE_STRENGTH] += 200; + return true; default: break; } diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index ef95fb9be..c708c946c 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -58,6 +58,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId) collector_->CollectItemStats(proto); + if (enable_overflow_penalty_) + ApplyOverflowPenalty(player_); + GenerateWeights(player_); for (uint32 i = 0; i < STATS_TYPE_MAX; i++) { @@ -89,6 +92,9 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId) collector_->CollectEnchantStats(enchant); + if (enable_overflow_penalty_) + ApplyOverflowPenalty(player_); + GenerateWeights(player_); for (uint32 i = 0; i < STATS_TYPE_MAX; i++) { @@ -102,9 +108,6 @@ void StatsWeightCalculator::GenerateWeights(Player* player) { GenerateBasicWeights(player); GenerateAdditionalWeights(player); - - if (enable_overflow_penalty_) - ApplyOverflowPenalty(player); } void StatsWeightCalculator::GenerateBasicWeights(Player* player) @@ -219,15 +222,15 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) } else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution { - stats_weights_[STATS_TYPE_AGILITY] += 1.1f; + stats_weights_[STATS_TYPE_AGILITY] += 1.3f; stats_weights_[STATS_TYPE_STRENGTH] += 2.5f; stats_weights_[STATS_TYPE_INTELLECT] += 0.15f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f; - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.5f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.7f; stats_weights_[STATS_TYPE_HIT] += 1.9f; - stats_weights_[STATS_TYPE_CRIT] += 1.2f; - stats_weights_[STATS_TYPE_HASTE] += 1.3f; + stats_weights_[STATS_TYPE_CRIT] += 1.3f; + stats_weights_[STATS_TYPE_HASTE] += 1.2f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } @@ -414,12 +417,12 @@ void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate co void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { - // penalty for different type armor - if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH && - proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass)) - { - weight_ *= 0.8; - } + // // penalty for different type armor + // if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH && + // proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass)) + // { + // weight_ *= 1.0; + // } // double hand if (proto->Class == ITEM_CLASS_WEAPON) { @@ -444,7 +447,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) } // spec with double hand // fury without duel wield, arms, bear, retribution, blood dk - if (isDoubleHand && + if (!isDoubleHand && ((cls == CLASS_HUNTER && !player_->CanDualWield()) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) || @@ -452,13 +455,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) || (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))) { - weight_ *= 10; + weight_ *= 0.1; } // fury with titan's grip - if (isDoubleHand && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && + if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM) && (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip())) { - weight_ *= 10; + weight_ *= 0.1; } } if (proto->Class == ITEM_CLASS_WEAPON) @@ -481,6 +484,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 1.1; } + bool slowDelay = proto->Delay > 2500; + if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && slowDelay) + weight_ *= 1.1; } } @@ -505,37 +511,57 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) { { float hit_current, hit_overflow; - if (type_ == CollectorType::SPELL) + float validPoints; + // m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + // m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE); + if (GetHitOverflowType(player) == CollectorType::SPELL) { - hit_current = player->GetRatingBonusValue(CR_HIT_SPELL); + hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); + hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); hit_overflow = SPELL_HIT_OVERFLOW; + if (hit_overflow > hit_current) + validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_SPELL); + else + validPoints = 0; } - else if (type_ == CollectorType::MELEE) + else if (GetHitOverflowType(player) == CollectorType::MELEE) { - hit_current = player->GetRatingBonusValue(CR_HIT_MELEE); + hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + hit_current += player->GetRatingBonusValue(CR_HIT_MELEE); hit_overflow = MELEE_HIT_OVERFLOW; + if (hit_overflow > hit_current) + validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_MELEE); + else + validPoints = 0; } else { - hit_current = player->GetRatingBonusValue(CR_HIT_RANGED); + hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + hit_current += player->GetRatingBonusValue(CR_HIT_RANGED); hit_overflow = RANGED_HIT_OVERFLOW; + if (hit_overflow > hit_current) + validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_RANGED); + else + validPoints = 0; } - if (hit_current >= hit_overflow) - stats_weights_[STATS_TYPE_HIT] = 0.0f; - else if (hit_current >= hit_overflow * 0.8) - stats_weights_[STATS_TYPE_HIT] /= 1.5; + collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints); } { if (type_ == CollectorType::MELEE) { float expertise_current, expertise_overflow; - expertise_current = player->GetRatingBonusValue(CR_EXPERTISE); + expertise_current = player->GetUInt32Value(PLAYER_EXPERTISE); + expertise_current += player->GetRatingBonusValue(CR_EXPERTISE); expertise_overflow = EXPERTISE_OVERFLOW; - if (expertise_current >= expertise_overflow) - stats_weights_[STATS_TYPE_EXPERTISE] = 0.0f; - else if (expertise_current >= expertise_overflow * 0.8) - stats_weights_[STATS_TYPE_EXPERTISE] /= 1.5; + + float validPoints; + if (expertise_overflow > expertise_current) + validPoints = (expertise_overflow - expertise_current) / player->GetRatingMultiplier(CR_EXPERTISE); + else + validPoints = 0; + + collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints); } } @@ -545,10 +571,14 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) float defense_current, defense_overflow; defense_current = player->GetRatingBonusValue(CR_DEFENSE_SKILL); defense_overflow = DEFENSE_OVERFLOW; - if (defense_current >= defense_overflow) - stats_weights_[STATS_TYPE_DEFENSE] /= 2; - else if (defense_current >= defense_overflow * 0.8) - stats_weights_[STATS_TYPE_DEFENSE] /= 1.5; + + float validPoints; + if (defense_overflow > defense_current) + validPoints = (defense_overflow - defense_current) / player->GetRatingMultiplier(CR_DEFENSE_SKILL); + else + validPoints = 0; + + collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints); } } @@ -558,10 +588,29 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) float armor_penetration_current, armor_penetration_overflow; armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION); armor_penetration_overflow = ARMOR_PENETRATION_OVERFLOW; - if (armor_penetration_current >= armor_penetration_overflow) - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] = 0.0f; - if (armor_penetration_current >= armor_penetration_overflow * 0.8) - stats_weights_[STATS_TYPE_ARMOR_PENETRATION] /= 1.5; + + float validPoints; + if (armor_penetration_overflow > armor_penetration_current) + validPoints = (armor_penetration_overflow - armor_penetration_current) / player->GetRatingMultiplier(CR_ARMOR_PENETRATION); + else + validPoints = 0; + + collector_->stats[STATS_TYPE_ARMOR_PENETRATION] = std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints); } } -} \ No newline at end of file +} + +CollectorType StatsWeightCalculator::GetHitOverflowType(Player* player) +{ + cls = player->getClass(); + tab = AiFactory::GetPlayerSpecTab(player); + if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) + return CollectorType::SPELL; + if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT) + return CollectorType::SPELL; + if (cls == CLASS_ROGUE) + return CollectorType::SPELL; + + return type_; +} + \ No newline at end of file diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index 721eff9e6..81b8aa677 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -15,7 +15,7 @@ enum StatsOverflowThreshold { - SPELL_HIT_OVERFLOW = 17, + SPELL_HIT_OVERFLOW = 14, MELEE_HIT_OVERFLOW = 8, RANGED_HIT_OVERFLOW = 8, EXPERTISE_OVERFLOW = 26, @@ -48,6 +48,7 @@ class StatsWeightCalculator bool NotBestArmorType(uint32 item_subclass_armor); void ApplyOverflowPenalty(Player* player); + CollectorType GetHitOverflowType(Player* player); private: Player* player_; From 9fb8f2fdf4f65931123301c706de748a373f8276 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 15:12:25 +0800 Subject: [PATCH 02/11] Dps paladin --- src/strategy/paladin/DpsPaladinStrategy.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/strategy/paladin/DpsPaladinStrategy.cpp b/src/strategy/paladin/DpsPaladinStrategy.cpp index 723fffd3b..efd55a6a9 100644 --- a/src/strategy/paladin/DpsPaladinStrategy.cpp +++ b/src/strategy/paladin/DpsPaladinStrategy.cpp @@ -83,18 +83,20 @@ DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrat NextAction** DpsPaladinStrategy::getDefaultActions() { return NextAction::array(0, - new NextAction("crusader strike", ACTION_DEFAULT + 0.6f), - new NextAction("hammer of wrath", ACTION_DEFAULT + 0.5f), + new NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f), + new NextAction("crusader strike", ACTION_DEFAULT + 0.5f), new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.4f), new NextAction("divine storm", ACTION_DEFAULT + 0.3f), new NextAction("consecration", ACTION_DEFAULT + 0.1f), - new NextAction("melee", ACTION_DEFAULT), NULL); + new NextAction("melee", ACTION_DEFAULT), nullptr); } void DpsPaladinStrategy::InitTriggers(std::vector& triggers) { GenericPaladinStrategy::InitTriggers(triggers); + // triggers.push_back(new TriggerNode( + // "enough mana", NextAction::array(0, new NextAction("consecration", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( From 67892378431483198c329fdbd0af263b3e91f3b8 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 15:12:39 +0800 Subject: [PATCH 03/11] Dps hunter --- src/factory/StatsWeightCalculator.cpp | 16 +++++++++++++++- src/factory/StatsWeightCalculator.h | 1 + src/strategy/hunter/DpsHunterStrategy.cpp | 11 ++++++----- src/strategy/hunter/HunterActions.h | 11 +++++++++++ src/strategy/hunter/HunterAiObjectContext.cpp | 5 +++++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index c708c946c..bf08e19e0 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -108,6 +108,7 @@ void StatsWeightCalculator::GenerateWeights(Player* player) { GenerateBasicWeights(player); GenerateAdditionalWeights(player); + ApplyWeightFinetune(player); } void StatsWeightCalculator::GenerateBasicWeights(Player* player) @@ -458,7 +459,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) weight_ *= 0.1; } // fury with titan's grip - if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM) && + if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) && (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip())) { weight_ *= 0.1; @@ -600,6 +601,19 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) } } +void StatsWeightCalculator::ApplyWeightFinetune(Player* player) +{ + { + if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED) + { + float armor_penetration_current, armor_penetration_overflow; + armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION); + if (armor_penetration_current > 50) + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f; + } + } +} + CollectorType StatsWeightCalculator::GetHitOverflowType(Player* player) { cls = player->getClass(); diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index 81b8aa677..f6f4bf7ef 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -48,6 +48,7 @@ class StatsWeightCalculator bool NotBestArmorType(uint32 item_subclass_armor); void ApplyOverflowPenalty(Player* player); + void ApplyWeightFinetune(Player* player); CollectorType GetHitOverflowType(Player* player); private: diff --git a/src/strategy/hunter/DpsHunterStrategy.cpp b/src/strategy/hunter/DpsHunterStrategy.cpp index 325f3b93e..e9d0e2f5d 100644 --- a/src/strategy/hunter/DpsHunterStrategy.cpp +++ b/src/strategy/hunter/DpsHunterStrategy.cpp @@ -41,12 +41,13 @@ DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy NextAction** DpsHunterStrategy::getDefaultActions() { return NextAction::array( - 0, new NextAction("kill shot", ACTION_DEFAULT + 0.6f), new NextAction("chimera shot", ACTION_DEFAULT + 0.5f), - new NextAction("explosive shot", ACTION_DEFAULT + 0.4f), new NextAction("aimed shot", ACTION_DEFAULT + 0.3f), - /*new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),*/ new NextAction("steady shot", ACTION_DEFAULT + 0.1f), + 0, new NextAction("kill shot", ACTION_DEFAULT + 0.8f), new NextAction("chimera shot", ACTION_DEFAULT + 0.7f), + new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f), + new NextAction("silencing shot", ACTION_DEFAULT + 0.4f), + new NextAction("kill command", ACTION_DEFAULT + 0.3f), + new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), + new NextAction("steady shot", ACTION_DEFAULT + 0.1f), new NextAction("auto shot", ACTION_DEFAULT), nullptr); - // return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new - // NextAction("auto attack", 9.0f), nullptr); } void DpsHunterStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/hunter/HunterActions.h b/src/strategy/hunter/HunterActions.h index 868905c61..87595b3ba 100644 --- a/src/strategy/hunter/HunterActions.h +++ b/src/strategy/hunter/HunterActions.h @@ -78,6 +78,9 @@ END_SPELL_ACTION() BEGIN_RANGED_SPELL_ACTION(CastKillShotAction, "kill shot") END_SPELL_ACTION() +BEGIN_RANGED_SPELL_ACTION(CastSilencingShotAction, "silencing shot") +END_SPELL_ACTION() + BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot") END_SPELL_ACTION() @@ -139,6 +142,14 @@ class CastMendPetAction : public CastAuraSpellAction std::string const GetTargetName() override { return "pet target"; } }; +class CastKillCommandAction : public CastAuraSpellAction +{ +public: + CastKillCommandAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "kill command") {} + + std::string const GetTargetName() override { return "pet target"; } +}; + class CastRevivePetAction : public CastBuffSpellAction { public: diff --git a/src/strategy/hunter/HunterAiObjectContext.cpp b/src/strategy/hunter/HunterAiObjectContext.cpp index 32548aadf..dbfb6a54e 100644 --- a/src/strategy/hunter/HunterAiObjectContext.cpp +++ b/src/strategy/hunter/HunterAiObjectContext.cpp @@ -143,6 +143,7 @@ class HunterAiObjectContextInternal : public NamedObjectContext creators["scorpid sting"] = &HunterAiObjectContextInternal::scorpid_sting; creators["hunter's mark"] = &HunterAiObjectContextInternal::hunters_mark; creators["mend pet"] = &HunterAiObjectContextInternal::mend_pet; + creators["kill command"] = &HunterAiObjectContextInternal::kill_command; creators["revive pet"] = &HunterAiObjectContextInternal::revive_pet; creators["call pet"] = &HunterAiObjectContextInternal::call_pet; creators["black arrow"] = &HunterAiObjectContextInternal::black_arrow; @@ -171,6 +172,7 @@ class HunterAiObjectContextInternal : public NamedObjectContext creators["steady shot"] = &HunterAiObjectContextInternal::steady_shot; creators["kill shot"] = &HunterAiObjectContextInternal::kill_shot; creators["misdirection on main tank"] = &HunterAiObjectContextInternal::misdirection_on_main_tank; + creators["silencing shot"] = &HunterAiObjectContextInternal::silencing_shot; } private: @@ -196,6 +198,7 @@ class HunterAiObjectContextInternal : public NamedObjectContext static Action* scorpid_sting(PlayerbotAI* botAI) { return new CastScorpidStingAction(botAI); } static Action* hunters_mark(PlayerbotAI* botAI) { return new CastHuntersMarkAction(botAI); } static Action* mend_pet(PlayerbotAI* botAI) { return new CastMendPetAction(botAI); } + static Action* kill_command(PlayerbotAI* botAI) { return new CastKillCommandAction(botAI); } static Action* revive_pet(PlayerbotAI* botAI) { return new CastRevivePetAction(botAI); } static Action* call_pet(PlayerbotAI* botAI) { return new CastCallPetAction(botAI); } static Action* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); } @@ -217,6 +220,8 @@ class HunterAiObjectContextInternal : public NamedObjectContext static Action* steady_shot(PlayerbotAI* ai) { return new CastSteadyShotAction(ai); } static Action* kill_shot(PlayerbotAI* ai) { return new CastKillShotAction(ai); } static Action* misdirection_on_main_tank(PlayerbotAI* ai) { return new CastMisdirectionOnMainTankAction(ai); } + static Action* silencing_shot(PlayerbotAI* ai) { return new CastSilencingShotAction(ai); } + }; HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) From 5de242e80b49d4f5e08fc43f1c0d7bb44eed5f12 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 15:12:48 +0800 Subject: [PATCH 04/11] Hunter pet talents init --- conf/playerbots.conf.dist | 22 +++- src/PlayerbotAIConfig.cpp | 57 ++++++++- src/PlayerbotAIConfig.h | 3 + src/factory/PlayerbotFactory.cpp | 203 +++++++++++++++++++++---------- 4 files changed, 214 insertions(+), 71 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 88b5213cb..9f80241b0 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -821,13 +821,25 @@ AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732 AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 AiPlayerbot.PremadeSpecName.3.1 = mm pve -AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42915,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233135030051 -AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 +AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732 +AiPlayerbot.PremadeSpecLink.3.1.60 = -005305111230013233125030151-5 +AiPlayerbot.PremadeSpecLink.3.1.80 = 502-005305131230013233135031351-5000002 AiPlayerbot.PremadeSpecName.3.2 = surv pve AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135001331 -AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330522135301331 +AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311 +AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330532135301321 + +# HUNTER PET +# +# Ferocity +AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003030103010101 +AiPlayerbot.PremadeHunterPetLink.0.20 = 2100013030103010122 +# Tenacity +AiPlayerbot.PremadeHunterPetLink.1.16 = 21103000300120101001 +AiPlayerbot.PremadeHunterPetLink.1.20 = 21303010300120101002 +# Cunning +AiPlayerbot.PremadeHunterPetLink.2.16 = 2100020330000211001 +AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221 # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index ffc41581a..d285490a1 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -331,6 +331,17 @@ bool PlayerbotAIConfig::Initialize() parsedSpecLinkOrder[cls][spec][level] = ParseTempTalentsOrder(cls, premadeSpecLink[cls][spec][level]); } } + for (uint32 spec = 0; spec < 3; ++spec) + { + for (uint32 points = 0; points < 21; ++points) + { + std::ostringstream os; + os << "AiPlayerbot.PremadeHunterPetLink." << spec << "." << points; + premadeHunterPetLink[spec][points] = sConfigMgr->GetOption(os.str().c_str(), "", false); + parsedHunterPetLinkOrder[spec][points] = + ParseTempPetTalentsOrder(spec, premadeHunterPetLink[spec][points]); + } + } for (uint32 spec = 0; spec < MAX_SPECNO; ++spec) { std::ostringstream os; @@ -486,7 +497,7 @@ bool PlayerbotAIConfig::Initialize() RandomPlayerbotFactory::CreateRandomBots(); if (sPlayerbotAIConfig->addClassCommand) sRandomPlayerbotMgr->PrepareAddclassCache(); - + if (World::IsStopped()) { return true; @@ -760,3 +771,47 @@ std::vector> PlayerbotAIConfig::ParseTempTalentsOrder(uint32 } return res; } + +std::vector> PlayerbotAIConfig::ParseTempPetTalentsOrder(uint32 spec, std::string tab_link) +{ + // check bad link + // uint32 classMask = 1 << (cls - 1); + std::vector spells; + std::vector> orders; + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const* talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) + continue; + + TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); + if (!talentTabInfo) + continue; + + if (!((1 << spec) & talentTabInfo->petTalentMask)) + continue; + // skip some duplicate spells like dash/dive + if (talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203) + continue; + + spells.push_back(talentInfo); + } + std::sort(spells.begin(), spells.end(), + [&](TalentEntry const* lhs, TalentEntry const* rhs) + { return lhs->Row != rhs->Row ? lhs->Row < rhs->Row : lhs->Col < rhs->Col; }); + for (int i = 0; i < tab_link.size(); i++) + { + if (i >= spells.size()) + { + break; + } + int lvl = tab_link[i] - '0'; + if (lvl == 0) + continue; + orders.push_back({spells[i]->Row, spells[i]->Col, (uint32)lvl}); + } + // sort by talent tab size + std::sort(orders.begin(), orders.end(), [&](auto& lhs, auto& rhs) { return lhs.size() > rhs.size(); }); + + return orders; +} diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 57f3ce809..f37571317 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -195,7 +195,9 @@ class PlayerbotAIConfig std::string premadeSpecGlyph[MAX_CLASSES][MAX_SPECNO]; std::vector parsedSpecGlyph[MAX_CLASSES][MAX_SPECNO]; std::string premadeSpecLink[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL]; + std::string premadeHunterPetLink[3][21]; std::vector> parsedSpecLinkOrder[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL]; + std::vector> parsedHunterPetLinkOrder[3][21]; uint32 randomClassSpecProb[MAX_CLASSES][MAX_SPECNO]; uint32 randomClassSpecIndex[MAX_CLASSES][MAX_SPECNO]; @@ -325,6 +327,7 @@ class PlayerbotAIConfig void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel); static std::vector> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order); + static std::vector> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order); }; #define sPlayerbotAIConfig PlayerbotAIConfig::instance() diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a5116fdfb..3dfc50dc5 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -39,6 +39,7 @@ #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) +const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING, SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING, SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING, @@ -611,8 +612,10 @@ void PlayerbotFactory::InitPetTalents() // pet_family->petTalentType); return; } - // pet->resetTalents(); std::unordered_map> spells; + bool diveTypePet = (1LL << ci->family) & diveMask; + LOG_INFO("playerbots", "DIVEMASK:{}", diveMask); + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(i); @@ -624,49 +627,112 @@ void PlayerbotFactory::InitPetTalents() // prevent learn talent for different family (cheating) if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) continue; - + bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203; + if (diveClass && !diveTypePet) + continue; + bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 || talentInfo->TalentID == 2109; + if (dashClass && diveTypePet) + continue; spells[talentInfo->Row].push_back(talentInfo); } - uint32 curTalentPoints = pet->GetFreeTalentPoints(); + std::vector> order = sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); - int row = 0; - // LOG_INFO("playerbots", "{} learning, max talent points: {}, cur: {}", bot->GetName().c_str(), maxTalentPoints, - // curTalentPoints); - for (auto i = spells.begin(); i != spells.end(); ++i, ++row) + + if (order.empty()) { - std::vector& spells_row = i->second; - if (spells_row.empty()) + int row = 0; + for (auto i = spells.begin(); i != spells.end(); ++i, ++row) { - LOG_INFO("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first); - continue; + std::vector& spells_row = i->second; + if (spells_row.empty()) + { + LOG_INFO("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first); + continue; + } + int attemptCount = 0; + // keep learning for the last row + while (!spells_row.empty() && + ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && + attemptCount++ < 10 && pet->GetFreeTalentPoints()) + { + int index = urand(0, spells_row.size() - 1); + TalentEntry const* talentInfo = spells_row[index]; + int maxRank = 0; + for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank) + { + uint32 spellId = talentInfo->RankID[rank]; + if (!spellId) + continue; + + maxRank = rank; + } + // LOG_INFO("playerbots", "{} learn pet talent {}({})", bot->GetName().c_str(), talentInfo->TalentID, + // maxRank); + if (talentInfo->DependsOn) + { + bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn, + std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); + } + bot->LearnPetTalent(pet->GetGUID(), talentInfo->TalentID, maxRank); + spells_row.erase(spells_row.begin() + index); + } } - int attemptCount = 0; - // keep learning for the last row - while (!spells_row.empty() && - ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && - attemptCount++ < 10 && pet->GetFreeTalentPoints()) + } + else + { + uint32 spec = pet_family->petTalentType; + uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); + while (startPoints > 1 && startPoints < 20 && + sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) { - int index = urand(0, spells_row.size() - 1); - TalentEntry const* talentInfo = spells_row[index]; - int maxRank = 0; - for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank) + startPoints--; + } + + for (uint32 points = startPoints; points <= 20; points++) + { + if (sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points].size() == 0) + continue; + for (std::vector& p : sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points]) { - uint32 spellId = talentInfo->RankID[rank]; - if (!spellId) - continue; + uint32 row = p[0], col = p[1], lvl = p[2]; + uint32 talentID = 0; + uint32 learnLevel = 0; + std::vector& spell = spells[row]; + for (TalentEntry const* talentInfo : spell) + { + if (talentInfo->Col != col) + { + continue; + } + if (talentInfo->DependsOn) + { + bot->LearnPetTalent(pet->GetGUID(),talentInfo->DependsOn, + std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); + } + talentID = talentInfo->TalentID; - maxRank = rank; + uint32 currentTalentRank = 0; + for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank) + { + if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank])) + { + currentTalentRank = rank + 1; + break; + } + } + learnLevel = std::min(lvl, pet->GetFreeTalentPoints() + currentTalentRank) - 1; + } + bot->LearnPetTalent(pet->GetGUID(), talentID, learnLevel); + if (pet->GetFreeTalentPoints() == 0) + { + break; + } } - // LOG_INFO("playerbots", "{} learn pet talent {}({})", bot->GetName().c_str(), talentInfo->TalentID, - // maxRank); - if (talentInfo->DependsOn) + if (pet->GetFreeTalentPoints() == 0) { - bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn, - std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); + break; } - bot->LearnPetTalent(pet->GetGUID(), talentInfo->TalentID, maxRank); - spells_row.erase(spells_row.begin() + index); } } bot->SendTalentsInfoData(true); @@ -1467,6 +1533,7 @@ void PlayerbotFactory::InitEquipment(bool incremental) else if (blevel == 80) delta = 9; + StatsWeightCalculator calculator(bot); for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) @@ -1484,6 +1551,18 @@ void PlayerbotFactory::InitEquipment(bool incremental) if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) continue; + Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + + if (!incremental && oldItem) + { + continue; + } + + if (oldItem) + { + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + uint32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { @@ -1555,41 +1634,19 @@ void PlayerbotFactory::InitEquipment(bool incremental) break; } } while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_NORMAL); - } - - StatsWeightCalculator calculator(bot); - for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) - { - if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) - continue; - - if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) - continue; - - if (level < 25 && slot == EQUIPMENT_SLOT_NECK) - continue; - - if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) - continue; std::vector& ids = items[slot]; if (ids.empty()) { continue; } - Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - - if (incremental && !IsDesiredReplacement(oldItem)) - { - continue; - } - + float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) { uint32 skipProb = 25; - if (urand(0, 100) <= skipProb) + if (urand(1, 100) <= skipProb) continue; uint32 newItemId = ids[index]; @@ -1601,9 +1658,6 @@ void PlayerbotFactory::InitEquipment(bool incremental) if (!CanEquipItem(proto)) continue; - if (oldItem && oldItem->GetTemplate()->ItemId == newItemId) - continue; - if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; @@ -1618,10 +1672,7 @@ void PlayerbotFactory::InitEquipment(bool incremental) { continue; } - if (oldItem) - { - bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); - } + uint16 dest; if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) { @@ -1632,10 +1683,28 @@ void PlayerbotFactory::InitEquipment(bool incremental) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); - // bot->AutoUnequipOffhandIfNeed(); - // EnchantItem(newItem); } } + // secondary init for better equips + if (!incremental) + InitEquipment(true); + + // for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) + // { + // if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) + // continue; + + // if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) + // continue; + + // if (level < 25 && slot == EQUIPMENT_SLOT_NECK) + // continue; + + // if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) + // continue; + + + // } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) @@ -3818,7 +3887,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) if (!gemProperties) continue; - if ((socketColor & gemProperties->color) == 0) + if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket continue; uint32 enchant_id = gemProperties->spellitemenchantement; @@ -3829,6 +3898,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) StatsWeightCalculator calculator(bot); float score = calculator.CalculateEnchant(enchant_id); if (curCount[0] != 0) + { // Ensure meta gem activation for (int i = 1; i < curCount.size(); i++) { @@ -3838,6 +3908,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) break; } } + } + if (socketColor & gemProperties->color) + score *= 1.2; if (score > bestGemScore) { enchantIdChosen = enchant_id; From 484a2ae4583a34c6aa2b28e46f13e34ee11e6355 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 18:08:19 +0800 Subject: [PATCH 05/11] Item usage and equip initialization --- src/PlayerbotMgr.cpp | 32 +++++++++++++++ src/factory/PlayerbotFactory.cpp | 56 +++++++++++++------------- src/factory/PlayerbotFactory.h | 2 +- src/strategy/values/ItemUsageValue.cpp | 2 +- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 1958f9aa9..7389c72c0 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -845,6 +845,22 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strncmp(cmd, "initself=", 9)) { + if (!strcmp(cmd, "initself=uncommon")) + { + if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + { + // OnBotLogin(master); + PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_UNCOMMON); + factory.Randomize(false); + messages.push_back("initself ok"); + return messages; + } + else + { + messages.push_back("ERROR: Only GM can use this command."); + return messages; + } + } if (!strcmp(cmd, "initself=rare")) { if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) @@ -877,6 +893,22 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg return messages; } } + if (!strcmp(cmd, "initself=legendary")) + { + if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + { + // OnBotLogin(master); + PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_LEGENDARY); + factory.Randomize(false); + messages.push_back("initself ok"); + return messages; + } + else + { + messages.push_back("ERROR: Only GM can use this command."); + return messages; + } + } int32 gs; if (sscanf(cmd, "initself=%d", &gs) != -1) { diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 3dfc50dc5..a40e28a42 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -614,7 +614,6 @@ void PlayerbotFactory::InitPetTalents() } std::unordered_map> spells; bool diveTypePet = (1LL << ci->family) & diveMask; - LOG_INFO("playerbots", "DIVEMASK:{}", diveMask); for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) { @@ -1513,7 +1512,7 @@ void Shuffle(std::vector& items) // } // } -void PlayerbotFactory::InitEquipment(bool incremental) +void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { std::unordered_map> items; // int tab = AiFactory::GetPlayerSpecTab(bot); @@ -1553,16 +1552,13 @@ void PlayerbotFactory::InitEquipment(bool incremental) Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - if (!incremental && oldItem) - { - continue; - } - - if (oldItem) + if (second_chance && oldItem) { bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); } + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + uint32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { @@ -1668,17 +1664,38 @@ void PlayerbotFactory::InitEquipment(bool incremental) bestItemForSlot = newItemId; } } + if (bestItemForSlot == 0) { continue; } - uint16 dest; if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) { continue; } + + if (incremental && oldItem) + { + float old_score = calculator.CalculateItem(oldItem->GetEntry()); + if (bestScoreForSlot < 1.2f * old_score) + continue; + } + + if (oldItem) + { + uint8 bagIndex = oldItem->GetBagSlot(); + uint8 slot = oldItem->GetSlot(); + uint8 dstBag = NULL_BAG; + + WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); + packet << bagIndex << slot << dstBag; + bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); + } + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); + bot->AutoUnequipOffhandIfNeed(); + // bot->AutoUnequipOffhandIfNeed(); if (newItem) { newItem->AddToWorld(); @@ -1686,25 +1703,10 @@ void PlayerbotFactory::InitEquipment(bool incremental) } } // secondary init for better equips - if (!incremental) - InitEquipment(true); + if (!incremental && !second_chance) + InitEquipment(incremental, true); - // for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) - // { - // if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) - // continue; - // if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) - // continue; - - // if (level < 25 && slot == EQUIPMENT_SLOT_NECK) - // continue; - - // if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) - // continue; - - - // } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) @@ -1978,7 +1980,7 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true)) { - InventoryResult result = bot->CanEquipItem(slot, dest, pItem, true, false); + InventoryResult result = botAI->CanEquipItem(slot, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; return result == EQUIP_ERR_OK; diff --git a/src/factory/PlayerbotFactory.h b/src/factory/PlayerbotFactory.h index f88ce96ed..e77947e54 100644 --- a/src/factory/PlayerbotFactory.h +++ b/src/factory/PlayerbotFactory.h @@ -122,7 +122,7 @@ class PlayerbotFactory static void InitTalentsByParsedSpecLink(Player* bot, std::vector> parsedSpecLink, bool reset); void InitAvailableSpells(); void InitClassSpells(); - void InitEquipment(bool incremental); + void InitEquipment(bool incremental, bool second_chance = false); void InitPet(); void InitAmmo(); static uint32 CalcMixedGearScore(uint32 gs, uint32 quality); diff --git a/src/strategy/values/ItemUsageValue.cpp b/src/strategy/values/ItemUsageValue.cpp index 9bdf206de..ca688a2db 100644 --- a/src/strategy/values/ItemUsageValue.cpp +++ b/src/strategy/values/ItemUsageValue.cpp @@ -166,7 +166,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto) return ITEM_USAGE_NONE; uint16 dest; - InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, false); + InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; From 406949f6ddb84ffe5b3269658d10d707ea9b9c27 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 20:08:44 +0800 Subject: [PATCH 06/11] Misdirection and tricks of the trade --- src/PlayerbotMgr.cpp | 4 ++-- src/strategy/hunter/DpsHunterStrategy.cpp | 2 +- src/strategy/hunter/GenericHunterStrategy.cpp | 2 +- .../rogue/AssassinationRogueStrategy.cpp | 2 +- src/strategy/rogue/DpsRogueStrategy.cpp | 2 +- src/strategy/triggers/GenericTriggers.cpp | 17 +++++++++++++++++ src/strategy/triggers/GenericTriggers.h | 8 ++++++++ src/strategy/triggers/TriggerContext.h | 3 +++ 8 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 7389c72c0..491e93ecc 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -724,14 +724,14 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje sPlayerbotAIConfig->autoInitEquipLevelLimitRatio; PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore); factory.Randomize(false); - return "ok, gear score limit: " + std::to_string(mixedGearScore / (ITEM_QUALITY_EPIC + 1)) + + return "ok, gear score limit: " + std::to_string(mixedGearScore / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + "(for epic)"; } else if (cmd.starts_with("init=") && sscanf(cmd.c_str(), "init=%d", &gs) != -1) { PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, gs); factory.Randomize(false); - return "ok, gear score limit: " + std::to_string(gs / (ITEM_QUALITY_EPIC + 1)) + "(for epic)"; + return "ok, gear score limit: " + std::to_string(gs / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + "(for epic)"; } } diff --git a/src/strategy/hunter/DpsHunterStrategy.cpp b/src/strategy/hunter/DpsHunterStrategy.cpp index e9d0e2f5d..3ba171a26 100644 --- a/src/strategy/hunter/DpsHunterStrategy.cpp +++ b/src/strategy/hunter/DpsHunterStrategy.cpp @@ -45,7 +45,7 @@ NextAction** DpsHunterStrategy::getDefaultActions() new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f), new NextAction("silencing shot", ACTION_DEFAULT + 0.4f), new NextAction("kill command", ACTION_DEFAULT + 0.3f), - new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), + // new NextAction("arcane shot", ACTION_DEFAULT + 0.2f), new NextAction("steady shot", ACTION_DEFAULT + 0.1f), new NextAction("auto shot", ACTION_DEFAULT), nullptr); } diff --git a/src/strategy/hunter/GenericHunterStrategy.cpp b/src/strategy/hunter/GenericHunterStrategy.cpp index e59df4cd4..0974e2d45 100644 --- a/src/strategy/hunter/GenericHunterStrategy.cpp +++ b/src/strategy/hunter/GenericHunterStrategy.cpp @@ -95,7 +95,7 @@ void GenericHunterStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("enemy too close for auto shot", NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); triggers.push_back( - new TriggerNode("misdirection on main tank", + new TriggerNode("low tank threat", NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL))); triggers.push_back( new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr))); diff --git a/src/strategy/rogue/AssassinationRogueStrategy.cpp b/src/strategy/rogue/AssassinationRogueStrategy.cpp index 380ac292f..5f3d22115 100644 --- a/src/strategy/rogue/AssassinationRogueStrategy.cpp +++ b/src/strategy/rogue/AssassinationRogueStrategy.cpp @@ -83,7 +83,7 @@ void AssassinationRogueStrategy::InitTriggers(std::vector& trigger new TriggerNode("medium aoe", NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), NULL))); triggers.push_back(new TriggerNode( - "tricks of the trade on main tank", + "low tank threat", NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), NULL))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/rogue/DpsRogueStrategy.cpp b/src/strategy/rogue/DpsRogueStrategy.cpp index 91cc80445..0fd0d0033 100644 --- a/src/strategy/rogue/DpsRogueStrategy.cpp +++ b/src/strategy/rogue/DpsRogueStrategy.cpp @@ -141,7 +141,7 @@ void DpsRogueStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( - "tricks of the trade on main tank", + "low tank threat", NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), nullptr))); } diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 2c69b72a2..7826616f2 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -14,6 +14,7 @@ #include "Playerbots.h" #include "SharedDefines.h" #include "TemporarySummon.h" +#include "ThreatMgr.h" #include "Timer.h" bool LowManaTrigger::IsActive() @@ -185,6 +186,22 @@ bool MyAttackerCountTrigger::IsActive() return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount; } +bool LowTankThreatTrigger::IsActive() +{ + Unit* mt = AI_VALUE(Unit*, "main tank"); + if (!mt) + return false; + + Unit* current_target = AI_VALUE(Unit*, "current target"); + if (!current_target) + return false; + + ThreatMgr& mgr = current_target->GetThreatMgr(); + float threat = mgr.GetThreat(bot); + float tankThreat = mgr.GetThreat(mt); + return tankThreat == 0.0f || threat > tankThreat * 0.5f; +} + bool AoeTrigger::IsActive() { Unit* current_target = AI_VALUE(Unit*, "current target"); diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 132e6b668..b72174474 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -10,6 +10,7 @@ #include "HealthTriggers.h" #include "RangeTriggers.h" +#include "Trigger.h" class PlayerbotAI; class Unit; @@ -248,6 +249,13 @@ class MediumThreatTrigger : public MyAttackerCountTrigger MediumThreatTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 2) {} }; +class LowTankThreatTrigger : public Trigger +{ +public: + LowTankThreatTrigger(PlayerbotAI* botAI) : Trigger(botAI, "low tank threat") {} + bool IsActive() override; +}; + class AoeTrigger : public AttackerCountTrigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index e1c4ec7de..860ee6824 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -102,6 +102,7 @@ class TriggerContext : public NamedObjectContext creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy; creators["medium threat"] = &TriggerContext::MediumThreat; + creators["low tank threat"] = &TriggerContext::low_tank_threat; creators["dead"] = &TriggerContext::Dead; creators["corpse near"] = &TriggerContext::corpse_near; @@ -321,6 +322,8 @@ class TriggerContext : public NamedObjectContext static Trigger* ComboPointsNotFull(PlayerbotAI* botAI) { return new ComboPointsNotFullTrigger(botAI); } static Trigger* ComboPointsNotFullAndHighEnergy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "combo points not full", "high energy available"); } static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } + static Trigger* low_tank_threat(PlayerbotAI* botAI) { return new LowTankThreatTrigger(botAI); } + // static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); } static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); } static Trigger* PartyMemberDead(PlayerbotAI* botAI) { return new PartyMemberDeadTrigger(botAI); } From 6ba4384184b3e2a0a86dd89aab41bf909d27b301 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 22:50:02 +0800 Subject: [PATCH 07/11] Init equipment and init available spell speed up --- conf/playerbots.conf.dist | 12 +- src/PlayerbotAI.cpp | 10 +- src/factory/PlayerbotFactory.cpp | 162 ++++++++++++++++++++------ src/factory/StatsWeightCalculator.cpp | 28 ++--- src/factory/StatsWeightCalculator.h | 2 +- 5 files changed, 150 insertions(+), 64 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 9f80241b0..f987fe79d 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -822,8 +822,8 @@ AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 AiPlayerbot.PremadeSpecName.3.1 = mm pve AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.1.60 = -005305111230013233125030151-5 -AiPlayerbot.PremadeSpecLink.3.1.80 = 502-005305131230013233135031351-5000002 +AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233125031051 +AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002 AiPlayerbot.PremadeSpecName.3.2 = surv pve AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732 AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311 @@ -977,7 +977,7 @@ AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55 AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501 AiPlayerbot.PremadeSpecName.9.1 = emo pve AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459 -AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530131201-55 +AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051 AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55 AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005 AiPlayerbot.PremadeSpecName.9.2 = destro pve @@ -1177,11 +1177,11 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2 # # -AiPlayerbot.RandomClassSpecProb.9.0 = 40 +AiPlayerbot.RandomClassSpecProb.9.0 = 45 AiPlayerbot.RandomClassSpecIndex.9.0 = 0 -AiPlayerbot.RandomClassSpecProb.9.1 = 40 +AiPlayerbot.RandomClassSpecProb.9.1 = 45 AiPlayerbot.RandomClassSpecIndex.9.1 = 1 -AiPlayerbot.RandomClassSpecProb.9.2 = 20 +AiPlayerbot.RandomClassSpecProb.9.2 = 10 AiPlayerbot.RandomClassSpecIndex.9.2 = 2 # diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index e80304bed..5df7402bb 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -5277,11 +5277,9 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, if (pItem->IsBindedNotWith(bot)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; - // Yunfan: skip it - // // check count of items (skip for auto move for same player from bank) - // InventoryResult res = bot->CanTakeMoreSimilarItems(pItem); - // if (res != EQUIP_ERR_OK) - // return res; + InventoryResult res = bot->CanTakeMoreSimilarItems(pItem); + if (res != EQUIP_ERR_OK) + return res; ScalingStatDistributionEntry const* ssd = pProto->ScalingStatDistribution @@ -5300,7 +5298,7 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, if (!bot->CanUseAttackType(bot->GetAttackBySlot(eslot))) return EQUIP_ERR_NOT_WHILE_DISARMED; - InventoryResult res = bot->CanUseItem(pItem, not_loading); + res = bot->CanUseItem(pItem, not_loading); if (res != EQUIP_ERR_OK) return res; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a40e28a42..a2bca1b9d 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -287,7 +287,7 @@ void PlayerbotFactory::Randomize(bool incremental) LOG_DEBUG("playerbots", "Initializing equipmemt..."); if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - InitEquipment(incremental); + InitEquipment(incremental, incremental ? false : true); } // bot->SaveToDB(false, false); if (pmo) @@ -951,7 +951,12 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa } else { - uint32 point = urand(1, 100); + uint32 pointSum = 0; + for (int i = 0; i < MAX_SPECNO; i++) + { + pointSum += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; + } + uint32 point = urand(1, pointSum); uint32 currentP = 0; int i; for (i = 0; i < MAX_SPECNO; i++) @@ -1356,7 +1361,8 @@ bool PlayerbotFactory::CanEquipItem(ItemTemplate const* proto) return true; uint32 requiredLevel = proto->RequiredLevel; - bool hasItem = bot->HasItemCount(proto->ItemId, 1, true); + // disable since bad performance + bool hasItem = bot->HasItemCount(proto->ItemId, 1, false); // bot->GetItemCount() // !requiredLevel -> it's a quest reward item if (!requiredLevel && hasItem) @@ -1573,10 +1579,13 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType)) { - if (itemId == 46978) - { // shaman earth ring totem + if (itemId == 46978) // shaman earth ring totem + { continue; } + uint32 skipProb = 25; + if (urand(1, 100) <= skipProb) + continue; // disable next expansion gear if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728) @@ -1603,8 +1612,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (proto->Quality != desiredQuality) continue; - if (!CanEquipItem(proto)) - continue; + // if (!CanEquipItem(proto)) + // continue; if (proto->Class == ITEM_CLASS_ARMOR && (slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS || @@ -1621,9 +1630,9 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) proto->Class != ITEM_CLASS_WEAPON) continue; - uint16 dest = 0; - if (CanEquipUnseenItem(slot, dest, itemId)) - items[slot].push_back(itemId); + // uint16 dest = 0; + // if (CanEquipUnseenItem(slot, dest, itemId)) + items[slot].push_back(itemId); } } if (items[slot].size() >= 25) @@ -1640,26 +1649,20 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) - { - uint32 skipProb = 25; - if (urand(1, 100) <= skipProb) - continue; - + { uint32 newItemId = ids[index]; - uint16 dest; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); - if (!CanEquipItem(proto)) - continue; - - if (!CanEquipUnseenItem(slot, dest, newItemId)) - continue; - float cur_score = calculator.CalculateItem(newItemId); if (cur_score > bestScoreForSlot) { + // delay heavy check to here + if (!CanEquipItem(proto)) + continue; + uint16 dest; + if (!CanEquipUnseenItem(slot, dest, newItemId)) + continue; bestScoreForSlot = cur_score; bestItemForSlot = newItemId; } @@ -1695,18 +1698,107 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - // bot->AutoUnequipOffhandIfNeed(); if (newItem) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); } } - // secondary init for better equips - if (!incremental && !second_chance) - InitEquipment(incremental, true); - + // Secondary init for better equips + /// @todo: clean up duplicate code + if (!incremental && second_chance) + { + for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) + { + if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) + continue; + + if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2)) + continue; + + if (level < 30 && slot == EQUIPMENT_SLOT_NECK) + continue; + + if (level < 25 && slot == EQUIPMENT_SLOT_HEAD) + continue; + + if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) + continue; + Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + + if (oldItem) + { + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + + std::vector& ids = items[slot]; + if (ids.empty()) + { + continue; + } + + float bestScoreForSlot = -1; + uint32 bestItemForSlot = 0; + for (int index = 0; index < ids.size(); index++) + { + uint32 newItemId = ids[index]; + + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); + + float cur_score = calculator.CalculateItem(newItemId); + if (cur_score > bestScoreForSlot) + { + // delay heavy check to here + if (!CanEquipItem(proto)) + continue; + uint16 dest; + if (!CanEquipUnseenItem(slot, dest, newItemId)) + continue; + bestScoreForSlot = cur_score; + bestItemForSlot = newItemId; + } + } + + if (bestItemForSlot == 0) + { + continue; + } + uint16 dest; + if (!CanEquipUnseenItem(slot, dest, bestItemForSlot)) + { + continue; + } + + if (incremental && oldItem) + { + float old_score = calculator.CalculateItem(oldItem->GetEntry()); + if (bestScoreForSlot < 1.2f * old_score) + continue; + } + + if (oldItem) + { + uint8 bagIndex = oldItem->GetBagSlot(); + uint8 slot = oldItem->GetSlot(); + uint8 dstBag = NULL_BAG; + + WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); + packet << bagIndex << slot << dstBag; + bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); + } + + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); + bot->AutoUnequipOffhandIfNeed(); + if (newItem) + { + newItem->AddToWorld(); + newItem->AddToUpdateQueueOf(bot); + } + } + } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) @@ -1876,11 +1968,11 @@ void PlayerbotFactory::InitBags(bool destroyOld) continue; } Item* newItem = bot->EquipNewItem(dest, newItemId, true); - if (newItem) - { - newItem->AddToWorld(); - newItem->AddToUpdateQueueOf(bot); - } + // if (newItem) + // { + // newItem->AddToWorld(); + // newItem->AddToUpdateQueueOf(bot); + // } } } @@ -2254,6 +2346,7 @@ void PlayerbotFactory::InitAvailableSpells() trainerIdCache.push_back(trainerId); } } + uint32 learnedCounter = 0; for (uint32 trainerId : trainerIdCache) { TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId); @@ -2297,7 +2390,6 @@ void PlayerbotFactory::InitAvailableSpells() { continue; } - if (tSpell->learnedSpell[0]) { bot->learnSpell(tSpell->learnedSpell[0], false); @@ -2307,6 +2399,8 @@ void PlayerbotFactory::InitAvailableSpells() botAI->CastSpell(tSpell->spell, bot); } } + if (++learnedCounter > 20) + break; } } diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index bf08e19e0..c3a328f02 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -32,6 +32,15 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) cls = player->getClass(); tab = AiFactory::GetPlayerSpecTab(player); + if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) + hitOverflowType_ = CollectorType::SPELL; + else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT) + hitOverflowType_ = CollectorType::SPELL; + else if (cls == CLASS_ROGUE) + hitOverflowType_ = CollectorType::SPELL; + else + hitOverflowType_ = type_; + enable_overflow_penalty_ = true; enable_item_set_bonus_ = true; enable_quality_blend_ = true; @@ -515,7 +524,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) float validPoints; // m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); // m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE); - if (GetHitOverflowType(player) == CollectorType::SPELL) + if (hitOverflowType_ == CollectorType::SPELL) { hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); @@ -525,7 +534,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else validPoints = 0; } - else if (GetHitOverflowType(player) == CollectorType::MELEE) + else if (hitOverflowType_ == CollectorType::MELEE) { hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); hit_current += player->GetRatingBonusValue(CR_HIT_MELEE); @@ -613,18 +622,3 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player) } } } - -CollectorType StatsWeightCalculator::GetHitOverflowType(Player* player) -{ - cls = player->getClass(); - tab = AiFactory::GetPlayerSpecTab(player); - if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY) - return CollectorType::SPELL; - if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT) - return CollectorType::SPELL; - if (cls == CLASS_ROGUE) - return CollectorType::SPELL; - - return type_; -} - \ No newline at end of file diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index f6f4bf7ef..d65966ff0 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -49,11 +49,11 @@ class StatsWeightCalculator void ApplyOverflowPenalty(Player* player); void ApplyWeightFinetune(Player* player); - CollectorType GetHitOverflowType(Player* player); private: Player* player_; CollectorType type_; + CollectorType hitOverflowType_; std::unique_ptr collector_; uint8 cls; int tab; From 0526948a29a8bbe4dc06f314ff025b8a8199891d Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Wed, 4 Sep 2024 22:58:29 +0800 Subject: [PATCH 08/11] TwoRoundsGearInit config option --- conf/playerbots.conf.dist | 3 + src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 1 + src/factory/PlayerbotFactory.cpp | 94 +++++++++++++------------------- 4 files changed, 44 insertions(+), 55 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index f987fe79d..4eabcb341 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -216,6 +216,9 @@ AiPlayerbot.AutoEquipUpgradeLoot = 1 # Default: 1.1 (Equip when the equipment score is 1.1 times higher than the current) AiPlayerbot.EquipUpgradeThreshold = 1.1 +# Two rounds of equipment initialization to create more suitable gear +AiPlayerbot.TwoRoundsGearInit = 0 + # # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index d285490a1..c97ab4f86 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -471,6 +471,7 @@ bool PlayerbotAIConfig::Initialize() autoPickReward = sConfigMgr->GetOption("AiPlayerbot.AutoPickReward", "yes"); autoEquipUpgradeLoot = sConfigMgr->GetOption("AiPlayerbot.AutoEquipUpgradeLoot", true); equipUpgradeThreshold = sConfigMgr->GetOption("AiPlayerbot.EquipUpgradeThreshold", 1.1f); + twoRoundsGearInit = sConfigMgr->GetOption("AiPlayerbot.TwoRoundsGearInit", false); syncQuestWithPlayer = sConfigMgr->GetOption("AiPlayerbot.SyncQuestWithPlayer", true); syncQuestForPlayer = sConfigMgr->GetOption("AiPlayerbot.SyncQuestForPlayer", false); autoTrainSpells = sConfigMgr->GetOption("AiPlayerbot.AutoTrainSpells", "yes"); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index f37571317..3aa47617e 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -271,6 +271,7 @@ class PlayerbotAIConfig std::string autoPickReward; bool autoEquipUpgradeLoot; float equipUpgradeThreshold; + bool twoRoundsGearInit; bool syncQuestWithPlayer; bool syncQuestForPlayer; std::string autoTrainSpells; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a2bca1b9d..a5ada5252 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -39,7 +39,8 @@ #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) -const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); +const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | + (1LL << 33) | (1LL << 24) | (1LL << 34); uint32 PlayerbotFactory::tradeSkills[] = {SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING, SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING, SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING, @@ -287,7 +288,7 @@ void PlayerbotFactory::Randomize(bool incremental) LOG_DEBUG("playerbots", "Initializing equipmemt..."); if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { - InitEquipment(incremental, incremental ? false : true); + InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit); } // bot->SaveToDB(false, false); if (pmo) @@ -626,16 +627,19 @@ void PlayerbotFactory::InitPetTalents() // prevent learn talent for different family (cheating) if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) continue; - bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203; + bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || + talentInfo->TalentID == 2203; if (diveClass && !diveTypePet) continue; - bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 || talentInfo->TalentID == 2109; + bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 || + talentInfo->TalentID == 2109; if (dashClass && diveTypePet) continue; spells[talentInfo->Row].push_back(talentInfo); } - std::vector> order = sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; + std::vector> order = + sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); if (order.empty()) @@ -652,8 +656,8 @@ void PlayerbotFactory::InitPetTalents() int attemptCount = 0; // keep learning for the last row while (!spells_row.empty() && - ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && - attemptCount++ < 10 && pet->GetFreeTalentPoints()) + ((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) && + attemptCount++ < 10 && pet->GetFreeTalentPoints()) { int index = urand(0, spells_row.size() - 1); TalentEntry const* talentInfo = spells_row[index]; @@ -683,7 +687,7 @@ void PlayerbotFactory::InitPetTalents() uint32 spec = pet_family->petTalentType; uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); while (startPoints > 1 && startPoints < 20 && - sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) + sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) { startPoints--; } @@ -706,7 +710,7 @@ void PlayerbotFactory::InitPetTalents() } if (talentInfo->DependsOn) { - bot->LearnPetTalent(pet->GetGUID(),talentInfo->DependsOn, + bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn, std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1)); } talentID = talentInfo->TalentID; @@ -946,9 +950,10 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa /// @todo: match current talent with template specTab = AiFactory::GetPlayerSpecTab(bot); /// @todo: fix cat druid hardcode - if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931)) + if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && + !bot->HasAura(16931)) specTab = 3; - } + } else { uint32 pointSum = 0; @@ -1579,7 +1584,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType)) { - if (itemId == 46978) // shaman earth ring totem + if (itemId == 46978) // shaman earth ring totem { continue; } @@ -1614,7 +1619,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) // if (!CanEquipItem(proto)) // continue; - + if (proto->Class == ITEM_CLASS_ARMOR && (slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS || slot == EQUIPMENT_SLOT_CHEST || slot == EQUIPMENT_SLOT_WAIST || @@ -1645,11 +1650,11 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - + float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) - { + { uint32 newItemId = ids[index]; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); @@ -1677,7 +1682,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - + if (incremental && oldItem) { float old_score = calculator.CalculateItem(oldItem->GetEntry()); @@ -1695,7 +1700,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) packet << bagIndex << slot << dstBag; bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); } - + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); if (newItem) @@ -1706,7 +1711,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } // Secondary init for better equips /// @todo: clean up duplicate code - if (!incremental && second_chance) + if (second_chance) { for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { @@ -1725,25 +1730,17 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2)) continue; - Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - - if (oldItem) - { + if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); - } - - oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); std::vector& ids = items[slot]; if (ids.empty()) - { continue; - } - + float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; for (int index = 0; index < ids.size(); index++) - { + { uint32 newItemId = ids[index]; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId); @@ -1771,25 +1768,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - - if (incremental && oldItem) - { - float old_score = calculator.CalculateItem(oldItem->GetEntry()); - if (bestScoreForSlot < 1.2f * old_score) - continue; - } - - if (oldItem) - { - uint8 bagIndex = oldItem->GetBagSlot(); - uint8 slot = oldItem->GetSlot(); - uint8 dstBag = NULL_BAG; - - WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3); - packet << bagIndex << slot << dstBag; - bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); - } - Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); if (newItem) @@ -2810,7 +2788,10 @@ void PlayerbotFactory::InitAmmo() bot->SetAmmo(entry); } -uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); } +uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) +{ + return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); +} void PlayerbotFactory::InitMounts() { @@ -2978,7 +2959,8 @@ std::vector PlayerbotFactory::GetCurrentGemsCount() Item* pItem2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i); if (pItem2 && !pItem2->IsBroken() && pItem2->HasSocket()) { - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT; ++enchant_slot) + for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT; + ++enchant_slot) { if (enchant_slot == BONUS_ENCHANTMENT_SLOT) continue; @@ -3486,10 +3468,10 @@ void PlayerbotFactory::InitInventoryEquip() if (proto->Class == ITEM_CLASS_WEAPON && !CanEquipWeapon(proto)) continue; - + if (proto->Quality != desiredQuality) continue; - + if (!CanEquipItem(proto)) continue; @@ -3936,7 +3918,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) { continue; } - if (enchant->requiredSkill && (!bot->HasSkill(enchant->requiredSkill) || (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue))) + if (enchant->requiredSkill && + (!bot->HasSkill(enchant->requiredSkill) || + (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue))) { continue; } @@ -3973,7 +3957,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) int32 enchantIdChosen = -1; int32 colorChosen; float bestGemScore = -1; - for (uint32 &enchantGem : availableGems) + for (uint32& enchantGem : availableGems) { ItemTemplate const* gemTemplate = sObjectMgr->GetItemTemplate(enchantGem); if (!gemTemplate) @@ -3983,7 +3967,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld) if (!gemProperties) continue; - if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket + if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket continue; uint32 enchant_id = gemProperties->spellitemenchantement; From bbc95c953a65f3256a130f2d3b0b6422273e5dc0 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 5 Sep 2024 01:10:42 +0800 Subject: [PATCH 09/11] Fix DeleteRandomBotAccounts crash --- src/PlayerbotAIConfig.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index c97ab4f86..60b20a95c 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -496,13 +496,13 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); RandomPlayerbotFactory::CreateRandomBots(); - if (sPlayerbotAIConfig->addClassCommand) - sRandomPlayerbotMgr->PrepareAddclassCache(); - if (World::IsStopped()) { return true; } + if (sPlayerbotAIConfig->addClassCommand) + sRandomPlayerbotMgr->PrepareAddclassCache(); + sRandomItemMgr->Init(); sRandomItemMgr->InitAfterAhBot(); sPlayerbotTextMgr->LoadBotTexts(); From 369f3373edbb238c197e37e930baab0b7c2dfdab Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 5 Sep 2024 15:04:46 +0800 Subject: [PATCH 10/11] Fix unique equip init and bots login --- src/RandomPlayerbotMgr.cpp | 5 ++- src/factory/PlayerbotFactory.cpp | 49 ++++++++++++++--------- src/strategy/actions/ListSpellsAction.cpp | 12 ++++-- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 21bd012a3..0f06e0a50 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -370,7 +370,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) break; } - if (loginBots) + if (loginBots && botLoading.empty()) { loginBots += updateBots; loginBots = std::min(loginBots, maxNewBots); @@ -1041,6 +1041,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) SetEventValue(bot, "login", 0, 0); + if (!player->IsInWorld()) + return false; + if (player->GetGroup() || player->HasUnitState(UNIT_STATE_IN_FLIGHT)) return false; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index a5ada5252..47db56b68 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -235,6 +235,7 @@ void PlayerbotFactory::Randomize(bool incremental) pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1"); LOG_DEBUG("playerbots", "Initializing spells (step 1)..."); // bot->LearnDefaultSkills(); + bot->LearnDefaultSkills(); InitClassSpells(); InitAvailableSpells(); if (pmo) @@ -425,7 +426,10 @@ void PlayerbotFactory::Refresh() InitFood(); InitReagents(); // InitPotions(); - InitTalentsTree(true, true, true); + if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + { + InitTalentsTree(true, true, true); + } InitPet(); InitPetTalents(); InitClassSpells(); @@ -885,7 +889,7 @@ void PlayerbotFactory::ClearEverything() ClearSpells(); ClearInventory(); ResetQuests(); - bot->SaveToDB(false, false); + // bot->SaveToDB(false, false); } void PlayerbotFactory::ClearSpells() @@ -1616,7 +1620,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (proto->Quality != desiredQuality) continue; - + // delay heavy check // if (!CanEquipItem(proto)) // continue; @@ -1634,7 +1638,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE && proto->Class != ITEM_CLASS_WEAPON) continue; - + // delay heavy check // uint16 dest = 0; // if (CanEquipUnseenItem(slot, dest, itemId)) items[slot].push_back(itemId); @@ -1690,6 +1694,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) continue; } + if (oldItem) { uint8 bagIndex = oldItem->GetBagSlot(); @@ -1701,13 +1706,18 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); } + oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + // fail to store in bag + if (oldItem) + continue; + Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - if (newItem) - { - newItem->AddToWorld(); - newItem->AddToUpdateQueueOf(bot); - } + // if (newItem) + // { + // newItem->AddToWorld(); + // newItem->AddToUpdateQueueOf(bot); + // } } // Secondary init for better equips /// @todo: clean up duplicate code @@ -1770,11 +1780,11 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - if (newItem) - { - newItem->AddToWorld(); - newItem->AddToUpdateQueueOf(bot); - } + // if (newItem) + // { + // newItem->AddToWorld(); + // newItem->AddToUpdateQueueOf(bot); + // } } } } @@ -2305,8 +2315,6 @@ void PlayerbotFactory::SetRandomSkill(uint16 id) void PlayerbotFactory::InitAvailableSpells() { - bot->LearnDefaultSkills(); - if (trainerIdCache.empty()) { CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates(); @@ -2324,7 +2332,8 @@ void PlayerbotFactory::InitAvailableSpells() trainerIdCache.push_back(trainerId); } } - uint32 learnedCounter = 0; + // uint32 learnedCounter = 0; + // uint32 oktest = 0; for (uint32 trainerId : trainerIdCache) { TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId); @@ -2368,6 +2377,7 @@ void PlayerbotFactory::InitAvailableSpells() { continue; } + // oktest++; if (tSpell->learnedSpell[0]) { bot->learnSpell(tSpell->learnedSpell[0], false); @@ -2377,8 +2387,9 @@ void PlayerbotFactory::InitAvailableSpells() botAI->CastSpell(tSpell->spell, bot); } } - if (++learnedCounter > 20) - break; + // LOG_INFO("playerbots", "C: {}, ok: {}", ++learnedCounter, oktest); + // if (++learnedCounter > 20) + // break; } } diff --git a/src/strategy/actions/ListSpellsAction.cpp b/src/strategy/actions/ListSpellsAction.cpp index 75d25ac96..80277ea89 100644 --- a/src/strategy/actions/ListSpellsAction.cpp +++ b/src/strategy/actions/ListSpellsAction.cpp @@ -11,13 +11,13 @@ std::map ListSpellsAction::skillSpells; std::set ListSpellsAction::vendorItems; -bool CompareSpells(std::pair& s1, std::pair& s2) +bool CompareSpells(const std::pair& s1, const std::pair& s2) { SpellInfo const* si1 = sSpellMgr->GetSpellInfo(s1.first); SpellInfo const* si2 = sSpellMgr->GetSpellInfo(s2.first); if (!si1 || !si2) { - LOG_ERROR("playerbots", "SpellInfo missing."); + LOG_ERROR("playerbots", "SpellInfo missing. {} {}", s1.first, s2.first); return false; } uint32 p1 = si1->SchoolMask * 20000; @@ -54,7 +54,7 @@ bool CompareSpells(std::pair& s1, std::pairSpellName[0], si1->SpellName[1]) > 0; + return strcmp(si1->SpellName[0], si2->SpellName[0]) > 0; } return p1 > p2; @@ -273,7 +273,11 @@ std::vector> ListSpellsAction::GetSpellList(std:: if (out.str().empty()) continue; - + + if (itr->first == 0) + { + LOG_ERROR("playerbots", "?! {}", itr->first); + } spells.push_back(std::pair(itr->first, out.str())); alreadySeenList += spellInfo->SpellName[0]; alreadySeenList += ","; From 73f699fe8983a65e08d0cee165950003f8a340a4 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Thu, 5 Sep 2024 15:57:32 +0800 Subject: [PATCH 11/11] Fix initself crash --- src/PlayerbotAI.cpp | 1 + src/factory/PlayerbotFactory.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 5df7402bb..0504b6b7f 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -5257,6 +5257,7 @@ bool PlayerbotAI::EqualLowercaseName(std::string s1, std::string s2) return true; } +// A custom CanEquipItem (remove AutoUnequipOffhand in FindEquipSlot to prevent unequip on `item usage` calculation) InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading) const { dest = 0; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 47db56b68..a6dc88b74 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -2060,7 +2060,8 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true)) { - InventoryResult result = botAI->CanEquipItem(slot, dest, pItem, true, true); + InventoryResult result = botAI ? botAI->CanEquipItem(slot, dest, pItem, true, true) + : bot->CanEquipItem(slot, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; return result == EQUIP_ERR_OK; @@ -2382,10 +2383,10 @@ void PlayerbotFactory::InitAvailableSpells() { bot->learnSpell(tSpell->learnedSpell[0], false); } - else - { - botAI->CastSpell(tSpell->spell, bot); - } + // else + // { + // botAI->CastSpell(tSpell->spell, bot); + // } } // LOG_INFO("playerbots", "C: {}, ok: {}", ++learnedCounter, oktest); // if (++learnedCounter > 20)