Skip to content

Commit

Permalink
Core/Movement: fix blink, try to keep units from going underground (#68)
Browse files Browse the repository at this point in the history
* Core/Movement: fix blink, try to keep units from going underground

* tweak
  • Loading branch information
jasongdove authored Oct 22, 2024
1 parent 8636b18 commit 8ed42de
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 112 deletions.
134 changes: 53 additions & 81 deletions src/server/game/Entities/Object/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2365,98 +2365,53 @@ void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const
z = new_z + 0.08f; // just to be sure that we are not a few pixel under the surface
}

void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z, float* groundZ) const
{
float _offset = GetPositionH() < 2.0f ? 2.0f : 0.0f; // For find correct position Z
bool isFalling = m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR);
// TODO: Allow transports to be part of dynamic vmap tree
if (GetTransport())
{
if (groundZ)
*groundZ = z;

switch (GetTypeId())
return;
}

if (Unit const* unit = ToUnit())
{
case TYPEID_UNIT:
if (!unit->CanFly())
{
Unit* victim = ToCreature()->getVictim();
if (victim)
{
// anyway creature move to victim for thinly Z distance (shun some VMAP wrong ground calculating)
if (fabs(GetPositionZ() - victim->GetPositionZ()) < 2.5f)
return;
}
// 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
? GetWaterOrGroundLevel(x, y, z + _offset, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK))
: ((ground_z = GetHeight(x, y, z + _offset, true)));

if (isFalling) // Allowed point in air if we falling
if ((z - max_z) > 2.0f)
return;

max_z += GetPositionH();
ground_z += GetPositionH();
if (max_z > INVALID_HEIGHT)
{
if (z > max_z && !IsInWater())
z = max_z;
else if (z < ground_z)
z = ground_z;
}
}
bool canSwim = unit->CanSwim();
float ground_z = z;
float max_z;
if (canSwim)
max_z = GetWaterOrGroundLevel(x, y, z, &ground_z);
else
{
float ground_z = GetHeight(x, y, z + _offset, true);
ground_z += GetPositionH();
if (z < ground_z)
z = ground_z;
}
break;
}
case TYPEID_PLAYER:
{
// for server controlled moves playr work same as creature (but it can always swim)
if (!ToPlayer()->CanFly())
{
float ground_z = z;
float max_z = GetWaterOrGroundLevel(x, y, z + _offset, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK));
max_z += GetPositionH();

if (isFalling) // Allowed point in air if we falling
if ((z - max_z) > 2.0f)
return;
max_z = ground_z = GetHeight(x, y, z);

ground_z += GetPositionH();
if (max_z > INVALID_HEIGHT)
{
if (z > max_z && !IsInWater())
z = max_z;
else if (z < ground_z)
z = ground_z;
}
}
else
if (max_z > INVALID_HEIGHT)
{
float ground_z = GetHeight(x, y, z + _offset, true);
ground_z += GetPositionH();
if (z < ground_z)
// hovering units cannot go below their hover height
float hoverOffset = unit->GetHoverOffset();
max_z += hoverOffset;
ground_z += hoverOffset;

if (z > max_z)
z = max_z;
else if (z < ground_z)
z = ground_z;
}
break;

if (groundZ)
*groundZ = ground_z;
}
default:
else
{
float ground_z = GetHeight(x, y, z + _offset, true);
ground_z += GetPositionH();

if (isFalling) // Allowed point in air if we falling
if ((z - ground_z) > 2.0f)
return;

if (ground_z > INVALID_HEIGHT)
float ground_z = GetHeight(x, y, z) + unit->GetHoverOffset();
if (z < ground_z)
z = ground_z;
break;

if (groundZ)
*groundZ = ground_z;
}
}
}
Expand Down Expand Up @@ -3550,10 +3505,27 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float
}
}

float groundZ = VMAP_INVALID_HEIGHT_VALUE;
Trinity::NormalizeMapCoord(pos.m_positionX);
Trinity::NormalizeMapCoord(pos.m_positionY);
UpdateAllowedPositionZ(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
UpdateAllowedPositionZ(pos.m_positionX, pos.m_positionY, pos.m_positionZ, &groundZ);
pos.SetOrientation(GetOrientation());

// position has no ground under it (or is too far away)
if (groundZ <= INVALID_HEIGHT)
{
if (Unit const* unit = ToUnit())
{
// unit can fly, ignore
if (unit->CanFly())
return;

// fall back to gridHeight if any
float gridHeight = GetMap()->GetGridMapHeigh(pos.m_positionX, pos.m_positionY);
if (gridHeight > INVALID_HEIGHT)
pos.m_positionZ = gridHeight + unit->GetHoverOffset();
}
}
}

