Core/Player: Implement PvP Talents
This commit is contained in:
@@ -393,6 +393,9 @@ namespace Game.Entities
|
||||
SendMessageToSet(duelWinner, true);
|
||||
}
|
||||
|
||||
duel.opponent.DisablePvpRules();
|
||||
DisablePvpRules();
|
||||
|
||||
Global.ScriptMgr.OnPlayerDuelEnd(duel.opponent, this, type);
|
||||
|
||||
switch (type)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user