Core/Player: Implement PvP Talents

This commit is contained in:
hondacrx
2018-03-06 11:10:51 -05:00
parent 42fc86c400
commit c842332393
26 changed files with 631 additions and 68 deletions
@@ -24,6 +24,8 @@ namespace Framework.Constants
public const int MaxTalentTiers = 7;
public const int MaxTalentColumns = 3;
public const int MaxTalentRank = 5;
public const int MaxPvpTalentTiers = 6;
public const int MaxPvpTalentColumns = 3;
public const int MinSpecializationLevel = 10;
public const int MaxSpecializations = 4;
public const int MaxMasterySpells = 2;
@@ -67,6 +69,7 @@ namespace Framework.Constants
public const byte MaxHonorLevel = 50;
public const byte LevelMinHonor = 110;
public const uint SpellPvpRulesEnabled = 134735;
}
public struct MoneyConstants
@@ -140,7 +140,7 @@ namespace Framework.Constants
ModRegenDuringCombat = 116,
ModMechanicResistance = 117,
ModHealingPct = 118,
Unk119 = 119, // Old SharePetTracking
PvpTalents = 119,
Untrackable = 120,
Empathy = 121,
ModOffhandDamagePct = 122,
@@ -137,6 +137,7 @@ namespace Framework.Database
PrepareStatement(CharStatements.SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyphId FROM character_glyphs WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_TALENTS, "SELECT talentId, talentGroup FROM character_talent WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_PVP_TALENTS, "SELECT talentId, talentGroup FROM character_pvp_talent WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1");
@@ -584,6 +585,7 @@ namespace Framework.Database
PrepareStatement(CharStatements.DEL_GUILD_BANK_EVENTLOG_BY_PLAYER, "DELETE FROM guild_bank_eventlog WHERE PlayerGuid = ?");
PrepareStatement(CharStatements.DEL_CHAR_GLYPHS, "DELETE FROM character_glyphs WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_TALENT, "DELETE FROM character_talent WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_PVP_TALENT, "DELETE FROM character_pvp_talent WHERE guid = ?");
PrepareStatement(CharStatements.DEL_CHAR_SKILLS, "DELETE FROM character_skills WHERE guid = ?");
PrepareStatement(CharStatements.UPD_CHAR_MONEY, "UPDATE characters SET money = ? WHERE guid = ?");
PrepareStatement(CharStatements.INS_CHAR_ACTION, "INSERT INTO character_action (guid, spec, button, action, type) VALUES (?, ?, ?, ?, ?)");
@@ -616,6 +618,7 @@ namespace Framework.Database
PrepareStatement(CharStatements.DEL_PETITION_SIGNATURE_BY_OWNER, "DELETE FROM petition_sign WHERE ownerguid = ?");
PrepareStatement(CharStatements.INS_CHAR_GLYPHS, "INSERT INTO character_glyphs VALUES(?, ?, ?)");
PrepareStatement(CharStatements.INS_CHAR_TALENT, "INSERT INTO character_talent (guid, talentId, talentGroup) VALUES (?, ?, ?)");
PrepareStatement(CharStatements.INS_CHAR_PVP_TALENT, "INSERT INTO character_pvp_talent (guid, talentId, talentGroup) VALUES (?, ?, ?)");
PrepareStatement(CharStatements.UPD_CHAR_LIST_SLOT, "UPDATE characters SET slot = ? WHERE guid = ? AND account = ?");
PrepareStatement(CharStatements.INS_CHAR_FISHINGSTEPS, "INSERT INTO character_fishingsteps (guid, fishingSteps) VALUES (?, ?)");
PrepareStatement(CharStatements.DEL_CHAR_FISHINGSTEPS, "DELETE FROM character_fishingsteps WHERE guid = ?");
@@ -827,6 +830,7 @@ namespace Framework.Database
SEL_CHARACTER_BGDATA,
SEL_CHARACTER_GLYPHS,
SEL_CHARACTER_TALENTS,
SEL_CHARACTER_PVP_TALENTS,
SEL_CHARACTER_TRANSMOG_OUTFITS,
SEL_CHARACTER_SKILLS,
SEL_CHARACTER_RANDOMBG,
@@ -1207,6 +1211,7 @@ namespace Framework.Database
DEL_GUILD_BANK_EVENTLOG_BY_PLAYER,
DEL_CHAR_GLYPHS,
DEL_CHAR_TALENT,
DEL_CHAR_PVP_TALENT,
DEL_CHAR_SKILLS,
UPD_CHAR_MONEY,
INS_CHAR_ACTION,
@@ -1237,6 +1242,7 @@ namespace Framework.Database
DEL_PETITION_SIGNATURE_BY_OWNER,
INS_CHAR_GLYPHS,
INS_CHAR_TALENT,
INS_CHAR_PVP_TALENT,
UPD_CHAR_LIST_SLOT,
INS_CHAR_FISHINGSTEPS,
DEL_CHAR_FISHINGSTEPS,
@@ -693,6 +693,14 @@ namespace Framework.Database
// PvpReward.db2
PrepareStatement(HotfixStatements.SEL_PVP_REWARD, "SELECT ID, HonorLevel, Prestige, RewardPackID FROM pvp_reward ORDER BY ID DESC");
// PvpTalent.db2
PrepareStatement(HotfixStatements.SEL_PVP_TALENT, "SELECT ID, Description, SpellID, OverridesSpellID, ExtraSpellID, TierID, ColumnIndex, Flags, ClassID, " +
"SpecID, Role FROM pvp_talent ORDER BY ID DESC");
PrepareStatement(HotfixStatements.SEL_PVP_TALENT_LOCALE, "SELECT ID, Description_lang FROM pvp_talent_locale WHERE locale = ?");
// PvpTalentUnlock.db2
PrepareStatement(HotfixStatements.SEL_PVP_TALENT_UNLOCK, "SELECT ID, TierID, ColumnIndex, HonorLevel FROM pvp_talent_unlock ORDER BY ID DESC");
// QuestFactionReward.db2
PrepareStatement(HotfixStatements.SEL_QUEST_FACTION_REWARD, "SELECT ID, QuestRewFactionValue1, QuestRewFactionValue2, QuestRewFactionValue3, " +
"QuestRewFactionValue4, QuestRewFactionValue5, QuestRewFactionValue6, QuestRewFactionValue7, QuestRewFactionValue8, QuestRewFactionValue9, " +
@@ -1385,6 +1393,11 @@ namespace Framework.Database
SEL_PVP_REWARD,
SEL_PVP_TALENT,
SEL_PVP_TALENT_LOCALE,
SEL_PVP_TALENT_UNLOCK,
SEL_QUEST_FACTION_REWARD,
SEL_QUEST_MONEY_REWARD,
+4
View File
@@ -183,6 +183,8 @@ namespace Game.DataStorage
PrestigeLevelInfoStorage = DBReader.Read<PrestigeLevelInfoRecord>("PrestigeLevelInfo.db2", HotfixStatements.SEL_PRESTIGE_LEVEL_INFO, HotfixStatements.SEL_PRESTIGE_LEVEL_INFO_LOCALE);
PVPDifficultyStorage = DBReader.Read<PVPDifficultyRecord>("PVPDifficulty.db2", HotfixStatements.SEL_PVP_DIFFICULTY);
PvpRewardStorage = DBReader.Read<PvpRewardRecord>("PvpReward.db2", HotfixStatements.SEL_PVP_REWARD);
PvpTalentStorage = DBReader.Read<PvpTalentRecord>("PvpTalent.db2", HotfixStatements.SEL_PVP_TALENT, HotfixStatements.SEL_PVP_TALENT_LOCALE);
PvpTalentUnlockStorage = DBReader.Read<PvpTalentUnlockRecord>("PvpTalentUnlock.db2", HotfixStatements.SEL_PVP_TALENT_UNLOCK);
QuestFactionRewardStorage = DBReader.Read<QuestFactionRewardRecord>("QuestFactionReward.db2", HotfixStatements.SEL_QUEST_FACTION_REWARD);
QuestMoneyRewardStorage = DBReader.Read<QuestMoneyRewardRecord>("QuestMoneyReward.db2", HotfixStatements.SEL_QUEST_MONEY_REWARD);
QuestPackageItemStorage = DBReader.Read<QuestPackageItemRecord>("QuestPackageItem.db2", HotfixStatements.SEL_QUEST_PACKAGE_ITEM);
@@ -512,6 +514,8 @@ namespace Game.DataStorage
public static DB6Storage<PrestigeLevelInfoRecord> PrestigeLevelInfoStorage;
public static DB6Storage<PVPDifficultyRecord> PVPDifficultyStorage;
public static DB6Storage<PvpRewardRecord> PvpRewardStorage;
public static DB6Storage<PvpTalentRecord> PvpTalentStorage;
public static DB6Storage<PvpTalentUnlockRecord> PvpTalentUnlockStorage;
public static DB6Storage<QuestFactionRewardRecord> QuestFactionRewardStorage;
public static DB6Storage<QuestMoneyRewardRecord> QuestMoneyRewardStorage;
public static DB6Storage<QuestPackageItemRecord> QuestPackageItemStorage;
@@ -48,78 +48,30 @@ namespace Game.DataStorage
return result;
}
public ulong ReadUInt64(int numBits)
public ulong ReadUInt64(int bitWidth, int bitOffset)
{
ulong result = FastStruct<ulong>.ArrayToStructure(ref m_array[m_readOffset + (m_readPos >> 3)]) << (64 - numBits - (m_readPos & 7)) >> (64 - numBits);
m_readPos += numBits;
int bitsToRead = bitOffset & 7;
ulong result = FastStruct<ulong>.ArrayToStructure(ref m_array[m_readOffset + (m_readPos >> 3)]) << (64 - bitsToRead - bitWidth) >> (64 - bitWidth);
m_readPos += bitWidth;
return result;
}
public Value32 ReadValue32(int numBits)
public Value64 ReadValue64(int bitWidth, int bitOffset = 0, bool isSigned = false)
{
unsafe
{
ulong result = ReadUInt32(numBits);
return *(Value32*)&result;
}
}
ulong result = ReadUInt64(bitWidth, bitOffset);
if (isSigned)
{
ulong mask = 1ul << (bitWidth - 1);
result = (result ^ mask) - mask;
}
public Value64 ReadValue64(int numBits)
{
unsafe
{
ulong result = ReadUInt64(numBits);
return *(Value64*)&result;
}
}
}
public struct Value32
{
unsafe fixed byte Value[4];
public T GetValue<T>() where T : struct
{
unsafe
{
fixed (byte* ptr = Value)
return FastStruct<T>.ArrayToStructure(ref ptr[0]);
}
}
public byte[] GetBytes(int bitSize)
{
if (((bitSize + 7) / 8) != NextPow2((bitSize + 7) / 8))
{
}
byte[] data = new byte[NextPow2((bitSize + 7) / 8)];
unsafe
{
fixed (byte* ptr = Value)
{
for (var i = 0; i < data.Length; ++i)
data[i] = ptr[i];
}
}
return data;
}
private int NextPow2(int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return Math.Max(v, 1);
}
}
public struct Value64
{
unsafe fixed byte Value[8];
@@ -135,9 +87,6 @@ namespace Game.DataStorage
public byte[] GetBytes(int bitSize)
{
var val = ((bitSize + 7) / 8);
var val1 = NextPow2((bitSize + 7) / 8);
byte[] data = new byte[NextPow2((bitSize + 7) / 8)];
unsafe
{
@@ -275,7 +275,7 @@ namespace Game.DataStorage
if (!Copies.ContainsKey(idcopy))
Copies.Add(idcopy, new List<int>());
Copies[idcopy].Add(id);
}
}
@@ -429,7 +429,7 @@ namespace Game.DataStorage
}
else
{
data.AddRange(bitReader.ReadValue64(bitWidth).GetBytes(bitWidth));
data.AddRange(bitReader.ReadValue64(bitWidth, ColumnMeta[f].BitOffset, ColumnMeta[f].Cardinality == 1).GetBytes(bitWidth));
}
break;
+48
View File
@@ -350,6 +350,41 @@ namespace Game.DataStorage
CliDB.PvpRewardStorage.Clear();
for (var x = 0; x < (int)Class.Max; ++x)
{
_pvpTalentsByPosition[x] = new List<PvpTalentRecord>[PlayerConst.MaxPvpTalentTiers][];
for (var y = 0; y < PlayerConst.MaxPvpTalentTiers; ++y)
{
_pvpTalentsByPosition[x][y] = new List<PvpTalentRecord>[PlayerConst.MaxPvpTalentColumns];
for (var z = 0; z < PlayerConst.MaxPvpTalentColumns; ++z)
_pvpTalentsByPosition[x][y][z] = new List<PvpTalentRecord>();
}
}
for (var i = 0; i < PlayerConst.MaxPvpTalentTiers; ++i)
_pvpTalentUnlock[i] = new uint[PlayerConst.MaxPvpTalentColumns];
foreach (PvpTalentRecord talentInfo in CliDB.PvpTalentStorage.Values)
{
//ASSERT(talentInfo->ClassID < MAX_CLASSES);
//ASSERT(talentInfo->TierID < MAX_PVP_TALENT_TIERS, "MAX_PVP_TALENT_TIERS must be at least %u", talentInfo.TierID + 1);
//ASSERT(talentInfo->ColumnIndex < MAX_PVP_TALENT_COLUMNS, "MAX_PVP_TALENT_COLUMNS must be at least %u", talentInfo.ColumnIndex + 1);
if (talentInfo.ClassID == 0)
{
for (uint i = 1; i < (int)Class.Max; ++i)
_pvpTalentsByPosition[i][talentInfo.TierID][talentInfo.ColumnIndex].Add(talentInfo);
}
else
_pvpTalentsByPosition[talentInfo.ClassID][talentInfo.TierID][talentInfo.ColumnIndex].Add(talentInfo);
}
foreach (PvpTalentUnlockRecord talentUnlock in CliDB.PvpTalentUnlockStorage.Values)
{
//ASSERT(talentUnlock->TierID < MAX_PVP_TALENT_TIERS, "MAX_PVP_TALENT_TIERS must be at least %u", talentUnlock->TierID + 1);
//ASSERT(talentUnlock->ColumnIndex < MAX_PVP_TALENT_COLUMNS, "MAX_PVP_TALENT_COLUMNS must be at least %u", talentUnlock->ColumnIndex + 1);
_pvpTalentUnlock[talentUnlock.TierID][talentUnlock.ColumnIndex] = (uint)talentUnlock.HonorLevel;
}
foreach (QuestPackageItemRecord questPackageItem in CliDB.QuestPackageItemStorage.Values)
{
if (!_questPackages.ContainsKey(questPackageItem.QuestPackageID))
@@ -1146,6 +1181,17 @@ namespace Game.DataStorage
return value;
}
public uint GetRequiredHonorLevelForPvpTalent(PvpTalentRecord talentInfo)
{
//ASSERT(talentInfo);
return _pvpTalentUnlock[talentInfo.TierID][talentInfo.ColumnIndex];
}
public List<PvpTalentRecord> GetPvpTalentsByPosition(uint classId, uint tier, uint column)
{
return _pvpTalentsByPosition[classId][tier][column];
}
public List<QuestPackageItemRecord> GetQuestPackageItems(uint questPackageID)
{
if( _questPackages.ContainsKey(questPackageID))
@@ -1468,6 +1514,8 @@ namespace Game.DataStorage
MultiMap<uint, uint> _phasesByGroup = new MultiMap<uint, uint>();
Dictionary<PowerType, PowerTypeRecord> _powerTypes = new Dictionary<PowerType, PowerTypeRecord>();
Dictionary<Tuple<uint /*prestige level*/, uint /*honor level*/>, uint> _pvpRewardPack = new Dictionary<Tuple<uint, uint>, uint>();
List<PvpTalentRecord>[][][] _pvpTalentsByPosition = new List<PvpTalentRecord>[(int)Class.Max][][];
uint[][] _pvpTalentUnlock = new uint[PlayerConst.MaxPvpTalentTiers][];
Dictionary<uint, Tuple<List<QuestPackageItemRecord>, List<QuestPackageItemRecord>>> _questPackages = new Dictionary<uint, Tuple<List<QuestPackageItemRecord>, List<QuestPackageItemRecord>>>();
MultiMap<uint, RewardPackXItemRecord> _rewardPackItems = new MultiMap<uint, RewardPackXItemRecord>();
Dictionary<uint, uint> _rulesetItemUpgrade = new Dictionary<uint, uint>();
@@ -175,4 +175,27 @@ namespace Game.DataStorage
public byte Prestige;
public ushort RewardPackID;
}
public sealed class PvpTalentRecord
{
public uint Id;
public LocalizedString Description;
public uint SpellID;
public uint OverridesSpellID;
public int ExtraSpellID;
public int TierID;
public byte ColumnIndex;
public byte Flags;
public byte ClassID;
public ushort SpecID;
public byte Role;
}
public sealed class PvpTalentUnlockRecord
{
public uint Id;
public byte TierID;
public byte ColumnIndex;
public byte HonorLevel;
}
}
@@ -393,6 +393,9 @@ namespace Game.Entities
SendMessageToSet(duelWinner, true);
}
duel.opponent.DisablePvpRules();
DisablePvpRules();
Global.ScriptMgr.OnPlayerDuelEnd(duel.opponent, this, type);
switch (type)
+40
View File
@@ -948,6 +948,20 @@ namespace Game.Entities
while (result.NextRow());
}
}
void _LoadPvpTalents(SQLResult result)
{
// "SELECT TalentID, TalentGroup FROM character_pvp_talent WHERE guid = ?"
if (!result.IsEmpty())
{
do
{
PvpTalentRecord talent = CliDB.PvpTalentStorage.LookupByKey(result.Read<uint>(0));
if (talent != null)
AddPvpTalent(talent, result.Read<byte>(1), false);
}
while (result.NextRow());
}
}
void _LoadGlyphs(SQLResult result)
{
// SELECT talentGroup, glyphId from character_glyphs WHERE guid = ?
@@ -1977,6 +1991,29 @@ namespace Game.Entities
trans.Append(stmt);
}
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_PVP_TALENT);
stmt.AddValue(0, GetGUID().GetCounter());
trans.Append(stmt);
for (byte group = 0; group < PlayerConst.MaxSpecializations; ++group)
{
talents = GetPvpTalentMap(group);
foreach (var pair in talents.ToList())
{
if (pair.Value == PlayerSpellState.Removed)
{
talents.Remove(pair.Key);
continue;
}
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_PVP_TALENT);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, pair.Key);
stmt.AddValue(2, group);
trans.Append(stmt);
}
}
}
public void _SaveMail(SQLTransaction trans)
{
@@ -2852,6 +2889,7 @@ namespace Game.Entities
SetUInt32Value(PlayerFields.CurrentSpecId, spec.Id);
_LoadTalents(holder.GetResult(PlayerLoginQueryLoad.Talents));
_LoadPvpTalents(holder.GetResult(PlayerLoginQueryLoad.PvpTalents));
_LoadSpells(holder.GetResult(PlayerLoginQueryLoad.Spells));
GetSession().GetCollectionMgr().LoadToys();
GetSession().GetCollectionMgr().LoadHeirlooms();
@@ -2880,6 +2918,8 @@ namespace Game.Entities
InitTalentForLevel();
LearnDefaultSkills();
LearnCustomSpells();
if (getLevel() < PlayerConst.LevelMinHonor)
ResetPvpTalents();
// must be before inventory (some items required reputation check)
reputationMgr.LoadFromDB(holder.GetResult(PlayerLoginQueryLoad.Reputation));
@@ -317,11 +317,13 @@ namespace Game.Entities
for (byte i = 0; i < PlayerConst.MaxSpecializations; ++i)
{
Talents[i] = new Dictionary<uint, PlayerSpellState>();
PvpTalents[i] = new Dictionary<uint, PlayerSpellState>();
Glyphs[i] = new List<uint>();
}
}
public Dictionary<uint, PlayerSpellState>[] Talents = new Dictionary<uint, PlayerSpellState>[PlayerConst.MaxSpecializations];
public Dictionary<uint, PlayerSpellState>[] PvpTalents = new Dictionary<uint, PlayerSpellState>[PlayerConst.MaxSpecializations];
public List<uint>[] Glyphs = new List<uint>[PlayerConst.MaxSpecializations];
public uint ResetTalentsCost;
public long ResetTalentsTime;
@@ -122,6 +122,11 @@ namespace Game.Entities
UpdateAreaDependentAuras(newArea);
UpdateAreaAndZonePhase();
if (IsAreaThatActivatesPvpTalents(newArea))
EnablePvpRules();
else
DisablePvpRules();
// previously this was in UpdateZone (but after UpdateArea) so nothing will break
pvpInfo.IsInNoPvPArea = false;
if (area != null && area.IsSanctuary()) // in sanctuary
+212
View File
@@ -23,6 +23,7 @@ using Game.BattleGrounds;
using Game.DataStorage;
using Game.Network.Packets;
using Game.PvP;
using Game.Spells;
using System;
using System.Collections.Generic;
@@ -333,6 +334,217 @@ namespace Game.Entities
public uint GetHonorLevel() { return GetUInt32Value(PlayerFields.HonorLevel); }
public bool IsMaxHonorLevelAndPrestige() { return IsMaxPrestige() && GetHonorLevel() == PlayerConst.MaxHonorLevel; }
void ResetPvpTalents()
{
foreach (var talentInfo in CliDB.PvpTalentStorage.Values)
{
if (talentInfo.ClassID != 0 && talentInfo.ClassID != (int)GetClass())
continue;
RemovePvpTalent(talentInfo);
}
SQLTransaction trans = new SQLTransaction();
_SaveTalents(trans);
_SaveSpells(trans);
DB.Characters.CommitTransaction(trans);
}
public TalentLearnResult LearnPvpTalent(uint talentID, ref uint spellOnCooldown)
{
if (IsInCombat())
return TalentLearnResult.FailedAffectingCombat;
if (getLevel() < PlayerConst.LevelMinHonor)
return TalentLearnResult.FailedUnknown;
PvpTalentRecord talentInfo = CliDB.PvpTalentStorage.LookupByKey(talentID);
if (talentInfo == null)
return TalentLearnResult.FailedUnknown;
if (talentInfo.SpecID != 0)
{
if (talentInfo.SpecID != GetInt32Value(PlayerFields.CurrentSpecId))
return TalentLearnResult.FailedUnknown;
}
else if (talentInfo.Role < 7)
{
if (talentInfo.Role != CliDB.ChrSpecializationStorage.LookupByKey(GetUInt32Value(PlayerFields.CurrentSpecId)).Role)
return TalentLearnResult.FailedUnknown;
}
// prevent learn talent for different class (cheating)
if (talentInfo.ClassID != 0 && talentInfo.ClassID != (int)GetClass())
return TalentLearnResult.FailedUnknown;
if (GetPrestigeLevel() == 0)
if (Global.DB2Mgr.GetRequiredHonorLevelForPvpTalent(talentInfo) > GetHonorLevel())
return TalentLearnResult.FailedUnknown;
// Check if player doesn't have any talent in current tier
for (uint c = 0; c < PlayerConst.MaxPvpTalentColumns; ++c)
{
foreach (PvpTalentRecord talent in Global.DB2Mgr.GetPvpTalentsByPosition((uint)GetClass(), (uint)talentInfo.TierID, c))
{
if (HasPvpTalent(talent.Id, GetActiveTalentGroup()) && !HasFlag(PlayerFields.Flags, PlayerFlags.Resting) && HasFlag(UnitFields.Flags, UnitFlags.ImmuneToNpc))
return TalentLearnResult.FailedRestArea;
if (GetSpellHistory().HasCooldown(talent.SpellID))
{
spellOnCooldown = talent.SpellID;
return TalentLearnResult.FailedCantRemoveTalent;
}
RemovePvpTalent(talent);
}
}
if (!AddPvpTalent(talentInfo, GetActiveTalentGroup(), true))
return TalentLearnResult.FailedUnknown;
return TalentLearnResult.LearnOk;
}
bool AddPvpTalent(PvpTalentRecord talent, byte activeTalentGroup, bool learning)
{
//ASSERT(talent);
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talent.SpellID);
if (spellInfo == null)
{
Log.outError(LogFilter.Spells, $"Player.AddPvpTalent: Spell (ID: {talent.SpellID}) does not exist.");
return false;
}
if (!Global.SpellMgr.IsSpellValid(spellInfo, this, false))
{
Log.outError(LogFilter.Spells, $"Player.AddPvpTalent: Spell (ID: {talent.SpellID}) is invalid");
return false;
}
if (HasPvpRulesEnabled())
LearnSpell(talent.SpellID, false);
// Move this to toggle ?
if (talent.OverridesSpellID != 0)
AddOverrideSpell(talent.OverridesSpellID, talent.SpellID);
if (learning)
GetPvpTalentMap(activeTalentGroup)[talent.Id] = PlayerSpellState.New;
else
GetPvpTalentMap(activeTalentGroup)[talent.Id] = PlayerSpellState.Unchanged;
return true;
}
void RemovePvpTalent(PvpTalentRecord talent)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(talent.SpellID);
if (spellInfo == null)
return;
RemoveSpell(talent.SpellID, true);
// Move this to toggle ?
if (talent.OverridesSpellID != 0)
RemoveOverrideSpell(talent.OverridesSpellID, talent.SpellID);
// if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
if (!GetPvpTalentMap(GetActiveTalentGroup()).ContainsKey(talent.Id))
GetPvpTalentMap(GetActiveTalentGroup())[talent.Id] = PlayerSpellState.Removed;
}
public void TogglePvpTalents(bool enable)
{
var pvpTalents = GetPvpTalentMap(GetActiveTalentGroup());
foreach (var pair in pvpTalents)
{
PvpTalentRecord pvpTalentInfo = CliDB.PvpTalentStorage.LookupByKey(pair.Key);
if (enable && pair.Value != PlayerSpellState.Removed)
LearnSpell(pvpTalentInfo.SpellID, false);
else
RemoveSpell(pvpTalentInfo.SpellID, true);
}
}
bool HasPvpTalent(uint talentID, byte activeTalentGroup)
{
return GetPvpTalentMap(activeTalentGroup).ContainsKey(talentID) && GetPvpTalentMap(activeTalentGroup)[talentID] != PlayerSpellState.Removed;
}
public void EnablePvpRules(bool dueToCombat = false)
{
if (HasPvpRulesEnabled())
return;
if (!HasSpell(195710)) // Honorable Medallion
CastSpell(this, 208682); // Learn Gladiator's Medallion
CastSpell(this, PlayerConst.SpellPvpRulesEnabled);
if (!dueToCombat)
{
Aura aura = GetAura(PlayerConst.SpellPvpRulesEnabled);
if (aura != null)
{
aura.SetMaxDuration(-1);
aura.SetDuration(-1);
}
}
}
void DisablePvpRules()
{
// Don't disable pvp rules when in pvp zone.
if (IsInAreaThatActivatesPvpTalents())
return;
if (GetCombatTimer() == 0)
RemoveAurasDueToSpell(PlayerConst.SpellPvpRulesEnabled);
else
{
Aura aura = GetAura(PlayerConst.SpellPvpRulesEnabled);
if (aura != null)
aura.SetDuration(aura.GetSpellInfo().GetMaxDuration());
}
}
bool HasPvpRulesEnabled()
{
return HasAura(PlayerConst.SpellPvpRulesEnabled);
}
bool IsInAreaThatActivatesPvpTalents()
{
return IsAreaThatActivatesPvpTalents(GetAreaId());
}
bool IsAreaThatActivatesPvpTalents(uint areaID)
{
if (InBattleground())
return true;
AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(areaID);
if (area != null)
{
do
{
if (area.IsSanctuary())
return false;
if (area.Flags[0].HasAnyFlag(AreaFlags.Arena))
return true;
if (Global.BattleFieldMgr.GetBattlefieldToZoneId(area.Id) != null)
return true;
area = CliDB.AreaTableStorage.LookupByKey(area.ParentAreaID);
} while (area != null);
}
return false;
}
public Dictionary<uint, PlayerSpellState> GetPvpTalentMap(uint spec) { return _specializationInfo.PvpTalents[spec]; }
//BGs
public Battleground GetBattleground()
+65 -1
View File
@@ -306,6 +306,32 @@ namespace Game.Entities
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
}
foreach (var talentInfo in CliDB.PvpTalentStorage.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 != 0 && 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);
}
// Remove spec specific spells
RemoveSpecializationSpells();
@@ -334,6 +360,19 @@ namespace Game.Entities
}
}
foreach (var talentInfo in CliDB.PvpTalentStorage.Values)
{
// learn only talents for character class (or x-class talents)
if (talentInfo.ClassID != 0 && talentInfo.ClassID != (int)GetClass())
continue;
if (talentInfo.SpellID == 0)
continue;
if (HasPvpTalent(talentInfo.Id, GetActiveTalentGroup()))
AddPvpTalent(talentInfo, GetActiveTalentGroup(), true);
}
LearnSpecializationSpells();
if (CanUseMastery())
@@ -498,7 +537,7 @@ namespace Game.Entities
continue;
var talents = GetTalentMap(i);
var pvpTalents = GetPvpTalentMap(i);
UpdateTalentData.TalentGroupInfo groupInfoPkt = new UpdateTalentData.TalentGroupInfo();
groupInfoPkt.SpecID = spec.Id;
@@ -528,6 +567,31 @@ namespace Game.Entities
groupInfoPkt.TalentIDs.Add((ushort)pair.Key);
}
foreach (var pair in pvpTalents)
{
if (pair.Value == PlayerSpellState.Removed)
continue;
PvpTalentRecord talentInfo = CliDB.PvpTalentStorage.LookupByKey(pair.Key);
if (talentInfo == null)
{
Log.outError(LogFilter.Player, $"Player.SendTalentsInfoData: Player '{GetName()}' ({GetGUID().ToString()}) has unknown pvp talent id: {pair.Key}");
continue;
}
if (talentInfo.ClassID != 0 && talentInfo.ClassID != (int)GetClass())
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)pair.Key);
}
packet.Info.TalentGroups.Add(groupInfoPkt);
}
+11
View File
@@ -494,6 +494,14 @@ namespace Game.Entities
UpdateAfkReport(now);
if (GetCombatTimer() != 0) // Only set when in pvp combat
{
Aura aura = GetAura(PlayerConst.SpellPvpRulesEnabled);
if (aura != null)
if (!aura.IsPermanent())
aura.SetDuration(aura.GetSpellInfo().GetMaxDuration());
}
if (IsAIEnabled && GetAI() != null)
GetAI().UpdateAI(diff);
else if (NeedChangeAI)
@@ -5307,6 +5315,9 @@ namespace Game.Entities
InitTalentForLevel();
InitTaxiNodesForLevel();
if (level < PlayerConst.LevelMinHonor)
ResetPvpTalents();
UpdateAllStats();
if (WorldConfig.GetBoolValue(WorldCfg.AlwaysMaxskill)) // Max weapon skill when leveling up
+5
View File
@@ -1279,7 +1279,12 @@ namespace Game.Entities
return;
if (PvP)
{
m_CombatTimer = 5000;
Player me = ToPlayer();
if (me)
me.EnablePvpRules(true);
}
if (IsInCombat() || HasUnitState(UnitState.Evade))
return;
+5
View File
@@ -2520,6 +2520,10 @@ namespace Game
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.Talents, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_PVP_TALENTS);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.PvpTalents, stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PLAYER_ACCOUNT_DATA);
stmt.AddValue(0, lowGuid);
SetQuery(PlayerLoginQueryLoad.AccountData, stmt);
@@ -2615,6 +2619,7 @@ namespace Game
BgData,
Glyphs,
Talents,
PvpTalents,
AccountData,
Skills,
WeeklyQuestStatus,
+3
View File
@@ -77,6 +77,9 @@ namespace Game
player.SendPacket(packet);
plTarget.SendPacket(packet);
player.EnablePvpRules();
plTarget.EnablePvpRules();
}
void HandleDuelCancelled()
+26
View File
@@ -52,6 +52,32 @@ namespace Game
GetPlayer().SendTalentsInfoData();
}
[WorldPacketHandler(ClientOpcodes.LearnPvpTalents)]
void HandleLearnPvpTalentsOpcode(LearnPvpTalents packet)
{
LearnPvpTalentsFailed learnPvpTalentsFailed = new LearnPvpTalentsFailed();
bool anythingLearned = false;
foreach (ushort talentId in packet.Talents)
{
TalentLearnResult result = _player.LearnPvpTalent(talentId, ref learnPvpTalentsFailed.SpellID);
if (result != 0)
{
if (learnPvpTalentsFailed.Reason == 0)
learnPvpTalentsFailed.Reason = (uint)result;
learnPvpTalentsFailed.Talents.Add(talentId);
}
else
anythingLearned = true;
}
if (learnPvpTalentsFailed.Reason != 0)
SendPacket(learnPvpTalentsFailed);
if (anythingLearned)
_player.SendTalentsInfoData();
}
[WorldPacketHandler(ClientOpcodes.ConfirmRespecWipe)]
void HandleConfirmRespecWipe(ConfirmRespecWipe confirmRespecWipe)
{
@@ -18,6 +18,8 @@
using Framework.Constants;
using Game.Entities;
using System.Collections.Generic;
using Framework.Collections;
using Framework.Dynamic;
namespace Game.Network.Packets
{
@@ -144,6 +146,39 @@ namespace Game.Network.Packets
public bool IsFullUpdate;
}
class LearnPvpTalents : ClientPacket
{
public LearnPvpTalents(WorldPacket packet) : base(packet) { }
public override void Read()
{
int size = _worldPacket.ReadBits<int>(6);
for (int i = 0; i < size; ++i)
Talents[i] = _worldPacket.ReadUInt16();
}
public Array<ushort> Talents = new Array<ushort>(6);
}
class LearnPvpTalentsFailed : ServerPacket
{
public LearnPvpTalentsFailed() : base(ServerOpcodes.LearnPvpTalentsFailed) { }
public override void Write()
{
_worldPacket.WriteBits(Reason, 4);
_worldPacket.WriteUInt32(SpellID);
_worldPacket.WriteUInt32(Talents.Count);
foreach (var id in Talents)
_worldPacket.WriteUInt16(id);
}
public uint Reason;
public uint SpellID;
public List<ushort> Talents = new List<ushort>();
}
//Structs
struct GlyphBinding
{
+17 -1
View File
@@ -3431,7 +3431,7 @@ namespace Game.Spells
// Save old powers for further calculation
int oldPower = target.GetPower(powerType);
int oldMaxPower = target.GetMaxPower(powerType);
// Handle aura effect for max power
target.HandleStatModifier(unitMod, UnitModifierType.TotalPCT, GetAmount(), apply);
@@ -5953,6 +5953,22 @@ namespace Game.Spells
caster.RemoveAreaTrigger(this);
}
}
[AuraEffectHandler(AuraType.PvpTalents)]
void HandleAuraPvpTalents(AuraApplication auraApp, AuraEffectHandleModes mode, bool apply)
{
if (!mode.HasAnyFlag(AuraEffectHandleModes.Real))
return;
Player target = auraApp.GetTarget().ToPlayer();
if (target)
{
if (apply)
target.TogglePvpTalents(true);
else if (!target.HasAuraType(AuraType.PvpTalents))
target.TogglePvpTalents(false);
}
}
#endregion
}
+26 -1
View File
@@ -1086,6 +1086,30 @@ LOCK TABLES `character_pet_declinedname` WRITE;
/*!40000 ALTER TABLE `character_pet_declinedname` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `character_pvp_talent`
--
DROP TABLE IF EXISTS `character_pvp_talent`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `character_pvp_talent` (
`guid` bigint(20) unsigned NOT NULL,
`talentId` mediumint(8) unsigned NOT NULL,
`talentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`guid`,`talentId`,`talentGroup`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `character_pvp_talent`
--
LOCK TABLES `character_pvp_talent` WRITE;
/*!40000 ALTER TABLE `character_pvp_talent` DISABLE KEYS */;
/*!40000 ALTER TABLE `character_pvp_talent` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `character_queststatus`
--
@@ -3537,7 +3561,8 @@ INSERT INTO `updates` VALUES
('2017_10_29_00_characters.sql','8CFC473E7E87E58C317A72016BF69E9050D3BC83','ARCHIVED','2017-04-19 00:07:40',25),
('2018_02_03_00_characters.sql','73E9BFD848D7A22F2A7DD89CF64E30E3A8689512','ARCHIVED','2018-02-03 23:52:42',0),
('2018_02_08_00_characters.sql','75FA162A9B85D678B26F972371265F1EC2C75187','ARCHIVED','2018-02-08 22:23:28',0),
('2018_02_19_00_characters.sql','75A0FFAFD0633921708DB0F72F9CC9796ACB960B','RELEASED','2018-02-19 22:33:32',117);
('2018_02_19_00_characters.sql','75A0FFAFD0633921708DB0F72F9CC9796ACB960B','RELEASED','2018-02-19 22:33:32',117),
('2018_03_04_00_characters.sql','2A4CD2EE2547E718490706FADC78BF36F0DED8D6','RELEASED','2018-03-04 18:15:24',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
@@ -0,0 +1,10 @@
--
-- Table structure for table `character_pvp_talent`
--
DROP TABLE IF EXISTS `character_pvp_talent`;
CREATE TABLE `character_pvp_talent` (
`guid` bigint(20) unsigned NOT NULL,
`talentId` mediumint(8) unsigned NOT NULL,
`talentGroup` tinyint(3) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`guid`,`talentId`,`talentGroup`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -0,0 +1,44 @@
--
-- Table structure for table `pvp_talent`
--
DROP TABLE IF EXISTS `pvp_talent`;
CREATE TABLE `pvp_talent` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`Description` text,
`SpellID` int(10) NOT NULL DEFAULT '0',
`OverridesSpellID` int(10) NOT NULL DEFAULT '0',
`ExtraSpellID` int(10) NOT NULL DEFAULT '0',
`TierID` int(10) NOT NULL DEFAULT '0',
`ColumnIndex` int(10) NOT NULL DEFAULT '0',
`Flags` int(10) NOT NULL DEFAULT '0',
`ClassID` int(10) NOT NULL DEFAULT '0',
`SpecID` int(10) NOT NULL DEFAULT '0',
`Role` int(10) NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Table structure for table `pvp_talent_locale`
--
DROP TABLE IF EXISTS `pvp_talent_locale`;
CREATE TABLE `pvp_talent_locale` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Description_lang` text,
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Table structure for table `pvp_talent_unlock`
--
DROP TABLE IF EXISTS `pvp_talent_unlock`;
CREATE TABLE `pvp_talent_unlock` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`TierID` int(10) NOT NULL DEFAULT '0',
`ColumnIndex` int(10) NOT NULL DEFAULT '0',
`HonorLevel` int(10) NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@@ -0,0 +1,11 @@
--
-- Table structure for table `pvp_item`
--
DROP TABLE IF EXISTS `pvp_item`;
CREATE TABLE `pvp_item` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`ItemID` int(10) unsigned NOT NULL DEFAULT '0',
`ItemLevelBonus` tinyint(3) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;