From 24efa7efa27855d66616b366eb21bcc37ad7e18f Mon Sep 17 00:00:00 2001 From: Yunfan Li <56597220+liyunfan1223@users.noreply.github.com> Date: Sat, 8 Mar 2025 19:36:06 +0800 Subject: [PATCH] General improvement on init and strats (#1064) * Potions strats and potions init * Druid and shaman spell in low level * Ammo init improvement * Rogue low level * Fix melee attack action (for caster with no mana) * Disable pet spells that reduce dps * Talents improvement * Remove CanFreeMove check * Reduce penalty for non-dagger weapon for rogue --- conf/playerbots.conf.dist | 12 ++-- src/AiFactory.cpp | 8 +-- src/RandomItemMgr.cpp | 69 +++++++++---------- src/RandomItemMgr.h | 4 +- src/factory/PlayerbotFactory.cpp | 36 ++++++++-- src/factory/StatsWeightCalculator.cpp | 7 +- src/strategy/actions/AttackAction.cpp | 18 +++-- src/strategy/actions/GenericActions.cpp | 7 +- src/strategy/actions/MovementActions.cpp | 2 +- src/strategy/actions/UseItemAction.cpp | 2 +- src/strategy/druid/CasterDruidStrategy.cpp | 3 +- src/strategy/druid/DruidTriggers.h | 4 +- .../druid/GenericDruidNonCombatStrategy.cpp | 2 + src/strategy/druid/GenericDruidStrategy.cpp | 2 + src/strategy/generic/DpsAssistStrategy.cpp | 7 +- src/strategy/generic/DpsAssistStrategy.h | 3 +- src/strategy/generic/TankAssistStrategy.cpp | 13 +++- src/strategy/generic/TankAssistStrategy.h | 3 +- src/strategy/hunter/GenericHunterStrategy.cpp | 5 -- src/strategy/priest/ShadowPriestStrategy.cpp | 4 +- .../rogue/AssassinationRogueStrategy.cpp | 33 +++++++-- src/strategy/rogue/RogueActions.cpp | 11 +++ src/strategy/rogue/RogueActions.h | 6 +- src/strategy/rogue/RogueAiObjectContext.cpp | 2 +- src/strategy/shaman/CasterShamanStrategy.cpp | 15 ++-- src/strategy/shaman/GenericShamanStrategy.cpp | 10 +++ src/strategy/triggers/GenericTriggers.cpp | 7 ++ src/strategy/triggers/GenericTriggers.h | 8 +++ src/strategy/triggers/RangeTriggers.cpp | 6 +- src/strategy/triggers/TriggerContext.h | 2 + 30 files changed, 207 insertions(+), 104 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 37e1ad89..8838e2f0 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -963,8 +963,8 @@ AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331 AiPlayerbot.PremadeSpecName.3.0 = bm pve AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732 -AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351 -AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021 +AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243110531051 +AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243120531251-025305101 AiPlayerbot.PremadeSpecName.3.1 = mm pve AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732 AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051 @@ -998,16 +998,16 @@ AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221 AiPlayerbot.PremadeSpecName.4.0 = as pve AiPlayerbot.PremadeSpecGlyph.4.0 = 45768,43379,45761,43380,43378,45766 -AiPlayerbot.PremadeSpecLink.4.0.60 = 005323005350100520103331051 -AiPlayerbot.PremadeSpecLink.4.0.80 = 005323005350100520103331051-005005005003-2 +AiPlayerbot.PremadeSpecLink.4.0.60 = 005303104352100520103331051 +AiPlayerbot.PremadeSpecLink.4.0.80 = 005303104352100520103331051-005005005003-2 AiPlayerbot.PremadeSpecName.4.1 = combat pve AiPlayerbot.PremadeSpecGlyph.4.1 = 42962,43379,45762,43380,43378,42969 AiPlayerbot.PremadeSpecLink.4.1.60 = -0252051000035015223100501251 AiPlayerbot.PremadeSpecLink.4.1.80 = 00532000523-0252051000035015223100501251 AiPlayerbot.PremadeSpecName.4.2 = subtlety pve AiPlayerbot.PremadeSpecGlyph.4.2 = 42967,43379,45764,43380,43378,45767 -AiPlayerbot.PremadeSpecLink.4.2.60 = --5120122030321121050135031241 -AiPlayerbot.PremadeSpecLink.4.2.80 = 0053231-2-5120222030321121050135231251 +AiPlayerbot.PremadeSpecLink.4.2.60 = --5022012030321121350115031151 +AiPlayerbot.PremadeSpecLink.4.2.80 = 30532010114--5022012030321121350115031151 # # diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 6f5f459a..ce2ca5e9 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -275,7 +275,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (!player->InBattleground()) { - engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "duel", "boost", nullptr); + engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "potions", "duel", "boost", nullptr); } if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster()) { @@ -375,13 +375,13 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa // } break; case CLASS_ROGUE: - if (tab == ROGUE_TAB_ASSASSINATION) + if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) { - engine->addStrategiesNoInit("melee", "dps assist", "aoe", /*"behind",*/ nullptr); + engine->addStrategiesNoInit("melee", "dps assist", "aoe", nullptr); } else { - engine->addStrategiesNoInit("dps", "dps assist", "aoe", /*"behind",*/ nullptr); + engine->addStrategiesNoInit("dps", "dps assist", "aoe", nullptr); } break; case CLASS_WARLOCK: diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index 6641ec96..9a7ad711 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -2313,8 +2313,9 @@ void RandomItemMgr::BuildAmmoCache() for (uint32 subClass = ITEM_SUBCLASS_ARROW; subClass <= ITEM_SUBCLASS_BULLET; subClass++) { QueryResult results = WorldDatabase.Query( - "SELECT entry, Flags FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} and duration = 0 " - "ORDER BY stackable DESC, RequiredLevel DESC", + "SELECT entry FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} AND duration = 0 " + "AND (Flags & 16) = 0 AND dmg_min1 != 0 AND RequiredLevel != 0 " + "ORDER BY stackable DESC, ItemLevel DESC", ITEM_CLASS_PROJECTILE, subClass, level); if (!results) continue; @@ -2322,35 +2323,27 @@ void RandomItemMgr::BuildAmmoCache() { Field* fields = results->Fetch(); uint32 entry = fields[0].Get(); - uint32 flags = fields[1].Get(); - if (flags & ITEM_FLAG_DEPRECATED) - { - continue; - } - ammoCache[level][subClass] = entry; + ammoCache[level][subClass].push_back(entry); ++counter; - break; } while (results->NextRow()); } } - LOG_INFO("server.loading", "Cached {} types of ammo", counter); // TEST + LOG_INFO("server.loading", "Cached {} ammo", counter); // TEST } -uint32 RandomItemMgr::GetAmmo(uint32 level, uint32 subClass) { return ammoCache[level][subClass]; } +std::vector RandomItemMgr::GetAmmo(uint32 level, uint32 subClass) { return ammoCache[level][subClass]; } void RandomItemMgr::BuildPotionCache() { uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); - // if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) - // maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); - LOG_INFO("server.loading", "Building potion cache for {} levels", maxLevel); + LOG_INFO("playerbots", "Building potion cache for {} levels", maxLevel); ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore(); uint32 counter = 0; - for (uint32 level = 1; level <= maxLevel + 1; level += 10) + for (uint32 level = 1; level <= maxLevel; level++) { uint32 effects[] = {SPELL_EFFECT_HEAL, SPELL_EFFECT_ENERGIZE}; for (uint8 i = 0; i < 2; ++i) @@ -2367,8 +2360,9 @@ void RandomItemMgr::BuildPotionCache() (proto->SubClass != ITEM_SUBCLASS_POTION && proto->SubClass != ITEM_SUBCLASS_FLASK) || proto->Bonding != NO_BIND) continue; - - if (proto->RequiredLevel && (proto->RequiredLevel > level || proto->RequiredLevel < level - 10)) + + uint32 requiredLevel = proto->RequiredLevel; + if (requiredLevel > level || (level > 15 && requiredLevel < level - 15)) continue; if (proto->RequiredSkill) @@ -2380,39 +2374,44 @@ void RandomItemMgr::BuildPotionCache() if (proto->Duration & 0x80000000) continue; - for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++) + + if (proto->AllowableClass != -1) + continue; + + bool hybrid = false; + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[0].SpellId); + if (!spellInfo) + continue; + // do not accept hybrid potion + for (uint8 i = 1; i < 3; i++) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId); - if (!spellInfo) - continue; - - for (uint8 i = 0; i < 3; i++) + if (spellInfo->Effects[i].Effect != 0) { - if (spellInfo->Effects[i].Effect == effect) - { - potionCache[level / 10][effect].push_back(itr.first); - break; - } + hybrid = true; + break; } } + if (hybrid) + continue; + + if (spellInfo->Effects[0].Effect == effect) + potionCache[level][effect].push_back(itr.first); } } } - for (uint32 level = 1; level <= maxLevel + 1; level += 10) + for (uint32 level = 1; level <= maxLevel; level++) { uint32 effects[] = {SPELL_EFFECT_HEAL, SPELL_EFFECT_ENERGIZE}; for (uint8 i = 0; i < 2; ++i) { uint32 effect = effects[i]; - uint32 size = potionCache[level / 10][effect].size(); - ++counter; - - LOG_DEBUG("server.loading", "Potion cache for level={}, effect={}: {} items", level, effect, size); + uint32 size = potionCache[level][effect].size(); + counter += size; } } - LOG_INFO("server.loading", "Cached {} types of potions", counter); // TEST + LOG_INFO("playerbots", "Cached {} potions", counter); } void RandomItemMgr::BuildFoodCache() @@ -2478,7 +2477,7 @@ void RandomItemMgr::BuildFoodCache() uint32 RandomItemMgr::GetRandomPotion(uint32 level, uint32 effect) { - std::vector potions = potionCache[(level - 1) / 10][effect]; + const std::vector &potions = potionCache[level][effect]; if (potions.empty()) return 0; diff --git a/src/RandomItemMgr.h b/src/RandomItemMgr.h index 110074f1..6ff954a0 100644 --- a/src/RandomItemMgr.h +++ b/src/RandomItemMgr.h @@ -157,7 +157,7 @@ public: uint32 GetStatWeight(Player* player, uint32 itemId); uint32 GetLiveStatWeight(Player* player, uint32 itemId); uint32 GetRandomItem(uint32 level, RandomItemType type, RandomItemPredicate* predicate = nullptr); - uint32 GetAmmo(uint32 level, uint32 subClass); + std::vector GetAmmo(uint32 level, uint32 subClass); uint32 GetRandomPotion(uint32 level, uint32 effect); uint32 GetRandomFood(uint32 level, uint32 category); uint32 GetFood(uint32 level, uint32 category); @@ -195,7 +195,7 @@ private: std::map predicates; BotEquipCache equipCache; std::map> viableSlots; - std::map> ammoCache; + std::map>> ammoCache; std::map>> potionCache; std::map>> foodCache; std::map> tradeCache; diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 245f55c6..c98bf5c5 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -652,6 +652,9 @@ void PlayerbotFactory::AddConsumables() void PlayerbotFactory::InitPetTalents() { + if (bot->GetLevel() <= 70 && sPlayerbotAIConfig->limitTalentsExpansion) + return; + Pet* pet = bot->GetPet(); if (!pet) { @@ -1670,9 +1673,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (proto->Quality != desiredQuality) continue; - // delay heavy check - // if (!CanEquipItem(proto)) - // continue; if (proto->Class == ITEM_CLASS_ARMOR && (slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS || @@ -1688,9 +1688,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE && proto->Class != ITEM_CLASS_WEAPON) continue; - // delay heavy check - // uint16 dest = 0; - // if (CanEquipUnseenItem(slot, dest, itemId)) items[slot].push_back(itemId); } } @@ -2830,7 +2827,28 @@ void PlayerbotFactory::InitAmmo() if (!subClass) return; - uint32 entry = sRandomItemMgr->GetAmmo(level, subClass); + std::vector ammoEntryList = sRandomItemMgr->GetAmmo(level, subClass); + uint32 entry = 0; + for (uint32 tEntry : ammoEntryList) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(tEntry); + if (!proto) + continue; + + // disable next expansion ammo + if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && tEntry >= 23728) + continue; + + if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 70 && tEntry >= 35570) + continue; + + entry = tEntry; + break; + } + + if (!entry) + return; + uint32 count = bot->GetItemCount(entry); uint32 maxCount = bot->getClass() == CLASS_HUNTER ? 6000 : 1000; @@ -2984,6 +3002,10 @@ void PlayerbotFactory::InitPotions() for (uint8 i = 0; i < 2; ++i) { uint32 effect = effects[i]; + + if (effect == SPELL_EFFECT_ENERGIZE && !bot->GetPower(POWER_MANA)) + continue; + FindPotionVisitor visitor(bot, effect); IterateItems(&visitor); if (!visitor.GetResult().empty()) diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index ab2093e9..00a2f3d3 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -126,7 +126,7 @@ void StatsWeightCalculator::GenerateWeights(Player* player) void StatsWeightCalculator::GenerateBasicWeights(Player* player) { // Basic weights - stats_weights_[STATS_TYPE_STAMINA] += 0.01f; + stats_weights_[STATS_TYPE_STAMINA] += 0.1f; stats_weights_[STATS_TYPE_ARMOR] += 0.001f; stats_weights_[STATS_TYPE_BONUS] += 1.0f; @@ -508,9 +508,10 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 0.1; } - if (cls == CLASS_ROGUE && tab == ROGUE_TAB_ASSASSINATION && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) + if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) && + proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) { - weight_ *= 0.1; + weight_ *= 0.5; } if (cls == CLASS_ROGUE && player_->HasAura(13964) && (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) diff --git a/src/strategy/actions/AttackAction.cpp b/src/strategy/actions/AttackAction.cpp index 98fff5b9..58c47128 100644 --- a/src/strategy/actions/AttackAction.cpp +++ b/src/strategy/actions/AttackAction.cpp @@ -53,6 +53,16 @@ bool AttackMyTargetAction::Execute(Event event) bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) { + Unit* oldTarget = context->GetValue("current target")->Get(); + bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot); + + bool sameTarget = oldTarget == target && bot->GetVictim() == target; + bool inCombat = botAI->GetState() == BOT_STATE_COMBAT; + bool sameAttackMode = bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == shouldMelee; + // there's no reason to do attack again + if (sameTarget && inCombat && sameAttackMode) + return false; + if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE || bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) { @@ -131,11 +141,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) ObjectGuid guid = target->GetGUID(); bot->SetSelection(target->GetGUID()); - Unit* oldTarget = context->GetValue("current target")->Get(); - bool melee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot); - - if (oldTarget == target && botAI->GetState() == BOT_STATE_COMBAT && bot->GetVictim() == target && (bot->HasUnitState(UNIT_STATE_MELEE_ATTACKING) == melee)) - return false; + context->GetValue("old target")->Set(oldTarget); @@ -158,7 +164,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/) } botAI->ChangeEngine(BOT_STATE_COMBAT); - bot->Attack(target, melee); + bot->Attack(target, shouldMelee); /* prevent pet dead immediately in group */ // if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat()) { // with_pet = false; diff --git a/src/strategy/actions/GenericActions.cpp b/src/strategy/actions/GenericActions.cpp index 9f76e1e2..cfbce10c 100644 --- a/src/strategy/actions/GenericActions.cpp +++ b/src/strategy/actions/GenericActions.cpp @@ -51,11 +51,10 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) continue; bool shouldApply = true; - // spellId == 4511 || spellId == 54424 || spellId == 57564 || spellId == 57565 || - // spellId == 57566 || spellId == 57567 || - // cat stealth, prowl - if (spellId == 1742 || spellId == 24450) + if (spellId == 1742 /*cower*/ || spellId == 24450 /*Prowl*/ || + spellId == 47482 /*Leap*/ /* || spellId == 47481 Gnaw*/) { + shouldApply = false; } bool isAutoCast = false; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 232617aa..6d9320e1 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -2017,7 +2017,7 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) if (currentTarget) { // Normally, move to left or right is the best position - bool isTanking = (currentTarget->CanFreeMove()) && (currentTarget->GetVictim() == bot); + bool isTanking = (!currentTarget->isFrozen() && !currentTarget->HasRootAura()) && (currentTarget->GetVictim() == bot); float angle = bot->GetAngle(currentTarget); float angleLeft = angle + (float)M_PI / 2; float angleRight = angle - (float)M_PI / 2; diff --git a/src/strategy/actions/UseItemAction.cpp b/src/strategy/actions/UseItemAction.cpp index d90f1310..3747ef67 100644 --- a/src/strategy/actions/UseItemAction.cpp +++ b/src/strategy/actions/UseItemAction.cpp @@ -63,7 +63,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni if (bot->CanUseItem(item) != EQUIP_ERR_OK) return false; - if (bot->IsNonMeleeSpellCast(true)) + if (bot->IsNonMeleeSpellCast(false)) return false; uint8 bagIndex = item->GetBagSlot(); diff --git a/src/strategy/druid/CasterDruidStrategy.cpp b/src/strategy/druid/CasterDruidStrategy.cpp index 8182702d..ff134960 100644 --- a/src/strategy/druid/CasterDruidStrategy.cpp +++ b/src/strategy/druid/CasterDruidStrategy.cpp @@ -143,7 +143,8 @@ void CasterDruidStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("eclipse (lunar)", NextAction::array(0, new NextAction("starfire", ACTION_NORMAL + 6), nullptr))); triggers.push_back( - new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 9), NULL))); + new TriggerNode("medium mana", NextAction::array(0, new NextAction("innervate", ACTION_HIGH + 9), nullptr))); + triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr))); } diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 50020835..1ccb7236 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -113,10 +113,10 @@ public: SavageRoarTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "savage roar") {} }; -class NaturesGraspTrigger : public BoostTrigger +class NaturesGraspTrigger : public BuffTrigger { public: - NaturesGraspTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "nature's grasp") {} + NaturesGraspTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "nature's grasp") {} }; class EntanglingRootsTrigger : public HasCcTargetTrigger diff --git a/src/strategy/druid/GenericDruidNonCombatStrategy.cpp b/src/strategy/druid/GenericDruidNonCombatStrategy.cpp index be5e822f..0d7e4a4b 100644 --- a/src/strategy/druid/GenericDruidNonCombatStrategy.cpp +++ b/src/strategy/druid/GenericDruidNonCombatStrategy.cpp @@ -169,4 +169,6 @@ void GenericDruidBuffStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("mark of the wild on party", 13.0f), nullptr))); triggers.push_back(new TriggerNode("thorns on main tank", NextAction::array(0, new NextAction("thorns on main tank", 11.0f), nullptr))); + triggers.push_back(new TriggerNode("thorns", + NextAction::array(0, new NextAction("thorns", 10.0f), nullptr))); } diff --git a/src/strategy/druid/GenericDruidStrategy.cpp b/src/strategy/druid/GenericDruidStrategy.cpp index d06a0e56..ef0ac2ea 100644 --- a/src/strategy/druid/GenericDruidStrategy.cpp +++ b/src/strategy/druid/GenericDruidStrategy.cpp @@ -121,6 +121,8 @@ void GenericDruidStrategy::InitTriggers(std::vector& triggers) // NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY + 5), nullptr))); triggers.push_back(new TriggerNode("combat party member dead", NextAction::array(0, new NextAction("rebirth", ACTION_HIGH + 9), NULL))); + triggers.push_back(new TriggerNode("being attacked", + NextAction::array(0, new NextAction("nature's grasp", ACTION_HIGH + 1), nullptr))); } void DruidCureStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/generic/DpsAssistStrategy.cpp b/src/strategy/generic/DpsAssistStrategy.cpp index 11047e26..6c1b2475 100644 --- a/src/strategy/generic/DpsAssistStrategy.cpp +++ b/src/strategy/generic/DpsAssistStrategy.cpp @@ -7,10 +7,11 @@ #include "Playerbots.h" -void DpsAssistStrategy::InitTriggers(std::vector& triggers) +NextAction** DpsAssistStrategy::getDefaultActions() { - triggers.push_back( - new TriggerNode("not dps target active", NextAction::array(0, new NextAction("dps assist", 50.0f), nullptr))); + return NextAction::array( + 0, new NextAction("dps assist", 50.0f), + nullptr); } void DpsAoeStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/generic/DpsAssistStrategy.h b/src/strategy/generic/DpsAssistStrategy.h index 6d70d279..44c5b409 100644 --- a/src/strategy/generic/DpsAssistStrategy.h +++ b/src/strategy/generic/DpsAssistStrategy.h @@ -16,8 +16,7 @@ public: DpsAssistStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {} std::string const getName() override { return "dps assist"; } - // uint32 GetType() const override { return STRATEGY_TYPE_DPS; } - void InitTriggers(std::vector& triggers) override; + NextAction** getDefaultActions() override; }; class DpsAoeStrategy : public NonCombatStrategy diff --git a/src/strategy/generic/TankAssistStrategy.cpp b/src/strategy/generic/TankAssistStrategy.cpp index 46ad4d96..bf385bfc 100644 --- a/src/strategy/generic/TankAssistStrategy.cpp +++ b/src/strategy/generic/TankAssistStrategy.cpp @@ -7,8 +7,15 @@ #include "Playerbots.h" -void TankAssistStrategy::InitTriggers(std::vector& triggers) +NextAction** TankAssistStrategy::getDefaultActions() { - triggers.push_back( - new TriggerNode("tank assist", NextAction::array(0, new NextAction("tank assist", 50.0f), nullptr))); + return NextAction::array( + 0, new NextAction("tank assist", 50.0f), + nullptr); } + +// void TankAssistStrategy::InitTriggers(std::vector& triggers) +// { +// triggers.push_back( +// new TriggerNode("tank assist", NextAction::array(0, new NextAction("tank assist", 50.0f), nullptr))); +// } diff --git a/src/strategy/generic/TankAssistStrategy.h b/src/strategy/generic/TankAssistStrategy.h index 924773ad..c4cb7c09 100644 --- a/src/strategy/generic/TankAssistStrategy.h +++ b/src/strategy/generic/TankAssistStrategy.h @@ -17,7 +17,8 @@ public: std::string const getName() override { return "tank assist"; } uint32 GetType() const override { return STRATEGY_TYPE_TANK; } - void InitTriggers(std::vector& triggers) override; + NextAction** getDefaultActions() override; + // void InitTriggers(std::vector& triggers) override; }; #endif diff --git a/src/strategy/hunter/GenericHunterStrategy.cpp b/src/strategy/hunter/GenericHunterStrategy.cpp index e48b5011..9e32b4ee 100644 --- a/src/strategy/hunter/GenericHunterStrategy.cpp +++ b/src/strategy/hunter/GenericHunterStrategy.cpp @@ -146,9 +146,4 @@ void HunterTrapWeaveStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode( "immolation trap no cd", NextAction::array(0, new NextAction("reach melee", ACTION_HIGH + 3), nullptr))); - - // triggers.push_back(new TriggerNode( - // "scare beast", NextAction::array(0, new NextAction("scare beast on cc", ACTION_HIGH + 3), nullptr))); - // triggers.push_back(new TriggerNode( - // "freezing trap", NextAction::array(0, new NextAction("freezing trap on cc", ACTION_HIGH + 3), nullptr))); } diff --git a/src/strategy/priest/ShadowPriestStrategy.cpp b/src/strategy/priest/ShadowPriestStrategy.cpp index b84c639a..30fd5975 100644 --- a/src/strategy/priest/ShadowPriestStrategy.cpp +++ b/src/strategy/priest/ShadowPriestStrategy.cpp @@ -61,9 +61,9 @@ void ShadowPriestAoeStrategy::InitTriggers(std::vector& triggers) void ShadowPriestDebuffStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode( - "devouring plague", NextAction::array(0, new NextAction("devouring plague", ACTION_HIGH + 3), nullptr))); + "vampiric touch", NextAction::array(0, new NextAction("vampiric touch", ACTION_HIGH + 3), nullptr))); triggers.push_back(new TriggerNode( - "vampiric touch", NextAction::array(0, new NextAction("vampiric touch", ACTION_HIGH + 2), nullptr))); + "devouring plague", NextAction::array(0, new NextAction("devouring plague", ACTION_HIGH + 2), nullptr))); triggers.push_back(new TriggerNode( "shadow word: pain", NextAction::array(0, new NextAction("shadow word: pain", ACTION_HIGH + 1), nullptr))); // triggers.push_back(new TriggerNode("feedback", NextAction::array(0, new NextAction("feedback", 80.0f), diff --git a/src/strategy/rogue/AssassinationRogueStrategy.cpp b/src/strategy/rogue/AssassinationRogueStrategy.cpp index 6069b383..cd733210 100644 --- a/src/strategy/rogue/AssassinationRogueStrategy.cpp +++ b/src/strategy/rogue/AssassinationRogueStrategy.cpp @@ -10,6 +10,8 @@ public: { creators["mutilate"] = &mutilate; creators["envenom"] = &envenom; + creators["backstab"] = &backstab; + creators["rupture"] = &rupture; } private: @@ -17,16 +19,30 @@ private: { return new ActionNode("mutilate", /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("sinister strike"), NULL), + /*A*/ NextAction::array(0, new NextAction("backstab"), nullptr), /*C*/ NULL); } static ActionNode* envenom(PlayerbotAI* ai) { return new ActionNode("envenom", /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("eviscerate"), NULL), + /*A*/ NextAction::array(0, new NextAction("rupture"), nullptr), /*C*/ NULL); } + static ActionNode* backstab(PlayerbotAI* ai) + { + return new ActionNode("backstab", + /*P*/ NULL, + /*A*/ NextAction::array(0, new NextAction("sinister strike"), nullptr), + /*C*/ NULL); + } + static ActionNode* rupture(PlayerbotAI* botAI) + { + return new ActionNode("rupture", + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("eviscerate"), nullptr), + /*C*/ nullptr); + } }; AssassinationRogueStrategy::AssassinationRogueStrategy(PlayerbotAI* ai) : MeleeCombatStrategy(ai) @@ -48,7 +64,7 @@ void AssassinationRogueStrategy::InitTriggers(std::vector& trigger new NextAction("ambush", ACTION_HIGH + 6), nullptr))); triggers.push_back(new TriggerNode("high energy available", - NextAction::array(0, new NextAction("mutilate", ACTION_NORMAL + 3), NULL))); + NextAction::array(0, new NextAction("mutilate", ACTION_NORMAL + 3), nullptr))); triggers.push_back(new TriggerNode( "hunger for blood", NextAction::array(0, new NextAction("hunger for blood", ACTION_HIGH + 6), NULL))); @@ -57,8 +73,13 @@ void AssassinationRogueStrategy::InitTriggers(std::vector& trigger NextAction::array(0, new NextAction("slice and dice", ACTION_HIGH + 5), NULL))); triggers.push_back(new TriggerNode("combo points 3 available", - NextAction::array(0, new NextAction("envenom", ACTION_HIGH + 4), NULL))); + NextAction::array(0, new NextAction("envenom", ACTION_HIGH + 5), + new NextAction("eviscerate", ACTION_HIGH + 3), nullptr))); + triggers.push_back(new TriggerNode("target with combo points almost dead", + NextAction::array(0, new NextAction("envenom", ACTION_HIGH + 4), + new NextAction("eviscerate", ACTION_HIGH + 2), nullptr))); + triggers.push_back( new TriggerNode("expose armor", NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), NULL))); @@ -69,8 +90,8 @@ void AssassinationRogueStrategy::InitTriggers(std::vector& trigger new TriggerNode("low health", NextAction::array(0, new NextAction("evasion", ACTION_HIGH + 9), new NextAction("feint", ACTION_HIGH + 8), nullptr))); - triggers.push_back( - new TriggerNode("critical health", NextAction::array(0, new NextAction("cloak of shadows", ACTION_HIGH + 7), nullptr))); + triggers.push_back(new TriggerNode( + "critical health", NextAction::array(0, new NextAction("cloak of shadows", ACTION_HIGH + 7), nullptr))); triggers.push_back( new TriggerNode("kick", NextAction::array(0, new NextAction("kick", ACTION_INTERRUPT + 2), NULL))); diff --git a/src/strategy/rogue/RogueActions.cpp b/src/strategy/rogue/RogueActions.cpp index 54733caa..d037347f 100644 --- a/src/strategy/rogue/RogueActions.cpp +++ b/src/strategy/rogue/RogueActions.cpp @@ -44,6 +44,17 @@ bool CastVanishAction::isUseful() return !botAI->HasAura(23333, bot) && !botAI->HasAura(23335, bot) && !botAI->HasAura(34976, bot); } +bool CastEnvenomAction::isUseful() +{ + return AI_VALUE2(uint8, "energy", "self target") >= 35; +} + +bool CastEnvenomAction::isPossible() +{ + // alternate to eviscerate if talents unlearned + return botAI->HasAura(58410, bot) /* Master Poisoner */; +} + bool CastTricksOfTheTradeOnMainTankAction::isUseful() { return CastSpellAction::isUseful() && AI_VALUE2(float, "distance", GetTargetName()) < 20.0f; diff --git a/src/strategy/rogue/RogueActions.h b/src/strategy/rogue/RogueActions.h index 35d846ea..c6fc4b5b 100644 --- a/src/strategy/rogue/RogueActions.h +++ b/src/strategy/rogue/RogueActions.h @@ -127,10 +127,12 @@ public: CastKickOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "kick") {} }; -class EnvenomAction : public CastMeleeSpellAction +class CastEnvenomAction : public CastMeleeSpellAction { public: - EnvenomAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "envenom") {} + CastEnvenomAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "envenom") {} + bool isUseful() override; + bool isPossible() override; }; class CastTricksOfTheTradeOnMainTankAction : public BuffOnMainTankAction diff --git a/src/strategy/rogue/RogueAiObjectContext.cpp b/src/strategy/rogue/RogueAiObjectContext.cpp index 351c7c12..a63b97e1 100644 --- a/src/strategy/rogue/RogueAiObjectContext.cpp +++ b/src/strategy/rogue/RogueAiObjectContext.cpp @@ -173,7 +173,7 @@ private: static Action* check_stealth(PlayerbotAI* botAI) { return new CheckStealthAction(botAI); } static Action* sap(PlayerbotAI* botAI) { return new CastSapAction(botAI); } static Action* unstealth(PlayerbotAI* botAI) { return new UnstealthAction(botAI); } - static Action* envenom(PlayerbotAI* ai) { return new EnvenomAction(ai); } + static Action* envenom(PlayerbotAI* ai) { return new CastEnvenomAction(ai); } static Action* tricks_of_the_trade_on_main_tank(PlayerbotAI* ai) { return new CastTricksOfTheTradeOnMainTankAction(ai); diff --git a/src/strategy/shaman/CasterShamanStrategy.cpp b/src/strategy/shaman/CasterShamanStrategy.cpp index 50babc94..208b9593 100644 --- a/src/strategy/shaman/CasterShamanStrategy.cpp +++ b/src/strategy/shaman/CasterShamanStrategy.cpp @@ -27,9 +27,9 @@ private: static ActionNode* totem_of_wrath(PlayerbotAI* botAI) { return new ActionNode("totem of wrath", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("flametongue totem"), NULL), - /*C*/ NULL); + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("flametongue totem"), nullptr), + /*C*/ nullptr); } }; @@ -67,7 +67,11 @@ void CasterShamanStrategy::InitTriggers(std::vector& triggers) // triggers.push_back(new TriggerNode("frost shock snare", NextAction::array(0, new NextAction("frost // shock", 21.0f), nullptr))); triggers.push_back( - new TriggerNode("no fire totem", NextAction::array(0, new NextAction("totem of wrath", 15.0f), NULL))); + new TriggerNode("no fire totem", NextAction::array(0, + new NextAction("totem of wrath", 15.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))); @@ -86,4 +90,7 @@ void CasterAoeShamanStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("light aoe", NextAction::array(0, new NextAction("chain lightning", 25.0f), nullptr))); + + triggers.push_back( + new TriggerNode("medium aoe", NextAction::array(0, new NextAction("fire nova", 24.0f), nullptr))); } diff --git a/src/strategy/shaman/GenericShamanStrategy.cpp b/src/strategy/shaman/GenericShamanStrategy.cpp index 95ed14f2..b04063e0 100644 --- a/src/strategy/shaman/GenericShamanStrategy.cpp +++ b/src/strategy/shaman/GenericShamanStrategy.cpp @@ -22,6 +22,7 @@ public: creators["riptide"] = &riptide; creators["riptide on party"] = &riptide_on_party; creators["earth shock"] = &earth_shock; + creators["water shield"] = &water_shield; } private: @@ -97,6 +98,15 @@ private: /*A*/ nullptr, /*C*/ nullptr); } + + static ActionNode* water_shield([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("water shield", + /*P*/ nullptr, + /*A*/ NextAction::array(0, new NextAction("lightning shield"), nullptr), + /*C*/ nullptr); + } + }; GenericShamanStrategy::GenericShamanStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) diff --git a/src/strategy/triggers/GenericTriggers.cpp b/src/strategy/triggers/GenericTriggers.cpp index d1bfb32b..bbda4c9c 100644 --- a/src/strategy/triggers/GenericTriggers.cpp +++ b/src/strategy/triggers/GenericTriggers.cpp @@ -198,6 +198,13 @@ bool MyAttackerCountTrigger::IsActive() return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount; } +bool MediumThreatTrigger::IsActive() +{ + if (!AI_VALUE(Unit*, "main tank")) + return false; + return MyAttackerCountTrigger::IsActive(); +} + bool LowTankThreatTrigger::IsActive() { Unit* mt = AI_VALUE(Unit*, "main tank"); diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index f7e1fb2a..dd4e4650 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -243,10 +243,18 @@ public: std::string const getName() override { return "my attacker count"; } }; +class BeingAttackedTrigger : public MyAttackerCountTrigger +{ +public: + BeingAttackedTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 1) {} + std::string const getName() override { return "being attacked"; } +}; + class MediumThreatTrigger : public MyAttackerCountTrigger { public: MediumThreatTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 2) {} + bool IsActive() override; }; class LowTankThreatTrigger : public Trigger diff --git a/src/strategy/triggers/RangeTriggers.cpp b/src/strategy/triggers/RangeTriggers.cpp index 772170c3..0095ba96 100644 --- a/src/strategy/triggers/RangeTriggers.cpp +++ b/src/strategy/triggers/RangeTriggers.cpp @@ -19,7 +19,7 @@ static float GetSpeedInMotion(Unit* target) bool EnemyTooCloseForSpellTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); - return target && (target->GetVictim() != bot || target->isFrozen() || !target->CanFreeMove()) && + return target && (target->GetVictim() != bot || target->isFrozen() || target->HasRootAura()) && target->GetObjectSize() <= 10.0f && target->IsWithinCombatRange(bot, MIN_MELEE_REACH); // Unit* target = AI_VALUE(Unit*, "current target"); // if (!target) { @@ -69,7 +69,7 @@ bool EnemyTooCloseForAutoShotTrigger::IsActive() if (spellId && bot->HasSpellCooldown(spellId)) trapToCast = false; - return !trapToCast && (target->GetVictim() != bot || target->isFrozen() || !target->CanFreeMove()) && + return !trapToCast && (target->GetVictim() != bot || target->isFrozen() || target->HasRootAura()) && bot->IsWithinMeleeRange(target); // if (target->GetTarget() == bot->GetGUID() && !bot->GetGroup() && !target->HasUnitState(UNIT_STATE_ROOT) && @@ -100,7 +100,7 @@ bool EnemyTooCloseForShootTrigger::IsActive() Unit* target = AI_VALUE(Unit*, "current target"); // target->IsWithinCombatRange() - return target && (target->GetVictim() != bot || target->isFrozen() || !target->CanFreeMove()) && + return target && (target->GetVictim() != bot || target->isFrozen() || target->HasRootAura()) && target->IsWithinCombatRange(bot, MIN_MELEE_REACH); // Unit* target = AI_VALUE(Unit*, "current target"); diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 2a18c0a1..d47b75bd 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -107,6 +107,7 @@ public: creators["combo points not full"] = &TriggerContext::ComboPointsNotFull; creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy; + creators["being attacked"] = &TriggerContext::BeingAttacked; creators["medium threat"] = &TriggerContext::MediumThreat; creators["low tank threat"] = &TriggerContext::low_tank_threat; @@ -333,6 +334,7 @@ private: } 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* BeingAttacked(PlayerbotAI* botAI) { return new BeingAttackedTrigger(botAI); } static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); } static Trigger* low_tank_threat(PlayerbotAI* botAI) { return new LowTankThreatTrigger(botAI); } // static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); }