Core/Spell Mechanics: introduce global cooldown manager for server-side checks for GCD (ported from mangos)
This commit is contained in:
@@ -122,7 +122,7 @@ void PetAI::UpdateAI(const uint32 diff)
|
||||
return;
|
||||
|
||||
// Autocast (casted only in combat or persistent spells in any state)
|
||||
if (me->GetGlobalCooldown() == 0 && !me->HasUnitState(UNIT_STAT_CASTING))
|
||||
if (!me->HasUnitState(UNIT_STAT_CASTING))
|
||||
{
|
||||
typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
|
||||
TargetSpellList targetSpellStore;
|
||||
@@ -137,6 +137,9 @@ void PetAI::UpdateAI(const uint32 diff)
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
|
||||
continue;
|
||||
|
||||
// ignore some combinations of combat state and combat/noncombat spells
|
||||
if (!me->getVictim())
|
||||
{
|
||||
|
||||
@@ -154,7 +154,6 @@ m_formation(NULL)
|
||||
|
||||
m_CreatureSpellCooldowns.clear();
|
||||
m_CreatureCategoryCooldowns.clear();
|
||||
m_GlobalCooldown = 0;
|
||||
DisableReputationGain = false;
|
||||
//m_unit_movement_flags = MONSTER_MOVE_WALK;
|
||||
|
||||
@@ -430,11 +429,6 @@ bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data)
|
||||
|
||||
void Creature::Update(uint32 diff)
|
||||
{
|
||||
if (m_GlobalCooldown <= diff)
|
||||
m_GlobalCooldown = 0;
|
||||
else
|
||||
m_GlobalCooldown -= diff;
|
||||
|
||||
if (IsAIEnabled && TriggerJustRespawned)
|
||||
{
|
||||
TriggerJustRespawned = false;
|
||||
@@ -2185,8 +2179,6 @@ void Creature::AddCreatureSpellCooldown(uint32 spellid)
|
||||
|
||||
if (spellInfo->Category)
|
||||
_AddCreatureCategoryCooldown(spellInfo->Category, time(NULL));
|
||||
|
||||
m_GlobalCooldown = spellInfo->StartRecoveryTime;
|
||||
}
|
||||
|
||||
bool Creature::HasCategoryCooldown(uint32 spell_id) const
|
||||
@@ -2195,10 +2187,6 @@ bool Creature::HasCategoryCooldown(uint32 spell_id) const
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
// check global cooldown if spell affected by it
|
||||
if (spellInfo->StartRecoveryCategory > 0 && m_GlobalCooldown > 0)
|
||||
return true;
|
||||
|
||||
CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category);
|
||||
return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILLISECONDS)) > time(NULL));
|
||||
}
|
||||
|
||||
@@ -577,7 +577,6 @@ class Creature : public Unit, public GridObject<Creature>
|
||||
uint32 m_spells[CREATURE_MAX_SPELLS];
|
||||
CreatureSpellCooldowns m_CreatureSpellCooldowns;
|
||||
CreatureSpellCooldowns m_CreatureCategoryCooldowns;
|
||||
uint32 m_GlobalCooldown;
|
||||
|
||||
bool canStartAttack(Unit const* u, bool force) const;
|
||||
float GetAttackDistance(Unit const* pl) const;
|
||||
@@ -646,8 +645,6 @@ class Creature : public Unit, public GridObject<Creature>
|
||||
void GetHomePosition(float &x, float &y, float &z, float &ori) { m_homePosition.GetPosition(x, y, z, ori); }
|
||||
Position GetHomePosition() { return m_homePosition; }
|
||||
|
||||
uint32 GetGlobalCooldown() const { return m_GlobalCooldown; }
|
||||
|
||||
uint32 GetWaypointPath(){return m_path_id;}
|
||||
void LoadPath(uint32 pathid) { m_path_id = pathid; }
|
||||
|
||||
|
||||
@@ -1640,6 +1640,8 @@ class Player : public Unit, public GridObject<Player>
|
||||
void RemoveSpellCategoryCooldown(uint32 cat, bool update = false);
|
||||
void SendClearCooldown(uint32 spell_id, Unit* target);
|
||||
|
||||
GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; }
|
||||
|
||||
void RemoveCategoryCooldown(uint32 cat);
|
||||
void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false);
|
||||
void RemoveAllSpellCooldown();
|
||||
@@ -2545,6 +2547,8 @@ class Player : public Unit, public GridObject<Player>
|
||||
PlayerTalentMap *m_talents[MAX_TALENT_SPECS];
|
||||
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
|
||||
|
||||
GlobalCooldownMgr m_GlobalCooldownMgr;
|
||||
|
||||
uint8 m_activeSpec;
|
||||
uint8 m_specsCount;
|
||||
|
||||
|
||||
@@ -192,6 +192,26 @@ m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this)
|
||||
m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Methods of class GlobalCooldownMgr
|
||||
bool GlobalCooldownMgr::HasGlobalCooldown(SpellEntry const* spellInfo) const
|
||||
{
|
||||
GlobalCooldownList::const_iterator itr = m_GlobalCooldowns.find(spellInfo->StartRecoveryCategory);
|
||||
return itr != m_GlobalCooldowns.end() && itr->second.duration && getMSTimeDiff(itr->second.cast_time, getMSTime()) < itr->second.duration;
|
||||
}
|
||||
|
||||
void GlobalCooldownMgr::AddGlobalCooldown(SpellEntry const* spellInfo, uint32 gcd)
|
||||
{
|
||||
m_GlobalCooldowns[spellInfo->StartRecoveryCategory] = GlobalCooldown(gcd, getMSTime());
|
||||
}
|
||||
|
||||
void GlobalCooldownMgr::CancelGlobalCooldown(SpellEntry const* spellInfo)
|
||||
{
|
||||
m_GlobalCooldowns[spellInfo->StartRecoveryCategory].duration = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Methods of class Unit
|
||||
Unit::~Unit()
|
||||
{
|
||||
// set current spells as deletable
|
||||
|
||||
@@ -937,6 +937,30 @@ enum CurrentSpellTypes
|
||||
#define CURRENT_FIRST_NON_MELEE_SPELL 1
|
||||
#define CURRENT_MAX_SPELL 4
|
||||
|
||||
struct GlobalCooldown
|
||||
{
|
||||
explicit GlobalCooldown(uint32 _dur = 0, uint32 _time = 0) : duration(_dur), cast_time(_time) {}
|
||||
|
||||
uint32 duration;
|
||||
uint32 cast_time;
|
||||
};
|
||||
|
||||
typedef UNORDERED_MAP<uint32 /*category*/, GlobalCooldown> GlobalCooldownList;
|
||||
|
||||
class GlobalCooldownMgr // Shared by Player and CharmInfo
|
||||
{
|
||||
public:
|
||||
GlobalCooldownMgr() {}
|
||||
|
||||
public:
|
||||
bool HasGlobalCooldown(SpellEntry const* spellInfo) const;
|
||||
void AddGlobalCooldown(SpellEntry const* spellInfo, uint32 gcd);
|
||||
void CancelGlobalCooldown(SpellEntry const* spellInfo);
|
||||
|
||||
private:
|
||||
GlobalCooldownList m_GlobalCooldowns;
|
||||
};
|
||||
|
||||
enum ActiveStates
|
||||
{
|
||||
ACT_PASSIVE = 0x01, // 0x01 - passive
|
||||
@@ -1057,6 +1081,8 @@ struct CharmInfo
|
||||
|
||||
CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
|
||||
|
||||
GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; }
|
||||
|
||||
void SetIsCommandAttack(bool val);
|
||||
bool IsCommandAttack();
|
||||
void SetIsAtStay(bool val);
|
||||
@@ -1088,6 +1114,8 @@ struct CharmInfo
|
||||
float m_stayX;
|
||||
float m_stayY;
|
||||
float m_stayZ;
|
||||
|
||||
GlobalCooldownMgr m_GlobalCooldownMgr;
|
||||
};
|
||||
|
||||
// for clearing special attacks
|
||||
|
||||
@@ -295,7 +295,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
|
||||
}
|
||||
|
||||
if (spellInfo->StartRecoveryCategory > 0)
|
||||
if (pet->ToCreature()->GetGlobalCooldown() > 0)
|
||||
if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
|
||||
return;
|
||||
|
||||
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
@@ -753,7 +753,7 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
|
||||
}
|
||||
|
||||
if (spellInfo->StartRecoveryCategory > 0) // Check if spell is affected by GCD
|
||||
if (caster->GetTypeId() == TYPEID_UNIT && caster->ToCreature()->GetGlobalCooldown() > 0)
|
||||
if (caster->GetTypeId() == TYPEID_UNIT && caster->GetCharmInfo() && caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
|
||||
{
|
||||
caster->SendPetCastFail(spellId, SPELL_FAILED_NOT_READY);
|
||||
return;
|
||||
|
||||
@@ -3001,6 +3001,8 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const * triggere
|
||||
m_caster->SetCurrentCastedSpell(this);
|
||||
SendSpellStart();
|
||||
|
||||
TriggerGlobalCooldown();
|
||||
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
m_caster->ToPlayer()->AddGlobalCooldown(m_spellInfo,this);
|
||||
|
||||
@@ -3027,6 +3029,7 @@ void Spell::cancel()
|
||||
switch (oldState)
|
||||
{
|
||||
case SPELL_STATE_PREPARING:
|
||||
CancelGlobalCooldown();
|
||||
case SPELL_STATE_DELAYED:
|
||||
SendInterrupted(0);
|
||||
SendCastResult(SPELL_FAILED_INTERRUPTED);
|
||||
@@ -4680,6 +4683,10 @@ SpellCastResult Spell::CheckCast(bool strict)
|
||||
}
|
||||
}
|
||||
|
||||
// Check global cooldown
|
||||
if (strict && !m_IsTriggeredSpell && HasGlobalCooldown())
|
||||
return SPELL_FAILED_NOT_READY;
|
||||
|
||||
// only allow triggered spells if at an ended battleground
|
||||
if (!m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
if (Battleground * bg = m_caster->ToPlayer()->GetBattleground())
|
||||
@@ -7285,3 +7292,65 @@ void Spell::CallScriptAfterUnitTargetSelectHandlers(std::list<Unit*>& unitTarget
|
||||
(*scritr)->_FinishScriptCall();
|
||||
}
|
||||
}
|
||||
|
||||
enum eGCD {
|
||||
MIN_GCD = 1000,
|
||||
MAX_GCD = 1500
|
||||
};
|
||||
|
||||
bool Spell::HasGlobalCooldown()
|
||||
{
|
||||
// Only player or controlled units have global cooldown
|
||||
if (m_caster->GetCharmInfo())
|
||||
return m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo);
|
||||
else if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
return m_caster->ToPlayer()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Spell::TriggerGlobalCooldown()
|
||||
{
|
||||
int32 gcd = m_spellInfo->StartRecoveryTime;
|
||||
if (!gcd)
|
||||
return;
|
||||
|
||||
// Global cooldown can't leave range 1..1.5 secs
|
||||
// There are some spells (mostly not casted directly by player) that have < 1 sec and > 1.5 sec global cooldowns
|
||||
// but as tests show are not affected by any spell mods.
|
||||
if (m_spellInfo->StartRecoveryTime >= MIN_GCD && m_spellInfo->StartRecoveryTime <= MAX_GCD)
|
||||
{
|
||||
// gcd modifier auras are applied only to own spells and only players have such mods
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this);
|
||||
|
||||
// Apply haste rating
|
||||
gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED));
|
||||
if (gcd < MIN_GCD)
|
||||
gcd = MIN_GCD;
|
||||
else if (gcd > MAX_GCD)
|
||||
gcd = MAX_GCD;
|
||||
}
|
||||
|
||||
// Only players or controlled units have global cooldown
|
||||
if (m_caster->GetCharmInfo())
|
||||
m_caster->GetCharmInfo()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd);
|
||||
else if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
m_caster->ToPlayer()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd);
|
||||
}
|
||||
|
||||
void Spell::CancelGlobalCooldown()
|
||||
{
|
||||
if (!m_spellInfo->StartRecoveryTime)
|
||||
return;
|
||||
|
||||
// Cancel global cooldown when interrupting current cast
|
||||
if (m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this)
|
||||
return;
|
||||
|
||||
// Only players or controlled units have global cooldown
|
||||
if (m_caster->GetCharmInfo())
|
||||
m_caster->GetCharmInfo()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo);
|
||||
else if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
||||
m_caster->ToPlayer()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo);
|
||||
}
|
||||
|
||||
@@ -543,6 +543,9 @@ class Spell
|
||||
|
||||
void SetSpellValue(SpellValueMod mod, int32 value);
|
||||
protected:
|
||||
bool HasGlobalCooldown();
|
||||
void TriggerGlobalCooldown();
|
||||
void CancelGlobalCooldown();
|
||||
|
||||
void SendLoot(uint64 guid, LootType loottype);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user