diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 3d0866f44e0..3e72a5058ee 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -32,7 +32,7 @@ # LEVELS # GEAR # QUESTS -# ACTIVITIES +# ACTIVITY # SPELLS # STRATEGIES # RPG STRATEGY diff --git a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp index ab897a1b21a..58ee42cfaf9 100644 --- a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp +++ b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp @@ -343,7 +343,7 @@ bool BGJoinAction::isUseful() return false; // check Deserter debuff - if (!bot->CanJoinToBattleground()) + if (bot->IsDeserter()) return false; // check if has free queue slots (pointless as already making sure not in queue) diff --git a/src/Ai/Base/Actions/BuyAction.cpp b/src/Ai/Base/Actions/BuyAction.cpp index f0729f2518e..e3d454dd93d 100644 --- a/src/Ai/Base/Actions/BuyAction.cpp +++ b/src/Ai/Base/Actions/BuyAction.cpp @@ -213,13 +213,7 @@ bool BuyAction::Execute(Event event) } } - if (!vendored) - { - botAI->TellError("There are no vendors nearby"); - return false; - } - - return true; + return vendored; } bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto) diff --git a/src/Ai/Base/Actions/GuildCreateActions.cpp b/src/Ai/Base/Actions/GuildCreateActions.cpp index c536475f101..59696ecea09 100644 --- a/src/Ai/Base/Actions/GuildCreateActions.cpp +++ b/src/Ai/Base/Actions/GuildCreateActions.cpp @@ -296,7 +296,7 @@ bool PetitionTurnInAction::isUseful() bool BuyTabardAction::Execute(Event /*event*/) { - bool canBuy = botAI->DoSpecificAction("buy", Event("buy tabard", "Hitem:5976:")); + bool canBuy = botAI->DoSpecificAction("buy", Event("buy tabard", "Hitem:5976:"), true); if (canBuy && AI_VALUE2(uint32, "item count", chat->FormatQItem(5976))) return true; diff --git a/src/Ai/Base/Actions/TellEmblemsAction.cpp b/src/Ai/Base/Actions/TellEmblemsAction.cpp new file mode 100644 index 00000000000..4d69baa11b4 --- /dev/null +++ b/src/Ai/Base/Actions/TellEmblemsAction.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "TellEmblemsAction.h" + +#include + +#include "Event.h" +#include "Playerbots.h" + +bool TellEmblemsAction::Execute(Event /*event*/) +{ + static std::array const emblemIds = { + 29434, // Badge of Justice + 40752, // Emblem of Heroism + 40753, // Emblem of Valor + 45624, // Emblem of Conquest + 47241, // Emblem of Triumph + 49426 // Emblem of Frost + }; + + botAI->TellMaster("=== Emblems ==="); + + for (uint32 itemId : emblemIds) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (!proto) + continue; + + uint32 count = bot->GetItemCount(itemId, false); + std::ostringstream out; + out << chat->FormatItem(proto, count); + botAI->TellMaster(out); + } + + return true; +} diff --git a/src/Ai/Base/Actions/TellEmblemsAction.h b/src/Ai/Base/Actions/TellEmblemsAction.h new file mode 100644 index 00000000000..570fb2d045b --- /dev/null +++ b/src/Ai/Base/Actions/TellEmblemsAction.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_TELLEMBLEMSACTION_H +#define _PLAYERBOT_TELLEMBLEMSACTION_H + +#include "InventoryAction.h" + +class PlayerbotAI; + +class TellEmblemsAction : public InventoryAction +{ +public: + TellEmblemsAction(PlayerbotAI* botAI) : InventoryAction(botAI, "emblems") {} + + bool Execute(Event event) override; +}; + +#endif diff --git a/src/Ai/Base/Actions/TellReputationAction.cpp b/src/Ai/Base/Actions/TellReputationAction.cpp index 0ccff606a22..22c11e16529 100644 --- a/src/Ai/Base/Actions/TellReputationAction.cpp +++ b/src/Ai/Base/Actions/TellReputationAction.cpp @@ -5,34 +5,23 @@ #include "TellReputationAction.h" +#include + #include "Event.h" #include "PlayerbotAI.h" #include "ReputationMgr.h" -bool TellReputationAction::Execute(Event /*event*/) -{ - Player* master = GetMaster(); - if (!master) - return false; - - ObjectGuid selection = master->GetTarget(); - if (selection.IsEmpty()) - return false; +#include "SharedDefines.h" - Unit* unit = ObjectAccessor::GetUnit(*master, selection); - if (!unit) - return false; - - FactionTemplateEntry const* factionTemplate = unit->GetFactionTemplateEntry(); - uint32 faction = factionTemplate->faction; - FactionEntry const* entry = sFactionStore.LookupEntry(faction); - int32 reputation = bot->GetReputationMgr().GetReputation(faction); +std::string TellReputationAction::BuildReputationLine(FactionEntry const* entry) +{ + ReputationMgr& repMgr = bot->GetReputationMgr(); + ReputationRank rank = repMgr.GetRank(entry); + int32 reputation = repMgr.GetReputation(entry->ID); std::ostringstream out; - out << entry->name[0] << ": "; - out << "|cff"; + out << entry->name[0] << ": |cff"; - ReputationRank rank = bot->GetReputationMgr().GetRank(entry); switch (rank) { case REP_HATED: @@ -71,7 +60,65 @@ bool TellReputationAction::Execute(Event /*event*/) base -= ReputationMgr::PointsInRank[i]; out << " (" << (reputation - base) << "/" << ReputationMgr::PointsInRank[rank] << ")"; - botAI->TellMaster(out); + return out.str(); +} + +bool TellReputationAction::Execute(Event event) +{ + std::string const param = event.getParam(); + if (param == "all") + { + ReputationMgr& repMgr = bot->GetReputationMgr(); + std::vector lines; + + FactionStateList const& stateList = repMgr.GetStateList(); + lines.reserve(stateList.size()); + + for (auto const& itr : stateList) + { + FactionState const& faction = itr.second; + if (!(faction.Flags & FACTION_FLAG_VISIBLE)) + continue; + + if (faction.Flags & (FACTION_FLAG_HIDDEN | FACTION_FLAG_INVISIBLE_FORCED) && + !(faction.Flags & FACTION_FLAG_SPECIAL)) + continue; + + FactionEntry const* entry = sFactionStore.LookupEntry(faction.ID); + if (!entry) + continue; + + lines.push_back(BuildReputationLine(entry)); + } + + std::sort(lines.begin(), lines.end()); + + botAI->TellMaster("=== Reputations ==="); + for (auto const& line : lines) + botAI->TellMaster(line); + + return true; + } + + Player* master = GetMaster(); + if (!master) + return false; + + ObjectGuid selection = master->GetTarget(); + if (selection.IsEmpty()) + return false; + + Unit* unit = ObjectAccessor::GetUnit(*master, selection); + if (!unit) + return false; + + FactionTemplateEntry const* factionTemplate = unit->GetFactionTemplateEntry(); + + FactionEntry const* entry = sFactionStore.LookupEntry(factionTemplate->faction); + if (!entry) + return false; + + botAI->TellMaster(BuildReputationLine(entry)); return true; } diff --git a/src/Ai/Base/Actions/TellReputationAction.h b/src/Ai/Base/Actions/TellReputationAction.h index 3adaa66d568..d97d0d17774 100644 --- a/src/Ai/Base/Actions/TellReputationAction.h +++ b/src/Ai/Base/Actions/TellReputationAction.h @@ -6,8 +6,11 @@ #ifndef _PLAYERBOT_TELLREPUTATIONACTION_H #define _PLAYERBOT_TELLREPUTATIONACTION_H +#include + #include "Action.h" +struct FactionEntry; class PlayerbotAI; class TellReputationAction : public Action @@ -16,6 +19,9 @@ class TellReputationAction : public Action TellReputationAction(PlayerbotAI* botAI) : Action(botAI, "reputation") {} bool Execute(Event event) override; + +private: + std::string BuildReputationLine(FactionEntry const* entry); }; #endif diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index af51c23ae07..497ae2e9c13 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -66,6 +66,7 @@ #include "TaxiAction.h" #include "TeleportAction.h" #include "TellCastFailedAction.h" +#include "TellEmblemsAction.h" #include "TellItemCountAction.h" #include "TellLosAction.h" #include "TellReputationAction.h" @@ -120,6 +121,7 @@ class ChatActionContext : public NamedObjectContext creators["teleport"] = &ChatActionContext::teleport; creators["taxi"] = &ChatActionContext::taxi; creators["repair"] = &ChatActionContext::repair; + creators["emblems"] = &ChatActionContext::emblems; creators["use"] = &ChatActionContext::use; creators["item count"] = &ChatActionContext::item_count; creators["equip"] = &ChatActionContext::equip; @@ -276,6 +278,7 @@ class ChatActionContext : public NamedObjectContext static Action* item_count(PlayerbotAI* botAI) { return new TellItemCountAction(botAI); } static Action* use(PlayerbotAI* botAI) { return new UseItemAction(botAI); } static Action* repair(PlayerbotAI* botAI) { return new RepairAllAction(botAI); } + static Action* emblems(PlayerbotAI* botAI) { return new TellEmblemsAction(botAI); } static Action* taxi(PlayerbotAI* botAI) { return new TaxiAction(botAI); } static Action* teleport(PlayerbotAI* botAI) { return new TeleportAction(botAI); } static Action* release(PlayerbotAI* botAI) { return new ReleaseSpiritAction(botAI); } diff --git a/src/Ai/Base/ChatTriggerContext.h b/src/Ai/Base/ChatTriggerContext.h index 7742a9305be..ef8827c2927 100644 --- a/src/Ai/Base/ChatTriggerContext.h +++ b/src/Ai/Base/ChatTriggerContext.h @@ -41,6 +41,7 @@ class ChatTriggerContext : public NamedObjectContext creators["teleport"] = &ChatTriggerContext::teleport; creators["taxi"] = &ChatTriggerContext::taxi; creators["repair"] = &ChatTriggerContext::repair; + creators["emblems"] = &ChatTriggerContext::emblems; creators["u"] = &ChatTriggerContext::use; creators["use"] = &ChatTriggerContext::use; creators["c"] = &ChatTriggerContext::item_count; @@ -235,6 +236,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* item_count(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "c"); } static Trigger* use(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "use"); } static Trigger* repair(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "repair"); } + static Trigger* emblems(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "emblems"); } static Trigger* taxi(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "taxi"); } static Trigger* teleport(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "teleport"); } static Trigger* q(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "q"); } diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index 8d5449ef3bb..2e4503c18e9 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -114,6 +114,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("pet attack", { NextAction("pet attack", relevance) })); triggers.push_back(new TriggerNode("roll", { NextAction("roll", relevance) })); triggers.push_back(new TriggerNode("focus heal", { NextAction("focus heal targets", relevance) })); + triggers.push_back(new TriggerNode("emblems", { NextAction("emblems", relevance) })); } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) @@ -138,6 +139,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("teleport"); supported.push_back("taxi"); supported.push_back("repair"); + supported.push_back("emblems"); supported.push_back("talents"); supported.push_back("spells"); supported.push_back("co"); @@ -202,8 +204,8 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("unlock items"); supported.push_back("unlock traded item"); supported.push_back("tame"); - supported.push_back("glyphs"); // Added for custom Glyphs - supported.push_back("glyph equip"); // Added for custom Glyphs + supported.push_back("glyphs"); + supported.push_back("glyph equip"); supported.push_back("pet"); supported.push_back("pet attack"); supported.push_back("wait for attack time"); diff --git a/src/Ai/Base/Trigger/PvpTriggers.cpp b/src/Ai/Base/Trigger/PvpTriggers.cpp index 31fcd0357e3..b20c5df979a 100644 --- a/src/Ai/Base/Trigger/PvpTriggers.cpp +++ b/src/Ai/Base/Trigger/PvpTriggers.cpp @@ -297,7 +297,7 @@ bool PlayerWantsInBattlegroundTrigger::IsActive() if (bot->GetBattleground() && bot->GetBattleground()->GetStatus() == STATUS_IN_PROGRESS) return false; - if (!bot->CanJoinToBattleground()) + if (bot->IsDeserter()) return false; return true; diff --git a/src/Ai/Base/Value/ItemUsageValue.cpp b/src/Ai/Base/Value/ItemUsageValue.cpp index c3d976f0fd0..a1f9688d277 100644 --- a/src/Ai/Base/Value/ItemUsageValue.cpp +++ b/src/Ai/Base/Value/ItemUsageValue.cpp @@ -180,19 +180,11 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, delete pItem; if (result != EQUIP_ERR_OK && result != EQUIP_ERR_CANT_CARRY_MORE_OF_THIS) - { return ITEM_USAGE_NONE; - } - // Check is unique items are equipped or not - bool needToCheckUnique = false; - if (result == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS) - { - needToCheckUnique = true; - } - else if (itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE)) - { - needToCheckUnique = true; - } + + // Check if unique items are equipped or not + bool needToCheckUnique = result == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS || + itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE); if (needToCheckUnique) { @@ -206,28 +198,27 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, bool isEquipped = (totalItemCount > bagItemCount); if (isEquipped) - { return ITEM_USAGE_NONE; // Item is already equipped - } // If not equipped, continue processing } - if (itemProto->Class == ITEM_CLASS_QUIVER) - if (bot->getClass() != CLASS_HUNTER) - return ITEM_USAGE_NONE; + if (itemProto->Class == ITEM_CLASS_QUIVER && bot->getClass() != CLASS_HUNTER) + return ITEM_USAGE_NONE; if (itemProto->Class == ITEM_CLASS_CONTAINER) { if (itemProto->SubClass != ITEM_SUBCLASS_CONTAINER) return ITEM_USAGE_NONE; // Todo add logic for non-bag containers. We want to look at professions/class and // only replace if non-bag is larger than bag. - if (GetSmallestBagSize() >= itemProto->ContainerSlots) return ITEM_USAGE_NONE; return ITEM_USAGE_EQUIP; } + if (itemProto->Class == ITEM_CLASS_WEAPON && itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MISC) + return ITEM_USAGE_NONE; + bool shouldEquip = false; // uint32 statWeight = sRandomItemMgr.GetLiveStatWeight(bot, itemProto->ItemId); StatsWeightCalculator calculator(bot); @@ -254,19 +245,14 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true); // Check if dest wasn't set correctly by CanEquipItem and use FindEquipSlot instead // This occurs with unique items that are already in the bots bags when CanEquipItem is called - if (dest == 0) + if (dest == 0 && dstSlot != NULL_SLOT) { - if (dstSlot != NULL_SLOT) - { - // Construct dest from dstSlot - dest = (INVENTORY_SLOT_BAG_0 << 8) | dstSlot; - } + // Construct dest from dstSlot + dest = (INVENTORY_SLOT_BAG_0 << 8) | dstSlot; } if (dstSlot == EQUIPMENT_SLOT_FINGER1 || dstSlot == EQUIPMENT_SLOT_TRINKET1) - { possibleSlots = 2; - } // Check weapon case separately to keep things a bit cleaner bool have2HWeapon = false; @@ -283,14 +269,9 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2); // If the bot can Titan Grip, ignore any 2H weapon that isn't a 2H sword, mace, or axe. - if (bot->CanTitanGrip()) - { - // If this weapon is 2H but not one of the valid TG weapon types, do not equip it at all. - if (itemProto->InventoryType == INVTYPE_2HWEAPON && !isValidTGWeapon) - { - return ITEM_USAGE_NONE; - } - } + // If this weapon is 2H but not one of the valid TG weapon types, do not equip it at all. + if (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON && !isValidTGWeapon) + return ITEM_USAGE_NONE; // Now handle the logic for equipping and possible offhand slots // If the bot can Dual Wield and: @@ -317,9 +298,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, if (shouldEquipInSlot) return ITEM_USAGE_EQUIP; else - { return ITEM_USAGE_BAD_EQUIP; - } } ItemTemplate const* oldItemProto = oldItem->GetTemplate(); @@ -328,22 +307,16 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, { // uint32 oldStatWeight = sRandomItemMgr.GetLiveStatWeight(bot, oldItemProto->ItemId); if (itemScore || oldScore) - { shouldEquipInSlot = itemScore > oldScore * sPlayerbotAIConfig.equipUpgradeThreshold; - } } // Bigger quiver if (itemProto->Class == ITEM_CLASS_QUIVER) { if (!oldItem || oldItemProto->ContainerSlots < itemProto->ContainerSlots) - { return ITEM_USAGE_EQUIP; - } else - { return ITEM_USAGE_NONE; - } } bool existingShouldEquip = true; diff --git a/src/Ai/Class/Druid/Action/DruidActions.cpp b/src/Ai/Class/Druid/Action/DruidActions.cpp index 2eb809480f4..c01cc4342e3 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidActions.cpp @@ -11,6 +11,22 @@ #include "AoeValues.h" #include "TargetValue.h" +namespace +{ + bool PrepareThornsTarget(PlayerbotAI* botAI, Unit* target) + { + if (!target) + return false; + + Aura* existingThorns = botAI->GetAura("thorns", target, true); + if (!existingThorns) + return true; + + target->RemoveOwnedAura(existingThorns, AURA_REMOVE_BY_CANCEL); + return true; + } +} + std::vector CastAbolishPoisonAction::getAlternatives() { return NextAction::merge({ NextAction("cure poison") }, @@ -33,6 +49,21 @@ bool CastLifebloomOnMainTankAction::isUseful() return !lifebloom || lifebloom->GetStackAmount() < 3 || lifebloom->GetDuration() < 2000; } +bool CastThornsAction::Execute(Event event) +{ + return PrepareThornsTarget(botAI, GetTarget()) && CastBuffSpellAction::Execute(event); +} + +bool CastThornsOnPartyAction::Execute(Event event) +{ + return PrepareThornsTarget(botAI, GetTarget()) && BuffOnPartyAction::Execute(event); +} + +bool CastThornsOnMainTankAction::Execute(Event event) +{ + return PrepareThornsTarget(botAI, GetTarget()) && BuffOnMainTankAction::Execute(event); +} + Value* CastEntanglingRootsCcAction::GetTargetValue() { return context->GetValue("cc target", "entangling roots"); diff --git a/src/Ai/Class/Druid/Action/DruidActions.h b/src/Ai/Class/Druid/Action/DruidActions.h index 016c3dfc433..7e02a985fd0 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.h +++ b/src/Ai/Class/Druid/Action/DruidActions.h @@ -114,18 +114,24 @@ class CastThornsAction : public CastBuffSpellAction { public: CastThornsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "thorns") {} + + bool Execute(Event event) override; }; class CastThornsOnPartyAction : public BuffOnPartyAction { public: CastThornsOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "thorns") {} + + bool Execute(Event event) override; }; class CastThornsOnMainTankAction : public BuffOnMainTankAction { public: CastThornsOnMainTankAction(PlayerbotAI* botAI) : BuffOnMainTankAction(botAI, "thorns", false) {} + + bool Execute(Event event) override; }; class CastLifebloomOnMainTankAction : public BuffOnMainTankAction diff --git a/src/Ai/Raid/Aq20/RaidAq20TriggerContext.h b/src/Ai/Raid/Aq20/RaidAq20TriggerContext.h index b49ae1c6b79..b0307ca6a28 100644 --- a/src/Ai/Raid/Aq20/RaidAq20TriggerContext.h +++ b/src/Ai/Raid/Aq20/RaidAq20TriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H #define _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidAq20Triggers.h" diff --git a/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h b/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h index 97ff7453a47..86bcf8e4764 100644 --- a/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h +++ b/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h @@ -1,8 +1,6 @@ #ifndef _PLAYERBOT_RAIDAQ20STRATEGY_H #define _PLAYERBOT_RAIDAQ20STRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidAq20Strategy : public Strategy diff --git a/src/Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h b/src/Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h index aa6b57c9f9e..de2ce005867 100644 --- a/src/Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h +++ b/src/Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDBWLTRIGGERCONTEXT_H #define _PLAYERBOT_RAIDBWLTRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidBwlTriggers.h" diff --git a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h index 4308871c856..e09ea2f3ee5 100644 --- a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h +++ b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h @@ -2,8 +2,6 @@ #ifndef _PLAYERBOT_RAIDBWLSTRATEGY_H #define _PLAYERBOT_RAIDBWLSTRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidBwlStrategy : public Strategy diff --git a/src/Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h b/src/Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h index 0c58f6cbffb..c545e10eba6 100644 --- a/src/Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h +++ b/src/Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H #define _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidEoETriggers.h" diff --git a/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h b/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h index eb7a147bd6f..ba9802116a1 100644 --- a/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h +++ b/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h @@ -1,8 +1,6 @@ #ifndef _PLAYERBOT_RAIDEOESTRATEGY_H #define _PLAYERBOT_RAIDEOESTRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidEoEStrategy : public Strategy diff --git a/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h b/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h index d12b0ce4672..35a0f138eda 100644 --- a/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h +++ b/src/Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h @@ -2,7 +2,7 @@ #define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H #include "RaidGruulsLairTriggers.h" -#include "AiObjectContext.h" +#include "NamedObjectContext.h" class RaidGruulsLairTriggerContext : public NamedObjectContext { diff --git a/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h b/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h index ba6f33f0763..0d41d57a800 100644 --- a/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h +++ b/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h @@ -2,7 +2,6 @@ #define _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H #include "Strategy.h" -#include "Multiplier.h" class RaidGruulsLairStrategy : public Strategy { diff --git a/src/Ai/Raid/Icecrown/RaidIccTriggerContext.h b/src/Ai/Raid/Icecrown/RaidIccTriggerContext.h index 64c320c7207..83f3004668e 100644 --- a/src/Ai/Raid/Icecrown/RaidIccTriggerContext.h +++ b/src/Ai/Raid/Icecrown/RaidIccTriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H #define _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidIccTriggers.h" diff --git a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h b/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h index 53967c33441..fbd54cc6482 100644 --- a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h +++ b/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h @@ -1,10 +1,7 @@ #ifndef _PLAYERBOT_RAIDICCSTRATEGY_H #define _PLAYERBOT_RAIDICCSTRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" -#include "RaidIccMultipliers.h" class RaidIccStrategy : public Strategy { diff --git a/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h b/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h index e3f606c9497..a9c430734f5 100644 --- a/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h +++ b/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h @@ -2,7 +2,7 @@ #define _PLAYERBOT_RAIDKARAZHANTRIGGERCONTEXT_H #include "RaidKarazhanTriggers.h" -#include "AiObjectContext.h" +#include "NamedObjectContext.h" class RaidKarazhanTriggerContext : public NamedObjectContext { diff --git a/src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.h b/src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.h index 7d6b16deef7..4f95bf7b4a4 100644 --- a/src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.h +++ b/src/Ai/Raid/Karazhan/Strategy/RaidKarazhanStrategy.h @@ -2,7 +2,6 @@ #define _PLAYERBOT_RAIDKARAZHANSTRATEGY_H_ #include "Strategy.h" -#include "Multiplier.h" class RaidKarazhanStrategy : public Strategy { diff --git a/src/Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h b/src/Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h index 525fe496e81..482152e0ee6 100644 --- a/src/Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h +++ b/src/Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h @@ -2,7 +2,7 @@ #define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H #include "RaidMagtheridonTriggers.h" -#include "AiObjectContext.h" +#include "NamedObjectContext.h" class RaidMagtheridonTriggerContext : public NamedObjectContext { diff --git a/src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.h b/src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.h index 7b8ab8f9b18..4d21464aece 100644 --- a/src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.h +++ b/src/Ai/Raid/Magtheridon/Strategy/RaidMagtheridonStrategy.h @@ -2,7 +2,6 @@ #define _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H #include "Strategy.h" -#include "Multiplier.h" class RaidMagtheridonStrategy : public Strategy { diff --git a/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h b/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h index a62d851dc0d..1f694fe6502 100644 --- a/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h +++ b/src/Ai/Raid/MoltenCore/RaidMcTriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H #define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "BossAuraTriggers.h" #include "NamedObjectContext.h" #include "RaidMcTriggers.h" diff --git a/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h b/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h index 45b503e9333..6e77910ec44 100644 --- a/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h +++ b/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h @@ -1,8 +1,6 @@ #ifndef _PLAYERBOT_RAIDMCSTRATEGY_H #define _PLAYERBOT_RAIDMCSTRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidMcStrategy : public Strategy diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h index 4d1557d566b..83afc273d79 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h @@ -6,7 +6,6 @@ #ifndef _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H #define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidNaxxTriggers.h" diff --git a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h index 4b8a9a7c095..d2ce821a8f7 100644 --- a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h +++ b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h @@ -2,8 +2,6 @@ #ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H #define _PLAYERBOT_RAIDNAXXSTRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidNaxxStrategy : public Strategy diff --git a/src/Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h b/src/Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h index b8a1f4b316f..3c1d406928a 100644 --- a/src/Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h +++ b/src/Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H #define _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidOsTriggers.h" diff --git a/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h b/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h index 44983f1fa8e..0d9ae7871be 100644 --- a/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h +++ b/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h @@ -1,8 +1,6 @@ #ifndef _PLAYERBOT_RAIDOSSTRATEGY_H #define _PLAYERBOT_RAIDOSSTRATEGY_H -#include "AiObjectContext.h" -#include "Multiplier.h" #include "Strategy.h" class RaidOsStrategy : public Strategy diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp index e2fe3cbe6d7..5b470ee5ca1 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp @@ -99,8 +99,12 @@ bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event /*event*/) if (bot->IsWithinDist2d(bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->radius)) return false; // Already safe + // Stop current spell first + bot->AttackStop(); + bot->InterruptNonMeleeSpells(false); + // bot->Yell("Moving to Safe Zone!", LANG_UNIVERSAL); - return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bot->GetPositionZ(), + return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->pos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h index 3943aaf6052..d5b8eafd999 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.h @@ -2,7 +2,6 @@ #ifndef _PLAYERBOT_RAIDONYXIAACTIONS_H_ #define _PLAYERBOT_RAIDONYXIAACTIONS_H_ -#include "Action.h" #include "AttackAction.h" #include "GenericSpellActions.h" #include "MovementActions.h" @@ -45,42 +44,45 @@ class RaidOnyxiaMoveToSafeZoneAction : public MovementAction bool Execute(Event event) override; private: - std::vector GetSafeZonesForBreath(uint32 spellId) + static std::vector GetSafeZonesForBreath(uint32 spellId) { - // Define your safe zone coordinates based on the map - // Example assumes Onyxia's lair map coordinates - float z = bot->GetPositionZ(); // Stay at current height + // Safe zone coordinates based on the map + // Assumes Onyxia's lair map coordinates switch (spellId) { case 17086: // N to S case 18351: // S to N - return {SafeZone{Position(-10.0f, -180.0f, z), 5.0f}, - SafeZone{Position(-20.0f, -250.0f, z), 5.0f}}; // Bottom Safe Zone + return { + SafeZone{Position(-10.0f, -180.0f, -87.0f), 5.0f}, + SafeZone{Position(-20.0f, -250.0f, -88.0f), 5.0f} + }; // Bottom Safe Zone case 18576: // E to W case 18609: // W to E return { - SafeZone{Position(20.0f, -210.0f, z), 5.0f}, - SafeZone{Position(-75.0f, -210.0f, z), 5.0f}, + SafeZone{Position(20.0f, -210.0f, -85.5f), 5.0f}, + SafeZone{Position(-75.0f, -210.0f, -83.4f), 5.0f}, }; // Left Safe Zone case 18564: // SE to NW case 18584: // NW to SE return { - SafeZone{Position(-60.0f, -195.0f, z), 5.0f}, - SafeZone{Position(10.0f, -240.0f, z), 5.0f}, + SafeZone{Position(-60.0f, -195.0f, -85.0f), 5.0f}, + SafeZone{Position(10.0f, -240.0f, -85.9f), 5.0f}, }; // NW Safe Zone case 18596: // SW to NE case 18617: // NE to SW return { - SafeZone{Position(7.0f, -185.0f, z), 5.0f}, - SafeZone{Position(-60.0f, -240.0f, z), 5.0f}, + SafeZone{Position(7.0f, -185.0f, -86.2f), 5.0f}, + SafeZone{Position(-60.0f, -240.0f, -85.2f), 5.0f}, }; // NE Safe Zone default: - return {SafeZone{Position(0.0f, 0.0f, z), 5.0f}}; // Fallback center - shouldn't ever happen + return { + SafeZone{Position(-40.0f, -214.0f, -86.6f), 5.0f} + }; // Fallback center - shouldn't ever happen } } }; diff --git a/src/Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h b/src/Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h index dba18f56420..daf624a0bad 100644 --- a/src/Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h +++ b/src/Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h @@ -1,7 +1,6 @@ #ifndef _PLAYERBOT_RAIDONYXIATRIGGERCONTEXT_H #define _PLAYERBOT_RAIDONYXIATRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidOnyxiaTriggers.h" diff --git a/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h b/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h index 737fd3a387a..5b0f8d5e3b1 100644 --- a/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h +++ b/src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h @@ -7,7 +7,7 @@ #define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H #include "RaidSSCTriggers.h" -#include "AiObjectContext.h" +#include "NamedObjectContext.h" class RaidSSCTriggerContext : public NamedObjectContext { diff --git a/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h b/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h index a994600ba76..08d315d5abe 100644 --- a/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h +++ b/src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h @@ -7,7 +7,6 @@ #define _PLAYERBOT_RAIDSSCSTRATEGY_H_ #include "Strategy.h" -#include "Multiplier.h" class RaidSSCStrategy : public Strategy { diff --git a/src/Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h b/src/Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h index c6b4922d7b7..0bf1d0fdcc8 100644 --- a/src/Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h +++ b/src/Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h @@ -2,7 +2,7 @@ #define _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERCONTEXT_H #include "RaidTempestKeepTriggers.h" -#include "AiObjectContext.h" +#include "NamedObjectContext.h" class RaidTempestKeepTriggerContext : public NamedObjectContext { diff --git a/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.h b/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.h index 77fd29c360a..b19600bab6e 100644 --- a/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.h +++ b/src/Ai/Raid/TempestKeep/Strategy/RaidTempestKeepStrategy.h @@ -2,7 +2,6 @@ #define _PLAYERBOT_RAIDTEMPESTKEEPSTRATEGY_H_ #include "Strategy.h" -#include "Multiplier.h" class RaidTempestKeepStrategy : public Strategy { diff --git a/src/Ai/Raid/Ulduar/RaidUlduarTriggerContext.h b/src/Ai/Raid/Ulduar/RaidUlduarTriggerContext.h index e4243fb1062..e093f579745 100644 --- a/src/Ai/Raid/Ulduar/RaidUlduarTriggerContext.h +++ b/src/Ai/Raid/Ulduar/RaidUlduarTriggerContext.h @@ -6,7 +6,6 @@ #ifndef _PLAYERBOT_RAIDULDUARTRIGGERCONTEXT_H #define _PLAYERBOT_RAIDULDUARTRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "NamedObjectContext.h" #include "RaidUlduarTriggers.h" #include "BossAuraTriggers.h" diff --git a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h index bb2feefe4af..c391f6bdb6f 100644 --- a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h +++ b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h @@ -2,7 +2,6 @@ #ifndef _PLAYERBOT_RAIDULDUARSTRATEGY_H #define _PLAYERBOT_RAIDULDUARSTRATEGY_H -#include "AiObjectContext.h" #include "Strategy.h" class RaidUlduarStrategy : public Strategy diff --git a/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h b/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h index 6566793fde1..6cb5e0f386b 100644 --- a/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h +++ b/src/Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h @@ -6,7 +6,6 @@ #ifndef _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H #define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H -#include "AiObjectContext.h" #include "BossAuraTriggers.h" #include "NamedObjectContext.h" #include "RaidVoATriggers.h" diff --git a/src/Ai/Raid/VaultOfArchavon/Strategy/RaidVoAStrategy.h b/src/Ai/Raid/VaultOfArchavon/Strategy/RaidVoAStrategy.h index 04ed2ac3a2f..c30261fe806 100644 --- a/src/Ai/Raid/VaultOfArchavon/Strategy/RaidVoAStrategy.h +++ b/src/Ai/Raid/VaultOfArchavon/Strategy/RaidVoAStrategy.h @@ -3,10 +3,6 @@ #define _PLAYERBOT_RAIDVOASTRATEGY_H #include "Strategy.h" -#include "PlayerbotAI.h" -#include "string" -#include "Trigger.h" -#include "vector" class RaidVoAStrategy : public Strategy { diff --git a/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h b/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h index 5be8bad7f08..cb8bac864c0 100644 --- a/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h +++ b/src/Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h @@ -7,7 +7,7 @@ #define _PLAYERBOT_RAIDZULAMANTRIGGERCONTEXT_H #include "RaidZulAmanTriggers.h" -#include "AiObjectContext.h" +#include "NamedObjectContext.h" class RaidZulAmanTriggerContext : public NamedObjectContext { diff --git a/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h b/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h index c49e088886d..2cb5e8171ea 100644 --- a/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h +++ b/src/Ai/Raid/ZulAman/Strategy/RaidZulAmanStrategy.h @@ -7,7 +7,6 @@ #define _PLAYERBOT_RAIDZULAMANSTRATEGY_H_ #include "Strategy.h" -#include "Multiplier.h" class RaidZulAmanStrategy : public Strategy { diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.cpp b/src/Ai/World/Rpg/Action/NewRpgAction.cpp index ca0ca243360..290be0c0af9 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgAction.cpp @@ -3,6 +3,7 @@ #include #include +#include "AreaDefines.h" #include "BroadcastHelper.h" #include "ChatHelper.h" #include "G3D/Vector2.h" @@ -468,10 +469,14 @@ bool NewRpgTravelFlightAction::Execute(Event /*event*/) data.inFlight = true; return false; } - Creature* flightMaster = ObjectAccessor::GetCreature(*bot, data.fromFlightMaster); + + if (bot->GetDistance(data.flightMasterPos) > INTERACTION_DISTANCE) + return MoveFarTo(data.flightMasterPos); + + Creature* flightMaster = bot->FindNearestCreature(data.flightMasterEntry, INTERACTION_DISTANCE * 3); if (!flightMaster || !flightMaster->IsAlive()) { - botAI->rpgInfo.ChangeToIdle(); + info.ChangeToIdle(); return true; } if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE) @@ -487,7 +492,8 @@ bool NewRpgTravelFlightAction::Execute(Event /*event*/) { LOG_DEBUG("playerbots", "[New RPG] {} active taxi path {} (from {} to {}) failed", bot->GetName(), flightMaster->GetEntry(), nodes[0], nodes[nodes.size() - 1]); - botAI->rpgInfo.ChangeToIdle(); + info.ChangeToIdle(); + return true; } return true; } diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 092b115387f..336c7599dea 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -1027,19 +1027,21 @@ WorldPosition NewRpgBaseAction::SelectRandomCampPos(Player* bot) return dest; } -bool NewRpgBaseAction::SelectRandomFlightTaxiNode(ObjectGuid& flightMaster, std::vector& path) +bool NewRpgBaseAction::SelectRandomFlightTaxiNode(uint32& flightMasterEntry, WorldPosition& flightMasterPos, std::vector& path) { - flightMaster = sTravelMgr.GetNearestFlightMasterGuid(bot); - if (!flightMaster) + TravelMgr::FlightMasterInfo const* info = sTravelMgr.GetNearestFlightMasterInfo(bot); + if (!info) return false; std::vector> availablePaths = sTravelMgr.GetOptimalFlightDestinations(bot); if (availablePaths.empty()) return false; + flightMasterEntry = info->templateEntry; + flightMasterPos = info->pos; path = availablePaths[urand(0, availablePaths.size() - 1)]; LOG_DEBUG("playerbots", "[New RPG] Bot {} select random flight taxi node from:{} (node {}) to:{} ({} available)", - bot->GetName(), flightMaster.GetEntry(), path[0], path[path.size() - 1], availablePaths.size()); + bot->GetName(), flightMasterEntry, path[0], path[path.size() - 1], availablePaths.size()); return true; } @@ -1139,11 +1141,12 @@ bool NewRpgBaseAction::RandomChangeStatus(std::vector candidateSta } case RPG_TRAVEL_FLIGHT: { - ObjectGuid flightMaster; + uint32 flightMasterEntry = 0; + WorldPosition flightMasterPos; std::vector path; - if (SelectRandomFlightTaxiNode(flightMaster, path)) + if (SelectRandomFlightTaxiNode(flightMasterEntry, flightMasterPos, path)) { - botAI->rpgInfo.ChangeToTravelFlight(flightMaster, path); + botAI->rpgInfo.ChangeToTravelFlight(flightMasterEntry, flightMasterPos, path); return true; } return false; @@ -1220,9 +1223,10 @@ bool NewRpgBaseAction::CheckRpgStatusAvailable(NewRpgStatus status) } case RPG_TRAVEL_FLIGHT: { - ObjectGuid flightMaster; + uint32 flightMasterEntry = 0; + WorldPosition flightMasterPos; std::vector path; - return SelectRandomFlightTaxiNode(flightMaster, path); + return SelectRandomFlightTaxiNode(flightMasterEntry, flightMasterPos, path); } case RPG_OUTDOOR_PVP: { diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.h b/src/Ai/World/Rpg/Action/NewRpgBaseAction.h index eaba7244628..e73a2321966 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.h +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.h @@ -54,7 +54,7 @@ class NewRpgBaseAction : public MovementAction bool GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector& poiInfo, bool toComplete = false); static WorldPosition SelectRandomGrindPos(Player* bot); static WorldPosition SelectRandomCampPos(Player* bot); - bool SelectRandomFlightTaxiNode(ObjectGuid& flightMaster, std::vector& path); + bool SelectRandomFlightTaxiNode(uint32& flightMasterEntry, WorldPosition& flightMasterPos, std::vector& path); bool RandomChangeStatus(std::vector candidateStatus); bool CheckRpgStatusAvailable(NewRpgStatus status); diff --git a/src/Ai/World/Rpg/NewRpgInfo.cpp b/src/Ai/World/Rpg/NewRpgInfo.cpp index 4935503fc25..780430f6db5 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.cpp +++ b/src/Ai/World/Rpg/NewRpgInfo.cpp @@ -37,11 +37,12 @@ void NewRpgInfo::ChangeToDoQuest(uint32 questId, const Quest* quest) data = do_quest; } -void NewRpgInfo::ChangeToTravelFlight(ObjectGuid fromFlightMaster, std::vector path) +void NewRpgInfo::ChangeToTravelFlight(uint32 flightMasterEntry, WorldPosition flightMasterPos, std::vector path) { startT = getMSTime(); TravelFlight flight; - flight.fromFlightMaster = fromFlightMaster; + flight.flightMasterEntry = flightMasterEntry; + flight.flightMasterPos = flightMasterPos; flight.path = std::move(path); flight.inFlight = false; data = flight; @@ -157,7 +158,7 @@ std::string NewRpgInfo::ToString() else if constexpr (std::is_same_v) { out << "TRAVEL_FLIGHT"; - out << "\nfromFlightMaster: " << arg.fromFlightMaster.GetEntry(); + out << "\nflightMasterEntry: " << arg.flightMasterEntry; out << "\nfromNode: " << arg.path[0]; out << "\ntoNode: " << arg.path[arg.path.size() - 1]; out << "\ninFlight: " << arg.inFlight; diff --git a/src/Ai/World/Rpg/NewRpgInfo.h b/src/Ai/World/Rpg/NewRpgInfo.h index 9e6abdda4c7..5896915a4d3 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.h +++ b/src/Ai/World/Rpg/NewRpgInfo.h @@ -49,7 +49,8 @@ struct NewRpgInfo // RPG_TRAVEL_FLIGHT struct TravelFlight { - ObjectGuid fromFlightMaster{}; + uint32 flightMasterEntry{0}; + WorldPosition flightMasterPos{}; std::vector path; bool inFlight{false}; }; @@ -96,7 +97,7 @@ struct NewRpgInfo void ChangeToWanderNpc(); void ChangeToWanderRandom(); void ChangeToDoQuest(uint32 questId, const Quest* quest); - void ChangeToTravelFlight(ObjectGuid fromFlightMaster, std::vector path); + void ChangeToTravelFlight(uint32 flightMasterEntry, WorldPosition flightMasterPos, std::vector path); void ChangeToOutdoorPvp(ObjectGuid::LowType capturePointSpawnId = 0); void ChangeToRest(); void ChangeToIdle(); diff --git a/src/Bot/Factory/RandomPlayerbotFactory.cpp b/src/Bot/Factory/RandomPlayerbotFactory.cpp index 617e4006cdf..5307151916f 100644 --- a/src/Bot/Factory/RandomPlayerbotFactory.cpp +++ b/src/Bot/Factory/RandomPlayerbotFactory.cpp @@ -619,7 +619,7 @@ void RandomPlayerbotFactory::CreateRandomBots() else password = accountName; - AccountMgr::CreateAccount(accountName, password); + sAccountMgr->CreateAccount(accountName, password); LOG_DEBUG("playerbots", "Account {} created for random bots", accountName.c_str()); } diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 357678928fe..8feb87cb585 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -54,9 +54,9 @@ #include "Unit.h" #include "UpdateTime.h" #include "Vehicle.h" -#include "../../../../src/server/scripts/Spells/spell_dk.cpp" -const int SPELL_TITAN_GRIP = 49152; +constexpr uint32 SPELL_TITAN_GRIP = 49152; +constexpr uint32 SPELL_DK_FROST_PRESENCE = 48263; std::vector PlayerbotAI::dispel_whitelist = { "mutating injection", diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 5c0922fb9ed..3c0a9054c09 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -2077,7 +2077,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot) bot->DurabilityRepairAll(false, 1.0f, false); bot->SetFullHealth(); - bot->SetPvP(true); + bot->SetPvP(sWorld->IsPvPRealm()); PlayerbotFactory factory(bot, bot->GetLevel()); factory.Refresh(); @@ -2642,6 +2642,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) { // ObjectGuid::LowType guid = player->GetGUID().GetCounter(); //not used, conditional could be rewritten for // simplicity. line marked for removal. + player->SetPvP(sWorld->IsPvPRealm()); } else { diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index 1868bc2e386..adc1e4a3edd 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -8,6 +8,7 @@ #include #include +#include "AreaDefines.h" #include "Creature.h" #include "Log.h" #include "ObjectAccessor.h" @@ -28,67 +29,60 @@ // Navigation data -enum class CityId : uint8 -{ - STORMWIND, - IRONFORGE, - DARNASSUS, - EXODAR, - ORGRIMMAR, - UNDERCITY, - THUNDER_BLUFF, - SILVERMOON_CITY, - SHATTRATH_CITY, - DALARAN +struct Capital +{ + uint32 zoneId; + TeamId team; + char const* name; + std::vector bankers; }; -static const std::unordered_map> bankerToCity = { - {2455, {CityId::STORMWIND, TEAM_ALLIANCE}}, {2456, {CityId::STORMWIND, TEAM_ALLIANCE}}, {2457, {CityId::STORMWIND, TEAM_ALLIANCE}}, - {2460, {CityId::IRONFORGE, TEAM_ALLIANCE}}, {2461, {CityId::IRONFORGE, TEAM_ALLIANCE}}, {5099, {CityId::IRONFORGE, TEAM_ALLIANCE}}, - {4155, {CityId::DARNASSUS, TEAM_ALLIANCE}}, {4208, {CityId::DARNASSUS, TEAM_ALLIANCE}}, {4209, {CityId::DARNASSUS, TEAM_ALLIANCE}}, - {17773, {CityId::EXODAR, TEAM_ALLIANCE}}, {18350, {CityId::EXODAR, TEAM_ALLIANCE}}, {16710, {CityId::EXODAR, TEAM_ALLIANCE}}, - {3320, {CityId::ORGRIMMAR, TEAM_HORDE}}, {3309, {CityId::ORGRIMMAR, TEAM_HORDE}}, {3318, {CityId::ORGRIMMAR, TEAM_HORDE}}, - {4549, {CityId::UNDERCITY, TEAM_HORDE}}, {2459, {CityId::UNDERCITY, TEAM_HORDE}}, {2458, {CityId::UNDERCITY, TEAM_HORDE}}, {4550, {CityId::UNDERCITY, TEAM_HORDE}}, - {2996, {CityId::THUNDER_BLUFF, TEAM_HORDE}}, {8356, {CityId::THUNDER_BLUFF, TEAM_HORDE}}, {8357, {CityId::THUNDER_BLUFF, TEAM_HORDE}}, - {17631, {CityId::SILVERMOON_CITY, TEAM_HORDE}}, {17632, {CityId::SILVERMOON_CITY, TEAM_HORDE}}, {17633, {CityId::SILVERMOON_CITY, TEAM_HORDE}}, - {16615, {CityId::SILVERMOON_CITY, TEAM_HORDE}}, {16616, {CityId::SILVERMOON_CITY, TEAM_HORDE}}, {16617, {CityId::SILVERMOON_CITY, TEAM_HORDE}}, - {19246, {CityId::SHATTRATH_CITY, TEAM_NEUTRAL}}, {19338, {CityId::SHATTRATH_CITY, TEAM_NEUTRAL}}, - {19034, {CityId::SHATTRATH_CITY, TEAM_NEUTRAL}}, {19318, {CityId::SHATTRATH_CITY, TEAM_NEUTRAL}}, - {30604, {CityId::DALARAN, TEAM_NEUTRAL}}, {30605, {CityId::DALARAN, TEAM_NEUTRAL}}, {30607, {CityId::DALARAN, TEAM_NEUTRAL}}, - {28675, {CityId::DALARAN, TEAM_NEUTRAL}}, {28676, {CityId::DALARAN, TEAM_NEUTRAL}}, {28677, {CityId::DALARAN, TEAM_NEUTRAL}} +static const std::vector capitals = { + { AREA_STORMWIND_CITY, TEAM_ALLIANCE, "Stormwind", {2455, 2456, 2457} }, + { AREA_IRONFORGE, TEAM_ALLIANCE, "Ironforge", {2460, 2461, 5099} }, + { AREA_DARNASSUS, TEAM_ALLIANCE, "Darnassus", {4155, 4208, 4209} }, + { AREA_THE_EXODAR, TEAM_ALLIANCE, "Exodar", {17773, 18350, 16710} }, + { AREA_ORGRIMMAR, TEAM_HORDE, "Orgrimmar", {3320, 3309, 3318} }, + { AREA_UNDERCITY, TEAM_HORDE, "Undercity", {4549, 2459, 2458, 4550} }, + { AREA_THUNDER_BLUFF, TEAM_HORDE, "Thunder Bluff", {2996, 8356, 8357} }, + { AREA_SILVERMOON_CITY, TEAM_HORDE, "Silvermoon", {17631, 17632, 17633, 16615, 16616, 16617} }, + { AREA_SHATTRATH_CITY, TEAM_NEUTRAL, "Shattrath", {19246, 19338, 19034, 19318} }, + { AREA_DALARAN, TEAM_NEUTRAL, "Dalaran", {30604, 30605, 30607, 28675, 28676, 28677, 29530} } }; -static const std::unordered_map> cityToBankers = { - {CityId::STORMWIND, {2455, 2456, 2457}}, - {CityId::IRONFORGE, {2460, 2461, 5099}}, - {CityId::DARNASSUS, {4155, 4208, 4209}}, - {CityId::EXODAR, {17773, 18350, 16710}}, - {CityId::ORGRIMMAR, {3320, 3309, 3318}}, - {CityId::UNDERCITY, {4549, 2459, 2458, 4550}}, - {CityId::THUNDER_BLUFF, {2996, 8356, 8357}}, - {CityId::SILVERMOON_CITY, {17631, 17632, 17633, 16615, 16616, 16617}}, - {CityId::SHATTRATH_CITY, {19246, 19338, 19034, 19318}}, - {CityId::DALARAN, {30604, 30605, 30607, 28675, 28676, 28677, 29530}} -}; +static Capital const* FindCapitalByZone(uint32 zoneId) +{ + for (Capital const& capital : capitals) + if (capital.zoneId == zoneId) + return &capital; + return nullptr; +} + +static Capital const* FindCapitalByBanker(uint16 bankerEntry) +{ + for (Capital const& capital : capitals) + for (uint16 bankerId : capital.bankers) + if (bankerId == bankerEntry) + return &capital; + return nullptr; +} -static int GetCityWeight(CityId city) +static int GetCityWeight(uint32 zoneId) { - int weight = 0; - switch (city) + switch (zoneId) { - case CityId::STORMWIND: weight = sPlayerbotAIConfig.weightTeleToStormwind; break; - case CityId::IRONFORGE: weight = sPlayerbotAIConfig.weightTeleToIronforge; break; - case CityId::DARNASSUS: weight = sPlayerbotAIConfig.weightTeleToDarnassus; break; - case CityId::EXODAR: weight = sPlayerbotAIConfig.weightTeleToExodar; break; - case CityId::ORGRIMMAR: weight = sPlayerbotAIConfig.weightTeleToOrgrimmar; break; - case CityId::UNDERCITY: weight = sPlayerbotAIConfig.weightTeleToUndercity; break; - case CityId::THUNDER_BLUFF: weight = sPlayerbotAIConfig.weightTeleToThunderBluff; break; - case CityId::SILVERMOON_CITY: weight = sPlayerbotAIConfig.weightTeleToSilvermoonCity; break; - case CityId::SHATTRATH_CITY: weight = sPlayerbotAIConfig.weightTeleToShattrathCity; break; - case CityId::DALARAN: weight = sPlayerbotAIConfig.weightTeleToDalaran; break; - default: weight = 0; break; + case AREA_STORMWIND_CITY: return sPlayerbotAIConfig.weightTeleToStormwind; + case AREA_IRONFORGE: return sPlayerbotAIConfig.weightTeleToIronforge; + case AREA_DARNASSUS: return sPlayerbotAIConfig.weightTeleToDarnassus; + case AREA_THE_EXODAR: return sPlayerbotAIConfig.weightTeleToExodar; + case AREA_ORGRIMMAR: return sPlayerbotAIConfig.weightTeleToOrgrimmar; + case AREA_UNDERCITY: return sPlayerbotAIConfig.weightTeleToUndercity; + case AREA_THUNDER_BLUFF: return sPlayerbotAIConfig.weightTeleToThunderBluff; + case AREA_SILVERMOON_CITY: return sPlayerbotAIConfig.weightTeleToSilvermoonCity; + case AREA_SHATTRATH_CITY: return sPlayerbotAIConfig.weightTeleToShattrathCity; + case AREA_DALARAN: return sPlayerbotAIConfig.weightTeleToDalaran; } - return weight; + return 0; } WorldPosition::WorldPosition(std::string const str) @@ -4369,76 +4363,117 @@ void TravelMgr::Init() LOG_INFO("playerbots", "Playerbots Taxi graph and destination cache built."); } -Creature* TravelMgr::GetNearestFlightMaster(Player* bot) +TravelMgr::FlightMasterInfo const* TravelMgr::GetNearestFlightMasterInfo(Player* bot) const { - std::map& flightMasterCache = + auto const& flightMasterCache = (bot->GetTeamId() == TEAM_ALLIANCE) ? allianceFlightMasterCache : hordeFlightMasterCache; - Creature* nearestFlightMaster = nullptr; + FlightMasterInfo const* nearest = nullptr; float nearestDistance = std::numeric_limits::max(); - for (auto const& [entry, pos] : flightMasterCache) + for (auto const& [dbGuid, info] : flightMasterCache) { - if (pos.GetMapId() != bot->GetMapId()) - continue; - - float distance = bot->GetExactDist2dSq(pos); - if (distance > nearestDistance) + if (info.pos.GetMapId() != bot->GetMapId()) continue; - Creature* flightMaster = ObjectAccessor::GetSpawnedCreatureByDBGUID(bot->GetMapId(), entry); - if (flightMaster) + float distance = bot->GetExactDist2dSq(info.pos); + if (distance < nearestDistance) { nearestDistance = distance; - nearestFlightMaster = flightMaster; + nearest = &info; } } - return nearestFlightMaster; + return nearest; } -ObjectGuid TravelMgr::GetNearestFlightMasterGuid(Player* bot) +std::vector TravelMgr::GetFlightNodesInZone(uint32 zoneId, TeamId team, uint32 excludeNode) const { - Creature* nearestFlightMaster = GetNearestFlightMaster(bot); - if (!nearestFlightMaster) - return ObjectGuid::Empty; - - return nearestFlightMaster->GetGUID(); + auto const& cache = (team == TEAM_ALLIANCE) ? allianceFlightMasterCache : hordeFlightMasterCache; + std::unordered_set seen; + std::vector result; + for (auto const& [entry, info] : cache) + { + if (info.zoneId != zoneId || info.taxiNodeId == 0 || info.taxiNodeId == excludeNode) + continue; + if (seen.insert(info.taxiNodeId).second) + result.push_back(info.taxiNodeId); + } + return result; } std::vector> TravelMgr::GetOptimalFlightDestinations(Player* bot) { std::vector> validDestinations; - Creature* nearestFlightMaster = GetNearestFlightMaster(bot); - if (!nearestFlightMaster || bot->GetDistance(nearestFlightMaster) > 500.0f) + FlightMasterInfo const* nearestFlightMaster = GetNearestFlightMasterInfo(bot); + if (!nearestFlightMaster || bot->GetDistance(nearestFlightMaster->pos) > 500.0f) return validDestinations; - uint32 fromNode = sObjectMgr->GetNearestTaxiNode(nearestFlightMaster->GetPositionX(), nearestFlightMaster->GetPositionY(), - nearestFlightMaster->GetPositionZ(), nearestFlightMaster->GetMapId(), - bot->GetTeamId()); + uint32 fromNode = nearestFlightMaster->taxiNodeId; if (!fromNode) return validDestinations; - std::vector candidateLocations; - if (bot->GetLevel() >= 10 && urand(0, 100) < sPlayerbotAIConfig.probTeleToBankers * 100) - candidateLocations = GetCityLocations(bot); + TaxiNodesEntry const* startNode = sTaxiNodesStore.LookupEntry(fromNode); + if (!startNode) + return validDestinations; + + uint32 botLevel = bot->GetLevel(); - std::vector hubLocations = GetTravelHubs(bot); - candidateLocations.insert(candidateLocations.end(), hubLocations.begin(), hubLocations.end()); + // Bots already in a capital shouldn't have another capital picked as a + // flight destination — that just shuffles them between cities. + bool botInCapital = false; + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetZoneId())) + botInCapital = (area->flags & AREA_FLAG_CAPITAL) != 0; - for (auto const& loc : candidateLocations) + //Simplify destination delection. Its either target cities (Based on config value) or target world. + std::vector candidateZones; + if (botLevel >= 10 && !botInCapital && urand(0, 100) < sPlayerbotAIConfig.probTeleToBankers * 100) { - uint32 candidateNode = sObjectMgr->GetNearestTaxiNode(loc.GetPositionX(), loc.GetPositionY(), - loc.GetPositionZ(), loc.GetMapId(), - bot->GetTeamId()); - if (!candidateNode) - continue; + TeamId botTeam = bot->GetTeamId(); + for (Capital const& capital : capitals) + { + if (capital.team != TEAM_NEUTRAL && capital.team != botTeam) + continue; + candidateZones.push_back(capital.zoneId); + } + } + if (candidateZones.empty()) + { + for (auto const& [zoneId, bracket] : zone2LevelBracket) + { + if (botLevel < bracket.low || botLevel > bracket.high) + continue; + if (GetFlightNodesInZone(zoneId, bot->GetTeamId(), fromNode).empty()) + continue; + candidateZones.push_back(zoneId); + } + } - std::vector path = sTravelNodeMap.FindTaxiPath(fromNode, candidateNode); - if (!path.empty()) - validDestinations.push_back(path); + if (candidateZones.empty()) + return validDestinations; + + while (!candidateZones.empty()) + { + uint32 zoneIndex = urand(0, candidateZones.size() - 1); + uint32 pickedZone = candidateZones[zoneIndex]; + + std::vector usableNodes = GetFlightNodesInZone(pickedZone, bot->GetTeamId(), fromNode); + + if (!usableNodes.empty()) + { + uint32 pickedNode = usableNodes[urand(0, usableNodes.size() - 1)]; + std::vector path = sTravelNodeMap.FindTaxiPath(fromNode, pickedNode); + if (!path.empty()) + { + validDestinations.push_back(std::move(path)); + return validDestinations; + } + } + + candidateZones.erase(candidateZones.begin() + zoneIndex); } + return validDestinations; } @@ -4472,34 +4507,34 @@ std::vector TravelMgr::GetCityLocations(Player* bot) return fallbackLocations; TeamId botTeamId = bot->GetTeamId(); - std::unordered_set validBankerCities; + std::unordered_set validBankerCities; for (auto& loc : bankerLocsPerLevelCache[level]) { - auto cityIt = bankerToCity.find(loc.entry); - if (cityIt == bankerToCity.end()) + Capital const* capital = FindCapitalByBanker(loc.entry); + if (!capital) continue; - TeamId cityTeamId = cityIt->second.second; + TeamId cityTeamId = capital->team; if (cityTeamId == botTeamId || (cityTeamId == TEAM_NEUTRAL) ) - validBankerCities.insert(cityIt->second.first); + validBankerCities.insert(capital->zoneId); } // Fallback if no valid cities if (validBankerCities.empty()) return fallbackLocations; // Apply weights to valid cities - std::vector weightedCities; - for (CityId city : validBankerCities) + std::vector weightedCities; + for (uint32 zoneId : validBankerCities) { - int weight = GetCityWeight(city); + int weight = GetCityWeight(zoneId); if (weight <= 0) continue; for (int i = 0; i < weight; ++i) - weightedCities.push_back(city); + weightedCities.push_back(zoneId); } // Fallback if no valid cities @@ -4507,9 +4542,11 @@ std::vector TravelMgr::GetCityLocations(Player* bot) return fallbackLocations; // Pick a weighted city randomly, then a random banker in that city - CityId selectedCity = weightedCities[urand(0, weightedCities.size() - 1)]; - - auto const& bankers = cityToBankers.at(selectedCity); + uint32 selectedCity = weightedCities[urand(0, weightedCities.size() - 1)]; + Capital const* selectedCapital = FindCapitalByZone(selectedCity); + if (!selectedCapital) + return fallbackLocations; + auto const& bankers = selectedCapital->bankers; uint32 selectedBankerEntry = bankers[urand(0, bankers.size() - 1)]; auto locIt = bankerEntryToLocation.find(selectedBankerEntry); if (locIt != bankerEntryToLocation.end()) @@ -4520,78 +4557,78 @@ std::vector TravelMgr::GetCityLocations(Player* bot) void TravelMgr::PrepareZone2LevelBracket() { - // Classic WoW - Low - level zones - zone2LevelBracket[1] = {5, 12}; // Dun Morogh - zone2LevelBracket[12] = {5, 12}; // Elwynn Forest - zone2LevelBracket[14] = {5, 12}; // Durotar - zone2LevelBracket[85] = {5, 12}; // Tirisfal Glades - zone2LevelBracket[141] = {5, 12}; // Teldrassil - zone2LevelBracket[215] = {5, 12}; // Mulgore - zone2LevelBracket[3430] = {5, 12}; // Eversong Woods - zone2LevelBracket[3524] = {5, 12}; // Azuremyst Isle - - // Classic WoW - Mid - level zones - zone2LevelBracket[17] = {10, 25}; // Barrens - zone2LevelBracket[38] = {10, 20}; // Loch Modan - zone2LevelBracket[40] = {10, 21}; // Westfall - zone2LevelBracket[130] = {10, 23}; // Silverpine Forest - zone2LevelBracket[148] = {10, 21}; // Darkshore - zone2LevelBracket[3433] = {10, 22}; // Ghostlands - zone2LevelBracket[3525] = {10, 21}; // Bloodmyst Isle - - // Classic WoW - High - level zones - zone2LevelBracket[10] = {19, 33}; // Deadwind Pass - zone2LevelBracket[11] = {21, 30}; // Wetlands - zone2LevelBracket[44] = {16, 28}; // Redridge Mountains - zone2LevelBracket[267] = {20, 34}; // Hillsbrad Foothills - zone2LevelBracket[331] = {18, 33}; // Ashenvale - zone2LevelBracket[400] = {24, 36}; // Thousand Needles - zone2LevelBracket[406] = {16, 29}; // Stonetalon Mountains - - // Classic WoW - Higher - level zones - zone2LevelBracket[3] = {36, 46}; // Badlands - zone2LevelBracket[8] = {36, 46}; // Swamp of Sorrows - zone2LevelBracket[15] = {35, 46}; // Dustwallow Marsh - zone2LevelBracket[16] = {45, 52}; // Azshara - zone2LevelBracket[33] = {32, 47}; // Stranglethorn Vale - zone2LevelBracket[45] = {30, 42}; // Arathi Highlands - zone2LevelBracket[47] = {42, 51}; // Hinterlands - zone2LevelBracket[51] = {45, 51}; // Searing Gorge - zone2LevelBracket[357] = {40, 52}; // Feralas - zone2LevelBracket[405] = {30, 41}; // Desolace - zone2LevelBracket[440] = {41, 52}; // Tanaris - - // Classic WoW - Top - level zones - zone2LevelBracket[4] = {52, 57}; // Blasted Lands - zone2LevelBracket[28] = {50, 60}; // Western Plaguelands - zone2LevelBracket[46] = {51, 60}; // Burning Steppes - zone2LevelBracket[139] = {54, 62}; // Eastern Plaguelands - zone2LevelBracket[361] = {47, 57}; // Felwood - zone2LevelBracket[490] = {49, 56}; // Un'Goro Crater - zone2LevelBracket[618] = {54, 61}; // Winterspring - zone2LevelBracket[1377] = {54, 63}; // Silithus - - // The Burning Crusade - Zones - zone2LevelBracket[3483] = {58, 66}; // Hellfire Peninsula - zone2LevelBracket[3518] = {64, 70}; // Nagrand - zone2LevelBracket[3519] = {62, 73}; // Terokkar Forest - zone2LevelBracket[3520] = {66, 73}; // Shadowmoon Valley - zone2LevelBracket[3521] = {60, 67}; // Zangarmarsh - zone2LevelBracket[3522] = {64, 73}; // Blade's Edge Mountains - zone2LevelBracket[3523] = {67, 73}; // Netherstorm - zone2LevelBracket[4080] = {68, 73}; // Isle of Quel'Danas - - // Wrath of the Lich King - Zones - zone2LevelBracket[65] = {71, 77}; // Dragonblight - zone2LevelBracket[66] = {74, 80}; // Zul'Drak - zone2LevelBracket[67] = {77, 80}; // Storm Peaks - zone2LevelBracket[210] = {77, 80}; // Icecrown Glacier - zone2LevelBracket[394] = {72, 78}; // Grizzly Hills - zone2LevelBracket[495] = {68, 74}; // Howling Fjord - zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest - zone2LevelBracket[3537] = {68, 75}; // Borean Tundra - zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin - zone2LevelBracket[4197] = {79, 80}; // Wintergrasp + // Classic WoW - starter zones + zone2LevelBracket[AREA_DUN_MOROGH] = {5, 12}; + zone2LevelBracket[AREA_ELWYNN_FOREST] = {5, 12}; + zone2LevelBracket[AREA_DUROTAR] = {5, 12}; + zone2LevelBracket[AREA_TIRISFAL_GLADES] = {5, 12}; + zone2LevelBracket[AREA_TELDRASSIL] = {5, 12}; + zone2LevelBracket[AREA_MULGORE] = {5, 12}; + zone2LevelBracket[AREA_EVERSONG_WOODS] = {5, 12}; + zone2LevelBracket[AREA_AZUREMYST_ISLE] = {5, 12}; + + // Classic WoW - low level zones + zone2LevelBracket[AREA_THE_BARRENS] = {10, 25}; + zone2LevelBracket[AREA_LOCH_MODAN] = {10, 20}; + zone2LevelBracket[AREA_WESTFALL] = {10, 21}; + zone2LevelBracket[AREA_SILVERPINE_FOREST] = {10, 23}; + zone2LevelBracket[AREA_DARKSHORE] = {10, 21}; + zone2LevelBracket[AREA_GHOSTLANDS] = {10, 22}; + zone2LevelBracket[AREA_BLOODMYST_ISLE] = {10, 21}; + + // Classic WoW - mid-level zones + zone2LevelBracket[AREA_DUSKWOOD] = {19, 33}; + zone2LevelBracket[AREA_WETLANDS] = {21, 30}; + zone2LevelBracket[AREA_REDRIDGE_MOUNTAINS] = {16, 28}; + zone2LevelBracket[AREA_HILLSBRAD_FOOTHILLS] = {20, 34}; + zone2LevelBracket[AREA_ASHENVALE] = {18, 33}; + zone2LevelBracket[AREA_THOUSAND_NEEDLES] = {24, 36}; + zone2LevelBracket[AREA_STONETALON_MOUNTAINS] = {16, 29}; + + // Classic WoW - 30-52 zones + zone2LevelBracket[AREA_BADLANDS] = {36, 46}; + zone2LevelBracket[AREA_SWAMP_OF_SORROWS] = {36, 46}; + zone2LevelBracket[AREA_DUSTWALLOW_MARSH] = {35, 46}; + zone2LevelBracket[AREA_AZSHARA] = {45, 52}; + zone2LevelBracket[AREA_STRANGLETHORN_VALE] = {32, 47}; + zone2LevelBracket[AREA_ARATHI_HIGHLANDS] = {30, 42}; + zone2LevelBracket[AREA_THE_HINTERLANDS] = {42, 51}; + zone2LevelBracket[AREA_SEARING_GORGE] = {45, 51}; + zone2LevelBracket[AREA_FERALAS] = {40, 52}; + zone2LevelBracket[AREA_DESOLACE] = {30, 41}; + zone2LevelBracket[AREA_TANARIS] = {41, 52}; + + // Classic WoW - top level zones + zone2LevelBracket[AREA_BLASTED_LANDS] = {52, 57}; + zone2LevelBracket[AREA_WESTERN_PLAGUELANDS] = {50, 60}; + zone2LevelBracket[AREA_BURNING_STEPPES] = {51, 60}; + zone2LevelBracket[AREA_EASTERN_PLAGUELANDS] = {54, 62}; + zone2LevelBracket[361] = {47, 57}; // Felwood (no AREA_ define) + zone2LevelBracket[490] = {49, 56}; // Un'Goro Crater (no AREA_ define) + zone2LevelBracket[AREA_WINTERSPRING] = {54, 61}; + zone2LevelBracket[AREA_SILITHUS] = {54, 63}; + + // The Burning Crusade zones + zone2LevelBracket[AREA_HELLFIRE_PENINSULA] = {58, 66}; + zone2LevelBracket[AREA_NAGRAND] = {64, 70}; + zone2LevelBracket[AREA_TEROKKAR_FOREST] = {62, 73}; + zone2LevelBracket[AREA_SHADOWMOON_VALLEY] = {66, 73}; + zone2LevelBracket[AREA_ZANGARMARSH] = {60, 67}; + zone2LevelBracket[AREA_BLADES_EDGE_MOUNTAINS] = {64, 73}; + zone2LevelBracket[AREA_NETHERSTORM] = {67, 73}; + zone2LevelBracket[AREA_ISLE_OF_QUEL_DANAS] = {68, 73}; + + // Wrath of the Lich King zones + zone2LevelBracket[AREA_DRAGONBLIGHT] = {71, 77}; + zone2LevelBracket[AREA_ZUL_DRAK] = {74, 80}; + zone2LevelBracket[AREA_THE_STORM_PEAKS] = {77, 80}; + zone2LevelBracket[210] = {77, 80}; // Icecrown Glacier (no AREA_ define) + zone2LevelBracket[AREA_GRIZZLY_HILLS] = {72, 78}; + zone2LevelBracket[AREA_HOWLING_FJORD] = {68, 74}; + zone2LevelBracket[AREA_CRYSTALSONG_FOREST] = {77, 80}; + zone2LevelBracket[AREA_BOREAN_TUNDRA] = {68, 75}; + zone2LevelBracket[AREA_SHOLAZAR_BASIN] = {75, 80}; + zone2LevelBracket[AREA_WINTERGRASP] = {79, 80}; // Override with values from config for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig.zoneBrackets) @@ -4650,13 +4687,15 @@ void TravelMgr::PrepareDestinationCache() (creatureTemplate->unit_flags & 4096) == 0 && creatureTemplate->rank == 0) { - uint32 roundX = (x / 50.0f) * 10.0f; - uint32 roundY = (y / 50.0f) * 10.0f; - uint32 roundZ = (z / 50.0f) * 10.0f; + uint32 roundX = static_cast(std::round(x / 50.0f)); + uint32 roundY = static_cast(std::round(y / 50.0f)); + uint32 roundZ = static_cast(std::round(z / 50.0f)); tempLocsCache[std::make_tuple(mapId, roundX, roundY, roundZ)].push_back(creatureData); tempCreatureCache[templateEntry][areaId].push_back(WorldLocation(mapId, x, y, z)); } // FLIGHT MASTERS + // Entry 29480 is Grimwing (Storm Peaks) + // Entry 3838 is Vesprystus in Rut'Theran. Need Travel Node system to resolve this one. else if ((creatureTemplate->npcflag & UNIT_NPC_FLAG_FLIGHTMASTER || creatureTemplate->npcflag & UNIT_NPC_FLAG_INNKEEPER) && creatureTemplate->Entry != 3838 && creatureTemplate->Entry != 29480) @@ -4669,23 +4708,39 @@ void TravelMgr::PrepareDestinationCache() { WorldPosition pos(mapId, x, y, z, orient); if (forHorde) - hordeFlightMasterCache[guid] = pos; + { + FlightMasterInfo info; + info.pos = pos; + info.zoneId = areaId; + info.taxiNodeId = sObjectMgr->GetNearestTaxiNode(x, y, z, mapId, TEAM_HORDE); + info.templateEntry = templateEntry; + info.dbGuid = guid; + hordeFlightMasterCache[guid] = info; + } if (forAlliance) - allianceFlightMasterCache[guid] = pos; + { + FlightMasterInfo info; + info.pos = pos; + info.zoneId = areaId; + info.taxiNodeId = sObjectMgr->GetNearestTaxiNode(x, y, z, mapId, TEAM_ALLIANCE); + info.templateEntry = templateEntry; + info.dbGuid = guid; + allianceFlightMasterCache[guid] = info; + } flightMastersCount++; // Zones that have flight masters but no innkeepers — use flight master as hub static const std::set zonesWithoutInnkeeper = { - 4, // Blasted Lands (52-57) - 16, // Azshara (45-52) - 28, // Western Plaguelands (50-60) - 46, // Burning Steppes (51-60) - 51, // Searing Gorge (45-51) + AREA_BLASTED_LANDS, + AREA_AZSHARA, + AREA_WESTERN_PLAGUELANDS, + AREA_BURNING_STEPPES, + AREA_SEARING_GORGE, 361, // Felwood (47-57) 490, // Un'Goro Crater (49-56) - 2817, // Crystalsong Forest (77-80) - 4197 // Wintergrasp (79-80) + AREA_CRYSTALSONG_FOREST, + AREA_WINTERGRASP }; if (zonesWithoutInnkeeper.count(areaId)) { @@ -4756,7 +4811,7 @@ void TravelMgr::PrepareDestinationCache() // Process temporary caches for (auto const& [gridTuple, creatureDataList] : tempLocsCache) { - if (creatureDataList.size() > 2) + if (creatureDataList.size() >= 2) { CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureDataList[0].id1); uint32 level = (creatureTemplate->minlevel + creatureTemplate->maxlevel + 1) / 2; diff --git a/src/Mgr/Travel/TravelMgr.h b/src/Mgr/Travel/TravelMgr.h index f300ae6361f..99c8c8e4c01 100644 --- a/src/Mgr/Travel/TravelMgr.h +++ b/src/Mgr/Travel/TravelMgr.h @@ -846,6 +846,21 @@ class TravelTarget : AiObject class TravelMgr { public: + struct NpcLocation + { + WorldLocation loc; + uint32 entry; + }; + + struct FlightMasterInfo + { + WorldPosition pos; + uint32 zoneId; // resolved once at cache load + uint32 taxiNodeId; // DBC taxi node nearest to this flight master + uint32 templateEntry; // creature template ID (for ObjectGuid construction) + uint32 dbGuid; // DB spawn GUID (for ObjectGuid construction) + }; + static TravelMgr& instance() { static TravelMgr instance; @@ -858,12 +873,14 @@ class TravelMgr // Navigation void Init(); - Creature* GetNearestFlightMaster(Player* bot); - ObjectGuid GetNearestFlightMasterGuid(Player* bot); + + FlightMasterInfo const* GetNearestFlightMasterInfo(Player* bot) const; std::vector> GetOptimalFlightDestinations(Player* bot); const std::vector GetTeleportLocations(Player* bot); const std::vector GetTravelHubs(Player* bot); std::vector GetCityLocations(Player* bot); + std::vector GetFlightNodesInZone(uint32 zoneId, TeamId team, uint32 excludeNode = 0) const; + bool SelectAuctioneerByMap(Player* bot, NpcLocation& outAuctioneer); const std::vector& GetLocsPerLevelCache(uint8 level) { return locsPerLevelCache[level]; } template @@ -975,8 +992,8 @@ class TravelMgr }; // Navigation caches - std::map allianceFlightMasterCache; - std::map hordeFlightMasterCache; + std::map allianceFlightMasterCache; + std::map hordeFlightMasterCache; std::map> allianceHubsPerLevelCache; std::map> hordeHubsPerLevelCache; std::map> bankerLocsPerLevelCache; diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index 3b4996e974e..9d25d4ea755 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -2467,7 +2467,7 @@ std::vector TravelNodeMap::FindTaxiPath(uint32 fromNode, uint32 toNode) TaxiNodesEntry const* startNode = sTaxiNodesStore.LookupEntry(fromNode); TaxiNodesEntry const* endNode = sTaxiNodesStore.LookupEntry(toNode); - if (!startNode || !endNode || startNode->map_id != endNode->map_id) + if (!startNode || !endNode) return {}; auto cacheItr = taxiPathCache.find(fromNode);