[11720] Implement spline movement subsystem

Spline movement controls movements of server-side controlled units (monster movement, taxi movement, etc).
Proper implementation of effects such as charge, jump, cyclic movement will rely on it.
However, need improve our states system before.

Technical changes:

 1. Added linear, catmullrom and bezier3 splines which based on client's algorthims. They can be reused for proper transport position interpolation.
 2. Precission increased. There are no more position desync issues since client's position calculation formulas used.
 3. Now possible to move by paths with multiple points, send whole path to client.
This commit is contained in:
SilverIce 2011-07-08 17:25:13 +03:00
parent e302fce513
commit 9d566398ad
52 changed files with 2471 additions and 1203 deletions

View file

@ -45,10 +45,10 @@
#include "MapPersistentStateMgr.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "Path.h"
#include "Traveller.h"
#include "VMapFactory.h"
#include "MovementGenerator.h"
#include "movement/MoveSplineInit.h"
#include "movement/MoveSpline.h"
#include <math.h>
#include <stdarg.h>
@ -184,7 +184,8 @@ void GlobalCooldownMgr::CancelGlobalCooldown(SpellEntry const* spellInfo)
Unit::Unit() :
i_motionMaster(this), m_ThreatManager(this), m_HostileRefManager(this),
m_charmInfo(NULL),
m_vehicleInfo(NULL)
m_vehicleInfo(NULL),
movespline(new Movement::MoveSpline())
{
m_objectType |= TYPEMASK_UNIT;
m_objectTypeId = TYPEID_UNIT;
@ -279,6 +280,7 @@ Unit::~Unit()
delete m_charmInfo;
delete m_vehicleInfo;
delete movespline;
// those should be already removed at "RemoveFromWorld()" call
MANGOS_ASSERT(m_gameObj.size() == 0);
@ -345,7 +347,7 @@ void Unit::Update( uint32 update_diff, uint32 p_time )
ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth()*0.20f);
ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, GetHealth() < GetMaxHealth()*0.35f);
ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, GetHealth() > GetMaxHealth()*0.75f);
UpdateSplineMovement(p_time);
i_motionMaster.UpdateMotion(p_time);
}
@ -360,83 +362,13 @@ bool Unit::haveOffhandWeapon() const
return false;
}
void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, SplineType type, SplineFlags flags, uint32 Time, Player* player, ...)
{
va_list vargs;
va_start(vargs,player);
float moveTime = (float)Time;
WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) );
data << GetPackGUID();
data << uint8(0); // new in 3.1 bool, used to toggle MOVEFLAG2_UNK4 = 0x0040 on client side
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << uint32(WorldTimer::getMSTime());
data << uint8(type); // unknown
switch(type)
{
case SPLINETYPE_NORMAL: // normal packet
break;
case SPLINETYPE_STOP: // stop packet (raw pos?)
va_end(vargs);
SendMessageToSet( &data, true );
return;
case SPLINETYPE_FACINGSPOT: // facing spot, not used currently
{
data << float(va_arg(vargs,double));
data << float(va_arg(vargs,double));
data << float(va_arg(vargs,double));
break;
}
case SPLINETYPE_FACINGTARGET:
data << uint64(va_arg(vargs,uint64)); // ObjectGuid in fact
break;
case SPLINETYPE_FACINGANGLE:
data << float(va_arg(vargs,double)); // facing angle
break;
}
data << uint32(flags); // splineflags
data << uint32(moveTime); // Time in between points
data << uint32(1); // 1 single waypoint
data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B
va_end(vargs);
if(player)
player->GetSession()->SendPacket(&data);
else
SendMessageToSet( &data, true );
}
void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime, Player* player)
{
if (!transitTime)
{
if(GetTypeId()==TYPEID_PLAYER)
{
Traveller<Player> traveller(*(Player*)this);
transitTime = traveller.GetTotalTravelTimeTo(x, y, z);
}
else
{
Traveller<Creature> traveller(*(Creature*)this);
transitTime = traveller.GetTotalTravelTimeTo(x, y, z);
}
}
//float orientation = (float)atan2((double)dy, (double)dx);
SplineFlags flags = GetTypeId() == TYPEID_PLAYER ? SPLINEFLAG_WALKMODE : ((Creature*)this)->GetSplineFlags();
SendMonsterMove(x, y, z, SPLINETYPE_NORMAL, flags, transitTime, player);
}
void Unit::SendHeartBeat(bool toSelf)
void Unit::SendHeartBeat()
{
m_movementInfo.UpdateTime(WorldTimer::getMSTime());
WorldPacket data(MSG_MOVE_HEARTBEAT, 64);
data << GetPackGUID();
data << m_movementInfo;
SendMessageToSet(&data, toSelf);
SendMessageToSet(&data, true);
}
void Unit::resetAttackTimer(WeaponAttackType type)
@ -3740,29 +3672,21 @@ void Unit::SetInFront(Unit const* target)
SetOrientation(GetAngle(target));
}
void Unit::SetFacingTo(float ori, bool bToSelf /*= false*/)
void Unit::SetFacingTo(float ori)
{
// update orientation at server
SetOrientation(ori);
// and client
SendHeartBeat(bToSelf);
Movement::MoveSplineInit init(*this);
init.SetFacing(ori);
init.Launch();
}
// Consider move this to Creature:: since only creature appear to be able to use this
void Unit::SetFacingToObject(WorldObject* pObject)
{
if (GetTypeId() != TYPEID_UNIT)
return;
// never face when already moving
if (!IsStopped())
return;
// TODO: figure out under what conditions creature will move towards object instead of facing it where it currently is.
SetOrientation(GetAngle(pObject));
SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(), SPLINETYPE_FACINGTARGET, ((Creature*)this)->GetSplineFlags(), 0, NULL, pObject->GetObjectGuid().GetRawValue());
SetFacingTo(GetAngle(pObject));
}
bool Unit::isInAccessablePlaceFor(Creature const* c) const
@ -8211,19 +8135,17 @@ void Unit::UpdateWalkMode(Unit* source, bool self)
CallForAllControlledUnits(UpdateWalkModeHelper(source), CONTROLLED_PET|CONTROLLED_GUARDIANS|CONTROLLED_CHARM|CONTROLLED_MINIPET);
else if (self)
{
bool on = source->GetTypeId() == TYPEID_PLAYER
? ((Player*)source)->HasMovementFlag(MOVEFLAG_WALK_MODE)
: ((Creature*)source)->HasSplineFlag(SPLINEFLAG_WALKMODE);
bool on = source->m_movementInfo.HasMovementFlag(MOVEFLAG_WALK_MODE);
if (on)
{
if (((Creature*)this)->IsPet() && hasUnitState(UNIT_STAT_FOLLOW))
((Creature*)this)->AddSplineFlag(SPLINEFLAG_WALKMODE);
((Creature*)this)->SetWalk(true);
}
else
{
if (((Creature*)this)->IsPet())
((Creature*)this)->RemoveSplineFlag(SPLINEFLAG_WALKMODE);
((Creature*)this)->SetWalk(false);
}
}
else
@ -8469,20 +8391,7 @@ void Unit::SetDeathState(DeathState s)
RemoveMiniPet();
UnsummonAllTotems();
// after removing a Fearaura (in RemoveAllAurasOnDeath)
// Unit::SetFeared is called and makes that creatures attack player again
if (GetTypeId() == TYPEID_UNIT)
{
clearUnitState(UNIT_STAT_MOVING);
GetMap()->CreatureRelocation((Creature*)this, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(), SPLINETYPE_NORMAL, SPLINEFLAG_WALKMODE, 0);
}
else
{
if (!IsStopped())
StopMoving();
}
StopMoving();
ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
@ -9981,12 +9890,9 @@ void Unit::StopMoving()
if (!IsInWorld())
return;
// send explicit stop packet
// player expected for correct work SPLINEFLAG_WALKMODE
SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(), SPLINETYPE_STOP, GetTypeId() == TYPEID_PLAYER ? SPLINEFLAG_WALKMODE : SPLINEFLAG_NONE, 0);
// update position and orientation for near players
SendHeartBeat(false);
Movement::MoveSplineInit init(*this);
init.SetFacing(GetOrientation());
init.Launch();
}
void Unit::SetFeared(bool apply, ObjectGuid casterGuid, uint32 spellID, uint32 time)
@ -10529,7 +10435,7 @@ void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool ca
GetMap()->CreatureRelocation((Creature*)this, x, y, z, orientation);
SendHeartBeat(false);
SendHeartBeat();
// finished relocation, movegen can different from top before creature relocation,
// but apply Reset expected to be safe in any case
@ -10539,49 +10445,12 @@ void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool ca
}
}
void Unit::MonsterMove(float x, float y, float z, uint32 transitTime)
void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
{
SplineFlags flags = GetTypeId() == TYPEID_PLAYER ? SPLINEFLAG_WALKMODE : ((Creature*)this)->GetSplineFlags();
SendMonsterMove(x, y, z, SPLINETYPE_NORMAL, flags, transitTime);
if (GetTypeId() != TYPEID_PLAYER)
{
Creature* c = (Creature*)this;
// Creature relocation acts like instant movement generator, so current generator expects interrupt/reset calls to react properly
if (!c->GetMotionMaster()->empty())
if (MovementGenerator *movgen = c->GetMotionMaster()->top())
movgen->Interrupt(*c);
GetMap()->CreatureRelocation((Creature*)this, x, y, z, 0.0f);
// finished relocation, movegen can different from top before creature relocation,
// but apply Reset expected to be safe in any case
if (!c->GetMotionMaster()->empty())
if (MovementGenerator *movgen = c->GetMotionMaster()->top())
movgen->Reset(*c);
}
}
void Unit::MonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime)
{
SendMonsterMoveWithSpeed(x, y, z, transitTime );
if (GetTypeId() != TYPEID_PLAYER)
{
Creature* c = (Creature*)this;
// Creature relocation acts like instant movement generator, so current generator expects interrupt/reset calls to react properly
if (!c->GetMotionMaster()->empty())
if (MovementGenerator *movgen = c->GetMotionMaster()->top())
movgen->Interrupt(*c);
GetMap()->CreatureRelocation((Creature*)this, x, y, z, 0.0f);
// finished relocation, movegen can different from top before creature relocation,
// but apply Reset expected to be safe in any case
if (!c->GetMotionMaster()->empty())
if (MovementGenerator *movgen = c->GetMotionMaster()->top())
movgen->Reset(*c);
}
Movement::MoveSplineInit init(*this);
init.MoveTo(x,y,z);
init.SetVelocity(speed);
init.Launch();
}
struct SetPvPHelper
@ -10955,3 +10824,31 @@ void Unit::SetVehicleId(uint32 entry)
SendMessageToSet(&data, true);
}
}
void Unit::UpdateSplineMovement(uint32 t_diff)
{
enum{
POSITION_UPDATE_DELAY = 400,
};
if (movespline->Finalized())
return;
movespline->updateState(t_diff);
bool arrived = movespline->Finalized();
if (arrived)
m_movementInfo.RemoveMovementFlag(MovementFlags(MOVEFLAG_SPLINE_ENABLED|MOVEFLAG_FORWARD));
m_movesplineTimer.Update(t_diff);
if (m_movesplineTimer.Passed() || arrived)
{
m_movesplineTimer.Reset(POSITION_UPDATE_DELAY);
Movement::Location loc = movespline->ComputePosition();
if (GetTypeId() == TYPEID_PLAYER)
((Player*)this)->SetPosition(loc.x,loc.y,loc.z,loc.orientation);
else
GetMap()->CreatureRelocation((Creature*)this,loc.x,loc.y,loc.z,loc.orientation);
}
}