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:
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user