diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 1b02e28c1..5a7900e1f 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -201,6 +201,7 @@ namespace Game.Entities transport.CalculatePassengerPosition(ref x, ref y, ref z, ref o); } + UpdateAllowedPositionZ(x, y, ref z); SetHomePosition(x, y, z, o); GetMap().CreatureRelocation(this, x, y, z, o); } @@ -897,11 +898,7 @@ namespace Game.Entities LoadCreaturesAddon(); //! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there - if (HasUnitMovementFlag(MovementFlag.Hover)) - { - //! Relocate again with updated Z coord - posZ += m_unitData.HoverHeight; - } + posZ += GetHoverOffset(); LastUsedScriptID = GetScriptId(); @@ -2825,7 +2822,7 @@ namespace Game.Entities // 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 isInAir = (MathFunctions.fuzzyGt(GetPositionZMinusOffset(), ground + MapConst.GroundHeightTolerance) || MathFunctions.fuzzyLt(GetPositionZMinusOffset(), ground - MapConst.GroundHeightTolerance)); // Can be underground too, prevent the falling + bool isInAir = (MathFunctions.fuzzyGt(GetPositionZ(), ground + MapConst.GroundHeightTolerance) || MathFunctions.fuzzyLt(GetPositionZ(), ground - MapConst.GroundHeightTolerance)); // Can be underground too, prevent the falling if (GetCreatureTemplate().InhabitType.HasAnyFlag(InhabitType.Air) && isInAir && !IsFalling()) { @@ -3138,7 +3135,7 @@ namespace Game.Entities return false; //We should set first home position, because then AI calls home movement - SetHomePosition(data.spawnPoint); + SetHomePosition(this); m_deathState = DeathState.Alive; diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 16d53f8fd..7c11f3da1 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -2095,7 +2095,9 @@ namespace Game.Entities public void UpdateGroundPositionZ(float x, float y, ref float z) { - z = GetMapHeight(x, y, z); + float newZ = GetMapHeight(x, y, z); + if (newZ > MapConst.InvalidHeight) + z = newZ + (IsUnit() ? ToUnit().GetHoverOffset() : 0.0f); } public void UpdateAllowedPositionZ(float x, float y, ref float z) @@ -2104,66 +2106,45 @@ namespace Game.Entities if (GetTransport()) return; - switch (GetTypeId()) + Unit unit = ToUnit(); + if (unit != null) { - case TypeId.Unit: + if (!unit.CanFly()) { - // non fly unit don't must be in air - // non swim unit must be at ground (mostly speedup, because it don't must be in water and water level check less fast - if (!ToCreature().CanFly()) - { - bool canSwim = ToCreature().CanSwim(); - float ground_z = z; - float max_z = canSwim - ? GetMapWaterOrGroundLevel(x, y, z, ref ground_z) - : (ground_z = GetMapHeight(x, y, z)); - if (max_z > MapConst.InvalidHeight) - { - if (z > max_z) - z = max_z; - else if (z < ground_z) - z = ground_z; - } - } + bool canSwim = unit.CanSwim(); + float groundZ = z; + float max_z; + if (canSwim) + max_z = GetMapWaterOrGroundLevel(x, y, z, ref groundZ); else + max_z = groundZ = GetMapHeight(x, y, z); + + if (max_z > MapConst.InvalidHeight) { - float ground_z = GetMapHeight(x, y, z); - if (MathF.Abs(z - ground_z) < GetCollisionHeight()) - z = ground_z; + // hovering units cannot go below their hover height + float hoverOffset = unit.GetHoverOffset(); + max_z += hoverOffset; + groundZ += hoverOffset; + + if (z > max_z) + z = max_z; + else if (z < groundZ) + z = groundZ; } - break; } - case TypeId.Player: + else { - // for server controlled moves playr work same as creature (but it can always swim) - if (!ToPlayer().CanFly()) - { - float ground_z = z; - float max_z = GetMapWaterOrGroundLevel(x, y, z, ref ground_z); - if (max_z > MapConst.InvalidHeight) - { - if (z > max_z) - z = max_z; - else if (z < ground_z) - z = ground_z; - } - } - else - { - float ground_z = GetMapHeight(x, y, z); - if (MathF.Abs(z - ground_z) < GetCollisionHeight()) - z = ground_z; - } - break; - } - default: - { - float ground_z = GetMapHeight(x, y, z); - if (ground_z > MapConst.InvalidHeight) + float ground_z = GetMapHeight(x, y, z) + unit.GetHoverOffset(); + if (z < ground_z) z = ground_z; - break; } } + else + { + float ground_z = GetMapHeight(x, y, z); + if (ground_z > MapConst.InvalidHeight) + z = ground_z; + } } public void GetNearPoint2D(out float x, out float y, float distance2d, float absAngle) diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 1ab989010..13acb7345 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -1531,7 +1531,7 @@ namespace Game.Entities float dx = GetPositionX() - obj.GetPositionX(); float dy = GetPositionY() - obj.GetPositionY(); - float dz = GetPositionZMinusOffset() - obj.GetPositionZMinusOffset(); + float dz = GetPositionZ() - obj.GetPositionZ(); float distsq = (dx * dx) + (dy * dy) + (dz * dz); float maxdist = GetMeleeRange(obj) + GetTotalAuraModifier(AuraType.ModAutoAttackRange); diff --git a/Source/Game/Entities/Unit/Unit.Movement.cs b/Source/Game/Entities/Unit/Unit.Movement.cs index cd4962b0c..73d26daa8 100644 --- a/Source/Game/Entities/Unit/Unit.Movement.cs +++ b/Source/Game/Entities/Unit/Unit.Movement.cs @@ -198,7 +198,7 @@ namespace Game.Entities return; MoveSplineInit init = new(this); - init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset(), false); + init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZ(), false); init.SetFacing(ori); init.Launch(); } @@ -616,6 +616,11 @@ namespace Game.Entities GetVehicleKit().RelocatePassengers(); } + public float GetHoverOffset() + { + return HasUnitMovementFlag(MovementFlag.Hover) ? m_unitData.HoverHeight : 0.0f; + } + public bool IsWithinBoundaryRadius(Unit obj) { if (!obj || !IsInMap(obj) || !IsInPhase(obj)) @@ -1577,15 +1582,6 @@ namespace Game.Entities SendMessageToSet(data, true); } - public float GetPositionZMinusOffset() - { - float offset = 0.0f; - if (HasUnitMovementFlag(MovementFlag.Hover)) - offset = m_unitData.HoverHeight; - - return GetPositionZ() - offset; - } - public Unit GetUnitBeingMoved() { Player player = ToPlayer(); diff --git a/Source/Game/Handlers/MovementHandler.cs b/Source/Game/Handlers/MovementHandler.cs index a61a502ef..d358d44c9 100644 --- a/Source/Game/Handlers/MovementHandler.cs +++ b/Source/Game/Handlers/MovementHandler.cs @@ -264,10 +264,7 @@ namespace Game return; } - float z = loc.GetPositionZ(); - if (GetPlayer().HasUnitMovementFlag(MovementFlag.Hover)) - z += GetPlayer().m_unitData.HoverHeight; - + float z = loc.GetPositionZ() + GetPlayer().GetHoverOffset(); GetPlayer().Relocate(loc.GetPositionX(), loc.GetPositionY(), z, loc.GetOrientation()); GetPlayer().SetFallInformation(0, GetPlayer().GetPositionZ()); diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 7f6f2b303..31111c57a 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -1016,12 +1016,6 @@ namespace Game.Maps var oldcell = player.GetCurrentCell(); var newcell = new Cell(x, y); - //! If hovering, always increase our server-side Z position - //! Client automatically projects correct position based on Z coord sent in monster move - //! and HoverHeight sent in object updates - if (player.HasUnitMovementFlag(MovementFlag.Hover)) - z += player.m_unitData.HoverHeight; - player.Relocate(x, y, z, orientation); if (player.IsVehicle()) player.GetVehicleKit().RelocatePassengers(); @@ -1052,12 +1046,6 @@ namespace Game.Maps if (!respawnRelocationOnFail && GetGrid(new_cell.GetGridX(), new_cell.GetGridY()) == null) return; - //! If hovering, always increase our server-side Z position - //! Client automatically projects correct position based on Z coord sent in monster move - //! and HoverHeight sent in object updates - if (creature.HasUnitMovementFlag(MovementFlag.Hover)) - z += creature.m_unitData.HoverHeight; - Cell old_cell = creature.GetCurrentCell(); // delay creature move for grid/cell to grid/cell moves if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell)) diff --git a/Source/Game/Movement/Generators/HomeMovement.cs b/Source/Game/Movement/Generators/HomeMovement.cs index 7beea0e73..d97f34244 100644 --- a/Source/Game/Movement/Generators/HomeMovement.cs +++ b/Source/Game/Movement/Generators/HomeMovement.cs @@ -67,6 +67,7 @@ namespace Game.AI owner.GetHomePosition(out x, out y, out z, out o); init.SetFacing(o); } + owner.UpdateAllowedPositionZ(x, y, ref z); init.MoveTo(x, y, z); init.SetWalk(false); init.Launch(); diff --git a/Source/Game/Movement/Generators/TargetMovement.cs b/Source/Game/Movement/Generators/TargetMovement.cs index 1a28e4510..50f8794fb 100644 --- a/Source/Game/Movement/Generators/TargetMovement.cs +++ b/Source/Game/Movement/Generators/TargetMovement.cs @@ -141,30 +141,39 @@ namespace Game.Movement float x, y, z; if (updateDestination || _path == null) { + float size = owner.GetCombatReach(); + float hoverDiff = owner.GetHoverOffset() - GetTarget().GetHoverOffset(); if (_offset == 0) { if (GetTarget().IsWithinDistInMap(owner, SharedConst.ContactDistance)) return; // to nearest contact position - GetTarget().GetContactPoint(owner, out x, out y, out z); + if (hoverDiff != 0f) + size = size > hoverDiff ? MathF.Sqrt(size * size - hoverDiff * hoverDiff) : 0.0f; + + GetTarget().GetNearPoint(owner, out x, out y, out z, size, SharedConst.ContactDistance, GetTarget().GetAngle(owner)); } else { float distance = _offset + 1.0f; - float size = owner.GetCombatReach(); if (owner.IsPet() && GetTarget().GetTypeId() == TypeId.Player) { distance = 1.0f; size = 1.0f; } + else if (hoverDiff != 0) + size = size > hoverDiff ? MathF.Sqrt(size * size - hoverDiff * hoverDiff) : 0.0f; if (GetTarget().IsWithinDistInMap(owner, distance)) return; GetTarget().GetClosePoint(out x, out y, out z, size, _offset, _angle); } + + if (owner.IsHovering()) + owner.UpdateAllowedPositionZ(x, y, ref z); } else { diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 12b1ed8d6..5b1bf650d 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -286,7 +286,7 @@ namespace Game.Movement { // we are already close enough. We just need to turn toward the target without changing position. MoveSplineInit init = new(_owner); - init.MoveTo(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZMinusOffset()); + init.MoveTo(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ()); init.SetFacing(target); init.Launch(); StartMovement(new EffectMovementGenerator(id), MovementSlot.Active); @@ -440,7 +440,7 @@ namespace Game.Movement if (_owner.IsFlying()) point.Z = z; else - point.Z = _owner.GetMapHeight(point.X, point.Y, z); + point.Z = _owner.GetMapHeight(point.X, point.Y, z) + _owner.GetHoverOffset(); init.args.path[i] = point; } @@ -536,7 +536,7 @@ namespace Game.Movement } MoveSplineInit init = new(_owner); - init.MoveTo(_owner.GetPositionX(), _owner.GetPositionY(), tz, false); + init.MoveTo(_owner.GetPositionX(), _owner.GetPositionY(), tz + _owner.GetHoverOffset(), false); init.SetFall(); init.Launch(); StartMovement(new EffectMovementGenerator(id), MovementSlot.Controlled);