diff --git a/doc/EventAI.md b/doc/EventAI.md index dd0bb9213..62a205c4b 100644 --- a/doc/EventAI.md +++ b/doc/EventAI.md @@ -131,7 +131,7 @@ For all ACTION_T_RANDOM Actions, When a Particular Param is selected for the Eve 23 ACTION_T_INC_PHASE Value Increments the phase by (Param1). May be negative to decrement, but should not be zero. 24 ACTION_T_EVADE No Params Forces the creature to evade, wiping all threat and dropping combat. 25 ACTION_T_FLEE_FOR_ASSIST No Params Causes the creature to flee for assistence (often at low health). -26 ACTION_T_QUEST_EVENT_ALL QuestId Calls GroupEventHappens with (Param1). Only used if it's _expected_ event should call quest completion for all players in a current party. +26 ACTION_T_QUEST_EVENT_ALL QuestId, UseThreatList Calls GroupEventHappens with (Param1). Only used if it's _expected_ event should call quest completion for all players in a current party. Can be used for the creature's threat list, for complex scripted events. 27 ACTION_T_CASTCREATUREGO_ALL QuestId, SpellId Calls CastedCreatureOrGo for all players on the threat list with quest id specified in (Param1) and spell id in (Param2). 28 ACTION_T_REMOVEAURASFROMSPELL Target, Spellid Removes all auras on a target (Param1) caused by a spell (Param2). 29 ACTION_T_RANGED_MOVEMENT Distance, Angle Changes the movement generator to a ranged type. (note: default melee type can still be set by using 0 as angle and 0 as distance). @@ -701,10 +701,11 @@ NOTE: All Param Values Are 0 for this Action. 26 = ACTION_T_QUEST_EVENT_ALL: ------------------------------ Parameter 1: QuestId - The quest ID to finish for everyone. +Parameter 2: UseThreatList - Bool. If set to 1 (true), it will complete the QuestId for all the players in the threat list. If set to 0 (false), it will use the action invoker. This action does the same thing as the ACTION_T_QUEST_EVENT does but it does it for all players in the creature's threat list. NOTE: If a player is not in the NPC's threat list for whatever reason, he/she won't get the quest completed. - + --------------------------------- 27 = ACTION_T_CASTCREATUREGO_ALL: --------------------------------- diff --git a/src/game/Object/Creature.cpp b/src/game/Object/Creature.cpp index 3031a56ba..0b14db367 100644 --- a/src/game/Object/Creature.cpp +++ b/src/game/Object/Creature.cpp @@ -2241,8 +2241,9 @@ bool Creature::IsOutOfThreatArea(Unit* pVictim) const CreatureDataAddon const* Creature::GetCreatureAddon() const { - if (CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(GetGUIDLow())) - return addon; + if (!(GetObjectGuid().GetHigh() == HIGHGUID_PET)) // pets have guidlow that is conflicting with normal guidlows hence GetGUIDLow() gives wrong info + if (CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(GetGUIDLow())) + return addon; // dependent from difficulty mode entry if (GetEntry() != GetCreatureInfo()->Entry) diff --git a/src/game/Object/CreatureEventAI.cpp b/src/game/Object/CreatureEventAI.cpp index 41cd2a99d..0ba583602 100644 --- a/src/game/Object/CreatureEventAI.cpp +++ b/src/game/Object/CreatureEventAI.cpp @@ -832,7 +832,14 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 m_creature->DoFleeToGetAssistance(); break; case ACTION_T_QUEST_EVENT_ALL: - if (pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER) + if (action.quest_event_all.useThreatList) + { + ThreatList const& threatList = m_creature->GetThreatManager().getThreatList(); + for (ThreatList::const_iterator i = threatList.begin(); i != threatList.end(); ++i) + if (Player* temp = m_creature->GetMap()->GetPlayer((*i)->getUnitGuid())) + temp->GroupEventHappens(action.quest_event_all.questId, m_creature); + } + else if (pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER) ((Player*)pActionInvoker)->GroupEventHappens(action.quest_event_all.questId, m_creature); break; case ACTION_T_CAST_EVENT_ALL: diff --git a/src/game/Object/CreatureEventAI.h b/src/game/Object/CreatureEventAI.h index a70550525..4c674c491 100644 --- a/src/game/Object/CreatureEventAI.h +++ b/src/game/Object/CreatureEventAI.h @@ -103,7 +103,7 @@ enum EventAI_ActionType ACTION_T_INC_PHASE = 23, // Value (may be negative to decrement phase, should not be 0) ACTION_T_EVADE = 24, // No Params ACTION_T_FLEE_FOR_ASSIST = 25, // No Params - ACTION_T_QUEST_EVENT_ALL = 26, // QuestID + ACTION_T_QUEST_EVENT_ALL = 26, // QuestID, UseThreatList (1 = true, 0 = false) ACTION_T_CAST_EVENT_ALL = 27, // CreatureId, SpellId ACTION_T_REMOVEAURASFROMSPELL = 28, // Target, Spellid ACTION_T_RANGED_MOVEMENT = 29, // Distance, Angle @@ -297,6 +297,7 @@ struct CreatureEventAI_Action struct { uint32 questId; + uint32 useThreatList; // bool: 1 = true; 0 = false } quest_event_all; // ACTION_T_CAST_EVENT_ALL = 27 struct diff --git a/src/game/Object/Object.h b/src/game/Object/Object.h index 6ad7321e8..dfe3292a9 100644 --- a/src/game/Object/Object.h +++ b/src/game/Object/Object.h @@ -65,6 +65,12 @@ enum TempSummonType TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN = 9, // despawns after a specified time (OOC) OR when the creature dies }; +enum TempSummonLinkedAura +{ + TEMPSUMMON_LINKED_AURA_OWNER_CHECK = 0x00000001, + TEMPSUMMON_LINKED_AURA_REMOVE_OWNER = 0x00000002 +}; + enum PhaseMasks { PHASEMASK_NORMAL = 0x00000001, diff --git a/src/game/Object/Pet.cpp b/src/game/Object/Pet.cpp index a01326da3..4602c4011 100644 --- a/src/game/Object/Pet.cpp +++ b/src/game/Object/Pet.cpp @@ -39,7 +39,9 @@ Pet::Pet(PetType type) : m_resetTalentsCost(0), m_resetTalentsTime(0), m_usedTalentCount(0), m_removed(false), m_petType(type), m_duration(0), m_bonusdamage(0), m_auraUpdateMask(0), m_loading(false), - m_declinedname(NULL), m_petModeFlags(PET_MODE_DEFAULT) + m_declinedname(nullptr), m_petModeFlags(PET_MODE_DEFAULT), m_retreating(false), + m_stayPosSet(false), m_stayPosX(0), m_stayPosY(0), m_stayPosZ(0), m_stayPosO(0), + m_opener(0), m_openerMinRange(0), m_openerMaxRange(0) { m_name = "Pet"; m_regenTimer = 4000; diff --git a/src/game/Object/Pet.h b/src/game/Object/Pet.h index 451fe7c30..c2408d653 100644 --- a/src/game/Object/Pet.h +++ b/src/game/Object/Pet.h @@ -254,6 +254,21 @@ class Pet : public Creature PetSpellMap m_spells; AutoSpellList m_autospells; + uint32 m_opener; + uint32 m_openerMinRange; + uint32 m_openerMaxRange; + + uint32 GetSpellOpener() { return m_opener; } + uint32 GetSpellOpenerMinRange() { return m_openerMinRange; } + uint32 GetSpellOpenerMaxRange() { return m_openerMaxRange; } + + void SetSpellOpener(uint32 spellId = 0, uint32 minRange = 0, uint32 maxRange = 0) + { + m_opener = spellId; + m_openerMinRange = minRange; + m_openerMaxRange = maxRange; + } + void InitPetCreateSpells(); bool resetTalents(bool no_cost = false); diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 8f4bf0b3d..e2cbc2a88 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -2298,6 +2298,9 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 NpcFlagsmask) if (!guid || !IsInWorld() || IsTaxiFlying()) return NULL; + // set player as interacting + DoInteraction(guid); + // not in interactive state if (hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL)) return NULL; @@ -2339,12 +2342,15 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 NpcFlagsmask) return unit; } -GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type) const +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type) { // some basic checks if (!guid || !IsInWorld() || IsTaxiFlying()) return NULL; + // set player as interacting + DoInteraction(guid); + // not in interactive state if (hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL)) return NULL; @@ -21270,6 +21276,24 @@ void Player::SetClientControl(Unit* target, uint8 allowMove) GetSession()->SendPacket(&data); } +void Player::Uncharm() +{ + if (Unit* charm = GetCharm()) + { + charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM); + charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS); + charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS_PET); + if (charm == GetMover()) + { + SetMover(nullptr); + GetCamera().ResetView(); + RemoveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY); + SetCharm(nullptr); + SetClientControl(this, 1); + } + } +} + void Player::UpdateZoneDependentAuras() { // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update @@ -22699,6 +22723,15 @@ void Player::UnsummonPetTemporaryIfAny() pet->Unsummon(PET_SAVE_AS_CURRENT, this); } +void Player::UnsummonPetIfAny() +{ + Pet* pet = GetPet(); + if (!pet) + return; + + pet->Unsummon(PET_SAVE_NOT_IN_SLOT, this); +} + void Player::ResummonPetTemporaryUnSummonedIfAny() { if (!m_temporaryUnsummonedPetNumber) @@ -24307,6 +24340,41 @@ float Player::GetCollisionHeight(bool mounted) const } } +// set data to accept next resurrect response and process it with required data +void Player::setResurrectRequestData(Unit* caster, uint32 health, uint32 mana) +{ + m_resurrectGuid = caster->GetObjectGuid(); + m_resurrectMap = caster->GetMapId(); + caster->GetPosition(m_resurrectX, m_resurrectY, m_resurrectZ); + m_resurrectHealth = health; + m_resurrectMana = mana; + m_resurrectToGhoul = false; +} + +// we can use this to prepare data in case we have to resurrect player in ghoul form +void Player::setResurrectRequestDataToGhoul(Unit* caster) +{ + setResurrectRequestData(caster, 0, 0); + m_resurrectToGhoul = true; +} + +// player is interacting so we have to remove non authorized aura +void Player::DoInteraction(ObjectGuid const& interactObjGuid) +{ + if (interactObjGuid.IsUnit()) + { + // remove some aura like stealth aura + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); + } + else if (interactObjGuid.IsGameObject()) + { + // remove some aura like stealth aura + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_USE); + } + SendForcedObjectUpdate(); +} + + void Player::SendPetitionSignResult(ObjectGuid petitionGuid, Player* player, uint32 result) { WorldPacket data(SMSG_PETITION_SIGN_RESULTS, 8 + 8 + 4); diff --git a/src/game/Object/Player.h b/src/game/Object/Player.h index 9f59dee96..79157843e 100644 --- a/src/game/Object/Player.h +++ b/src/game/Object/Player.h @@ -1128,7 +1128,7 @@ class Player : public Unit void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time); Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint32 NpcFlagsmask); - GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type = MAX_GAMEOBJECT_TYPE) const; + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, uint32 gameobject_type = MAX_GAMEOBJECT_TYPE); void ToggleAFK(); void ToggleDND(); @@ -1214,7 +1214,13 @@ class Player : public Unit * \param: bool inRestPlace > if it was offline, is the player was in city/tavern/inn? * \returns: float **/ - float ComputeRest(time_t timePassed, bool offline = false, bool inRestPlace = false); + float ComputeRest(time_t timePassed, bool offline = false, bool inRestPlace = false); + + /** + * \brief: player is interacting with something. + * \param: ObjectGuid interactObj > object that interact with this player + **/ + void DoInteraction(ObjectGuid const& interactObjGuid); RestType GetRestType() const { @@ -1817,6 +1823,8 @@ class Player : public Unit uint32 GetLastPotionId() { return m_lastPotionId; } void UpdatePotionCooldown(Spell* spell = NULL); + void setResurrectRequestData(Unit* caster, uint32 health, uint32 mana); + void setResurrectRequestDataToGhoul(Unit* caster); void setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana) { m_resurrectGuid = guid; @@ -2413,6 +2421,7 @@ class Player : public Unit void SetMover(Unit* target) { m_mover = target ? target : this; } Unit* GetMover() const { return m_mover; } bool IsSelfMover() const { return m_mover == this; }// normal case for player not controlling other unit + void Uncharm() override; ObjectGuid const& GetFarSightGuid() const { return GetGuidValue(PLAYER_FARSIGHT); } @@ -2472,6 +2481,7 @@ class Player : public Unit uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; } void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; } void UnsummonPetTemporaryIfAny(); + void UnsummonPetIfAny(); void ResummonPetTemporaryUnSummonedIfAny(); bool IsPetNeedBeTemporaryUnsummoned() const { return !IsInWorld() || !IsAlive() || IsMounted() /*+in flight*/; } @@ -2745,6 +2755,7 @@ class Player : public Unit uint32 m_resurrectMap; float m_resurrectX, m_resurrectY, m_resurrectZ; uint32 m_resurrectHealth, m_resurrectMana; + bool m_resurrectToGhoul; WorldSession* m_session; diff --git a/src/game/Object/TemporarySummon.cpp b/src/game/Object/TemporarySummon.cpp index 4170c7f41..83402c9b5 100644 --- a/src/game/Object/TemporarySummon.cpp +++ b/src/game/Object/TemporarySummon.cpp @@ -27,7 +27,7 @@ #include "CreatureAI.h" TemporarySummon::TemporarySummon(ObjectGuid summoner) : - Creature(CREATURE_SUBTYPE_TEMPORARY_SUMMON), m_type(TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN), m_timer(0), m_lifetime(0), m_summoner(summoner) + Creature(CREATURE_SUBTYPE_TEMPORARY_SUMMON), m_type(TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN), m_timer(0), m_lifetime(0), m_summoner(summoner), m_linkedToOwnerAura(0) { } @@ -189,31 +189,93 @@ void TemporarySummon::Update(uint32 update_diff, uint32 diff) break; } + switch (m_deathState) + { + case ALIVE: + if (m_linkedToOwnerAura & TEMPSUMMON_LINKED_AURA_OWNER_CHECK) + { + // we have to check if owner still have the required aura + Unit* owner = GetCharmerOrOwner(); + uint32 const& spellId = GetUInt32Value(UNIT_CREATED_BY_SPELL); + if (!owner || !spellId || !owner->HasAura(spellId)) + UnSummon(); + } + break; + + case DEAD: + case CORPSE: + if (m_linkedToOwnerAura & TEMPSUMMON_LINKED_AURA_REMOVE_OWNER) + { + RemoveAuraFromOwner(); + m_linkedToOwnerAura = 0; // we dont need to recheck + } + + default: + break; + } + Creature::Update(update_diff, diff); } -void TemporarySummon::Summon(TempSummonType type, uint32 lifetime) +void TemporarySummon::SetSummonProperties(TempSummonType type, uint32 lifetime) { m_type = type; m_timer = lifetime; m_lifetime = lifetime; +} + +void TemporarySummon::Summon(TempSummonType type, uint32 lifetime) +{ + SetSummonProperties(type, lifetime); + + GetMap()->Add((Creature*)this); AIM_Initialize(); - GetMap()->Add((Creature*)this); } void TemporarySummon::UnSummon() { CombatStop(); + if (m_linkedToOwnerAura & TEMPSUMMON_LINKED_AURA_REMOVE_OWNER) + RemoveAuraFromOwner(); + if (GetSummonerGuid().IsCreatureOrVehicle()) + { if (Creature* sum = GetMap()->GetCreature(GetSummonerGuid())) if (sum->AI()) sum->AI()->SummonedCreatureDespawn(this); + } + else if (GetSummonerGuid().IsPlayer()) // if player that summoned this creature was MCing it, uncharm + if (Player* player = GetMap()->GetPlayer(GetSummonerGuid())) + if (player->GetMover() == this) + player->Uncharm(); + + if (AI()) + AI()->SummonedCreatureDespawn(this); AddObjectToRemoveList(); } +void TemporarySummon::RemoveAuraFromOwner() +{ + // creature is dead and we have to remove the charmer aura if exist + uint32 const& spellId = GetUInt32Value(UNIT_CREATED_BY_SPELL); + if (spellId) + { + + if (Unit* charmer = GetCharmer()) + { + charmer->RemoveAurasDueToSpell(spellId); + charmer->ResetControlState(false); + } + else if (Unit* owner = GetOwner()) + { + owner->RemoveAurasDueToSpell(spellId); + } + } +} + void TemporarySummon::SaveToDB() { } diff --git a/src/game/Object/TemporarySummon.h b/src/game/Object/TemporarySummon.h index 6799e7e50..1af73d683 100644 --- a/src/game/Object/TemporarySummon.h +++ b/src/game/Object/TemporarySummon.h @@ -35,12 +35,15 @@ class TemporarySummon : public Creature virtual ~TemporarySummon() {}; void Update(uint32 update_diff, uint32 time) override; + void SetSummonProperties(TempSummonType type, uint32 lifetime); void Summon(TempSummonType type, uint32 lifetime); - void UnSummon(); + void MANGOS_DLL_SPEC UnSummon(); void SaveToDB(); ObjectGuid const& GetSummonerGuid() const { return m_summoner ; } Unit* GetSummoner() const { return ObjectAccessor::GetUnit(*this, m_summoner); } + void SetLinkedToOwnerAura(uint32 flags) { m_linkedToOwnerAura |= flags; }; private: + void RemoveAuraFromOwner(); void SaveToDB(uint32, uint8, uint32) override // overwrited of Creature::SaveToDB - don't must be called { MANGOS_ASSERT(false); @@ -54,6 +57,7 @@ class TemporarySummon : public Creature uint32 m_timer; uint32 m_lifetime; ObjectGuid m_summoner; + uint32 m_linkedToOwnerAura; }; class TemporarySummonWaypoint : public TemporarySummon diff --git a/src/game/Object/Unit.cpp b/src/game/Object/Unit.cpp index 6fefd8a91..3937eae55 100644 --- a/src/game/Object/Unit.cpp +++ b/src/game/Object/Unit.cpp @@ -12298,3 +12298,225 @@ void Unit::SendCollisionHeightUpdate(float height) } } +// This will create a new creature and set the current unit as the controller of that new creature +Unit* Unit::TakePossessOf(SpellEntry const* spellEntry, SummonPropertiesEntry const* summonProp, SpellEffectEntry const* spellEffect, float x, float y, float z, float ang) +{ + int32 const& creatureEntry = spellEffect->EffectMiscValue; + CreatureInfo const* cinfo = ObjectMgr::GetCreatureTemplate(creatureEntry); + if (!cinfo) + { + sLog.outErrorDb("WorldObject::SummonCreature: Creature (Entry: %u) not existed for summoner: %s. ", creatureEntry, GetGuidStr().c_str()); + return nullptr; + } + + if (GetCharm()) + { + sLog.outError("Unit::TakePossessOf> There is already a charmed creature for %s its : %s. ", GetGuidStr().c_str(), GetCharm()->GetGuidStr().c_str()); + return nullptr; + } + + TemporarySummon* pCreature = new TemporarySummon(GetObjectGuid()); + + CreatureCreatePos pos(GetMap(), x, y, z, ang, GetPhaseMask()); + + if (x == 0.0f && y == 0.0f && z == 0.0f) + pos = CreatureCreatePos(this, GetOrientation(), CONTACT_DISTANCE, ang); + + if (!pCreature->Create(GetMap()->GenerateLocalLowGuid(cinfo->GetHighGuid()), pos, cinfo)) + { + delete pCreature; + return nullptr; + } + + Player* player = GetTypeId() == TYPEID_PLAYER ? static_cast(this): nullptr; + + pCreature->setFaction(getFaction()); // set same faction than player + pCreature->SetRespawnCoord(pos); // set spawn coord + pCreature->SetCharmerGuid(GetObjectGuid()); // save guid of the charmer + pCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, spellEntry->Id); // set the spell id used to create this (may be used for removing corresponding aura + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); // set flag for client that mean this unit is controlled by a player + pCreature->addUnitState(UNIT_STAT_CONTROLLED); // also set internal unit state flag + pCreature->SelectLevel(getLevel()); // set level to same level than summoner TODO:: not sure its always the case... + pCreature->SetLinkedToOwnerAura(TEMPSUMMON_LINKED_AURA_OWNER_CHECK | TEMPSUMMON_LINKED_AURA_REMOVE_OWNER); // set what to do if linked aura is removed or the creature is dead. + pCreature->SetWalk(IsWalking(), true); // sync the walking state with the summoner + + // important before adding to the map! + SetCharmGuid(pCreature->GetObjectGuid()); // save guid of charmed creature + + pCreature->SetSummonProperties(TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000); // set 5s corpse decay + GetMap()->Add(static_cast(pCreature)); // create the creature in the client + + // Give the control to the player + if (player) + { + player->GetCamera().SetView(pCreature); // modify camera view to the creature view + player->SetClientControl(pCreature, 1); // transfer client control to the creature + player->SetMover(pCreature); // set mover so now we know that creature is "moved" by this unit + player->SendForcedObjectUpdate(); // we have to update client data here to avoid problem with the "release spirit" windows reappear. + } + + // initialize AI + pCreature->AIM_Initialize(); + + if (player) + { + // Initialize pet bar + if (CharmInfo* charmInfo = pCreature->InitCharmInfo(pCreature)) + charmInfo->InitPossessCreateSpells(); + player->PossessSpellInitialize(); + } + else + { + // fire just summoned hook + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI()) + ((Creature*)this)->AI()->JustSummoned(pCreature); + } + + // Creature Linking, Initial load is handled like respawn + if (pCreature->IsLinkingEventTrigger()) + GetMap()->GetCreatureLinkingHolder()->DoCreatureLinkingEvent(LINKING_EVENT_RESPAWN, pCreature); + + // return the creature therewith the summoner has access to it + return pCreature; +} + +bool Unit::TakePossessOf(Unit* possessed) +{ + Player* player = nullptr; + if (GetTypeId() == TYPEID_PLAYER) + player = static_cast(this); + + possessed->addUnitState(UNIT_STAT_CONTROLLED); + possessed->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + possessed->SetCharmerGuid(GetObjectGuid()); + possessed->setFaction(getFaction()); + + SetCharm(possessed); + + Creature* possessedCreature = nullptr; + if (possessed->GetTypeId() == TYPEID_UNIT) + possessedCreature = static_cast(possessed); + + if (player) + { + player->GetCamera().SetView(possessed); + player->SetClientControl(possessed, 1); + player->SetMover(possessed); + player->SendForcedObjectUpdate(); + + if (possessedCreature && possessedCreature->IsPet() && possessedCreature->GetObjectGuid() == GetPetGuid()) + { + possessed->StopMoving(); + possessed->GetMotionMaster()->Clear(false); + possessed->GetMotionMaster()->MoveIdle(); + return true; + } + else if (CharmInfo* charmInfo = possessed->InitCharmInfo(possessed)) + { + charmInfo->InitPossessCreateSpells(); + charmInfo->SetReactState(REACT_PASSIVE); + charmInfo->SetCommandState(COMMAND_STAY); + } + player->PossessSpellInitialize(); + } + + possessed->CombatStop(true); + possessed->DeleteThreatList(); + possessed->getHostileRefManager().deleteReferences(); + + if (possessedCreature) + { + possessedCreature->AIM_Initialize(); + } + else if (possessed->GetTypeId() == TYPEID_PLAYER) + { + static_cast(possessed)->SetClientControl(possessed, 0); + } + return true; +} + +void Unit::ResetControlState(bool attackCharmer /*= true*/) +{ + Player* player = nullptr; + if (GetTypeId() == TYPEID_PLAYER) + player = static_cast(this); + + Unit* possessed = GetCharm(); + + if (!possessed) + { + if (player) + { + player->GetCamera().ResetView(); + player->SetClientControl(player, 1); + player->SetMover(nullptr); + } + return; + } + + Creature* possessedCreature = static_cast(possessed); + + possessed->clearUnitState(UNIT_STAT_CONTROLLED); + possessed->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + possessed->SetCharmerGuid(ObjectGuid()); + SetCharmGuid(ObjectGuid()); + + if (player) + { + player->SetClientControl(possessed, 0); + player->SetMover(nullptr); + player->GetCamera().ResetView(); + + if (possessedCreature->IsPet() && possessedCreature->GetObjectGuid() == GetPetGuid()) + { + // out of range pet dismissed + if (!possessedCreature->IsWithinDistInMap(this, possessedCreature->GetMap()->GetVisibilityDistance())) + { + player->RemovePet(PET_SAVE_REAGENTS); + } + else + { + possessedCreature->GetMotionMaster()->MoveFollow(this, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } + + return; + } + else + player->RemovePetActionBar(); + } + + possessed->CombatStop(true); + possessed->DeleteThreatList(); + possessed->getHostileRefManager().deleteReferences(); + + if (possessed->GetTypeId() == TYPEID_PLAYER) + { + Player* possessedPlayer = static_cast(possessed); + possessedPlayer->setFactionForRace(possessedPlayer->getRace()); + possessedPlayer->SetClientControl(possessedPlayer, 1); + } + else if (possessedCreature) + { + if (possessedCreature->IsPet() && possessedCreature->GetObjectGuid() == GetPetGuid()) + { + // out of range pet dismissed + if (!possessedCreature->IsWithinDistInMap(this, possessedCreature->GetMap()->GetVisibilityDistance())) + { + player->RemovePet(PET_SAVE_REAGENTS); + } + else + { + possessedCreature->GetMotionMaster()->MoveFollow(this, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } + } + else if (attackCharmer) + { + CreatureInfo const* cinfo = possessedCreature->GetCreatureInfo(); + possessedCreature->setFaction(cinfo->FactionAlliance); + possessedCreature->AIM_Initialize(); + possessedCreature->AttackedBy(this); + } + } +} + + diff --git a/src/game/Object/Unit.h b/src/game/Object/Unit.h index 9ac6ce4f5..d5e78d207 100644 --- a/src/game/Object/Unit.h +++ b/src/game/Object/Unit.h @@ -2826,7 +2826,7 @@ class Unit : public WorldObject * - \ref AuraType::SPELL_AURA_MOD_POSSESS * - \ref AuraType::SPELL_AURA_MOD_POSSESS_PET */ - void Uncharm(); + virtual void Uncharm(); /** * Does the same as \ref Unit::GetCharmerOrOwnerGuid but returns the \ref Unit for that instead * @return the \ref Unit that's charming this one or owning it, NULL if there is none @@ -3530,6 +3530,15 @@ class Unit : public WorldObject void BuildMoveHoverPacket(WorldPacket* data, bool apply, uint32 value); void BuildMoveLevitatePacket(WorldPacket* data, bool apply, uint32 value); + // Take possession of an unit (pet, creature, ...) + bool TakePossessOf(Unit* possessed); + + // Take possession of a new spawned unit + Unit* TakePossessOf(SpellEntry const* spellEntry, SummonPropertiesEntry const* summonProp, SpellEffectEntry const* spellEffect, float x, float y, float z, float ang); + + // Reset control to player + void ResetControlState(bool attackCharmer = true); + protected: explicit Unit(); diff --git a/src/game/Server/SharedDefines.h b/src/game/Server/SharedDefines.h index 525fcb1cc..8bb80d6d7 100644 --- a/src/game/Server/SharedDefines.h +++ b/src/game/Server/SharedDefines.h @@ -1493,7 +1493,7 @@ enum Targets TARGET_91 = 91, TARGET_SUMMONER = 92, TARGET_CONTROLLED_VEHICLE = 94, - TARGET_95 = 95, + TARGET_VEHICLE_DRIVER = 95, TARGET_VEHICLE_PASSENGER_0 = 96, TARGET_VEHICLE_PASSENGER_1 = 97, TARGET_VEHICLE_PASSENGER_2 = 98, diff --git a/src/game/WorldHandlers/AuctionHouseHandler.cpp b/src/game/WorldHandlers/AuctionHouseHandler.cpp index fe740318d..ecd3ae6a1 100644 --- a/src/game/WorldHandlers/AuctionHouseHandler.cpp +++ b/src/game/WorldHandlers/AuctionHouseHandler.cpp @@ -52,10 +52,6 @@ void WorldSession::HandleAuctionHelloOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - SendAuctionHello(unit); } @@ -295,10 +291,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - for (uint32 i = 0; i < itemCount; ++i) { ObjectGuid itemGuid = guids[i]; @@ -398,10 +390,6 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recv_data) // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* pl = GetPlayer(); @@ -477,10 +465,6 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recv_data) // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* pl = GetPlayer(); @@ -556,10 +540,6 @@ void WorldSession::HandleAuctionListBidderItems(WorldPacket& recv_data) // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4 + 4 + 4)); Player* pl = GetPlayer(); data << uint32(0); // add 0 as count @@ -604,10 +584,6 @@ void WorldSession::HandleAuctionListOwnerItems(WorldPacket& recv_data) // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4 + 4 + 4)); data << uint32(0); // amount place holder @@ -677,10 +653,6 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recv_data) AuctionSorter sorter(Sort, GetPlayer()); std::sort(auctions.begin(), auctions.end(), sorter); - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // DEBUG_LOG("Auctionhouse search %s list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", // auctioneerGuid.GetString().c_str(), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable); diff --git a/src/game/WorldHandlers/GuildHandler.cpp b/src/game/WorldHandlers/GuildHandler.cpp index 3891e0369..90dd74787 100644 --- a/src/game/WorldHandlers/GuildHandler.cpp +++ b/src/game/WorldHandlers/GuildHandler.cpp @@ -959,10 +959,6 @@ void WorldSession::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - Guild* guild = sGuildMgr.GetGuildById(GetPlayer()->GetGuildId()); if (!guild) { diff --git a/src/game/WorldHandlers/ItemHandler.cpp b/src/game/WorldHandlers/ItemHandler.cpp index 5db0fb79c..8ac9bb157 100644 --- a/src/game/WorldHandlers/ItemHandler.cpp +++ b/src/game/WorldHandlers/ItemHandler.cpp @@ -351,10 +351,6 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - Item* pItem = _player->GetItemByGuid(itemGuid); if (pItem) { @@ -457,10 +453,6 @@ void WorldSession::HandleBuybackItem(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - Item* pItem = _player->GetItemFromBuyBackSlot(slot); if (pItem) { @@ -569,10 +561,6 @@ void WorldSession::SendListInventory(ObjectGuid vendorguid) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // Stop the npc if moving pCreature->StopMoving(); diff --git a/src/game/WorldHandlers/NPCHandler.cpp b/src/game/WorldHandlers/NPCHandler.cpp index 7a2973c17..55df8d320 100644 --- a/src/game/WorldHandlers/NPCHandler.cpp +++ b/src/game/WorldHandlers/NPCHandler.cpp @@ -65,10 +65,6 @@ void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - SendTabardVendorActivate(guid); } @@ -90,10 +86,6 @@ void WorldSession::HandleBankerActivateOpcode(WorldPacket& recv_data) if (!CheckBanker(guid)) return; - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - SendShowBank(guid); } @@ -156,10 +148,6 @@ void WorldSession::SendTrainerList(ObjectGuid guid, const std::string& strTitle) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // trainer list loaded at check; if (!unit->IsTrainerOf(_player, true)) return; @@ -259,10 +247,6 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recv_data) uint32 trainState = 2; - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - if (!unit->IsTrainerOf(_player, true)) trainState = 1; @@ -348,10 +332,6 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - pCreature->StopMoving(); if (pCreature->IsSpiritGuide()) @@ -381,10 +361,6 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recv_data) DEBUG_LOG("Gossip code: %s", code.c_str()); } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - uint32 sender = _player->PlayerTalkClass->GossipOptionSender(gossipListId); uint32 action = _player->PlayerTalkClass->GossipOptionAction(gossipListId); @@ -431,10 +407,6 @@ void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - SendSpiritResurrect(); } @@ -506,10 +478,6 @@ void WorldSession::HandleBinderActivateOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - SendBindPoint(unit); } @@ -544,10 +512,6 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - SendStablePet(npcGUID); } @@ -656,10 +620,6 @@ void WorldSession::HandleStablePet(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - Pet* pet = _player->GetPet(); // can't place in stable dead pet @@ -716,10 +676,6 @@ void WorldSession::HandleUnstablePet(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - uint32 creature_id = 0; { @@ -785,10 +741,6 @@ void WorldSession::HandleBuyStableSlot(WorldPacket& recv_data) SendStableResult(STABLE_ERR_STABLE); return; } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } void WorldSession::HandleStableRevivePet(WorldPacket &/* recv_data */) @@ -810,10 +762,6 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - Pet* pet = _player->GetPet(); if (!pet || pet->getPetType() != HUNTER_PET) @@ -885,10 +833,6 @@ void WorldSession::HandleRepairItemOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // reputation discount float discountMod = _player->GetReputationPriceDiscount(unit); diff --git a/src/game/WorldHandlers/PetHandler.cpp b/src/game/WorldHandlers/PetHandler.cpp index 8dcda78e9..0df67d89f 100644 --- a/src/game/WorldHandlers/PetHandler.cpp +++ b/src/game/WorldHandlers/PetHandler.cpp @@ -150,9 +150,12 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) - if (((Creature*)pet)->IsPet()) + { + Creature* petC = (Creature*)pet; + if (petC->IsPet()) + { - Pet* p = (Pet*)pet; + Pet* p = (Pet*)petC; if (p->getPetType() == HUNTER_PET) p->Unsummon(PET_SAVE_AS_DELETED, _player); else @@ -161,7 +164,16 @@ void WorldSession::HandlePetAction(WorldPacket& recv_data) } else // charmed _player->Uncharm(); + + if (petC->IsTemporarySummon()) // special case when pet was temporary summon through DoSummonPossesed + { + petC->ForcedDespawn(); + return; + } + + ((Pet*)pet)->SetStayPosition(); break; + } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } diff --git a/src/game/WorldHandlers/PetitionsHandler.cpp b/src/game/WorldHandlers/PetitionsHandler.cpp index f1d741525..9c2122c6a 100644 --- a/src/game/WorldHandlers/PetitionsHandler.cpp +++ b/src/game/WorldHandlers/PetitionsHandler.cpp @@ -88,10 +88,6 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - if (!pCreature->IsTabardDesigner()) { sLog.outError("WORLD: HandlePetitionBuyOpcode - unsupported npc type, npc: %s", guidNPC.GetString().c_str()); @@ -673,10 +669,6 @@ void WorldSession::SendPetitionShowList(ObjectGuid guid) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // only guild petitions currently used if (!pCreature->IsTabardDesigner()) { diff --git a/src/game/WorldHandlers/QuestHandler.cpp b/src/game/WorldHandlers/QuestHandler.cpp index c608c9c11..cf41734ca 100644 --- a/src/game/WorldHandlers/QuestHandler.cpp +++ b/src/game/WorldHandlers/QuestHandler.cpp @@ -100,10 +100,6 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // Stop the npc if moving pCreature->StopMoving(); diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index 12cff13a6..cd4d40f39 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -2257,6 +2257,11 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (m_caster->IsBoarded() && m_caster->GetTransportInfo()->IsOnVehicle()) targetUnitMap.push_back((Unit*)m_caster->GetTransportInfo()->GetTransport()); break; + case TARGET_VEHICLE_DRIVER: + if (m_caster->IsVehicle()) + if (Unit* vehicleDriver = m_caster->GetCharmer()) + targetUnitMap.push_back(vehicleDriver); + break; case TARGET_VEHICLE_PASSENGER_0: case TARGET_VEHICLE_PASSENGER_1: case TARGET_VEHICLE_PASSENGER_2: @@ -3704,8 +3709,10 @@ void Spell::_handle_immediate_phase() if(!spellEffect) continue; // persistent area auras target only the ground - if(spellEffect->Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) - HandleEffects(NULL, NULL, NULL, SpellEffectIndex(j)); + if (spellEffect->Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA || + //summon a gameobject at the spell's destination xyz + (spellEffect->Effect == SPELL_EFFECT_TRANS_DOOR && spellEffect->EffectImplicitTargetA == TARGET_AREAEFFECT_GO_AROUND_DEST)) + HandleEffects(nullptr, nullptr, nullptr, SpellEffectIndex(j)); } } @@ -6505,6 +6512,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) target = m_targets.getUnitTarget(); bool need = false; + bool script = false; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(SpellEffectIndex(i)); @@ -6523,9 +6531,16 @@ SpellCastResult Spell::CheckPetCast(Unit* target) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; break; } + else if (spellEffect->EffectImplicitTargetA == TARGET_SCRIPT_COORDINATES) + { + script = true; + continue; + } } if (need) m_targets.setUnitTarget(target); + else if (script == true) + return CheckCast(true); Unit* _target = m_targets.getUnitTarget(); diff --git a/src/game/WorldHandlers/SpellAuras.cpp b/src/game/WorldHandlers/SpellAuras.cpp index 3c008176f..a36989dda 100644 --- a/src/game/WorldHandlers/SpellAuras.cpp +++ b/src/game/WorldHandlers/SpellAuras.cpp @@ -4278,117 +4278,47 @@ void Aura::HandleAuraModScale(bool apply, bool /*Real*/) void Aura::HandleModPossess(bool apply, bool Real) { if (!Real) - { return; } + return; Unit* target = GetTarget(); // not possess yourself if (GetCasterGuid() == target->GetObjectGuid()) - { return; } + return; Unit* caster = GetCaster(); - if (!caster || caster->GetTypeId() != TYPEID_PLAYER) - { return; } - - Player* p_caster = (Player*)caster; - Camera& camera = p_caster->GetCamera(); + if (!caster || caster->GetTypeId() != TYPEID_PLAYER) // TODO:: well i know some bosses can take control of player??? + return; if (apply) { - target->addUnitState(UNIT_STAT_CONTROLLED); - - target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - target->SetCharmerGuid(p_caster->GetObjectGuid()); - target->setFaction(p_caster->getFaction()); - - // target should became visible at SetView call(if not visible before): - // otherwise client\p_caster will ignore packets from the target(SetClientControl for example) - camera.SetView(target); - - p_caster->SetCharm(target); - p_caster->SetClientControl(target, 1); - p_caster->SetMover(target); - - target->CombatStop(true); - target->DeleteThreatList(); - target->getHostileRefManager().deleteReferences(); - - if (CharmInfo* charmInfo = target->InitCharmInfo(target)) + if (caster->GetTypeId() == TYPEID_PLAYER) { - charmInfo->InitPossessCreateSpells(); - charmInfo->SetReactState(REACT_PASSIVE); - charmInfo->SetCommandState(COMMAND_STAY); + //remove any existing charm just in case + caster->Uncharm(); + + //pets should be removed when possesing a target if somehow check was bypassed + ((Player*)caster)->UnsummonPetIfAny(); } - p_caster->PossessSpellInitialize(); - - if (target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)target)->AIM_Initialize(); - } - else if (target->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)target)->SetClientControl(target, 0); - } + caster->TakePossessOf(target); } else - { - p_caster->SetCharm(NULL); - - p_caster->SetClientControl(target, 0); - p_caster->SetMover(NULL); - - // there is a possibility that target became invisible for client\p_caster at ResetView call: - // it must be called after movement control unapplying, not before! the reason is same as at aura applying - camera.ResetView(); - - p_caster->RemovePetActionBar(); - - // on delete only do caster related effects - if (m_removeMode == AURA_REMOVE_BY_DELETE) - { return; } - - target->clearUnitState(UNIT_STAT_CONTROLLED); - - target->CombatStop(true); - target->DeleteThreatList(); - target->getHostileRefManager().deleteReferences(); - - target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - - target->SetCharmerGuid(ObjectGuid()); - - if (target->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)target)->setFactionForRace(target->getRace()); - ((Player*)target)->SetClientControl(target, 1); - } - else if (target->GetTypeId() == TYPEID_UNIT) - { - CreatureInfo const* cinfo = ((Creature*)target)->GetCreatureInfo(); - target->setFaction(cinfo->FactionAlliance); - } - - if (target->GetTypeId() == TYPEID_UNIT) - { - ((Creature*)target)->AIM_Initialize(); - target->AttackedBy(caster); - } - } + caster->ResetControlState(); } void Aura::HandleModPossessPet(bool apply, bool Real) { if (!Real) - { return; } + return; Unit* caster = GetCaster(); if (!caster || caster->GetTypeId() != TYPEID_PLAYER) - { return; } + return; Unit* target = GetTarget(); if (target->GetTypeId() != TYPEID_UNIT || !((Creature*)target)->IsPet()) - { return; } + return; Pet* pet = (Pet*)target; @@ -4397,52 +4327,23 @@ void Aura::HandleModPossessPet(bool apply, bool Real) if (apply) { - pet->addUnitState(UNIT_STAT_CONTROLLED); + if (caster->GetTypeId() == TYPEID_PLAYER) + { + //remove any existing charm just in case + caster->Uncharm(); - // target should became visible at SetView call(if not visible before): - // otherwise client\p_caster will ignore packets from the target(SetClientControl for example) - camera.SetView(pet); - - p_caster->SetCharm(pet); - p_caster->SetClientControl(pet, 1); - ((Player*)caster)->SetMover(pet); - - pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); + //pets should be removed when possesing a target if somehow check was bypassed + ((Player*)caster)->UnsummonPetIfAny(); + } pet->StopMoving(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MoveIdle(); + + caster->TakePossessOf(target); } else - { - p_caster->SetCharm(NULL); - p_caster->SetClientControl(pet, 0); - p_caster->SetMover(NULL); - - // there is a possibility that target became invisible for client\p_caster at ResetView call: - // it must be called after movement control unapplying, not before! the reason is same as at aura applying - camera.ResetView(); - - // on delete only do caster related effects - if (m_removeMode == AURA_REMOVE_BY_DELETE) - { return; } - - pet->clearUnitState(UNIT_STAT_CONTROLLED); - - pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - - pet->AttackStop(); - - // out of range pet dismissed - if (!pet->IsWithinDistInMap(p_caster, pet->GetMap()->GetVisibilityDistance())) - { - p_caster->RemovePet(PET_SAVE_REAGENTS); - } - else - { - pet->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - } - } + caster->ResetControlState(); } void Aura::HandleAuraModPetTalentsPoints(bool /*Apply*/, bool Real) @@ -4472,6 +4373,15 @@ void Aura::HandleModCharm(bool apply, bool Real) if (apply) { + if (caster->GetTypeId() == TYPEID_PLAYER) + { + //remove any existing charm just in case + caster->Uncharm(); + + //pets should be removed when possesing a target if somehow check was bypassed + ((Player*)caster)->UnsummonPetIfAny(); + } + // is it really need after spell check checks? target->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM, GetHolder()); target->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS, GetHolder()); @@ -4874,9 +4784,11 @@ void Aura::HandleInvisibility(bool apply, bool Real) if (Real && target->GetTypeId() == TYPEID_PLAYER) { - // apply glow vision - target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); - + if (((Player*)target)->GetMover() == nullptr) // check if the player doesnt have a mover, when player is hidden during MC of creature + { + // apply glow vision + target->SetByteFlag(PLAYER_FIELD_BYTES2, 1, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); + } } // apply only if not in GM invisibility and not stealth diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index 1c2d7325c..154a6e1b1 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -5556,9 +5556,7 @@ void Spell::EffectSummonType(SpellEffectEntry const* effect) } case SUMMON_PROP_GROUP_CONTROLLABLE: { - // TODO: Fix spell 46619 - if (m_spellInfo->Id != 46619) - summonResult = DoSummonPossessed(summonPositions, summon_prop, effect, level); + summonResult = DoSummonPossessed(summonPositions, summon_prop, effect, level); break; } case SUMMON_PROP_GROUP_VEHICLE: @@ -5871,56 +5869,20 @@ bool Spell::DoSummonPossessed(CreatureSummonPositions& list, SummonPropertiesEnt { MANGOS_ASSERT(!list.empty() && prop); - uint32 creatureEntry = effect->EffectMiscValue; - CreatureInfo const* cInfo = ObjectMgr::GetCreatureTemplate(creatureEntry); - if (!cInfo) + uint32 const& creatureEntry = effect->EffectMiscValue; + + Unit* newUnit = m_caster->TakePossessOf(m_spellInfo, prop, effect, list[0].x, list[0].y, list[0].z, m_caster->GetOrientation()); + if (!newUnit) { - sLog.outErrorDb("Spell::DoSummonPossessed: creature entry %u not found for spell %u.", creatureEntry, m_spellInfo->Id); + sLog.outError("Spell::DoSummonPossessed: creature entry %d for spell %u could not be summoned.", creatureEntry, m_spellInfo->Id); return false; } - Creature* spawnCreature = m_caster->SummonCreature(creatureEntry, list[0].x, list[0].y, list[0].z, m_caster->GetOrientation(), TEMPSUMMON_CORPSE_DESPAWN, 0); - if (!spawnCreature) - { - sLog.outError("Spell::DoSummonPossessed: creature entry %u for spell %u could not be summoned.", creatureEntry, m_spellInfo->Id); - return false; - } - - list[0].creature = spawnCreature; - - // Changes to be sent - spawnCreature->SetCharmerGuid(m_caster->GetObjectGuid()); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - spawnCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - - spawnCreature->SetLevel(level); - - spawnCreature->SetWalk(m_caster->IsWalking(), true); - // TODO: Set Fly (ie glyph dependend) - - // Internal changes - spawnCreature->addUnitState(UNIT_STAT_CONTROLLED); - - // Changes to owner - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - Player* player = (Player*)m_caster; - - player->GetCamera().SetView(spawnCreature); - - player->SetCharm(spawnCreature); - player->SetClientControl(spawnCreature, 1); - player->SetMover(spawnCreature); - - if (CharmInfo* charmInfo = spawnCreature->InitCharmInfo(spawnCreature)) - charmInfo->InitPossessCreateSpells(); - player->PossessSpellInitialize(); - } + list[0].creature = static_cast(newUnit); // Notify Summoner if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(spawnCreature); + ((Creature*)m_originalCaster)->AI()->JustSummoned(list[0].creature); return true; } @@ -11368,6 +11330,11 @@ void Spell::EffectTransmitted(SpellEffectEntry const* effect) } break; } + case GAMEOBJECT_TYPE_SPELLCASTER: + { + m_caster->AddGameObject(pGameObj); + break; + } case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_CHEST: default: diff --git a/src/game/WorldHandlers/TaxiHandler.cpp b/src/game/WorldHandlers/TaxiHandler.cpp index 7a20c652f..4bb4d8d16 100644 --- a/src/game/WorldHandlers/TaxiHandler.cpp +++ b/src/game/WorldHandlers/TaxiHandler.cpp @@ -85,10 +85,6 @@ void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket& recv_data) return; } - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - // unknown taxi node case if (SendLearnNewTaxiNode(unit)) return; @@ -119,10 +115,6 @@ void WorldSession::SendTaxiMenu(Creature* unit) void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode) { - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) GetPlayer()->GetMotionMaster()->MovementExpired(false);