mirror of
https://github.com/mangosfour/server.git
synced 2025-12-12 10:37:03 +00:00
[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:
parent
e302fce513
commit
9d566398ad
52 changed files with 2471 additions and 1203 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue