diff options
Diffstat (limited to 'src')
37 files changed, 985 insertions, 282 deletions
diff --git a/src/bindings/scripts/include/sc_creature.h b/src/bindings/scripts/include/sc_creature.h index da40e2eab60..70a43a4df52 100644 --- a/src/bindings/scripts/include/sc_creature.h +++ b/src/bindings/scripts/include/sc_creature.h @@ -75,6 +75,9 @@ struct TRINITY_DLL_DECL ScriptedAI : public CreatureAI //Called at waypoint reached or PointMovement end void MovementInform(uint32, uint32){} + // Called when AI is temporarily replaced or put back when possess is applied or removed + void OnPossess(bool apply) {} + //************* // Variables //************* diff --git a/src/bindings/scripts/scripts/npc/npc_escortAI.cpp b/src/bindings/scripts/scripts/npc/npc_escortAI.cpp index 2c3163b9a7c..04efb0c952d 100644 --- a/src/bindings/scripts/scripts/npc/npc_escortAI.cpp +++ b/src/bindings/scripts/scripts/npc/npc_escortAI.cpp @@ -256,6 +256,25 @@ void npc_escortAI::MovementInform(uint32 type, uint32 id) } } +void npc_escortAI::OnPossess(bool apply) +{ + // We got possessed in the middle of being escorted, store the point + // where we left off to come back to when possess is removed + if (IsBeingEscorted) + { + if (apply) + m_creature->GetPosition(LastPos.x, LastPos.y, LastPos.z); + else + { + Returning = true; + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z); + } + } +} + + + void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs) { Escort_Waypoint t(id, x, y, z, WaitTimeMs); diff --git a/src/bindings/scripts/scripts/npc/npc_escortAI.h b/src/bindings/scripts/scripts/npc/npc_escortAI.h index 73928f7e2f8..a805bdd45bb 100644 --- a/src/bindings/scripts/scripts/npc/npc_escortAI.h +++ b/src/bindings/scripts/scripts/npc/npc_escortAI.h @@ -51,6 +51,8 @@ struct TRINITY_DLL_DECL npc_escortAI : public ScriptedAI void MovementInform(uint32, uint32); + void OnPossess(bool apply); + // EscortAI functions void AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs = 0); diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 38650242d2d..13ee7c275a7 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -560,6 +560,8 @@ ChatCommand * ChatHandler::getCommandTable() { "unfreeze", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnFreezeCommand, "", NULL }, { "listfreeze", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListFreezeCommand, "", NULL }, { "flusharenapoints", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, + { "possess", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePossessCommand, "", NULL }, + { "unpossess", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnPossessCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; diff --git a/src/game/Chat.h b/src/game/Chat.h index 08e90f34414..05cb23417f3 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -449,6 +449,8 @@ class ChatHandler bool HandleDebugArenaCommand(const char * args); bool HandleDebugThreatList(const char * args); bool HandleDebugHostilRefList(const char * args); + bool HandlePossessCommand(const char* args); + bool HandleUnPossessCommand(const char* args); Player* getSelectedPlayer(); Creature* getSelectedCreature(); diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 54f51fd2dd7..b15010ec67d 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -46,6 +46,7 @@ #include "CellImpl.h" #include "OutdoorPvPMgr.h" #include "GameEvent.h" +#include "PossessedAI.h" // apply implementation of the singletons #include "Policies/SingletonImp.h" @@ -117,7 +118,7 @@ uint32 CreatureInfo::GetFirstValidModelId() const } Creature::Creature() : -Unit(), i_AI(NULL), +Unit(), i_AI(NULL), i_AI_possessed(NULL), lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0), m_lootMoney(0), m_lootRecipient(0), m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f), @@ -145,6 +146,8 @@ Creature::~Creature() delete i_AI; i_AI = NULL; + + DeletePossessedAI(); } void Creature::AddToWorld() @@ -364,7 +367,7 @@ void Creature::Update(uint32 diff) setDeathState( JUST_ALIVED ); //Call AI respawn virtual function - i_AI->JustRespawned(); + AI()->JustRespawned(); MapManager::Instance().GetMap(GetMapId(), this)->Add(this); } @@ -428,7 +431,7 @@ void Creature::Update(uint32 diff) { // do not allow the AI to be changed during update m_AI_locked = true; - i_AI->UpdateAI(diff); + AI()->UpdateAI(diff); m_AI_locked = false; } @@ -525,6 +528,10 @@ bool Creature::AIM_Initialize() return false; } + // don't allow AI switch when possessed + if (isPossessed()) + return false; + CreatureAI * oldAI = i_AI; i_motionMaster.Initialize(); i_AI = FactorySelector::selectAI(this); @@ -533,6 +540,28 @@ bool Creature::AIM_Initialize() return true; } +void Creature::InitPossessedAI() +{ + if (!isPossessed()) return; + + if (!i_AI_possessed) + i_AI_possessed = new PossessedAI(*this); + + // Signal the old AI that it's been disabled + i_AI->OnPossess(true); +} + +void Creature::DeletePossessedAI() +{ + if (!i_AI_possessed) return; + + delete i_AI_possessed; + i_AI_possessed = NULL; + + // Signal the old AI that it's been re-enabled + i_AI->OnPossess(false); +} + bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data) { SetMapId(map->GetId()); diff --git a/src/game/Creature.h b/src/game/Creature.h index 0ccc1ca3f9d..679b17f416f 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -395,6 +395,7 @@ typedef std::map<uint32,time_t> CreatureSpellCooldowns; class TRINITY_DLL_SPEC Creature : public Unit { CreatureAI *i_AI; + CreatureAI *i_AI_possessed; public: @@ -455,9 +456,11 @@ class TRINITY_DLL_SPEC Creature : public Unit bool IsInEvadeMode() const; bool AIM_Initialize(); + void InitPossessedAI(); + void DeletePossessedAI(); void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type); - CreatureAI* AI() { return i_AI; } + CreatureAI* AI() { return isPossessed() && i_AI_possessed ? i_AI_possessed : i_AI; } uint32 GetShieldBlockValue() const //dunno mob block value { diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h index d44ab81bd3a..a1f78be1e86 100644 --- a/src/game/CreatureAI.h +++ b/src/game/CreatureAI.h @@ -121,6 +121,9 @@ class TRINITY_DLL_SPEC CreatureAI // Called at waypoint reached or point movement finished virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {} + + // Called when AI is temporarily replaced or put back when possess is applied or removed + virtual void OnPossess(bool apply) {} }; struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature> diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp index 6a0c5637893..7219f71cd81 100644 --- a/src/game/CreatureAIRegistry.cpp +++ b/src/game/CreatureAIRegistry.cpp @@ -24,6 +24,7 @@ #include "AggressorAI.h" #include "GuardAI.h" #include "PetAI.h" +#include "PossessedAI.h" #include "TotemAI.h" #include "OutdoorPvPObjectiveAI.h" #include "RandomMovementGenerator.h" @@ -44,6 +45,7 @@ namespace AIRegistry (new CreatureAIFactory<PetAI>("PetAI"))->RegisterSelf(); (new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf(); (new CreatureAIFactory<OutdoorPvPObjectiveAI>("OutdoorPvPObjectiveAI"))->RegisterSelf(); + (new CreatureAIFactory<PossessedAI>("PossessedAI"))->RegisterSelf(); (new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf(); (new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf(); diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp index 575dabca430..2e0d297305c 100644 --- a/src/game/CreatureAISelector.cpp +++ b/src/game/CreatureAISelector.cpp @@ -56,12 +56,14 @@ namespace FactorySelector { if( creature->isGuard() ) ai_factory = ai_registry.GetRegistryItem("GuardAI"); - else if(creature->isPet() || creature->isCharmed()) + else if(creature->isPet() || (creature->isCharmed() && !creature->isPossessed())) ai_factory = ai_registry.GetRegistryItem("PetAI"); else if(creature->isTotem()) ai_factory = ai_registry.GetRegistryItem("TotemAI"); else if(creature->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER) ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); + else if(creature->isPossessed()) + creature->InitPossessedAI(); } // select by permit check diff --git a/src/game/GridNotifiers.cpp b/src/game/GridNotifiers.cpp index 45487cdf395..3c422542484 100644 --- a/src/game/GridNotifiers.cpp +++ b/src/game/GridNotifiers.cpp @@ -40,6 +40,8 @@ Trinity::PlayerNotifier::Visit(PlayerMapType &m) iter->getSource()->UpdateVisibilityOf(&i_player); i_player.UpdateVisibilityOf(iter->getSource()); + if (i_player.isPossessedByPlayer()) + ((Player*)i_player.GetCharmer())->UpdateVisibilityOf(iter->getSource()); } } @@ -139,54 +141,66 @@ VisibleNotifier::Notify() i_player.SendAuraDurationsForTarget((Unit*)(*vItr)); } -void -MessageDeliverer::Visit(PlayerMapType &m) +void +Deliverer::Visit(PlayerMapType &m) { - for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { - if( i_toSelf || iter->getSource() != &i_player) + if (!i_dist || iter->getSource()->GetDistance(&i_source) <= i_dist) { - if(WorldSession* session = iter->getSource()->GetSession()) - session->SendPacket(i_message); + // Send packet to possessor + if (iter->getSource()->isPossessedByPlayer()) + SendPacket((Player*)iter->getSource()->GetCharmer()); + VisitObject(iter->getSource()); } } } -void -ObjectMessageDeliverer::Visit(PlayerMapType &m) +void +Deliverer::Visit(CreatureMapType &m) { - for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { - if(WorldSession* session = iter->getSource()->GetSession()) - session->SendPacket(i_message); + if (!i_dist || iter->getSource()->GetDistance(&i_source) <= i_dist) + { + // Send packet to possessor + if (iter->getSource()->isPossessedByPlayer()) + SendPacket((Player*)iter->getSource()->GetCharmer()); + } } } void -MessageDistDeliverer::Visit(PlayerMapType &m) +Deliverer::SendPacket(Player* plr) { - for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + if (!plr) + return; + // Don't send the packet to possesor if not supposed to + if (!i_toPossessor && plr->isPossessing() && plr->GetCharmGUID() == i_source.GetGUID()) + return; + + if (plr_list.find(plr->GetGUID()) == plr_list.end()) { - if( (i_toSelf || iter->getSource() != &i_player ) && - (!i_ownTeamOnly || iter->getSource()->GetTeam() == i_player.GetTeam() ) && - (!i_dist || iter->getSource()->GetDistance(&i_player) <= i_dist) ) - { - if(WorldSession* session = iter->getSource()->GetSession()) - session->SendPacket(i_message); - } + if (WorldSession* session = plr->GetSession()) + session->SendPacket(i_message); + plr_list.insert(plr->GetGUID()); } } void -ObjectMessageDistDeliverer::Visit(PlayerMapType &m) +MessageDeliverer::VisitObject(Player* plr) { - for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + if (i_toSelf || plr != &i_source) + SendPacket(plr); +} + +void +MessageDistDeliverer::VisitObject(Player* plr) +{ + if( (i_toSelf || plr != &i_source ) && + (!i_ownTeamOnly || (i_source.GetTypeId() == TYPEID_PLAYER && plr->GetTeam() == ((Player&)i_source).GetTeam())) ) { - if( !i_dist || iter->getSource()->GetDistance(&i_object) <= i_dist ) - { - if(WorldSession* session = iter->getSource()->GetSession()) - session->SendPacket(i_message); - } + SendPacket(plr); } } diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h index ed2ddfa22e7..daa5ece7c6a 100644 --- a/src/game/GridNotifiers.h +++ b/src/game/GridNotifiers.h @@ -89,44 +89,46 @@ namespace Trinity void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); } }; - struct TRINITY_DLL_DECL MessageDeliverer + struct TRINITY_DLL_DECL Deliverer { - Player &i_player; + WorldObject &i_source; WorldPacket *i_message; - bool i_toSelf; - MessageDeliverer(Player &pl, WorldPacket *msg, bool to_self) : i_player(pl), i_message(msg), i_toSelf(to_self) {} + std::set<uint64> plr_list; + bool i_toPossessor; + float i_dist; + Deliverer(WorldObject &src, WorldPacket *msg, bool to_possessor, float dist = 0.0f) : i_source(src), i_message(msg), i_toPossessor(to_possessor), i_dist(dist) {} void Visit(PlayerMapType &m); + void Visit(CreatureMapType &m); + virtual void VisitObject(Player* plr) = 0; + void SendPacket(Player* plr); template<class SKIP> void Visit(GridRefManager<SKIP> &) {} }; + + struct TRINITY_DLL_DECL MessageDeliverer : public Deliverer + { + bool i_toSelf; + MessageDeliverer(Player &pl, WorldPacket *msg, bool to_possessor, bool to_self) : Deliverer(pl, msg, to_possessor), i_toSelf(to_self) {} + void VisitObject(Player* plr); + }; - struct TRINITY_DLL_DECL ObjectMessageDeliverer + struct TRINITY_DLL_DECL ObjectMessageDeliverer : public Deliverer { - WorldPacket *i_message; - explicit ObjectMessageDeliverer(WorldPacket *msg) : i_message(msg) {} - void Visit(PlayerMapType &m); - template<class SKIP> void Visit(GridRefManager<SKIP> &) {} + explicit ObjectMessageDeliverer(WorldObject &src, WorldPacket *msg, bool to_possessor) : Deliverer(src, msg, to_possessor) {} + void VisitObject(Player* plr) { SendPacket(plr); } }; - struct TRINITY_DLL_DECL MessageDistDeliverer + struct TRINITY_DLL_DECL MessageDistDeliverer : public Deliverer { - Player &i_player; - WorldPacket *i_message; bool i_toSelf; bool i_ownTeamOnly; - float i_dist; - MessageDistDeliverer(Player &pl, WorldPacket *msg, float dist, bool to_self, bool ownTeamOnly) : i_player(pl), i_message(msg), i_dist(dist), i_toSelf(to_self), i_ownTeamOnly(ownTeamOnly) {} - void Visit(PlayerMapType &m); - template<class SKIP> void Visit(GridRefManager<SKIP> &) {} + MessageDistDeliverer(Player &pl, WorldPacket *msg, bool to_possessor, float dist, bool to_self, bool ownTeamOnly) : Deliverer(pl, msg, to_possessor, dist), i_toSelf(to_self), i_ownTeamOnly(ownTeamOnly) {} + void VisitObject(Player* plr); }; - struct TRINITY_DLL_DECL ObjectMessageDistDeliverer + struct TRINITY_DLL_DECL ObjectMessageDistDeliverer : public Deliverer { - WorldObject &i_object; - WorldPacket *i_message; - float i_dist; - ObjectMessageDistDeliverer(WorldObject &obj, WorldPacket *msg, float dist) : i_object(obj), i_message(msg), i_dist(dist) {} - void Visit(PlayerMapType &m); - template<class SKIP> void Visit(GridRefManager<SKIP> &) {} + ObjectMessageDistDeliverer(WorldObject &obj, WorldPacket *msg, bool to_possessor, float dist) : Deliverer(obj, msg, to_possessor, dist) {} + void VisitObject(Player* plr) { SendPacket(plr); } }; struct TRINITY_DLL_DECL ObjectUpdater diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 47a3cc1a59b..4ab83c9fd3f 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -6544,3 +6544,29 @@ bool ChatHandler::HandleGroupRemoveCommand(const char* args) return true; } + +bool ChatHandler::HandlePossessCommand(const char* args) +{ + Unit* pUnit = getSelectedUnit(); + if(!pUnit) + return false; + + // Don't allow unlimited possession of players + if (pUnit->GetTypeId() == TYPEID_PLAYER) + return false; + + m_session->GetPlayer()->Possess(pUnit); + + return true; +} + +bool ChatHandler::HandleUnPossessCommand(const char* args) +{ + // Use this command to also unpossess ourselves + if (m_session->GetPlayer()->isPossessed()) + m_session->GetPlayer()->UnpossessSelf(false); + else + m_session->GetPlayer()->RemovePossess(false); + + return true; +} diff --git a/src/game/Makefile.am b/src/game/Makefile.am index 0003e859bb4..8cebc1715d0 100644 --- a/src/game/Makefile.am +++ b/src/game/Makefile.am @@ -217,6 +217,8 @@ $(srcdir)/PlayerDump.cpp \ $(srcdir)/PlayerDump.h \ $(srcdir)/PointMovementGenerator.cpp \ $(srcdir)/PointMovementGenerator.h \ +$(srcdir)/PossessedAI.cpp \ +$(srcdir)/PossessedAI.h \ $(srcdir)/QueryHandler.cpp \ $(srcdir)/QuestDef.cpp \ $(srcdir)/QuestDef.h \ diff --git a/src/game/Map.cpp b/src/game/Map.cpp index c47d5dd3a64..0d251ee5912 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -255,7 +255,7 @@ template<> void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell) { // add to world object registry in grid - if(obj->isPet()) + if(obj->isPet() || obj->isPossessedByPlayer()) { (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID()); obj->SetCurrentCell(cell); @@ -299,7 +299,7 @@ template<> void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell) { // remove from world object registry in grid - if(obj->isPet()) + if(obj->isPet() || obj->isPossessedByPlayer()) { (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID()); } @@ -311,6 +311,27 @@ void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell) } template<class T> +void Map::SwitchGridContainers(T* obj, bool active) +{ + CellPair pair = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + Cell cell(pair); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + + if (active) + { + (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject<T>(obj, obj->GetGUID()); + (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<T>(obj, obj->GetGUID()); + } else + { + (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<T>(obj, obj->GetGUID()); + (*grid)(cell.CellX(), cell.CellY()).AddGridObject<T>(obj, obj->GetGUID()); + } +} + +template void Map::SwitchGridContainers(Creature *, bool); +template void Map::SwitchGridContainers(Corpse *, bool); + +template<class T> void Map::DeleteFromWorld(T* obj) { // Note: In case resurrectable corpse and pet its removed from gloabal lists in own destructors @@ -467,7 +488,7 @@ Map::Add(T *obj) AddNotifier(obj,cell,p); } -void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self) +void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self, bool to_possessor) { CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); @@ -483,13 +504,13 @@ void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self) if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) return; - Trinity::MessageDeliverer post_man(*player, msg, to_self); + Trinity::MessageDeliverer post_man(*player, msg, to_possessor, to_self); TypeContainerVisitor<Trinity::MessageDeliverer, WorldTypeMapContainer > message(post_man); CellLock<ReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *this); } -void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg) +void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg, bool to_possessor) { CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); @@ -506,13 +527,13 @@ void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg) if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) return; - Trinity::ObjectMessageDeliverer post_man(msg); + Trinity::ObjectMessageDeliverer post_man(*obj, msg, to_possessor); TypeContainerVisitor<Trinity::ObjectMessageDeliverer, WorldTypeMapContainer > message(post_man); CellLock<ReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *this); } -void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only) +void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only, bool to_possessor) { CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); @@ -528,13 +549,13 @@ void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, boo if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) return; - Trinity::MessageDistDeliverer post_man(*player, msg, dist, to_self, own_team_only); + Trinity::MessageDistDeliverer post_man(*player, msg, to_possessor, dist, to_self, own_team_only); TypeContainerVisitor<Trinity::MessageDistDeliverer , WorldTypeMapContainer > message(post_man); CellLock<ReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *this); } -void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist) +void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist, bool to_possessor) { CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); @@ -551,7 +572,7 @@ void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist) if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) return; - Trinity::ObjectMessageDistDeliverer post_man(*obj, msg,dist); + Trinity::ObjectMessageDistDeliverer post_man(*obj, msg, to_possessor, dist); TypeContainerVisitor<Trinity::ObjectMessageDistDeliverer, WorldTypeMapContainer > message(post_man); CellLock<ReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, message, *this); @@ -649,6 +670,7 @@ Map::Remove(T *obj, bool remove) assert( grid != NULL ); obj->RemoveFromWorld(); + RemoveFromGrid(obj,grid,cell); UpdateObjectVisibility(obj,cell,p); @@ -697,6 +719,11 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati // if move then update what player see and who seen UpdatePlayerVisibility(player,new_cell,new_val); UpdateObjectsVisibilityFor(player,new_cell,new_val); + + // also update what possessing player sees + if(player->isPossessedByPlayer()) + UpdateObjectsVisibilityFor((Player*)player->GetCharmer(), new_cell, new_val); + PlayerRelocationNotify(player,new_cell,new_val); NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY()); if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE ) @@ -725,10 +752,16 @@ Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang #endif AddCreatureToMoveList(creature,x,y,z,ang); // in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList + if(creature->isPossessedByPlayer()) + EnsureGridLoadedForPlayer(new_cell, (Player*)creature->GetCharmer(), false); } else { creature->Relocate(x, y, z, ang); + // Update visibility back to player who is controlling the creature + if(creature->isPossessedByPlayer()) + UpdateObjectsVisibilityFor((Player*)creature->GetCharmer(), new_cell, new_val); + CreatureRelocationNotify(creature,new_cell,new_val); } assert(CheckGridIntegrity(creature,true)); diff --git a/src/game/Map.h b/src/game/Map.h index 0c1cc094e96..5e4b408b505 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -140,10 +140,10 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O virtual void Update(const uint32&); - void MessageBroadcast(Player *, WorldPacket *, bool to_self); - void MessageBroadcast(WorldObject *, WorldPacket *); - void MessageDistBroadcast(Player *, WorldPacket *, float dist, bool to_self, bool own_team_only = false); - void MessageDistBroadcast(WorldObject *, WorldPacket *, float dist); + void MessageBroadcast(Player *, WorldPacket *, bool to_self, bool to_possessor); + void MessageBroadcast(WorldObject *, WorldPacket *, bool to_possessor); + void MessageDistBroadcast(Player *, WorldPacket *, float dist, bool to_self, bool to_possessor, bool own_team_only = false); + void MessageDistBroadcast(WorldObject *, WorldPacket *, float dist, bool to_possessor); void PlayerRelocation(Player *, float x, float y, float z, float angl); void CreatureRelocation(Creature *creature, float x, float y, float, float); @@ -236,6 +236,8 @@ class TRINITY_DLL_SPEC Map : public GridRefManager<NGridType>, public Trinity::O void markCell(uint32 pCellId) { marked_cells.set(pCellId); } Creature* GetCreatureInMap(uint64 guid); GameObject* GetGameObjectInMap(uint64 guid); + + template<class T> void SwitchGridContainers(T* obj, bool active); private: void LoadVMap(int pX, int pY); void LoadMap(uint32 mapid, uint32 instanceid, int x,int y); diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index d2109561119..792db2e06f4 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -189,9 +189,6 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 4+1+4+4+4+4+4); - if(GetPlayer()->GetDontMove()) - return; - /* extract packet */ MovementInfo movementInfo; uint32 MovementFlags; @@ -204,9 +201,6 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) recv_data >> movementInfo.z; recv_data >> movementInfo.o; - //Save movement flags - _player->SetUnitMovementFlags(MovementFlags); - if(MovementFlags & MOVEMENTFLAG_ONTRANSPORT) { // recheck @@ -263,6 +257,20 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) if (!Trinity::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o)) return; + // Handle possessed unit movement separately + Unit* pos_unit = GetPlayer()->GetCharm(); + if (pos_unit && pos_unit->isPossessed()) // can be charmed but not possessed + { + HandlePossessedMovement(recv_data, movementInfo, MovementFlags); + return; + } + + if (GetPlayer()->GetDontMove()) + return; + + //Save movement flags + GetPlayer()->SetUnitMovementFlags(MovementFlags); + /* handle special cases */ if (MovementFlags & MOVEMENTFLAG_ONTRANSPORT) { @@ -284,7 +292,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) if ((*iter)->GetGUID() == movementInfo.t_guid) { // unmount before boarding - _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); GetPlayer()->m_transport = (*iter); (*iter)->AddPassenger(GetPlayer()); @@ -304,60 +312,9 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) movementInfo.t_time = 0; } - // fall damage generation (ignore in flight case that can be triggred also at lags in moment teleportation to another map). - if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight()) - { - Player *target = GetPlayer(); - - //Players with Feather Fall or low fall time, or physical immunity (charges used) are ignored - if (movementInfo.fallTime > 1100 && !target->isDead() && !target->isGameMaster() && - !target->HasAuraType(SPELL_AURA_HOVER) && !target->HasAuraType(SPELL_AURA_FEATHER_FALL) && - !target->HasAuraType(SPELL_AURA_FLY) && !target->IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL,true) ) - { - //Safe fall, fall time reduction - int32 safe_fall = target->GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); - uint32 fall_time = (movementInfo.fallTime > (safe_fall*10)) ? movementInfo.fallTime - (safe_fall*10) : 0; - - if(fall_time > 1100) //Prevent damage if fall time < 1100 - { - //Fall Damage calculation - float fallperc = float(fall_time)/1100; - uint32 damage = (uint32)(((fallperc*fallperc -1) / 9 * target->GetMaxHealth())*sWorld.getRate(RATE_DAMAGE_FALL)); - - float height = movementInfo.z; - target->UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); - - if (damage > 0) - { - //Prevent fall damage from being more than the player maximum health - if (damage > target->GetMaxHealth()) - damage = target->GetMaxHealth(); - - // Gust of Wind - if (target->GetDummyAura(43621)) - damage = target->GetMaxHealth()/2; - - target->EnvironmentalDamage(target->GetGUID(), DAMAGE_FALL, damage); - } - - //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction - DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, target->GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); - } - } - - //handle fall and logout at the same time (logout started before fall finished) - /* outdated and create problems with sit at stun sometime - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE)) - { - target->SetStandState(PLAYER_STATE_SIT); - // Can't move - WorldPacket data( SMSG_FORCE_MOVE_ROOT, 12 ); - data.append(target->GetPackGUID()); - data << (uint32)2; - SendPacket( &data ); - } - */ - } + // handle fall damage + if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND) + GetPlayer()->HandleFallDamage(movementInfo); if(((MovementFlags & MOVEMENTFLAG_SWIMMING) != 0) != GetPlayer()->IsInWater()) { @@ -381,30 +338,66 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if(movementInfo.z < -500.0f) + GetPlayer()->HandleFallUnderMap(); +} + +void WorldSession::HandlePossessedMovement(WorldPacket& recv_data, MovementInfo& movementInfo, uint32& MovementFlags) +{ + // Whatever the client is controlling, it will send the GUID of the original player. + // If current player is controlling, it must be handled like the controlled player sent these opcodes + + Unit* pos_unit = GetPlayer()->GetCharm(); + + if (pos_unit->GetTypeId() == TYPEID_PLAYER && ((Player*)pos_unit)->GetDontMove()) + return; + + //Save movement flags + pos_unit->SetUnitMovementFlags(MovementFlags); + + // Remove possession if possessed unit enters a transport + if (MovementFlags & MOVEMENTFLAG_ONTRANSPORT) { - if(GetPlayer()->InBattleGround() - && GetPlayer()->GetBattleGround() - && GetPlayer()->GetBattleGround()->HandlePlayerUnderMap(_player)) + GetPlayer()->RemovePossess(true); + return; + } + + recv_data.put<uint32>(5, getMSTime()); + WorldPacket data(recv_data.GetOpcode(), pos_unit->GetPackGUID().size()+recv_data.size()); + data.append(pos_unit->GetPackGUID()); + data.append(recv_data.contents(), recv_data.size()); + // Send the packet to self but not to the possessed player; for creatures the first bool is irrelevant + pos_unit->SendMessageToSet(&data, true, false); + + // Possessed is a player + if (pos_unit->GetTypeId() == TYPEID_PLAYER) + { + Player* plr = (Player*)pos_unit; + + if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND) + plr->HandleFallDamage(movementInfo); + + if(((MovementFlags & MOVEMENTFLAG_SWIMMING) != 0) != plr->IsInWater()) { - // do nothing, the handle already did if returned true + // Now client not include swimming flag in case jumping under water + plr->SetInWater( !plr->IsInWater() || plr->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); } - 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(GetPlayer()->isAlive()) - { - GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); - // change the death state to CORPSE to prevent the death timer from - // starting in the next player update - GetPlayer()->KillPlayer(); - GetPlayer()->BuildPlayerRepop(); - } + + plr->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o, false); + plr->m_movementInfo = movementInfo; - // cancel the death timer here if started - GetPlayer()->RepopAtGraveyard(); + if(plr->isMovingOrTurning()) + plr->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + if(movementInfo.z < -500.0f) + { + GetPlayer()->RemovePossess(false); + plr->HandleFallUnderMap(); } + } + else // Possessed unit is a creature + { + Map* map = MapManager::Instance().GetMap(pos_unit->GetMapId(), pos_unit); + map->CreatureRelocation((Creature*)pos_unit, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); } } diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 506acb6b7d8..4fbd030217a 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -1411,14 +1411,14 @@ void WorldObject::BuildTeleportAckMsg(WorldPacket *data, float x, float y, float *data << uint32(0); } -void WorldObject::SendMessageToSet(WorldPacket *data, bool /*bToSelf*/) +void WorldObject::SendMessageToSet(WorldPacket *data, bool /*fake*/, bool bToPossessor) { - MapManager::Instance().GetMap(m_mapId, this)->MessageBroadcast(this, data); + MapManager::Instance().GetMap(m_mapId, this)->MessageBroadcast(this, data, bToPossessor); } -void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/) +void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/, bool bToPossessor) { - MapManager::Instance().GetMap(m_mapId, this)->MessageDistBroadcast(this, data, dist); + MapManager::Instance().GetMap(m_mapId, this)->MessageDistBroadcast(this, data, dist, bToPossessor); } void WorldObject::SendObjectDeSpawnAnim(uint64 guid) diff --git a/src/game/Object.h b/src/game/Object.h index f17ec4d0920..943b28b7943 100644 --- a/src/game/Object.h +++ b/src/game/Object.h @@ -413,8 +413,8 @@ class TRINITY_DLL_SPEC WorldObject : public Object float GetAngle( const float x, const float y ) const; bool HasInArc( const float arcangle, const WorldObject* obj ) const; - virtual void SendMessageToSet(WorldPacket *data, bool self); - virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self); + virtual void SendMessageToSet(WorldPacket *data, bool self, bool to_possessor = true); + virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool to_possessor = true); void BuildHeartBeatMsg( WorldPacket *data ) const; void BuildTeleportAckMsg( WorldPacket *data, float x, float y, float z, float ang) const; bool IsBeingTeleported() { return mSemaphoreTeleport; } diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp index 39687e27e49..c81a6303be6 100644 --- a/src/game/ObjectAccessor.cpp +++ b/src/game/ObjectAccessor.cpp @@ -622,8 +622,30 @@ void ObjectAccessor::WorldObjectChangeAccumulator::Visit(PlayerMapType &m) { for(PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) - if(iter->getSource()->HaveAtClient(&i_object)) - ObjectAccessor::_buildPacket(iter->getSource(), &i_object, i_updateDatas); + { + BuildPacket(iter->getSource()); + if (iter->getSource()->isPossessedByPlayer()) + BuildPacket((Player*)iter->getSource()->GetCharmer()); + } +} + +void +ObjectAccessor::WorldObjectChangeAccumulator::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter) + if (iter->getSource()->isPossessedByPlayer()) + BuildPacket((Player*)iter->getSource()->GetCharmer()); +} + +void +ObjectAccessor::WorldObjectChangeAccumulator::BuildPacket(Player* plr) +{ + // Only send update once to a player + if (plr_list.find(plr->GetGUID()) == plr_list.end() && plr->HaveAtClient(&i_object)) + { + ObjectAccessor::_buildPacket(plr, &i_object, i_updateDatas); + plr_list.insert(plr->GetGUID()); + } } void diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h index b4ff57001a3..d7dde01f640 100644 --- a/src/game/ObjectAccessor.h +++ b/src/game/ObjectAccessor.h @@ -208,8 +208,11 @@ class TRINITY_DLL_DECL ObjectAccessor : public Trinity::Singleton<ObjectAccessor { UpdateDataMapType &i_updateDatas; WorldObject &i_object; + std::set<uint64> plr_list; WorldObjectChangeAccumulator(WorldObject &obj, UpdateDataMapType &d) : i_updateDatas(d), i_object(obj) {} void Visit(PlayerMapType &); + void Visit(CreatureMapType &); + void BuildPacket(Player* plr); template<class SKIP> void Visit(GridRefManager<SKIP> &) {} }; diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index d8186da36de..01329fe5e89 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -548,7 +548,7 @@ void Pet::Update(uint32 diff) { // unsummon pet that lost owner Unit* owner = GetOwner(); - if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID()) + if(!owner || (!IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) && !isPossessed()) || isControlled() && !owner->GetPetGUID()) { Remove(PET_SAVE_NOT_IN_SLOT, true); return; diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index c162f22c64b..60a4bec0891 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -109,7 +109,6 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if(pet->GetTypeId() != TYPEID_PLAYER) { - pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) ((Creature*)pet)->AI()->AttackStart(TargetUnit); @@ -139,8 +138,13 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->setDeathState(CORPSE); } - else // charmed - _player->Uncharm(); + else // charmed or possessed + { + if (_player->isPossessing()) + _player->RemovePossess(true); + else + _player->Uncharm(); + } break; default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); @@ -194,7 +198,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) int16 result = spell->PetCanCast(unit_target); //auto turn to target unless possessed - if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) + if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed()) { pet->SetInFront(unit_target); if( unit_target->GetTypeId() == TYPEID_PLAYER ) @@ -222,7 +226,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) pet->SendPetAIReaction(guid1); } - if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) + if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed()) { pet->clearUnitState(UNIT_STAT_FOLLOW); if(pet->getVictim()) @@ -236,7 +240,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) } else { - if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) + if(pet->isPossessed()) { WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); data << uint32(spellid) << uint8(2) << uint8(result); @@ -478,7 +482,10 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) } else if(pet->GetGUID() == _player->GetCharmGUID()) { - _player->Uncharm(); + if (_player->isPossessing()) + _player->RemovePossess(true); + else + _player->Uncharm(); } } } @@ -601,21 +608,19 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) recvPacket >> guid >> spellid; + // This opcode is also sent from charmed and possessed units (players and creatures) if(!_player->GetPet() && !_player->GetCharm()) return; - if(ObjectAccessor::FindPlayer(guid)) - return; - - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + Unit* caster = ObjectAccessor::GetUnit(*_player, guid); - if(!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) + if(!caster || (caster != _player->GetPet() && caster != _player->GetCharm())) { sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } - if (pet->GetGlobalCooldown() > 0) + if (caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->GetGlobalCooldown() > 0) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); @@ -626,41 +631,53 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) } // do not cast not learned spells - if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) + if(!caster->HasSpell(spellid) || IsPassiveSpell(spellid)) return; SpellCastTargets targets; - if(!targets.read(&recvPacket,pet)) + if(!targets.read(&recvPacket,caster)) return; - pet->clearUnitState(UNIT_STAT_FOLLOW); + caster->clearUnitState(UNIT_STAT_FOLLOW); - Spell *spell = new Spell(pet, spellInfo, false); + Spell *spell = new Spell(caster, spellInfo, false); spell->m_targets = targets; int16 result = spell->PetCanCast(NULL); if(result == -1) { - pet->AddCreatureSpellCooldown(spellid); - if(pet->isPet()) + if(caster->GetTypeId() == TYPEID_UNIT) { - Pet* p = (Pet*)pet; - p->CheckLearning(spellid); - //10% chance to play special pet attack talk, else growl - //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell - if(p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) - pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); - else - pet->SendPetAIReaction(guid); + Creature* pet = (Creature*)caster; + pet->AddCreatureSpellCooldown(spellid); + if(pet->isPet()) + { + Pet* p = (Pet*)pet; + p->CheckLearning(spellid); + // 10% chance to play special pet attack talk, else growl + // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell + if(p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) + pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); + else + pet->SendPetAIReaction(guid); + } } spell->prepare(&(spell->m_targets)); } else { - pet->SendPetCastFail(spellid, result); - if(!pet->HasSpellCooldown(spellid)) - pet->SendPetClearCooldown(spellid); + caster->SendPetCastFail(spellid, result); + if(caster->GetTypeId() == TYPEID_PLAYER) + { + if(!((Player*)caster)->HasSpellCooldown(spellid)) + caster->SendPetClearCooldown(spellid); + } + else + { + if(!((Creature*)caster)->HasSpellCooldown(spellid)) + caster->SendPetClearCooldown(spellid); + } spell->finish(false); delete spell; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index dc4092540d9..02d16277476 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -469,6 +469,9 @@ Player::~Player () for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) itr->second.save->RemovePlayer(this); + if (isPossessing()) + RemovePossess(false); + delete m_declinedname; } @@ -1253,7 +1256,7 @@ void Player::Update( uint32 p_time ) SendUpdateToOutOfRangeGroupMembers(); Pet* pet = GetPet(); - if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE)) + if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && !pet->isPossessed()) { RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); return; @@ -1534,6 +1537,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati SetSemaphoreTeleport(true); + // Remove any possession on the player before teleporting + if (isPossessedByPlayer()) + ((Player*)GetCharmer())->RemovePossess(); + // The player was ported to another map and looses the duel immediatly. // We have to perform this check before the teleport, otherwise the // ObjectAccessor won't find the flag. @@ -1747,6 +1754,7 @@ void Player::RemoveFromWorld() if(IsInWorld()) { ///- Release charmed creatures, unsummon totems and remove pets/guardians + RemovePossess(false); Uncharm(); UnsummonAllTotems(); RemoveMiniPet(); @@ -5266,19 +5274,19 @@ void Player::SaveRecallPosition() m_recallO = GetOrientation(); } -void Player::SendMessageToSet(WorldPacket *data, bool self) +void Player::SendMessageToSet(WorldPacket *data, bool self, bool to_possessor) { - MapManager::Instance().GetMap(GetMapId(), this)->MessageBroadcast(this, data, self); + MapManager::Instance().GetMap(GetMapId(), this)->MessageBroadcast(this, data, self, to_possessor); } -void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self) +void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool to_possessor) { - MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self); + MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self, to_possessor); } -void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only) +void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only, bool to_possessor) { - MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self,own_team_only); + MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self, to_possessor, own_team_only); } void Player::SendDirectMessage(WorldPacket *data) @@ -15973,7 +15981,6 @@ void Player::Uncharm() return; charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM); - charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS); } void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const @@ -16006,7 +16013,7 @@ void Player::TextEmote(const std::string text) { WorldPacket data(SMSG_MESSAGECHAT, 200); BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL); - SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) ); + SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT), true ); } void Player::Whisper(std::string text, uint32 language,uint64 receiver) @@ -18505,13 +18512,253 @@ void Player::SetCanBlock( bool value ) UpdateBlockPercentage(); } -bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const +void Player::HandleFallDamage(MovementInfo& movementInfo) { - for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr) - if(itr->pos == this->pos) - return true; + //Players with Feather Fall or low fall time, or physical immunity (charges used) are ignored + if (!isInFlight() && movementInfo.fallTime > 1100 && !isDead() && !isGameMaster() && + !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) && + !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL,true) ) + { + //Safe fall, fall time reduction + int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); + uint32 fall_time = (movementInfo.fallTime > (safe_fall*10)) ? movementInfo.fallTime - (safe_fall*10) : 0; - return false; + if(fall_time > 1100) //Prevent damage if fall time < 1100 + { + //Fall Damage calculation + float fallperc = float(fall_time)/1100; + uint32 damage = (uint32)(((fallperc*fallperc -1) / 9 * GetMaxHealth())*sWorld.getRate(RATE_DAMAGE_FALL)); + + float height = movementInfo.z; + UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); + + if (damage > 0) + { + //Prevent fall damage from being more than the player maximum health + if (damage > GetMaxHealth()) + damage = GetMaxHealth(); + + // Gust of Wind + if (GetDummyAura(43621)) + damage = GetMaxHealth()/2; + + EnvironmentalDamage(GetGUID(), DAMAGE_FALL, damage); + } + + //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction + DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); + } + } +} + +void Player::HandleFallUnderMap() +{ + if(InBattleGround() && GetBattleGround() + && GetBattleGround()->HandlePlayerUnderMap(this)) + { + // 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(isAlive()) + { + EnvironmentalDamage(GetGUID(),DAMAGE_FALL_TO_VOID, GetMaxHealth()); + // change the death state to CORPSE to prevent the death timer from + // starting in the next player update + KillPlayer(); + BuildPlayerRepop(); + } + + // cancel the death timer here if started + RepopAtGraveyard(); + } +} + +void Player::Possess(Unit *target) +{ + if(!target || target == this) + return; + + // Don't allow possession of someone else's pet + if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && target != GetPet()) + return; + + // Don't allow possession on transports or when in flight; also remove possession from the now-to-be-possessed + if (target->GetTypeId() == TYPEID_PLAYER) + { + if (((Player*)target)->m_transport || ((Player*)target)->isInFlight()) + return; + if (target->isPossessing()) + ((Player*)target)->RemovePossess(true); + } + + // Remove any previous possession from the target + if (target->isPossessedByPlayer()) + ((Player*)target->GetCharmer())->RemovePossess(false); + else if (target->isCharmed()) + target->UncharmSelf(); // Target isn't possessed, but charmed; uncharm before possessing + + // Remove our previous possession + if (isPossessing()) + RemovePossess(true); + else if (GetCharm()) // We are charming a creature, not possessing it; uncharm ourself first + Uncharm(); + + // Interrupt any current casting of the target + if(target->IsNonMeleeSpellCasted(true)) + target->InterruptNonMeleeSpells(true); + + // Update the proper unit fields + SetPossessedTarget(target); + + target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction()); + target->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + SetUInt64Value(PLAYER_FARSIGHT, target->GetGUID()); + + if(target->GetTypeId() == TYPEID_UNIT) + { + // Set target to active in the grid and place it in the world container to be picked up by all regular player cell visits + Map* map = target->GetMap(); + map->SwitchGridContainers((Creature*)target, true); + target->setActive(true); + + ((Creature*)target)->InitPossessedAI(); // Initialize the possessed AI + target->StopMoving(); + target->GetMotionMaster()->Clear(false); + target->GetMotionMaster()->MoveIdle(); + } + + target->CombatStop(); + target->DeleteThreatList(); + + // Pets already have a properly initialized CharmInfo, don't overwrite it. + if(target->GetTypeId() == TYPEID_PLAYER || (target->GetTypeId() == TYPEID_UNIT && !((Creature*)target)->isPet())) + { + CharmInfo* charmInfo = target->InitCharmInfo(target); + charmInfo->InitPossessCreateSpells(); + } + + // Disable control for target player and remove AFK + if(target->GetTypeId() == TYPEID_PLAYER) + { + if(((Player*)target)->isAFK()) + ((Player*)target)->ToggleAFK(); + ((Player*)target)->SetViewport(target->GetGUID(), false); + } + + // Set current viewport to target unit, controllable + SetViewport(target->GetGUID(), true); + + PossessSpellInitialize(); +} + +void Player::RemovePossess(bool attack) +{ + Unit* target = GetCharm(); + if(!target || !target->isPossessed()) + return; + + // Remove area auras from possessed + Unit::AuraMap& tAuras = target->GetAuras(); + for(Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();) + { + if(itr->second && itr->second->IsAreaAura()) + target->RemoveAura(itr); + else + ++itr; + } + + RemovePossessedTarget(); + + if(target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->setFactionForRace(target->getRace()); + else if(target->GetTypeId() == TYPEID_UNIT) + { + // Set creature to inactive in grid and place it back into the grid container + Map* map = target->GetMap(); + target->setActive(false); + map->SwitchGridContainers((Creature*)target, false); + + if(((Creature*)target)->isPet()) + { + if(Unit* owner = target->GetOwner()) + target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, owner->getFaction()); + } else + { + if(CreatureInfo const* cInfo = ((Creature*)target)->GetCreatureInfo()) + target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, cInfo->faction_A); + } + } + + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + SetUInt64Value(PLAYER_FARSIGHT, 0); + + // Remove pet spell action bar + WorldPacket data(SMSG_PET_SPELLS, 8); + data << uint64(0); + m_session->SendPacket(&data); + + // Restore original view + SetViewport(GetGUID(), true); + if(target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->SetViewport(target->GetGUID(), true); + else + { + if(((Creature*)target)->isPet()) + { + ((Pet*)target)->InitPetCreateSpells(); + PetSpellInitialize(); + } + + if (target->isAlive()) + { + // If we're still hostile to our target, continue attacking otherwise reset threat and go home + if (target->getVictim()) + { + Unit* victim = target->getVictim(); + FactionTemplateEntry const* t_faction = target->getFactionTemplateEntry(); + FactionTemplateEntry const* v_faction = victim->getFactionTemplateEntry(); + // Unit::IsHostileTo will always return true since the unit is always hostile to its victim + if (t_faction && v_faction && !t_faction->IsHostileTo(*v_faction)) + { + // Stop combat and remove the target from the threat lists of all its victims + target->CombatStop(); + target->getHostilRefManager().deleteReferences(); + target->DeleteThreatList(); + target->GetMotionMaster()->Clear(); + target->GetMotionMaster()->MoveTargetedHome(); + } + } + else if (target->GetTypeId() == TYPEID_UNIT) + { + target->GetMotionMaster()->Clear(); + target->GetMotionMaster()->MoveTargetedHome(); + } + + // Add high amount of threat on the player + if(target != GetPet() && attack) + target->AddThreat(this, 1000000.0f); + } + // Delete the assigned possessed AI + ((Creature*)target)->DeletePossessedAI(); + } +} + +void Player::SetViewport(uint64 guid, bool moveable) +{ + WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, 8+1); + data.appendPackGUID(guid); // Packed guid of object to set client's view to + data << (moveable ? uint8(0x01) : uint8(0x00)); // 0 - can't move; 1 - can move + m_session->SendPacket(&data); + sLog.outDetail("Viewport for "I64FMT" (%s) changed to "I64FMT, GetGUID(), GetName(), guid); } bool Player::isAllowUseBattleGroundObject() @@ -18524,3 +18771,12 @@ bool Player::isAllowUseBattleGroundObject() isAlive() // live player ); } + +bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const +{ + for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr) + if(itr->pos == this->pos) + return true; + + return false; +} diff --git a/src/game/Player.h b/src/game/Player.h index 7971d73349f..4727aba1716 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -905,6 +905,10 @@ class TRINITY_DLL_SPEC Player : public Unit // always active void setActive(bool) {} + void SetViewport(uint64 guid, bool movable); + void Possess(Unit *target); + void RemovePossess(bool attack = true); + bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); bool TeleportTo(WorldLocation const &loc, uint32 options = 0) @@ -1421,6 +1425,7 @@ class TRINITY_DLL_SPEC Player : public Unit PlayerSpellMap const& GetSpellMap() const { return m_spells; } PlayerSpellMap & GetSpellMap() { return m_spells; } + ActionButtonList const& GetActionButtonList() const { return m_actionButtons; } void AddSpellMod(SpellModifier* mod, bool apply); int32 GetTotalFlatMods(uint32 spellId, SpellModOp op); @@ -1619,10 +1624,10 @@ class TRINITY_DLL_SPEC Player : public Unit bool SetPosition(float x, float y, float z, float orientation, bool teleport = false); void UpdateUnderwaterState( Map * m, float x, float y, float z ); - void SendMessageToSet(WorldPacket *data, bool self);// overwrite Object::SendMessageToSet - void SendMessageToSetInRange(WorldPacket *data, float fist, bool self); + void SendMessageToSet(WorldPacket *data, bool self, bool to_possessor = true);// overwrite Object::SendMessageToSet + void SendMessageToSetInRange(WorldPacket *data, float fist, bool self, bool to_possessor = true); // overwrite Object::SendMessageToSetInRange - void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only); + void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only, bool to_possessor); static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true); @@ -1948,6 +1953,8 @@ class TRINITY_DLL_SPEC Player : public Unit bool IsFlying() const { return HasUnitMovementFlag(MOVEMENTFLAG_FLYING); } void HandleDrowning(); + void HandleFallDamage(MovementInfo& movementInfo); + void HandleFallUnderMap(); void SetClientControl(Unit* target, uint8 allowMove); diff --git a/src/game/PossessedAI.cpp b/src/game/PossessedAI.cpp new file mode 100644 index 00000000000..cb0fde5c1d6 --- /dev/null +++ b/src/game/PossessedAI.cpp @@ -0,0 +1,124 @@ +/*
+ * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
+ *
+ * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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 "PossessedAI.h"
+#include "Creature.h"
+#include "World.h"
+
+void PossessedAI::AttackStart(Unit *u)
+{
+ if( i_pet.getVictim() || !u )
+ return;
+
+ if(i_pet.Attack(u, true))
+ i_victimGuid = u->GetGUID();
+
+ // Do not autochase our target, and also make sure our current movement generator
+ // is removed since the motion master is reset before this function is called
+ i_pet.GetMotionMaster()->Clear(false);
+ i_pet.GetMotionMaster()->MoveIdle();
+}
+
+bool PossessedAI::_needToStop() const
+{
+ if(!i_pet.getVictim() || !i_pet.isAlive())
+ return true;
+
+ // This is needed for charmed creatures, as once their target was reset other effects can trigger threat
+ if(i_pet.getVictim() == i_pet.GetCharmer())
+ return true;
+
+ return !i_pet.getVictim()->isTargetableForAttack();
+}
+
+void PossessedAI::_stopAttack()
+{
+ if( !i_victimGuid )
+ return;
+
+ Unit* victim = Unit::GetUnit(i_pet, i_victimGuid );
+
+ if ( !victim )
+ return;
+
+ assert(!i_pet.getVictim() || i_pet.getVictim() == victim);
+
+ if( !i_pet.isAlive() )
+ {
+ i_pet.StopMoving();
+ i_pet.GetMotionMaster()->Clear(false);
+ i_pet.GetMotionMaster()->MoveIdle();
+ i_victimGuid = 0;
+ i_pet.CombatStop();
+ i_pet.getHostilRefManager().deleteReferences();
+
+ return;
+ }
+
+ i_pet.GetMotionMaster()->Clear(false);
+ i_pet.GetMotionMaster()->MoveIdle();
+ i_victimGuid = 0;
+ i_pet.AttackStop();
+}
+
+void PossessedAI::UpdateAI(const uint32 diff)
+{
+ // update i_victimGuid if i_pet.getVictim() !=0 and changed
+ if(i_pet.getVictim())
+ i_victimGuid = i_pet.getVictim()->GetGUID();
+
+ // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
+ if( i_victimGuid )
+ {
+ if( _needToStop() )
+ {
+ _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now
+ return;
+ }
+ else if(i_pet.IsWithinCombatDist(i_pet.getVictim(), ATTACK_DISTANCE) && i_pet.isAttackReady())
+ {
+ i_pet.AttackerStateUpdate(i_pet.getVictim());
+
+ i_pet.resetAttackTimer();
+
+ if( _needToStop() )
+ _stopAttack();
+ }
+ }
+}
+
+bool PossessedAI::_isVisible(Unit *u) const
+{
+ return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_MONSTER)
+ && u->isVisibleForOrDetect(&i_pet,true);
+}
+
+void PossessedAI::JustDied(Unit *u)
+{
+ // We died while possessed, disable our loot
+ i_pet.RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+}
+
+void PossessedAI::KilledUnit(Unit* victim)
+{
+ // We killed a creature, disable victim's loot
+ if (victim->GetTypeId() == TYPEID_UNIT)
+ victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+}
diff --git a/src/game/PossessedAI.h b/src/game/PossessedAI.h new file mode 100644 index 00000000000..f906b82a32c --- /dev/null +++ b/src/game/PossessedAI.h @@ -0,0 +1,54 @@ +/*
+ * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
+ *
+ * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/>
+ *
+ * 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
+ */
+
+#ifndef MANGOS_POSSESSEDAI_H
+#define MANGOS_POSSESSEDAI_H
+
+#include "CreatureAI.h"
+
+class Creature;
+
+class TRINITY_DLL_DECL PossessedAI : public CreatureAI
+{
+ public:
+ PossessedAI(Creature &c) : i_pet(c), i_victimGuid(0) {}
+
+ // Possessed creatures shouldn't aggro by themselves
+ void MoveInLineOfSight(Unit *) {}
+ void AttackStart(Unit *);
+ void EnterEvadeMode() {}
+ void JustDied(Unit*);
+ void KilledUnit(Unit* victim);
+ void AttackedBy(Unit*) {}
+ bool IsVisible(Unit * u) const { return _isVisible(u); }
+
+ void UpdateAI(const uint32);
+ // Never permit this to be used, it must always be initialized with Creature::InitPossessedAI()
+ static int Permissible(const Creature *) { return PERMIT_BASE_NO; }
+
+ private:
+ bool _isVisible(Unit *) const;
+ bool _needToStop(void) const;
+ void _stopAttack(void);
+
+ Creature &i_pet;
+ uint64 i_victimGuid;
+};
+#endif
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 1ee787d042e..615b61566cd 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -4121,7 +4121,8 @@ int16 Spell::PetCanCast(Unit* target) //TARGET_DUELVSPLAYER is positive AND negative duelvsplayertar |= (m_spellInfo->EffectImplicitTargetA[j] == TARGET_DUELVSPLAYER); } - if(m_caster->IsFriendlyTo(target) && !duelvsplayertar) + // AoE spells have the caster as their target + if(m_caster->IsFriendlyTo(target) && m_caster != target && !duelvsplayertar) { return SPELL_FAILED_BAD_TARGETS; } diff --git a/src/game/Spell.h b/src/game/Spell.h index 79addb5bfcc..bdfffff3db9 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -240,6 +240,7 @@ class Spell void EffectDualWield(uint32 i); void EffectPickPocket(uint32 i); void EffectAddFarsight(uint32 i); + void EffectSummonPossessed(uint32 i); void EffectSummonWild(uint32 i); void EffectSummonGuardian(uint32 i); void EffectHealMechanical(uint32 i); diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 0f20a414c8a..d0a70858b71 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -49,6 +49,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" +#include "TemporarySummon.h" #define NULL_AURA_SLOT 0xFF @@ -527,8 +528,8 @@ void Aura::Update(uint32 diff) } } - // Channeled aura required check distance from caster - if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID()) + // Channeled aura required check distance from caster except in possessed cases + if(IsChanneledSpell(m_spellProto) && m_caster_guid != m_target->GetGUID() && !m_target->isPossessed()) { Unit* caster = GetCaster(); if(!caster) @@ -2003,6 +2004,7 @@ void Aura::HandleAuraDummy(bool apply, bool Real) caster->CastSpell(m_target,finalSpelId,true,NULL,this); return; } + // Dark Fiend if(GetId()==45934) { @@ -2018,6 +2020,13 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_target->CastSpell(m_target,47287,true,NULL,this); return; } + + // Eye of Kilrogg, unsummon eye when aura is gone + if(GetId() == 126 && caster->GetTypeId() == TYPEID_PLAYER && caster->GetCharm()) + { + ((TemporarySummon*)caster->GetCharm())->UnSummon(); + return; + } } // AT APPLY & REMOVE @@ -2886,56 +2895,11 @@ void Aura::HandleModPossess(bool apply, bool Real) if( apply ) { - m_target->SetCharmerGUID(GetCasterGUID()); - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,caster->getFaction()); - caster->SetCharm(m_target); - - m_target->CombatStop(); - m_target->DeleteThreatList(); - if(m_target->GetTypeId() == TYPEID_UNIT) - { - m_target->StopMoving(); - m_target->GetMotionMaster()->Clear(); - m_target->GetMotionMaster()->MoveIdle(); - CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target); - charmInfo->InitPossessCreateSpells(); - } - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)caster)->PossessSpellInitialize(); - } + if (caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)caster)->Possess(m_target); } else - { - m_target->SetCharmerGUID(0); - - if(m_target->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_target)->setFactionForRace(m_target->getRace()); - else if(m_target->GetTypeId() == TYPEID_UNIT) - { - CreatureInfo const *cinfo = ((Creature*)m_target)->GetCreatureInfo(); - m_target->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction_A); - } - - caster->SetCharm(0); - - if(caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - ((Player*)caster)->GetSession()->SendPacket(&data); - } - if(m_target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)m_target)->AIM_Initialize(); - - if (((Creature*)m_target)->AI()) - ((Creature*)m_target)->AI()->AttackStart(caster); - } - } - if(caster->GetTypeId() == TYPEID_PLAYER) - caster->SetUInt64Value(PLAYER_FARSIGHT,apply ? m_target->GetGUID() : 0); + m_target->UnpossessSelf(true); } void Aura::HandleModPossessPet(bool apply, bool Real) @@ -2951,13 +2915,11 @@ void Aura::HandleModPossessPet(bool apply, bool Real) if(apply) { - caster->SetUInt64Value(PLAYER_FARSIGHT, m_target->GetGUID()); - m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + ((Player*)caster)->Possess(m_target); } else { - caster->SetUInt64Value(PLAYER_FARSIGHT, 0); - m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN5); + ((Player*)caster)->RemovePossess(false); } } diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 39329e58bfd..e8060d282e6 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -132,7 +132,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT - &Spell::EffectSummonGuardian, // 73 SPELL_EFFECT_SUMMON_POSSESSED + &Spell::EffectSummonPossessed, // 73 SPELL_EFFECT_SUMMON_POSSESSED &Spell::EffectSummonTotem, // 74 SPELL_EFFECT_SUMMON_TOTEM &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD @@ -3127,9 +3127,11 @@ void Spell::EffectSummonType(uint32 i) switch(m_spellInfo->EffectMiscValueB[i]) { case SUMMON_TYPE_GUARDIAN: + EffectSummonGuardian(i); + break; case SUMMON_TYPE_POSESSED: case SUMMON_TYPE_POSESSED2: - EffectSummonGuardian(i); + EffectSummonPossessed(i); break; case SUMMON_TYPE_WILD: EffectSummonWild(i); @@ -3677,6 +3679,28 @@ void Spell::EffectSummonGuardian(uint32 i) } } +void Spell::EffectSummonPossessed(uint32 i) +{ + uint32 creatureEntry = m_spellInfo->EffectMiscValue[i]; + if(!creatureEntry) + return; + + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + uint32 level = m_caster->getLevel(); + + float px, py, pz; + m_caster->GetClosePoint(px, py, pz, DEFAULT_WORLD_OBJECT_SIZE); + + int32 duration = GetSpellDuration(m_spellInfo); + + TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN; + + Creature* c = m_caster->SummonCreature(creatureEntry, px, py, pz, m_caster->GetOrientation(), summonType, duration); + ((Player*)m_caster)->Possess(c); +} + void Spell::EffectTeleUnitsFaceCaster(uint32 i) { if(!unitTarget) diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index ba1eb20c631..051e1adbc30 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -302,6 +302,10 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) return; } + // can't use our own spells when we're in possession of another unit, + if(_player->isPossessing()) + return; + // client provided targets SpellCastTargets targets; if(!targets.read(&recvPacket,_player)) @@ -348,6 +352,21 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) if (!spellInfo) return; + // Remove possess aura from the possessed as well + if(_player->isPossessing()) + { + for (int i = 0; i < 3; ++i) + { + if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS || + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS_PET) + { + _player->RemoveAurasDueToSpellByCancel(spellId); + _player->GetCharm()->RemoveAurasDueToSpellByCancel(spellId); + return; + } + } + } + // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL)) return; diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp index e46cda6143d..357a32b4ecb 100644 --- a/src/game/TemporarySummon.cpp +++ b/src/game/TemporarySummon.cpp @@ -174,6 +174,8 @@ void TemporarySummon::UnSummon() { CombatStop(); + UnpossessSelf(false); + CleanupsBeforeDelete(); AddObjectToRemoveList(); diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index f1d8269cd65..0c94c095c3e 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -226,6 +226,7 @@ Unit::Unit() m_removedAuras = 0; m_charmInfo = NULL; m_unit_movement_flags = 0; + m_isPossessed = false; // remove aurastates allowing special moves for(int i=0; i < MAX_REACTIVE; ++i) @@ -701,10 +702,6 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if( pVictim->GetTypeId()==TYPEID_PLAYER && !damageFromSpiritOfRedemtionTalent ) ((Player*)pVictim)->SetPvPDeath(player!=NULL); - // Call KilledUnit for creatures - if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI()) - ((Creature*)this)->AI()->KilledUnit(pVictim); - // 10% durability loss on death // clean InHateListOf if (pVictim->GetTypeId() == TYPEID_PLAYER) @@ -718,6 +715,9 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); ((Player*)pVictim)->GetSession()->SendPacket(&data); } + // Call KilledUnit for creatures + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI()) + ((Creature*)this)->AI()->KilledUnit(pVictim); } else // creature died { @@ -729,6 +729,11 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa cVictim->DeleteThreatList(); cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); } + + // Call KilledUnit for creatures, this needs to be called after the lootable flag is set + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI()) + ((Creature*)this)->AI()->KilledUnit(pVictim); + // Call creature just died function if (cVictim->AI()) cVictim->AI()->JustDied(this); @@ -773,6 +778,13 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa he->DuelComplete(DUEL_INTERUPTED); } + // Possessed unit died, restore control to possessor + pVictim->UnpossessSelf(false); + + // Possessor died, remove possession + if(pVictim->GetTypeId() == TYPEID_PLAYER && pVictim->isPossessing()) + ((Player*)pVictim)->RemovePossess(false); + // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill) if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround())) { @@ -7169,6 +7181,29 @@ void Unit::SetCharm(Unit* charmed) SetUInt64Value(UNIT_FIELD_CHARM,charmed ? charmed->GetGUID() : 0); } +void Unit::UncharmSelf() +{ + if (!GetCharmer()) + return; + + RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM); +} + +void Unit::UnpossessSelf(bool attack) +{ + if (!isPossessed() || !GetCharmer()) + return; + + if (GetCharmer()->GetTypeId() == TYPEID_PLAYER) + ((Player*)GetCharmer())->RemovePossess(attack); + else + { + GetCharmer()->SetCharm(0); + SetCharmerGUID(0); + m_isPossessed = false; + } +} + void Unit::UnsummonAllTotems() { for (int8 i = 0; i < MAX_TOTEM; ++i) @@ -8588,16 +8623,19 @@ bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) return false; } + // If the player is currently possessing, update visibility from the possessed unit's location + const Unit* target = u->GetTypeId() == TYPEID_PLAYER && u->isPossessing() ? u->GetCharm() : u; + // different visible distance checks if(u->isInFlight()) // what see player in flight { // use object grey distance for all (only see objects any way) - if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f))) + if (!IsWithinDistInMap(target,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f))) return false; } else if(!isAlive()) // distance for show body { - if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f))) + if (!IsWithinDistInMap(target,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f))) return false; } else if(GetTypeId()==TYPEID_PLAYER) // distance for show player @@ -8605,14 +8643,14 @@ bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) if(u->GetTypeId()==TYPEID_PLAYER) { // Players far than max visible distance for player or not in our map are not visible too - if (!at_same_transport && !IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f))) + if (!at_same_transport && !IsWithinDistInMap(target,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f))) return false; } else { // Units far than max visible distance for creature or not in our map are not visible too // Active unit should always be visibile - if (!IsWithinDistInMap(u, u->isActive() + if (!IsWithinDistInMap(target, target->isActive() ? (MAX_VISIBILITY_DISTANCE - (inVisibleList ? 0.0f : World::GetVisibleUnitGreyDistance())) : (World::GetMaxVisibleDistanceForCreature() + (inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))) return false; @@ -8621,13 +8659,13 @@ bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) else if(GetCharmerOrOwnerGUID()) // distance for show pet/charmed { // Pet/charmed far than max visible distance for player or not in our map are not visible too - if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f))) + if (!IsWithinDistInMap(target,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f))) return false; } else // distance for show creature { // Units far than max visible distance for creature or not in our map are not visible too - if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f))) + if (!IsWithinDistInMap(target,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f))) return false; } @@ -9871,6 +9909,8 @@ void Unit::CleanupsBeforeDelete() RemoveAllGameObjects(); RemoveAllDynObjects(); GetMotionMaster()->Clear(false); // remove different non-standard movement generators. + + UnpossessSelf(false); } RemoveFromWorld(); } @@ -9923,17 +9963,17 @@ void CharmInfo::InitEmptyActionBar() void CharmInfo::InitPossessCreateSpells() { - if(m_unit->GetTypeId() == TYPEID_PLAYER) - return; - - InitEmptyActionBar(); //charm action bar - - for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x) + InitEmptyActionBar(); + if(m_unit->GetTypeId() == TYPEID_UNIT) { - if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x])) - m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true); - else - AddSpellToAB(0, ((Creature*)m_unit)->m_spells[x], ACT_CAST); + for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + { + uint32 spellid = ((Creature*)m_unit)->m_spells[i]; + if(IsPassiveSpell(spellid)) + m_unit->CastSpell(m_unit, spellid, true); + else + AddSpellToAB(0, spellid, ACT_CAST); + } } } diff --git a/src/game/Unit.h b/src/game/Unit.h index 66988b0a1b7..2482648180e 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -989,10 +989,32 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void SetPet(Pet* pet); void SetCharm(Unit* pet); + void SetPossessedTarget(Unit* target) + { + if (!target) return; + SetCharm(target); + target->SetCharmerGUID(GetGUID()); + target->m_isPossessed = true; + } + void RemovePossessedTarget() + { + if (!GetCharm()) return; + GetCharm()->SetCharmerGUID(0); + GetCharm()->m_isPossessed = false; + SetCharm(0); + } + bool isCharmed() const { return GetCharmerGUID() != 0; } + bool isPossessed() const { return m_isPossessed; } + bool isPossessedByPlayer() const { return m_isPossessed && IS_PLAYER_GUID(GetCharmerGUID()); } + bool isPossessing() const { return GetCharm() && GetCharm()->isPossessed(); } + bool isPossessing(Unit* u) const { return u->isPossessed() && GetCharmGUID() == u->GetGUID(); } + bool isPossessingCreature() const { return isPossessing() && IS_CREATURE_GUID(GetCharmGUID()); } CharmInfo* GetCharmInfo() { return m_charmInfo; } CharmInfo* InitCharmInfo(Unit* charm); + void UncharmSelf(); + void UnpossessSelf(bool attack); Pet* CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id = 0); @@ -1316,6 +1338,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject float m_speed_rate[MAX_MOVE_TYPE]; CharmInfo *m_charmInfo; + bool m_isPossessed; virtual SpellSchoolMask GetMeleeDamageSchoolMask() const; diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index f4e4e1c1d46..d8f5c8877cb 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -366,6 +366,13 @@ void WorldSession::LogoutPlayer(bool Save) if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket) _player->RemoveFromGroup(); + // Unpossess the current possessed unit of player + if (_player->isPossessing()) + _player->RemovePossess(false); + + // Remove any possession of this player on logout + _player->UnpossessSelf(false); + ///- Remove the player from the world // the player may not be in the world when logging out // e.g if he got disconnected during a transfer to another map diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index e544891b899..c4ff01884ff 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -31,6 +31,7 @@ class MailItemsInfo; struct ItemPrototype; struct AuctionEntry; struct DeclinedName; +struct MovementInfo; class Creature; class Item; @@ -317,6 +318,7 @@ class TRINITY_DLL_SPEC WorldSession void HandleMoveWorldportAckOpcode(); // for server-side calls void HandleMovementOpcodes(WorldPacket& recvPacket); + void HandlePossessedMovement(WorldPacket& recv_data, MovementInfo& movementInfo, uint32& MovementFlags); void HandleSetActiveMoverOpcode(WorldPacket &recv_data); void HandleMoveTimeSkippedOpcode(WorldPacket &recv_data); |