Various external fixes - part 3

- ported commits from cmangos repositories
    - also:
        + fix Movement::MoveSpline::ComputePosition to return only positive angles
        + improved algorithm for finding a random reachable point on ground
This commit is contained in:
H0zen 2016-02-19 01:06:54 +02:00
parent 78c5faed9d
commit c7a96ab067
9 changed files with 113 additions and 88 deletions

View File

@ -2267,3 +2267,23 @@ uint32 GameObject::GetScriptId()
{ {
return sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) ? sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) : sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, GetEntry()); return sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) ? sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) : sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, GetEntry());
} }
float GameObject::GetInteractionDistance() const
{
float maxdist = INTERACTION_DISTANCE;
switch (GetGoType())
{
// TODO: find out how the client calculates the maximal usage distance to spellless working
// gameobjects like mailboxes - 10.0 is a just an abitrary choosen number
case GAMEOBJECT_TYPE_MAILBOX:
maxdist = 10.0f;
break;
case GAMEOBJECT_TYPE_FISHINGHOLE:
case GAMEOBJECT_TYPE_FISHINGNODE:
maxdist = 20.0f + CONTACT_DISTANCE; // max spell range
break;
default:
break;
}
return maxdist;
}

View File

@ -697,6 +697,8 @@ class GameObject : public WorldObject
void SetCapturePointSlider(float value, bool isLocked); void SetCapturePointSlider(float value, bool isLocked);
float GetCapturePointSliderValue() const { return m_captureSlider; } float GetCapturePointSliderValue() const { return m_captureSlider; }
float GetInteractionDistance() const; // Get the maximum distance for a GO to interact with
uint32 GetScriptId(); uint32 GetScriptId();
GridReference<GameObject>& GetGridRef() { return m_gridRef; } GridReference<GameObject>& GetGridRef() { return m_gridRef; }

View File

