[9581] Fixed apply damage reduction to melee/ranged damage.
[getmangos.git] / src / game / MovementHandler.cpp
blob0cb045cd90b4080fad56b676164a1e7be0beb118
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->Reset(*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 (time_t timeReset = sInstanceSaveMgr.GetResetTimeFor(mEntry->MapID,diff))
149 uint32 timeleft = uint32(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");
175 ObjectGuid guid;
177 recv_data >> guid.ReadAsPacked();
179 uint32 flags, time;
180 recv_data >> flags >> time;
181 DEBUG_LOG("Guid: %s", guid.GetString().c_str());
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->GetObjectGuid())
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 ObjectGuid guid;
238 MovementInfo movementInfo;
240 recv_data >> guid.ReadAsPacked();
241 recv_data >> movementInfo;
242 /*----------------*/
244 if (!MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o))
246 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
247 return;
250 /* handle special cases */
251 if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT))
253 // transports size limited
254 // (also received at zeppelin/lift leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
255 if( movementInfo.GetTransportPos()->x > 50 || movementInfo.GetTransportPos()->y > 50 || movementInfo.GetTransportPos()->z > 100 )
257 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
258 return;
261 if( !MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x + movementInfo.GetTransportPos()->x, movementInfo.GetPos()->y + movementInfo.GetTransportPos()->y,
262 movementInfo.GetPos()->z + movementInfo.GetTransportPos()->z, movementInfo.GetPos()->o + movementInfo.GetTransportPos()->o) )
264 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
265 return;
268 // if we boarded a transport, add us to it
269 if (plMover && !plMover->m_transport)
271 // elevators also cause the client to send MOVEFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list
272 for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter)
274 if ((*iter)->GetGUID() == movementInfo.GetTransportGuid())
276 plMover->m_transport = (*iter);
277 (*iter)->AddPassenger(plMover);
278 break;
283 else if (plMover && plMover->m_transport) // if we were on a transport, leave
285 plMover->m_transport->RemovePassenger(plMover);
286 plMover->m_transport = NULL;
287 movementInfo.SetTransportData(0, 0.0f, 0.0f, 0.0f, 0.0f, 0, -1);
290 // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
291 if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight())
292 plMover->HandleFall(movementInfo);
294 if (plMover && (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING) != plMover->IsInWater()))
296 // now client not include swimming flag in case jumping under water
297 plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z) );
300 /*----------------------*/
302 /* process position-change */
303 movementInfo.UpdateTime(getMSTime());
305 WorldPacket data(opcode, recv_data.size());
306 data.appendPackGUID(mover->GetGUID()); // write guid
307 movementInfo.Write(data); // write data
308 GetPlayer()->SendMessageToSet(&data, false);
310 if(plMover) // nothing is charmed, or player charmed
312 plMover->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o);
313 plMover->m_movementInfo = movementInfo;
314 plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
316 // after move info set
317 if ((opcode == MSG_MOVE_SET_WALK_MODE || opcode == MSG_MOVE_SET_RUN_MODE))
318 plMover->UpdateWalkMode(plMover, false);
320 if(plMover->isMovingOrTurning())
321 plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
323 if(movementInfo.GetPos()->z < -500.0f)
325 if(plMover->InBattleGround()
326 && plMover->GetBattleGround()
327 && plMover->GetBattleGround()->HandlePlayerUnderMap(_player))
329 // do nothing, the handle already did if returned true
331 else
333 // NOTE: this is actually called many times while falling
334 // even after the player has been teleported away
335 // TODO: discard movement packets after the player is rooted
336 if(plMover->isAlive())
338 plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
339 // pl can be alive if GM/etc
340 if(!plMover->isAlive())
342 // change the death state to CORPSE to prevent the death timer from
343 // starting in the next player update
344 plMover->KillPlayer();
345 plMover->BuildPlayerRepop();
349 // cancel the death timer here if started
350 plMover->RepopAtGraveyard();
354 else // creature charmed
356 if(mover->IsInWorld())
357 mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o);
361 void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
363 uint32 opcode = recv_data.GetOpcode();
364 sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
365 /* extract packet */
366 ObjectGuid guid;
367 MovementInfo movementInfo;
368 float newspeed;
370 recv_data >> guid.ReadAsPacked();
371 recv_data >> Unused<uint32>(); // counter or moveEvent
372 recv_data >> movementInfo;
373 recv_data >> newspeed;
375 // now can skip not our packet
376 if(_player->GetObjectGuid() != guid)
378 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
379 return;
381 /*----------------*/
383 // client ACK send one packet for mounted/run case and need skip all except last from its
384 // in other cases anti-cheat check can be fail in false case
385 UnitMoveType move_type;
386 UnitMoveType force_move_type;
388 static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
390 switch(opcode)
392 case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
393 case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
394 case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; force_move_type = MOVE_RUN_BACK; break;
395 case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
396 case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; force_move_type = MOVE_SWIM_BACK; break;
397 case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break;
398 case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break;
399 case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break;
400 case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break;
401 default:
402 sLog.outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode);
403 return;
406 // skip all forced speed changes except last and unexpected
407 // in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both.
408 if(_player->m_forced_speed_changes[force_move_type] > 0)
410 --_player->m_forced_speed_changes[force_move_type];
411 if(_player->m_forced_speed_changes[force_move_type] > 0)
412 return;
415 if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f)
417 if(_player->GetSpeed(move_type) > newspeed) // must be greater - just correct
419 sLog.outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
420 move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed);
421 _player->SetSpeedRate(move_type,_player->GetSpeedRate(move_type),true);
423 else // must be lesser - cheating
425 sLog.outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)",
426 _player->GetName(),_player->GetSession()->GetAccountId(),_player->GetSpeed(move_type), newspeed);
427 _player->GetSession()->KickPlayer();
432 void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
434 sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
435 recv_data.hexlike();
437 uint64 guid;
438 recv_data >> guid;
440 if(_player->m_mover->GetGUID() != guid)
442 sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " I64FMT " and should be " I64FMT, _player->m_mover->GetGUID(), guid);
443 return;
447 void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
449 sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
450 recv_data.hexlike();
452 ObjectGuid old_mover_guid;
453 MovementInfo mi;
455 recv_data >> old_mover_guid.ReadAsPacked();
456 recv_data >> mi;
458 if(_player->m_mover->GetObjectGuid() == old_mover_guid)
460 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.GetRawValue());
461 recv_data.rpos(recv_data.wpos()); // prevent warnings spam
462 return;
465 _player->m_movementInfo = mi;
468 void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
470 sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
471 recv_data.hexlike();
473 ObjectGuid guid;
474 MovementInfo mi;
476 recv_data >> guid.ReadAsPacked();
477 recv_data >> mi;
479 uint64 vehicleGUID = _player->GetCharmGUID();
481 if(!vehicleGUID) // something wrong here...
482 return;
484 _player->m_movementInfo = mi;
486 // using charm guid, because we don't have vehicle guid...
487 if(Vehicle *vehicle = _player->GetMap()->GetVehicle(vehicleGUID))
489 // Aura::HandleAuraControlVehicle will call Player::ExitVehicle
490 vehicle->RemoveSpellsCausingAura(SPELL_AURA_CONTROL_VEHICLE);
494 void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/)
496 //sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM");
498 WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
499 data << uint64(GetPlayer()->GetGUID());
501 GetPlayer()->SendMessageToSet(&data, false);
504 void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data )
506 sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK");
508 ObjectGuid guid; // guid - unused
509 MovementInfo movementInfo;
511 recv_data >> guid.ReadAsPacked();
512 recv_data >> Unused<uint32>(); // unk
513 recv_data >> movementInfo;
516 void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data )
518 sLog.outDebug("CMSG_MOVE_HOVER_ACK");
520 ObjectGuid guid; // guid - unused
521 MovementInfo movementInfo;
523 recv_data >> guid.ReadAsPacked();
524 recv_data >> Unused<uint32>(); // unk1
525 recv_data >> movementInfo;
526 recv_data >> Unused<uint32>(); // unk2
529 void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data)
531 sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK");
533 ObjectGuid guid; // guid - unused
534 MovementInfo movementInfo;
536 recv_data >> guid.ReadAsPacked();
537 recv_data >> Unused<uint32>(); // unk1
538 recv_data >> movementInfo;
539 recv_data >> Unused<uint32>(); // unk2
542 void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
544 if(!_player->isAlive() || _player->isInCombat() )
545 return;
547 uint64 summoner_guid;
548 bool agree;
549 recv_data >> summoner_guid;
550 recv_data >> agree;
552 _player->SummonIfPossible(agree);