diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index c6c0dfe1..675c322b 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -916,7 +916,7 @@ AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776 AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351 AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031 AiPlayerbot.PremadeSpecName.7.1 = enh pve -AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,41540 +AiPlayerbot.PremadeSpecGlyph.7.1 = 41542,43385,41539,43386,44923,45771 AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051 AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051 AiPlayerbot.PremadeSpecName.7.2 = resto pve @@ -996,7 +996,7 @@ AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031501531050013051 AiPlayerbot.PremadeSpecLink.11.2.80 = 05320001--230033312031512531153313051 AiPlayerbot.PremadeSpecName.11.3 = cat pve AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604 -AiPlayerbot.PremadeSpecLink.11.3.60 = -553202032322010052100030310501 +AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501 AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012 # @@ -1183,11 +1183,12 @@ AiPlayerbot.RandomClassSpecIndex.9.2 = 2 AiPlayerbot.RandomClassSpecProb.11.0 = 20 AiPlayerbot.RandomClassSpecIndex.11.0 = 0 -AiPlayerbot.RandomClassSpecProb.11.1 = 40 +AiPlayerbot.RandomClassSpecProb.11.1 = 25 AiPlayerbot.RandomClassSpecIndex.11.1 = 1 -AiPlayerbot.RandomClassSpecProb.11.2 = 40 +AiPlayerbot.RandomClassSpecProb.11.2 = 35 AiPlayerbot.RandomClassSpecIndex.11.2 = 2 - +AiPlayerbot.RandomClassSpecProb.11.3 = 20 +AiPlayerbot.RandomClassSpecIndex.11.3 = 3 # # # diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 05f2daec..d107b0dc 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -71,7 +71,6 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) max = tabs[i]; } } - return tab; } else @@ -481,11 +480,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2)) engine->addStrategiesNoInit("caster", "caster aoe", nullptr); - if (player->getClass() == CLASS_DRUID && tab == 1) - engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr); + // if (player->getClass() == CLASS_DRUID && tab == 1) + // engine->addStrategiesNoInit(/*"behind",*/ "dps", nullptr); - if (player->getClass() == CLASS_ROGUE) - engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr); + // if (player->getClass() == CLASS_ROGUE) + // engine->addStrategiesNoInit(/*"behind",*/ "stealth", nullptr); } } diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 848ee41e..78c79d49 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -2001,6 +2001,10 @@ bool PlayerbotAI::IsDps(Player* player) { return true; } + if (tab == DRUID_TAB_FERAL && !IsTank(player)) + { + return true; + } break; case CLASS_SHAMAN: if (tab != SHAMAN_TAB_RESTORATION) @@ -2795,8 +2799,8 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, } uint32 CastingTime = !spellInfo->IsChanneled() ? spellInfo->CalcCastTime(bot) : spellInfo->GetDuration(); - bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT; - if ((CastingTime || interruptOnMove) && bot->isMoving()) + // bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT; + if ((CastingTime || spellInfo->IsAutoRepeatRangedSpell()) && bot->isMoving()) { if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { @@ -5177,6 +5181,32 @@ uint32 PlayerbotAI::GetBuffedCount(Player* player, std::string const spellname) return bcount; } +int32 PlayerbotAI::GetNearGroupMemberCount(float dis) +{ + int count = 1; // yourself + if (Group* group = bot->GetGroup()) + { + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (member == bot) // calculated + continue; + + if (!member || !member->IsInWorld()) + continue; + + if (member->GetMapId() != bot->GetMapId()) + continue; + + if (member->GetExactDist(bot) > dis) + continue; + + count++; + } + } + return count; +} + bool PlayerbotAI::CanMove() { // do not allow if not vehicle driver diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 61242629..73bc61a0 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -469,6 +469,7 @@ public: void ImbueItem(Item* item); void EnchantItemT(uint32 spellid, uint8 slot); uint32 GetBuffedCount(Player* player, std::string const spellname); + int32 GetNearGroupMemberCount(float dis = sPlayerbotAIConfig->sightDistance); virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 1267591e..a87135f1 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -13,6 +13,7 @@ #include "Playerbots.h" #include "RandomItemMgr.h" #include "RandomPlayerbotFactory.h" +#include "RandomPlayerbotMgr.h" #include "Talentspec.h" template @@ -329,15 +330,22 @@ bool PlayerbotAIConfig::Initialize() parsedSpecLinkOrder[cls][spec][level] = ParseTempTalentsOrder(cls, premadeSpecLink[cls][spec][level]); } } - for (uint32 spec = 0; spec < 3; ++spec) + for (uint32 spec = 0; spec < MAX_SPECNO; ++spec) { std::ostringstream os; os << "AiPlayerbot.RandomClassSpecProb." << cls << "." << spec; - randomClassSpecProb[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), 33); + uint32 def; + if (spec <= 1) + def = 33; + else if (spec == 2) + def = 34; + else + def = 0; + randomClassSpecProb[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), def, false); os.str(""); os.clear(); os << "AiPlayerbot.RandomClassSpecIndex." << cls << "." << spec; - randomClassSpecIndex[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), spec + 1); + randomClassSpecIndex[cls][spec] = sConfigMgr->GetOption(os.str().c_str(), spec, false); } } @@ -475,6 +483,9 @@ bool PlayerbotAIConfig::Initialize() selfBotLevel = sConfigMgr->GetOption("AiPlayerbot.SelfBotLevel", 1); RandomPlayerbotFactory::CreateRandomBots(); + if (sPlayerbotAIConfig->addClassCommand) + sRandomPlayerbotMgr->PrepareAddclassCache(); + if (World::IsStopped()) { return true; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 10f2bfe5..21bd012a 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -162,8 +162,6 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) { sPlayerbotCommandServer->Start(); PrepareTeleportCache(); - if (sPlayerbotAIConfig->addClassCommand) - PrepareAddclassCache(); } BattlegroundData.clear(); diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index 2105477c..5c35aefa 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -169,6 +169,7 @@ public: void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; } static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; } + void PrepareAddclassCache(); std::map> addclassCache; protected: void OnBotLoginInternal(Player* const bot) override; @@ -193,7 +194,6 @@ private: void RandomTeleport(Player* bot, std::vector& locs, bool hearth = false); uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ); void PrepareTeleportCache(); - void PrepareAddclassCache(); typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*); std::vector players; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 511d92a0..e03447cb 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -878,14 +878,31 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa uint32 total_tabs = tabs[0] + tabs[1] + tabs[2]; if (increment && total_tabs != 0) { + /// @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)) + specTab = 3; + } else { - uint32 point = urand(0, 100); - uint32 p1 = sPlayerbotAIConfig->randomClassSpecProb[cls][0]; - uint32 p2 = p1 + sPlayerbotAIConfig->randomClassSpecProb[cls][1]; - specTab = point < p1 ? 0 : (point < p2 ? 1 : 2); + uint32 point = urand(1, 100); + uint32 currentP = 0; + int i; + for (i = 0; i < MAX_SPECNO; i++) + { + currentP += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; + if (point <= currentP) + { + specTab = i; + break; + } + } + if (i == MAX_SPECNO) + { + specTab = 0; + LOG_ERROR("playerbots", "Fail to select spec num for bot {}! Set to 0.", bot->GetName()); + } } if (reset) { @@ -896,12 +913,13 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa { InitTalentsByTemplate(specTab); } - else - { - InitTalents(specTab); - if (bot->GetFreeTalentPoints()) - InitTalents((specTab + 1) % 3); - } + // always use template now + // else + // { + // InitTalents(specTab); + // if (bot->GetFreeTalentPoints()) + // InitTalents((specTab + 1) % 3); + // } bot->SendTalentsInfoData(false); } @@ -1433,7 +1451,7 @@ void Shuffle(std::vector& items) void PlayerbotFactory::InitEquipment(bool incremental) { std::unordered_map> items; - int tab = AiFactory::GetPlayerSpecTab(bot); + // int tab = AiFactory::GetPlayerSpecTab(bot); uint32 blevel = bot->GetLevel(); int32 delta = 2; @@ -3062,6 +3080,9 @@ void PlayerbotFactory::InitGlyphs(bool increment) uint8 cls = bot->getClass(); uint8 tab = AiFactory::GetPlayerSpecTab(bot); + /// @todo: fix cat druid hardcode + if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931)) + tab = 3; std::list glyphs; ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator i = itemTemplates->begin(); i != itemTemplates->end(); ++i) diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index 543de321..b0055272 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -132,7 +132,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HASTE] += 1.8f; stats_weights_[STATS_TYPE_RANGED_DPS] += 5.0f; } - else if ((cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player))) + else if (cls == CLASS_ROGUE && tab == ROGUE_TAB_COMBAT) { stats_weights_[STATS_TYPE_AGILITY] += 1.8f; stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; @@ -143,7 +143,19 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_HASTE] += 1.4f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; - } + } + else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player)) + { + stats_weights_[STATS_TYPE_AGILITY] += 2.4f; + stats_weights_[STATS_TYPE_STRENGTH] += 2.3f; + stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; + stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 2.1f; + stats_weights_[STATS_TYPE_HIT] += 1.9f; + stats_weights_[STATS_TYPE_CRIT] += 1.8f; + stats_weights_[STATS_TYPE_HASTE] += 1.4f; + stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 5.0f; + } else if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)) { stats_weights_[STATS_TYPE_AGILITY] += 1.7f; @@ -224,13 +236,13 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_STRENGTH] += 1.1f; stats_weights_[STATS_TYPE_INTELLECT] += 0.5f; stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f; - stats_weights_[STATS_TYPE_SPELL_POWER] += 1.0f; + stats_weights_[STATS_TYPE_SPELL_POWER] += 0.9f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 1.2f; stats_weights_[STATS_TYPE_HIT] += 1.7f; stats_weights_[STATS_TYPE_CRIT] += 1.4f; stats_weights_[STATS_TYPE_HASTE] += 1.8f; stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; - stats_weights_[STATS_TYPE_MELEE_DPS] += 5.2f; + stats_weights_[STATS_TYPE_MELEE_DPS] += 8.5f; } else if (cls == CLASS_WARLOCK || cls == CLASS_MAGE || diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index 76c1bc54..d56c5e6f 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -172,7 +172,7 @@ public: creators["rtsc"] = &ChatActionContext::rtsc; creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut; creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut; - creators["tell expected dps"] = &ChatActionContext::tell_expected_dps; + creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps; creators["join"] = &ChatActionContext::join; creators["calc"] = &ChatActionContext::calc; } @@ -271,7 +271,7 @@ private: static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); } static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); } static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); } - static Action* tell_expected_dps(PlayerbotAI* ai) { return new TellExpectedDpsAction(ai); } + static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); } static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); } static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); } }; diff --git a/src/strategy/actions/GenericSpellActions.cpp b/src/strategy/actions/GenericSpellActions.cpp index 05938358..2425a223 100644 --- a/src/strategy/actions/GenericSpellActions.cpp +++ b/src/strategy/actions/GenericSpellActions.cpp @@ -364,5 +364,5 @@ bool CastDebuffSpellAction::isUseful() return false; } return CastAuraSpellAction::isUseful() && - (target->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime; + (target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime; } \ No newline at end of file diff --git a/src/strategy/actions/TellLosAction.cpp b/src/strategy/actions/TellLosAction.cpp index 159fb845..03dd3569 100644 --- a/src/strategy/actions/TellLosAction.cpp +++ b/src/strategy/actions/TellLosAction.cpp @@ -130,10 +130,10 @@ bool TellAuraAction::Execute(Event event) return true; } -bool TellExpectedDpsAction::Execute(Event event) +bool TellEstimatedDpsAction::Execute(Event event) { - float dps = AI_VALUE(float, "expected group dps"); - botAI->TellMaster("Expected Group DPS: " + std::to_string(dps)); + float dps = AI_VALUE(float, "estimated group dps"); + botAI->TellMaster("Estimated Group DPS: " + std::to_string(dps)); return true; } diff --git a/src/strategy/actions/TellLosAction.h b/src/strategy/actions/TellLosAction.h index 1adc56d0..988564cf 100644 --- a/src/strategy/actions/TellLosAction.h +++ b/src/strategy/actions/TellLosAction.h @@ -30,10 +30,10 @@ public: virtual bool Execute(Event event); }; -class TellExpectedDpsAction : public Action +class TellEstimatedDpsAction : public Action { public: - TellExpectedDpsAction(PlayerbotAI* ai) : Action(ai, "tell expected dps") {} + TellEstimatedDpsAction(PlayerbotAI* ai) : Action(ai, "tell estimated dps") {} virtual bool Execute(Event event); }; diff --git a/src/strategy/druid/CatDpsDruidStrategy.cpp b/src/strategy/druid/CatDpsDruidStrategy.cpp index 80120330..0ae00e28 100644 --- a/src/strategy/druid/CatDpsDruidStrategy.cpp +++ b/src/strategy/druid/CatDpsDruidStrategy.cpp @@ -70,7 +70,7 @@ private: { return new ActionNode("mangle (cat)", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("claw"), nullptr), + /*A*/ nullptr, /*C*/ nullptr); } @@ -122,38 +122,51 @@ CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrateg NextAction** CatDpsDruidStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.1f), nullptr); + return NextAction::array(0, new NextAction("shred", ACTION_DEFAULT + 0.4f), + new NextAction("tiger's fury", ACTION_DEFAULT + 0.1f), nullptr); } void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) { FeralDruidStrategy::InitTriggers(triggers); + // Default priority + triggers.push_back(new TriggerNode("high energy available", + NextAction::array(0, new NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f), nullptr))); + triggers.push_back(new TriggerNode("high energy available", + NextAction::array(0, new NextAction("claw", ACTION_DEFAULT + 0.2f), nullptr))); triggers.push_back( - new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 2), nullptr))); + new TriggerNode("faerie fire (feral)", + NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f), nullptr))); + + // Main spell triggers.push_back( - new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_NORMAL + 5), nullptr))); + new TriggerNode("cat form", NextAction::array(0, new NextAction("cat form", ACTION_HIGH + 8), nullptr))); + triggers.push_back( + new TriggerNode("savage roar", NextAction::array(0, new NextAction("savage roar", ACTION_HIGH + 7), nullptr))); + triggers.push_back(new TriggerNode("combo points available", + NextAction::array(0, new NextAction("rip", ACTION_HIGH + 6), nullptr))); triggers.push_back(new TriggerNode( - "combo points available", NextAction::array(0, new NextAction("ferocious bite", ACTION_NORMAL + 9), nullptr))); + "ferocious bite time", NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 5), nullptr))); triggers.push_back( - new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_EMERGENCY + 1), nullptr))); + new TriggerNode("target with combo points almost dead", + NextAction::array(0, new NextAction("ferocious bite", ACTION_HIGH + 4), nullptr))); + triggers.push_back(new TriggerNode("mangle (cat)", + NextAction::array(0, new NextAction("mangle (cat)", ACTION_HIGH + 3), nullptr))); + triggers.push_back(new TriggerNode("rake", NextAction::array(0, new NextAction("rake", ACTION_HIGH + 2), nullptr))); + triggers.push_back( + new TriggerNode("medium threat", NextAction::array(0, new NextAction("cower", ACTION_HIGH + 1), nullptr))); + + // AOE + triggers.push_back( + new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( - "faerie fire (feral)", NextAction::array(0, new NextAction("faerie fire (feral)", ACTION_HIGH), nullptr))); + "light aoe", NextAction::array(0, new NextAction("rake on attacker", ACTION_HIGH + 2), nullptr))); + // Reach target triggers.push_back(new TriggerNode( - "tiger's fury", NextAction::array(0, new NextAction("tiger's fury", ACTION_EMERGENCY + 1), nullptr))); + "enemy out of melee", NextAction::array(0, new NextAction("feral charge - cat", ACTION_HIGH + 9), nullptr))); triggers.push_back( - new TriggerNode("behind target", NextAction::array(0, new NextAction("pounce", ACTION_HIGH + 1), nullptr))); - // triggers.push_back(new TriggerNode("player has no flag", NextAction::array(0, new NextAction("prowl", - // ACTION_HIGH), nullptr))); triggers.push_back(new TriggerNode("enemy out of melee", NextAction::array(0, new - // NextAction("prowl", ACTION_INTERRUPT + 1), nullptr))); - triggers.push_back( - new TriggerNode("player has flag", NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY), nullptr))); - triggers.push_back(new TriggerNode("enemy flagcarrier near", - NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY), nullptr))); + new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("dash", ACTION_HIGH + 8), nullptr))); } -void CatAoeDruidStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode("medium aoe", NextAction::array(0, new NextAction("swipe (cat)", ACTION_HIGH + 2), nullptr))); -} +void CatAoeDruidStrategy::InitTriggers(std::vector& triggers) {} diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index b1181ae2..04652487 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -83,6 +83,8 @@ public: creators["moonfire"] = &DruidTriggerFactoryInternal::moonfire; creators["nature's grasp"] = &DruidTriggerFactoryInternal::natures_grasp; creators["tiger's fury"] = &DruidTriggerFactoryInternal::tigers_fury; + creators["berserk"] = &DruidTriggerFactoryInternal::berserk; + creators["savage roar"] = &DruidTriggerFactoryInternal::savage_roar; creators["rake"] = &DruidTriggerFactoryInternal::rake; creators["mark of the wild"] = &DruidTriggerFactoryInternal::mark_of_the_wild; creators["mark of the wild on party"] = &DruidTriggerFactoryInternal::mark_of_the_wild_on_party; @@ -101,6 +103,8 @@ public: creators["party member remove curse"] = &DruidTriggerFactoryInternal::party_member_remove_curse; creators["eclipse (solar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_solar_cooldown; creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown; + creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat; + creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time; } private: @@ -117,6 +121,8 @@ private: static Trigger* faerie_fire(PlayerbotAI* botAI) { return new FaerieFireTrigger(botAI); } static Trigger* natures_grasp(PlayerbotAI* botAI) { return new NaturesGraspTrigger(botAI); } static Trigger* tigers_fury(PlayerbotAI* botAI) { return new TigersFuryTrigger(botAI); } + static Trigger* berserk(PlayerbotAI* botAI) { return new BerserkTrigger(botAI); } + static Trigger* savage_roar(PlayerbotAI* botAI) { return new SavageRoarTrigger(botAI); } static Trigger* rake(PlayerbotAI* botAI) { return new RakeTrigger(botAI); } static Trigger* mark_of_the_wild(PlayerbotAI* botAI) { return new MarkOfTheWildTrigger(botAI); } static Trigger* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new MarkOfTheWildOnPartyTrigger(botAI); } @@ -133,6 +139,8 @@ private: static Trigger* party_member_remove_curse(PlayerbotAI* ai) { return new DruidPartyMemberRemoveCurseTrigger(ai); } static Trigger* eclipse_solar_cooldown(PlayerbotAI* ai) { return new EclipseSolarCooldownTrigger(ai); } static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); } + static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); } + static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); } }; class DruidAiObjectContextInternal : public NamedObjectContext @@ -174,6 +182,7 @@ public: creators["mangle (cat)"] = &DruidAiObjectContextInternal::mangle_cat; creators["swipe (cat)"] = &DruidAiObjectContextInternal::swipe_cat; creators["rake"] = &DruidAiObjectContextInternal::rake; + creators["rake on attacker"] = &DruidAiObjectContextInternal::rake_on_attacker; creators["ferocious bite"] = &DruidAiObjectContextInternal::ferocious_bite; creators["rip"] = &DruidAiObjectContextInternal::rip; creators["cower"] = &DruidAiObjectContextInternal::cower; @@ -188,6 +197,7 @@ public: creators["abolish poison on party"] = &DruidAiObjectContextInternal::abolish_poison_on_party; creators["berserk"] = &DruidAiObjectContextInternal::berserk; creators["tiger's fury"] = &DruidAiObjectContextInternal::tigers_fury; + creators["savage roar"] = &DruidAiObjectContextInternal::savage_roar; creators["mark of the wild"] = &DruidAiObjectContextInternal::mark_of_the_wild; creators["mark of the wild on party"] = &DruidAiObjectContextInternal::mark_of_the_wild_on_party; creators["regrowth"] = &DruidAiObjectContextInternal::regrowth; @@ -257,6 +267,7 @@ private: static Action* mangle_cat(PlayerbotAI* botAI) { return new CastMangleCatAction(botAI); } static Action* swipe_cat(PlayerbotAI* botAI) { return new CastSwipeCatAction(botAI); } static Action* rake(PlayerbotAI* botAI) { return new CastRakeAction(botAI); } + static Action* rake_on_attacker(PlayerbotAI* botAI) { return new CastRakeOnMeleeAttackersAction(botAI); } static Action* ferocious_bite(PlayerbotAI* botAI) { return new CastFerociousBiteAction(botAI); } static Action* rip(PlayerbotAI* botAI) { return new CastRipAction(botAI); } static Action* cower(PlayerbotAI* botAI) { return new CastCowerAction(botAI); } @@ -271,6 +282,7 @@ private: static Action* abolish_poison_on_party(PlayerbotAI* botAI) { return new CastAbolishPoisonOnPartyAction(botAI); } static Action* berserk(PlayerbotAI* botAI) { return new CastBerserkAction(botAI); } static Action* tigers_fury(PlayerbotAI* botAI) { return new CastTigersFuryAction(botAI); } + static Action* savage_roar(PlayerbotAI* botAI) { return new CastSavageRoarAction(botAI); } static Action* mark_of_the_wild(PlayerbotAI* botAI) { return new CastMarkOfTheWildAction(botAI); } static Action* mark_of_the_wild_on_party(PlayerbotAI* botAI) { return new CastMarkOfTheWildOnPartyAction(botAI); } static Action* regrowth(PlayerbotAI* botAI) { return new CastRegrowthAction(botAI); } diff --git a/src/strategy/druid/DruidCatActions.cpp b/src/strategy/druid/DruidCatActions.cpp new file mode 100644 index 00000000..03571a8e --- /dev/null +++ b/src/strategy/druid/DruidCatActions.cpp @@ -0,0 +1 @@ +#include "DruidCatActions.h" diff --git a/src/strategy/druid/DruidCatActions.h b/src/strategy/druid/DruidCatActions.h index 579bdd00..cf4c6c35 100644 --- a/src/strategy/druid/DruidCatActions.h +++ b/src/strategy/druid/DruidCatActions.h @@ -35,10 +35,23 @@ public: CastTigersFuryAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "tiger's fury") {} }; +class CastSavageRoarAction : public CastBuffSpellAction +{ +public: + CastSavageRoarAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "savage roar") {} + std::string const GetTargetName() override { return "current target"; } +}; + class CastRakeAction : public CastDebuffSpellAction { public: - CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake") {} + CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake", true, 6.0f) {} +}; + +class CastRakeOnMeleeAttackersAction : public CastDebuffSpellOnMeleeAttackerAction +{ +public: + CastRakeOnMeleeAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnMeleeAttackerAction(botAI, "rake", true, 6.0f) {} }; class CastClawAction : public CastMeleeSpellAction @@ -65,10 +78,10 @@ public: CastFerociousBiteAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ferocious bite") {} }; -class CastRipAction : public CastMeleeSpellAction +class CastRipAction : public CastMeleeDebuffSpellAction { public: - CastRipAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "rip") {} + CastRipAction(PlayerbotAI* botAI) : CastMeleeDebuffSpellAction(botAI, "rip", true, 12.0f) {} }; class CastShredAction : public CastMeleeSpellAction diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 392b487e..3b8fdce7 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -9,6 +9,8 @@ #include "CureTriggers.h" #include "GenericTriggers.h" #include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" #include "SharedDefines.h" class PlayerbotAI; @@ -95,10 +97,22 @@ public: BashInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "bash") {} }; -class TigersFuryTrigger : public BoostTrigger +class TigersFuryTrigger : public BuffTrigger { public: - TigersFuryTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "tiger's fury") {} + TigersFuryTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "tiger's fury") {} +}; + +class BerserkTrigger : public BoostTrigger +{ +public: + BerserkTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "berserk") {} +}; + +class SavageRoarTrigger : public BuffTrigger +{ +public: + SavageRoarTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "savage roar") {} }; class NaturesGraspTrigger : public BoostTrigger @@ -212,4 +226,43 @@ public: bool IsActive() override { return bot->HasSpellCooldown(48518); } }; +class MangleCatTrigger : public DebuffTrigger +{ +public: + MangleCatTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "mangle (cat)", 1, false, 0.0f) {} + bool IsActive() override + { + return DebuffTrigger::IsActive() && !botAI->HasAura("mangle (bear)", GetTarget(), false, false, -1, true) + && !botAI->HasAura("trauma", GetTarget(), false, false, -1, true); + } +}; + +class FerociousBiteTimeTrigger : public Trigger +{ +public: + FerociousBiteTimeTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite time") {} + bool IsActive() override + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + + uint8 cp = AI_VALUE2(uint8, "combo", "current target"); + if (cp < 5) + return false; + + Aura* roar = botAI->GetAura("savage roar", bot); + bool roarCheck = !roar || roar->GetDuration() > 8000; + if (!roarCheck) + return false; + + Aura* rip = botAI->GetAura("rip", target, true); + bool ripCheck = !rip || rip->GetDuration() > 8000; + if (!ripCheck) + return false; + + return true; + } +}; + #endif diff --git a/src/strategy/druid/FeralDruidStrategy.cpp b/src/strategy/druid/FeralDruidStrategy.cpp index ed4b030b..91e315b2 100644 --- a/src/strategy/druid/FeralDruidStrategy.cpp +++ b/src/strategy/druid/FeralDruidStrategy.cpp @@ -112,4 +112,6 @@ void FeralDruidStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr))); triggers.push_back(new TriggerNode("enemy flagcarrier near", NextAction::array(0, new NextAction("dash", ACTION_EMERGENCY + 2), nullptr))); + triggers.push_back( + new TriggerNode("berserk", NextAction::array(0, new NextAction("berserk", ACTION_HIGH + 6), nullptr))); } diff --git a/src/strategy/generic/CastTimeStrategy.cpp b/src/strategy/generic/CastTimeStrategy.cpp index 7d1b4545..483e2b8c 100644 --- a/src/strategy/generic/CastTimeStrategy.cpp +++ b/src/strategy/generic/CastTimeStrategy.cpp @@ -47,7 +47,7 @@ float CastTimeMultiplier::GetValue(Action* action) return 1.0f; } - if (castTime > (1000 * target->GetHealth() / AI_VALUE(float, "expected group dps"))) + if (castTime > (1000 * target->GetHealth() / AI_VALUE(float, "estimated group dps"))) { return 0.1f; } diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 78c5aef7..58db48f5 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -89,7 +89,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back( new TriggerNode("bwl", NextAction::array(0, new NextAction("bwl chat shortcut", relevance), NULL))); triggers.push_back( - new TriggerNode("dps", NextAction::array(0, new NextAction("tell expected dps", relevance), NULL))); + new TriggerNode("dps", NextAction::array(0, new NextAction("tell estimated dps", relevance), NULL))); triggers.push_back( new TriggerNode("disperse", NextAction::array(0, new NextAction("disperse set", relevance), NULL))); } diff --git a/src/strategy/rogue/RogueAiObjectContext.cpp b/src/strategy/rogue/RogueAiObjectContext.cpp index 355c8953..351c7c12 100644 --- a/src/strategy/rogue/RogueAiObjectContext.cpp +++ b/src/strategy/rogue/RogueAiObjectContext.cpp @@ -77,8 +77,6 @@ public: creators["tricks of the trade on main tank"] = &RogueTriggerFactoryInternal::tricks_of_the_trade_on_main_tank; creators["adrenaline rush"] = &RogueTriggerFactoryInternal::adrenaline_rush; creators["blade fury"] = &RogueTriggerFactoryInternal::blade_fury; - creators["target with combo points almost dead"] = - &RogueTriggerFactoryInternal::target_with_combo_points_almost_dead; } private: @@ -102,10 +100,6 @@ private: { return new TricksOfTheTradeOnMainTankTrigger(ai); } - static Trigger* target_with_combo_points_almost_dead(PlayerbotAI* ai) - { - return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f); - } }; class RogueAiObjectContextInternal : public NamedObjectContext diff --git a/src/strategy/rogue/RogueTriggers.cpp b/src/strategy/rogue/RogueTriggers.cpp index 31e1a390..d4b89443 100644 --- a/src/strategy/rogue/RogueTriggers.cpp +++ b/src/strategy/rogue/RogueTriggers.cpp @@ -124,14 +124,3 @@ bool OffHandWeaponNoEnchantTrigger::IsActive() return false; return true; } - -bool TargetWithComboPointsLowerHealTrigger::IsActive() -{ - Unit* target = AI_VALUE(Unit*, "current target"); - if (!target || !target->IsAlive() || !target->IsInWorld()) - { - return false; - } - return ComboPointsAvailableTrigger::IsActive() && - (target->GetHealth() / AI_VALUE(float, "expected group dps")) <= lifeTime; -} \ No newline at end of file diff --git a/src/strategy/rogue/RogueTriggers.h b/src/strategy/rogue/RogueTriggers.h index 467bd9fc..48ce772e 100644 --- a/src/strategy/rogue/RogueTriggers.h +++ b/src/strategy/rogue/RogueTriggers.h @@ -127,17 +127,6 @@ public: TricksOfTheTradeOnMainTankTrigger(PlayerbotAI* ai) : BuffOnMainTankTrigger(ai, "tricks of the trade", true) {} }; -class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger -{ -public: - TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f) - : ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime) - { - } - bool IsActive() override; -private: - float lifeTime; -}; #endif diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index 88e3ca06..c11c523d 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -66,7 +66,9 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) // shock", 21.0f), nullptr))); triggers.push_back( new TriggerNode("no fire totem", NextAction::array(0, new NextAction("totem of wrath", 15.0f), NULL))); - + triggers.push_back(new TriggerNode("fire elemental totem", + NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr))); + triggers.push_back( new TriggerNode("medium mana", NextAction::array(0, new NextAction("thunderstorm", ACTION_HIGH + 1), nullptr))); diff --git a/src/strategy/shaman/HealShamanStrategy.cpp b/src/strategy/shaman/HealShamanStrategy.cpp index 7fda4651..ff305559 100644 --- a/src/strategy/shaman/HealShamanStrategy.cpp +++ b/src/strategy/shaman/HealShamanStrategy.cpp @@ -104,6 +104,11 @@ void HealShamanStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("medium mana", NextAction::array(0, new NextAction("mana tide totem", ACTION_HIGH + 5), NULL))); + triggers.push_back( + new TriggerNode("no fire totem", NextAction::array(0, new NextAction("flametongue totem", 7.0f), + new NextAction("searing totem", 6.0f), nullptr))); + triggers.push_back(new TriggerNode("fire elemental totem", + NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr))); triggers.push_back(new TriggerNode( "party member to heal out of spell range", NextAction::array(0, new NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 1), nullptr))); diff --git a/src/strategy/shaman/MeleeShamanStrategy.cpp b/src/strategy/shaman/MeleeShamanStrategy.cpp index 0764f8a6..d38c163a 100644 --- a/src/strategy/shaman/MeleeShamanStrategy.cpp +++ b/src/strategy/shaman/MeleeShamanStrategy.cpp @@ -72,11 +72,8 @@ void MeleeShamanStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("flame shock", NextAction::array(0, new NextAction("flame shock", 20.0f), nullptr))); triggers.push_back( - new TriggerNode("maelstrom weapon", NextAction::array(0, new NextAction("lightning bolt", 25.0f), nullptr))); - triggers.push_back(new TriggerNode("not facing target", - NextAction::array(0, new NextAction("set facing", ACTION_NORMAL + 7), nullptr))); - // triggers.push_back(new TriggerNode("enemy too close for melee", NextAction::array(0, new NextAction("move out of - // enemy contact", ACTION_NORMAL + 8), nullptr))); + new TriggerNode("maelstrom weapon 4", NextAction::array(0, new NextAction("lightning bolt", 25.0f), nullptr))); + triggers.push_back(new TriggerNode( "medium aoe", NextAction::array(0, new NextAction("strength of earth totem", ACTION_LIGHT_HEAL), nullptr))); triggers.push_back(new TriggerNode( @@ -86,9 +83,8 @@ void MeleeShamanStrategy::InitTriggers(std::vector& triggers) "no fire totem", NextAction::array(0, new NextAction("reach melee", 23.0f), new NextAction("magma totem", 22.0f), nullptr))); - triggers.push_back(new TriggerNode("fire elemental totem", - NextAction::array(0, new NextAction("reach melee", 33.0f), - new NextAction("fire elemental totem", 32.0f), nullptr))); + triggers.push_back(new TriggerNode( + "fire elemental totem", NextAction::array(0, new NextAction("fire elemental totem melee", 32.0f), nullptr))); triggers.push_back( new TriggerNode("no air totem", NextAction::array(0, new NextAction("windfury totem", 20.0f), nullptr))); diff --git a/src/strategy/shaman/ShamanActions.cpp b/src/strategy/shaman/ShamanActions.cpp index 7394c320..9ce29240 100644 --- a/src/strategy/shaman/ShamanActions.cpp +++ b/src/strategy/shaman/ShamanActions.cpp @@ -17,7 +17,7 @@ bool CastTotemAction::isUseful() { return false; } - float dps = AI_VALUE(float, "expected group dps"); + float dps = AI_VALUE(float, "estimated group dps"); if (target->GetHealth() / dps < needLifeTime) { return false; @@ -51,11 +51,14 @@ bool CastMagmaTotemAction::isUseful() { } bool CastFireNovaAction::isUseful() { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; Creature* fireTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[1]); if (!fireTotem) return false; - if (bot->GetDistance(fireTotem) > 8.0f) + if (target->GetDistance(fireTotem) > 8.0f) return false; return CastMeleeSpellAction::isUseful(); diff --git a/src/strategy/shaman/ShamanActions.h b/src/strategy/shaman/ShamanActions.h index 2568cd11..c4f6ce38 100644 --- a/src/strategy/shaman/ShamanActions.h +++ b/src/strategy/shaman/ShamanActions.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_SHAMANACTIONS_H #include "GenericSpellActions.h" +#include "Playerbots.h" #include "SharedDefines.h" class PlayerbotAI; @@ -426,6 +427,20 @@ public: virtual bool isUseful() override { return CastTotemAction::isUseful(); } }; +class CastFireElementalTotemMeleeAction : public CastTotemAction +{ +public: + CastFireElementalTotemMeleeAction(PlayerbotAI* ai) : CastTotemAction(ai, "fire elemental totem", "", 0.0f) {} + virtual std::string const GetTargetName() override { return "self target"; } + virtual bool isUseful() override + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target || !bot->IsWithinMeleeRange(target)) + return false; + return CastTotemAction::isUseful(); + } +}; + class CastWrathOfAirTotemAction : public CastTotemAction { public: diff --git a/src/strategy/shaman/ShamanAiObjectContext.cpp b/src/strategy/shaman/ShamanAiObjectContext.cpp index f4a821ce..e67f4230 100644 --- a/src/strategy/shaman/ShamanAiObjectContext.cpp +++ b/src/strategy/shaman/ShamanAiObjectContext.cpp @@ -110,13 +110,17 @@ public: creators["no water totem"] = &ShamanATriggerFactoryInternal::no_water_totem; creators["no air totem"] = &ShamanATriggerFactoryInternal::no_air_totem; creators["earth shield on main tank"] = &ShamanATriggerFactoryInternal::earth_shield_on_main_tank; - creators["maelstrom weapon"] = &ShamanATriggerFactoryInternal::maelstrom_weapon; + creators["maelstrom weapon 3"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_3; + creators["maelstrom weapon 4"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_4; + creators["maelstrom weapon 5"] = &ShamanATriggerFactoryInternal::maelstrom_weapon_5; creators["flame shock"] = &ShamanATriggerFactoryInternal::flame_shock; creators["wrath of air totem"] = &ShamanATriggerFactoryInternal::wrath_of_air_totem; } private: - static Trigger* maelstrom_weapon(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI); } + static Trigger* maelstrom_weapon_3(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 3); } + static Trigger* maelstrom_weapon_4(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 4); } + static Trigger* maelstrom_weapon_5(PlayerbotAI* botAI) { return new MaelstromWeaponTrigger(botAI, 5); } static Trigger* heroism(PlayerbotAI* botAI) { return new HeroismTrigger(botAI); } static Trigger* bloodlust(PlayerbotAI* botAI) { return new BloodlustTrigger(botAI); } static Trigger* elemental_mastery(PlayerbotAI* botAI) { return new ElementalMasteryTrigger(botAI); } @@ -234,6 +238,7 @@ public: creators["lava burst"] = &ShamanAiObjectContextInternal::lava_burst; creators["earth shield on main tank"] = &ShamanAiObjectContextInternal::earth_shield_on_main_tank; creators["fire elemental totem"] = &ShamanAiObjectContextInternal::fire_elemental_totem; + creators["fire elemental totem melee"] = &ShamanAiObjectContextInternal::fire_elemental_totem_melee; creators["totem of wrath"] = &ShamanAiObjectContextInternal::totem_of_wrath; creators["wrath of air totem"] = &ShamanAiObjectContextInternal::wrath_of_air_totem; creators["shamanistic rage"] = &ShamanAiObjectContextInternal::shamanistic_rage; @@ -314,6 +319,7 @@ private: static Action* earth_shield_on_main_tank(PlayerbotAI* ai) { return new CastEarthShieldOnMainTankAction(ai); } static Action* totem_of_wrath(PlayerbotAI* ai) { return new CastTotemOfWrathAction(ai); } static Action* fire_elemental_totem(PlayerbotAI* ai) { return new CastFireElementalTotemAction(ai); } + static Action* fire_elemental_totem_melee(PlayerbotAI* ai) { return new CastFireElementalTotemMeleeAction(ai); } static Action* wrath_of_air_totem(PlayerbotAI* ai) { return new CastWrathOfAirTotemAction(ai); } static Action* shamanistic_rage(PlayerbotAI* ai) { return new CastShamanisticRageAction(ai); } static Action* feral_spirit(PlayerbotAI* ai) { return new CastFeralSpiritAction(ai); } diff --git a/src/strategy/shaman/ShamanTriggers.h b/src/strategy/shaman/ShamanTriggers.h index 97e9ec03..2ab651a1 100644 --- a/src/strategy/shaman/ShamanTriggers.h +++ b/src/strategy/shaman/ShamanTriggers.h @@ -241,7 +241,7 @@ public: class MaelstromWeaponTrigger : public HasAuraStackTrigger { public: - MaelstromWeaponTrigger(PlayerbotAI* botAI) : HasAuraStackTrigger(botAI, "maelstrom weapon", 5) {} + MaelstromWeaponTrigger(PlayerbotAI* botAI, int stack = 5) : HasAuraStackTrigger(botAI, "maelstrom weapon", stack) {} }; class WindShearInterruptEnemyHealerSpellTrigger : public InterruptEnemyHealerTrigger diff --git a/src/strategy/shaman/TotemsShamanStrategy.cpp b/src/strategy/shaman/TotemsShamanStrategy.cpp index cfa0c3be..f3346a5c 100644 --- a/src/strategy/shaman/TotemsShamanStrategy.cpp +++ b/src/strategy/shaman/TotemsShamanStrategy.cpp @@ -13,20 +13,13 @@ void TotemsShamanStrategy::InitTriggers(std::vector& triggers) { GenericShamanStrategy::InitTriggers(triggers); - triggers.push_back(new TriggerNode("fire elemental totem", - NextAction::array(0, new NextAction("fire elemental totem", 32.0f), nullptr))); - triggers.push_back( - new TriggerNode("no air totem", NextAction::array(0, new NextAction("wrath of air totem", 8.0f), NULL))); + new TriggerNode("no air totem", NextAction::array(0, new NextAction("wrath of air totem", 8.0f), nullptr))); triggers.push_back( new TriggerNode("no water totem", NextAction::array(0, new NextAction("mana spring totem", 7.0f), new NextAction("healing stream totem", 6.0f), nullptr))); - triggers.push_back( - new TriggerNode("no fire totem", NextAction::array(0, new NextAction("flametongue totem", 7.0f), - new NextAction("searing totem", 6.0f), nullptr))); - triggers.push_back(new TriggerNode("strength of earth totem", - NextAction::array(0, new NextAction("strength of earth totem", 6.0f), NULL))); + NextAction::array(0, new NextAction("strength of earth totem", 6.0f), nullptr))); } diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index c1f80eb7..32253186 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -83,6 +83,17 @@ bool EnergyAvailable::IsActive() { return AI_VALUE2(uint8, "energy", "self targe bool ComboPointsAvailableTrigger::IsActive() { return AI_VALUE2(uint8, "combo", "current target") >= amount; } +bool TargetWithComboPointsLowerHealTrigger::IsActive() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target || !target->IsAlive() || !target->IsInWorld()) + { + return false; + } + return ComboPointsAvailableTrigger::IsActive() && + (target->GetHealth() / AI_VALUE(float, "estimated group dps")) <= lifeTime; +} + bool LoseAggroTrigger::IsActive() { return !AI_VALUE2(bool, "has aggro", "current target"); } bool HasAggroTrigger::IsActive() { return AI_VALUE2(bool, "has aggro", "current target"); } @@ -221,7 +232,7 @@ bool DebuffTrigger::IsActive() { return false; } - return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "expected group dps")) >= needLifeTime; + return BuffTrigger::IsActive() && (target->GetHealth() / AI_VALUE(float, "estimated group dps")) >= needLifeTime; } bool DebuffOnBossTrigger::IsActive() diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index 35a49e3f..674b514d 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -113,6 +113,19 @@ public: bool IsActive() override; }; +class TargetWithComboPointsLowerHealTrigger : public ComboPointsAvailableTrigger +{ +public: + TargetWithComboPointsLowerHealTrigger(PlayerbotAI* ai, int32 combo_point = 5, float lifeTime = 8.0f) + : ComboPointsAvailableTrigger(ai, combo_point), lifeTime(lifeTime) + { + } + bool IsActive() override; + +private: + float lifeTime; +}; + class LoseAggroTrigger : public Trigger { public: diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 788b7743..acad38f0 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -96,6 +96,7 @@ 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["medium threat"] = &TriggerContext::MediumThreat; @@ -309,6 +310,10 @@ private: } static Trigger* ComboPointsAvailable(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI); } static Trigger* ComboPoints3Available(PlayerbotAI* botAI) { return new ComboPointsAvailableTrigger(botAI, 3); } + static Trigger* target_with_combo_points_almost_dead(PlayerbotAI* ai) + { + return new TargetWithComboPointsLowerHealTrigger(ai, 3, 3.0f); + } 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/AttackerWithoutAuraTargetValue.cpp b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp index a3945e64..99489af4 100644 --- a/src/strategy/values/AttackerWithoutAuraTargetValue.cpp +++ b/src/strategy/values/AttackerWithoutAuraTargetValue.cpp @@ -36,3 +36,36 @@ Unit* AttackerWithoutAuraTargetValue::Calculate() return result; } + +Unit* MeleeAttackerWithoutAuraTargetValue::Calculate() +{ + GuidVector attackers = botAI->GetAiObjectContext()->GetValue("attackers")->Get(); + // Unit* target = botAI->GetAiObjectContext()->GetValue("current target")->Get(); + uint32 max_health = 0; + Unit* result = nullptr; + for (ObjectGuid const guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!bot->IsWithinMeleeRange(unit)) + continue; + + if (checkArc && !bot->HasInArc(CAST_ANGLE_IN_FRONT, unit)) + continue; + + if (unit->GetHealth() < max_health) + { + continue; + } + + if (!botAI->HasAura(qualifier, unit, false, true)) + { + max_health = unit->GetHealth(); + result = unit; + } + } + + return result; +} diff --git a/src/strategy/values/AttackerWithoutAuraTargetValue.h b/src/strategy/values/AttackerWithoutAuraTargetValue.h index 73867f9d..7d7d0666 100644 --- a/src/strategy/values/AttackerWithoutAuraTargetValue.h +++ b/src/strategy/values/AttackerWithoutAuraTargetValue.h @@ -28,7 +28,9 @@ protected: class MeleeAttackerWithoutAuraTargetValue : public AttackerWithoutAuraTargetValue { public: - MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI) : AttackerWithoutAuraTargetValue(botAI, "melee") {} + MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI, bool checkArc = true) : AttackerWithoutAuraTargetValue(botAI, "melee"), checkArc(checkArc) {} + Unit* Calculate() override; + bool checkArc; }; #endif diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index e2e12cb3..5fc276df 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -291,7 +291,7 @@ Unit* DpsTargetValue::Calculate() return rti; // FindLeastHpTargetStrategy strategy(botAI); - float dps = AI_VALUE(float, "expected group dps"); + float dps = AI_VALUE(float, "estimated group dps"); if (botAI->IsCaster(bot)) { CasterFindTargetSmartStrategy strategy(botAI, dps); diff --git a/src/strategy/values/EstimatedLifetimeValue.cpp b/src/strategy/values/EstimatedLifetimeValue.cpp new file mode 100644 index 00000000..4352934f --- /dev/null +++ b/src/strategy/values/EstimatedLifetimeValue.cpp @@ -0,0 +1,135 @@ +#include "EstimatedLifetimeValue.h" + +#include "AiFactory.h" +#include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +float EstimatedLifetimeValue::Calculate() +{ + Unit* target = AI_VALUE(Unit*, qualifier); + if (!target || !target->IsAlive()) + { + return 0.0f; + } + float dps = AI_VALUE(float, "estimated group dps"); + bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3; + if (aoePenalty) + dps *= 0.75; + float res = target->GetHealth() / dps; + // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); + return res; +} + +float EstimatedGroupDpsValue::Calculate() +{ + float totalDps; + + 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 + continue; + + if (!member || !member->IsInWorld()) + continue; + + if (member->GetMapId() != bot->GetMapId()) + continue; + + if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance) + continue; + + groupPlayer.push_back(member); + } + } + for (Player* player : groupPlayer) + { + float roleMultiplier; + if (botAI->IsTank(player)) + roleMultiplier = 0.3f; + else if (botAI->IsHeal(player)) + roleMultiplier = 0.1f; + else + roleMultiplier = 1.0f; + 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; + if (gs_modifier < 0.75) + gs_modifier = 0.75; + if (gs_modifier > 4) + gs_modifier = 4; + totalDps += basicDps * roleMultiplier * gs_modifier; + } + + return totalDps; +} + +float EstimatedGroupDpsValue::GetBasicDps(uint32 level) +{ + float basic_dps; + + if (level <= 15) + { + basic_dps = 5 + level * 1; + } + else if (level <= 25) + { + basic_dps = 20 + (level - 15) * 2; + } + else if (level <= 45) + { + basic_dps = 40 + (level - 25) * 3; + } + else if (level <= 55) + { + basic_dps = 100 + (level - 45) * 20; + } + else if (level <= 60) + { + basic_dps = 300 + (level - 55) * 50; + } + else if (level <= 70) + { + basic_dps = 450 + (level - 60) * 40; + } + else + { + basic_dps = 750 + (level - 70) * 175; + } + return basic_dps; +} + +float EstimatedGroupDpsValue::GetBasicGs(uint32 level) +{ + float basic_gs; + + if (level <= 8) + { + basic_gs = (level + 5) * 2; + } + else if (level <= 15) + { + basic_gs = (level + 5) * 3; + } + else if (level <= 60) + { + basic_gs = (level + 5) * 4; + } + else if (level <= 70) + { + basic_gs = (85 + (level - 60) * 3) * 4; + } + else + { + basic_gs = (155 + (level - 70) * 4) * 4; + } + return basic_gs; +} \ No newline at end of file diff --git a/src/strategy/values/ExpectedLifetimeValue.h b/src/strategy/values/EstimatedLifetimeValue.h similarity index 51% rename from src/strategy/values/ExpectedLifetimeValue.h rename to src/strategy/values/EstimatedLifetimeValue.h index 2afcece8..f050ac71 100644 --- a/src/strategy/values/ExpectedLifetimeValue.h +++ b/src/strategy/values/EstimatedLifetimeValue.h @@ -3,8 +3,8 @@ * and/or modify it under version 2 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H -#define _PLAYERBOT_EXPECTEDLIFETIMEVALUE_H +#ifndef _PLAYERBOT_EstimatedLifetimeValue_H +#define _PLAYERBOT_EstimatedLifetimeValue_H #include "NamedObjectContext.h" #include "PossibleTargetsValue.h" @@ -15,22 +15,26 @@ class PlayerbotAI; class Unit; // [target health] / [expected group single target dps] = [expected lifetime] -class ExpectedLifetimeValue : public FloatCalculatedValue, public Qualified +class EstimatedLifetimeValue : public FloatCalculatedValue, public Qualified { public: - ExpectedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "expected lifetime") {} + EstimatedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated lifetime") {} public: float Calculate() override; }; -class ExpectedGroupDpsValue : public FloatCalculatedValue +class EstimatedGroupDpsValue : public FloatCalculatedValue { public: - ExpectedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "expected group dps", 20 * 1000) {} + EstimatedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated group dps", 20 * 1000) {} public: float Calculate() override; + +protected: + float GetBasicDps(uint32 level); + float GetBasicGs(uint32 level); }; #endif diff --git a/src/strategy/values/ExpectedLifetimeValue.cpp b/src/strategy/values/ExpectedLifetimeValue.cpp deleted file mode 100644 index a8a554b6..00000000 --- a/src/strategy/values/ExpectedLifetimeValue.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "ExpectedLifetimeValue.h" - -#include "AiFactory.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" -#include "SharedDefines.h" - -float ExpectedLifetimeValue::Calculate() -{ - Unit* target = AI_VALUE(Unit*, qualifier); - if (!target || !target->IsAlive()) - { - return 0.0f; - } - float dps = AI_VALUE(float, "expected group dps"); - float res = target->GetHealth() / dps; - // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); - return res; -} - -float ExpectedGroupDpsValue::Calculate() -{ - float dps_num; - Group* group = bot->GetGroup(); - if (!group) - { - dps_num = 0.7; - } - else - { - dps_num = group->GetMembersCount() * 0.7; - } - uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(bot, true, false, 12); - // efficiency record based on rare gear level, is there better calculation method? - // float dps_efficiency = 1; - float basic_dps; - int32 basic_gs; - int32 level = bot->GetLevel(); - - if (level <= 15) - { - basic_dps = 5 + level * 1; - } - else if (level <= 25) - { - basic_dps = 20 + (level - 15) * 2; - } - else if (level <= 40) - { - basic_dps = 40 + (level - 30) * 4; - } - else if (level <= 55) - { - basic_dps = 100 + (level - 45) * 20; - } - else if (level <= 60) - { - basic_dps = 300 + (level - 55) * 50; - } - else if (level <= 70) - { - basic_dps = 450 + (level - 60) * 40; - } - else - { - basic_dps = 750 + (level - 70) * 175; - } - - if (level <= 8) - { - basic_gs = (level + 5) * 2; - } - else if (level <= 15) - { - basic_gs = (level + 5) * 3; - } - else if (level <= 60) - { - basic_gs = (level + 5) * 4; - } - else if (level <= 70) - { - basic_gs = (85 + (level - 60) * 3) * 4; - } - else - { - basic_gs = (155 + (level - 70) * 4) * 4; - } - float gap = mixedGearScore - basic_gs; - float gs_modifier = (float)mixedGearScore / basic_gs - 1; - gs_modifier = gs_modifier * 3 + 1; - - if (gs_modifier < 0.75) - gs_modifier = 0.75; - if (gs_modifier > 4) - gs_modifier = 4; - - return dps_num * basic_dps * gs_modifier; -} \ No newline at end of file diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 6fb93fdf..f8f4a8fe 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -26,7 +26,7 @@ #include "DuelTargetValue.h" #include "EnemyHealerTargetValue.h" #include "EnemyPlayerValue.h" -#include "ExpectedLifetimeValue.h" +#include "EstimatedLifetimeValue.h" #include "Formations.h" #include "GrindTargetValue.h" #include "GroupValues.h" @@ -299,8 +299,8 @@ public: creators["boss target"] = &ValueContext::boss_target; creators["nearest triggers"] = &ValueContext::nearest_triggers; creators["neglect threat"] = &ValueContext::neglect_threat; - creators["expected lifetime"] = &ValueContext::expected_lifetime; - creators["expected group dps"] = &ValueContext::expected_group_dps; + creators["estimated lifetime"] = &ValueContext::expected_lifetime; + creators["estimated group dps"] = &ValueContext::expected_group_dps; creators["area debuff"] = &ValueContext::area_debuff; creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange; creators["disperse distance"] = &ValueContext::disperse_distance; @@ -538,8 +538,8 @@ private: static UntypedValue* boss_target(PlayerbotAI* ai) { return new BossTargetValue(ai); } static UntypedValue* nearest_triggers(PlayerbotAI* ai) { return new NearestTriggersValue(ai); } static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); } - static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); } - static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); } + static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new EstimatedLifetimeValue(ai); } + static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new EstimatedGroupDpsValue(ai); } static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); } static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); } static UntypedValue* disperse_distance(PlayerbotAI* ai) { return new DisperseDistanceValue(ai); }