@ -2072,22 +2072,7 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameo
{ {
if (uint32(go->GetGoType()) == gameobject_type || gameobject_type == MAX_GAMEOBJECT_TYPE) if (uint32(go->GetGoType()) == gameobject_type || gameobject_type == MAX_GAMEOBJECT_TYPE)
{ {
float maxdist; float maxdist = go->GetInteractionDistance();
switch (go->GetGoType())
{
// TODO: find out how the client calculates the maximal usage distance to spellless working
// gameobjects like mailboxes - 10.0 is a just an abitrary choosen number
case GAMEOBJECT_TYPE_MAILBOX:
maxdist = 10.0f;
break;
case GAMEOBJECT_TYPE_FISHINGHOLE:
maxdist = 20.0f + CONTACT_DISTANCE; // max spell range
break;
default:
maxdist = INTERACTION_DISTANCE;
break;
}
if (go->IsWithinDistInMap(this, maxdist) && go->isSpawned()) if (go->IsWithinDistInMap(this, maxdist) && go->isSpawned())
{ return go; } { return go; }

View File

@ -2213,25 +2213,39 @@ bool Map::GetReachableRandomPointOnGround(float& x, float& y, float& z, float ra
return false; return false;
// here we have a valid position but the point can have a big Z in some case // here we have a valid position but the point can have a big Z in some case
// next code will check angle from 2 points // next code will check angle from 2 points of view: x-axis and y-axis movement
// c // c
// /| // /|
// / | // / |
// b/__|a // b/__|a
// project vector to get only positive value // project vector to get only positive value
float ab = fabs(x - i_x);
float ac = fabs(z - i_z); float ac = fabs(z - i_z);
float delta = 0;
// slope represented by c angle (in radian) // slope represented by b angle (in radian)
float slope = 0; float slope = 0;
const float MAX_SLOPE_IN_RADIAN = 50.0f / 180.0f * M_PI_F; // 50(degree) max seem best value for walkable slope const float MAX_SLOPE_IN_RADIAN = 50.0f / 180.0f * M_PI_F; // 50(degree) max seem best value for walkable slope
// check ab vector to avoid divide by 0 delta = fabs(x - i_x); // check x-axis movement
if (ab > 0.0f) if (delta > 0.0f) // check to avoid divide by 0
{ {
// compute c angle and convert it from radian to degree // compute slope
slope = atan(ac / ab); slope = atan(ac / delta);
if (slope < MAX_SLOPE_IN_RADIAN)
{
x = i_x;
y = i_y;
z = i_z;
return true;
}
}
delta = fabs(y - i_y); // check y-axis movement
if (delta > 0.0f) // check to avoid divide by 0
{
// compute slope
slope = atan(ac / delta);
if (slope < MAX_SLOPE_IN_RADIAN) if (slope < MAX_SLOPE_IN_RADIAN)
{ {
x = i_x; x = i_x;

View File

@ -256,10 +256,13 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recv_data)
if (!_player->IsSelfMover()) if (!_player->IsSelfMover())
{ return; } { return; }
GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid); GameObject* obj = _player->GetMap()->GetGameObject(guid);
if (!obj) if (!obj)
{ return; } { return; }
if (!obj->IsWithinDistInMap(_player, obj->GetInteractionDistance()))
{ return; }
// Additional check preventing exploits (ie loot despawned chests) // Additional check preventing exploits (ie loot despawned chests)
if (!obj->isSpawned()) if (!obj->isSpawned())
{ {

View File

@ -47,14 +47,14 @@ void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket& recv_data)
void WorldSession::SendTaxiStatus(ObjectGuid guid) void WorldSession::SendTaxiStatus(ObjectGuid guid)
{ {
// cheating checks // cheating checks
Creature* unit = GetPlayer()->GetMap()->GetCreature(guid); Creature* unit = _player->GetMap()->GetCreature(guid);
if (!unit) if (!unit)
{ {
DEBUG_LOG("WorldSession::SendTaxiStatus - %s not found or you can't interact with it.", guid.GetString().c_str()); DEBUG_LOG("WorldSession::SendTaxiStatus - %s not found or you can't interact with it.", guid.GetString().c_str());
return; return;
} }
uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam()); uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), _player->GetTeam());
// not found nearest // not found nearest
if (curloc == 0) if (curloc == 0)
@ -64,7 +64,7 @@ void WorldSession::SendTaxiStatus(ObjectGuid guid)
WorldPacket data(SMSG_TAXINODE_STATUS, 9); WorldPacket data(SMSG_TAXINODE_STATUS, 9);
data << ObjectGuid(guid); data << ObjectGuid(guid);
data << uint8(GetPlayer()->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0); data << uint8(_player->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0);
SendPacket(&data); SendPacket(&data);
DEBUG_LOG("WORLD: Sent SMSG_TAXINODE_STATUS"); DEBUG_LOG("WORLD: Sent SMSG_TAXINODE_STATUS");
@ -78,7 +78,7 @@ void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket& recv_data)
recv_data >> guid; recv_data >> guid;
// cheating checks // cheating checks
Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER); Creature* unit = _player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!unit) if (!unit)
{ {
DEBUG_LOG("WORLD: HandleTaxiQueryAvailableNodes - %s not found or you can't interact with him.", guid.GetString().c_str()); DEBUG_LOG("WORLD: HandleTaxiQueryAvailableNodes - %s not found or you can't interact with him.", guid.GetString().c_str());
@ -86,8 +86,8 @@ void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket& recv_data)
} }
// remove fake death // remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) if (_player->hasUnitState(UNIT_STAT_DIED))
{ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } { _player->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); }
// unknown taxi node case // unknown taxi node case
if (SendLearnNewTaxiNode(unit)) if (SendLearnNewTaxiNode(unit))
@ -100,7 +100,7 @@ void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket& recv_data)
void WorldSession::SendTaxiMenu(Creature* unit) void WorldSession::SendTaxiMenu(Creature* unit)
{ {
// find current node // find current node
uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam()); uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), _player->GetTeam());
if (curloc == 0) if (curloc == 0)
{ return; } { return; }
@ -111,7 +111,7 @@ void WorldSession::SendTaxiMenu(Creature* unit)
data << uint32(1); data << uint32(1);
data << unit->GetObjectGuid(); data << unit->GetObjectGuid();
data << uint32(curloc); data << uint32(curloc);
GetPlayer()->m_taxi.AppendTaximaskTo(data, GetPlayer()->IsTaxiCheater()); _player->m_taxi.AppendTaximaskTo(data, _player->IsTaxiCheater());
SendPacket(&data); SendPacket(&data);
DEBUG_LOG("WORLD: Sent SMSG_SHOWTAXINODES"); DEBUG_LOG("WORLD: Sent SMSG_SHOWTAXINODES");
@ -120,27 +120,27 @@ void WorldSession::SendTaxiMenu(Creature* unit)
void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode) void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode)
{ {
// remove fake death // remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) if (_player->hasUnitState(UNIT_STAT_DIED))
{ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } { _player->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); }
while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) while (_player->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
{ GetPlayer()->GetMotionMaster()->MovementExpired(false); } { _player->GetMotionMaster()->MovementExpired(false); }
if (mountDisplayId) if (mountDisplayId)
{ GetPlayer()->Mount(mountDisplayId); } { _player->Mount(mountDisplayId); }
GetPlayer()->GetMotionMaster()->MoveTaxiFlight(path, pathNode); _player->GetMotionMaster()->MoveTaxiFlight(path, pathNode);
} }
bool WorldSession::SendLearnNewTaxiNode(Creature* unit) bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
{ {
// find current node // find current node
uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam()); uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), _player->GetTeam());
if (curloc == 0) if (curloc == 0)
{ return true; } // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result. { return true; } // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result.
if (GetPlayer()->m_taxi.SetTaximaskNode(curloc)) if (_player->m_taxi.SetTaximaskNode(curloc))
{ {
WorldPacket msg(SMSG_NEW_TAXI_PATH, 0); WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
SendPacket(&msg); SendPacket(&msg);
@ -174,7 +174,7 @@ void WorldSession::HandleActivateTaxiExpressOpcode(WorldPacket& recv_data)
recv_data >> guid >> _totalcost >> node_count; recv_data >> guid >> _totalcost >> node_count;
Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER); Creature* npc = _player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!npc) if (!npc)
{ {
DEBUG_LOG("WORLD: HandleActivateTaxiExpressOpcode - %s not found or you can't interact with it.", guid.GetString().c_str()); DEBUG_LOG("WORLD: HandleActivateTaxiExpressOpcode - %s not found or you can't interact with it.", guid.GetString().c_str());
@ -186,6 +186,13 @@ void WorldSession::HandleActivateTaxiExpressOpcode(WorldPacket& recv_data)
{ {
uint32 node; uint32 node;
recv_data >> node; recv_data >> node;
if (!_player->m_taxi.IsTaximaskNodeKnown(node) && !_player->IsTaxiCheater())
{
SendActivateTaxiReply(ERR_TAXINOTVISITED);
recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet
return;
}
nodes.push_back(node); nodes.push_back(node);
} }
@ -194,7 +201,7 @@ void WorldSession::HandleActivateTaxiExpressOpcode(WorldPacket& recv_data)
DEBUG_LOG("WORLD: Received opcode CMSG_ACTIVATETAXIEXPRESS from %d to %d" , nodes.front(), nodes.back()); DEBUG_LOG("WORLD: Received opcode CMSG_ACTIVATETAXIEXPRESS from %d to %d" , nodes.front(), nodes.back());
GetPlayer()->ActivateTaxiPathTo(nodes, npc); _player->ActivateTaxiPathTo(nodes, npc);
} }
void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data) void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
@ -212,41 +219,41 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
// 1) end taxi path in far (multi-node) flight // 1) end taxi path in far (multi-node) flight
// 2) switch from one map to other in case multi-map taxi path // 2) switch from one map to other in case multi-map taxi path
// we need process only (1) // we need process only (1)
uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination(); uint32 curDest = _player->m_taxi.GetTaxiDestination();
if (!curDest) if (!curDest)
{ return; } { return; }
TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest); TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest);
// far teleport case // far teleport case
if (curDestNode && curDestNode->map_id != GetPlayer()->GetMapId()) if (curDestNode && curDestNode->map_id != _player->GetMapId())
{ {
if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) if (_player->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
{ {
// short preparations to continue flight // short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(_player->GetMotionMaster()->top());
flight->Interrupt(*GetPlayer()); // will reset at map landing flight->Interrupt(*_player); // will reset at map landing
flight->SetCurrentNodeAfterTeleport(); flight->SetCurrentNodeAfterTeleport();
TaxiPathNodeEntry const& node = flight->GetPath()[flight->GetCurrentNode()]; TaxiPathNodeEntry const& node = flight->GetPath()[flight->GetCurrentNode()];
flight->SkipCurrentNode(); flight->SkipCurrentNode();
GetPlayer()->TeleportTo(curDestNode->map_id, node.x, node.y, node.z, GetPlayer()->GetOrientation()); _player->TeleportTo(curDestNode->map_id, node.x, node.y, node.z, _player->GetOrientation());
} }
return; return;
} }
uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination(); uint32 destinationnode = _player->m_taxi.NextTaxiDestination();
if (destinationnode > 0) // if more destinations to go if (destinationnode > 0) // if more destinations to go
{ {
// current source node for next destination // current source node for next destination
uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource(); uint32 sourcenode = _player->m_taxi.GetTaxiSource();
// Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path) // Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path)
if (GetPlayer()->IsTaxiCheater()) if (_player->IsTaxiCheater())
{ {
if (GetPlayer()->m_taxi.SetTaximaskNode(sourcenode)) if (_player->m_taxi.SetTaximaskNode(sourcenode))
{ {
WorldPacket data(SMSG_NEW_TAXI_PATH, 0); WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
_player->GetSession()->SendPacket(&data); _player->GetSession()->SendPacket(&data);
@ -255,7 +262,7 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
DEBUG_LOG("WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode); DEBUG_LOG("WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode);
uint32 mountDisplayId = sObjectMgr.GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam()); uint32 mountDisplayId = sObjectMgr.GetTaxiMountDisplayId(sourcenode, _player->GetTeam());
uint32 path, cost; uint32 path, cost;
sObjectMgr.GetTaxiPath(sourcenode, destinationnode, path, cost); sObjectMgr.GetTaxiPath(sourcenode, destinationnode, path, cost);
@ -263,10 +270,10 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
if (path && mountDisplayId) if (path && mountDisplayId)
{ SendDoFlight(mountDisplayId, path, 1); } // skip start fly node { SendDoFlight(mountDisplayId, path, 1); } // skip start fly node
else else
{ GetPlayer()->m_taxi.ClearTaxiDestinations(); } // clear problematic path and next { _player->m_taxi.ClearTaxiDestinations(); } // clear problematic path and next
} }
else else
{ GetPlayer()->m_taxi.ClearTaxiDestinations(); } // not destinations, clear source node { _player->m_taxi.ClearTaxiDestinations(); } // not destinations, clear source node
} }
void WorldSession::HandleActivateTaxiOpcode(WorldPacket& recv_data) void WorldSession::HandleActivateTaxiOpcode(WorldPacket& recv_data)
@ -279,12 +286,19 @@ void WorldSession::HandleActivateTaxiOpcode(WorldPacket& recv_data)
recv_data >> guid >> nodes[0] >> nodes[1]; recv_data >> guid >> nodes[0] >> nodes[1];
DEBUG_LOG("WORLD: Received opcode CMSG_ACTIVATETAXI from %d to %d" , nodes[0], nodes[1]); DEBUG_LOG("WORLD: Received opcode CMSG_ACTIVATETAXI from %d to %d" , nodes[0], nodes[1]);
Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER); Creature* npc = _player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!npc) if (!npc)
{ {
DEBUG_LOG("WORLD: HandleActivateTaxiOpcode - %s not found or you can't interact with it.", guid.GetString().c_str()); DEBUG_LOG("WORLD: HandleActivateTaxiOpcode - %s not found or you can't interact with it.", guid.GetString().c_str());
return; return;
} }
if (!_player->IsTaxiCheater())
GetPlayer()->ActivateTaxiPathTo(nodes, npc); {
if (!_player->m_taxi.IsTaximaskNodeKnown(nodes[0]) || !_player->m_taxi.IsTaximaskNodeKnown(nodes[1]))
{
SendActivateTaxiReply(ERR_TAXINOTVISITED);
return;
}
}
_player->ActivateTaxiPathTo(nodes, npc);
} }

