Core/Players: Implemented new talent system
Port From (https://github.com/TrinityCore/TrinityCore/commit/0cc5ab8372f19dad7412038d52dcd39db5e0e171)
This commit is contained in:
@@ -141,7 +141,7 @@ namespace Game.Entities
|
||||
|
||||
public bool HasValue() { return _hasValue; }
|
||||
}
|
||||
|
||||
|
||||
public class UpdateFieldArray<T> where T : new()
|
||||
{
|
||||
public T[] _values;
|
||||
@@ -437,6 +437,12 @@ namespace Game.Entities
|
||||
return updateField;
|
||||
}
|
||||
|
||||
public UpdateFieldString ModifyValue(UpdateFieldString updateField)
|
||||
{
|
||||
MarkChanged(updateField);
|
||||
return updateField;
|
||||
}
|
||||
|
||||
public ref U ModifyValue<U>(UpdateFieldArray<U> updateField, int index) where U : new()
|
||||
{
|
||||
MarkChanged(updateField, index);
|
||||
@@ -472,6 +478,12 @@ namespace Game.Entities
|
||||
_changesMask.Set(updateField.Bit);
|
||||
}
|
||||
|
||||
public void MarkChanged(UpdateFieldString updateField)
|
||||
{
|
||||
_changesMask.Set(updateField.BlockBit);
|
||||
_changesMask.Set(updateField.Bit);
|
||||
}
|
||||
|
||||
public void MarkChanged<U>(UpdateFieldArray<U> updateField, int index) where U : new()
|
||||
{
|
||||
_changesMask.Set(updateField.Bit);
|
||||
|
||||
@@ -824,7 +824,7 @@ namespace Game.Entities
|
||||
public abstract void BuildValuesCreate(WorldPacket data, Player target);
|
||||
public abstract void BuildValuesUpdate(WorldPacket data, Player target);
|
||||
|
||||
public void SetUpdateFieldValue<T>(IUpdateField<T> updateField, T newValue) where T : new()
|
||||
public void SetUpdateFieldValue<T>(IUpdateField<T> updateField, T newValue)
|
||||
{
|
||||
if (!newValue.Equals(updateField.GetValue()))
|
||||
{
|
||||
@@ -863,7 +863,7 @@ namespace Game.Entities
|
||||
SetUpdateFieldValue(ref value, (T)(value | (dynamic)flag));
|
||||
}
|
||||
|
||||
public void RemoveUpdateFieldFlagValue<T>(IUpdateField<T> updateField, T flag) where T : new()
|
||||
public void RemoveUpdateFieldFlagValue<T>(IUpdateField<T> updateField, T flag)
|
||||
{
|
||||
//static_assert(std::is_integral < T >::value, "SetUpdateFieldFlagValue must be used with integral types");
|
||||
SetUpdateFieldValue(updateField, (T)(updateField.GetValue() & ~(dynamic)flag));
|
||||
|
||||
@@ -1010,6 +1010,146 @@ namespace Game.Entities
|
||||
while (result.NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
void _LoadTraits(SQLResult configsResult, SQLResult entriesResult)
|
||||
{
|
||||
MultiMap<int, TraitEntryPacket> traitEntriesByConfig = new();
|
||||
if (!entriesResult.IsEmpty())
|
||||
{
|
||||
// 0 1, 2 3 4
|
||||
// SELECT traitConfigId, traitNodeId, traitNodeEntryId, rank, grantedRanks FROM character_trait_entry WHERE guid = ?
|
||||
do
|
||||
{
|
||||
TraitEntryPacket traitEntry = new();
|
||||
traitEntry.TraitNodeID = entriesResult.Read<int>(1);
|
||||
traitEntry.TraitNodeEntryID = entriesResult.Read<int>(2);
|
||||
traitEntry.Rank = entriesResult.Read<int>(3);
|
||||
traitEntry.GrantedRanks = entriesResult.Read<int>(4);
|
||||
|
||||
if (!TraitMgr.IsValidEntry(traitEntry))
|
||||
continue;
|
||||
|
||||
traitEntriesByConfig.Add(entriesResult.Read<int>(0), traitEntry);
|
||||
|
||||
} while (entriesResult.NextRow());
|
||||
}
|
||||
|
||||
if (!configsResult.IsEmpty())
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// SELECT traitConfigId, type, chrSpecializationId, combatConfigFlags, localIdentifier, skillLineId, traitSystemId, `name` FROM character_trait_config WHERE guid = ?
|
||||
do
|
||||
{
|
||||
TraitConfigPacket traitConfig = new();
|
||||
traitConfig.ID = configsResult.Read<int>(0);
|
||||
traitConfig.Type = (TraitConfigType)configsResult.Read<int>(1);
|
||||
switch (traitConfig.Type)
|
||||
{
|
||||
case TraitConfigType.Combat:
|
||||
traitConfig.ChrSpecializationID = configsResult.Read<int>(2);
|
||||
traitConfig.CombatConfigFlags = (TraitCombatConfigFlags)configsResult.Read<int>(3);
|
||||
traitConfig.LocalIdentifier = configsResult.Read<int>(4);
|
||||
break;
|
||||
case TraitConfigType.Profession:
|
||||
traitConfig.SkillLineID = configsResult.Read<uint>(5);
|
||||
break;
|
||||
case TraitConfigType.Generic:
|
||||
traitConfig.TraitSystemID = configsResult.Read<int>(6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
traitConfig.Name = configsResult.Read<string>(7);
|
||||
|
||||
foreach (var traitEntry in traitEntriesByConfig.LookupByKey(traitConfig.ID))
|
||||
traitConfig.Entries.Add(traitEntry);
|
||||
|
||||
if (TraitMgr.ValidateConfig(traitConfig, this) != TalentLearnResult.LearnOk)
|
||||
{
|
||||
traitConfig.Entries.Clear();
|
||||
foreach (TraitEntry grantedEntry in TraitMgr.GetGrantedTraitEntriesForConfig(traitConfig, this))
|
||||
traitConfig.Entries.Add(new TraitEntryPacket(grantedEntry));
|
||||
}
|
||||
|
||||
AddTraitConfig(traitConfig);
|
||||
|
||||
} while (configsResult.NextRow());
|
||||
}
|
||||
|
||||
bool hasConfigForSpec(int specId)
|
||||
{
|
||||
return m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.Type == (int)TraitConfigType.Combat
|
||||
&& traitConfig.ChrSpecializationID == specId
|
||||
&& (traitConfig.CombatConfigFlags & (int)TraitCombatConfigFlags.ActiveForSpec) != 0;
|
||||
}) >= 0;
|
||||
}
|
||||
|
||||
int findFreeLocalIdentifier(int specId)
|
||||
{
|
||||
int index = 1;
|
||||
while (m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.Type == (int)TraitConfigType.Combat
|
||||
&& traitConfig.ChrSpecializationID == specId
|
||||
&& traitConfig.LocalIdentifier == index;
|
||||
}) >= 0)
|
||||
++index;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < PlayerConst.MaxSpecializations - 1 /*initial spec doesnt get a config*/; ++i)
|
||||
{
|
||||
var spec = Global.DB2Mgr.GetChrSpecializationByIndex(GetClass(), i);
|
||||
if (spec != null)
|
||||
{
|
||||
if (hasConfigForSpec((int)spec.Id))
|
||||
continue;
|
||||
|
||||
TraitConfigPacket traitConfig = new();
|
||||
traitConfig.Type = TraitConfigType.Combat;
|
||||
traitConfig.ChrSpecializationID = (int)spec.Id;
|
||||
traitConfig.CombatConfigFlags = TraitCombatConfigFlags.ActiveForSpec;
|
||||
traitConfig.LocalIdentifier = findFreeLocalIdentifier((int)spec.Id);
|
||||
traitConfig.Name = spec.Name[GetSession().GetSessionDbcLocale()];
|
||||
|
||||
CreateTraitConfig(traitConfig);
|
||||
}
|
||||
}
|
||||
|
||||
int activeConfig = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.Type == (int)TraitConfigType.Combat
|
||||
&& traitConfig.ChrSpecializationID == GetPrimarySpecialization()
|
||||
&& (traitConfig.CombatConfigFlags & (int)TraitCombatConfigFlags.ActiveForSpec) != 0;
|
||||
});
|
||||
|
||||
if (activeConfig >= 0)
|
||||
SetActiveCombatTraitConfigID(m_activePlayerData.TraitConfigs[activeConfig].ID);
|
||||
|
||||
foreach (TraitConfig traitConfig in m_activePlayerData.TraitConfigs)
|
||||
{
|
||||
switch ((TraitConfigType)(int)traitConfig.Type)
|
||||
{
|
||||
case TraitConfigType.Combat:
|
||||
if (traitConfig.ID != m_activePlayerData.ActiveCombatTraitConfigID)
|
||||
continue;
|
||||
break;
|
||||
case TraitConfigType.Profession:
|
||||
if (!HasSkill((uint)(int)traitConfig.SkillLineID))
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ApplyTraitConfig(traitConfig.ID, true);
|
||||
}
|
||||
}
|
||||
|
||||
void _LoadGlyphs(SQLResult result)
|
||||
{
|
||||
// SELECT talentGroup, glyphId from character_glyphs WHERE guid = ?
|
||||
@@ -1863,6 +2003,23 @@ namespace Game.Entities
|
||||
|
||||
void _SaveActions(SQLTransaction trans)
|
||||
{
|
||||
int traitConfigId = 0;
|
||||
|
||||
TraitConfig traitConfig = GetTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID);
|
||||
if (traitConfig != null)
|
||||
{
|
||||
int usedSavedTraitConfigIndex = m_activePlayerData.TraitConfigs.FindIndexIf(savedConfig =>
|
||||
{
|
||||
return (TraitConfigType)(int)savedConfig.Type == TraitConfigType.Combat
|
||||
&& ((TraitCombatConfigFlags)(int)savedConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) == TraitCombatConfigFlags.None
|
||||
&& ((TraitCombatConfigFlags)(int)savedConfig.CombatConfigFlags & TraitCombatConfigFlags.SharedActionBars) == TraitCombatConfigFlags.None
|
||||
&& savedConfig.LocalIdentifier == traitConfig.LocalIdentifier;
|
||||
});
|
||||
|
||||
if (usedSavedTraitConfigIndex >= 0)
|
||||
traitConfigId = m_activePlayerData.TraitConfigs[usedSavedTraitConfigIndex].ID;
|
||||
}
|
||||
|
||||
PreparedStatement stmt;
|
||||
|
||||
foreach (var pair in m_actionButtons.ToList())
|
||||
@@ -1873,9 +2030,10 @@ namespace Game.Entities
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_ACTION);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, GetActiveTalentGroup());
|
||||
stmt.AddValue(2, pair.Key);
|
||||
stmt.AddValue(3, pair.Value.GetAction());
|
||||
stmt.AddValue(4, (byte)pair.Value.GetButtonType());
|
||||
stmt.AddValue(2, traitConfigId);
|
||||
stmt.AddValue(3, pair.Key);
|
||||
stmt.AddValue(4, pair.Value.GetAction());
|
||||
stmt.AddValue(5, (byte)pair.Value.GetButtonType());
|
||||
trans.Append(stmt);
|
||||
|
||||
pair.Value.uState = ActionButtonUpdateState.UnChanged;
|
||||
@@ -1887,6 +2045,7 @@ namespace Game.Entities
|
||||
stmt.AddValue(2, GetGUID().GetCounter());
|
||||
stmt.AddValue(3, pair.Key);
|
||||
stmt.AddValue(4, GetActiveTalentGroup());
|
||||
stmt.AddValue(5, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
|
||||
pair.Value.uState = ActionButtonUpdateState.UnChanged;
|
||||
@@ -1896,6 +2055,7 @@ namespace Game.Entities
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, pair.Key);
|
||||
stmt.AddValue(2, GetActiveTalentGroup());
|
||||
stmt.AddValue(3, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
|
||||
m_actionButtons.Remove(pair.Key);
|
||||
@@ -2135,6 +2295,99 @@ namespace Game.Entities
|
||||
trans.Append(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
void _SaveTraits(SQLTransaction trans)
|
||||
{
|
||||
PreparedStatement stmt = null;
|
||||
foreach (var (traitConfigId, state) in m_traitConfigStates)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PlayerSpellState.Changed:
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_TRAIT_ENTRIES);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_TRAIT_CONFIGS);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
|
||||
TraitConfig traitConfig = GetTraitConfig(traitConfigId);
|
||||
if (traitConfig != null)
|
||||
{
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_TRAIT_CONFIGS);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfig.ID);
|
||||
stmt.AddValue(2, traitConfig.Type);
|
||||
switch ((TraitConfigType)(int)traitConfig.Type)
|
||||
{
|
||||
case TraitConfigType.Combat:
|
||||
stmt.AddValue(3, traitConfig.ChrSpecializationID);
|
||||
stmt.AddValue(4, traitConfig.CombatConfigFlags);
|
||||
stmt.AddValue(5, traitConfig.LocalIdentifier);
|
||||
stmt.AddNull(6);
|
||||
stmt.AddNull(7);
|
||||
break;
|
||||
case TraitConfigType.Profession:
|
||||
stmt.AddNull(3);
|
||||
stmt.AddNull(4);
|
||||
stmt.AddNull(5);
|
||||
stmt.AddValue(6, traitConfig.SkillLineID);
|
||||
stmt.AddNull(7);
|
||||
break;
|
||||
case TraitConfigType.Generic:
|
||||
stmt.AddNull(3);
|
||||
stmt.AddNull(4);
|
||||
stmt.AddNull(5);
|
||||
stmt.AddNull(6);
|
||||
stmt.AddValue(7, traitConfig.TraitSystemID);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
stmt.AddValue(8, traitConfig.Name);
|
||||
trans.Append(stmt);
|
||||
|
||||
foreach (var traitEntry in traitConfig.Entries)
|
||||
{
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_TRAIT_ENTRIES);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfig.ID);
|
||||
stmt.AddValue(2, traitEntry.TraitNodeID);
|
||||
stmt.AddValue(3, traitEntry.TraitNodeEntryID);
|
||||
stmt.AddValue(4, traitEntry.Rank);
|
||||
stmt.AddValue(5, traitEntry.GrantedRanks);
|
||||
trans.Append(stmt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PlayerSpellState.Removed:
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_TRAIT_ENTRIES);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_TRAIT_CONFIGS);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_ACTION_BY_TRAIT_CONFIG);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfigId);
|
||||
trans.Append(stmt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_traitConfigStates.Clear();
|
||||
}
|
||||
|
||||
public void _SaveMail(SQLTransaction trans)
|
||||
{
|
||||
PreparedStatement stmt;
|
||||
@@ -3074,6 +3327,8 @@ namespace Game.Entities
|
||||
LearnDefaultSkills();
|
||||
LearnCustomSpells();
|
||||
|
||||
_LoadTraits(holder.GetResult(PlayerLoginQueryLoad.TraitConfigs), holder.GetResult(PlayerLoginQueryLoad.TraitEntries)); // must be after loading spells
|
||||
|
||||
// must be before inventory (some items required reputation check)
|
||||
reputationMgr.LoadFromDB(holder.GetResult(PlayerLoginQueryLoad.Reputation));
|
||||
|
||||
@@ -3086,7 +3341,7 @@ namespace Game.Entities
|
||||
// update items with duration and realtime
|
||||
UpdateItemDuration(time_diff, true);
|
||||
|
||||
_LoadActions(holder.GetResult(PlayerLoginQueryLoad.Actions));
|
||||
StartLoadingActionButtons();
|
||||
|
||||
// unread mails and next delivery time, actual mails not loaded
|
||||
_LoadMail(holder.GetResult(PlayerLoginQueryLoad.Mails),
|
||||
@@ -3633,6 +3888,7 @@ namespace Game.Entities
|
||||
_SaveMonthlyQuestStatus(characterTransaction);
|
||||
_SaveGlyphs(characterTransaction);
|
||||
_SaveTalents(characterTransaction);
|
||||
_SaveTraits(characterTransaction);
|
||||
_SaveSpells(characterTransaction);
|
||||
GetSpellHistory().SaveToDB<Player>(characterTransaction);
|
||||
_SaveActions(characterTransaction);
|
||||
@@ -4183,6 +4439,14 @@ namespace Game.Entities
|
||||
|
||||
Garrison.DeleteFromDB(guid, trans);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_TRAIT_ENTRIES_BY_CHAR);
|
||||
stmt.AddValue(0, guid);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_TRAIT_CONFIGS_BY_CHAR);
|
||||
stmt.AddValue(0, guid);
|
||||
trans.Append(stmt);
|
||||
|
||||
Global.CharacterCacheStorage.DeleteCharacterCacheEntry(playerGuid, name);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -257,6 +257,8 @@ namespace Game.Entities
|
||||
uint m_PlayedTimeTotal;
|
||||
uint m_PlayedTimeLevel;
|
||||
|
||||
Dictionary<int, PlayerSpellState> m_traitConfigStates = new();
|
||||
|
||||
Dictionary<byte, ActionButton> m_actionButtons = new();
|
||||
ObjectGuid m_playerSharingQuest;
|
||||
uint m_sharedQuestId;
|
||||
|
||||
@@ -1024,13 +1024,17 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
public bool HasSkill(SkillType skill)
|
||||
{
|
||||
return HasSkill((uint)skill);
|
||||
}
|
||||
public bool HasSkill(uint skill)
|
||||
{
|
||||
if (skill == 0)
|
||||
return false;
|
||||
|
||||
SkillInfo skillInfoField = m_activePlayerData.Skill;
|
||||
|
||||
var skillStatusData = mSkillStatus.LookupByKey((uint)skill);
|
||||
var skillStatusData = mSkillStatus.LookupByKey(skill);
|
||||
return skillStatusData != null && skillStatusData.State != SkillState.Deleted && skillInfoField.SkillRank[skillStatusData.Pos] != 0;
|
||||
}
|
||||
public void SetSkill(SkillType skill, uint step, uint newVal, uint maxVal)
|
||||
@@ -2046,14 +2050,14 @@ namespace Game.Entities
|
||||
SendPacket(new SendUnlearnSpells());
|
||||
}
|
||||
|
||||
public void LearnSpell(uint spellId, bool dependent, uint fromSkill = 0, bool suppressMessaging = false)
|
||||
public void LearnSpell(uint spellId, bool dependent, uint fromSkill = 0, bool suppressMessaging = false, int? traitDefinitionId = null)
|
||||
{
|
||||
PlayerSpell spell = m_spells.LookupByKey(spellId);
|
||||
|
||||
bool disabled = (spell != null) && spell.Disabled;
|
||||
bool active = !disabled || spell.Active;
|
||||
|
||||
bool learning = AddSpell(spellId, active, true, dependent, false, false, fromSkill);
|
||||
bool learning = AddSpell(spellId, active, true, dependent, false, false, fromSkill, traitDefinitionId);
|
||||
|
||||
// prevent duplicated entires in spell book, also not send if not in world (loading)
|
||||
if (learning && IsInWorld)
|
||||
@@ -2061,6 +2065,7 @@ namespace Game.Entities
|
||||
LearnedSpells learnedSpells = new();
|
||||
LearnedSpellInfo learnedSpellInfo = new();
|
||||
learnedSpellInfo.SpellID = spellId;
|
||||
learnedSpellInfo.TraitDefinitionID = traitDefinitionId;
|
||||
learnedSpells.SuppressMessaging = suppressMessaging;
|
||||
learnedSpells.ClientLearnedSpellData.Add(learnedSpellInfo);
|
||||
SendPacket(learnedSpells);
|
||||
@@ -2315,7 +2320,7 @@ namespace Game.Entities
|
||||
return null;
|
||||
}
|
||||
|
||||
bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0)
|
||||
bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0, int? traitDefinitionId = null)
|
||||
{
|
||||
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None);
|
||||
if (spellInfo == null)
|
||||
@@ -2394,6 +2399,18 @@ namespace Game.Entities
|
||||
dependent_set = true;
|
||||
}
|
||||
|
||||
if (spell.TraitDefinitionId != traitDefinitionId)
|
||||
{
|
||||
if (spell.TraitDefinitionId.HasValue)
|
||||
{
|
||||
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(spell.TraitDefinitionId.Value);
|
||||
if (traitDefinition != null)
|
||||
RemoveOverrideSpell((uint)traitDefinition.OverridesSpellID, spellId);
|
||||
}
|
||||
|
||||
spell.TraitDefinitionId = traitDefinitionId;
|
||||
}
|
||||
|
||||
// update active state for known spell
|
||||
if (spell.Active != active && spell.State != PlayerSpellState.Removed && !spell.Disabled)
|
||||
{
|
||||
@@ -2475,6 +2492,8 @@ namespace Game.Entities
|
||||
newspell.Active = active;
|
||||
newspell.Dependent = dependent;
|
||||
newspell.Disabled = disabled;
|
||||
if (traitDefinitionId.HasValue)
|
||||
newspell.TraitDefinitionId = traitDefinitionId.Value;
|
||||
|
||||
// replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
|
||||
if (newspell.Active && !newspell.Disabled && spellInfo.IsRanked())
|
||||
@@ -2524,26 +2543,73 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
bool castSpell = false;
|
||||
|
||||
// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
|
||||
// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
|
||||
if (!loading && spellInfo.HasAttribute(SpellCustomAttributes.IsTalent) && spellInfo.HasEffect(SpellEffectName.LearnSpell))
|
||||
{
|
||||
// ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
|
||||
CastSpell(this, spellId, true);
|
||||
castSpell = true;
|
||||
}
|
||||
// also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
|
||||
else if (spellInfo.IsPassive())
|
||||
{
|
||||
if (HandlePassiveSpellLearn(spellInfo))
|
||||
CastSpell(this, spellId, true);
|
||||
}
|
||||
castSpell = HandlePassiveSpellLearn(spellInfo);
|
||||
else if (spellInfo.HasEffect(SpellEffectName.SkillStep))
|
||||
{
|
||||
CastSpell(this, spellId, true);
|
||||
return false;
|
||||
}
|
||||
castSpell = true;
|
||||
else if (spellInfo.HasAttribute(SpellAttr1.CastWhenLearned))
|
||||
CastSpell(this, spellId, true);
|
||||
castSpell = true;
|
||||
|
||||
if (castSpell)
|
||||
{
|
||||
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
|
||||
|
||||
if (traitDefinitionId.HasValue)
|
||||
{
|
||||
TraitConfig traitConfig = GetTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID);
|
||||
if (traitConfig != null)
|
||||
{
|
||||
int traitEntryIndex = traitConfig.Entries.FindIndexIf(traitEntry =>
|
||||
{
|
||||
return CliDB.TraitNodeEntryStorage.LookupByKey(traitEntry.TraitNodeEntryID)?.TraitDefinitionID == traitDefinitionId;
|
||||
});
|
||||
|
||||
int rank = 0;
|
||||
if (traitEntryIndex >= 0)
|
||||
rank = traitConfig.Entries[traitEntryIndex].Rank + traitConfig.Entries[traitEntryIndex].GrantedRanks;
|
||||
|
||||
if (rank > 0)
|
||||
{
|
||||
var traitDefinitionEffectPoints = TraitMgr.GetTraitDefinitionEffectPointModifiers(traitDefinitionId.Value);
|
||||
if (traitDefinitionEffectPoints != null)
|
||||
{
|
||||
foreach (TraitDefinitionEffectPointsRecord traitDefinitionEffectPoint in traitDefinitionEffectPoints)
|
||||
{
|
||||
if (traitDefinitionEffectPoint.EffectIndex >= spellInfo.GetEffects().Count)
|
||||
continue;
|
||||
|
||||
float basePoints = Global.DB2Mgr.GetCurveValueAt((uint)traitDefinitionEffectPoint.CurveID, rank);
|
||||
if (traitDefinitionEffectPoint.GetOperationType() == TraitPointsOperationType.Multiply)
|
||||
basePoints *= spellInfo.GetEffect((uint)traitDefinitionEffectPoint.EffectIndex).CalcBaseValue(this, null, 0, -1);
|
||||
|
||||
args.AddSpellMod(SpellValueMod.BasePoint0 + traitDefinitionEffectPoint.EffectIndex, (int)basePoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CastSpell(this, spellId, args);
|
||||
if (spellInfo.HasEffect(SpellEffectName.SkillStep))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (traitDefinitionId.HasValue)
|
||||
{
|
||||
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitDefinitionId.Value);
|
||||
if (traitDefinition != null)
|
||||
AddOverrideSpell(traitDefinition.OverridesSpellID, spellId);
|
||||
}
|
||||
|
||||
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
|
||||
uint freeProfs = GetFreePrimaryProfessionPoints();
|
||||
@@ -3525,5 +3591,6 @@ namespace Game.Entities
|
||||
public bool Active;
|
||||
public bool Dependent;
|
||||
public bool Disabled;
|
||||
public int? TraitDefinitionId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +349,8 @@ namespace Game.Entities
|
||||
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
|
||||
}
|
||||
|
||||
ApplyTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID, false);
|
||||
|
||||
// Remove spec specific spells
|
||||
RemoveSpecializationSpells();
|
||||
|
||||
@@ -357,6 +359,16 @@ namespace Game.Entities
|
||||
|
||||
SetActiveTalentGroup(spec.OrderIndex);
|
||||
SetPrimarySpecialization(spec.Id);
|
||||
int specTraitConfigIndex = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||
&& traitConfig.ChrSpecializationID == spec.Id
|
||||
&& ((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) != TraitCombatConfigFlags.None;
|
||||
});
|
||||
if (specTraitConfigIndex >= 0)
|
||||
SetActiveCombatTraitConfigID(m_activePlayerData.TraitConfigs[specTraitConfigIndex].ID);
|
||||
else
|
||||
SetActiveCombatTraitConfigID(0);
|
||||
|
||||
foreach (var talentInfo in CliDB.TalentStorage.Values)
|
||||
{
|
||||
@@ -399,20 +411,11 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID, true);
|
||||
|
||||
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().AddCallback(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);
|
||||
}));
|
||||
StartLoadingActionButtons();
|
||||
|
||||
UpdateDisplayPower();
|
||||
PowerType pw = GetPowerType();
|
||||
@@ -477,6 +480,48 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
void StartLoadingActionButtons(Action callback = null)
|
||||
{
|
||||
uint traitConfigId = 0;
|
||||
|
||||
TraitConfig traitConfig = GetTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID);
|
||||
if (traitConfig != null)
|
||||
{
|
||||
int usedSavedTraitConfigIndex = m_activePlayerData.TraitConfigs.FindIndexIf(savedConfig =>
|
||||
{
|
||||
return (TraitConfigType)(int)savedConfig.Type == TraitConfigType.Combat
|
||||
&& ((TraitCombatConfigFlags)(int)savedConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) == TraitCombatConfigFlags.None
|
||||
&& ((TraitCombatConfigFlags)(int)savedConfig.CombatConfigFlags & TraitCombatConfigFlags.SharedActionBars) == TraitCombatConfigFlags.None
|
||||
&& savedConfig.LocalIdentifier == traitConfig.LocalIdentifier;
|
||||
});
|
||||
|
||||
if (usedSavedTraitConfigIndex >= 0)
|
||||
traitConfigId = (uint)(int)m_activePlayerData.TraitConfigs[usedSavedTraitConfigIndex].ID;
|
||||
}
|
||||
|
||||
// load them asynchronously
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACTIONS_SPEC);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, GetActiveTalentGroup());
|
||||
stmt.AddValue(2, traitConfigId);
|
||||
|
||||
var myGuid = GetGUID();
|
||||
|
||||
WorldSession mySess = GetSession();
|
||||
mySess.GetQueryProcessor().AddCallback(DB.Characters.AsyncQuery(stmt)
|
||||
.WithCallback(result =>
|
||||
{
|
||||
// safe callback, we can't pass this pointer directly
|
||||
// in case player logs out before db response (player would be deleted in that case)
|
||||
Player thisPlayer = mySess.GetPlayer();
|
||||
if (thisPlayer != null && thisPlayer.GetGUID() == myGuid)
|
||||
thisPlayer.LoadActions(result);
|
||||
|
||||
if (callback != null)
|
||||
callback();
|
||||
}));
|
||||
}
|
||||
|
||||
public Dictionary<uint, PlayerSpellState> GetTalentMap(uint spec) { return _specializationInfo.Talents[spec]; }
|
||||
public List<uint> GetGlyphs(byte spec) { return _specializationInfo.Glyphs[spec]; }
|
||||
|
||||
@@ -803,5 +848,357 @@ namespace Game.Entities
|
||||
{
|
||||
return GetPvpTalentMap(activeTalentGroup).Contains(talentID);
|
||||
}
|
||||
|
||||
//Traits
|
||||
public void CreateTraitConfig(TraitConfigPacket traitConfig)
|
||||
{
|
||||
int configId = TraitMgr.GenerateNewTraitConfigId();
|
||||
bool hasConfigId(int id)
|
||||
{
|
||||
return m_activePlayerData.TraitConfigs.FindIndexIf(config => config.ID == id) >= 0;
|
||||
}
|
||||
|
||||
while (hasConfigId(configId))
|
||||
configId = TraitMgr.GenerateNewTraitConfigId();
|
||||
|
||||
traitConfig.ID = configId;
|
||||
|
||||
int traitConfigIndex = m_activePlayerData.TraitConfigs.Size();
|
||||
AddTraitConfig(traitConfig);
|
||||
|
||||
foreach (TraitEntry grantedEntry in TraitMgr.GetGrantedTraitEntriesForConfig(traitConfig, this))
|
||||
{
|
||||
var entryIndex = traitConfig.Entries.Find(entry => entry.TraitNodeID == grantedEntry.TraitNodeID && entry.TraitNodeEntryID == grantedEntry.TraitNodeEntryID);
|
||||
if (entryIndex == null)
|
||||
{
|
||||
TraitConfig value = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, traitConfigIndex);
|
||||
AddDynamicUpdateFieldValue(value.ModifyValue(value.Entries), grantedEntry);
|
||||
}
|
||||
}
|
||||
|
||||
m_traitConfigStates[(int)configId] = PlayerSpellState.Changed;
|
||||
}
|
||||
|
||||
void AddTraitConfig(TraitConfigPacket traitConfig)
|
||||
{
|
||||
var setter = new TraitConfig();
|
||||
setter.ModifyValue(setter.ID).SetValue(traitConfig.ID);
|
||||
setter.ModifyValue(setter.Name).SetValue(traitConfig.Name);
|
||||
setter.ModifyValue(setter.Type).SetValue((int)traitConfig.Type);
|
||||
setter.ModifyValue(setter.SkillLineID).SetValue((int)traitConfig.SkillLineID);
|
||||
setter.ModifyValue(setter.ChrSpecializationID).SetValue(traitConfig.ChrSpecializationID);
|
||||
setter.ModifyValue(setter.CombatConfigFlags).SetValue((int)traitConfig.CombatConfigFlags);
|
||||
setter.ModifyValue(setter.LocalIdentifier).SetValue(traitConfig.LocalIdentifier);
|
||||
setter.ModifyValue(setter.TraitSystemID).SetValue(traitConfig.TraitSystemID);
|
||||
|
||||
AddDynamicUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs), setter);
|
||||
|
||||
foreach (TraitEntryPacket traitEntry in traitConfig.Entries)
|
||||
{
|
||||
TraitEntry newEntry = new();
|
||||
newEntry.TraitNodeID = traitEntry.TraitNodeID;
|
||||
newEntry.TraitNodeEntryID = traitEntry.TraitNodeEntryID;
|
||||
newEntry.Rank = traitEntry.Rank;
|
||||
newEntry.GrantedRanks = traitEntry.GrantedRanks;
|
||||
AddDynamicUpdateFieldValue(setter.ModifyValue(setter.Entries), newEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public TraitConfig GetTraitConfig(int configId)
|
||||
{
|
||||
int index = m_activePlayerData.TraitConfigs.FindIndexIf(config => config.ID == configId);
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
return m_activePlayerData.TraitConfigs[index];
|
||||
}
|
||||
|
||||
public void UpdateTraitConfig(TraitConfigPacket newConfig, int savedConfigId, bool withCastTime)
|
||||
{
|
||||
int index = m_activePlayerData.TraitConfigs.FindIndexIf(config => config.ID == newConfig.ID);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
if (withCastTime)
|
||||
{
|
||||
CastSpell(this, TraitMgr.COMMIT_COMBAT_TRAIT_CONFIG_CHANGES_SPELL_ID, new CastSpellExtraArgs(SpellValueMod.BasePoint0, savedConfigId).SetCustomArg(newConfig));
|
||||
return;
|
||||
}
|
||||
|
||||
bool isActiveConfig = true;
|
||||
bool loadActionButtons = false;
|
||||
switch ((TraitConfigType)(int)m_activePlayerData.TraitConfigs[index].Type)
|
||||
{
|
||||
case TraitConfigType.Combat:
|
||||
isActiveConfig = newConfig.ID == m_activePlayerData.ActiveCombatTraitConfigID;
|
||||
loadActionButtons = m_activePlayerData.TraitConfigs[index].LocalIdentifier != newConfig.LocalIdentifier;
|
||||
break;
|
||||
case TraitConfigType.Profession:
|
||||
isActiveConfig = HasSkill((uint)(int)m_activePlayerData.TraitConfigs[index].SkillLineID);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Action finalizeTraitConfigUpdate = () =>
|
||||
{
|
||||
TraitConfig newTraitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, index);
|
||||
SetUpdateFieldValue(newTraitConfig.ModifyValue(newTraitConfig.LocalIdentifier), newConfig.LocalIdentifier);
|
||||
|
||||
ApplyTraitEntryChanges(newConfig.ID, newConfig, isActiveConfig, true);
|
||||
|
||||
if (savedConfigId != 0)
|
||||
ApplyTraitEntryChanges(savedConfigId, newConfig, false, false);
|
||||
|
||||
if (((TraitCombatConfigFlags)(int)newConfig.CombatConfigFlags).HasFlag(TraitCombatConfigFlags.StarterBuild))
|
||||
SetTraitConfigUseStarterBuild(newConfig.ID, true);
|
||||
};
|
||||
|
||||
if (loadActionButtons)
|
||||
{
|
||||
SQLTransaction trans = new SQLTransaction();
|
||||
_SaveActions(trans);
|
||||
DB.Characters.CommitTransaction(trans);
|
||||
|
||||
StartLoadingActionButtons(finalizeTraitConfigUpdate);
|
||||
}
|
||||
else
|
||||
finalizeTraitConfigUpdate();
|
||||
}
|
||||
|
||||
void ApplyTraitEntryChanges(int editedConfigId, TraitConfigPacket newConfig, bool applyTraits, bool consumeCurrencies)
|
||||
{
|
||||
int editedIndex = m_activePlayerData.TraitConfigs.FindIndexIf(config => config.ID == editedConfigId);
|
||||
if (editedIndex < 0)
|
||||
return;
|
||||
|
||||
TraitConfig editedConfig = m_activePlayerData.TraitConfigs[editedIndex];
|
||||
|
||||
// remove traits not found in new config
|
||||
List<int> entryIndicesToRemove = new();
|
||||
for (int i = 0; i < editedConfig.Entries.Size(); ++i)
|
||||
{
|
||||
TraitEntry oldEntry = editedConfig.Entries[i];
|
||||
var entryItr = newConfig.Entries.Find(ufEntry => ufEntry.TraitNodeID == oldEntry.TraitNodeID && ufEntry.TraitNodeEntryID == oldEntry.TraitNodeEntryID);
|
||||
if (entryItr != null)
|
||||
continue;
|
||||
|
||||
if (applyTraits)
|
||||
ApplyTraitEntry(oldEntry.TraitNodeEntryID, 0, 0, false);
|
||||
|
||||
entryIndicesToRemove.Add(i);
|
||||
}
|
||||
|
||||
foreach (int indexToRemove in entryIndicesToRemove)
|
||||
{
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, editedIndex);
|
||||
RemoveDynamicUpdateFieldValue(traitConfig.ModifyValue(traitConfig.Entries), indexToRemove);
|
||||
}
|
||||
|
||||
List<TraitEntryPacket> costEntries = new();
|
||||
|
||||
// apply new traits
|
||||
for (var i = 0; i < newConfig.Entries.Count; ++i)
|
||||
{
|
||||
TraitEntryPacket newEntry = newConfig.Entries[i];
|
||||
int oldEntryIndex = editedConfig.Entries.FindIndexIf(ufEntry => ufEntry.TraitNodeID == newEntry.TraitNodeID && ufEntry.TraitNodeEntryID == newEntry.TraitNodeEntryID);
|
||||
if (oldEntryIndex < 0)
|
||||
{
|
||||
if (consumeCurrencies)
|
||||
costEntries.Add(newEntry);
|
||||
|
||||
TraitConfig newTraitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, editedIndex);
|
||||
TraitEntry newUfEntry = new();
|
||||
newUfEntry.TraitNodeID = newEntry.TraitNodeID;
|
||||
newUfEntry.TraitNodeEntryID = newEntry.TraitNodeEntryID;
|
||||
newUfEntry.Rank = newEntry.Rank;
|
||||
newUfEntry.GrantedRanks = newEntry.GrantedRanks;
|
||||
|
||||
AddDynamicUpdateFieldValue(newTraitConfig.ModifyValue(newTraitConfig.Entries), newUfEntry);
|
||||
|
||||
if (applyTraits)
|
||||
ApplyTraitEntry(newUfEntry.TraitNodeEntryID, newUfEntry.Rank, 0, true);
|
||||
}
|
||||
else if (newEntry.Rank != editedConfig.Entries[oldEntryIndex].Rank || newEntry.GrantedRanks != editedConfig.Entries[oldEntryIndex].GrantedRanks)
|
||||
{
|
||||
if (consumeCurrencies && newEntry.Rank > editedConfig.Entries[oldEntryIndex].Rank)
|
||||
{
|
||||
TraitEntryPacket costEntry = new();
|
||||
costEntry.Rank -= editedConfig.Entries[oldEntryIndex].Rank;
|
||||
costEntries.Add(newEntry);
|
||||
}
|
||||
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, editedIndex);
|
||||
TraitEntry traitEntry = traitConfig.ModifyValue(traitConfig.Entries, oldEntryIndex);
|
||||
traitEntry.Rank = newEntry.Rank;
|
||||
traitEntry.GrantedRanks = newEntry.GrantedRanks;
|
||||
SetUpdateFieldValue(traitConfig.Entries, oldEntryIndex, traitEntry);
|
||||
|
||||
if (applyTraits)
|
||||
ApplyTraitEntry(newEntry.TraitNodeEntryID, newEntry.Rank, newEntry.GrantedRanks, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (consumeCurrencies)
|
||||
{
|
||||
Dictionary<int, int> currencies = new();
|
||||
foreach (TraitEntryPacket costEntry in costEntries)
|
||||
TraitMgr.FillSpentCurrenciesMap(costEntry, currencies);
|
||||
|
||||
foreach (var (traitCurrencyId, amount) in currencies)
|
||||
{
|
||||
TraitCurrencyRecord traitCurrency = CliDB.TraitCurrencyStorage.LookupByKey(traitCurrencyId);
|
||||
if (traitCurrency == null)
|
||||
continue;
|
||||
|
||||
switch (traitCurrency.GetCurrencyType())
|
||||
{
|
||||
case TraitCurrencyType.Gold:
|
||||
ModifyMoney(-amount);
|
||||
break;
|
||||
case TraitCurrencyType.CurrencyTypesBased:
|
||||
ModifyCurrency((CurrencyTypes)traitCurrency.CurrencyTypesID, -amount);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_traitConfigStates[(int)editedConfigId] = PlayerSpellState.Changed;
|
||||
}
|
||||
|
||||
public void RenameTraitConfig(int editedConfigId, string newName)
|
||||
{
|
||||
int editedIndex = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.ID == editedConfigId
|
||||
&& (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||
&& ((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) == TraitCombatConfigFlags.None;
|
||||
});
|
||||
if (editedIndex < 0)
|
||||
return;
|
||||
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, editedIndex);
|
||||
SetUpdateFieldValue(traitConfig.ModifyValue(traitConfig.Name), newName);
|
||||
|
||||
m_traitConfigStates[editedConfigId] = PlayerSpellState.Changed;
|
||||
}
|
||||
|
||||
public void DeleteTraitConfig(int deletedConfigId)
|
||||
{
|
||||
int deletedIndex = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.ID == deletedConfigId
|
||||
&& (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||
&& ((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) == TraitCombatConfigFlags.None;
|
||||
});
|
||||
if (deletedIndex < 0)
|
||||
return;
|
||||
|
||||
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(m_activePlayerData)
|
||||
.ModifyValue(m_activePlayerData.TraitConfigs), deletedIndex);
|
||||
|
||||
m_traitConfigStates[deletedConfigId] = PlayerSpellState.Removed;
|
||||
}
|
||||
|
||||
void ApplyTraitConfig(int configId, bool apply)
|
||||
{
|
||||
TraitConfig traitConfig = GetTraitConfig(configId);
|
||||
if (traitConfig == null)
|
||||
return;
|
||||
|
||||
foreach (TraitEntry traitEntry in traitConfig.Entries)
|
||||
ApplyTraitEntry(traitEntry.TraitNodeEntryID, traitEntry.Rank, traitEntry.GrantedRanks, apply);
|
||||
}
|
||||
|
||||
void ApplyTraitEntry(int traitNodeEntryId, int rank, int grantedRanks, bool apply)
|
||||
{
|
||||
TraitNodeEntryRecord traitNodeEntry = CliDB.TraitNodeEntryStorage.LookupByKey(traitNodeEntryId);
|
||||
if (traitNodeEntry == null)
|
||||
return;
|
||||
|
||||
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitNodeEntry.TraitDefinitionID);
|
||||
if (traitDefinition == null)
|
||||
return;
|
||||
|
||||
if (traitDefinition.SpellID != 0)
|
||||
{
|
||||
if (apply)
|
||||
LearnSpell(traitDefinition.SpellID, true, 0, false, traitNodeEntry.TraitDefinitionID);
|
||||
else
|
||||
RemoveSpell(traitDefinition.SpellID);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTraitConfigUseStarterBuild(int traitConfigId, bool useStarterBuild)
|
||||
{
|
||||
int configIndex = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.ID == traitConfigId
|
||||
&& (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||
&& ((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) != TraitCombatConfigFlags.None;
|
||||
});
|
||||
if (configIndex < 0)
|
||||
return;
|
||||
|
||||
bool currentlyUsesStarterBuild = ((TraitCombatConfigFlags)(int)m_activePlayerData.TraitConfigs[configIndex].CombatConfigFlags).HasFlag(TraitCombatConfigFlags.StarterBuild);
|
||||
if (currentlyUsesStarterBuild == useStarterBuild)
|
||||
return;
|
||||
|
||||
if (useStarterBuild)
|
||||
{
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, configIndex);
|
||||
SetUpdateFieldFlagValue(traitConfig.ModifyValue(traitConfig.CombatConfigFlags), (int)TraitCombatConfigFlags.StarterBuild);
|
||||
}
|
||||
else
|
||||
{
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, configIndex);
|
||||
RemoveUpdateFieldFlagValue(traitConfig.ModifyValue(traitConfig.CombatConfigFlags), (int)TraitCombatConfigFlags.StarterBuild);
|
||||
}
|
||||
|
||||
m_traitConfigStates[(int)traitConfigId] = PlayerSpellState.Changed;
|
||||
}
|
||||
|
||||
public void SetTraitConfigUseSharedActionBars(int traitConfigId, bool usesSharedActionBars, bool isLastSelectedSavedConfig)
|
||||
{
|
||||
int configIndex = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||
{
|
||||
return traitConfig.ID == traitConfigId
|
||||
&& (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||
&& ((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) == TraitCombatConfigFlags.None;
|
||||
});
|
||||
if (configIndex < 0)
|
||||
return;
|
||||
|
||||
bool currentlyUsesSharedActionBars = ((TraitCombatConfigFlags)(int)m_activePlayerData.TraitConfigs[configIndex].CombatConfigFlags).HasFlag(TraitCombatConfigFlags.SharedActionBars);
|
||||
if (currentlyUsesSharedActionBars == usesSharedActionBars)
|
||||
return;
|
||||
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, configIndex);
|
||||
if (usesSharedActionBars)
|
||||
{
|
||||
SetUpdateFieldFlagValue(traitConfig.ModifyValue(traitConfig.CombatConfigFlags), (int)TraitCombatConfigFlags.SharedActionBars);
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_ACTION_BY_TRAIT_CONFIG);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, traitConfigId);
|
||||
DB.Characters.Execute(stmt);
|
||||
|
||||
if (isLastSelectedSavedConfig)
|
||||
StartLoadingActionButtons(); // load action buttons that were saved in shared mode
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveUpdateFieldFlagValue(traitConfig.ModifyValue(traitConfig.CombatConfigFlags), (int)TraitCombatConfigFlags.SharedActionBars);
|
||||
|
||||
// trigger a save with traitConfigId
|
||||
foreach (var (_, button) in m_actionButtons)
|
||||
if (button.uState != ActionButtonUpdateState.Deleted)
|
||||
button.uState = ActionButtonUpdateState.New;
|
||||
}
|
||||
|
||||
m_traitConfigStates[traitConfigId] = PlayerSpellState.Changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6839,7 +6839,9 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
//New shit
|
||||
|
||||
void SetActiveCombatTraitConfigID(int traitConfigId) { SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ActiveCombatTraitConfigID), (uint)traitConfigId); }
|
||||
|
||||
void InitPrimaryProfessions()
|
||||
{
|
||||
SetFreePrimaryProfessions(WorldConfig.GetUIntValue(WorldCfg.MaxPrimaryTradeSkill));
|
||||
|
||||
Reference in New Issue
Block a user