aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bindings/scripts/include/sc_creature.h3
-rw-r--r--src/bindings/scripts/scripts/npc/npc_escortAI.cpp19
-rw-r--r--src/bindings/scripts/scripts/npc/npc_escortAI.h2
-rw-r--r--src/game/Chat.cpp2
-rw-r--r--src/game/Chat.h2
-rw-r--r--src/game/Creature.cpp35
-rw-r--r--src/game/Creature.h5
-rw-r--r--src/game/CreatureAI.h3
-rw-r--r--src/game/CreatureAIRegistry.cpp2
-rw-r--r--src/game/CreatureAISelector.cpp4
-rw-r--r--src/game/GridNotifiers.cpp68
-rw-r--r--src/game/GridNotifiers.h48
-rw-r--r--src/game/Level3.cpp26
-rw-r--r--src/game/Makefile.am2
-rw-r--r--src/game/Map.cpp53
-rw-r--r--src/game/Map.h10
-rw-r--r--src/game/MovementHandler.cpp153
-rw-r--r--src/game/Object.cpp8
-rw-r--r--src/game/Object.h4
-rw-r--r--src/game/ObjectAccessor.cpp26
-rw-r--r--src/game/ObjectAccessor.h3
-rw-r--r--src/game/Pet.cpp2
-rw-r--r--src/game/PetHandler.cpp77
-rw-r--r--src/game/Player.cpp284
-rw-r--r--src/game/Player.h13
-rw-r--r--src/game/PossessedAI.cpp124
-rw-r--r--src/game/PossessedAI.h54
-rw-r--r--src/game/Spell.cpp3
-rw-r--r--src/game/Spell.h1
-rw-r--r--src/game/SpellAuras.cpp70
-rw-r--r--src/game/SpellEffects.cpp28
-rw-r--r--src/game/SpellHandler.cpp19
-rw-r--r--src/game/TemporarySummon.cpp2
-rw-r--r--src/game/Unit.cpp80
-rw-r--r--src/game/Unit.h23
-rw-r--r--src/game/WorldSession.cpp7
-rw-r--r--src/game/WorldSession.h2
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);