void WorldObject::MovePositionToTransportCollision(Position &pos, float dist, float angle)
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Entities/Object/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ class WorldObject : public Object, public WorldLocation
float GetObjectSize() const;
virtual float GetCombatReach() const { return 0.0f; } // overridden (only) in Unit
void UpdateGroundPositionZ(float x, float y, float &z) const;
void UpdateAllowedPositionZ(float x, float y, float &z) const;
void UpdateAllowedPositionZ(float x, float y, float &z, float* groundZ = nullptr) const;
virtual bool IsInWater() const { return false; }
virtual bool IsUnderWater() const { return false; }

Expand Down
19 changes: 8 additions & 11 deletions src/server/game/Maps/Map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3047,13 +3047,10 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
{
// find raw .map surface under Z coordinates
float mapHeight = VMAP_INVALID_HEIGHT_VALUE;
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
{
float gridHeight = gmap->getHeight(x, y);
// look from a bit higher pos to find the floor, ignore under surface case
if (z + 2.0f > gridHeight)
mapHeight = gridHeight;
}
float gridHeight = GetGridMapHeigh(x, y);
// look from a bit higher pos to find the floor, ignore under surface case
if (z + 2.0f > gridHeight)
mapHeight = gridHeight;

float vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
if (checkVMap)
Expand All @@ -3075,11 +3072,11 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
// or if the distance of the vmap height is less the land height distance
if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z))
return vmapHeight;
return mapHeight;
// better use .map surface height

return mapHeight; // better use .map surface height
}
return vmapHeight;
// we have only vmapHeight (if have)

return vmapHeight; // we have only vmapHeight (if have)
}

return mapHeight; // explicitly use map data
Expand Down
52 changes: 38 additions & 14 deletions src/server/game/Spells/Spell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,28 +1225,29 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge

void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
{
m_targets.SetDst(*m_caster);
SpellDestination dest(*m_caster);
//m_targets.SetDst(*m_caster);

switch (targetType.GetTarget())
{
case TARGET_DEST_HOME:
if (Player* playerCaster = m_caster->ToPlayer())
m_targets.SetDst(playerCaster->m_homebindX, playerCaster->m_homebindY, playerCaster->m_homebindZ, playerCaster->GetOrientation(), playerCaster->m_homebindMapId);
dest = SpellDestination(playerCaster->m_homebindX, playerCaster->m_homebindY, playerCaster->m_homebindZ, playerCaster->GetOrientation(), playerCaster->m_homebindMapId);
return;
case TARGET_DEST_DB:
if (SpellTargetPosition const* st = sSpellMgr->GetSpellTargetPosition(m_spellInfo->Id))
{
// TODO: fix this check
if (m_spellInfo->HasEffect(SPELL_EFFECT_TELEPORT_L) || m_spellInfo->HasEffect(SPELL_EFFECT_TELEPORT))
m_targets.SetDst(st->target_X, st->target_Y, st->target_Z, st->target_Orientation, (int32)st->target_mapId);
dest = SpellDestination(st->target_X, st->target_Y, st->target_Z, st->target_Orientation, (int32)st->target_mapId);
else if (st->target_mapId == m_caster->GetMapId())
m_targets.SetDst(st->target_X, st->target_Y, st->target_Z, st->target_Orientation);
dest = SpellDestination(st->target_X, st->target_Y, st->target_Z, st->target_Orientation);
}
else
{
TC_LOG_DEBUG("spells", "SPELL: unknown target coordinates for spell ID %u", m_spellInfo->Id);
WorldObject* target = m_targets.GetObjectTarget();
m_targets.SetDst(target ? *target : *m_caster);
if (WorldObject* target = m_targets.GetObjectTarget())
dest = SpellDestination(*target);
}
return;
case TARGET_DEST_CASTER_FISHING:
Expand Down Expand Up @@ -1280,8 +1281,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
return;
}

Position pos{x, y, liquidLevel, m_caster->GetOrientation()};
m_targets.ModDst(pos);
dest = SpellDestination(x, y, liquidLevel, m_caster->GetOrientation());
return;
}
default:
Expand All @@ -1306,7 +1306,6 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
else if (targetType.GetTarget() == TARGET_DEST_CASTER_RANDOM)
dist = objSize + (dist - objSize) * static_cast<float>(rand_norm());

