diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index d4090c0ef..4f04d1799 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -953,68 +953,70 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target void MovementAction::UpdateMovementState() { - const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move - bot->HasUnitState(UNIT_STATE_LOST_CONTROL) || - bot->IsRooted() || - bot->isFrozen() || - bot->IsPolymorphed(); + const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move + bot->HasUnitState(UNIT_STATE_LOST_CONTROL) || bot->IsRooted() || bot->isFrozen() || bot->IsPolymorphed(); // no update movement flags while movement is current restricted. if (!isCurrentlyRestricted && bot->IsAlive()) { // state flags - const auto master = botAI ? botAI->GetMaster() : nullptr; // real player or not - const bool masterIsFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true; - const bool masterIsSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true; - const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER + const auto master = botAI ? botAI->GetMaster() : nullptr; + const auto liquidState = bot->GetLiquidData().Status; const float gZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); - const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + const bool onGroundZ = bot->GetPositionZ() < gZ + 1.f; + const bool canSwim = liquidState == LIQUID_MAP_IN_WATER || liquidState == LIQUID_MAP_UNDER_WATER; + const bool canFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura(); + const bool canWaterWalk = bot->HasWaterWalkAura(); + const bool isMasterFlying = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) : true; + const bool isMasterSwimming = master ? master->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) : true; const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING); - const bool isWaterArea = liquidState != LIQUID_MAP_NO_WATER; - const bool isUnderWater = liquidState == LIQUID_MAP_UNDER_WATER; - const bool isInWater = liquidState == LIQUID_MAP_IN_WATER; - const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); const bool isSwimming = bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - const bool wantsToWaterWalk = bot->HasWaterWalkAura(); - const bool wantsToSwim = isInWater || isUnderWater; - const bool onGroundZ = (bot->GetPositionZ() < gZ + 1.f) && !isWaterArea; + const bool isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); bool movementFlagsUpdated = false; - // handle water state - if (isWaterArea && !isFlying) + // handle water (fragile logic do not alter without testing every detail, animation and transition) + if (liquidState != LIQUID_MAP_NO_WATER && !isFlying) { - // water walking - if (wantsToWaterWalk && !isWaterWalking && !masterIsSwimming) + if (canWaterWalk && !isMasterSwimming && !isWaterWalking) { - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->SetSwim(false); bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); movementFlagsUpdated = true; } - // swimming - else if (wantsToSwim && !isSwimming && masterIsSwimming) + else if ((!canWaterWalk || isMasterSwimming) && isWaterWalking) { bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + if (canSwim) + bot->SetSwim(true); + movementFlagsUpdated = true; + } + else if (!canSwim && isSwimming) + { + bot->SetSwim(false); movementFlagsUpdated = true; } } - else if (isSwimming || isWaterWalking) + + // reset when not around water while swimming or water walking + if (liquidState == LIQUID_MAP_NO_WATER && (isSwimming || isWaterWalking)) { - // reset water flags - bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + bot->SetSwim(false); bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); movementFlagsUpdated = true; } - // handle flying state - if (wantsToFly && !isFlying && masterIsFlying) + // handle flying + if ((canFly && !isFlying) && isMasterFlying) { bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); - movementFlagsUpdated = true; + + // required for transition and state monitoring. + if (MotionMaster* mm = bot->GetMotionMaster()) + mm->MoveTakeoff(0, {bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() + 1.F}, 0.F, true); } - else if ((!wantsToFly || onGroundZ) && isFlying) + else if ((!canFly && !isWaterWalking && isFlying) || (!isMasterFlying && isFlying && onGroundZ)) { bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); @@ -1022,15 +1024,16 @@ void MovementAction::UpdateMovementState() movementFlagsUpdated = true; } - // detect if movement restrictions have been lifted, CC just ended. + // detect if movement/CC restrictions have been ended, refresh movement state for animations. if (wasMovementRestricted) - movementFlagsUpdated = true; // refresh movement state to ensure animations play correctly + movementFlagsUpdated = true; + // movement flags should only be updated between state changes, if not it will break certain effects. if (movementFlagsUpdated) bot->SendMovementFlagUpdate(); } - // Save current state for the next check + // Save current state for the next check wasMovementRestricted = isCurrentlyRestricted; // Temporary speed increase in group @@ -1813,12 +1816,11 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen if (!mm) return; - // enable water walking - if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING)) + // bot water collision correction + if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING) && unit->HasWaterWalkAura()) { float gZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gZ, false); - // z = gZ; no overwrite Z axe otherwise you cant steer the bots into swimming when water walking. } mm->Clear();