diff --git a/src/Ai/Base/Actions/CheckMountStateAction.cpp b/src/Ai/Base/Actions/CheckMountStateAction.cpp index 0d7fe4321..0e3abb724 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.cpp +++ b/src/Ai/Base/Actions/CheckMountStateAction.cpp @@ -4,9 +4,11 @@ */ #include "CheckMountStateAction.h" +#include "AreaDefines.h" #include "BattleGroundTactics.h" #include "BattlegroundEY.h" #include "BattlegroundWS.h" +#include "DBCStores.h" #include "Event.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" @@ -14,6 +16,8 @@ #include "ServerFacade.h" #include "SpellAuraEffects.h" +static constexpr uint32 SPELL_COLD_WEATHER_FLYING = 54197; + // Define the static map / init bool for caching bot preferred mount data globally std::unordered_map CheckMountStateAction::mountCache; bool CheckMountStateAction::preferredMountTableChecked = false; @@ -94,9 +98,10 @@ bool CheckMountStateAction::Execute(Event /*event*/) } bool inBattleground = bot->InBattleground(); + bool const noRealMaster = (!master || master == bot); // If there is a master and bot not in BG, follow master's mount state regardless of group leader - if (master && !inBattleground) + if (!noRealMaster && !inBattleground) { if (ShouldFollowMasterMountState(master, noAttackers, shouldMount)) return Mount(); @@ -110,8 +115,8 @@ bool CheckMountStateAction::Execute(Event /*event*/) return false; } - // If there is no master or bot in BG - if ((!master || inBattleground) && !bot->IsMounted() && + // No real master (random bot or self-bot) OR bot in BG + if ((noRealMaster || inBattleground) && !bot->IsMounted() && noAttackers && shouldMount && !bot->IsInCombat()) return Mount(); @@ -228,6 +233,39 @@ void CheckMountStateAction::Dismount() WorldPacket emptyPacket; bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + + bool const wantsFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + bool const isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); + bool const isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); + bool const hasGravityDisabled = bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); + if (!wantsFly && !isWaterWalking && (isFlying || hasGravityDisabled)) + { + bot->RemoveUnitMovementFlag( + MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY); + if (!bot->IsRooted()) + bot->SendMovementFlagUpdate(); + } +} + +void CheckMountStateAction::CompleteDismount(Player* bot) +{ + if (!bot || !bot->IsInWorld()) + return; + + float const x = bot->GetPositionX(); + float const y = bot->GetPositionY(); + float const startZ = bot->GetPositionZ(); + + float groundZ = startZ; + bot->UpdateAllowedPositionZ(x, y, groundZ); + + bot->GetMotionMaster()->MoveFall(); + MovementInfo fallInfo = bot->m_movementInfo; + // Need to set the start of the fall, otherwise the fall may start from too high of a Z and kill the bot. + bot->SetFallInformation(0, startZ); + fallInfo.pos.Relocate(x, y, groundZ); + bot->HandleFall(fallInfo); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR); } bool CheckMountStateAction::TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const @@ -434,6 +472,24 @@ bool CheckMountStateAction::ShouldDismountForMaster(Player* master) const return !isMasterMounted && bot->IsMounted(); } +static bool BotCanUseFlyingMount(Player const* bot) +{ + if (bot->GetPureSkillValue(SKILL_RIDING) < 225) + return false; + + AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetAreaId()); + if (!area || !area->IsFlyable()) + return false; + if (area->flags & AREA_FLAG_NO_FLY_ZONE) + return false; + + uint32 const vmap = GetVirtualMapForMapAndZone(bot->GetMapId(), bot->GetZoneId()); + if (vmap == MAP_NORTHREND && !bot->HasSpell(SPELL_COLD_WEATHER_FLYING)) + return false; + + return true; +} + int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const MountData& mountData) const { // Check riding skill and level requirements @@ -443,8 +499,10 @@ int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const Mou if (ridingSkill <= 75 && botLevel < static_cast(sPlayerbotAIConfig.useFastGroundMountAtMinLevel)) return 59; - // If there is a master and bot not in BG, use master's aura effects. - if (master && !bot->InBattleground()) + // check if bot has master and if master is self + bool const noRealMaster = (!master || master == bot); + + if (!noRealMaster && !bot->InBattleground()) { auto auraEffects = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED); if (!auraEffects.empty()) @@ -458,27 +516,27 @@ int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const Mou return 279; else if (masterInShapeshiftForm == FORM_FLIGHT) return 149; - } - else - { - // Bots on their own. - int32 speed = mountData.maxSpeed; - if (bot->InBattleground() && speed > 99) - return 99; - - return speed; + return 59; // walk pace } - return 59; + // No real master OR battleground: pick speed by skill tier. + if (!bot->InBattleground() && BotCanUseFlyingMount(bot)) + return (ridingSkill >= 300) ? 279 : 149; + + int32 maxGround = (ridingSkill >= 150) ? 99 : 59; + if (bot->InBattleground() && maxGround > 99) + maxGround = 99; + return maxGround; } uint32 CheckMountStateAction::GetMountType(Player* master) const { - if (!master) - return 0; + bool const noRealMaster = (!master || master == bot); + + if (noRealMaster) + return (!bot->InBattleground() && BotCanUseFlyingMount(bot)) ? 1 : 0; auto auraEffects = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED); - if (!auraEffects.empty()) { SpellInfo const* masterSpell = auraEffects.front()->GetSpellInfo(); diff --git a/src/Ai/Base/Actions/CheckMountStateAction.h b/src/Ai/Base/Actions/CheckMountStateAction.h index 9a21838e1..d1faa3798 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.h +++ b/src/Ai/Base/Actions/CheckMountStateAction.h @@ -42,6 +42,8 @@ public: bool isPossible() override { return true; } bool Mount(); + static void CompleteDismount(Player* bot); + private: Player* master; ShapeshiftForm masterInShapeshiftForm; diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index b2631f7d1..8b20b2e88 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -15,6 +15,7 @@ #include "ChannelMgr.h" #include "CharacterPackets.h" #include "ChatHelper.h" +#include "CheckMountStateAction.h" #include "Common.h" #include "CreatureData.h" #include "EmoteAction.h" @@ -1365,6 +1366,17 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) // */ return; } + case SMSG_DISMOUNT: + { + WorldPacket p(packet); + p.rpos(0); + ObjectGuid guid; + p >> guid.ReadAsPacked(); + if (guid != bot->GetGUID()) + return; + CheckMountStateAction::CompleteDismount(bot); + return; + } default: botOutgoingPacketHandlers.AddPacket(packet); }