[9143] Typos
[getmangos.git] / src / game / MovementHandler.cpp
blob0804e99167be41fb9acaa9ff45196e9efbc0f386
1 /*
2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "WorldPacket.h"
21 #include "WorldSession.h"
22 #include "Opcodes.h"
23 #include "Log.h"
24 #include "Corpse.h"
25 #include "Player.h"
26 #include "Vehicle.h"
27 #include "SpellAuras.h"
28 #include "MapManager.h"
29 #include "Transports.h"
30 #include "BattleGround.h"
31 #include "WaypointMovementGenerator.h"
32 #include "InstanceSaveMgr.h"
33 #include "ObjectMgr.h"
35 void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ )
37 sLog.outDebug( "WORLD: got MSG_MOVE_WORLDPORT_ACK." );
38 HandleMoveWorldportAckOpcode();
41 void WorldSession::HandleMoveWorldportAckOpcode()
43 // ignore unexpected far teleports
44 if(!GetPlayer()->IsBeingTeleportedFar())
45 return;
47 // get the teleport destination
48 WorldLocation &loc = GetPlayer()->GetTeleportDest();
50 // possible errors in the coordinate validity check
51 if(!MapManager::IsValidMapCoord(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation))
53 sLog.outError("WorldSession::HandleMoveWorldportAckOpcode: player %s (%d) was teleported far to a not valid location. (map:%u, x:%f, y:%f, "
54 "z:%f) We port him to his homebind instead..", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z);
55 // stop teleportation else we would try this again and again in LogoutPlayer...
56 GetPlayer()->SetSemaphoreTeleportFar(false);
57 // and teleport the player to a valid place
58 GetPlayer()->TeleportToHomebind();
59 return;
62 // get the destination map entry, not the current one, this will fix homebind and reset greeting
63 MapEntry const* mEntry = sMapStore.LookupEntry(loc.mapid);
64 InstanceTemplate const* mInstance = ObjectMgr::GetInstanceTemplate(loc.mapid);
66 // reset instance validity, except if going to an instance inside an instance
67 if(GetPlayer()->m_InstanceValid == false && !mInstance)
68 GetPlayer()->m_InstanceValid = true;
70 GetPlayer()->SetSemaphoreTeleportFar(false);
72 // relocate the player to the teleport destination
73 GetPlayer()->SetMap(sMapMgr.CreateMap(loc.mapid, GetPlayer()));
74 GetPlayer()->Relocate(loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation);
76 GetPlayer()->SendInitialPacketsBeforeAddToMap();
77 // the CanEnter checks are done in TeleporTo but conditions may change
78 // while the player is in transit, for example the map may get full
79 if(!GetPlayer()->GetMap()->Add(GetPlayer()))
81 //if player wasn't added to map, reset his map pointer!
82 GetPlayer()->ResetMap();
84 sLog.outError("WorldSession::HandleMoveWorldportAckOpcode: player %s (%d) was teleported far but couldn't be added to map. (map:%u, x:%f, y:%f, "
85 "z:%f) We port him to his homebind instead..", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z);
86 // teleport the player home
87 GetPlayer()->TeleportToHomebind();
88 return;
91 // battleground state prepare (in case join to BG), at relogin/tele player not invited
92 // only add to bg group and object, if the player was invited (else he entered through command)
93 if(_player->InBattleGround())
95 // cleanup setting if outdated
96 if(!mEntry->IsBattleGroundOrArena())
98 // We're not in BG
99 _player->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE);
100 // reset destination bg team
101 _player->SetBGTeam(0);
103 // join to bg case
104 else if(BattleGround *bg = _player->GetBattleGround())
106 if(_player->IsInvitedForBattleGroundInstance(_player->GetBattleGroundId()))
107 bg->AddPlayer(_player);
111 GetPlayer()->SendInitialPacketsAfterAddToMap();
113 // flight fast teleport case
114 if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
116 if(!_player->InBattleGround())
118 // short preparations to continue flight
119 FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
120 flight->Initialize(*GetPlayer());
121 return;
124 // battleground state prepare, stop flight
125 GetPlayer()->GetMotionMaster()->MovementExpired();
126 GetPlayer()->m_taxi.ClearTaxiDestinations();
129 // resurrect character at enter into instance where his corpse exist after add to map
130 Corpse *corpse = GetPlayer()->GetCorpse();
131 if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId())
133 if( mEntry->IsDungeon() )
135 GetPlayer()->ResurrectPlayer(0.5f);
136 GetPlayer()->SpawnCorpseBones();
140 if (mInstance)
142 Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid());
143 if(MapDifficulty const* mapDiff = GetMapDifficultyData(mEntry->MapID,diff))
145 if (mapDiff->resetTime)
147 if (uint32 timeReset = sInstanceSaveMgr.GetResetTimeFor(mEntry->MapID,diff))
149 uint32 timeleft = timeReset - time(NULL);
150 GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);
156 // mount allow check
157 if(!mEntry->IsMountAllowed())
158 _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
160 // honorless target
161 if(GetPlayer()->pvpInfo.inHostileArea)
162 GetPlayer()->CastSpell(GetPlayer(), 2479, true);
164 // resummon pet
165 GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
167 //lets process all delayed operations on successful teleport
168 GetPlayer()->ProcessDelayedOperations();
171 void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
173 sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
174 uint64 guid;
176 if(!recv_data.readPackGUID(guid))
177 return;
179 uint32 flags, time;
180 recv_data >> flags >> time;
181 DEBUG_LOG("Guid " UI64FMTD, guid);
182 DEBUG_LOG("Flags %u, time %u", flags, time/IN_MILISECONDS);
184 Unit *mover = _player->m_mover;
185 Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
187 if(!plMover || !plMover->IsBeingTeleportedNear())
188 return;
190 if(guid != plMover->GetGUID())
191 return;
193 plMover->SetSemaphoreTeleportNear(false);
195 uint32 old_zone = plMover->GetZoneId();
197 WorldLocation const& dest = plMover->GetTeleportDest();
199 plMover->SetPosition(dest.coord_x, dest.coord_y, dest.coord_z, dest.orientation, true);
201 uint32 newzone, newarea;
202 plMover->GetZoneAndAreaId(newzone, newarea);
203 plMover->UpdateZone(newzone, newarea);
205 // new zone
206 if(old_zone != newzone)
208 // honorless target
209 if(plMover->pvpInfo.inHostileArea)
210 plMover->CastSpell(plMover, 2479, true);
213 // resummon pet
214 GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
216 //lets process all delayed operations on successful teleport
217 GetPlayer()->ProcessDelayedOperations();
220 void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
222 uint32 opcode = recv_data.GetOpcode();
223 sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
224 recv_data.hexlike();
226 Unit *mover = _player->m_mover;
227 Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
229 // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
230 if(plMover && plMover->IsBeingTeleported())
232 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
233 return;
236 /* extract packet */
237 uint64 guid;
239 if(!recv_data.readPackGUID(guid))
240 return;
242 MovementInfo movementInfo;
243 movementInfo.guid = guid;
244 ReadMovementInfo(recv_data, &movementInfo);
245 /*----------------*/
247 if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o))
249 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
250 return;
253 /* handle special cases */
254 if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
256 // transports size limited
257 // (also received at zeppelin/lift leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
258 if( movementInfo.t_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 100 )
260 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
261 return;
264 if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y + movementInfo.t_y,
265 movementInfo.z + movementInfo.t_z, movementInfo.o + movementInfo.t_o) )
267 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
268 return;
271 // if we boarded a transport, add us to it
272 if (plMover && !plMover->m_transport)
274 // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list
275 for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter)
277 if ((*iter)->GetGUID() == movementInfo.t_guid)
279 plMover->m_transport = (*iter);
280 (*iter)->AddPassenger(plMover);
281 break;
286 else if (plMover && plMover->m_transport) // if we were on a transport, leave
288 plMover->m_transport->RemovePassenger(plMover);
289 plMover->m_transport = NULL;
290 movementInfo.t_x = 0.0f;
291 movementInfo.t_y = 0.0f;
292 movementInfo.t_z = 0.0f;
293 movementInfo.t_o = 0.0f;
294 movementInfo.t_time = 0;
295 movementInfo.t_seat = -1;
298 // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
299 if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight())
300 plMover->HandleFall(movementInfo);
302 if (plMover && (movementInfo.HasMovementFlag(MOVEMENTFLAG_SWIMMING) != plMover->IsInWater()))
304 // now client not include swimming flag in case jumping under water
305 plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) );
308 /*----------------------*/
310 /* process position-change */
311 WorldPacket data(opcode, recv_data.size());
312 movementInfo.time = getMSTime();
313 movementInfo.guid = mover->GetGUID();
314 WriteMovementInfo(&data, &movementInfo);
315 GetPlayer()->SendMessageToSet(&data, false);
317 if(plMover) // nothing is charmed, or player charmed
319 plMover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
320 plMover->m_movementInfo = movementInfo;
321 plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
323 if(plMover->isMovingOrTurning())
324 plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
326 if(movementInfo.z < -500.0f)
328 if(plMover->InBattleGround()
329 && plMover->GetBattleGround()
330 && plMover->GetBattleGround()->HandlePlayerUnderMap(_player))
332 // do nothing, the handle already did if returned true
334 else
336 // NOTE: this is actually called many times while falling
337 // even after the player has been teleported away
338 // TODO: discard movement packets after the player is rooted
339 if(plMover->isAlive())
341 plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
342 // pl can be alive if GM/etc
343 if(!plMover->isAlive())
345 // change the death state to CORPSE to prevent the death timer from
346 // starting in the next player update
347 plMover->KillPlayer();
348 plMover->BuildPlayerRepop();
352 // cancel the death timer here if started
353 plMover->RepopAtGraveyard();
357 else // creature charmed
359 if(mover->IsInWorld())
360 mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
364 void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
366 uint32 opcode = recv_data.GetOpcode();
367 sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
368 /* extract packet */
369 uint64 guid;
370 uint32 unk1;
371 float newspeed;
373 if(!recv_data.readPackGUID(guid))
374 return;
376 // now can skip not our packet
377 if(_player->GetGUID() != guid)
379 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
380 return;
383 // continue parse packet
384 recv_data >> unk1; // counter or moveEvent
386 MovementInfo movementInfo;
387 movementInfo.guid = guid;
388 ReadMovementInfo(recv_data, &movementInfo);
390 recv_data >> newspeed;
391 /*----------------*/
393 // client ACK send one packet for mounted/run case and need skip all except last from its
394 // in other cases anti-cheat check can be fail in false case
395 UnitMoveType move_type;
396 UnitMoveType force_move_type;
398 static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
400 switch(opcode)
402 case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
403 case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
404 case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; force_move_type = MOVE_RUN_BACK; break;
405 case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
406 case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; force_move_type = MOVE_SWIM_BACK; break;
407 case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break;
408 case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break;
409 case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break;
410 case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break;
411 default:
412 sLog.outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode);
413 return;
416 // skip all forced speed changes except last and unexpected
417 // in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both.
418 if(_player->m_forced_speed_changes[force_move_type] > 0)
420 --_player->m_forced_speed_changes[force_move_type];
421 if(_player->m_forced_speed_changes[force_move_type] > 0)
422 return;
425 if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f)
427 if(_player->GetSpeed(move_type) > newspeed) // must be greater - just correct
429 sLog.outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
430 move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed);
431 _player->SetSpeed(move_type,_player->GetSpeedRate(move_type),true);
433 else // must be lesser - cheating
435 sLog.outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)",
436 _player->GetName(),_player->GetSession()->GetAccountId(),_player->GetSpeed(move_type), newspeed);
437 _player->GetSession()->KickPlayer();
442 void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
444 sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
445 recv_data.hexlike();
447 uint64 guid;
448 recv_data >> guid;
450 if(_player->m_mover->GetGUID() != guid)
452 sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " I64FMT " and should be " I64FMT, _player->m_mover->GetGUID(), guid);
453 return;
457 void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
459 sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
460 recv_data.hexlike();
462 uint64 old_mover_guid;
464 if(!recv_data.readPackGUID(old_mover_guid))
465 return;
467 if(_player->m_mover->GetGUID() == old_mover_guid)
469 sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is " I64FMT " and should be " I64FMT " instead of " UI64FMTD, _player->m_mover->GetGUID(), _player->GetGUID(), old_mover_guid);
470 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
471 return;
474 MovementInfo mi;
475 mi.guid = old_mover_guid;
476 ReadMovementInfo(recv_data, &mi);
478 _player->m_movementInfo = mi;
481 void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
483 sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
484 recv_data.hexlike();
486 uint64 vehicleGUID = _player->GetCharmGUID();
488 if(!vehicleGUID) // something wrong here...
490 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
491 return;
494 uint64 guid;
496 if(!recv_data.readPackGUID(guid))
497 return;
499 MovementInfo mi;
500 mi.guid = guid;
501 ReadMovementInfo(recv_data, &mi);
503 _player->m_movementInfo = mi;
505 // using charm guid, because we don't have vehicle guid...
506 if(Vehicle *vehicle = _player->GetMap()->GetVehicle(vehicleGUID))
508 // Aura::HandleAuraControlVehicle will call Player::ExitVehicle
509 vehicle->RemoveSpellsCausingAura(SPELL_AURA_CONTROL_VEHICLE);
513 void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/)
515 //sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM");
517 WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
518 data << uint64(GetPlayer()->GetGUID());
520 GetPlayer()->SendMessageToSet(&data, false);
523 void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data )
525 sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK");
527 uint64 guid; // guid - unused
528 if(!recv_data.readPackGUID(guid))
529 return;
531 recv_data.read_skip<uint32>(); // unk
533 MovementInfo movementInfo;
534 ReadMovementInfo(recv_data, &movementInfo);
537 void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data )
539 sLog.outDebug("CMSG_MOVE_HOVER_ACK");
541 uint64 guid; // guid - unused
542 if(!recv_data.readPackGUID(guid))
543 return;
545 recv_data.read_skip<uint32>(); // unk
547 MovementInfo movementInfo;
548 ReadMovementInfo(recv_data, &movementInfo);
550 recv_data.read_skip<uint32>(); // unk2
553 void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data)
555 sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK");
557 uint64 guid; // guid - unused
558 if(!recv_data.readPackGUID(guid))
559 return;
561 recv_data.read_skip<uint32>(); // unk
563 MovementInfo movementInfo;
564 ReadMovementInfo(recv_data, &movementInfo);
566 recv_data.read_skip<uint32>(); // unk2
569 void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
571 if(!_player->isAlive() || _player->isInCombat() )
572 return;
574 uint64 summoner_guid;
575 bool agree;
576 recv_data >> summoner_guid;
577 recv_data >> agree;
579 _player->SummonIfPossible(agree);