diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 675c322b..88b5213c 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -342,6 +342,7 @@ AiPlayerbot.MediumHealth = 65 AiPlayerbot.AlmostFullHealth = 85 AiPlayerbot.LowMana = 15 AiPlayerbot.MediumMana = 40 +AiPlayerbot.HighMana = 65 # # diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 78c79d49..e80304be 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -4326,7 +4326,7 @@ void PlayerbotAI::_fillGearScoreData(Player* player, Item* item, std::vectorInventoryType; - uint32 level = mixed ? proto->ItemLevel * (1 + proto->Quality) : proto->ItemLevel; + uint32 level = mixed ? proto->ItemLevel * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(proto->Quality)) : proto->ItemLevel; switch (type) { @@ -5837,4 +5837,33 @@ void PlayerbotAI::PetFollow() charmInfo->RemoveStayPosition(); charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(); +} + +float PlayerbotAI::GetItemScoreMultiplier(ItemQualities quality) +{ + switch (quality) + { + // each quality increase 1.1x + case ITEM_QUALITY_POOR: + return 1.0f; + break; + case ITEM_QUALITY_NORMAL: + return 1.1f; + break; + case ITEM_QUALITY_UNCOMMON: + return 1.21f; + break; + case ITEM_QUALITY_RARE: + return 1.331f; + break; + case ITEM_QUALITY_EPIC: + return 1.4641f; + break; + case ITEM_QUALITY_LEGENDARY: + return 1.61051f; + break; + default: + break; + } + return 1.0f; } \ No newline at end of file diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 73bc61a0..c2a94625 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -569,6 +569,7 @@ public: std::set GetAllCurrentQuestIds(); std::set GetCurrentIncompleteQuestIds(); void PetFollow(); + static float GetItemScoreMultiplier(ItemQualities quality); private: static void _fillGearScoreData(Player* player, Item* item, std::vector* gearScore, uint32& twoHandScore, diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index a87135f1..ffc41581 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -94,6 +94,7 @@ bool PlayerbotAIConfig::Initialize() almostFullHealth = sConfigMgr->GetOption("AiPlayerbot.AlmostFullHealth", 85); lowMana = sConfigMgr->GetOption("AiPlayerbot.LowMana", 15); mediumMana = sConfigMgr->GetOption("AiPlayerbot.MediumMana", 40); + highMana = sConfigMgr->GetOption("AiPlayerbot.HighMana", 65); autoSaveMana = sConfigMgr->GetOption("AiPlayerbot.AutoSaveMana", true); saveManaThreshold = sConfigMgr->GetOption("AiPlayerbot.SaveManaThreshold", 60); autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", true); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 70536935..57f3ce80 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -63,7 +63,7 @@ public: tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance, targetPosRecalcDistance, farDistance, healDistance, aggroDistance; uint32 criticalHealth, lowHealth, mediumHealth, almostFullHealth; - uint32 lowMana, mediumMana; + uint32 lowMana, mediumMana, highMana; bool autoSaveMana; uint32 saveManaThreshold; bool autoAvoidAoe; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index e03447cb..a5116fdf 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -1217,7 +1217,7 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) { case CLASS_PRIEST: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE) + proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) return false; break; case CLASS_MAGE: @@ -1231,15 +1231,12 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) + proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; break; case CLASS_PALADIN: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) - return false; - break; case CLASS_DEATH_KNIGHT: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && @@ -1251,28 +1248,30 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && - proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) + proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; break; case CLASS_DRUID: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && - proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM) return false; break; case CLASS_HUNTER: - if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && - proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && + if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && + proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW) + proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM) return false; break; case CLASS_ROGUE: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && - proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && - proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) + proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN && + proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE) return false; break; } @@ -2646,7 +2645,7 @@ void PlayerbotFactory::InitAmmo() bot->SetAmmo(entry); } -uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * (quality + 1); } +uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); } void PlayerbotFactory::InitMounts() { diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index b0055272..ef95fb9b 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -12,6 +12,7 @@ #include "ItemTemplate.h" #include "ObjectMgr.h" #include "PlayerbotAI.h" +#include "PlayerbotFactory.h" #include "SharedDefines.h" #include "StatsCollector.h" #include "Unit.h" @@ -49,12 +50,12 @@ void StatsWeightCalculator::Reset() float StatsWeightCalculator::CalculateItem(uint32 itemId) { ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId); - + if (!proto) return 0.0f; Reset(); - + collector_->CollectItemStats(proto); GenerateWeights(player_); @@ -67,25 +68,25 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId) if (enable_item_set_bonus_) CalculateItemSetBonus(player_, proto); - + CalculateSocketBonus(player_, proto); if (enable_quality_blend_) // Blend with item quality and level - weight_ *= (proto->Quality + 1) * proto->ItemLevel; - + weight_ *= PlayerbotFactory::CalcMixedGearScore(proto->ItemLevel, proto->Quality); + return weight_; } float StatsWeightCalculator::CalculateEnchant(uint32 enchantId) { SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); - + if (!enchant) return 0.0f; Reset(); - + collector_->CollectEnchantStats(enchant); GenerateWeights(player_); @@ -121,7 +122,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_CRIT] += 1.5f; stats_weights_[STATS_TYPE_HASTE] += 1.4f; stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f; - } + } else if (cls == CLASS_HUNTER && tab == HUNTER_TAB_MARKSMANSHIP) { stats_weights_[STATS_TYPE_AGILITY] += 2.2f; @@ -168,7 +169,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } - else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury + else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY) // fury { stats_weights_[STATS_TYPE_AGILITY] += 1.8f; stats_weights_[STATS_TYPE_STRENGTH] += 2.6f; @@ -180,7 +181,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.5f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } - else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) // arm + else if (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) // arm { stats_weights_[STATS_TYPE_AGILITY] += 1.6f; stats_weights_[STATS_TYPE_STRENGTH] += 2.3f; @@ -192,7 +193,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 1.4f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } - else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk + else if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) // frost dk { stats_weights_[STATS_TYPE_AGILITY] += 1.8f; stats_weights_[STATS_TYPE_STRENGTH] += 2.6f; @@ -216,14 +217,14 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 1.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; } - else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution + else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution { stats_weights_[STATS_TYPE_AGILITY] += 1.1f; 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.8f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.5f; stats_weights_[STATS_TYPE_HIT] += 1.9f; stats_weights_[STATS_TYPE_CRIT] += 1.2f; stats_weights_[STATS_TYPE_HASTE] += 1.3f; @@ -244,8 +245,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f; } - else if (cls == CLASS_WARLOCK || - cls == CLASS_MAGE || + else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW) || // shadow (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ELEMENTAL) || // element (cls == CLASS_DRUID && tab == DRUID_TAB_BALANCE)) // balance @@ -272,7 +272,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HASTE] += 1.0f; stats_weights_[STATS_TYPE_RANGED_DPS] += 1.0f; } - else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)) + else if ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || + (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)) { stats_weights_[STATS_TYPE_AGILITY] += 2.0f; stats_weights_[STATS_TYPE_STRENGTH] += 1.0f; @@ -361,33 +362,33 @@ void StatsWeightCalculator::CalculateItemSetBonus(Player* player, ItemTemplate c if (player->ItemSetEff[i]) { ItemSetEffect* eff = player->ItemSetEff[i]; - + uint32 setId = eff->setid; if (itemSet != setId) continue; - - const ItemSetEntry *setEntry = sItemSetStore.LookupEntry(setId); + + const ItemSetEntry* setEntry = sItemSetStore.LookupEntry(setId); if (!setEntry) continue; - + uint32 itemCount = eff->item_count; uint32 max_items = 0; for (size_t j = 0; j < MAX_ITEM_SET_SPELLS; j++) max_items = std::max(max_items, setEntry->items_to_triggerspell[j]); if (itemCount < max_items) { - multiplier += 0.1f * itemCount; // 10% bonus for each item already equipped + multiplier += 0.1f * itemCount; // 10% bonus for each item already equipped } else { - multiplier = 1.0f; // All item set effect has been triggerred + multiplier = 1.0f; // All item set effect has been triggerred } break; } } if (i == player->ItemSetEff.size()) - multiplier = 1.05f; // this is the first item in the item set + multiplier = 1.05f; // this is the first item in the item set weight_ *= multiplier; } @@ -395,17 +396,18 @@ void StatsWeightCalculator::CalculateItemSetBonus(Player* player, ItemTemplate c void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate const* proto) { uint32 socketNum = 0; - for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot) + for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; + ++enchant_slot) { uint8 socketColor = proto->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color; - - if (!socketColor) // no socket slot + + if (!socketColor) // no socket slot continue; socketNum++; } - float multiplier = 1.0f + socketNum * 0.03f; // 3% bonus for socket + float multiplier = 1.0f + socketNum * 0.03f; // 3% bonus for socket weight_ *= multiplier; } @@ -469,13 +471,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 0.1; } - if (cls == CLASS_ROGUE && player_->HasAura(13964) - && (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) + if (cls == CLASS_ROGUE && player_->HasAura(13964) && + (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) { weight_ *= 1.1; } - if (cls == CLASS_WARRIOR && player_->HasAura(12785) - && (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2)) + if (cls == CLASS_WARRIOR && player_->HasAura(12785) && + (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2)) { weight_ *= 1.1; } @@ -523,7 +525,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) else if (hit_current >= hit_overflow * 0.8) stats_weights_[STATS_TYPE_HIT] /= 1.5; } - + { if (type_ == CollectorType::MELEE) { @@ -536,7 +538,7 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] /= 1.5; } } - + { if (type_ == CollectorType::MELEE) { diff --git a/src/strategy/actions/SayAction.cpp b/src/strategy/actions/SayAction.cpp index 81a7387b..5b12fabf 100644 --- a/src/strategy/actions/SayAction.cpp +++ b/src/strategy/actions/SayAction.cpp @@ -245,6 +245,8 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL); break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 25)); @@ -309,6 +311,8 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC GET_PLAYERBOT_AI(bot)->SayToGuild(BOT_TEXT2("suggest_toxic_links", placeholders)); break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 60)); @@ -404,6 +408,8 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha } break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 60)); } @@ -490,6 +496,8 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); break; } + default: + break; } GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("last said", "chat")->Set(time(0) + urand(5, 25)); } diff --git a/src/strategy/druid/CatDpsDruidStrategy.cpp b/src/strategy/druid/CatDpsDruidStrategy.cpp index 0ae00e28..e640cebd 100644 --- a/src/strategy/druid/CatDpsDruidStrategy.cpp +++ b/src/strategy/druid/CatDpsDruidStrategy.cpp @@ -122,8 +122,7 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg NextAction** CatDpsDruidStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), - new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr); + return NextAction::array(0, new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr); } void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) @@ -131,9 +130,17 @@ void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) FeralDruidStrategy::InitTriggers(triggers); // Default priority - triggers.push_back(new TriggerNode("high energy available", + triggers.push_back(new TriggerNode("almost full energy available", + NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr))); + triggers.push_back(new TriggerNode("combo points not full", + NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), nullptr))); + triggers.push_back(new TriggerNode("almost full energy available", NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr))); - triggers.push_back(new TriggerNode("high energy available", + triggers.push_back(new TriggerNode("combo points not full and high energy", + NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr))); + triggers.push_back(new TriggerNode("almost full energy available", + NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr))); + triggers.push_back(new TriggerNode("combo points not full and high energy", NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("faerie fire (feral)", diff --git a/src/strategy/druid/DruidTriggers.cpp b/src/strategy/druid/DruidTriggers.cpp index d839f973..a083ade1 100644 --- a/src/strategy/druid/DruidTriggers.cpp +++ b/src/strategy/druid/DruidTriggers.cpp @@ -22,8 +22,6 @@ bool ThornsOnPartyTrigger::IsActive() return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("thorns", GetTarget()); } -bool MoonfireTrigger::IsActive() { return DebuffTrigger::IsActive() && !GetTarget()->HasUnitState(UNIT_STATE_ROOT); } - bool EntanglingRootsKiteTrigger::IsActive() { return DebuffTrigger::IsActive() && AI_VALUE(uint8, "attacker count") < 3 && !GetTarget()->GetPower(POWER_MANA); diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 3b8fdce7..50020835 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -75,8 +75,6 @@ class MoonfireTrigger : public DebuffTrigger { public: MoonfireTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "moonfire", 1, true) {} - - bool IsActive() override; }; class FaerieFireTrigger : public DebuffTrigger @@ -252,12 +250,12 @@ public: return false; Aura* roar = botAI->GetAura("savage roar", bot); - bool roarCheck = !roar || roar->GetDuration() > 8000; + bool roarCheck = !roar || roar->GetDuration() > 10000; if (!roarCheck) return false; Aura* rip = botAI->GetAura("rip", target, true); - bool ripCheck = !rip || rip->GetDuration() > 8000; + bool ripCheck = !rip || rip->GetDuration() > 10000; if (!ripCheck) return false; diff --git a/src/strategy/generic/UseFoodStrategy.cpp b/src/strategy/generic/UseFoodStrategy.cpp index c0c29b51..04841e4a 100644 --- a/src/strategy/generic/UseFoodStrategy.cpp +++ b/src/strategy/generic/UseFoodStrategy.cpp @@ -5,12 +5,19 @@ #include "UseFoodStrategy.h" +#include "PlayerbotAIConfig.h" #include "Playerbots.h" void UseFoodStrategy::InitTriggers(std::vector& triggers) { Strategy::InitTriggers(triggers); + if (sPlayerbotAIConfig->freeFood) + triggers.push_back(new TriggerNode("medium health", NextAction::array(0, new NextAction("food", 3.0f), nullptr))); + else + triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr))); - triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("food", 3.0f), nullptr))); - triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr))); + if (sPlayerbotAIConfig->freeFood) + triggers.push_back(new TriggerNode("high mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr))); + else + triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("drink", 3.0f), nullptr))); } diff --git a/src/strategy/paladin/DpsPaladinStrategy.cpp b/src/strategy/paladin/DpsPaladinStrategy.cpp index ecbe3af7..723fffd3 100644 --- a/src/strategy/paladin/DpsPaladinStrategy.cpp +++ b/src/strategy/paladin/DpsPaladinStrategy.cpp @@ -82,9 +82,11 @@ DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrat NextAction** DpsPaladinStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("crusader strike", ACTION_DEFAULT + 0.4f), - new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.3f), - new NextAction("divine storm", ACTION_DEFAULT + 0.2f), + return NextAction::array(0, + new NextAction("crusader strike", ACTION_DEFAULT + 0.6f), + new NextAction("hammer of wrath", 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); } @@ -93,12 +95,17 @@ void DpsPaladinStrategy::InitTriggers(std::vector& triggers) { GenericPaladinStrategy::InitTriggers(triggers); + triggers.push_back( + new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( new TriggerNode("seal", NextAction::array(0, new NextAction("seal of corruption", ACTION_HIGH), NULL))); // triggers.push_back(new TriggerNode("seal", NextAction::array(0, new NextAction("seal of command", 90.0f), // nullptr))); triggers.push_back( new TriggerNode("low mana", NextAction::array(0, new NextAction("seal of wisdom", ACTION_HIGH + 5), nullptr))); + + triggers.push_back(new TriggerNode( + "avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr))); // triggers.push_back(new TriggerNode("sanctity aura", NextAction::array(0, new NextAction("sanctity aura", 90.0f), // nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("repentance or // shield", ACTION_CRITICAL_HEAL + 3), new NextAction("holy light", ACTION_CRITICAL_HEAL + 2), nullptr))); @@ -112,11 +119,11 @@ void DpsPaladinStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("repentance", NextAction::array(0, new NextAction("repentance", // ACTION_INTERRUPT + 2), nullptr))); triggers.push_back(new TriggerNode( - "medium aoe", NextAction::array(0, new NextAction("consecration", ACTION_HIGH + 3), nullptr))); - triggers.push_back( - new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_HIGH + 2), nullptr))); - triggers.push_back(new TriggerNode("target critical health", - NextAction::array(0, new NextAction("hammer of wrath", ACTION_HIGH), nullptr))); + "medium aoe", NextAction::array(0, + new NextAction("divine storm", ACTION_HIGH + 4), + new NextAction("consecration", ACTION_HIGH + 3), nullptr))); + // triggers.push_back(new TriggerNode("target critical health", + // NextAction::array(0, new NextAction("hammer of wrath", ACTION_HIGH), nullptr))); // triggers.push_back(new TriggerNode( // "not facing target", // NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), NULL))); diff --git a/src/strategy/paladin/GenericPaladinStrategy.cpp b/src/strategy/paladin/GenericPaladinStrategy.cpp index 676a6159..d25a7518 100644 --- a/src/strategy/paladin/GenericPaladinStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinStrategy.cpp @@ -37,7 +37,7 @@ void GenericPaladinStrategy::InitTriggers(std::vector& triggers) "protect party member", NextAction::array(0, new NextAction("blessing of protection on party", ACTION_EMERGENCY + 2), nullptr))); triggers.push_back( - new TriggerNode("medium mana", NextAction::array(0, new NextAction("divine plea", ACTION_HIGH), NULL))); + new TriggerNode("high mana", NextAction::array(0, new NextAction("divine plea", ACTION_HIGH), NULL))); } void PaladinCureStrategy::InitTriggers(std::vector& triggers) @@ -61,8 +61,7 @@ void PaladinCureStrategy::InitTriggers(std::vector& triggers) void PaladinBoostStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode( - "avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr))); + // triggers.push_back(new TriggerNode("divine favor", NextAction::array(0, new NextAction("divine favor", // ACTION_HIGH + 1), nullptr))); } diff --git a/src/strategy/paladin/HealPaladinStrategy.cpp b/src/strategy/paladin/HealPaladinStrategy.cpp index dd1edf23..70c461ce 100644 --- a/src/strategy/paladin/HealPaladinStrategy.cpp +++ b/src/strategy/paladin/HealPaladinStrategy.cpp @@ -50,7 +50,9 @@ void HealPaladinStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("medium group heal occasion", - NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5), nullptr))); + NextAction::array(0, new NextAction("divine sacrifice", ACTION_CRITICAL_HEAL + 5), + new NextAction("avenging wrath", ACTION_HIGH + 4), + nullptr))); triggers.push_back( new TriggerNode("party member critical health", diff --git a/src/strategy/paladin/TankPaladinStrategy.cpp b/src/strategy/paladin/TankPaladinStrategy.cpp index 649fe21e..553d2a7d 100644 --- a/src/strategy/paladin/TankPaladinStrategy.cpp +++ b/src/strategy/paladin/TankPaladinStrategy.cpp @@ -98,6 +98,8 @@ void TankPaladinStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("holy shield", ACTION_HIGH + 4), nullptr))); // triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of sanctuary", // ACTION_HIGH + 9), nullptr))); + triggers.push_back(new TriggerNode( + "avenging wrath", NextAction::array(0, new NextAction("avenging wrath", ACTION_HIGH + 2), nullptr))); triggers.push_back( new TriggerNode("target critical health", NextAction::array(0, new NextAction("hammer of wrath", ACTION_CRITICAL_HEAL), nullptr))); diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index c11c523d..746abf86 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -51,8 +51,8 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("enemy out of spell", NextAction::array(0, new NextAction("reach spell", // ACTION_NORMAL + 9), nullptr))); triggers.push_back(new TriggerNode("shaman weapon", NextAction::array(0, new // NextAction("flametongue weapon", 23.0f), nullptr))); - triggers.push_back(new TriggerNode( - "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr))); + // triggers.push_back(new TriggerNode( + // "enough mana", NextAction::array(0, new NextAction("chain lightning", ACTION_DEFAULT + 0.1f), nullptr))); triggers.push_back(new TriggerNode("main hand weapon no imbue", NextAction::array(0, new NextAction("flametongue weapon", 22.0f), nullptr))); diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index 32253186..2c69b72a 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -10,6 +10,7 @@ #include "BattlegroundWS.h" #include "CreatureAI.h" #include "ObjectGuid.h" +#include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "SharedDefines.h" #include "TemporarySummon.h" @@ -64,7 +65,7 @@ bool PetAttackTrigger::IsActive() bool HighManaTrigger::IsActive() { - return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < 65; + return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->highMana; } bool AlmostFullManaTrigger::IsActive() @@ -83,6 +84,8 @@ bool EnergyAvailable::IsActive() { return AI_VALUE2(uint8, "energy", "self targe bool ComboPointsAvailableTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") >= amount; } +bool ComboPointsNotFullTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") < amount; } + bool TargetWithComboPointsLowerHealTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 674b514d..132e6b66 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -126,6 +126,17 @@ private: float lifeTime; }; +class ComboPointsNotFullTrigger : public StatAvailable +{ +public: + ComboPointsNotFullTrigger(PlayerbotAI* botAI, int32 amount = 5, std::string const name = "combo points not full") + : StatAvailable(botAI, amount, name) + { + } + + bool IsActive() override; +}; + class LoseAggroTrigger : public Trigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index acad38f0..e1c4ec7d 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -64,6 +64,7 @@ public: creators["light energy available"] = &TriggerContext::LightEnergyAvailable; creators["medium energy available"] = &TriggerContext::MediumEnergyAvailable; creators["high energy available"] = &TriggerContext::HighEnergyAvailable; + creators["almost full energy available"] = &TriggerContext::AlmostFullEnergyAvailable; creators["loot available"] = &TriggerContext::LootAvailable; creators["no attackers"] = &TriggerContext::NoAttackers; @@ -97,6 +98,8 @@ public: creators["combo points available"] = &TriggerContext::ComboPointsAvailable; creators["combo points 3 available"] = &TriggerContext::ComboPoints3Available; creators["target with combo points almost dead"] = &TriggerContext::target_with_combo_points_almost_dead; + creators["combo points not full"] = &TriggerContext::ComboPointsNotFull; + creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy; creators["medium threat"] = &TriggerContext::MediumThreat; @@ -280,6 +283,7 @@ private: static Trigger* LightEnergyAvailable(PlayerbotAI* botAI) { return new LightEnergyAvailableTrigger(botAI); } static Trigger* MediumEnergyAvailable(PlayerbotAI* botAI) { return new MediumEnergyAvailableTrigger(botAI); } static Trigger* HighEnergyAvailable(PlayerbotAI* botAI) { return new HighEnergyAvailableTrigger(botAI); } + static Trigger* AlmostFullEnergyAvailable(PlayerbotAI* botAI) { return new EnergyAvailable(botAI, 90); } static Trigger* LootAvailable(PlayerbotAI* botAI) { return new LootAvailableTrigger(botAI); } static Trigger* NoAttackers(PlayerbotAI* botAI) { return new NoAttackersTrigger(botAI); } static Trigger* TankAssist(PlayerbotAI* botAI) { return new TankAssistTrigger(botAI); } @@ -314,6 +318,8 @@ private: { return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f); } + 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* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); } static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); } diff --git a/src/strategy/values/EstimatedLifetimeValue.cpp b/src/strategy/values/EstimatedLifetimeValue.cpp index 4352934f..b69861c4 100644 --- a/src/strategy/values/EstimatedLifetimeValue.cpp +++ b/src/strategy/values/EstimatedLifetimeValue.cpp @@ -3,6 +3,7 @@ #include "AiFactory.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotFactory.h" #include "Playerbots.h" #include "SharedDefines.h" @@ -26,13 +27,13 @@ float EstimatedGroupDpsValue::Calculate() { float totalDps; - std::vector groupPlayer={bot}; + std::vector groupPlayer = {bot}; if (Group* group = bot->GetGroup()) { for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (member == bot) // calculated + if (member == bot) // calculated continue; if (!member || !member->IsInWorld()) @@ -40,7 +41,7 @@ float EstimatedGroupDpsValue::Calculate() if (member->GetMapId() != bot->GetMapId()) continue; - + if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance) continue; @@ -59,16 +60,25 @@ float EstimatedGroupDpsValue::Calculate() float basicDps = GetBasicDps(player->GetLevel()); float basicGs = GetBasicGs(player->GetLevel()); uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(player, true, false, 12); - - float gap = (float)mixedGearScore / basicGs - 1; - float gs_modifier = gap * 3 + 1; + float gs_modifier = (float)mixedGearScore / basicGs; + // bonus for wotlk epic gear + if (mixedGearScore >= 300) + { + gs_modifier *= 1 + (mixedGearScore - 300) * 0.01; + } if (gs_modifier < 0.75) gs_modifier = 0.75; if (gs_modifier > 4) gs_modifier = 4; totalDps += basicDps * roleMultiplier * gs_modifier; } - + // Group buff bonus + if (groupPlayer.size() >= 25) + totalDps *= 1.2; + else if (groupPlayer.size() >= 10) + totalDps *= 1.1; + else if (groupPlayer.size() >= 5) + totalDps *= 1.05; return totalDps; } @@ -98,11 +108,11 @@ float EstimatedGroupDpsValue::GetBasicDps(uint32 level) } else if (level <= 70) { - basic_dps = 450 + (level - 60) * 40; + basic_dps = 550 + (level - 60) * 65; } else { - basic_dps = 750 + (level - 70) * 175; + basic_dps = 1200 + (level - 70) * 200; } return basic_dps; } @@ -113,23 +123,23 @@ float EstimatedGroupDpsValue::GetBasicGs(uint32 level) if (level <= 8) { - basic_gs = (level + 5) * 2; + basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_NORMAL); } else if (level <= 15) { - basic_gs = (level + 5) * 3; + basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_UNCOMMON); } else if (level <= 60) { - basic_gs = (level + 5) * 4; + basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_RARE); } else if (level <= 70) { - basic_gs = (85 + (level - 60) * 3) * 4; + basic_gs = PlayerbotFactory::CalcMixedGearScore(85 + (level - 60) * 3, ITEM_QUALITY_RARE); } else { - basic_gs = (155 + (level - 70) * 4) * 4; + basic_gs = PlayerbotFactory::CalcMixedGearScore(155 + (level - 70) * 4, ITEM_QUALITY_RARE); } return basic_gs; } \ No newline at end of file