diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 85208612520..454ae604c8a 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -1809,25 +1809,25 @@ void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time) m_CreatureSpellCooldowns[spell_id] = end_time; } -void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time) +void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t end_time) { - m_CreatureCategoryCooldowns[category] = apply_time; + m_CreatureCategoryCooldowns[category] = end_time; } -void Creature::AddCreatureSpellCooldown(uint32 spellid) +void Creature::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown) { - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); - if(!spellInfo) - return; - uint32 cooldown = GetSpellRecoveryTime(spellInfo); - if(cooldown) - _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILISECONDS); - if(spellInfo->Category) - _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL)); + // apply spellmod (in case creature is pet) + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown); + + if (!cooldown) + return; - m_GlobalCooldown = spellInfo->StartRecoveryTime; + _AddCreatureSpellCooldown(spellInfo->Id, time(NULL) + cooldown/IN_MILISECONDS); + if (spellInfo->Category) + _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL) + cooldown/IN_MILISECONDS); } bool Creature::HasCategoryCooldown(uint32 spell_id) const @@ -1841,7 +1841,7 @@ bool Creature::HasCategoryCooldown(uint32 spell_id) const return true; CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category); - return (itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILISECONDS)) > time(NULL)); + return (itr != m_CreatureCategoryCooldowns.end() && itr->second > time(NULL)); } bool Creature::HasSpellCooldown(uint32 spell_id) const diff --git a/src/game/Creature.h b/src/game/Creature.h index dfbcb771d5e..8ef6fa35e23 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -478,7 +478,7 @@ class MANGOS_DLL_SPEC Creature : public Unit void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time); void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time); - void AddCreatureSpellCooldown(uint32 spellid); + void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false); bool HasSpellCooldown(uint32 spell_id) const; bool HasCategoryCooldown(uint32 spell_id) const; @@ -610,6 +610,7 @@ class MANGOS_DLL_SPEC Creature : public Unit void GetSummonPoint(float &fX, float &fY, float &fZ, float &fOrient) const { fX = m_summonXpoint; fY = m_summonYpoint; fZ = m_summonZpoint; fOrient = m_summonOrientation; } uint32 GetGlobalCooldown() const { return m_GlobalCooldown; } + void SetGlobalCooldown(uint32 gcd){ m_GlobalCooldown = gcd; } void SetDeadByDefault (bool death_state) { m_isDeadByDefault = death_state; } diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 81aa3a0b04d..dbbc186c653 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -200,7 +200,7 @@ void PetAI::UpdateAI(const uint32 diff) } // Autocast (casted only in combat or persistent spells in any state) - if (m_creature->GetGlobalCooldown() == 0 && !m_creature->IsNonMeleeSpellCasted(false)) + if (!m_creature->IsNonMeleeSpellCasted(false)) { typedef std::vector > TargetSpellList; TargetSpellList targetSpellStore; @@ -215,6 +215,10 @@ void PetAI::UpdateAI(const uint32 diff) if (!spellInfo) continue; + // do not cast if spell is on global cooldown + if (spellInfo->StartRecoveryTime > 0 && m_creature->GetGlobalCooldown()) + continue; + // ignore some combinations of combat state and combat/noncombat spells if (!inCombat) { @@ -298,8 +302,6 @@ void PetAI::UpdateAI(const uint32 diff) m_creature->SendCreateUpdateToPlayer( (Player*)owner ); } - m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id); - spell->prepare(&targets); } diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 9ae70b7ec6c..f9054854f2c 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -170,8 +170,6 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) case ACT_ENABLED: // 0xC1 spell { Unit* unit_target = NULL; - if (((Creature*)pet)->GetGlobalCooldown() > 0) - return; if(guid2) unit_target = ObjectAccessor::GetUnit(*_player,guid2); @@ -184,6 +182,9 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) return; } + if (spellInfo->StartRecoveryTime && ((Creature*)pet)->GetGlobalCooldown() > 0) + return; + for(int i = 0; i < MAX_EFFECT_INDEX;++i) { if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) @@ -223,8 +224,6 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if(result == SPELL_CAST_OK) { - ((Creature*)pet)->AddCreatureSpellCooldown(spellid); - unit_target = spell->m_targets.getUnitTarget(); //10% chance to play special pet attack talk, else growl @@ -615,9 +614,6 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) return; } - if (pet->GetGlobalCooldown() > 0) - return; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { @@ -625,6 +621,9 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) return; } + if (spellInfo->StartRecoveryTime && pet->GetGlobalCooldown() > 0) + return; + // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; @@ -642,7 +641,6 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { - pet->AddCreatureSpellCooldown(spellid); if (pet->isPet()) { //10% chance to play special pet attack talk, else growl diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 7c35f2e2eaa..bc92c060e81 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -18446,15 +18446,18 @@ void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) m_spellCooldowns[spellid] = sc; } -void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell) +void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell, Unit* cooldownTarget) { + if (!cooldownTarget) + cooldownTarget = this; + // start cooldowns at server side, if any - AddSpellAndCategoryCooldowns(spellInfo, itemId, spell); + cooldownTarget->AddSpellAndCategoryCooldowns(spellInfo,itemId,spell); // Send activate cooldown timer (possible 0) at client side WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8)); data << uint32(spellInfo->Id); - data << uint64(GetGUID()); + data << uint64(cooldownTarget->GetGUID()); SendDirectMessage(&data); } diff --git a/src/game/Player.h b/src/game/Player.h index 613e22b0b23..3e2d40f4e1a 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1617,9 +1617,9 @@ class MANGOS_DLL_SPEC Player : public Unit time_t t = time(NULL); return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0; } - void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false ); + void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false); void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); - void SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId = 0, Spell* spell = NULL); + void SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId = 0, Spell* spell = NULL, Unit* cooldownTarget = NULL); void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id, bool update = false); void RemoveSpellCategoryCooldown(uint32 cat, bool update = false); diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 4388aa89e27..9c5bf705d54 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -2440,6 +2440,9 @@ void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) // set timer base at cast time ReSetTimer(); + // send global cooldown + SendGlobalCooldown(); + // stealth must be removed at cast starting (at show channel bar) // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) if ( !m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo) ) @@ -2474,6 +2477,7 @@ void Spell::cancel() switch (m_spellState) { case SPELL_STATE_PREPARING: + ResetGlobalCooldown(); case SPELL_STATE_DELAYED: { SendInterrupted(0); @@ -2858,24 +2862,67 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { - if(m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - Player* _player = (Player*)m_caster; - - // mana/health/etc potions, disabled by client (until combat out as declarate) - if (m_CastItem && m_CastItem->IsPotion()) + switch (m_caster->GetTypeId()) { - // need in some way provided data for Spell::finish SendCooldownEvent - _player->SetLastPotionId(m_CastItem->GetEntry()); - return; + case TYPEID_UNIT: + { + // store cooldown only for controlled creatures + if (!m_caster->isControlledByPlayer()) + return; + }break; + case TYPEID_PLAYER: + { + Player* _player = (Player*)m_caster; + + // mana/health/etc potions, disabled by client (until combat out as declarate) + if (m_CastItem && m_CastItem->IsPotion()) + { + // need in some way provided data for Spell::finish SendCooldownEvent + _player->SetLastPotionId(m_CastItem->GetEntry()); + return; + } + }break; + default: + return;; } // (1) have infinity cooldown but set at aura apply, (2) passive cooldown at triggering if(m_spellInfo->Attributes & (SPELL_ATTR_DISABLED_WHILE_ACTIVE | SPELL_ATTR_PASSIVE)) return; + m_caster->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this); +} + +void Spell::SendGlobalCooldown() +{ + if (!m_spellInfo->StartRecoveryTime) + return; + + // server side handling only for charmed creatures and pets + if (m_caster->GetTypeId() != TYPEID_UNIT) + return; + + if (!m_caster->isControlledByPlayer()) + return; + + ((Creature*)m_caster)->SetGlobalCooldown(m_spellInfo->StartRecoveryTime); +} + +void Spell::ResetGlobalCooldown() +{ + // spells without gcd can't reset it + if (!m_spellInfo->StartRecoveryTime) + return; + + // server side handling only for charmed creatures and pets + if (m_caster->GetTypeId() != TYPEID_UNIT) + return; + + if (!m_caster->isControlledByPlayer()) + return; - _player->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this); + ((Creature*)m_caster)->SetGlobalCooldown(0); + // need to send packet for controlled creatures (to the controller), this will clear gcd + ((Player*)m_caster->GetCharmerOrOwner())->SendClearCooldown(m_spellInfo->Id, m_caster); } void Spell::update(uint32 difftime) diff --git a/src/game/Spell.h b/src/game/Spell.h index 86520d6c6cc..8676d4dbd07 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -423,6 +423,8 @@ class Spell void SendSpellStart(); void SendSpellGo(); void SendSpellCooldown(); + void SendGlobalCooldown(); + void ResetGlobalCooldown(); void SendLogExecute(); void SendInterrupted(uint8 result); void SendChannelUpdate(uint32 time); diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 7be63467bf4..657512391b0 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -1218,11 +1218,20 @@ bool Aura::_RemoveAura() } // reset cooldown state for spells - if(caster && caster->GetTypeId() == TYPEID_PLAYER) + if (GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE && caster) { - if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE ) + // always send for players + if (caster->GetTypeId() == TYPEID_PLAYER) + { // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) ((Player*)caster)->SendCooldownEvent(GetSpellProto()); + } + // send to controller, if unit is player-controlled + if (caster->isControlledByPlayer()) + { + Player* controller = (Player*)(caster->GetCharmerOrOwner()); + controller->SendCooldownEvent(GetSpellProto(), 0, NULL, caster); + } } } diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 2d8c2e1a3a7..191fe1fa5aa 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -481,8 +481,7 @@ void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket) } pet->RemoveAurasDueToSpell(spellId); - - pet->AddCreatureSpellCooldown(spellId); + pet->AddSpellAndCategoryCooldowns(spellInfo, 0); } void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/) diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 63961f24e73..8cb888b36e3 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -8332,6 +8332,26 @@ bool Unit::IsNeutralToAll() const return my_faction->IsNeutralToAll(); } +bool Unit::isControlledByPlayer() const +{ + // this method will return true, if this unit is directly controlled by a player, + // that means player has a pet cast bar + Unit* owner = GetCharmerOrOwner(); + + if (!owner || owner->GetTypeId() != TYPEID_PLAYER) + return false; + + // player-charmed units always have pet action bar (hope this is correct) + if (isCharmed()) + return true; + + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet() && + ((Pet*)this)->isControlled()) + return true; + + return false; +} + bool Unit::Attack(Unit *victim, bool meleeAttack) { if(!victim || victim == this) diff --git a/src/game/Unit.h b/src/game/Unit.h index 23acdb589d0..47d5fc9f76f 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1356,6 +1356,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = NULL, Aura* triggeredByAura = NULL, uint64 originalCaster = 0); + virtual void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false) = 0; bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const; @@ -1415,6 +1416,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject return GetGUID(); } bool isCharmedOwnedByPlayerOrPlayer() const { return IS_PLAYER_GUID(GetCharmerOrOwnerOrOwnGUID()); } + bool isControlledByPlayer() const; Player* GetSpellModOwner();