diff --git a/src/Ai/Base/Actions/CheckMountStateAction.cpp b/src/Ai/Base/Actions/CheckMountStateAction.cpp index 0e3abb724..5da250790 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.cpp +++ b/src/Ai/Base/Actions/CheckMountStateAction.cpp @@ -17,6 +17,7 @@ #include "SpellAuraEffects.h" static constexpr uint32 SPELL_COLD_WEATHER_FLYING = 54197; +static constexpr float PARACHUTE_LAND_THRESHOLD = 15.0f; // Define the static map / init bool for caching bot preferred mount data globally std::unordered_map CheckMountStateAction::mountCache; @@ -61,6 +62,21 @@ MountData CollectMountData(const Player* bot) bool CheckMountStateAction::Execute(Event /*event*/) { + // Forced flight dismount: + // Bots get stale flight movement flags after a forced dismount (e.g: Dalaran) because the post landing dismount cleanup + // needs MSG_MOVE_FALL_LAND (a client opcode) and client movement packets. The stale flags cause the bot to be stuck with + // the parachute, or even keep the bot hovering indefinitely and block MMAP routing. + // Note: Without MSG_MOVE_FALL_LAND, HandleFall doesn't trigger, meaning bots don't get fall damage in forced dismounts anyway, + // so the parachute usage here is more of an immersion feature. + if (bot->HasFeatherFallAura()) + { + float floorZ = bot->GetMapHeight(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + if (floorZ != INVALID_HEIGHT && floorZ != VMAP_INVALID_HEIGHT_VALUE && + bot->GetPositionZ() - floorZ <= PARACHUTE_LAND_THRESHOLD) + bot->RemoveAurasByType(SPELL_AURA_FEATHER_FALL); + } + ClearStaleFlightFlags(); + // Determine if there are no attackers bool noAttackers = !AI_VALUE2(bool, "combat", "self target") || !AI_VALUE(uint8, "attacker count"); bool enemy = AI_VALUE(Unit*, "enemy player target"); @@ -204,7 +220,7 @@ bool CheckMountStateAction::Mount() // Get bot mount data MountData mountData = CollectMountData(bot); int32 masterMountType = GetMountType(master); - int32 masterSpeed = CalculateMasterMountSpeed(master, mountData); + int32 masterSpeed = CalculateMasterMountSpeed(master); // Try shapeshift if (TryForms(master, masterMountType, masterSpeed)) @@ -234,14 +250,17 @@ 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)) + ClearStaleFlightFlags(); +} + +void CheckMountStateAction::ClearStaleFlightFlags() +{ + if (bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura()) + return; + + if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY)) { - bot->RemoveUnitMovementFlag( - MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY); + bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_CAN_FLY); if (!bot->IsRooted()) bot->SendMovementFlagUpdate(); } @@ -490,7 +509,7 @@ static bool BotCanUseFlyingMount(Player const* bot) return true; } -int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const MountData& mountData) const +int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master) const { // Check riding skill and level requirements int32 ridingSkill = bot->GetPureSkillValue(SKILL_RIDING); diff --git a/src/Ai/Base/Actions/CheckMountStateAction.h b/src/Ai/Base/Actions/CheckMountStateAction.h index d1faa3798..0bf83cc5e 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.h +++ b/src/Ai/Base/Actions/CheckMountStateAction.h @@ -53,9 +53,10 @@ private: float CalculateDismountDistance() const; float CalculateMountDistance() const; void Dismount(); + void ClearStaleFlightFlags(); bool ShouldFollowMasterMountState(Player* master, bool noAttackers, bool shouldMount) const; bool ShouldDismountForMaster(Player* master) const; - int32 CalculateMasterMountSpeed(Player* master, const MountData& mountData) const; + int32 CalculateMasterMountSpeed(Player* master) const; bool CheckForSwiftMount() const; std::map>> GetAllMountSpells() const; bool TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const;