[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

@ -0,0 +1,189 @@
/*
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "packet_builder.h"
#include "MoveSpline.h"
#include "WorldPacket.h"
namespace Movement
{
inline void operator << (ByteBuffer& b, const Vector3& v)
{
b << v.x << v.y << v.z;
}
inline void operator >> (ByteBuffer& b, Vector3& v)
{
b >> v.x >> v.y >> v.z;
}
enum MonsterMoveType
{
MonsterMoveNormal = 0,
MonsterMoveStop = 1,
MonsterMoveFacingSpot = 2,
MonsterMoveFacingTarget = 3,
MonsterMoveFacingAngle = 4
};
void PacketBuilder::WriteCommonMonsterMovePart(const MoveSpline& move_spline, WorldPacket& data)
{
MoveSplineFlag splineflags = move_spline.splineflags;
/*if (mov.IsBoarded())
{
data.SetOpcode(SMSG_MONSTER_MOVE_TRANSPORT);
data << mov.GetTransport()->Owner.GetPackGUID();
data << int8(mov.m_unused.transport_seat);
}*/
data << uint8(0);
data << move_spline.spline.getPoint(move_spline.spline.first());
data << move_spline.GetId();
switch(splineflags & MoveSplineFlag::Mask_Final_Facing)
{
default:
data << uint8(MonsterMoveNormal);
break;
case MoveSplineFlag::Final_Target:
data << uint8(MonsterMoveFacingTarget);
data << move_spline.facing.target;
break;
case MoveSplineFlag::Final_Angle:
data << uint8(MonsterMoveFacingAngle);
data << move_spline.facing.angle;
break;
case MoveSplineFlag::Final_Point:
data << uint8(MonsterMoveFacingSpot);
data << move_spline.facing.f.x << move_spline.facing.f.y << move_spline.facing.f.z;
break;
}
// add fake Enter_Cycle flag - needed for client-side cyclic movement (client will erase first spline vertex after first cycle done)
splineflags.enter_cycle = move_spline.isCyclic();
data << uint32(splineflags & ~MoveSplineFlag::Mask_No_Monster_Move);
if (splineflags.animation)
{
data << splineflags.getAnimationId();
data << move_spline.effect_start_time;
}
data << move_spline.Duration();
if (splineflags.parabolic)
{
data << move_spline.vertical_acceleration;
data << move_spline.effect_start_time;
}
}
void WriteLinearPath(const Spline<int32>& spline, ByteBuffer& data)
{
uint32 last_idx = spline.getPointCount() - 3;
const Vector3 * real_path = &spline.getPoint(1);
data << last_idx;
data << real_path[last_idx]; // destination
if (last_idx > 1)
{
Vector3 middle = (real_path[0] + real_path[last_idx]) / 2.f;
Vector3 offset;
// first and last points already appended
for(uint32 i = 1; i < last_idx; ++i)
{
offset = middle - real_path[i];
data.appendPackXYZ(offset.x, offset.y, offset.z);
}
}
}
void WriteCatmullRomPath(const Spline<int32>& spline, ByteBuffer& data)
{
uint32 count = spline.getPointCount() - 3;
data << count;
data.append<Vector3>(&spline.getPoint(2), count);
}
void WriteCatmullRomCyclicPath(const Spline<int32>& spline, ByteBuffer& data)
{
uint32 count = spline.getPointCount() - 3;
data << uint32(count + 1);
data << spline.getPoint(1); // fake point, client will erase it from the spline after first cycle done
data.append<Vector3>(&spline.getPoint(1), count);
}
void PacketBuilder::WriteMonsterMove(const MoveSpline& move_spline, WorldPacket& data)
{
WriteCommonMonsterMovePart(move_spline, data);
const Spline<int32>& spline = move_spline.spline;
MoveSplineFlag splineflags = move_spline.splineflags;
if (splineflags & MoveSplineFlag::Mask_CatmullRom)
{
if (splineflags.cyclic)
WriteCatmullRomCyclicPath(spline, data);
else
WriteCatmullRomPath(spline, data);
}
else
WriteLinearPath(spline, data);
}
void PacketBuilder::WriteCreate(const MoveSpline& move_spline, ByteBuffer& data)
{
//WriteClientStatus(mov,data);
//data.append<float>(&mov.m_float_values[SpeedWalk], SpeedMaxCount);
//if (mov.SplineEnabled())
{
MoveSplineFlag splineFlags = move_spline.splineflags;
data << splineFlags.raw();
if (splineFlags.final_angle)
{
data << move_spline.facing.angle;
}
else if (splineFlags.final_target)
{
data << move_spline.facing.target;
}
else if(splineFlags.final_point)
{
data << move_spline.facing.f.x << move_spline.facing.f.y << move_spline.facing.f.z;
}
data << move_spline.timePassed();
data << move_spline.Duration();
data << move_spline.GetId();
data << float(1.f);//splineInfo.duration_mod;
data << float(1.f);//splineInfo.duration_mod_next;
data << move_spline.vertical_acceleration;
data << move_spline.effect_start_time;
uint32 nodes = move_spline.getPath().size();
data << nodes;
data.append<Vector3>(&move_spline.getPath()[0], nodes);
data << uint8(move_spline.spline.mode());
data << (move_spline.isCyclic() ? Vector3::zero() : move_spline.FinalDestination());
}
}
}