Port relevant cooldown category handling and Duel Cooldown reset script

Core/Spells: Cooldown updates
Core/Player: implement ResetCoolDownAfterDuel configurable feature
Core/Player rewritten duel CD reset system
Core/Spells: fixed some rare stealth/prowl visual bugs after duel cd reset
Core/Spells: Improved spell category cooldown handling

* Category cooldown is stored with the spell that started the cooldown (and only resetting cooldown on that spell will clear cooldowns on entire category - this fully mirrors client behavior)
* This significantly reduces the amount of data saved to database for cooldowns
* Spell casts from items that have a different category specified than on spell will now check for cooldown during the cast

Core/Misc: Warning fix
Scripts/DuelReset:

- impemented health and mana reset
- do not reset anything when duel is interrupted

Scripts/DuelReset:

- fixed druid mana restoration
- fixed bug when a player accepts duel with a spel on onHold true (like when stealth of rogue/druid is active)

Core/Spells: Fixed Raise Dead cooldown
Core/Spells: Fixed typo in ecbbcc8972
Core/Spell: fixed possible exploit in Duel Reset script

- thanks @Runico for reporting it

Scripts/DuelReset:

- do not reset when duel is fled
- minor improvement to SpellHistory::RestoreCooldonStateAfter duel method
This commit is contained in:
ariel- committed 2016-01-11 14:51:30 -03:00
1 parent e25514f0f8
commit 446cb1e5c5
19 files changed
+421 -172

No files matched your search

