fix: warning updating movement flags while rooted (#1858)

fixes https://github.com/mod-playerbots/mod-playerbots/issues/1854
-----
Also includes fixes for:
-----
* Bots swimming with waterWalk kept switching between swimming and
walking, as result jittering effect swimming under water when water
walking active
* Bots flying close above water they would land on water and start
walking, now they stay flying unless on solid ground they will land and
start walking by design

-----
Moved all flag setting to updateMovementState:
* So all movement flag are handled in updateMovementState which also
contains the restricted movement logic.
* Handle restricted movement logic and preventing SendMovementFlagUpdate
while being restricted.


-----
Known issue when flying the following bots feel a bit jittering, wont
touch for now at least till core movement changes quirks has been dealt
with.

The current code is the extended version of what is originally was
before core merge with refactored movements. Once the core movement
refactors are settled a bit more i would like to revisit this code; as i
would expect more imperative code and less manual flag setting e.g.
bot->SetWaterWalking, SetGravitiy..SetCanFly etc.
This commit is contained in:
bashermens
2025-11-21 20:45:23 +01:00
committed by GitHub
parent 0c1700c117
commit d97870facd

View File

@@ -971,73 +971,86 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target
void MovementAction::UpdateMovementState()
{
// state flags
const float gLvlZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
const bool onGround = bot->GetPositionZ() < gLvlZ + 1.f;
const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura();
const auto master = botAI ? botAI->GetMaster() : nullptr; // real or not
const bool masterIsFlying = master && master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
const bool isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
const auto liquidState = bot->GetLiquidData().Status; // default LIQUID_MAP_NO_WATER
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 isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move
bot->isFrozen() ||
bot->IsPolymorphed() ||
bot->HasRootAura() ||
bot->HasStunAura() ||
bot->HasConfuseAura() ||
bot->HasUnitState(UNIT_STATE_LOST_CONTROL);
// handle water state
if (isWaterArea)
// no update movement flags while movement is current restricted.
if (!isCurrentlyRestricted && bot->IsAlive())
{
// water walking
if (wantsToWaterWalk && !isWaterWalking && !isUnderWater && !isFlying)
// 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 float gZ = bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
const bool wantsToFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura();
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;
bool movementFlagsUpdated = false;
// handle water state
if (isWaterArea && !isFlying)
{
// water walking
if (wantsToWaterWalk && !isWaterWalking && !masterIsSwimming)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
movementFlagsUpdated = true;
}
// swimming
else if (wantsToSwim && !isSwimming && masterIsSwimming)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
movementFlagsUpdated = true;
}
}
else if (isSwimming || isWaterWalking)
{
// reset water flags
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
bot->AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
bot->SendMovementFlagUpdate();
}
// swimming
else if (wantsToSwim && !isSwimming && !wantsToWaterWalk && !isFlying)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
bot->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
bot->SendMovementFlagUpdate();
movementFlagsUpdated = true;
}
}
else
{
// reset flags, if not will inherit incorrect walk speed here and there
// when transistions between land and water.
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
bot->SendMovementFlagUpdate();
// handle flying state
if (wantsToFly && !isFlying && masterIsFlying)
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
movementFlagsUpdated = true;
}
else if ((!wantsToFly || onGroundZ) && isFlying)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
movementFlagsUpdated = true;
}
// detect if movement restrictions have been lifted, CC just ended.
if (wasMovementRestricted)
movementFlagsUpdated = true; // refresh movement state to ensure animations play correctly
if (movementFlagsUpdated)
bot->SendMovementFlagUpdate();
}
// handle flying state
if (wantsToFly && !isFlying && masterIsFlying)
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
bot->SendMovementFlagUpdate();
}
else if ((!wantsToFly || onGround) && isFlying)
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
bot->SendMovementFlagUpdate();
}
// See if the bot is currently slowed, rooted, or otherwise unable to move
bool isCurrentlyRestricted = bot->isFrozen() || bot->IsPolymorphed() || bot->HasRootAura() || bot->HasStunAura() ||
bot->HasConfuseAura() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL);
// Detect if movement restrictions have been lifted
if (wasMovementRestricted && !isCurrentlyRestricted && bot->IsAlive())
{
// CC just ended - refresh movement state to ensure animations play correctly
bot->SendMovementFlagUpdate();
}
// Save current state for the next check
// Save current state for the next check
wasMovementRestricted = isCurrentlyRestricted;
// Temporary speed increase in group
@@ -1820,25 +1833,12 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen
if (!mm)
return;
// enable flying
if (unit->HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
{
unit->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
unit->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
}
else
{
unit->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
unit->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
}
// enable water walking
if (unit->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING))
{
float gLvlZ = unit->GetMapWaterOrGroundLevel(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ());
unit->UpdatePosition(unit->GetPositionX(), unit->GetPositionY(), gLvlZ, false);
// z = gLvlZ; do not overwrite Z axex, otherwise you wont be able to steer the bots into swimming when water
// walking.
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();