3634bc7133
Port From (https://github.com/TrinityCore/TrinityCore/commit/ad4e63bae145ae49b584ab2fc621660430cec0d3)
602 lines
24 KiB
C#
602 lines
24 KiB
C#
/*
|
|
* Copyright (C) 2012-2019 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.Network.Packets;
|
|
using Game.Spells;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Game.Entities
|
|
{
|
|
public partial class Player
|
|
{
|
|
public void InitTalentForLevel()
|
|
{
|
|
uint level = getLevel();
|
|
// talents base at level diff (talents = level - 9 but some can be used already)
|
|
if (level < PlayerConst.MinSpecializationLevel)
|
|
ResetTalentSpecialization();
|
|
|
|
uint talentTiers = Global.DB2Mgr.GetNumTalentsAtLevel(level, GetClass());
|
|
if (level < 15)
|
|
{
|
|
// Remove all talent points
|
|
ResetTalents(true);
|
|
}
|
|
else
|
|
{
|
|
if (!GetSession().HasPermission(RBACPermissions.SkipCheckMoreTalentsThanAllowed))
|
|
{
|
|
for (uint t = talentTiers; t < PlayerConst.MaxTalentTiers; ++t)
|
|
for (uint c = 0; c < PlayerConst.MaxTalentColumns; ++c)
|
|
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(GetClass(), t, c))
|
|
RemoveTalent(talent);
|
|
}
|
|
}
|
|
|
|
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.MaxTalentTiers), talentTiers);
|
|
|
|
if (!GetSession().PlayerLoading())
|
|
SendTalentsInfoData(); // update at client
|
|
}
|
|
|
|
public bool AddTalent(TalentRecord talent, byte spec, bool learning)
|
|
{
|
|
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talent.SpellID);
|
|
if (spellInfo == null)
|
|
{
|
|
Log.outError(LogFilter.Spells, "Player.AddTalent: Spell (ID: {0}) does not exist.", talent.SpellID);
|
|
return false;
|
|
}
|
|
|
|
if (!Global.SpellMgr.IsSpellValid(spellInfo, this, false))
|
|
{
|
|
Log.outError(LogFilter.Spells, "Player.AddTalent: Spell (ID: {0}) is invalid", talent.SpellID);
|
|
return false;
|
|
}
|
|
|
|
if (talent.OverridesSpellID != 0)
|
|
AddOverrideSpell(talent.OverridesSpellID, talent.SpellID);
|
|
|
|
if (GetTalentMap(spec).ContainsKey(talent.Id))
|
|
GetTalentMap(spec)[talent.Id] = PlayerSpellState.Unchanged;
|
|
else
|
|
GetTalentMap(spec)[talent.Id] = learning ? PlayerSpellState.New : PlayerSpellState.Unchanged;
|
|
|
|
return true;
|
|
}
|
|
|
|
public void RemoveTalent(TalentRecord talent)
|
|
{
|
|
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talent.SpellID);
|
|
if (spellInfo == null)
|
|
return;
|
|
|
|
RemoveSpell(talent.SpellID, true);
|
|
|
|
// search for spells that the talent teaches and unlearn them
|
|
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
|
|
if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell)
|
|
RemoveSpell(effect.TriggerSpell, true);
|
|
|
|
if (talent.OverridesSpellID != 0)
|
|
RemoveOverrideSpell(talent.OverridesSpellID, talent.SpellID);
|
|
|
|
var talentMap = GetTalentMap(GetActiveTalentGroup());
|
|
// if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
|
|
if (talentMap.ContainsKey(talent.Id))
|
|
talentMap[talent.Id] = PlayerSpellState.Removed;
|
|
}
|
|
|
|
public TalentLearnResult LearnTalent(uint talentId, ref int spellOnCooldown)
|
|
{
|
|
if (IsInCombat())
|
|
return TalentLearnResult.FailedAffectingCombat;
|
|
|
|
if (IsDead())
|
|
return TalentLearnResult.FailedCantDoThatRightNow;
|
|
|
|
if (GetPrimarySpecialization() == 0)
|
|
return TalentLearnResult.FailedNoPrimaryTreeSelected;
|
|
|
|
TalentRecord talentInfo = CliDB.TalentStorage.LookupByKey(talentId);
|
|
if (talentInfo == null)
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
if (talentInfo.SpecID != 0 && talentInfo.SpecID != GetPrimarySpecialization())
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
// prevent learn talent for different class (cheating)
|
|
if (talentInfo.ClassID != (byte)GetClass())
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
// check if we have enough talent points
|
|
if (talentInfo.TierID >= m_activePlayerData.MaxTalentTiers)
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
// TODO: prevent changing talents that are on cooldown
|
|
|
|
// Check if there is a different talent for us to learn in selected slot
|
|
// Example situation:
|
|
// Warrior talent row 2 slot 0
|
|
// Talent.dbc has an entry for each specialization
|
|
// but only 2 out of 3 have SpecID != 0
|
|
// We need to make sure that if player is in one of these defined specs he will not learn the other choice
|
|
TalentRecord bestSlotMatch = null;
|
|
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(GetClass(), talentInfo.TierID, talentInfo.ColumnIndex))
|
|
{
|
|
if (talent.SpecID == 0)
|
|
bestSlotMatch = talent;
|
|
|
|
else if (talent.SpecID == GetPrimarySpecialization())
|
|
{
|
|
bestSlotMatch = talent;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (talentInfo != bestSlotMatch)
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
// Check if player doesn't have any talent in current tier
|
|
for (uint c = 0; c < PlayerConst.MaxTalentColumns; ++c)
|
|
{
|
|
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(GetClass(), talentInfo.TierID, c))
|
|
{
|
|
if (talent.SpecID != 0 && talent.SpecID != GetPrimarySpecialization())
|
|
continue;
|
|
|
|
if (!HasTalent(talent.Id, GetActiveTalentGroup()))
|
|
continue;
|
|
|
|
if (!HasPlayerFlag(PlayerFlags.Resting) && HasUnitFlag2(UnitFlags2.AllowChangingTalents))
|
|
return TalentLearnResult.FailedRestArea;
|
|
|
|
if (GetSpellHistory().HasCooldown(talent.SpellID))
|
|
{
|
|
spellOnCooldown = (int)talent.SpellID;
|
|
return TalentLearnResult.FailedCantRemoveTalent;
|
|
}
|
|
|
|
RemoveTalent(talent);
|
|
}
|
|
}
|
|
|
|
// spell not set in talent.dbc
|
|
uint spellid = talentInfo.SpellID;
|
|
if (spellid == 0)
|
|
{
|
|
Log.outError(LogFilter.Player, "Player.LearnTalent: Talent.dbc has no spellInfo for talent: {0} (spell id = 0)", talentId);
|
|
return TalentLearnResult.FailedUnknown;
|
|
}
|
|
|
|
// already known
|
|
if (HasTalent(talentId, GetActiveTalentGroup()) || HasSpell(spellid))
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
if (!AddTalent(talentInfo, GetActiveTalentGroup(), true))
|
|
return TalentLearnResult.FailedUnknown;
|
|
|
|
LearnSpell(spellid, false);
|
|
|
|
Log.outDebug(LogFilter.Misc, "Player.LearnTalent: TalentID: {0} Spell: {1} Group: {2}", talentId, spellid, GetActiveTalentGroup());
|
|
|
|
return TalentLearnResult.LearnOk;
|
|
}
|
|
|
|
public void ResetTalentSpecialization()
|
|
{
|
|
// Reset only talents that have different spells for each spec
|
|
Class class_ = GetClass();
|
|
for (uint t = 0; t < PlayerConst.MaxTalentTiers; ++t)
|
|
{
|
|
for (uint c = 0; c < PlayerConst.MaxTalentColumns; ++c)
|
|
{
|
|
if (Global.DB2Mgr.GetTalentsByPosition(class_, t, c).Count > 1)
|
|
{
|
|
foreach (TalentRecord talent in Global.DB2Mgr.GetTalentsByPosition(class_, t, c))
|
|
RemoveTalent(talent);
|
|
}
|
|
}
|
|
}
|
|
|
|
RemoveSpecializationSpells();
|
|
|
|
ChrSpecializationRecord defaultSpec = Global.DB2Mgr.GetDefaultChrSpecializationForClass(GetClass());
|
|
SetPrimarySpecialization(defaultSpec.Id);
|
|
SetActiveTalentGroup(defaultSpec.OrderIndex);
|
|
|
|
LearnSpecializationSpells();
|
|
|
|
SendTalentsInfoData();
|
|
UpdateItemSetAuras(false);
|
|
}
|
|
|
|
bool HasTalent(uint talentId, byte group)
|
|
{
|
|
return GetTalentMap(group).ContainsKey(talentId) && GetTalentMap(group)[talentId] != PlayerSpellState.Removed;
|
|
}
|
|
|
|
uint GetTalentResetCost() { return _specializationInfo.ResetTalentsCost; }
|
|
void SetTalentResetCost(uint cost) { _specializationInfo.ResetTalentsCost = cost; }
|
|
long GetTalentResetTime() { return _specializationInfo.ResetTalentsTime; }
|
|
void SetTalentResetTime(long time_) { _specializationInfo.ResetTalentsTime = time_; }
|
|
public uint GetPrimarySpecialization() { return m_playerData.CurrentSpecID; }
|
|
void SetPrimarySpecialization(uint spec) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.CurrentSpecID), spec); }
|
|
public byte GetActiveTalentGroup() { return _specializationInfo.ActiveGroup; }
|
|
void SetActiveTalentGroup(byte group) { _specializationInfo.ActiveGroup = group; }
|
|
|
|
// Loot Spec
|
|
public void SetLootSpecId(uint id) { SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.LootSpecID), (ushort)id); }
|
|
public uint GetLootSpecId() { return m_activePlayerData.LootSpecID; }
|
|
|
|
public uint GetDefaultSpecId()
|
|
{
|
|
return Global.DB2Mgr.GetDefaultChrSpecializationForClass(GetClass()).Id;
|
|
}
|
|
|
|
public void ActivateTalentGroup(ChrSpecializationRecord spec)
|
|
{
|
|
if (GetActiveTalentGroup() == spec.OrderIndex)
|
|
return;
|
|
|
|
if (IsNonMeleeSpellCast(false))
|
|
InterruptNonMeleeSpells(false);
|
|
|
|
SQLTransaction trans = new SQLTransaction();
|
|
_SaveActions(trans);
|
|
DB.Characters.CommitTransaction(trans);
|
|
|
|
// TO-DO: We need more research to know what happens with warlock's reagent
|
|
Pet pet = GetPet();
|
|
if (pet)
|
|
RemovePet(pet, PetSaveMode.NotInSlot);
|
|
|
|
ClearAllReactives();
|
|
UnsummonAllTotems();
|
|
ExitVehicle();
|
|
RemoveAllControlled();
|
|
|
|
// remove single target auras at other targets
|
|
var scAuras = GetSingleCastAuras();
|
|
foreach (var aura in scAuras)
|
|
{
|
|
if (aura.GetUnitOwner() != this)
|
|
aura.Remove();
|
|
}
|
|
|
|
// Let client clear his current Actions
|
|
SendActionButtons(2);
|
|
foreach (var talentInfo in CliDB.TalentStorage.Values)
|
|
{
|
|
// unlearn only talents for character class
|
|
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
|
|
// to prevent unexpected lost normal learned spell skip another class talents
|
|
if (talentInfo.ClassID != (int)GetClass())
|
|
continue;
|
|
|
|
if (talentInfo.SpellID == 0)
|
|
continue;
|
|
|
|
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talentInfo.SpellID);
|
|
if (spellInfo == null)
|
|
continue;
|
|
|
|
RemoveSpell(talentInfo.SpellID, true);
|
|
|
|
// search for spells that the talent teaches and unlearn them
|
|
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
|
|
if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell)
|
|
RemoveSpell(effect.TriggerSpell, true);
|
|
|
|
if (talentInfo.OverridesSpellID != 0)
|
|
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
|
|
}
|
|
|
|
foreach (var talentInfo in CliDB.PvpTalentStorage.Values)
|
|
{
|
|
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talentInfo.SpellID);
|
|
if (spellInfo == null)
|
|
continue;
|
|
|
|
RemoveSpell(talentInfo.SpellID, true);
|
|
|
|
// search for spells that the talent teaches and unlearn them
|
|
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
|
|
if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell)
|
|
RemoveSpell(effect.TriggerSpell, true);
|
|
|
|
if (talentInfo.OverridesSpellID != 0)
|
|
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
|
|
}
|
|
|
|
// Remove spec specific spells
|
|
RemoveSpecializationSpells();
|
|
|
|
foreach (uint glyphId in GetGlyphs(GetActiveTalentGroup()))
|
|
RemoveAurasDueToSpell(CliDB.GlyphPropertiesStorage.LookupByKey(glyphId).SpellID);
|
|
|
|
SetActiveTalentGroup(spec.OrderIndex);
|
|
SetPrimarySpecialization(spec.Id);
|
|
|
|
foreach (var talentInfo in CliDB.TalentStorage.Values)
|
|
{
|
|
// learn only talents for character class
|
|
if (talentInfo.ClassID != (int)GetClass())
|
|
continue;
|
|
|
|
if (talentInfo.SpellID == 0)
|
|
continue;
|
|
|
|
if (HasTalent(talentInfo.Id, GetActiveTalentGroup()))
|
|
{
|
|
LearnSpell(talentInfo.SpellID, false); // add the talent to the PlayerSpellMap
|
|
if (talentInfo.OverridesSpellID != 0)
|
|
AddOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
|
|
}
|
|
}
|
|
|
|
for (byte slot = 0; slot < PlayerConst.MaxPvpTalentSlots; ++slot)
|
|
{
|
|
PvpTalentRecord talentInfo = CliDB.PvpTalentStorage.LookupByKey(GetPvpTalentMap(GetActiveTalentGroup())[slot]);
|
|
if (talentInfo == null)
|
|
continue;
|
|
|
|
if (talentInfo.SpellID == 0)
|
|
continue;
|
|
|
|
AddPvpTalent(talentInfo, GetActiveTalentGroup(), slot);
|
|
}
|
|
|
|
LearnSpecializationSpells();
|
|
|
|
if (CanUseMastery())
|
|
{
|
|
for (uint i = 0; i < PlayerConst.MaxMasterySpells; ++i)
|
|
{
|
|
uint mastery = spec.MasterySpellID[i];
|
|
if (mastery != 0)
|
|
LearnSpell(mastery, false);
|
|
}
|
|
}
|
|
|
|
InitTalentForLevel();
|
|
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACTIONS_SPEC);
|
|
stmt.AddValue(0, GetGUID().GetCounter());
|
|
stmt.AddValue(1, GetActiveTalentGroup());
|
|
|
|
WorldSession mySess = GetSession();
|
|
mySess.GetQueryProcessor().AddQuery(DB.Characters.AsyncQuery(stmt).WithCallback(result =>
|
|
{
|
|
// in case player logs out before db response (player would be deleted in that case)
|
|
Player thisPlayer = mySess.GetPlayer();
|
|
if (thisPlayer != null)
|
|
thisPlayer.LoadActions(result);
|
|
}));
|
|
|
|
UpdateDisplayPower();
|
|
PowerType pw = GetPowerType();
|
|
if (pw != PowerType.Mana)
|
|
SetPower(PowerType.Mana, 0); // Mana must be 0 even if it isn't the active power type.
|
|
|
|
SetPower(pw, 0);
|
|
UpdateItemSetAuras(false);
|
|
|
|
// update visible transmog
|
|
for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i)
|
|
{
|
|
Item equippedItem = GetItemByPos(InventorySlots.Bag0, i);
|
|
if (equippedItem)
|
|
SetVisibleItemSlot(i, equippedItem);
|
|
}
|
|
|
|
foreach (uint glyphId in GetGlyphs(spec.OrderIndex))
|
|
CastSpell(this, CliDB.GlyphPropertiesStorage.LookupByKey(glyphId).SpellID, true);
|
|
|
|
ActiveGlyphs activeGlyphs = new ActiveGlyphs();
|
|
foreach (uint glyphId in GetGlyphs(spec.OrderIndex))
|
|
{
|
|
List<uint> bindableSpells = Global.DB2Mgr.GetGlyphBindableSpells(glyphId);
|
|
foreach (uint bindableSpell in bindableSpells)
|
|
if (HasSpell(bindableSpell) && !m_overrideSpells.ContainsKey(bindableSpell))
|
|
activeGlyphs.Glyphs.Add(new GlyphBinding(bindableSpell, (ushort)glyphId));
|
|
}
|
|
|
|
activeGlyphs.IsFullUpdate = true;
|
|
SendPacket(activeGlyphs);
|
|
|
|
var shapeshiftAuras = GetAuraEffectsByType(AuraType.ModShapeshift);
|
|
foreach (AuraEffect aurEff in shapeshiftAuras)
|
|
{
|
|
aurEff.HandleShapeshiftBoosts(this, false);
|
|
aurEff.HandleShapeshiftBoosts(this, true);
|
|
}
|
|
}
|
|
|
|
public Dictionary<uint, PlayerSpellState> GetTalentMap(uint spec) { return _specializationInfo.Talents[spec]; }
|
|
public List<uint> GetGlyphs(byte spec) { return _specializationInfo.Glyphs[spec]; }
|
|
|
|
public uint GetNextResetTalentsCost()
|
|
{
|
|
// The first time reset costs 1 gold
|
|
if (GetTalentResetCost() < 1 * MoneyConstants.Gold)
|
|
return 1 * MoneyConstants.Gold;
|
|
// then 5 gold
|
|
else if (GetTalentResetCost() < 5 * MoneyConstants.Gold)
|
|
return 5 * MoneyConstants.Gold;
|
|
// After that it increases in increments of 5 gold
|
|
else if (GetTalentResetCost() < 10 * MoneyConstants.Gold)
|
|
return 10 * MoneyConstants.Gold;
|
|
else
|
|
{
|
|
ulong months = (ulong)(Global.WorldMgr.GetGameTime() - GetTalentResetTime()) / Time.Month;
|
|
if (months > 0)
|
|
{
|
|
// This cost will be reduced by a rate of 5 gold per month
|
|
uint new_cost = (uint)(GetTalentResetCost() - 5 * MoneyConstants.Gold * months);
|
|
// to a minimum of 10 gold.
|
|
return new_cost < 10 * MoneyConstants.Gold ? 10 * MoneyConstants.Gold : new_cost;
|
|
}
|
|
else
|
|
{
|
|
// After that it increases in increments of 5 gold
|
|
uint new_cost = GetTalentResetCost() + 5 * MoneyConstants.Gold;
|
|
// until it hits a cap of 50 gold.
|
|
if (new_cost > 50 * MoneyConstants.Gold)
|
|
new_cost = 50 * MoneyConstants.Gold;
|
|
return new_cost;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ResetTalents(bool noCost = false)
|
|
{
|
|
Global.ScriptMgr.OnPlayerTalentsReset(this, noCost);
|
|
|
|
// not need after this call
|
|
if (HasAtLoginFlag(AtLoginFlags.ResetTalents))
|
|
RemoveAtLoginFlag(AtLoginFlags.ResetTalents, true);
|
|
|
|
uint cost = 0;
|
|
if (!noCost && !WorldConfig.GetBoolValue(WorldCfg.NoResetTalentCost))
|
|
{
|
|
cost = GetNextResetTalentsCost();
|
|
|
|
if (!HasEnoughMoney(cost))
|
|
{
|
|
SendBuyError(BuyResult.NotEnoughtMoney, null, 0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
RemovePet(null, PetSaveMode.NotInSlot, true);
|
|
|
|
foreach (var talentInfo in CliDB.TalentStorage.Values)
|
|
{
|
|
// unlearn only talents for character class
|
|
// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
|
|
// to prevent unexpected lost normal learned spell skip another class talents
|
|
if (talentInfo.ClassID != (uint)GetClass())
|
|
continue;
|
|
|
|
// skip non-existant talent ranks
|
|
if (talentInfo.SpellID == 0)
|
|
continue;
|
|
|
|
RemoveTalent(talentInfo);
|
|
}
|
|
|
|
SQLTransaction trans = new SQLTransaction();
|
|
_SaveTalents(trans);
|
|
_SaveSpells(trans);
|
|
DB.Characters.CommitTransaction(trans);
|
|
|
|
if (!noCost)
|
|
{
|
|
ModifyMoney(-cost);
|
|
UpdateCriteria(CriteriaTypes.GoldSpentForTalents, cost);
|
|
UpdateCriteria(CriteriaTypes.NumberOfTalentResets, 1);
|
|
|
|
SetTalentResetCost(cost);
|
|
SetTalentResetTime(Time.UnixTime);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void SendTalentsInfoData()
|
|
{
|
|
UpdateTalentData packet = new UpdateTalentData();
|
|
packet.Info.PrimarySpecialization = GetPrimarySpecialization();
|
|
packet.Info.ActiveGroup = GetActiveTalentGroup();
|
|
|
|
for (byte i = 0; i < PlayerConst.MaxSpecializations; ++i)
|
|
{
|
|
ChrSpecializationRecord spec = Global.DB2Mgr.GetChrSpecializationByIndex(GetClass(), i);
|
|
if (spec == null)
|
|
continue;
|
|
|
|
var talents = GetTalentMap(i);
|
|
var pvpTalents = GetPvpTalentMap(i);
|
|
|
|
UpdateTalentData.TalentGroupInfo groupInfoPkt = new UpdateTalentData.TalentGroupInfo();
|
|
groupInfoPkt.SpecID = spec.Id;
|
|
|
|
foreach (var pair in talents)
|
|
{
|
|
if (pair.Value == PlayerSpellState.Removed)
|
|
continue;
|
|
|
|
TalentRecord talentInfo = CliDB.TalentStorage.LookupByKey(pair.Key);
|
|
if (talentInfo == null)
|
|
{
|
|
Log.outError(LogFilter.Player, "Player {0} has unknown talent id: {1}", GetName(), pair.Key);
|
|
continue;
|
|
}
|
|
|
|
SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(talentInfo.SpellID);
|
|
if (spellEntry == null)
|
|
{
|
|
Log.outError(LogFilter.Player, "Player {0} has unknown talent spell: {1}", GetName(), talentInfo.SpellID);
|
|
continue;
|
|
}
|
|
|
|
groupInfoPkt.TalentIDs.Add((ushort)pair.Key);
|
|
}
|
|
|
|
for (byte slot = 0; slot < PlayerConst.MaxPvpTalentSlots; ++slot)
|
|
{
|
|
if (pvpTalents[slot] == 0)
|
|
continue;
|
|
|
|
PvpTalentRecord talentInfo = CliDB.PvpTalentStorage.LookupByKey(pvpTalents[slot]);
|
|
if (talentInfo == null)
|
|
{
|
|
Log.outError(LogFilter.Player, $"Player.SendTalentsInfoData: Player '{GetName()}' ({GetGUID().ToString()}) has unknown pvp talent id: {pvpTalents[slot]}");
|
|
continue;
|
|
}
|
|
|
|
SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(talentInfo.SpellID);
|
|
if (spellEntry == null)
|
|
{
|
|
Log.outError(LogFilter.Player, $"Player.SendTalentsInfoData: Player '{GetName()}' ({GetGUID().ToString()}) has unknown pvp talent spell: {talentInfo.SpellID}");
|
|
continue;
|
|
}
|
|
|
|
groupInfoPkt.PvPTalentIDs.Add((ushort)pvpTalents[slot]);
|
|
}
|
|
|
|
packet.Info.TalentGroups.Add(groupInfoPkt);
|
|
}
|
|
|
|
SendPacket(packet);
|
|
}
|
|
|
|
public void SendRespecWipeConfirm(ObjectGuid guid, uint cost)
|
|
{
|
|
RespecWipeConfirm respecWipeConfirm = new RespecWipeConfirm();
|
|
respecWipeConfirm.RespecMaster = guid;
|
|
respecWipeConfirm.Cost = cost;
|
|
respecWipeConfirm.RespecType = SpecResetType.Talents;
|
|
SendPacket(respecWipeConfirm);
|
|
}
|
|
}
|
|
}
|