Core/Unit: Fix movement hiccups in water (#24020)

* Core/Unit: Allow to define Units which can enter water but cannot swim

Allow to define Units which can enter water but cannot swim, i.e. crabs walking at the bottom of a sea.

* Add UNIT_FLAG_SWIMMING to creatures when entering combat

* Fix charmed creatures not entering water

* Always allow Creatures controlled by players to enter water

* Add swimming flag when possessing a unit and remove it properly at the end, even if the creature engaged combat before and after.
When adding/removing UNIT_FLAG_SWIMMING manually calling Creature::RefreshSwimmingFlag(true) might be required.

(cherry picked from commit fc1a0d936888395f114028af3337e7d674e0a43c)
This commit is contained in:
Giacomo Pozzoni
2022-03-05 23:04:24 +01:00
committed by Shauren
parent 701a3927c1
commit 6b242eee13
7 changed files with 55 additions and 6 deletions
+34 -2
View File
@@ -305,7 +305,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_grou
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0), m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0),
_regenerateHealth(true), _regenerateHealthLock(false) _regenerateHealth(true), _regenerateHealthLock(false), _isMissingSwimmingFlagOutOfCombat(false)
{ {
m_regenTimer = CREATURE_REGEN_INTERVAL; m_regenTimer = CREATURE_REGEN_INTERVAL;
@@ -2829,7 +2829,7 @@ void Creature::UpdateMovementFlags()
if (!isInAir) if (!isInAir)
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING); RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
SetSwim(GetMovementTemplate().IsSwimAllowed() && IsInWater()); SetSwim(CanSwim() && IsInWater());
} }
CreatureMovementData const& Creature::GetMovementTemplate() const CreatureMovementData const& Creature::GetMovementTemplate() const
@@ -2840,6 +2840,36 @@ CreatureMovementData const& Creature::GetMovementTemplate() const
return GetCreatureTemplate()->Movement; return GetCreatureTemplate()->Movement;
} }
bool Creature::CanSwim() const
{
if (Unit::CanSwim())
return true;
if (IsPet())
return true;
return false;
}
bool Creature::CanEnterWater() const
{
if (CanSwim())
return true;
return GetMovementTemplate().IsSwimAllowed();
}
void Creature::RefreshSwimmingFlag(bool recheck)
{
if (!_isMissingSwimmingFlagOutOfCombat || recheck)
_isMissingSwimmingFlagOutOfCombat = !HasUnitFlag(UNIT_FLAG_CAN_SWIM);
// Check if the creature has UNIT_FLAG_SWIMMING and add it if it's missing
// Creatures must be able to chase a target in water if they can enter water
if (_isMissingSwimmingFlagOutOfCombat && CanEnterWater())
AddUnitFlag(UNIT_FLAG_CAN_SWIM);
}
void Creature::AllLootRemovedFromCorpse() void Creature::AllLootRemovedFromCorpse()
{ {
if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient()) if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
@@ -3418,6 +3448,8 @@ void Creature::AtEngage(Unit* target)
if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED)) if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED))
Dismount(); Dismount();
RefreshSwimmingFlag();
if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
{ {
UpdateSpeed(MOVE_RUN); UpdateSpeed(MOVE_RUN);
+10 -1
View File
@@ -111,7 +111,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
CreatureMovementData const& GetMovementTemplate() const; CreatureMovementData const& GetMovementTemplate() const;
bool CanWalk() const { return GetMovementTemplate().IsGroundAllowed(); } bool CanWalk() const { return GetMovementTemplate().IsGroundAllowed(); }
bool CanSwim() const override { return GetMovementTemplate().IsSwimAllowed() || IsPet(); } bool CanSwim() const override;
bool CanEnterWater() const override;
bool CanFly() const override { return GetMovementTemplate().IsFlightAllowed() || IsFlying(); } bool CanFly() const override { return GetMovementTemplate().IsFlightAllowed() || IsFlying(); }
bool CanHover() const { return GetMovementTemplate().Ground == CreatureGroundMovementType::Hover || IsHovering(); } bool CanHover() const { return GetMovementTemplate().Ground == CreatureGroundMovementType::Hover || IsHovering(); }
@@ -369,6 +370,12 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void AtEngage(Unit* target) override; void AtEngage(Unit* target) override;
void AtDisengage() override; void AtDisengage() override;
bool HasSwimmingFlagOutOfCombat() const
{
return !_isMissingSwimmingFlagOutOfCombat;
}
void RefreshSwimmingFlag(bool recheck = false);
std::string GetDebugInfo() const override; std::string GetDebugInfo() const override;
protected: protected:
@@ -452,6 +459,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
// Regenerate health // Regenerate health
bool _regenerateHealth; // Set on creation bool _regenerateHealth; // Set on creation
bool _regenerateHealthLock; // Dynamically set bool _regenerateHealthLock; // Dynamically set
bool _isMissingSwimmingFlagOutOfCombat;
}; };
class TC_GAME_API AssistDelayEvent : public BasicEvent class TC_GAME_API AssistDelayEvent : public BasicEvent
+1
View File
@@ -2623,6 +2623,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendMovementSetCollisionHeight(float height, WorldPackets::Movement::UpdateCollisionHeightReason reason); void SendMovementSetCollisionHeight(float height, WorldPackets::Movement::UpdateCollisionHeightReason reason);
bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); } bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
bool CanEnterWater() const override { return true; }
std::string GetMapAreaAndZoneString() const; std::string GetMapAreaAndZoneString() const;
std::string GetCoordsMapAreaAndZoneString() const; std::string GetCoordsMapAreaAndZoneString() const;
+5 -2
View File
@@ -3006,7 +3006,7 @@ bool Unit::isInBackInMap(Unit const* target, float distance, float arc) const
bool Unit::isInAccessiblePlaceFor(Creature const* c) const bool Unit::isInAccessiblePlaceFor(Creature const* c) const
{ {
if (IsInWater()) if (IsInWater())
return c->CanSwim(); return c->CanEnterWater();
else else
return c->CanWalk() || c->CanFly(); return c->CanWalk() || c->CanFly();
} }
@@ -11150,6 +11150,9 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
AddUnitState(UNIT_STATE_CHARMED); AddUnitState(UNIT_STATE_CHARMED);
if (Creature* creature = ToCreature())
creature->RefreshSwimmingFlag();
if ((GetTypeId() != TYPEID_PLAYER) || (charmer->GetTypeId() != TYPEID_PLAYER)) if ((GetTypeId() != TYPEID_PLAYER) || (charmer->GetTypeId() != TYPEID_PLAYER))
{ {
// AI will schedule its own change if appropriate // AI will schedule its own change if appropriate
@@ -12107,7 +12110,7 @@ bool Unit::CanSwim() const
return true; return true;
if (HasUnitFlag2(UnitFlags2(0x1000000))) if (HasUnitFlag2(UnitFlags2(0x1000000)))
return false; return false;
if (IsPet() && HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT)) if (HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT))
return true; return true;
return HasUnitFlag(UnitFlags(UNIT_FLAG_RENAME | UNIT_FLAG_CAN_SWIM)); return HasUnitFlag(UnitFlags(UNIT_FLAG_RENAME | UNIT_FLAG_CAN_SWIM));
} }
+1
View File
@@ -1807,6 +1807,7 @@ class TC_GAME_API Unit : public WorldObject
virtual bool CanFly() const = 0; virtual bool CanFly() const = 0;
bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY); } bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY); }
bool IsFalling() const; bool IsFalling() const;
virtual bool CanEnterWater() const = 0;
virtual bool CanSwim() const; virtual bool CanSwim() const;
float GetHoverOffset() const float GetHoverOffset() const
@@ -144,6 +144,9 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, b
if (movementInform && HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) if (movementInform && HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED))
{ {
if (!owner->HasSwimmingFlagOutOfCombat())
owner->RemoveUnitFlag(UNIT_FLAG_CAN_SWIM);
owner->SetSpawnHealth(); owner->SetSpawnHealth();
owner->LoadCreaturesAddon(); owner->LoadCreaturesAddon();
if (owner->IsVehicle()) if (owner->IsVehicle())
+1 -1
View File
@@ -657,7 +657,7 @@ void PathGenerator::CreateFilter()
includeFlags |= NAV_GROUND; // walk includeFlags |= NAV_GROUND; // walk
// creatures don't take environmental damage // creatures don't take environmental damage
if (creature->CanSwim()) if (creature->CanEnterWater())
includeFlags |= (NAV_WATER | NAV_MAGMA_SLIME); // swim includeFlags |= (NAV_WATER | NAV_MAGMA_SLIME); // swim
} }
else // assume Player else // assume Player