+4
View File
@@ -1203,6 +1203,8 @@ CREATE TABLE `character_spell_cooldown` (
`spell` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'Spell Identifier',
`item` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Item Identifier',
`time` int(10) unsigned NOT NULL DEFAULT '0',
`categoryId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Spell category Id',
`categoryEnd` int(10) unsigned NOT NULL DEFAULT '0';
PRIMARY KEY (`guid`,`spell`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -2533,6 +2535,8 @@ CREATE TABLE `pet_spell_cooldown` (
`guid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier, Low part',
`spell` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'Spell Identifier',
`time` int(10) unsigned NOT NULL DEFAULT '0',
`categoryId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Spell category Id',
`categoryEnd` int(10) unsigned NOT NULL DEFAULT '0';
PRIMARY KEY (`guid`,`spell`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -0,0 +1,7 @@
ALTER TABLE `character_spell_cooldown`
ADD `categoryId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Spell category Id' AFTER `time`,
ADD `categoryEnd` int(10) unsigned NOT NULL DEFAULT '0' AFTER `categoryId`;
ALTER TABLE `pet_spell_cooldown`
ADD `categoryId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Spell category Id' AFTER `time`,
ADD `categoryEnd` int(10) unsigned NOT NULL DEFAULT '0' AFTER `categoryId`;
@@ -105,7 +105,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time FROM character_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time, categoryId, categoryEnd FROM character_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_GUILD_MEMBER, "SELECT guildid, rank FROM guild_member WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED, "SELECT g.guildid, g.name, gr.rname, gr.rid, gm.pnote, gm.offnote "
@@ -517,7 +517,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET knownTitles = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_RES_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET chosenTitle = 0 WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_SPELL_COOLDOWNS, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_SPELL_COOLDOWN, "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_SPELL_COOLDOWN, "INSERT INTO character_spell_cooldown (guid, spell, item, time, categoryId, categoryEnd) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER, "DELETE FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_ACTION, "DELETE FROM character_action WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_AURA, "DELETE FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
@@ -612,12 +612,12 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time, categoryId, categoryEnd FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time) VALUES (?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time, categoryId, categoryEnd) VALUES (?, ?, ?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? and spell = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
-10
View File
@@ -193,7 +193,6 @@ DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);
DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt);
DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt);
DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt);
SpellCategoryStore sSpellsByCategoryStore;
PetFamilySpellsStore sPetFamilySpellsStore;
@@ -546,15 +545,6 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex);//
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoriesStore, dbcPath,"SpellCategories.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoryStore, dbcPath, "SpellCategory.dbc");
for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
{
SpellEntry const* spell = sSpellStore.LookupEntry(i);
if (!spell)
continue;
if (SpellCategoriesEntry const* category = sSpellCategoriesStore.LookupEntry(spell->SpellCategoriesId))
sSpellsByCategoryStore[category->Category].insert(i);
}
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellReagentsStore, dbcPath,"SpellReagents.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellScalingStore, dbcPath,"SpellScaling.dbc");//15595
-1
View File
@@ -204,7 +204,6 @@ extern DBCStorage <SpellDurationEntry> sSpellDurationStore;
extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore;
extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore;
extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore;
extern SpellCategoryStore sSpellsByCategoryStore;
extern PetFamilySpellsStore sPetFamilySpellsStore;
extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore;
extern DBCStorage <SpellRangeEntry> sSpellRangeStore;
@@ -1917,8 +1917,6 @@ struct SpellCategoriesEntry
uint32 StartRecoveryCategory; // 145 m_startRecoveryCategory
};
typedef std::set<uint32> SpellCategorySet;
typedef std::map<uint32, SpellCategorySet > SpellCategoryStore;
typedef std::set<uint32> PetFamilySpellsSet;
typedef std::map<uint32, PetFamilySpellsSet > PetFamilySpellsStore;
@@ -486,14 +486,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; }
void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time);
void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time);
void AddCreatureSpellCooldown(uint32 spellid);
bool HasSpellCooldown(uint32 spell_id) const;
bool HasCategoryCooldown(uint32 spell_id) const;
uint32 GetCreatureSpellCooldownDelay(uint32 spellId) const;
// virtual void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override;
bool HasSpell(uint32 spellID) const override;
bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr);
@@ -567,8 +559,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
SpellInfo const* reachWithSpellCure(Unit* victim);
uint32 m_spells[CREATURE_MAX_SPELLS];
//CreatureSpellCooldowns m_CreatureSpellCooldowns;
//CreatureSpellCooldowns m_CreatureCategoryCooldowns;
bool CanStartAttack(Unit const* u, bool force) const;
float GetAttackDistance(Unit const* player) const;
+1 -1
View File
@@ -23258,7 +23258,7 @@ void Player::ApplyEquipCooldown(Item* pItem)
continue;
// Don't replace longer cooldowns by equip cooldown if we have any.
if (GetSpellHistory()->GetRemainingCooldown(spellData.SpellId) > 30 * IN_MILLISECONDS)
if (GetSpellHistory()->GetRemainingCooldown(sSpellMgr->EnsureSpellInfo(spellData.SpellId)) > 30 * IN_MILLISECONDS)
continue;
GetSpellHistory()->AddCooldown(spellData.SpellId, pItem->GetEntry(), std::chrono::seconds(30));
+11
View File
@@ -27,6 +27,7 @@
#include "PetDefines.h"
#include "QuestDef.h"
#include "SpellMgr.h"
#include "SpellHistory.h"
#include "Unit.h"
#include "Opcodes.h"
#include "WorldSession.h"
@@ -2147,6 +2148,12 @@ class Player : public Unit, public GridObject<Player>
bool RewardHonor(Unit* victim, uint32 groupsize, int32 honor = -1, bool pvptoken = false);
uint32 GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) const;
// duel health and mana reset methods
void SaveHealthBeforeDuel() { healthBeforeDuel = GetHealth(); }
void SaveManaBeforeDuel() { manaBeforeDuel = GetPower(POWER_MANA); }
void RestoreHealthAfterDuel() { SetHealth(healthBeforeDuel); }
void RestoreManaAfterDuel() { SetPower(POWER_MANA, manaBeforeDuel); }
//End of PvP System
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
@@ -2882,6 +2889,10 @@ class Player : public Unit, public GridObject<Player>
uint32 _activeCheats;
uint32 _maxPersonalArenaRate;
// variables to save health and mana before duel and restore them after duel
uint32 healthBeforeDuel;
uint32 manaBeforeDuel;
WorldLocation _corpseLocation;
};
@@ -93,6 +93,7 @@ void AddSC_npcs_special();
void AddSC_npc_taxi();
void AddSC_achievement_scripts();
void AddSC_action_ip_logger();
void AddSC_duel_reset();
//eastern kingdoms
void AddSC_alterac_valley(); //Alterac Valley
@@ -811,6 +812,7 @@ void AddWorldScripts()
// To avoid duplicate code, we check once /*ONLY*/ if logging is permitted or not.
if (sWorld->getBoolConfig(CONFIG_IP_BASED_ACTION_LOGGING))
AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp
AddSC_duel_reset();
#endif
}
+1 -1
View File
@@ -1404,7 +1404,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
{
// This additional check is needed to add a minimal delay before cooldown in in effect
// to allow all bubbles broken by a single damage source proc mana return
if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetId()) <= 11)
if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetSpellInfo()) <= 11)
break;
}
else // and add if needed
+1 -1
View File
@@ -4735,7 +4735,7 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_NOT_READY;
}
if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo))
if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry))
{
if (m_triggeredByAuraSpell)
return SPELL_FAILED_DONT_REPORT;
+215 -132
View File
@@ -41,8 +41,11 @@ struct SpellHistory::PersistenceHelper<Player>
if (!sSpellMgr->GetSpellInfo(*spellId))
return false;
cooldownEntry->SpellId = *spellId;
cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[2].GetUInt32()));
cooldownEntry->ItemId = fields[1].GetUInt32();
cooldownEntry->CategoryId = fields[3].GetUInt32();
cooldownEntry->CategoryEnd = Clock::from_time_t(time_t(fields[4].GetUInt32()));
return true;
}
@@ -51,6 +54,8 @@ struct SpellHistory::PersistenceHelper<Player>
stmt->setUInt32(index++, cooldown.first);
stmt->setUInt32(index++, cooldown.second.ItemId);
stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd)));
stmt->setUInt32(index++, cooldown.second.CategoryId);
stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CategoryEnd)));
}
};
@@ -68,8 +73,11 @@ struct SpellHistory::PersistenceHelper<Pet>
if (!sSpellMgr->GetSpellInfo(*spellId))
return false;
cooldownEntry->SpellId = *spellId;
cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[1].GetUInt32()));
cooldownEntry->ItemId = 0;
cooldownEntry->CategoryId = fields[2].GetUInt32();
cooldownEntry->CategoryEnd = Clock::from_time_t(time_t(fields[3].GetUInt32()));
return true;
}
@@ -77,6 +85,8 @@ struct SpellHistory::PersistenceHelper<Pet>
{
stmt->setUInt32(index++, cooldown.first);
stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd)));
stmt->setUInt32(index++, cooldown.second.CategoryId);
stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CategoryEnd)));
}
};
@@ -92,7 +102,11 @@ void SpellHistory::LoadFromDB(PreparedQueryResult cooldownsResult)
uint32 spellId;
CooldownEntry cooldown;
if (StatementInfo::ReadCooldown(cooldownsResult->Fetch(), &spellId, &cooldown))
{
_spellCooldowns[spellId] = cooldown;
if (cooldown.CategoryId)
_categoryCooldowns[cooldown.CategoryId] = &_spellCooldowns[spellId];
}
} while (cooldownsResult->NextRow());
}
@@ -125,40 +139,56 @@ void SpellHistory::Update()
{
SQLTransaction t;
Clock::time_point now = Clock::now();
for (auto itr = _categoryCooldowns.begin(); itr != _categoryCooldowns.end();)
{
if (itr->second->CategoryEnd < now)
itr = _categoryCooldowns.erase(itr);
else
++itr;
}
for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
{
if (itr->second.CooldownEnd < now)
itr = _spellCooldowns.erase(itr);
itr = EraseCooldown(itr);
else
++itr;
}
}
void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/)
{
HandleCooldowns(spellInfo, item ? item->GetEntry() : 0, spell);
}
void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell /*= nullptr*/)
{
if (Player* player = _owner->ToPlayer())
{
// potions start cooldown until exiting combat
if (item && (item->IsPotion() || spellInfo->IsCooldownStartedOnEvent()))
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemID))
{
player->SetLastPotionId(item->GetEntry());
return;
if (itemTemplate->IsPotion() || spellInfo->IsCooldownStartedOnEvent())
{
player->SetLastPotionId(itemID);
return;
}
}
}
if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive() || (spell && spell->IsIgnoringCooldowns()))
return;
StartCooldown(spellInfo, item ? item->GetEntry() : 0, spell);
StartCooldown(spellInfo, itemID, spell);
}
bool SpellHistory::IsReady(SpellInfo const* spellInfo) const
bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const
{
if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE)
if (IsSchoolLocked(spellInfo->GetSchoolMask()))
return false;
if (HasCooldown(spellInfo->Id))
if (HasCooldown(spellInfo->Id, itemId))
return false;
return true;
@@ -168,42 +198,33 @@ template<>
void SpellHistory::WritePacket<Pet>(WorldPacket& packet) const
{
Clock::time_point now = Clock::now();
uint8 cooldownsCount = _spellCooldowns.size();
packet << uint8(cooldownsCount);
for (auto const& spellCooldown : _spellCooldowns)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellCooldown.first);
if (!spellInfo)
{
packet << uint32(0);
packet << uint16(0);
packet << uint32(0);
packet << uint32(0);
continue;
}
packet << uint32(spellCooldown.first); // spell ID
packet << uint16(spellInfo->GetCategory()); // spell category
packet << uint32(spellCooldown.first); // spell ID
packet << uint16(spellCooldown.second.CategoryId); // spell category
if (!spellCooldown.second.OnHold)
{
uint32 cooldownDuration = spellCooldown.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now).count() : 0;
if (cooldownDuration <= 0)
std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now);
if (cooldownDuration.count() <= 0)
{
packet << uint32(0);
packet << uint32(0);
continue;
}
if (spellInfo->GetCategory())
std::chrono::milliseconds categoryDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CategoryEnd - now);
if (categoryDuration.count() > 0)
{
packet << uint32(0);
packet << uint32(cooldownDuration);
packet << uint32(categoryDuration.count());
}
else
{
packet << uint32(cooldownDuration);
packet << uint32(cooldownDuration.count());
packet << uint32(0);
}
}
@@ -216,23 +237,13 @@ void SpellHistory::WritePacket<Player>(WorldPacket& packet) const
Clock::time_point now = Clock::now();
Clock::time_point infTime = now + InfinityCooldownDelayCheck;
uint16 cooldownsCount = _spellCooldowns.size();
size_t dataPos = packet.wpos();
packet << uint16(cooldownsCount);
packet << uint16(_spellCooldowns.size());
for (auto const& spellCooldown : _spellCooldowns)
{
SpellInfo const* sEntry = sSpellMgr->GetSpellInfo(spellCooldown.first);
if (!sEntry)
{
--cooldownsCount;
continue;
}
packet << uint32(spellCooldown.first);
packet << uint16(spellCooldown.second.ItemId); // cast item id
packet << uint16(sEntry->GetCategory()); // spell category
packet << uint16(spellCooldown.second.CategoryId); // spell category
// send infinity cooldown in special format
if (spellCooldown.second.CooldownEnd >= infTime)
@@ -242,21 +253,26 @@ void SpellHistory::WritePacket<Player>(WorldPacket& packet) const
continue;
}
uint32 cooldownDuration = spellCooldown.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now).count() : 0;
std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now);
if (cooldownDuration.count() <= 0)
{
packet << uint32(0);
packet << uint32(0);
continue;
}
if (sEntry->GetCategory()) // may be wrong, but anyway better than nothing...
std::chrono::milliseconds categoryDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CategoryEnd - now);
if (categoryDuration.count() >= 0)
{
packet << uint32(0); // cooldown
packet << uint32(cooldownDuration); // category cooldown
packet << uint32(categoryDuration.count()); // category cooldown
}
else
{
packet << uint32(cooldownDuration); // cooldown
packet << uint32(cooldownDuration.count()); // cooldown
packet << uint32(0); // category cooldown
}
}
packet.put<uint16>(dataPos, cooldownsCount);
}
void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/)
@@ -266,32 +282,7 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
int32 cooldown = -1;
int32 categoryCooldown = -1;
// some special item spells without correct cooldown in SpellInfo
// cooldown information stored in item prototype
if (itemId)
{
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
{
for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
{
if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
{
categoryId = proto->Spells[idx].SpellCategory;
cooldown = proto->Spells[idx].SpellCooldown;
categoryCooldown = proto->Spells[idx].SpellCategoryCooldown;
break;
}
}
}
}
// if no cooldown found above then base at DBC data
if (cooldown < 0 && categoryCooldown < 0)
{
categoryId = spellInfo->GetCategory();
cooldown = spellInfo->RecoveryTime;
categoryCooldown = spellInfo->CategoryRecoveryTime;
}
GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown);
Clock::time_point curTime = Clock::now();
Clock::time_point catrecTime;
@@ -351,7 +342,7 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
// self spell cooldown
if (recTime != curTime)
{
AddCooldown(spellInfo->Id, itemId, recTime, onHold);
AddCooldown(spellInfo->Id, itemId, recTime, categoryId, catrecTime, onHold);
if (needsCooldownPacket)
{
@@ -363,73 +354,51 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
}
}
}
// category spells
if (categoryId && catrecTime != curTime)
{
SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(categoryId);
if (i_scstore != sSpellsByCategoryStore.end())
{
for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
{
if (*i_scset == spellInfo->Id) // skip main spell, already handled above
continue;
AddCooldown(*i_scset, itemId, catrecTime, onHold);
}
}
}
}
void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= nullptr*/, bool startCooldown /*= true*/)
{
// start cooldowns at server side, if any
if (startCooldown)
StartCooldown(spellInfo, itemId, spell);
// Send activate cooldown timer (possible 0) at client side
if (Player* player = GetPlayerOwner())
{
// Send activate cooldown timer (possible 0) at client side
uint32 category = spellInfo->GetCategory();
GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
auto categoryItr = _categoryCooldowns.find(category);
if (categoryItr != _categoryCooldowns.end() && categoryItr->second->SpellId != spellInfo->Id)
{
WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
data << uint32(categoryItr->second->SpellId);
data << uint64(_owner->GetGUID());
player->SendDirectMessage(&data);
if (startCooldown)
StartCooldown(sSpellMgr->EnsureSpellInfo(categoryItr->second->SpellId), itemId, spell);
}
WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
data << uint32(spellInfo->Id);
data << uint64(_owner->GetGUID());
player->SendDirectMessage(&data);
uint32 category = spellInfo->GetCategory();
if (category && spellInfo->CategoryRecoveryTime)
{
SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(category);
if (ct != sSpellsByCategoryStore.end())
{
for (auto const& cooldownPair : _spellCooldowns)
{
uint32 categorySpell = cooldownPair.first;
if (!ct->second.count(categorySpell))
continue;
if (categorySpell == spellInfo->Id) // skip main spell, already handled above
continue;
SpellInfo const* spellInfo2 = sSpellMgr->EnsureSpellInfo(categorySpell);
if (!spellInfo2->IsCooldownStartedOnEvent())
continue;
data.Initialize(SMSG_COOLDOWN_EVENT, 4 + 8);
data << uint32(categorySpell);
data << uint64(_owner->GetGUID());
player->SendDirectMessage(&data);
}
}
}
}
// start cooldowns at server side, if any
if (startCooldown)
StartCooldown(spellInfo, itemId, spell);
}
void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold /*= false*/)
void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold /*= false*/)
{
CooldownEntry& cooldownEntry = _spellCooldowns[spellId];
cooldownEntry.SpellId = spellId;
cooldownEntry.CooldownEnd = cooldownEnd;
cooldownEntry.ItemId = itemId;
cooldownEntry.CategoryId = categoryId;
cooldownEntry.CategoryEnd = categoryEnd;
cooldownEntry.OnHold = onHold;
if (categoryId)
_categoryCooldowns[categoryId] = &cooldownEntry;
}
void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs)
@@ -443,7 +412,7 @@ void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs)
if (itr->second.CooldownEnd + offset > now)
itr->second.CooldownEnd += offset;
else
_spellCooldowns.erase(itr);
EraseCooldown(itr);
if (Player* playerOwner = GetPlayerOwner())
{
@@ -477,7 +446,7 @@ void SpellHistory::ResetCooldown(CooldownStorageType::iterator& itr, bool update
}
}
itr = _spellCooldowns.erase(itr);
itr = EraseCooldown(itr);
}
void SpellHistory::ResetAllCooldowns()
@@ -492,31 +461,55 @@ void SpellHistory::ResetAllCooldowns()
SendClearCooldowns(cooldowns);
}
_categoryCooldowns.clear();
_spellCooldowns.clear();
}
bool SpellHistory::HasCooldown(uint32 spellId) const
bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const
{
return _spellCooldowns.count(spellId) != 0;
if (_spellCooldowns.count(spellInfo->Id) != 0)
return true;
uint32 category = 0;
GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
if (!category)
return false;
return _categoryCooldowns.count(category) != 0;
}
uint32 SpellHistory::GetRemainingCooldown(uint32 spellId) const
bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/) const
{
auto itr = _spellCooldowns.find(spellId);
if (itr == _spellCooldowns.end())
return 0;
return HasCooldown(sSpellMgr->EnsureSpellInfo(spellId), itemId);
}
uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const
{
Clock::time_point end;
auto itr = _spellCooldowns.find(spellInfo->Id);
if (itr != _spellCooldowns.end())
end = itr->second.CooldownEnd;
else
{
auto catItr = _categoryCooldowns.find(spellInfo->GetCategory());
if (catItr == _categoryCooldowns.end())
return 0;
end = catItr->second->CategoryEnd;
}
Clock::time_point now = Clock::now();
if (itr->second.CooldownEnd < now)
if (end < now)
return 0;
Clock::duration remaining = itr->second.CooldownEnd - now;
Clock::duration remaining = end - now;
return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count());
}
void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime)
{
Clock::time_point lockoutEnd = Clock::now() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime));
Clock::time_point now = Clock::now();
Clock::time_point lockoutEnd = now + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime));
for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
if (SpellSchoolMask(1 << i) & schoolMask)
_schoolLockouts[i] = lockoutEnd;
@@ -553,10 +546,10 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim
if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
continue;
if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellId) < lockoutTime)
if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime)
{
cooldowns[spellId] = lockoutTime;
AddCooldown(spellId, 0, lockoutEnd);
AddCooldown(spellId, 0, lockoutEnd, 0, now);
}
}
@@ -657,6 +650,96 @@ void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCoo
}
}
void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown)
{
ASSERT(cooldown || categoryId || categoryCooldown);
int32 tmpCooldown = -1;
uint32 tmpCategoryId = 0;
int32 tmpCategoryCooldown = -1;
// some special item spells without correct cooldown in SpellInfo
// cooldown information stored in item prototype
if (itemId)
{
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
{
for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
{
if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
{
tmpCooldown = proto->Spells[idx].SpellCooldown;
tmpCategoryId = proto->Spells[idx].SpellCategory;
tmpCategoryCooldown = proto->Spells[idx].SpellCategoryCooldown;
break;
}
}
}
}
// if no cooldown found above then base at DBC data
if (tmpCooldown < 0 && tmpCategoryCooldown < 0)
{
tmpCooldown = spellInfo->RecoveryTime;
tmpCategoryId = spellInfo->GetCategory();
tmpCategoryCooldown = spellInfo->CategoryRecoveryTime;
}
if (cooldown)
*cooldown = tmpCooldown;
if (categoryId)
*categoryId = tmpCategoryId;
if (categoryCooldown)
*categoryCooldown = tmpCategoryCooldown;
}
void SpellHistory::SaveCooldownStateBeforeDuel()
{
_spellCooldownsBeforeDuel = _spellCooldowns;
}
void SpellHistory::RestoreCooldownStateAfterDuel()
{
// category cooldows are not preserved.
if (Player* player = _owner->ToPlayer())
{
// add all profession CDs created while in duel (if any)
for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
{
SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS ||
spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS)
_spellCooldownsBeforeDuel[itr->first] = _spellCooldowns[itr->first];
}
// check for spell with onHold active before and during the duel
for (auto itr = _spellCooldownsBeforeDuel.begin(); itr != _spellCooldownsBeforeDuel.end(); ++itr)
{
if (!itr->second.OnHold && !_spellCooldowns[itr->first].OnHold)
_spellCooldowns[itr->first] = _spellCooldownsBeforeDuel[itr->first];
}
// update the client: restore old cooldowns
PacketCooldowns cooldowns;
for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
{
Clock::time_point now = Clock::now();
uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.CooldownEnd - now).count() : 0;
// cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || itr->second.OnHold)
continue;
cooldowns[itr->first] = cooldownDuration;
}
WorldPacket data;
BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS, cooldowns);
player->SendDirectMessage(&data);
}
}
template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult);
template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult);
template void SpellHistory::SaveToDB<Player>(SQLTransaction& trans);
+25 -10
View File
@@ -38,15 +38,16 @@ public:
struct CooldownEntry
{
CooldownEntry() : ItemId(0), OnHold(false) { }
CooldownEntry(Clock::time_point endTime, uint32 itemId) : CooldownEnd(endTime), ItemId(itemId), OnHold(false) { }
uint32 SpellId = 0;
Clock::time_point CooldownEnd;
uint32 ItemId;
bool OnHold;
uint32 ItemId = 0;
uint32 CategoryId = 0;
Clock::time_point CategoryEnd;
bool OnHold = false;
};
typedef std::unordered_map<uint32 /*spellId*/, CooldownEntry> CooldownStorageType;
typedef std::unordered_map<uint32 /*categoryId*/, CooldownEntry*> CategoryCooldownStorageType;
typedef std::unordered_map<uint32 /*categoryId*/, Clock::time_point> GlobalCooldownStorageType;
explicit SpellHistory(Unit* owner) : _owner(owner), _schoolLockouts() { }
@@ -60,7 +61,8 @@ public:
void Update();
void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr);
bool IsReady(SpellInfo const* spellInfo) const;
void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell = nullptr);
bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0) const;
template<class OwnerType>
void WritePacket(WorldPacket& packet) const;
@@ -74,10 +76,11 @@ public:
template<class Type, class Period>
void AddCooldown(uint32 spellId, uint32 itemId, std::chrono::duration<Type, Period> cooldownDuration)
{
AddCooldown(spellId, itemId, Clock::now() + std::chrono::duration_cast<Clock::duration>(cooldownDuration));
Clock::time_point now = Clock::now();
AddCooldown(spellId, itemId, now + std::chrono::duration_cast<Clock::duration>(cooldownDuration), 0, now);
}
void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold = false);
void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold = false);
void ModifyCooldown(uint32 spellId, int32 cooldownModMs);
void ResetCooldown(uint32 spellId, bool update = false);
void ResetCooldown(CooldownStorageType::iterator& itr, bool update = false);
@@ -102,8 +105,9 @@ public:
}
void ResetAllCooldowns();
bool HasCooldown(uint32 spellId) const;
uint32 GetRemainingCooldown(uint32 spellId) const;
bool HasCooldown(SpellInfo const* spellInfo, uint32 itemId = 0) const;
bool HasCooldown(uint32 spellId, uint32 itemId = 0) const;
uint32 GetRemainingCooldown(SpellInfo const* spellInfo) const;
// School lockouts
void LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime);
@@ -117,16 +121,27 @@ public:
void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown) const;
CooldownStorageType::size_type GetCooldownsSizeForPacket() const { return _spellCooldowns.size(); }
void SaveCooldownStateBeforeDuel();
void RestoreCooldownStateAfterDuel();
private:
Player* GetPlayerOwner() const;
void SendClearCooldowns(std::vector<int32> const& cooldowns) const;
CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr)
{
_categoryCooldowns.erase(itr->second.CategoryId);
return _spellCooldowns.erase(itr);
}
typedef std::unordered_map<uint32, uint32> PacketCooldowns;
void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) const;
static void GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown);
Unit* _owner;
CooldownStorageType _spellCooldowns;
CooldownStorageType _spellCooldownsBeforeDuel;
CategoryCooldownStorageType _categoryCooldowns;
Clock::time_point _schoolLockouts[MAX_SPELL_SCHOOL];
GlobalCooldownStorageType _globalCooldowns;
+2
View File
@@ -1225,6 +1225,8 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_MAX_WHO] = sConfigMgr->GetIntDefault("MaxWhoListReturns", 49);
m_bool_configs[CONFIG_START_ALL_SPELLS] = sConfigMgr->GetBoolDefault("PlayerStart.AllSpells", false);
m_int_configs[CONFIG_HONOR_AFTER_DUEL] = sConfigMgr->GetIntDefault("HonorPointsAfterDuel", 0);
m_bool_configs[CONFIG_RESET_DUEL_COOLDOWNS] = sConfigMgr->GetBoolDefault("ResetDuelCooldowns", false);
m_bool_configs[CONFIG_RESET_DUEL_HEALTH_MANA] = sConfigMgr->GetBoolDefault("ResetDuelHealthMana", false);
m_bool_configs[CONFIG_START_ALL_EXPLORED] = sConfigMgr->GetBoolDefault("PlayerStart.MapsExplored", false);
m_bool_configs[CONFIG_START_ALL_REP] = sConfigMgr->GetBoolDefault("PlayerStart.AllReputation", false);
m_bool_configs[CONFIG_ALWAYS_MAXSKILL] = sConfigMgr->GetBoolDefault("AlwaysMaxWeaponSkill", false);
+2
View File
@@ -171,6 +171,8 @@ enum WorldBoolConfigs
CONFIG_ALLOW_TRACK_BOTH_RESOURCES,
CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA,
CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA,
CONFIG_RESET_DUEL_COOLDOWNS,
CONFIG_RESET_DUEL_HEALTH_MANA,
BOOL_CONFIG_VALUE_COUNT
};
+10
View File
@@ -1331,6 +1331,15 @@ class spell_dk_raise_dead : public SpellScriptLoader
GetCaster()->CastSpell(targets, spellInfo, NULL, TRIGGERED_FULL_MASK);
}
void OverrideCooldown()
{
// Because the ghoul is summoned by one of triggered spells SendCooldownEvent is not sent for this spell
// but the client has locked it by itself so we need some link between this spell and the real spell summoning.
// Luckily such link already exists - spell category
// This starts infinite category cooldown which can later be used by SendCooldownEvent to send packet for this spell
GetCaster()->GetSpellHistory()->StartCooldown(GetSpellInfo(), 0, nullptr, true);
}
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_dk_raise_dead_SpellScript::CheckCast);
@@ -1339,6 +1348,7 @@ class spell_dk_raise_dead : public SpellScriptLoader
OnCast += SpellCastFn(spell_dk_raise_dead_SpellScript::ConsumeReagents);
OnEffectHitTarget += SpellEffectFn(spell_dk_raise_dead_SpellScript::HandleRaiseDead, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
OnEffectHitTarget += SpellEffectFn(spell_dk_raise_dead_SpellScript::HandleRaiseDead, EFFECT_2, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_dk_raise_dead_SpellScript::OverrideCooldown);
}
private:
+121
View File
@@ -0,0 +1,121 @@
/*
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.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, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "Player.h"
#include "Pet.h"
#include "SpellInfo.h"
class DuelResetScript : public PlayerScript
{
public:
DuelResetScript() : PlayerScript("DuelResetScript") { }
// Called when a duel starts (after 3s countdown)
void OnDuelStart(Player* player1, Player* player2) override
{
// Cooldowns reset
if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS))
{
player1->GetSpellHistory()->SaveCooldownStateBeforeDuel();
player2->GetSpellHistory()->SaveCooldownStateBeforeDuel();
ResetSpellCooldowns(player1, true);
ResetSpellCooldowns(player2, true);
}
// Health and mana reset
if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_HEALTH_MANA))
{
player1->SaveHealthBeforeDuel();
player1->SetHealth(player1->GetMaxHealth());
player2->SaveHealthBeforeDuel();
player2->SetHealth(player2->GetMaxHealth());
// check if player1 class uses mana
if (player1->getPowerType() == POWER_MANA || player1->getClass() == CLASS_DRUID)
{
player1->SaveManaBeforeDuel();
player1->SetPower(POWER_MANA, player1->GetMaxPower(POWER_MANA));
}
// check if player2 class uses mana
if (player2->getPowerType() == POWER_MANA || player2->getClass() == CLASS_DRUID)
{
player2->SaveManaBeforeDuel();
player2->SetPower(POWER_MANA, player2->GetMaxPower(POWER_MANA));
}
}
}
// Called when a duel ends
void OnDuelEnd(Player* winner, Player* loser, DuelCompleteType type) override
{
// do not reset anything if DUEL_INTERRUPTED or DUEL_FLED
if (type == DUEL_WON)
{
// Cooldown restore
if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS))
{
ResetSpellCooldowns(winner, true);
ResetSpellCooldowns(loser, true);
winner->GetSpellHistory()->RestoreCooldownStateAfterDuel();
loser->GetSpellHistory()->RestoreCooldownStateAfterDuel();
}
// Health and mana restore
if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_HEALTH_MANA))
{
winner->RestoreHealthAfterDuel();
loser->RestoreHealthAfterDuel();
// check if player1 class uses mana
if (winner->getPowerType() == POWER_MANA || winner->getClass() == CLASS_DRUID)
winner->RestoreManaAfterDuel();
// check if player2 class uses mana
if (loser->getPowerType() == POWER_MANA || loser->getClass() == CLASS_DRUID)
loser->RestoreManaAfterDuel();
}
}
}
void ResetSpellCooldowns(Player* player, bool removeActivePetCooldowns)
{
// remove cooldowns on spells that have < 10 min CD and has no onHold
player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS && !itr->second.OnHold;
}, true);
// pet cooldowns
if (removeActivePetCooldowns)
if (Pet* pet = player->GetPet())
pet->GetSpellHistory()->ResetAllCooldowns();
}
};
void AddSC_duel_reset()
{
new DuelResetScript();
}
@@ -2573,6 +2573,21 @@ PlayerStart.MapsExplored = 0
HonorPointsAfterDuel = 0
#
# ResetDuelCooldowns
# Description: Reset all cooldowns before duel starts and restore them when duel ends.
# Default: 0 - (Disabled)
# 1 - (Enabled)
ResetDuelCooldowns = 0
# ResetDuelHealthMana
# Description: Reset health and mana before duel starts and restore them when duel ends.
# Default: 0 - (Disabled)
# 1 - (Enabled)
ResetDuelHealthMana = 0
#
# AlwaysMaxWeaponSkill
# Description: Players will automatically gain max weapon/defense skill when logging in,