Core/Creatures: implemented most movement related static flags and migrated existing movement data into static flags

* implemented CREATURE_STATIC_FLAG_AQUATIC - creatures cannot leave liquids
* implemented CREATURE_STATIC_FLAG_AMPHIBIOUS - creatures can enter and leave liquids but remain on the ocean floor when swimming is not enabled until engaged
* implemented CREATURE_STATIC_FLAG_FLOATING - creatures have their gravity on spawn / reset disabled
* implemented CREATURE_STATIC_FLAG_SESSILE - creatures are rooted in place
* implemented CREATURE_STATIC_FLAG_CAN_SWIM - creature can swim in liquids
* implemented CREATURE_STATIC_FLAG_3_CANNOT_SWIM - Amphibious creatures cannot toggle on swimming
* implemented CREATURE_STATIC_FLAG_3_CANNOT_TURN - Creatures cannot turn at all
* implemented CREATURE_STATIC_FLAG_4_PREVENT_SWIM - Amphibious creatures won't toggle on swimming until their victim starts leaving the ocean floor
* partially implemented CREATURE_STATIC_FLAG_3_CANNOT_PENETRATE_WATER
* deprecated CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE as this flag was a hackfix to a wrong implementation that is now gone
Port From (https://github.com/TrinityCore/TrinityCore/commit/c541eda54d7e0dddeec329a6beac2948e0b0a40b)
This commit is contained in:
hondacrx
2024-02-29 17:08:30 -05:00
parent 4dded15a03
commit db05394d99
13 changed files with 207 additions and 204 deletions
@@ -27,8 +27,6 @@ namespace Game.Entities
// Regenerate health
bool _regenerateHealth; // Set on creation
bool _isMissingCanSwimFlagOutOfCombat;
uint? _defaultMountDisplayIdOverride;
int _creatureImmunitiesId;
uint _gossipMenuId;
+49 -90
View File
@@ -306,7 +306,7 @@ namespace Game.Entities
SetFaction(cInfo.Faction);
ObjectManager.ChooseCreatureFlags(cInfo, out ulong npcFlags, out uint unitFlags, out uint unitFlags2, out uint unitFlags3, data);
ObjectManager.ChooseCreatureFlags(cInfo, out ulong npcFlags, out uint unitFlags, out uint unitFlags2, out uint unitFlags3, _staticFlags, data);
if (cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.Worldevent))
npcFlags |= Global.GameEventMgr.GetNPCFlag(this);
@@ -399,7 +399,7 @@ namespace Game.Entities
SetIsCombatDisallowed(cInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.CannotEnterCombat));
InitializeMovementFlags();
InitializeMovementCapabilities();
LoadCreaturesAddon();
LoadCreaturesSparringHealth();
@@ -419,7 +419,6 @@ namespace Game.Entities
_staticFlags = flags;
// Apply all other side effects of flag changes
SetTemplateRooted(flags.HasFlag(CreatureStaticFlags.Sessile));
m_updateFlag.NoBirthAnim = flags.HasFlag(CreatureStaticFlags4.NoBirthAnim);
}
@@ -437,7 +436,7 @@ namespace Game.Entities
GetAI().JustAppeared();
}
UpdateMovementFlags();
UpdateMovementCapabilities();
switch (m_deathState)
{
@@ -1062,8 +1061,6 @@ namespace Game.Entities
if (!HasFlag(CreatureStaticFlags2.AllowMountedCombat))
Dismount();
RefreshCanSwimFlag();
if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
{
UpdateSpeed(UnitMoveType.Run);
@@ -1783,19 +1780,6 @@ namespace Game.Entities
SetHealth((m_deathState == DeathState.Alive || m_deathState == DeathState.JustRespawned) ? curhealth : 0);
}
void LoadTemplateRoot()
{
SetTemplateRooted(GetMovementTemplate().IsRooted());
}
public bool IsTemplateRooted() { return _staticFlags.HasFlag(CreatureStaticFlags.Sessile); }
public void SetTemplateRooted(bool rooted)
{
_staticFlags.ApplyFlag(CreatureStaticFlags.Sessile, rooted);
SetControlled(rooted, UnitState.Root);
}
public override bool HasQuest(uint questId)
{
return Global.ObjectMgr.GetCreatureQuestRelations(GetEntry()).HasQuest(questId);
@@ -2021,7 +2005,7 @@ namespace Game.Entities
if (m_formation != null && m_formation.GetLeader() == this)
m_formation.FormationReset(true);
bool needsFalling = (IsFlying() || IsHovering()) && !IsUnderWater();
bool needsFalling = (IsFlying() || IsHovering()) && !IsUnderWater() && !HasUnitState(UnitState.Root);
SetHover(false, false);
SetDisableGravity(false, false);
@@ -2041,7 +2025,7 @@ namespace Game.Entities
ResetPlayerDamageReq();
SetCannotReachTarget(false);
UpdateMovementFlags();
UpdateMovementCapabilities();
ClearUnitState(UnitState.AllErasable);
@@ -2050,7 +2034,7 @@ namespace Game.Entities
CreatureData creatureData = GetCreatureData();
CreatureTemplate cInfo = GetCreatureTemplate();
ObjectManager.ChooseCreatureFlags(cInfo, out ulong npcFlags, out uint unitFlags, out uint unitFlags2, out uint unitFlags3, creatureData);
ObjectManager.ChooseCreatureFlags(cInfo, out ulong npcFlags, out uint unitFlags, out uint unitFlags2, out uint unitFlags3, _staticFlags, creatureData);
if (cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.Worldevent))
npcFlags |= Global.GameEventMgr.GetNPCFlag(this);
@@ -2500,7 +2484,7 @@ namespace Game.Entities
dist += GetCombatReach() + victim.GetCombatReach();
// to prevent creatures in air ignore attacks because distance is already too high...
if (GetMovementTemplate().IsFlightAllowed())
if (CanFly())
return victim.IsInDist2d(m_homePosition, dist);
else
return victim.IsInDist(m_homePosition, dist);
@@ -2525,23 +2509,15 @@ namespace Game.Entities
CreatureAddon creatureAddon = GetCreatureAddon();
if (creatureAddon == null)
return false;
uint mountDisplayId = _defaultMountDisplayIdOverride.GetValueOrDefault(creatureAddon.mount);
if ( mountDisplayId != 0)
if (mountDisplayId != 0)
Mount(creatureAddon.mount);
SetStandState((UnitStandStateType)creatureAddon.standState);
ReplaceAllVisFlags((UnitVisFlags)creatureAddon.visFlags);
SetAnimTier((AnimTier)creatureAddon.animTier, false);
//! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2:
//! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs)
//! Check using InhabitType as movement flags are assigned dynamically
//! basing on whether the creature is in air or not
//! Set MovementFlag_Hover. Otherwise do nothing.
if (CanHover())
AddUnitMovementFlag(MovementFlag.Hover);
SetSheath((SheathState)creatureAddon.sheathState);
ReplaceAllPvpFlags((UnitPVPStateFlags)creatureAddon.pvpFlags);
@@ -2686,52 +2662,38 @@ namespace Game.Entities
bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.MapId != GetMapId(); }
void InitializeMovementFlags()
public void InitializeMovementCapabilities()
{
LoadTemplateRoot();
SetHover(GetMovementTemplate().IsHoverInitiallyEnabled());
SetDisableGravity(IsFloating());
SetControlled(IsSessile(), UnitState.Root);
// It does the same, for now
UpdateMovementFlags();
// If an amphibious creatures was swimming while engaged, disable swimming again
if (IsAmphibious() && !_staticFlags.HasFlag(CreatureStaticFlags.CanSwim))
RemoveUnitFlag(UnitFlags.CanSwim);
UpdateMovementCapabilities();
}
public void UpdateMovementFlags()
public void UpdateMovementCapabilities()
{
// Do not update movement flags if creature is controlled by a player (charm/vehicle)
if (m_playerMovingMe != null)
return;
// Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts
if (GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoMoveFlagsUpdate))
return;
// Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc)
float ground = GetFloorZ();
bool canHover = CanHover();
bool isInAir = (MathFunctions.fuzzyGt(GetPositionZ(), ground + (canHover ? m_unitData.HoverHeight : 0.0f) + MapConst.GroundHeightTolerance) || MathFunctions.fuzzyLt(GetPositionZ(), ground - MapConst.GroundHeightTolerance)); // Can be underground too, prevent the falling
if (GetMovementTemplate().IsFlightAllowed() && (isInAir || !GetMovementTemplate().IsGroundAllowed()) && !IsFalling())
{
if (GetMovementTemplate().Flight == CreatureFlightMovementType.CanFly)
SetCanFly(true);
else
SetDisableGravity(true);
if (!HasAuraType(AuraType.Hover) && GetMovementTemplate().Ground != CreatureGroundMovementType.Hover)
SetHover(false);
}
else
{
SetCanFly(false);
SetDisableGravity(false);
if (IsAlive() && (CanHover() || HasAuraType(AuraType.Hover)))
SetHover(true);
}
bool isInAir = (MathFunctions.fuzzyGt(GetPositionZ(), ground + GetHoverOffset() + MapConst.GroundHeightTolerance) || MathFunctions.fuzzyLt(GetPositionZ(), ground - MapConst.GroundHeightTolerance)); // Can be underground too, prevent the falling
if (!isInAir)
SetFall(false);
SetSwim(CanSwim() && IsInWater());
// Some Amphibious creatures toggle swimming while engaged
if (IsAmphibious() && !HasUnitFlag(UnitFlags.CantSwim) && !HasUnitFlag(UnitFlags.CanSwim))
if (!IsSwimPrevented() || (GetVictim() != null && !GetVictim().IsOnOceanFloor()))
SetUnitFlag(UnitFlags.CanSwim);
SetSwim(IsInWater() && CanSwim());
}
public CreatureMovementData GetMovementTemplate()
@@ -2754,29 +2716,7 @@ namespace Game.Entities
return false;
}
public override bool CanEnterWater()
{
if (CanSwim())
return true;
return GetMovementTemplate().IsSwimAllowed();
}
public void RefreshCanSwimFlag(bool recheck = false)
{
if (!_isMissingCanSwimFlagOutOfCombat || recheck)
_isMissingCanSwimFlagOutOfCombat = !HasUnitFlag(UnitFlags.CanSwim);
// Check if the creature has UNIT_FLAG_CAN_SWIM and add it if it's missing
// Creatures must be able to chase a target in water if they can enter water
if (_isMissingCanSwimFlagOutOfCombat && CanEnterWater())
SetUnitFlag(UnitFlags.CanSwim);
}
public bool HasCanSwimFlagOutOfCombat()
{
return !_isMissingCanSwimFlagOutOfCombat;
}
public override bool CanEnterWater() { return CanSwim() || IsAmphibious(); }
public void AllLootRemovedFromCorpse()
{
@@ -3384,9 +3324,28 @@ namespace Game.Entities
return GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.Guard);
}
public bool CanWalk() { return GetMovementTemplate().IsGroundAllowed(); }
public override bool CanFly() { return GetMovementTemplate().IsFlightAllowed() || IsFlying(); }
bool CanHover() { return GetMovementTemplate().Ground == CreatureGroundMovementType.Hover || IsHovering(); }
// Returns true if CREATURE_STATIC_FLAG_AQUATIC is set which strictly binds the creature to liquids
public bool IsAquatic() { return _staticFlags.HasFlag(CreatureStaticFlags.Aquatic); }
// Returns true if CREATURE_STATIC_FLAG_AMPHIBIOUS is set which allows a creature to enter and leave liquids while sticking to the ocean floor. These creatures will become able to swim when engaged
public bool IsAmphibious() { return _staticFlags.HasFlag(CreatureStaticFlags.Amphibious); }
// Returns true if CREATURE_STATIC_FLAG_FLOATING is set which is disabling the gravity of the creature on spawn and reset
public bool IsFloating() { return _staticFlags.HasFlag(CreatureStaticFlags.Floating); }
// Returns true if CREATURE_STATIC_FLAG_SESSILE is set which permanently roots the creature in place
public bool IsSessile() { return _staticFlags.HasFlag(CreatureStaticFlags.Sessile); }
// Returns true if CREATURE_STATIC_FLAG_3_CANNOT_PENETRATE_WATER is set which does not allow the creature to go below liquid surfaces
public bool CannotPenetrateWater() { return _staticFlags.HasFlag(CreatureStaticFlags3.CannotPenetrateWater); }
// Returns true if CREATURE_STATIC_FLAG_3_CANNOT_SWIM is set which prevents 'Amphibious' creatures from swimming when engaged
public bool IsSwimDisabled() { return _staticFlags.HasFlag(CreatureStaticFlags3.CannotSwim); }
// Returns true if CREATURE_STATIC_FLAG_4_PREVENT_SWIM is set which prevents 'Amphibious' creatures from swimming when engaged until the victim is no longer on the ocean floor
public bool IsSwimPrevented() { return _staticFlags.HasFlag(CreatureStaticFlags4.PreventSwim); }
public override bool CanFly() { return IsFlying() || HasUnitMovementFlag(MovementFlag.CanFly); }
public bool IsDungeonBoss() { return (GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.DungeonBoss)); }
public override bool IsAffectedByDiminishingReturns() { return base.IsAffectedByDiminishingReturns() || GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.AllDiminish); }
+4 -14
View File
@@ -279,38 +279,28 @@ namespace Game.Entities
public class CreatureMovementData
{
public CreatureGroundMovementType Ground;
public CreatureFlightMovementType Flight;
public bool Swim;
public bool Rooted;
public bool HoverInitiallyEnabled;
public CreatureChaseMovementType Chase;
public CreatureRandomMovementType Random;
public uint InteractionPauseTimer;
public CreatureMovementData()
{
Ground = CreatureGroundMovementType.Run;
Flight = CreatureFlightMovementType.None;
Swim = true;
Rooted = false;
Chase = CreatureChaseMovementType.Run;
Random = CreatureRandomMovementType.Walk;
InteractionPauseTimer = WorldConfig.GetUIntValue(WorldCfg.CreatureStopForPlayer);
}
public bool IsGroundAllowed() { return Ground != CreatureGroundMovementType.None; }
public bool IsSwimAllowed() { return Swim; }
public bool IsFlightAllowed() { return Flight != CreatureFlightMovementType.None; }
public bool IsRooted() { return Rooted; }
public CreatureChaseMovementType GetChase() { return Chase; }
public CreatureRandomMovementType GetRandom() { return Random; }
public bool IsHoverInitiallyEnabled() { return HoverInitiallyEnabled; }
public uint GetInteractionPauseTimer() { return InteractionPauseTimer; }
public override string ToString()
{
return $"Ground: {Ground}, Swim: {Swim}, Flight: {Flight} {(Rooted ? ", Rooted" : "")}, Chase: {Chase}, Random: {Random}, InteractionPauseTimer: {InteractionPauseTimer}";
return $"HoverInitiallyEnabled: {HoverInitiallyEnabled}, Chase: {Chase}, Random: {Random}, InteractionPauseTimer: {InteractionPauseTimer}";
}
}
+12 -1
View File
@@ -3752,7 +3752,18 @@ namespace Game.Entities
public float GetMapWaterOrGroundLevel(float x, float y, float z, ref float ground)
{
return GetMap().GetWaterOrGroundLevel(GetPhaseShift(), x, y, z, ref ground, IsTypeMask(TypeMask.Unit) ? !ToUnit().HasAuraType(AuraType.WaterWalk) : false, GetCollisionHeight());
bool swimming = true;
Creature creature = ToCreature();
if (creature != null)
swimming = !creature.CannotPenetrateWater() && !creature.HasAuraType(AuraType.WaterWalk);
else
{
Unit unit = ToUnit();
if (unit != null)
swimming = !unit.HasAuraType(AuraType.WaterWalk);
}
return GetMap().GetWaterOrGroundLevel(GetPhaseShift(), x, y, z, ref ground, swimming, GetCollisionHeight());
}
public float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = MapConst.DefaultHeightSearch)
+21 -8
View File
@@ -750,7 +750,7 @@ namespace Game.Entities
SendMessageToSet(packet, true);
}
if (IsCreature() && updateAnimTier && IsAlive() && !HasUnitState(UnitState.Root) && !ToCreature().IsTemplateRooted())
if (IsCreature() && updateAnimTier && IsAlive() && !HasUnitState(UnitState.Root))
{
if (IsGravityDisabled())
SetAnimTier(AnimTier.Fly);
@@ -1138,7 +1138,7 @@ namespace Game.Entities
SendMessageToSet(packet, true);
}
if (IsCreature() && updateAnimTier && IsAlive() && !HasUnitState(UnitState.Root) && !ToCreature().IsTemplateRooted())
if (IsCreature() && updateAnimTier && IsAlive() && !HasUnitState(UnitState.Root))
{
if (IsGravityDisabled())
SetAnimTier(AnimTier.Fly);
@@ -1186,12 +1186,25 @@ namespace Game.Entities
{
return IsWithinDistInMap(target, distance) && !HasInArc(MathFunctions.TwoPi - arc, target);
}
public bool IsInAccessiblePlaceFor(Creature c)
public bool IsInAccessiblePlaceFor(Creature creature)
{
if (IsInWater())
return c.CanEnterWater();
else
return c.CanWalk() || c.CanFly();
// Aquatic creatures are not allowed to leave liquids
if (!IsInWater() && creature.IsAquatic())
return false;
// Underwater special case. Some creatures may not go below liquid surfaces
if (IsUnderWater() && creature.CannotPenetrateWater())
return false;
// Water checks
if (IsInWater() && !creature.CanEnterWater())
return false;
// Some creatures are tied to the ocean floor and cannot chase swimming targets.
if (!IsOnOceanFloor() && creature.IsUnderWater() && creature.HasUnitFlag(UnitFlags.CantSwim))
return false;
return true;
}
public void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false) { NearTeleportTo(new Position(x, y, z, orientation), casting); }
@@ -1274,7 +1287,7 @@ namespace Game.Entities
SetStunned(false);
break;
case UnitState.Root:
if (HasAuraType(AuraType.ModRoot) || HasAuraType(AuraType.ModRoot2) || HasAuraType(AuraType.ModRootDisableGravity) || GetVehicle() != null || (IsCreature() && ToCreature().IsTemplateRooted()))
if (HasAuraType(AuraType.ModRoot) || HasAuraType(AuraType.ModRoot2) || HasAuraType(AuraType.ModRootDisableGravity) || GetVehicle() != null || (IsCreature() && ToCreature().IsSessile()))
return;
ClearUnitState(state);
-4
View File
@@ -412,10 +412,6 @@ namespace Game.Entities
AddUnitState(UnitState.Charmed);
Creature creature = ToCreature();
if (creature != null)
creature.RefreshCanSwimFlag();
if (!IsPlayer() || !charmer.IsPlayer())
{
// AI will schedule its own change if appropriate