initial commit

This commit is contained in:
hondacrx
2017-06-19 17:30:18 -04:00
commit 6e265ea24b
763 changed files with 488824 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+240
View File
@@ -0,0 +1,240 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 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/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Game.Spells
{
public class SkillDiscovery
{
public static void LoadSkillDiscoveryTable()
{
uint oldMSTime = Time.GetMSTime();
SkillDiscoveryStorage.Clear(); // need for reload
// 0 1 2 3
SQLResult result = DB.World.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
if (result.IsEmpty())
{
Log.outError(LogFilter.ServerLoading, "Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
return;
}
uint count = 0;
StringBuilder ssNonDiscoverableEntries = new StringBuilder();
List<uint> reportedReqSpells = new List<uint>();
do
{
uint spellId = result.Read<uint>(0);
int reqSkillOrSpell = result.Read<int>(1);
uint reqSkillValue = result.Read<uint>(2);
float chance = result.Read<float>(3);
if (chance <= 0) // chance
{
ssNonDiscoverableEntries.AppendFormat("spellId = {0} reqSkillOrSpell = {1} reqSkillValue = {2} chance = {3} (chance problem)\n", spellId, reqSkillOrSpell, reqSkillValue, chance);
continue;
}
if (reqSkillOrSpell > 0) // spell case
{
uint absReqSkillOrSpell = (uint)reqSkillOrSpell;
SpellInfo reqSpellInfo = Global.SpellMgr.GetSpellInfo(absReqSkillOrSpell);
if (reqSpellInfo == null)
{
if (!reportedReqSpells.Contains(absReqSkillOrSpell))
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) have not existed spell (ID: {1}) in `reqSpell` field in `skill_discovery_template` table", spellId, reqSkillOrSpell);
reportedReqSpells.Add(absReqSkillOrSpell);
}
continue;
}
// mechanic discovery
if (reqSpellInfo.Mechanic != Mechanics.Discovery &&
// explicit discovery ability
!reqSpellInfo.IsExplicitDiscovery())
{
if (!reportedReqSpells.Contains(absReqSkillOrSpell))
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc" +
" and not 100%% chance random discovery ability but listed for spellId {1} (and maybe more) in `skill_discovery_template` table",
absReqSkillOrSpell, spellId);
reportedReqSpells.Add(absReqSkillOrSpell);
}
continue;
}
SkillDiscoveryStorage.Add(reqSkillOrSpell, new SkillDiscoveryEntry(spellId, reqSkillValue, chance));
}
else if (reqSkillOrSpell == 0) // skill case
{
var bounds = Global.SpellMgr.GetSkillLineAbilityMapBounds(spellId);
if (bounds.Empty())
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table", spellId);
continue;
}
foreach (var _spell_idx in bounds)
SkillDiscoveryStorage.Add(-(int)_spell_idx.SkillLine, new SkillDiscoveryEntry(spellId, reqSkillValue, chance));
}
else
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) have negative value in `reqSpell` field in `skill_discovery_template` table", spellId);
continue;
}
++count;
}
while (result.NextRow());
if (ssNonDiscoverableEntries.Length != 0)
Log.outError(LogFilter.Sql, "Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n{0}", ssNonDiscoverableEntries.ToString());
// report about empty data for explicit discovery spells
foreach (var spellEntry in Global.SpellMgr.GetSpellInfoStorage().Values)
{
// skip not explicit discovery spells
if (!spellEntry.IsExplicitDiscovery())
continue;
if (!SkillDiscoveryStorage.ContainsKey((int)spellEntry.Id))
Log.outError(LogFilter.Sql, "Spell (ID: {0}) is 100% chance random discovery ability but not have data in `skill_discovery_template` table", spellEntry.Id);
}
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} skill discovery definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public static uint GetExplicitDiscoverySpell(uint spellId, Player player)
{
// explicit discovery spell chances (always success if case exist)
// in this case we have both skill and spell
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (tab.Empty())
return 0;
var bounds = Global.SpellMgr.GetSkillLineAbilityMapBounds(spellId);
uint skillvalue = !bounds.Empty() ? (uint)player.GetSkillValue((SkillType)bounds.FirstOrDefault().SkillLine) : 0;
float full_chance = 0;
foreach (var item_iter in tab)
if (item_iter.reqSkillValue <= skillvalue)
if (!player.HasSpell(item_iter.spellId))
full_chance += item_iter.chance;
float rate = full_chance / 100.0f;
float roll = (float)RandomHelper.randChance() * rate; // roll now in range 0..full_chance
foreach (var item_iter in tab)
{
if (item_iter.reqSkillValue > skillvalue)
continue;
if (player.HasSpell(item_iter.spellId))
continue;
if (item_iter.chance > roll)
return item_iter.spellId;
roll -= item_iter.chance;
}
return 0;
}
public static bool HasDiscoveredAllSpells(uint spellId, Player player)
{
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (tab.Empty())
return true;
foreach (var item_iter in tab)
if (!player.HasSpell(item_iter.spellId))
return false;
return true;
}
public static uint GetSkillDiscoverySpell(uint skillId, uint spellId, Player player)
{
uint skillvalue = skillId != 0 ? (uint)player.GetSkillValue((SkillType)skillId) : 0;
// check spell case
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (!tab.Empty())
{
foreach (var item_iter in tab)
{
if (RandomHelper.randChance(item_iter.chance * WorldConfig.GetFloatValue(WorldCfg.RateSkillDiscovery)) &&
item_iter.reqSkillValue <= skillvalue &&
!player.HasSpell(item_iter.spellId))
return item_iter.spellId;
}
return 0;
}
if (skillId == 0)
return 0;
// check skill line case
tab = SkillDiscoveryStorage.LookupByKey(-(int)skillId);
if (!tab.Empty())
{
foreach (var item_iter in tab)
{
if (RandomHelper.randChance(item_iter.chance * WorldConfig.GetFloatValue(WorldCfg.RateSkillDiscovery)) &&
item_iter.reqSkillValue <= skillvalue &&
!player.HasSpell(item_iter.spellId))
return item_iter.spellId;
}
return 0;
}
return 0;
}
static MultiMap<int, SkillDiscoveryEntry> SkillDiscoveryStorage = new MultiMap<int, SkillDiscoveryEntry>();
}
public class SkillDiscoveryEntry
{
public SkillDiscoveryEntry(uint _spellId = 0, uint req_skill_val = 0, float _chance = 0)
{
spellId = _spellId;
reqSkillValue = req_skill_val;
chance = _chance;
}
public uint spellId; // discavered spell
public uint reqSkillValue; // skill level limitation
public float chance; // chance
}
}
+227
View File
@@ -0,0 +1,227 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 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/>.
*/
using Framework.Database;
using Game.Entities;
using System.Collections.Generic;
namespace Game.Spells
{
public class SkillExtraItems
{
// loads the extra item creation info from DB
public static void LoadSkillExtraItemTable()
{
uint oldMSTime = Time.GetMSTime();
SkillExtraItemStorage.Clear(); // need for reload
// 0 1 2 3
SQLResult result = DB.World.Query("SELECT spellId, requiredSpecialization, additionalCreateChance, additionalMaxNum FROM skill_extra_item_template");
if (result.IsEmpty())
{
Log.outError(LogFilter.ServerLoading, "Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty.");
return;
}
uint count = 0;
do
{
uint spellId = result.Read<uint>(0);
if (!Global.SpellMgr.HasSpellInfo(spellId))
{
Log.outError(LogFilter.Sql, "Skill specialization {0} has non-existent spell id in `skill_extra_item_template`!", spellId);
continue;
}
uint requiredSpecialization = result.Read<uint>(1);
if (!Global.SpellMgr.HasSpellInfo(requiredSpecialization))
{
Log.outError(LogFilter.Sql, "Skill specialization {0} have not existed required specialization spell id {1} in `skill_extra_item_template`!", spellId, requiredSpecialization);
continue;
}
float additionalCreateChance = result.Read<float>(2);
if (additionalCreateChance <= 0.0f)
{
Log.outError(LogFilter.Sql, "Skill specialization {0} has too low additional create chance in `skill_extra_item_template`!", spellId);
continue;
}
byte additionalMaxNum = result.Read<byte>(3);
if (additionalMaxNum == 0)
{
Log.outError(LogFilter.Sql, "Skill specialization {0} has 0 max number of extra items in `skill_extra_item_template`!", spellId);
continue;
}
SkillExtraItemEntry skillExtraItemEntry = new SkillExtraItemEntry();
skillExtraItemEntry.requiredSpecialization = requiredSpecialization;
skillExtraItemEntry.additionalCreateChance = additionalCreateChance;
skillExtraItemEntry.additionalMaxNum = additionalMaxNum;
SkillExtraItemStorage[spellId] = skillExtraItemEntry;
++count;
}
while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} spell specialization definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public static bool CanCreateExtraItems(Player player, uint spellId, ref float additionalChance, ref byte additionalMax)
{
// get the info for the specified spell
var specEntry = SkillExtraItemStorage.LookupByKey(spellId);
if (specEntry == null)
return false;
// the player doesn't have the required specialization, return false
if (!player.HasSpell(specEntry.requiredSpecialization))
return false;
// set the arguments to the appropriate values
additionalChance = specEntry.additionalCreateChance;
additionalMax = specEntry.additionalMaxNum;
// enable extra item creation
return true;
}
static Dictionary<uint, SkillExtraItemEntry> SkillExtraItemStorage = new Dictionary<uint,SkillExtraItemEntry>();
}
class SkillExtraItemEntry
{
public SkillExtraItemEntry(uint rS = 0, float aCC = 0f, byte aMN = 0)
{
requiredSpecialization = rS;
additionalCreateChance = aCC;
additionalMaxNum = aMN;
}
// the spell id of the specialization required to create extra items
public uint requiredSpecialization;
// the chance to create one additional item
public float additionalCreateChance;
// maximum number of extra items created per crafting
public byte additionalMaxNum;
}
public class SkillPerfectItems
{
// loads the perfection proc info from DB
public static void LoadSkillPerfectItemTable()
{
uint oldMSTime = Time.GetMSTime();
SkillPerfectItemStorage.Clear(); // reload capability
// 0 1 2 3
SQLResult result = DB.World.Query("SELECT spellId, requiredSpecialization, perfectCreateChance, perfectItemType FROM skill_perfect_item_template");
if (result.IsEmpty())
{
Log.outError(LogFilter.ServerLoading, "Loaded 0 spell perfection definitions. DB table `skill_perfect_item_template` is empty.");
return;
}
uint count = 0;
do
{
uint spellId = result.Read<uint>(0);
if (!Global.SpellMgr.HasSpellInfo(spellId))
{
Log.outError(LogFilter.Sql, "Skill perfection data for spell {0} has non-existent spell id in `skill_perfect_item_template`!", spellId);
continue;
}
uint requiredSpecialization = result.Read<uint>(1);
if (!Global.SpellMgr.HasSpellInfo(requiredSpecialization))
{
Log.outError(LogFilter.Sql, "Skill perfection data for spell {0} has non-existent required specialization spell id {1} in `skill_perfect_item_template`!", spellId, requiredSpecialization);
continue;
}
float perfectCreateChance = result.Read<float>(2);
if (perfectCreateChance <= 0.0f)
{
Log.outError(LogFilter.Sql, "Skill perfection data for spell {0} has impossibly low proc chance in `skill_perfect_item_template`!", spellId);
continue;
}
uint perfectItemType = result.Read<uint>(3);
if (Global.ObjectMgr.GetItemTemplate(perfectItemType) == null)
{
Log.outError(LogFilter.Sql, "Skill perfection data for spell {0} references non-existent perfect item id {1} in `skill_perfect_item_template`!", spellId, perfectItemType);
continue;
}
SkillPerfectItemStorage[spellId] = new SkillPerfectItemEntry(requiredSpecialization, perfectCreateChance, perfectItemType);
++count;
}
while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} spell perfection definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public static bool CanCreatePerfectItem(Player player, uint spellId, ref float perfectCreateChance, ref uint perfectItemType)
{
var entry = SkillPerfectItemStorage.LookupByKey(spellId);
// no entry in DB means no perfection proc possible
if (entry == null)
return false;
// if you don't have the spell needed, then no procs for you
if (!player.HasSpell(entry.requiredSpecialization))
return false;
// set values as appropriate
perfectCreateChance = entry.perfectCreateChance;
perfectItemType = entry.perfectItemType;
// and tell the caller to start rolling the dice
return true;
}
static Dictionary<uint, SkillPerfectItemEntry> SkillPerfectItemStorage = new Dictionary<uint, SkillPerfectItemEntry>();
}
// struct to store information about perfection procs
// one entry per spell
class SkillPerfectItemEntry
{
public SkillPerfectItemEntry(uint rS = 0, float pCC = 0f, uint pIT = 0)
{
requiredSpecialization = rS;
perfectCreateChance = pCC;
perfectItemType = pIT;
}
// the spell id of the spell required - it's named "specialization" to conform with SkillExtraItemEntry
public uint requiredSpecialization;
// perfection proc chance
public float perfectCreateChance;
// itemid of the resulting perfect item
public uint perfectItemType;
}
}
+7748
View File
File diff suppressed because it is too large Load Diff
+440
View File
@@ -0,0 +1,440 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 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/>.
*/
using Framework.Constants;
using Game.Entities;
using Game.Network.Packets;
using System;
using System.Diagnostics.Contracts;
namespace Game.Spells
{
public class SpellCastTargets
{
public SpellCastTargets()
{
m_strTarget = "";
m_src = new SpellDestination();
m_dst = new SpellDestination();
}
public SpellCastTargets(Unit caster, SpellCastRequest spellCastRequest)
{
m_targetMask = spellCastRequest.Target.Flags;
m_objectTargetGUID = spellCastRequest.Target.Unit;
m_itemTargetGUID = spellCastRequest.Target.Item;
m_strTarget = spellCastRequest.Target.Name;
m_src = new SpellDestination();
m_dst = new SpellDestination();
if (spellCastRequest.Target.SrcLocation.HasValue)
{
m_src.TransportGUID = spellCastRequest.Target.SrcLocation.Value.Transport;
Position pos;
if (!m_src.TransportGUID.IsEmpty())
pos = m_src.TransportOffset;
else
pos = m_src.Position;
pos.Relocate(spellCastRequest.Target.SrcLocation.Value.Location);
if (spellCastRequest.Target.Orientation.HasValue)
pos.SetOrientation(spellCastRequest.Target.Orientation.Value);
}
if (spellCastRequest.Target.DstLocation.HasValue)
{
m_dst.TransportGUID = spellCastRequest.Target.DstLocation.Value.Transport;
Position pos;
if (!m_dst.TransportGUID.IsEmpty())
pos = m_dst.TransportOffset;
else
pos = m_dst.Position;
pos.Relocate(spellCastRequest.Target.DstLocation.Value.Location);
if (spellCastRequest.Target.Orientation.HasValue)
pos.SetOrientation(spellCastRequest.Target.Orientation.Value);
}
SetPitch(spellCastRequest.MissileTrajectory.Pitch);
SetSpeed(spellCastRequest.MissileTrajectory.Speed);
Update(caster);
}
public void Write(SpellTargetData data)
{
data.Flags = m_targetMask;
if (m_targetMask.HasAnyFlag(SpellCastTargetFlags.Unit | SpellCastTargetFlags.CorpseAlly | SpellCastTargetFlags.Gameobject | SpellCastTargetFlags.CorpseEnemy | SpellCastTargetFlags.UnitMinipet))
data.Unit = m_objectTargetGUID;
if (m_targetMask.HasAnyFlag(SpellCastTargetFlags.Item | SpellCastTargetFlags.TradeItem) && m_itemTarget)
data.Item = m_itemTarget.GetGUID();
if (m_targetMask.HasAnyFlag(SpellCastTargetFlags.SourceLocation))
{
data.SrcLocation.HasValue = true;
TargetLocation target = new TargetLocation();
target.Transport = m_src.TransportGUID; // relative position guid here - transport for example
if (!m_src.TransportGUID.IsEmpty())
target.Location = m_src.TransportOffset;
else
target.Location = m_src.Position;
data.SrcLocation.Value = target;
}
if (Convert.ToBoolean(m_targetMask & SpellCastTargetFlags.DestLocation))
{
data.DstLocation.HasValue = true;
TargetLocation target = new TargetLocation();
target.Transport = m_dst.TransportGUID; // relative position guid here - transport for example
if (!m_dst.TransportGUID.IsEmpty())
target.Location = m_dst.TransportOffset;
else
target.Location = m_dst.Position;
data.DstLocation.Value = target;
}
if (Convert.ToBoolean(m_targetMask & SpellCastTargetFlags.String))
data.Name = m_strTarget;
}
public ObjectGuid GetOrigUnitTargetGUID()
{
switch (m_origObjectTargetGUID.GetHigh())
{
case HighGuid.Player:
case HighGuid.Vehicle:
case HighGuid.Creature:
case HighGuid.Pet:
return m_origObjectTargetGUID;
default:
return ObjectGuid.Empty;
}
}
public void SetOrigUnitTarget(Unit target)
{
if (!target)
return;
m_origObjectTargetGUID = target.GetGUID();
}
public ObjectGuid GetUnitTargetGUID()
{
if (m_objectTargetGUID.IsUnit())
return m_objectTargetGUID;
return ObjectGuid.Empty;
}
public Unit GetUnitTarget()
{
if (m_objectTarget)
return m_objectTarget.ToUnit();
return null;
}
public void SetUnitTarget(Unit target)
{
if (target == null)
return;
m_objectTarget = target;
m_objectTargetGUID = target.GetGUID();
m_targetMask |= SpellCastTargetFlags.Unit;
}
ObjectGuid GetGOTargetGUID()
{
if (m_objectTargetGUID.IsAnyTypeGameObject())
return m_objectTargetGUID;
return ObjectGuid.Empty;
}
public GameObject GetGOTarget()
{
if (m_objectTarget != null)
return m_objectTarget.ToGameObject();
return null;
}
public void SetGOTarget(GameObject target)
{
if (target == null)
return;
m_objectTarget = target;
m_objectTargetGUID = target.GetGUID();
m_targetMask |= SpellCastTargetFlags.Gameobject;
}
public ObjectGuid GetCorpseTargetGUID()
{
if (m_objectTargetGUID.IsCorpse())
return m_objectTargetGUID;
return ObjectGuid.Empty;
}
public Corpse GetCorpseTarget()
{
if (m_objectTarget != null)
return m_objectTarget.ToCorpse();
return null;
}
public WorldObject GetObjectTarget()
{
return m_objectTarget;
}
public ObjectGuid GetObjectTargetGUID()
{
return m_objectTargetGUID;
}
public void RemoveObjectTarget()
{
m_objectTarget = null;
m_objectTargetGUID.Clear();
m_targetMask &= ~(SpellCastTargetFlags.UnitMask | SpellCastTargetFlags.CorpseMask | SpellCastTargetFlags.GameobjectMask);
}
public void SetItemTarget(Item item)
{
if (item == null)
return;
m_itemTarget = item;
m_itemTargetGUID = item.GetGUID();
m_itemTargetEntry = item.GetEntry();
m_targetMask |= SpellCastTargetFlags.Item;
}
public void SetTradeItemTarget(Player caster)
{
m_itemTargetGUID = ObjectGuid.TradeItem;
m_itemTargetEntry = 0;
m_targetMask |= SpellCastTargetFlags.TradeItem;
Update(caster);
}
public void UpdateTradeSlotItem()
{
if (m_itemTarget != null && Convert.ToBoolean(m_targetMask & SpellCastTargetFlags.TradeItem))
{
m_itemTargetGUID = m_itemTarget.GetGUID();
m_itemTargetEntry = m_itemTarget.GetEntry();
}
}
public SpellDestination GetSrc()
{
return m_src;
}
public Position GetSrcPos()
{
return m_src.Position;
}
void SetSrc(float x, float y, float z)
{
m_src = new SpellDestination(x, y, z);
m_targetMask |= SpellCastTargetFlags.SourceLocation;
}
void SetSrc(Position pos)
{
m_src = new SpellDestination(pos);
m_targetMask |= SpellCastTargetFlags.SourceLocation;
}
public void SetSrc(WorldObject wObj)
{
m_src = new SpellDestination(wObj);
m_targetMask |= SpellCastTargetFlags.SourceLocation;
}
public void ModSrc(Position pos)
{
Contract.Assert(m_targetMask.HasAnyFlag(SpellCastTargetFlags.SourceLocation));
m_src.Relocate(pos);
}
public void RemoveSrc()
{
m_targetMask &= ~SpellCastTargetFlags.SourceLocation;
}
public SpellDestination GetDst()
{
return m_dst;
}
public WorldLocation GetDstPos()
{
return m_dst.Position;
}
public void SetDst(float x, float y, float z, float orientation, uint mapId = 0xFFFFFFFF)
{
m_dst = new SpellDestination(x, y, z, orientation, mapId);
m_targetMask |= SpellCastTargetFlags.DestLocation;
}
public void SetDst(Position pos)
{
m_dst = new SpellDestination(pos);
m_targetMask |= SpellCastTargetFlags.DestLocation;
}
public void SetDst(WorldObject wObj)
{
m_dst = new SpellDestination(wObj);
m_targetMask |= SpellCastTargetFlags.DestLocation;
}
public void SetDst(SpellDestination spellDest)
{
m_dst = spellDest;
m_targetMask |= SpellCastTargetFlags.DestLocation;
}
public void SetDst(SpellCastTargets spellTargets)
{
m_dst = spellTargets.m_dst;
m_targetMask |= SpellCastTargetFlags.DestLocation;
}
public void ModDst(Position pos)
{
Contract.Assert(m_targetMask.HasAnyFlag(SpellCastTargetFlags.DestLocation));
m_dst.Relocate(pos);
}
public void ModDst(SpellDestination spellDest)
{
Contract.Assert(m_targetMask.HasAnyFlag(SpellCastTargetFlags.DestLocation));
m_dst = spellDest;
}
public void RemoveDst()
{
m_targetMask &= ~SpellCastTargetFlags.DestLocation;
}
public void Update(Unit caster)
{
m_objectTarget = !m_objectTargetGUID.IsEmpty() ? ((m_objectTargetGUID == caster.GetGUID()) ? caster : Global.ObjAccessor.GetWorldObject(caster, m_objectTargetGUID)) : null;
m_itemTarget = null;
if (caster is Player)
{
Player player = caster.ToPlayer();
if (m_targetMask.HasAnyFlag(SpellCastTargetFlags.Item))
m_itemTarget = player.GetItemByGuid(m_itemTargetGUID);
else if (m_targetMask.HasAnyFlag(SpellCastTargetFlags.TradeItem))
{
if (m_itemTargetGUID == ObjectGuid.TradeItem) // here it is not guid but slot. Also prevents hacking slots
{
TradeData pTrade = player.GetTradeData();
if (pTrade != null)
m_itemTarget = pTrade.GetTraderData().GetItem(TradeSlots.NonTraded);
}
}
if (m_itemTarget != null)
m_itemTargetEntry = m_itemTarget.GetEntry();
}
// update positions by transport move
if (HasSrc() && !m_src.TransportGUID.IsEmpty())
{
WorldObject transport = Global.ObjAccessor.GetWorldObject(caster, m_src.TransportGUID);
if (transport != null)
{
m_src.Position.Relocate(transport.GetPosition());
m_src.Position.RelocateOffset(m_src.TransportOffset);
}
}
if (HasDst() && !m_dst.TransportGUID.IsEmpty())
{
WorldObject transport = Global.ObjAccessor.GetWorldObject(caster, m_dst.TransportGUID);
if (transport != null)
{
m_dst.Position.Relocate(transport.GetPosition());
m_dst.Position.RelocateOffset(m_dst.TransportOffset);
}
}
}
public SpellCastTargetFlags GetTargetMask() { return m_targetMask; }
public void SetTargetMask(SpellCastTargetFlags newMask) { m_targetMask = newMask; }
public void SetTargetFlag(SpellCastTargetFlags flag) { m_targetMask |= flag; }
public ObjectGuid GetItemTargetGUID() { return m_itemTargetGUID; }
public Item GetItemTarget() { return m_itemTarget; }
public uint GetItemTargetEntry() { return m_itemTargetEntry; }
public bool HasSrc() { return Convert.ToBoolean(m_targetMask & SpellCastTargetFlags.SourceLocation); }
public bool HasDst() { return Convert.ToBoolean(m_targetMask & SpellCastTargetFlags.DestLocation); }
public bool HasTraj() { return m_speed != 0; }
public float GetPitch() { return m_pitch; }
public void SetPitch(float pitch) { m_pitch = pitch; }
float GetSpeed() { return m_speed; }
public void SetSpeed(float speed) { m_speed = speed; }
public float GetDist2d() { return m_src.Position.GetExactDist2d(m_dst.Position); }
public float GetSpeedXY() { return (float)(m_speed * Math.Cos(m_pitch)); }
public float GetSpeedZ() { return (float)(m_speed * Math.Sin(m_pitch)); }
public string GetTargetString() { return m_strTarget; }
#region Fields
SpellCastTargetFlags m_targetMask;
// objects (can be used at spell creating and after Update at casting)
WorldObject m_objectTarget;
Item m_itemTarget;
// object GUID/etc, can be used always
ObjectGuid m_origObjectTargetGUID;
ObjectGuid m_objectTargetGUID;
ObjectGuid m_itemTargetGUID;
uint m_itemTargetEntry;
SpellDestination m_src;
SpellDestination m_dst;
float m_pitch;
float m_speed;
string m_strTarget;
#endregion
}
}
File diff suppressed because it is too large Load Diff
+983
View File
@@ -0,0 +1,983 @@
/*
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
*
* 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 3 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/>.
*/
using Framework.Constants;
using Framework.Database;
using Game.DataStorage;
using Game.Entities;
using Game.Network.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.Spells
{
public class SpellHistory
{
public SpellHistory(Unit owner)
{
_owner = owner;
}
public void LoadFromDB<T>(SQLResult cooldownsResult, SQLResult chargesResult) where T : WorldObject
{
if (!cooldownsResult.IsEmpty())
{
do
{
uint spellId = cooldownsResult.Read<uint>(0);
if (!Global.SpellMgr.HasSpellInfo(spellId))
continue;
int index = (typeof(T) == typeof(Pet) ? 1 : 2);
CooldownEntry cooldownEntry = new CooldownEntry();
cooldownEntry.SpellId = spellId;
cooldownEntry.CooldownEnd = Time.UnixTimeToDateTime(cooldownsResult.Read<uint>(index++));
cooldownEntry.ItemId = 0;
cooldownEntry.CategoryId = cooldownsResult.Read<uint>(index++);
cooldownEntry.CategoryEnd = Time.UnixTimeToDateTime(cooldownsResult.Read<uint>(index++));
_spellCooldowns[spellId] = cooldownEntry;
if (cooldownEntry.CategoryId != 0)
_categoryCooldowns[cooldownEntry.CategoryId] = _spellCooldowns[spellId];
} while (cooldownsResult.NextRow());
}
if (!chargesResult.IsEmpty())
{
do
{
uint categoryId = chargesResult.Read<uint>(0);
if (!CliDB.SpellCategoryStorage.ContainsKey(categoryId))
continue;
ChargeEntry charges;
charges.RechargeStart = Time.UnixTimeToDateTime(chargesResult.Read<uint>(1));
charges.RechargeEnd = Time.UnixTimeToDateTime(chargesResult.Read<uint>(2));
_categoryCharges.Add(categoryId, charges);
} while (chargesResult.NextRow());
}
}
public void SaveToDB<T>(SQLTransaction trans) where T : WorldObject
{
PreparedStatement stmt;
if (typeof(T) == typeof(Pet))
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_PET_SPELL_COOLDOWNS);
stmt.AddValue(0, _owner.GetCharmInfo().GetPetNumber());
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_PET_SPELL_CHARGES);
stmt.AddValue(0, _owner.GetCharmInfo().GetPetNumber());
trans.Append(stmt);
byte index = 0;
foreach (var pair in _spellCooldowns)
{
if (!pair.Value.OnHold)
{
index = 0;
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_PET_SPELL_COOLDOWN);
stmt.AddValue(index++, _owner.GetCharmInfo().GetPetNumber());
stmt.AddValue(index++, pair.Key);
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.CooldownEnd));
stmt.AddValue(index++, pair.Value.CategoryId);
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.CategoryEnd));
trans.Append(stmt);
}
}
foreach (var pair in _categoryCharges)
{
index = 0;
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_PET_SPELL_CHARGES);
stmt.AddValue(index++, _owner.GetCharmInfo().GetPetNumber());
stmt.AddValue(index++, pair.Key);
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.RechargeStart));
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.RechargeEnd));
trans.Append(stmt);
}
}
else
{
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_SPELL_COOLDOWNS);
stmt.AddValue(0, _owner.GetGUID().GetCounter());
trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_SPELL_CHARGES);
stmt.AddValue(0, _owner.GetGUID().GetCounter());
trans.Append(stmt);
byte index = 0;
foreach (var pair in _spellCooldowns)
{
if (!pair.Value.OnHold)
{
index = 0;
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_SPELL_COOLDOWN);
stmt.AddValue(index++, _owner.GetGUID().GetCounter());
stmt.AddValue(index++, pair.Key);
stmt.AddValue(index++, pair.Value.ItemId);
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.CooldownEnd));
stmt.AddValue(index++, pair.Value.CategoryId);
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.CategoryEnd));
trans.Append(stmt);
}
}
foreach (var pair in _categoryCharges)
{
index = 0;
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_SPELL_CHARGES);
stmt.AddValue(index++, _owner.GetGUID().GetCounter());
stmt.AddValue(index++, pair.Key);
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.RechargeStart));
stmt.AddValue(index++, (uint)Time.DateTimeToUnixTime(pair.Value.RechargeEnd));
trans.Append(stmt);
}
}
}
public void Update()
{
DateTime now = DateTime.Now;
foreach (var pair in _categoryCooldowns.ToList())
{
if (pair.Value.CategoryEnd < now)
_categoryCooldowns.Remove(pair.Key);
}
foreach (var pair in _spellCooldowns.ToList())
{
if (pair.Value.CooldownEnd < now)
{
_categoryCooldowns.Remove(pair.Value.CategoryId);
_spellCooldowns.Remove(pair.Key);
}
}
foreach (var key in _categoryCharges.Keys)
{
var chargeRefreshTimes = _categoryCharges[key];
while (!chargeRefreshTimes.Empty() && chargeRefreshTimes.FirstOrDefault().RechargeEnd <= now)
chargeRefreshTimes.RemoveAt(0);
}
}
public void HandleCooldowns(SpellInfo spellInfo, Item item, Spell spell = null)
{
HandleCooldowns(spellInfo, item ? item.GetEntry() : 0u, spell);
}
public void HandleCooldowns(SpellInfo spellInfo, uint itemId, Spell spell = null)
{
if (ConsumeCharge(spellInfo.ChargeCategoryId))
return;
Player player = _owner.ToPlayer();
if (player)
{
// potions start cooldown until exiting combat
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId);
if (itemTemplate != null)
{
if (itemTemplate.IsPotion() || spellInfo.IsCooldownStartedOnEvent())
{
player.SetLastPotionId(itemId);
return;
}
}
}
if (spellInfo.IsCooldownStartedOnEvent() || spellInfo.IsPassive() || (spell && spell.IsIgnoringCooldowns()))
return;
StartCooldown(spellInfo, itemId, spell);
}
public bool IsReady(SpellInfo spellInfo, uint itemId = 0, bool ignoreCategoryCooldown = false)
{
if (spellInfo.PreventionType.HasAnyFlag(SpellPreventionType.Silence))
if (IsSchoolLocked(spellInfo.GetSchoolMask()))
return false;
if (HasCooldown(spellInfo.Id, itemId, ignoreCategoryCooldown))
return false;
if (!HasCharge(spellInfo.ChargeCategoryId))
return false;
return true;
}
public void WritePacket(SendSpellHistory sendSpellHistory)
{
DateTime now = DateTime.Now;
foreach (var p in _spellCooldowns)
{
SpellHistoryEntry historyEntry = new SpellHistoryEntry();
historyEntry.SpellID = p.Key;
historyEntry.ItemID = p.Value.ItemId;
if (p.Value.OnHold)
historyEntry.OnHold = true;
else
{
TimeSpan cooldownDuration = p.Value.CooldownEnd - now;
if (cooldownDuration.TotalMilliseconds <= 0)
continue;
historyEntry.RecoveryTime = (int)cooldownDuration.TotalMilliseconds;
TimeSpan categoryDuration = p.Value.CategoryEnd - now;
if (categoryDuration.TotalMilliseconds > 0)
{
historyEntry.Category = p.Value.CategoryId;
historyEntry.CategoryRecoveryTime = (int)categoryDuration.TotalMilliseconds;
}
}
sendSpellHistory.Entries.Add(historyEntry);
}
}
public void WritePacket(SendSpellCharges sendSpellCharges)
{
DateTime now = DateTime.Now;
foreach (var key in _categoryCharges.Keys)
{
var p = _categoryCharges[key];
if (!p.Empty())
{
TimeSpan cooldownDuration = p.FirstOrDefault().RechargeEnd - now;
if (cooldownDuration.TotalMilliseconds <= 0)
continue;
SpellChargeEntry chargeEntry = new SpellChargeEntry();
chargeEntry.Category = key;
chargeEntry.NextRecoveryTime = (uint)cooldownDuration.TotalMilliseconds;
chargeEntry.ConsumedCharges = (byte)p.Count;
sendSpellCharges.Entries.Add(chargeEntry);
}
}
}
public void WritePacket(PetSpells petSpells)
{
DateTime now = DateTime.Now;
foreach (var pair in _spellCooldowns)
{
PetSpellCooldown petSpellCooldown = new PetSpellCooldown();
petSpellCooldown.SpellID = pair.Key;
petSpellCooldown.Category = (ushort)pair.Value.CategoryId;
if (!pair.Value.OnHold)
{
var cooldownDuration = pair.Value.CooldownEnd - now;
if (cooldownDuration.TotalMilliseconds <= 0)
continue;
petSpellCooldown.Duration = (uint)cooldownDuration.TotalMilliseconds;
var categoryDuration = pair.Value.CategoryEnd - now;
if (categoryDuration.TotalMilliseconds > 0)
petSpellCooldown.CategoryDuration = (uint)categoryDuration.TotalMilliseconds;
}
else
petSpellCooldown.CategoryDuration = 0x80000000;
petSpells.Cooldowns.Add(petSpellCooldown);
}
foreach (var key in _categoryCharges.Keys)
{
var list = _categoryCharges[key];
if (!list.Empty())
{
var cooldownDuration = list.FirstOrDefault().RechargeEnd - now;
if (cooldownDuration.TotalMilliseconds <= 0)
continue;
PetSpellHistory petChargeEntry = new PetSpellHistory();
petChargeEntry.CategoryID = key;
petChargeEntry.RecoveryTime = (uint)cooldownDuration.TotalMilliseconds;
petChargeEntry.ConsumedCharges = (sbyte)list.Count;
petSpells.SpellHistory.Add(petChargeEntry);
}
}
}
public void StartCooldown(SpellInfo spellInfo, uint itemId, Spell spell = null, bool onHold = false)
{
// init cooldown values
uint categoryId = 0;
int cooldown = -1;
int categoryCooldown = -1;
GetCooldownDurations(spellInfo, itemId, ref cooldown, ref categoryId, ref categoryCooldown);
DateTime curTime = DateTime.Now;
DateTime catrecTime;
DateTime recTime;
bool needsCooldownPacket = false;
// overwrite time for selected category
if (onHold)
{
// use +MONTH as infinite cooldown marker
catrecTime = categoryCooldown > 0 ? (curTime + PlayerConst.InfinityCooldownDelay) : curTime;
recTime = cooldown > 0 ? (curTime + PlayerConst.InfinityCooldownDelay) : catrecTime;
}
else
{
// shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
// prevent 0 cooldowns set by another way
if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo.IsAutoRepeatRangedSpell() && spellInfo.Id != 75)))
cooldown = (int)_owner.GetUInt32Value(UnitFields.RangedAttackTime);
// Now we have cooldown data (if found any), time to apply mods
Player modOwner = _owner.GetSpellModOwner();
if (modOwner)
{
if (cooldown > 0)
modOwner.ApplySpellMod(spellInfo.Id, SpellModOp.Cooldown, ref cooldown, spell);
if (categoryCooldown > 0 && !spellInfo.HasAttribute(SpellAttr6.IgnoreCategoryCooldownMods))
modOwner.ApplySpellMod(spellInfo.Id, SpellModOp.Cooldown, ref categoryCooldown, spell);
}
if (_owner.HasAuraTypeWithAffectMask(AuraType.ModSpellCooldownByHaste, spellInfo))
{
cooldown = (int)(cooldown * _owner.GetFloatValue(UnitFields.ModCastHaste));
categoryCooldown = (int)(categoryCooldown * _owner.GetFloatValue(UnitFields.ModCastHaste));
}
if (_owner.HasAuraTypeWithAffectMask(AuraType.ModCooldownByHasteRegen, spellInfo))
{
cooldown = (int)(cooldown * _owner.GetFloatValue(UnitFields.ModHasteRegen));
categoryCooldown = (int)(categoryCooldown * _owner.GetFloatValue(UnitFields.ModHasteRegen));
}
int cooldownMod = _owner.GetTotalAuraModifier(AuraType.ModCooldown);
if (cooldownMod != 0)
{
// Apply SPELL_AURA_MOD_COOLDOWN only to own spells
Player playerOwner = GetPlayerOwner();
if (!playerOwner || playerOwner.HasSpell(spellInfo.Id))
{
needsCooldownPacket = true;
cooldown += cooldownMod * Time.InMilliseconds; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
}
}
// Apply SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN modifiers
// Note: This aura applies its modifiers to all cooldowns of spells with set category, not to category cooldown only
if (categoryId != 0)
{
int categoryModifier = _owner.GetTotalAuraModifierByMiscValue(AuraType.ModSpellCategoryCooldown, (int)categoryId);
if (categoryModifier != 0)
{
if (cooldown > 0)
cooldown += categoryModifier;
if (categoryCooldown > 0)
categoryCooldown += categoryModifier;
}
SpellCategoryRecord categoryEntry = CliDB.SpellCategoryStorage.LookupByKey(categoryId);
if (categoryEntry.Flags.HasAnyFlag(SpellCategoryFlags.CooldownExpiresAtDailyReset))
categoryCooldown = (int)(Time.UnixTimeToDateTime(Global.WorldMgr.GetNextDailyQuestsResetTime()) - DateTime.Now).TotalMilliseconds;
}
// replace negative cooldowns by 0
if (cooldown < 0)
cooldown = 0;
if (categoryCooldown < 0)
categoryCooldown = 0;
// no cooldown after applying spell mods
if (cooldown == 0 && categoryCooldown == 0)
return;
catrecTime = categoryCooldown != 0 ? curTime + TimeSpan.FromMilliseconds(categoryCooldown) : curTime;
recTime = cooldown != 0 ? curTime + TimeSpan.FromMilliseconds(cooldown) : catrecTime;
}
// self spell cooldown
if (recTime != curTime)
{
AddCooldown(spellInfo.Id, itemId, recTime, categoryId, catrecTime, onHold);
if (needsCooldownPacket)
{
Player playerOwner = GetPlayerOwner();
if (playerOwner)
{
SpellCooldownPkt spellCooldown = new SpellCooldownPkt();
spellCooldown.Caster = _owner.GetGUID();
spellCooldown.Flags = SpellCooldownFlags.None;
spellCooldown.SpellCooldowns.Add(new SpellCooldownStruct(spellInfo.Id, (uint)cooldown));
playerOwner.SendPacket(spellCooldown);
}
}
}
}
public void SendCooldownEvent(SpellInfo spellInfo, uint itemId = 0, Spell spell = null, bool startCooldown = true)
{
Player player = GetPlayerOwner();
if (player)
{
uint category = spellInfo.GetCategory();
GetCooldownDurations(spellInfo, itemId, ref category);
var categoryEntry = _categoryCooldowns.LookupByKey(category);
if (categoryEntry != null && categoryEntry.SpellId != spellInfo.Id)
{
player.SendPacket(new CooldownEvent(player != _owner, categoryEntry.SpellId));
if (startCooldown)
StartCooldown(Global.SpellMgr.GetSpellInfo(categoryEntry.SpellId), itemId, spell);
}
player.SendPacket(new CooldownEvent(player != _owner, spellInfo.Id));
}
// start cooldowns at server side, if any
if (startCooldown)
StartCooldown(spellInfo, itemId, spell);
}
public void AddCooldown(uint spellId, uint itemId, TimeSpan cooldownDuration)
{
DateTime now = DateTime.Now;
AddCooldown(spellId, itemId, now + cooldownDuration, 0, now);
}
public void AddCooldown(uint spellId, uint itemId, DateTime cooldownEnd, uint categoryId, DateTime categoryEnd, bool onHold = false)
{
CooldownEntry cooldownEntry = new CooldownEntry();
cooldownEntry.SpellId = spellId;
cooldownEntry.CooldownEnd = cooldownEnd;
cooldownEntry.ItemId = itemId;
cooldownEntry.CategoryId = categoryId;
cooldownEntry.CategoryEnd = categoryEnd;
cooldownEntry.OnHold = onHold;
_spellCooldowns[spellId] = cooldownEntry;
if (categoryId != 0)
_categoryCooldowns[categoryId] = cooldownEntry;
}
public void ModifyCooldown(uint spellId, int cooldownModMs)
{
TimeSpan offset = TimeSpan.FromMilliseconds(cooldownModMs);
ModifyCooldown(spellId, offset);
}
public void ModifyCooldown(uint spellId, TimeSpan offset)
{
var cooldownEntry = _spellCooldowns.LookupByKey(spellId);
if (offset.TotalMilliseconds == 0 || cooldownEntry == null)
return;
DateTime now = DateTime.Now;
if (cooldownEntry.CooldownEnd + offset > now)
cooldownEntry.CooldownEnd += offset;
else
{
_categoryCooldowns.Remove(cooldownEntry.CategoryId);
_spellCooldowns.Remove(spellId);
}
Player playerOwner = GetPlayerOwner();
if (playerOwner)
{
ModifyCooldown modifyCooldown = new ModifyCooldown();
modifyCooldown.IsPet = _owner != playerOwner;
modifyCooldown.SpellID = spellId;
modifyCooldown.DeltaTime = (int)offset.TotalMilliseconds;
playerOwner.SendPacket(modifyCooldown);
}
}
public void ResetCooldown(uint spellId, bool update = false)
{
var entry = _spellCooldowns.LookupByKey(spellId);
if (entry == null)
return;
if (update)
{
Player playerOwner = GetPlayerOwner();
if (playerOwner)
{
ClearCooldown clearCooldown = new ClearCooldown();
clearCooldown.IsPet = _owner != playerOwner;
clearCooldown.SpellID = spellId;
clearCooldown.ClearOnHold = false;
playerOwner.SendPacket(clearCooldown);
}
}
_categoryCooldowns.Remove(entry.CategoryId);
_spellCooldowns.Remove(spellId);
}
public void ResetCooldowns(Func<KeyValuePair<uint, CooldownEntry>, bool> predicate, bool update = false)
{
List<uint> resetCooldowns = new List<uint>();
foreach (var pair in _spellCooldowns)
{
if (predicate(pair))
{
resetCooldowns.Add(pair.Key);
ResetCooldown(pair.Key, false);
}
}
if (update && !resetCooldowns.Empty())
SendClearCooldowns(resetCooldowns);
}
public void ResetAllCooldowns()
{
Player playerOwner = GetPlayerOwner();
if (playerOwner)
{
List<uint> cooldowns = new List<uint>();
foreach (var id in _spellCooldowns.Keys)
cooldowns.Add(id);
SendClearCooldowns(cooldowns);
}
_categoryCooldowns.Clear();
_spellCooldowns.Clear();
}
public bool HasCooldown(uint spellId, uint itemId = 0, bool ignoreCategoryCooldown = false)
{
return HasCooldown(Global.SpellMgr.GetSpellInfo(spellId), itemId, ignoreCategoryCooldown);
}
public bool HasCooldown(SpellInfo spellInfo, uint itemId = 0, bool ignoreCategoryCooldown = false)
{
if (_spellCooldowns.ContainsKey(spellInfo.Id))
return true;
if (ignoreCategoryCooldown)
return false;
uint category = 0;
GetCooldownDurations(spellInfo, itemId, ref category);
if (category == 0)
category = spellInfo.GetCategory();
if (category == 0)
return false;
return _categoryCooldowns.ContainsKey(category);
}
public uint GetRemainingCooldown(SpellInfo spellInfo)
{
DateTime end;
var entry = _spellCooldowns.LookupByKey(spellInfo.Id);
if (entry != null)
end = entry.CooldownEnd;
else
{
var cooldownEntry = _categoryCooldowns.LookupByKey(spellInfo.GetCategory());
if (cooldownEntry == null)
return 0;
end = cooldownEntry.CategoryEnd;
}
DateTime now = DateTime.Now;
if (end < now)
return 0;
var remaining = end - now;
return (uint)remaining.TotalMilliseconds;
}
public void LockSpellSchool(SpellSchoolMask schoolMask, uint lockoutTime)
{
DateTime now = DateTime.Now;
DateTime lockoutEnd = now + TimeSpan.FromMilliseconds(lockoutTime);
for (int i = 0; i < (int)SpellSchools.Max; ++i)
if (Convert.ToBoolean((SpellSchoolMask)(1 << i) & schoolMask))
_schoolLockouts[i] = lockoutEnd;
List<uint> knownSpells = new List<uint>();
Player plrOwner = _owner.ToPlayer();
if (plrOwner)
{
foreach (var p in plrOwner.GetSpellMap())
if (p.Value.State != PlayerSpellState.Removed)
knownSpells.Add(p.Key);
}
else if (_owner.IsPet())
{
Pet petOwner = _owner.ToPet();
foreach (var p in petOwner.m_spells)
if (p.Value.state != PetSpellState.Removed)
knownSpells.Add(p.Key);
}
else
{
Creature creatureOwner = _owner.ToCreature();
for (byte i = 0; i < SharedConst.MaxCreatureSpells; ++i)
if (creatureOwner.m_spells[i] != 0)
knownSpells.Add(creatureOwner.m_spells[i]);
}
SpellCooldownPkt spellCooldown = new SpellCooldownPkt();
spellCooldown.Caster = _owner.GetGUID();
spellCooldown.Flags = SpellCooldownFlags.None;
foreach (uint spellId in knownSpells)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId);
if (spellInfo.IsCooldownStartedOnEvent())
continue;
if (!spellInfo.PreventionType.HasAnyFlag(SpellPreventionType.Silence))
continue;
if (Convert.ToBoolean(schoolMask & spellInfo.GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime)
{
spellCooldown.SpellCooldowns.Add(new SpellCooldownStruct(spellId, lockoutTime));
AddCooldown(spellId, 0, lockoutEnd, 0, now);
}
}
Player player = GetPlayerOwner();
if (player)
if (!spellCooldown.SpellCooldowns.Empty())
player.SendPacket(spellCooldown);
}
public bool IsSchoolLocked(SpellSchoolMask schoolMask)
{
DateTime now = DateTime.Now;
for (int i = 0; i < (int)SpellSchools.Max; ++i)
if (Convert.ToBoolean((SpellSchoolMask)(1 << i) & schoolMask))
if (_schoolLockouts[i] > now)
return true;
return false;
}
public bool ConsumeCharge(uint chargeCategoryId)
{
if (!CliDB.SpellCategoryStorage.ContainsKey(chargeCategoryId))
return false;
int chargeRecovery = GetChargeRecoveryTime(chargeCategoryId);
if (chargeRecovery > 0 && GetMaxCharges(chargeCategoryId) > 0)
{
DateTime recoveryStart;
var charges = _categoryCharges.LookupByKey(chargeCategoryId);
if (charges.Empty())
recoveryStart = DateTime.Now;
else
recoveryStart = charges.LastOrDefault().RechargeEnd;
charges.Add(new ChargeEntry(recoveryStart, TimeSpan.FromMilliseconds(chargeRecovery)));
return true;
}
return false;
}
public void RestoreCharge(uint chargeCategoryId)
{
var chargeList = _categoryCharges.LookupByKey(chargeCategoryId);
if (!chargeList.Empty())
{
chargeList.RemoveAt(0);
Player player = GetPlayerOwner();
if (player)
{
SetSpellCharges setSpellCharges = new SetSpellCharges();
setSpellCharges.Category = chargeCategoryId;
if (!chargeList.Empty())
setSpellCharges.NextRecoveryTime = (uint)(chargeList.FirstOrDefault().RechargeEnd - DateTime.Now).TotalMilliseconds;
setSpellCharges.ConsumedCharges = (byte)chargeList.Count;
setSpellCharges.IsPet = player != _owner;
player.SendPacket(setSpellCharges);
}
}
}
public void ResetCharges(uint chargeCategoryId)
{
var chargeList = _categoryCharges.LookupByKey(chargeCategoryId);
if (!chargeList.Empty())
{
_categoryCharges.Remove(chargeCategoryId);
Player player = GetPlayerOwner();
if (player)
{
ClearSpellCharges clearSpellCharges = new ClearSpellCharges();
clearSpellCharges.IsPet = _owner != player;
clearSpellCharges.Category = chargeCategoryId;
player.SendPacket(clearSpellCharges);
}
}
}
public void ResetAllCharges()
{
_categoryCharges.Clear();
Player player = GetPlayerOwner();
if (player)
{
ClearAllSpellCharges clearAllSpellCharges = new ClearAllSpellCharges();
clearAllSpellCharges.IsPet = _owner != player;
player.SendPacket(clearAllSpellCharges);
}
}
public bool HasCharge(uint chargeCategoryId)
{
if (!CliDB.SpellCategoryStorage.ContainsKey(chargeCategoryId))
return true;
// Check if the spell is currently using charges (untalented warlock Dark Soul)
int maxCharges = GetMaxCharges(chargeCategoryId);
if (maxCharges <= 0)
return true;
var chargeList = _categoryCharges.LookupByKey(chargeCategoryId);
return chargeList.Empty() || chargeList.Count < maxCharges;
}
public int GetMaxCharges(uint chargeCategoryId)
{
SpellCategoryRecord chargeCategoryEntry = CliDB.SpellCategoryStorage.LookupByKey(chargeCategoryId);
if (chargeCategoryEntry == null)
return 0;
uint charges = chargeCategoryEntry.MaxCharges;
charges += (uint)_owner.GetTotalAuraModifierByMiscValue(AuraType.ModMaxCharges, (int)chargeCategoryId);
return (int)charges;
}
public int GetChargeRecoveryTime(uint chargeCategoryId)
{
SpellCategoryRecord chargeCategoryEntry = CliDB.SpellCategoryStorage.LookupByKey(chargeCategoryId);
if (chargeCategoryEntry == null)
return 0;
int recoveryTime = chargeCategoryEntry.ChargeRecoveryTime;
recoveryTime += _owner.GetTotalAuraModifierByMiscValue(AuraType.ChargeRecoveryMod, (int)chargeCategoryId);
float recoveryTimeF = recoveryTime;
recoveryTimeF *= _owner.GetTotalAuraMultiplierByMiscValue(AuraType.ChargeRecoveryMultiplier, (int)chargeCategoryId);
if (_owner.HasAuraType(AuraType.ChargeRecoveryAffectedByHaste))
recoveryTimeF *= _owner.GetFloatValue(UnitFields.ModCastHaste);
if (_owner.HasAuraType(AuraType.ChargeRecoveryAffectedByHasteRegen))
recoveryTimeF *= _owner.GetFloatValue(UnitFields.ModHasteRegen);
return (int)Math.Floor(recoveryTimeF);
}
public bool HasGlobalCooldown(SpellInfo spellInfo)
{
return _globalCooldowns.ContainsKey(spellInfo.StartRecoveryCategory) && _globalCooldowns[spellInfo.StartRecoveryCategory] > DateTime.Now;
}
public void AddGlobalCooldown(SpellInfo spellInfo, uint duration)
{
_globalCooldowns[spellInfo.StartRecoveryCategory] = DateTime.Now + TimeSpan.FromMilliseconds(duration);
}
public void CancelGlobalCooldown(SpellInfo spellInfo)
{
_globalCooldowns[spellInfo.StartRecoveryCategory] = new DateTime();
}
public Player GetPlayerOwner()
{
return _owner.GetCharmerOrOwnerPlayerOrPlayerItself();
}
public void SendClearCooldowns(List<uint> cooldowns)
{
Player playerOwner = GetPlayerOwner();
if (playerOwner)
{
ClearCooldowns clearCooldowns = new ClearCooldowns();
clearCooldowns.IsPet = _owner != playerOwner;
clearCooldowns.SpellID = cooldowns;
playerOwner.SendPacket(clearCooldowns);
}
}
void GetCooldownDurations(SpellInfo spellInfo, uint itemId, ref uint categoryId)
{
int notUsed = 0;
GetCooldownDurations(spellInfo, itemId, ref notUsed, ref categoryId, ref notUsed);
}
void GetCooldownDurations(SpellInfo spellInfo, uint itemId, ref int cooldown, ref uint categoryId, ref int categoryCooldown)
{
int tmpCooldown = -1;
uint tmpCategoryId = 0;
int tmpCategoryCooldown = -1;
// cooldown information stored in ItemEffect.db2, overriding normal cooldown and category
if (itemId != 0)
{
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemId);
if (proto != null)
{
foreach (ItemEffectRecord itemEffect in proto.Effects)
{
if (itemEffect.SpellID == spellInfo.Id)
{
tmpCooldown = itemEffect.Cooldown;
tmpCategoryId = itemEffect.Category;
tmpCategoryCooldown = itemEffect.CategoryCooldown;
break;
}
}
}
}
// if no cooldown found above then base at DBC data
if (tmpCooldown < 0 && tmpCategoryCooldown < 0)
{
tmpCooldown = (int)spellInfo.RecoveryTime;
tmpCategoryId = spellInfo.GetCategory();
tmpCategoryCooldown = (int)spellInfo.CategoryRecoveryTime;
}
cooldown = tmpCooldown;
categoryId = tmpCategoryId;
categoryCooldown = tmpCategoryCooldown;
}
public void SaveCooldownStateBeforeDuel()
{
_spellCooldownsBeforeDuel = _spellCooldowns;
}
public void RestoreCooldownStateAfterDuel()
{
Player player = _owner.ToPlayer();
if (player)
{
// add all profession CDs created while in duel (if any)
foreach (var c in _spellCooldowns)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(c.Key);
if (spellInfo.RecoveryTime > 10 * Time.Minute * Time.InMilliseconds || spellInfo.CategoryRecoveryTime > 10 * Time.Minute * Time.InMilliseconds)
_spellCooldownsBeforeDuel[c.Key] = _spellCooldowns[c.Key];
}
// check for spell with onHold active before and during the duel
foreach (var pair in _spellCooldownsBeforeDuel)
{
if (!pair.Value.OnHold && _spellCooldowns.ContainsKey(pair.Key) && !_spellCooldowns[pair.Key].OnHold)
_spellCooldowns[pair.Key] = _spellCooldownsBeforeDuel[pair.Key];
}
// update the client: restore old cooldowns
SpellCooldownPkt spellCooldown = new SpellCooldownPkt();
spellCooldown.Caster = _owner.GetGUID();
spellCooldown.Flags = SpellCooldownFlags.IncludeEventCooldowns;
foreach (var c in _spellCooldowns)
{
DateTime now = DateTime.Now;
uint cooldownDuration = c.Value.CooldownEnd > now ? (uint)(c.Value.CooldownEnd - now).TotalMilliseconds : 0;
// cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
if (cooldownDuration <= 0 || cooldownDuration > 10 * Time.Minute * Time.InMilliseconds || c.Value.OnHold)
continue;
spellCooldown.SpellCooldowns.Add(new SpellCooldownStruct(c.Key, cooldownDuration));
}
player.SendPacket(spellCooldown);
}
}
Unit _owner;
Dictionary<uint, CooldownEntry> _spellCooldowns = new Dictionary<uint, CooldownEntry>();
Dictionary<uint, CooldownEntry> _spellCooldownsBeforeDuel = new Dictionary<uint, CooldownEntry>();
Dictionary<uint, CooldownEntry> _categoryCooldowns = new Dictionary<uint, CooldownEntry>();
DateTime[] _schoolLockouts = new DateTime[(int)SpellSchools.Max];
MultiMap<uint, ChargeEntry> _categoryCharges = new MultiMap<uint, ChargeEntry>();
Dictionary<uint, DateTime> _globalCooldowns = new Dictionary<uint, DateTime>();
public class CooldownEntry
{
public uint SpellId;
public DateTime CooldownEnd;
public uint ItemId;
public uint CategoryId;
public DateTime CategoryEnd;
public bool OnHold;
}
struct ChargeEntry
{
public ChargeEntry(DateTime startTime, TimeSpan rechargeTime)
{
RechargeStart = startTime;
RechargeEnd = startTime + rechargeTime;
}
public ChargeEntry(DateTime startTime, DateTime endTime)
{
RechargeStart = startTime;
RechargeEnd = endTime;
}
public DateTime RechargeStart;
public DateTime RechargeEnd;
}
class CooldownDurations
{
public int Cooldown = -1;
public uint CategoryId = 0;
public int CategoryCooldown = -1;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff