diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 83ce0f9e6..c0274574f 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -492,16 +492,18 @@ void PetAI::HandleReturnMovement() { if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning()) { - // Return to previous position where stay was clicked - if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE) + if (me->GetCharmInfo()->HasStayPosition()) { - float x, y, z; - - me->GetCharmInfo()->GetStayPosition(x, y, z); - ClearCharmInfoFlags(); - me->GetCharmInfo()->SetIsReturning(true); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(me->GetGUIDLow(), x, y, z); + // Return to previous position where stay was clicked + if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE) + { + float x, y, z; + me->GetCharmInfo()->GetStayPosition(x, y, z); + ClearCharmInfoFlags(); + me->GetCharmInfo()->SetIsReturning(true); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(me->GetUInt32Value(OBJECT_FIELD_GUID), x, y, z); + } } } } diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 1c07c1ac5..6ee5f97cb 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -40,7 +40,7 @@ Pet::Pet(Player* owner, PetType type) : Guardian(NULL, owner ? owner->GetGUID() : 0, true), m_usedTalentCount(0), m_removed(false), m_owner(owner), m_happinessTimer(PET_LOSE_HAPPINES_INTERVAL), m_petRegenTimer(PET_FOCUS_REGEN_INTERVAL), m_petType(type), m_duration(0), -m_auraRaidUpdateMask(0), m_loading(false), m_declinedname(NULL), asynchLoadType(PET_LOAD_DEFAULT) +m_auraRaidUpdateMask(0), m_loading(false), m_declinedname(NULL), m_tempspell(0), m_tempspellTarget(NULL), m_tempoldTarget(NULL), m_tempspellIsPositive(false), asynchLoadType(PET_LOAD_DEFAULT) { m_unitTypeMask |= UNIT_MASK_PET; if (type == HUNTER_PET) @@ -384,6 +384,115 @@ void Pet::Update(uint32 diff) } } + if (m_tempspell != 0) + { + Unit* tempspellTarget = m_tempspellTarget; + Unit* tempoldTarget = m_tempoldTarget; + bool tempspellIsPositive = m_tempspellIsPositive; + uint32 tempspell = m_tempspell; + Unit* charmer = GetCharmerOrOwner(); + if (!charmer) + return; + + if (!GetCharmInfo()) + return; + + if (tempspellTarget && tempspellTarget->IsAlive()) + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tempspell); + if (!spellInfo) + return; + float max_range = GetSpellMaxRangeForTarget(tempspellTarget, spellInfo); + + if (IsWithinLOSInMap(tempspellTarget) && GetDistance(tempspellTarget) < max_range) + { + if (tempspellTarget && !GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo) && !HasSpellCooldown(tempspell)) + { + StopMoving(); + GetMotionMaster()->Clear(false); + GetMotionMaster()->MoveIdle(); + + GetCharmInfo()->SetIsCommandAttack(false); + GetCharmInfo()->SetIsAtStay(true); + GetCharmInfo()->SetIsCommandFollow(false); + GetCharmInfo()->SetIsFollowing(false); + GetCharmInfo()->SetIsReturning(false); + GetCharmInfo()->SaveStayPosition(true); + + CastSpell(tempspellTarget, tempspell, true); + m_tempspell = 0; + m_tempspellTarget = NULL; + + if (tempspellIsPositive) + { + if (tempoldTarget && tempoldTarget->IsAlive()) + { + GetCharmInfo()->SetIsCommandAttack(true); + GetCharmInfo()->SetIsAtStay(false); + GetCharmInfo()->SetIsFollowing(false); + GetCharmInfo()->SetIsCommandFollow(false); + GetCharmInfo()->SetIsReturning(false); + + if (ToCreature() && ToCreature()->IsAIEnabled) + ToCreature()->AI()->AttackStart(tempoldTarget); + } + else + { + GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); + GetCharmInfo()->SetIsCommandAttack(false); + GetCharmInfo()->SetIsAtStay(false); + GetCharmInfo()->SetIsReturning(true); + GetCharmInfo()->SetIsCommandFollow(true); + GetCharmInfo()->SetIsFollowing(false); + GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, GetFollowAngle()); + } + + m_tempoldTarget = NULL; + m_tempspellIsPositive = false; + } + } + } + } + else + { + m_tempspell = 0; + m_tempspellTarget = NULL; + m_tempoldTarget = NULL; + m_tempspellIsPositive = false; + + Unit* victim = charmer->GetVictim(); + if (victim && victim->IsAlive()) + { + StopMoving(); + GetMotionMaster()->Clear(false); + GetMotionMaster()->MoveIdle(); + + GetCharmInfo()->SetIsCommandAttack(true); + GetCharmInfo()->SetIsAtStay(false); + GetCharmInfo()->SetIsFollowing(false); + GetCharmInfo()->SetIsCommandFollow(false); + GetCharmInfo()->SetIsReturning(false); + + if (ToCreature() && ToCreature()->IsAIEnabled) + ToCreature()->AI()->AttackStart(victim); + } + else + { + StopMoving(); + GetMotionMaster()->Clear(false); + GetMotionMaster()->MoveIdle(); + + GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); + GetCharmInfo()->SetIsCommandAttack(false); + GetCharmInfo()->SetIsAtStay(false); + GetCharmInfo()->SetIsReturning(true); + GetCharmInfo()->SetIsCommandFollow(true); + GetCharmInfo()->SetIsFollowing(false); + GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, GetFollowAngle()); + } + } + } + if (getPetType() == HUNTER_PET) { m_happinessTimer -= diff; @@ -2103,3 +2212,44 @@ void Pet::SetDisplayId(uint32 modelId) if (player->GetGroup()) player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID); } + +void Pet::CastWhenWillAvailable(uint32 spellid, Unit* spellTarget, Unit* oldTarget, bool spellIsPositive) +{ + if (!spellid) + return; + + if (!spellTarget) + return; + + m_tempspellTarget = spellTarget; + m_tempspell = spellid; + m_tempspellIsPositive = spellIsPositive; + + if (oldTarget) + m_tempoldTarget = oldTarget; +} + +void Pet::ClearCastWhenWillAvailable() +{ + m_tempspellIsPositive = false; + m_tempspell = 0; + m_tempspellTarget = NULL; + m_tempoldTarget = NULL; +} + +void Pet::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */) +{ + m_CreatureSpellCooldowns.erase(spell_id); + + if (update) + { + + if (Player* playerOwner = GetCharmerOrOwnerPlayerOrPlayerItself()) + { + WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8); + data << uint32(spell_id); + data << uint64(GetGUID()); + playerOwner->SendDirectMessage(&data); + } + } +} \ No newline at end of file diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index c9db736fc..d7e8371b9 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -120,6 +120,10 @@ class Pet : public Guardian void LearnPetPassives(); void CastPetAuras(bool current); + void CastWhenWillAvailable(uint32 spellid, Unit* spellTarget, Unit* oldTarget, bool spellIsPositive = false); + void ClearCastWhenWillAvailable(); + void RemoveSpellCooldown(uint32 spell_id, bool update /* = false */); + void _SaveSpellCooldowns(SQLTransaction& trans, bool logout); void _SaveAuras(SQLTransaction& trans, bool logout); void _SaveSpells(SQLTransaction& trans); @@ -175,6 +179,12 @@ class Pet : public Guardian int32 m_petRegenTimer; // xinef: used for focus regeneration DeclinedName *m_declinedname; + + Unit* m_tempspellTarget; + Unit* m_tempoldTarget; + bool m_tempspellIsPositive; + uint32 m_tempspell; + uint8 asynchLoadType; private: diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 01f150d38..c98972659 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -18623,6 +18623,18 @@ void CharmInfo::GetStayPosition(float &x, float &y, float &z) z = _stayZ; } +void CharmInfo::RemoveStayPosition() +{ + _stayX = 0.0f; + _stayY = 0.0f; + _stayZ = 0.0f; +} + +bool CharmInfo::HasStayPosition() +{ + return _stayX && _stayY && _stayZ; +} + void CharmInfo::SetIsAtStay(bool val) { _isAtStay = val; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 0447ac6fe..c7cd3e746 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1224,6 +1224,8 @@ struct CharmInfo bool IsReturning(); void SaveStayPosition(bool atCurrentPos); void GetStayPosition(float &x, float &y, float &z); + void RemoveStayPosition(); + bool HasStayPosition(); void SetForcedSpell(uint32 id) { _forcedSpellId = id; } int32 GetForcedSpell() { return _forcedSpellId; } diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index cb9297c1b..b782b7b45 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -507,6 +507,9 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid charmInfo->SetIsReturning(false); charmInfo->SetIsAtStay(!controlledMotion); charmInfo->SaveStayPosition(controlledMotion); + if (pet->ToPet()) + pet->ToPet()->ClearCastWhenWillAvailable(); + charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(0); @@ -518,6 +521,8 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid pet->InterruptNonMeleeSpells(false); pet->ClearInPetCombat(); pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle()); + if (pet->ToPet()) + pet->ToPet()->ClearCastWhenWillAvailable(); charmInfo->SetCommandState(COMMAND_FOLLOW); charmInfo->SetIsCommandAttack(false); @@ -525,7 +530,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid charmInfo->SetIsReturning(true); charmInfo->SetIsCommandFollow(true); charmInfo->SetIsFollowing(false); - + charmInfo->RemoveStayPosition(); charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(0); break; @@ -641,6 +646,8 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid { case REACT_PASSIVE: //passive pet->AttackStop(); + if (pet->ToPet()) + pet->ToPet()->ClearCastWhenWillAvailable(); pet->ClearInPetCombat(); case REACT_DEFENSIVE: //recovery @@ -658,9 +665,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid { Unit* unit_target = NULL; - if (guid2) - unit_target = ObjectAccessor::GetUnit(*_player, guid2); - // do not cast unknown spells SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) @@ -669,6 +673,11 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid return; } + if (guid2) + unit_target = ObjectAccessor::GetUnit(*_player, guid2); + else if (!spellInfo->IsPositive()) + return; + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY) @@ -743,6 +752,131 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(0); } + else if (pet->ToPet() && (result == SPELL_FAILED_LINE_OF_SIGHT || result == SPELL_FAILED_OUT_OF_RANGE)) + { + unit_target = spell->m_targets.GetUnitTarget(); + bool haspositiveeffect = false; + + if (!unit_target) + return; + + // search positive effects for spell + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (spellInfo->_IsPositiveEffect(i, true)) + { + haspositiveeffect = true; + break; + } + } + + if (pet->isPossessed() || pet->IsVehicle()) + Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); + else if (GetPlayer()->IsFriendlyTo(unit_target) && !haspositiveeffect) + spell->SendPetCastResult(SPELL_FAILED_TARGET_FRIENDLY); + else + spell->SendPetCastResult(SPELL_FAILED_DONT_REPORT); + + if (!pet->HasSpellCooldown(spellid)) + if(pet->ToPet()) + pet->ToPet()->RemoveSpellCooldown(spellid, true); + + spell->finish(false); + delete spell; + + if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY)) + return; + + bool tempspellIsPositive = false; + + if (!GetPlayer()->IsFriendlyTo(unit_target)) + { + // only place where pet can be player + Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2); + if (!TargetUnit) + return; + + if (Unit* owner = pet->GetOwner()) + if (!owner->IsValidAttackTarget(TargetUnit)) + return; + + pet->ClearUnitState(UNIT_STATE_FOLLOW); + // This is true if pet has no target or has target but targets differs. + if (pet->GetVictim() != TargetUnit || (pet->GetVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack())) + { + if (pet->GetVictim()) + pet->AttackStop(); + + if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature() && pet->ToCreature()->IsAIEnabled) + { + charmInfo->SetIsCommandAttack(true); + charmInfo->SetIsAtStay(false); + charmInfo->SetIsFollowing(false); + charmInfo->SetIsCommandFollow(false); + charmInfo->SetIsReturning(false); + + pet->ToCreature()->AI()->AttackStart(TargetUnit); + + if (pet->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) + pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); + else + pet->SendPetAIReaction(guid1); + } + else // charmed player + { + if (pet->GetVictim() && pet->GetVictim() != TargetUnit) + pet->AttackStop(); + + charmInfo->SetIsCommandAttack(true); + charmInfo->SetIsAtStay(false); + charmInfo->SetIsFollowing(false); + charmInfo->SetIsCommandFollow(false); + charmInfo->SetIsReturning(false); + + pet->Attack(TargetUnit, true); + pet->SendPetAIReaction(guid1); + } + + pet->ToPet()->CastWhenWillAvailable(spellid, unit_target, NULL, tempspellIsPositive); + } + } + else if (haspositiveeffect) + { + bool tempspellIsPositive = true; + pet->ClearUnitState(UNIT_STATE_FOLLOW); + // This is true if pet has no target or has target but targets differs. + Unit* victim = pet->GetVictim(); + if (victim) + { + pet->AttackStop(); + } + else + victim = NULL; + + if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature() && pet->ToCreature()->IsAIEnabled) + { + pet->StopMoving(); + pet->GetMotionMaster()->Clear(); + + charmInfo->SetIsCommandAttack(false); + charmInfo->SetIsAtStay(false); + charmInfo->SetIsFollowing(false); + charmInfo->SetIsCommandFollow(false); + charmInfo->SetIsReturning(false); + + pet->GetMotionMaster()->MoveChase(unit_target); + + if (pet->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && urand(0, 100) < 10) + pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); + else + { + pet->SendPetAIReaction(guid1); + } + + pet->ToPet()->CastWhenWillAvailable(spellid, unit_target, victim, tempspellIsPositive); + } + } + } else { // dont spam alerts