Position pos;
switch (targetType.GetTarget())
{
case TARGET_DEST_CASTER_FRONT_LEAP:
Expand All @@ -1315,12 +1314,24 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
if (!unitCaster)
break;

float dist = m_spellInfo->GetEffect(effIndex, m_diffMode)->CalcRadius(m_caster);
float angle = targetType.CalcDirectionAngle();
Position pos = dest._position;

Position pos = static_cast<Position>(*m_targets.GetDstPos());
unitCaster->MovePositionToFirstCollision(pos, dist, angle);
m_targets.SetDst(pos);
// Generate path to that point
if (!m_preGeneratedPath)
m_preGeneratedPath = std::make_unique<PathGenerator>(unitCaster);

m_preGeneratedPath->SetPathLengthLimit(dist);

// Should we use straightline here ? What do we do when we don't have a full path ?
bool pathResult = m_preGeneratedPath->CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
if (pathResult && m_preGeneratedPath->GetPathType() & (PATHFIND_NORMAL | PATHFIND_SHORTCUT))
{
pos.m_positionX = m_preGeneratedPath->GetActualEndPosition().x;
pos.m_positionY = m_preGeneratedPath->GetActualEndPosition().y;
pos.m_positionZ = m_preGeneratedPath->GetActualEndPosition().z;
dest.Relocate(pos);
}
break;

// if (canHitTargetInLOS && m_caster->IsCreature() && dist < 200.0f)
Expand Down Expand Up @@ -1410,16 +1421,29 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
case TARGET_DEST_CASTER_RIGHT:
case TARGET_DEST_CASTER_LEFT:
case TARGET_UNK_125:
{
Position pos = dest._position;

if (canHitTargetInLOS && m_caster->IsCreature() && dist < 200.0f)
m_caster->GetNearPoint2D(pos, dist, angle, false);
else
m_caster->GetFirstCollisionPosition(pos, dist, angle);

dest.Relocate(pos);
break;
}
default:
{
Position pos = dest._position;

m_caster->GetNearPoint2D(pos, dist, angle);

dest.Relocate(pos);
break;
}
}
m_targets.ModDst(pos);

m_targets.SetDst(dest);
}

void Spell::SelectImplicitGotoMoveTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& /*targetType*/, uint32 /*effMask*/)
Expand Down
6 changes: 1 addition & 5 deletions src/server/game/Spells/SpellEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6672,11 +6672,7 @@ void Spell::EffectLeap(SpellEffIndex /*effIndex*/)
if (!m_targets.HasDst())
return;

Position pos = static_cast<Position>(*m_targets.GetDstPos());
unitTarget->AddUnitState(UNIT_STATE_JUMPING);

// TC_LOG_DEBUG("spells", "EffectLeap If %i, X %f, Y %f, Z %f", m_spellInfo->Id, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());

Position pos = destTarget->GetPosition();
unitTarget->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), unitTarget == m_caster, false);
}

Expand Down
17 changes: 17 additions & 0 deletions src/server/game/Spells/SpellTargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ SpellDestination::SpellDestination(WorldObject const& wObj)
_position.SetOrientation(wObj.GetOrientation());
}

void SpellDestination::Relocate(Position const& pos)
{
if (!_transportGUID.IsEmpty())
{
Position offset;
_position.GetPositionOffsetTo(pos, offset);
_transportOffset.RelocateOffset(offset);
}
_position.Relocate(pos);
}

TargetInfo::TargetInfo(ObjectGuid tGUID, uint32 effMask) : TargetInfo()
{
targetGUID = tGUID;
Expand Down Expand Up @@ -314,6 +325,12 @@ void SpellCastTargets::SetDst(WorldObject const& wObj)
m_dst._position.SetMapId(m_caster->GetMapId());
}

void SpellCastTargets::SetDst(const SpellDestination& spellDest)
{
m_dst = spellDest;
m_targetMask |= TARGET_FLAG_DEST_LOCATION;
}

void SpellCastTargets::SetDst(SpellCastTargets const& spellTargets)
{
m_dst = spellTargets.m_dst;
Expand Down
3 changes: 3 additions & 0 deletions src/server/game/Spells/SpellTargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ struct SpellDestination
SpellDestination(Position const& pos);
SpellDestination(WorldObject const& wObj);

void Relocate(Position const& pos);

WorldLocation _position;
ObjectGuid _transportGUID;
Position _transportOffset;
Expand Down Expand Up @@ -187,6 +189,7 @@ class SpellCastTargets
void SetDst(float x, float y, float z, float orientation, uint32 mapId = MAPID_INVALID);
void SetDst(Position const& pos);
void SetDst(WorldObject const& wObj);
void SetDst(SpellDestination const& spellDest);
void SetDst(SpellCastTargets const& spellTargets);
void ModDst(Position const& pos);
void RemoveDst();
Expand Down

0 comments on commit 8ed42de

Please sign in to comment.