[10653] Implemented MSG_MOVE_KNOCK_BACK opcode

* Player's knockback movement now smooth and doesn't looks like teleporting
 * Cleanup WorldSession::HandleMovementOpcodes, separated opcode specific and generic movement handlers code
 * Handle Feign Death aura interrupting into more appropriate place
This commit is contained in:
SilverIce 2010-10-28 05:48:43 +03:00
parent 81d3368e29
commit 1871cf977f
6 changed files with 159 additions and 123 deletions

View file

@ -241,125 +241,27 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
recv_data >> movementInfo;
/*----------------*/
// ignore wrong guid (player attempt cheating own session for not own guid possible...)
if (guid != mover->GetObjectGuid())
if (!VerifyMovementInfo(movementInfo,guid,mover))
return;
if (!MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o))
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
/* handle special cases */
if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT))
{
// transports size limited
// (also received at zeppelin/lift leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
if( movementInfo.GetTransportPos()->x > 50 || movementInfo.GetTransportPos()->y > 50 || movementInfo.GetTransportPos()->z > 100 )
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
if( !MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x + movementInfo.GetTransportPos()->x, movementInfo.GetPos()->y + movementInfo.GetTransportPos()->y,
movementInfo.GetPos()->z + movementInfo.GetTransportPos()->z, movementInfo.GetPos()->o + movementInfo.GetTransportPos()->o) )
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
// if we boarded a transport, add us to it
if (plMover && !plMover->m_transport)
{
// elevators also cause the client to send MOVEFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list
for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter)
{
if ((*iter)->GetObjectGuid() == movementInfo.GetTransportGuid())
{
plMover->m_transport = (*iter);
(*iter)->AddPassenger(plMover);
break;
}
}
}
}
else if (plMover && plMover->m_transport) // if we were on a transport, leave
{
plMover->m_transport->RemovePassenger(plMover);
plMover->m_transport = NULL;
movementInfo.ClearTransportData();
}
// fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->IsTaxiFlying())
plMover->HandleFall(movementInfo);
if (plMover && (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING) != plMover->IsInWater()))
{
// now client not include swimming flag in case jumping under water
plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z) );
}
/*----------------------*/
/* process position-change */
movementInfo.UpdateTime(getMSTime());
HandleMoverRelocation(movementInfo,mover,plMover);
WorldPacket data(opcode, recv_data.size());
data.appendPackGUID(mover->GetGUID()); // write guid
movementInfo.Write(data); // write data
mover->SendMessageToSetExcept(&data, _player);
if(plMover) // nothing is charmed, or player charmed
{
plMover->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o);
plMover->m_movementInfo = movementInfo;
if (plMover)
plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
// after move info set
if ((opcode == MSG_MOVE_SET_WALK_MODE || opcode == MSG_MOVE_SET_RUN_MODE))
plMover->UpdateWalkMode(plMover, false);
// after move info set
if (opcode == MSG_MOVE_SET_WALK_MODE || opcode == MSG_MOVE_SET_RUN_MODE)
mover->UpdateWalkMode(mover, false);
if(plMover->isMovingOrTurning())
plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
if(movementInfo.GetPos()->z < -500.0f)
{
if(plMover->InBattleGround()
&& plMover->GetBattleGround()
&& plMover->GetBattleGround()->HandlePlayerUnderMap(_player))
{
// do nothing, the handle already did if returned true
}
else
{
// NOTE: this is actually called many times while falling
// even after the player has been teleported away
// TODO: discard movement packets after the player is rooted
if(plMover->isAlive())
{
plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, plMover->GetMaxHealth());
// pl can be alive if GM/etc
if(!plMover->isAlive())
{
// change the death state to CORPSE to prevent the death timer from
// starting in the next player update
plMover->KillPlayer();
plMover->BuildPlayerRepop();
}
}
// cancel the death timer here if started
plMover->RepopAtGraveyard();
}
}
}
else // creature charmed
{
if(mover->IsInWorld())
mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o);
}
WorldPacket data(opcode, recv_data.size());
data << mover->GetPackGUID(); // write guid
movementInfo.Write(data); // write data
mover->SendMessageToSetExcept(&data, _player);
}
void WorldSession::HandleForceSpeedChangeAckOpcodes(WorldPacket &recv_data)
@ -513,12 +415,36 @@ void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data )
{
DEBUG_LOG("CMSG_MOVE_KNOCK_BACK_ACK");
ObjectGuid guid; // guid - unused
Unit *mover = _player->GetMover();
Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
// ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
if(plMover && plMover->IsBeingTeleported())
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
ObjectGuid guid;
MovementInfo movementInfo;
recv_data >> guid.ReadAsPacked();
recv_data >> Unused<uint32>(); // unk
recv_data >> Unused<uint32>(); // knockback packets counter
recv_data >> movementInfo;
if (!VerifyMovementInfo(movementInfo,guid,mover))
return;
HandleMoverRelocation(movementInfo, mover, plMover);
WorldPacket data(MSG_MOVE_KNOCK_BACK, recv_data.size() + 15);
data << mover->GetPackGUID();
data << movementInfo;
data << movementInfo.GetJumpInfo().sinAngle;
data << movementInfo.GetJumpInfo().cosAngle;
data << movementInfo.GetJumpInfo().xyspeed;
data << movementInfo.GetJumpInfo().velocity;
mover->SendMessageToSetExcept(&data, _player);
}
void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data )
@ -559,3 +485,102 @@ void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
_player->SummonIfPossible(agree);
}
bool WorldSession::VerifyMovementInfo(MovementInfo& movementInfo, ObjectGuid& guid, Unit* mover) const
{
// ignore wrong guid (player attempt cheating own session for not own guid possible...)
if (guid != mover->GetObjectGuid())
return false;
if (!MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o))
return false;
if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT))
{
// transports size limited
// (also received at zeppelin/lift leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
if( movementInfo.GetTransportPos()->x > 50 || movementInfo.GetTransportPos()->y > 50 || movementInfo.GetTransportPos()->z > 100 )
return false;
if( !MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x + movementInfo.GetTransportPos()->x, movementInfo.GetPos()->y + movementInfo.GetTransportPos()->y,
movementInfo.GetPos()->z + movementInfo.GetTransportPos()->z, movementInfo.GetPos()->o + movementInfo.GetTransportPos()->o) )
{
return false;
}
}
return true;
}
void WorldSession::HandleMoverRelocation(MovementInfo& movementInfo, Unit* mover, Player* plMover)
{
movementInfo.UpdateTime(getMSTime());
if (plMover)
{
if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT) && !plMover->m_transport)
{
// elevators also cause the client to send MOVEFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list
for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter)
{
if ((*iter)->GetObjectGuid() == movementInfo.GetTransportGuid())
{
plMover->m_transport = (*iter);
(*iter)->AddPassenger(plMover);
break;
}
}
}
else if (plMover->m_transport) // if we were on a transport, leave
{
plMover->m_transport->RemovePassenger(plMover);
plMover->m_transport = NULL;
movementInfo.ClearTransportData();
}
if (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING) != plMover->IsInWater())
{
// now client not include swimming flag in case jumping under water
plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z) );
}
plMover->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o);
plMover->m_movementInfo = movementInfo;
if(movementInfo.GetPos()->z < -500.0f)
{
if(plMover->InBattleGround()
&& plMover->GetBattleGround()
&& plMover->GetBattleGround()->HandlePlayerUnderMap(_player))
{
// do nothing, the handle already did if returned true
}
else
{
// NOTE: this is actually called many times while falling
// even after the player has been teleported away
// TODO: discard movement packets after the player is rooted
if(plMover->isAlive())
{
plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, plMover->GetMaxHealth());
// pl can be alive if GM/etc
if(!plMover->isAlive())
{
// change the death state to CORPSE to prevent the death timer from
// starting in the next player update
plMover->KillPlayer();
plMover->BuildPlayerRepop();
}
}
// cancel the death timer here if started
plMover->RepopAtGraveyard();
}
}
}
else // creature charmed
{
if (mover->IsInWorld())
mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o);
}
}