View File

@ -61,6 +61,7 @@ namespace Movement
spline.evaluate_derivative(point_Idx, u, hermite); spline.evaluate_derivative(point_Idx, u, hermite);
c.orientation = atan2(hermite.y, hermite.x); c.orientation = atan2(hermite.y, hermite.x);
} }
c.orientation = G3D::wrap(c.orientation, 0.f, (float)G3D::twoPi());
return c; return c;
} }

View File

@ -79,33 +79,19 @@ namespace Movement
void WriteLinearPath(const Spline<int32>& spline, ByteBuffer& data) void WriteLinearPath(const Spline<int32>& spline, ByteBuffer& data)
{ {
uint32 pointCount = spline.getPointCount() - 3; Movement::SplineBase::ControlArray const& pathPoint = spline.getPoints(); // get ref of whole path points array
uint32 last_idx = pointCount;
const Vector3* real_path = &spline.getPoint(1);
Vector3 destination = real_path[last_idx];
size_t lastIndexPos = data.wpos(); uint32 pathSize = spline.last() - spline.first() - 1; // -1 as we send destination first and last index is destination
data << last_idx; MANGOS_ASSERT(pathSize >= 0); // should never be less than 0
Vector3 destination = pathPoint[spline.last()]; // destination of this path should be send right after path size
data << pathSize;
data << destination; data << destination;
if (last_idx > 1)
for (uint32 i = spline.first(); i < spline.first() + pathSize; i++) // from first real index (this array contain also special data)
{ {
Vector3 offset; Vector3 offset = destination - pathPoint[i]; // we have to send offset relative to destination instead of directly path point.
// first and last points already appended data.appendPackXYZ(offset.x, offset.y, offset.z); // we have to pack x,y,z before send
for (uint32 i = 1; i < pointCount; ++i)
{
offset = destination - real_path[i];
// TODO: check if there is a better way to handle this like reworking path formatting to avoid generating such zero offset
// [-CLASSIC] The client freezes or crashes when it gets a zero offset.
// If the offset would be rounded to zero, skip it.
if (fabs(offset.x) < 0.25 && fabs(offset.y) < 0.25 && fabs(offset.z) < 0.25)
{
// Remove 1 from the counter that will be sent to the client.
last_idx--;
data.put(lastIndexPos, last_idx);
continue;
}
data.appendPackXYZ(offset.x, offset.y, offset.z);
}
} }
} }
@ -151,17 +137,17 @@ namespace Movement
data << splineFlags.raw(); data << splineFlags.raw();
if (splineFlags.final_angle) if (splineFlags.final_point)
{ {
data << move_spline.facing.angle; data << move_spline.facing.f.x << move_spline.facing.f.y << move_spline.facing.f.z;
} }
else if (splineFlags.final_target) else if (splineFlags.final_target)
{ {
data << move_spline.facing.target; data << move_spline.facing.target;
} }
else if (splineFlags.final_point) else if (splineFlags.final_angle)
{ {
data << move_spline.facing.f.x << move_spline.facing.f.y << move_spline.facing.f.z; data << move_spline.facing.angle;
} }
data << move_spline.timePassed(); data << move_spline.timePassed();

@ -1 +1 @@
Subproject commit 8f492dbbd39ddd79af2ce254f7dbf76b8564bf7a Subproject commit cde7ea4a007a37347256cbbcb4f989145a7bb6d6