[Core] Improved Blink (c2618,c2623)
1 - Implement generating path ( where unit can walk - there blink will available ) 2 - Implement Water path (if blink starting before (or in) water - Z coord will safe, if starting IN water, and last point on a ground (if it available) - new Z will on a ground. 3 - Implement Safe blink , both parts : * - blink at abyss ( unit will stoped before abyss ) * - blink in falling, where difference between unit Z and land Z < 8 yards - unit will blink on a land, if blink in falling, but difference will more then 8 yards - just blink forward All parts of blink has check LoS (c2618) Blink -> Check if we are near of liquid level before assigning previous Z to next Z. (c2623) @cyberium Also when we tp to water we have to set final z a bit under the liquid level. Not at liquid level.
This commit is contained in:
parent
4de5fa3543
commit
4839283c1c
@ -4929,24 +4929,28 @@ SpellCastResult Spell::CheckCast(bool strict)
|
||||
case SPELL_EFFECT_LEAP:
|
||||
case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER:
|
||||
{
|
||||
float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
|
||||
float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation());
|
||||
float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation());
|
||||
// teleport a bit above terrain level to avoid falling below it
|
||||
float fz = m_caster->GetMap()->GetHeight(fx, fy, m_caster->GetPositionZ());
|
||||
if (fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled
|
||||
{ return SPELL_FAILED_TRY_AGAIN; }
|
||||
if (!m_caster || m_caster->IsTaxiFlying())
|
||||
return SPELL_FAILED_NOT_ON_TAXI;
|
||||
|
||||
float caster_pos_z = m_caster->GetPositionZ();
|
||||
// Control the caster to not climb or drop when +-fz > 8
|
||||
if (!(fz <= caster_pos_z + 8 && fz >= caster_pos_z - 8))
|
||||
{ return SPELL_FAILED_TRY_AGAIN; }
|
||||
// Blink has leap first and then removing of auras with root effect
|
||||
// need further research with this
|
||||
if (m_spellInfo->Effect[i] != SPELL_EFFECT_LEAP)
|
||||
{
|
||||
if (m_caster->hasUnitState(UNIT_STAT_ROOT))
|
||||
return SPELL_FAILED_ROOTED;
|
||||
}
|
||||
|
||||
// not allow use this effect at battleground until battleground start
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (((Player*)m_caster)->HasMovementFlag(MOVEFLAG_ONTRANSPORT))
|
||||
return SPELL_FAILED_NOT_ON_TRANSPORT;
|
||||
|
||||
// not allow use this effect at battleground until battleground start
|
||||
if (BattleGround const* bg = ((Player*)m_caster)->GetBattleGround())
|
||||
if (bg->GetStatus() != STATUS_IN_PROGRESS)
|
||||
{ return SPELL_FAILED_TRY_AGAIN; }
|
||||
return SPELL_FAILED_TRY_AGAIN;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "Util.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "G3D/Vector3.h"
|
||||
#ifdef ENABLE_ELUNA
|
||||
#include "LuaEngine.h"
|
||||
#endif /* ENABLE_ELUNA */
|
||||
@ -4360,24 +4361,160 @@ void Spell::EffectBlock(SpellEffectIndex /*eff_idx*/)
|
||||
|
||||
void Spell::EffectLeapForward(SpellEffectIndex eff_idx)
|
||||
{
|
||||
if (unitTarget->IsTaxiFlying())
|
||||
{ return; }
|
||||
float dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx]));
|
||||
const float IN_OR_UNDER_LIQUID_RANGE = 0.8f; // range to make player under liquid or on liquid surface from liquid level
|
||||
|
||||
if (m_spellInfo->rangeIndex == SPELL_RANGE_IDX_SELF_ONLY)
|
||||
G3D::Vector3 prevPos, nextPos;
|
||||
float orientation = unitTarget->GetOrientation();
|
||||
|
||||
prevPos.x = unitTarget->GetPositionX();
|
||||
prevPos.y = unitTarget->GetPositionY();
|
||||
prevPos.z = unitTarget->GetPositionZ();
|
||||
|
||||
float groundZ = prevPos.z;
|
||||
bool isPrevInLiquid = false;
|
||||
|
||||
// falling case
|
||||
if (!unitTarget->GetMap()->GetHeightInRange(prevPos.x, prevPos.y, groundZ, 3.0f) && unitTarget->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLING))
|
||||
{
|
||||
float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx]));
|
||||
nextPos.x = prevPos.x + dist * cos(orientation);
|
||||
nextPos.y = prevPos.y + dist * sin(orientation);
|
||||
nextPos.z = prevPos.z - 2.0f; // little hack to avoid the impression to go up when teleporting instead of continue to fall. This value may need some tweak
|
||||
|
||||
// before caster
|
||||
float fx, fy, fz;
|
||||
unitTarget->GetClosePoint(fx, fy, fz, unitTarget->GetObjectBoundingRadius(), dis);
|
||||
float ox, oy, oz;
|
||||
unitTarget->GetPosition(ox, oy, oz);
|
||||
//
|
||||
GridMapLiquidData liquidData;
|
||||
if (unitTarget->GetMap()->GetTerrain()->IsInWater(nextPos.x, nextPos.y, nextPos.z, &liquidData))
|
||||
{
|
||||
if (fabs(nextPos.z - liquidData.level) < 10.0f)
|
||||
nextPos.z = liquidData.level - IN_OR_UNDER_LIQUID_RANGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fix z to ground if near of it
|
||||
unitTarget->GetMap()->GetHeightInRange(nextPos.x, nextPos.y, nextPos.z, 10.0f);
|
||||
}
|
||||
|
||||
if (unitTarget->GetMap()->GetHitPosition(ox, oy, oz + 0.5f, fx, fy, fz, -0.5f))
|
||||
{ unitTarget->UpdateAllowedPositionZ(fx, fy, fz); }
|
||||
// check any obstacle and fix coords
|
||||
unitTarget->GetMap()->GetHitPosition(prevPos.x, prevPos.y, prevPos.z + 0.5f, nextPos.x, nextPos.y, nextPos.z, -0.5f);
|
||||
|
||||
unitTarget->NearTeleportTo(fx, fy, fz, unitTarget->GetOrientation(), unitTarget == m_caster);
|
||||
// teleport
|
||||
unitTarget->NearTeleportTo(nextPos.x, nextPos.y, nextPos.z, orientation, unitTarget == m_caster);
|
||||
|
||||
//sLog.outString("Falling BLINK!");
|
||||
return;
|
||||
}
|
||||
|
||||
// fix origin position if player was jumping and near of the ground but not in ground
|
||||
if (fabs(prevPos.z - groundZ) > 0.5f)
|
||||
prevPos.z = groundZ;
|
||||
|
||||
//check if in liquid
|
||||
isPrevInLiquid = unitTarget->GetMap()->GetTerrain()->IsInWater(prevPos.x, prevPos.y, prevPos.z);
|
||||
|
||||
const float step = 2.0f; // step length before next check slope/edge/water
|
||||
const float maxSlope = 50.0f; // 50(degree) max seem best value for walkable slope
|
||||
const float MAX_SLOPE_IN_RADIAN = maxSlope / 180.0f * M_PI_F;
|
||||
float nextZPointEstimation = 1.0f;
|
||||
float destx = prevPos.x + dist * cos(orientation);
|
||||
float desty = prevPos.y + dist * sin(orientation);
|
||||
const uint32 numChecks = ceil(fabs(dist / step));
|
||||
const float DELTA_X = (destx - prevPos.x) / numChecks;
|
||||
const float DELTA_Y = (desty - prevPos.y) / numChecks;
|
||||
|
||||
for (uint32 i = 1; i < numChecks + 1; ++i)
|
||||
{
|
||||
// compute next point average position
|
||||
nextPos.x = prevPos.x + DELTA_X;
|
||||
nextPos.y = prevPos.y + DELTA_Y;
|
||||
nextPos.z = prevPos.z + nextZPointEstimation;
|
||||
|
||||
bool isInLiquid = false;
|
||||
bool isInLiquidTested = false;
|
||||
bool isOnGround = false;
|
||||
GridMapLiquidData liquidData;
|
||||
|
||||
// try fix height for next position
|
||||
if (!unitTarget->GetMap()->GetHeightInRange(nextPos.x, nextPos.y, nextPos.z))
|
||||
{
|
||||
// we cant so test if we are on water
|
||||
if (!unitTarget->GetMap()->GetTerrain()->IsInWater(nextPos.x, nextPos.y, nextPos.z, &liquidData))
|
||||
{
|
||||
// not in water and cannot get correct height, maybe flying?
|
||||
//sLog.outString("Can't get height of point %u, point value %s", i, nextPos.toString().c_str());
|
||||
nextPos = prevPos;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
isInLiquid = true;
|
||||
isInLiquidTested = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
isOnGround = true; // player is on ground
|
||||
|
||||
if (isInLiquid || (!isInLiquidTested && unitTarget->GetMap()->GetTerrain()->IsInWater(nextPos.x, nextPos.y, nextPos.z, &liquidData)))
|
||||
{
|
||||
if (!isPrevInLiquid && fabs(liquidData.level - prevPos.z) > 2.0f)
|
||||
{
|
||||
// on edge of water with difference a bit to high to continue
|
||||
//sLog.outString("Ground vs liquid edge detected!");
|
||||
nextPos = prevPos;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((liquidData.level - IN_OR_UNDER_LIQUID_RANGE) > nextPos.z)
|
||||
nextPos.z = prevPos.z; // we are under water so next z equal prev z
|
||||
else
|
||||
nextPos.z = liquidData.level - IN_OR_UNDER_LIQUID_RANGE; // we are on water surface, so next z equal liquid level
|
||||
|
||||
isInLiquid = true;
|
||||
|
||||
float ground = nextPos.z;
|
||||
if (unitTarget->GetMap()->GetHeightInRange(nextPos.x, nextPos.y, ground))
|
||||
{
|
||||
if (nextPos.z < ground)
|
||||
{
|
||||
nextPos.z = ground;
|
||||
isOnGround = true; // player is on ground of the water
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//unitTarget->SummonCreature(VISUAL_WAYPOINT, nextPos.x, nextPos.y, nextPos.z, 0, TEMPSUMMON_TIMED_DESPAWN, 15000);
|
||||
float hitZ = nextPos.z + 1.5f;
|
||||
if (unitTarget->GetMap()->GetHitPosition(prevPos.x, prevPos.y, prevPos.z + 1.5f, nextPos.x, nextPos.y, hitZ, -1.0f))
|
||||
{
|
||||
//sLog.outString("Blink collision detected!");
|
||||
nextPos = prevPos;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isOnGround)
|
||||
{
|
||||
// project vector to get only positive value
|
||||
float ac = fabs(prevPos.z - nextPos.z);
|
||||
|
||||
// compute slope (in radian)
|
||||
float slope = atan(ac / step);
|
||||
|
||||
// check slope value
|
||||
if (slope > MAX_SLOPE_IN_RADIAN)
|
||||
{
|
||||
//sLog.outString("bad slope detected! %4.2f max %4.2f, ac(%4.2f)", slope * 180 / M_PI_F, maxSlope, ac);
|
||||
nextPos = prevPos;
|
||||
break;
|
||||
}
|
||||
//sLog.outString("slope is ok! %4.2f max %4.2f, ac(%4.2f)", slope * 180 / M_PI_F, maxSlope, ac);
|
||||
}
|
||||
|
||||
//sLog.outString("point %u is ok, coords %s", i, nextPos.toString().c_str());
|
||||
nextZPointEstimation = (nextPos.z - prevPos.z) / 2.0f;
|
||||
isPrevInLiquid = isInLiquid;
|
||||
prevPos = nextPos;
|
||||
}
|
||||
|
||||
unitTarget->NearTeleportTo(nextPos.x, nextPos.y, nextPos.z, orientation, unitTarget == m_caster);
|
||||
}
|
||||
|
||||
void Spell::EffectReputation(SpellEffectIndex eff_idx)
|
||||
|
Loading…
x
Reference in New Issue
Block a user