Core/Unit: Fix movement hiccups in water

* 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.
This commit is contained in:
Giacomo Pozzoni authored and Ovahlord committed 2021-01-11 12:33:54 +01:00
1 parent 6ba851e809
commit 5e3b57bae7
11 files changed
+60 -15

No files matched your search

+34 -3
View File
@@ -267,7 +267,7 @@ m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTi
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), _cyclicSplinePathId(0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false)
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _isMissingSwimmingFlagOutOfCombat(false)
{
m_valuesCount = UNIT_END;
@@ -2722,6 +2722,36 @@ CreatureMovementData const& Creature::GetMovementTemplate() const
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 = !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
// 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())
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
}
void Creature::AllLootRemovedFromCorpse()
{
if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
@@ -3014,8 +3044,7 @@ void Creature::UpdateMovementFlags()
if (!isInAir)
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
if (GetMovementTemplate().IsSwimAllowed())
SetSwim(IsInWater());
SetSwim(CanSwim() && IsInWater());
}
void Creature::SetObjectScale(float scale)
@@ -3250,6 +3279,8 @@ void Creature::AtEngage(Unit* target)
if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED))
Dismount();
RefreshSwimmingFlag();
MovementGeneratorType const movetype = GetMotionMaster()->GetCurrentMovementGeneratorType();
if (movetype == WAYPOINT_MOTION_TYPE || movetype == POINT_MOTION_TYPE || (IsAIEnabled && AI()->IsEscorted()))
SetHomePosition(GetPosition());
+10 -1
View File
@@ -95,7 +95,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
CreatureMovementData const& GetMovementTemplate() const;
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 CanHover() const { return GetMovementTemplate().Ground == CreatureGroundMovementType::Hover || IsHovering(); }
bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true) override;
@@ -366,6 +367,12 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void AtEngage(Unit* target) override;
void AtDisengage() override;
bool HasSwimmingFlagOutOfCombat() const
{
return !_isMissingSwimmingFlagOutOfCombat;
}
void RefreshSwimmingFlag(bool recheck = false);
protected:
bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0);
bool InitEntry(uint32 entry, CreatureData const* data = nullptr);
@@ -446,6 +453,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
} _spellFocusInfo;
CreatureTextRepeatGroup m_textRepeat;
bool _isMissingSwimmingFlagOutOfCombat;
};
class TC_GAME_API AssistDelayEvent : public BasicEvent
+1
View File
@@ -2374,6 +2374,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendMovementSetCollisionHeight(float height, UpdateCollisionHeightReason reason);
bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
bool CanEnterWater() const override { return true; }
std::string GetMapAreaAndZoneString() const;
std::string GetCoordsMapAreaAndZoneString() const;
+3 -3
View File
@@ -3130,7 +3130,7 @@ bool Unit::isInBackInMap(Unit const* target, float distance, float arc) const
bool Unit::isInAccessiblePlaceFor(Creature const* c) const
{
if (IsInWater())
return c->CanSwim();
return c->CanEnterWater();
else
return c->CanWalk() || c->CanFly();
}
@@ -13271,9 +13271,9 @@ bool Unit::CanSwim() const
return true;
if (HasFlag(UNIT_FIELD_FLAGS_2, 0x1000000))
return false;
if (IsPet() && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT))
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT))
return true;
return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_RENAME | UNIT_FLAG_UNK_15);
return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_RENAME | UNIT_FLAG_SWIMMING);
}
void Unit::NearTeleportTo(Position const& pos, bool casting /*= false*/)
+1
View File
@@ -1672,6 +1672,7 @@ class TC_GAME_API Unit : public WorldObject
virtual bool CanFly() const = 0;
bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY); }
bool IsFalling() const;
virtual bool CanEnterWater() const = 0;
virtual bool CanSwim() const;
float GetHoverOffset() const
+1 -1
View File
@@ -135,7 +135,7 @@ enum UnitFlags : uint32
UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3
UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
UNIT_FLAG_CANNOT_SWIM = 0x00004000, // 2.0.8
UNIT_FLAG_UNK_15 = 0x00008000,
UNIT_FLAG_SWIMMING = 0x00008000, // shows swim animation in water
UNIT_FLAG_NOT_ATTACKABLE_2 = 0x00010000,
UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok
UNIT_FLAG_STUNNED = 0x00040000, // 3.0.3 ok
@@ -81,6 +81,9 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
{
if (_arrived)
{
if (!owner->HasSwimmingFlagOutOfCombat())
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
owner->ClearUnitState(UNIT_STATE_EVADE);
owner->SetWalk(true);
owner->LoadCreaturesAddon();
+1 -1
View File
@@ -658,7 +658,7 @@ void PathGenerator::CreateFilter()
includeFlags |= NAV_GROUND; // walk
// creatures don't take environmental damage
if (creature->CanSwim())
if (creature->CanEnterWater())
includeFlags |= (NAV_WATER | NAV_MAGMA_SLIME); // swim
}
else // assume Player
+1 -1
View File
@@ -135,7 +135,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] =
CREATE_NAMED_ENUM(UNIT_FLAG_PVP),
CREATE_NAMED_ENUM(UNIT_FLAG_SILENCED),
CREATE_NAMED_ENUM(UNIT_FLAG_CANNOT_SWIM),
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_15),
CREATE_NAMED_ENUM(UNIT_FLAG_SWIMMING),
CREATE_NAMED_ENUM(UNIT_FLAG_NOT_ATTACKABLE_2),
CREATE_NAMED_ENUM(UNIT_FLAG_PACIFIED),
CREATE_NAMED_ENUM(UNIT_FLAG_STUNNED),
@@ -533,7 +533,7 @@ public:
me->RestoreFaction();
CombatAI::Reset();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
}
void SpellHit(Unit* pCaster, SpellInfo const* pSpell) override
@@ -628,7 +628,7 @@ public:
}
me->SetImmuneToPC(false);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
player->CastSpell(me, SPELL_DUEL, false);
player->CastSpell(player, SPELL_DUEL_FLAG, true);
@@ -280,10 +280,10 @@ class spell_varos_centrifuge_shield : public SpellScriptLoader
if (Unit* caster = GetCaster())
{
// flags taken from sniffs
if (caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15|UNIT_FLAG_IMMUNE_TO_NPC|UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_UNK_6))
if (caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_UNK_6))
{
caster->ToCreature()->SetReactState(REACT_PASSIVE);
caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15|UNIT_FLAG_UNK_6);
caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING | UNIT_FLAG_UNK_6);
caster->SetImmuneToAll(true, true);
}
}
@@ -294,7 +294,7 @@ class spell_varos_centrifuge_shield : public SpellScriptLoader
if (Unit* caster = GetCaster())
{
caster->ToCreature()->SetReactState(REACT_AGGRESSIVE);
caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15|UNIT_FLAG_UNK_6);
caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING | UNIT_FLAG_UNK_6);
caster->SetImmuneToAll(false);
}
}