Ported .Net Core commits:

hondacrx:
- Initial commit: Switch to .Net Core 2.0
- Fix build and removed not needed files
Fabi:
- Updated solution platforms.
- Changed folder structure.
- Change library target framework to netstandard2.0.
- Updated solution platforms again...
- Removed windows specific kernel32 function usage (Ctrl-C handler).
This commit is contained in:
Fabian
2017-10-26 17:23:44 +02:00
parent 227702e19c
commit a3dc7b3f48
844 changed files with 26064 additions and 1824 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
}
}
@@ -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;
}
}
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