Core/Players: Implemented new talent system
Port From (https://github.com/TrinityCore/TrinityCore/commit/0cc5ab8372f19dad7412038d52dcd39db5e0e171)
This commit is contained in:
@@ -2213,6 +2213,23 @@ namespace Framework.Constants
|
|||||||
SharedActionBars = 0x4
|
SharedActionBars = 0x4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum TraitCondFlags
|
||||||
|
{
|
||||||
|
None = 0x0,
|
||||||
|
IsGate = 0x1,
|
||||||
|
IsAlwaysMet = 0x2,
|
||||||
|
IsSufficient = 0x4,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TraitConditionType
|
||||||
|
{
|
||||||
|
Available = 0,
|
||||||
|
Visible = 1,
|
||||||
|
Granted = 2,
|
||||||
|
Increased = 3
|
||||||
|
}
|
||||||
|
|
||||||
public enum TraitConfigType
|
public enum TraitConfigType
|
||||||
{
|
{
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
@@ -2221,6 +2238,66 @@ namespace Framework.Constants
|
|||||||
Generic = 3
|
Generic = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TraitCurrencyType
|
||||||
|
{
|
||||||
|
Gold = 0,
|
||||||
|
CurrencyTypesBased = 1,
|
||||||
|
TraitSourced = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TraitEdgeType
|
||||||
|
{
|
||||||
|
VisualOnly = 0,
|
||||||
|
DeprecatedRankConnection = 1,
|
||||||
|
SufficientForAvailability = 2,
|
||||||
|
RequiredForAvailability = 3,
|
||||||
|
MutuallyExclusive = 4,
|
||||||
|
DeprecatedSelectionOption = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TraitNodeEntryType
|
||||||
|
{
|
||||||
|
SpendHex = 0,
|
||||||
|
SpendSquare = 1,
|
||||||
|
SpendCircle = 2,
|
||||||
|
SpendSmallCircle = 3,
|
||||||
|
DeprecatedSelect = 4,
|
||||||
|
DragAndDrop = 5,
|
||||||
|
SpendDiamond = 6,
|
||||||
|
ProfPath = 7,
|
||||||
|
ProfPerk = 8,
|
||||||
|
ProfPathUnlock = 9
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum TraitNodeGroupFlag
|
||||||
|
{
|
||||||
|
None = 0x0,
|
||||||
|
AvailableByDefault = 0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TraitNodeType
|
||||||
|
{
|
||||||
|
Single = 0,
|
||||||
|
Tiered = 1,
|
||||||
|
Selection = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TraitPointsOperationType
|
||||||
|
{
|
||||||
|
None = -1,
|
||||||
|
Set = 0,
|
||||||
|
Multiply = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum TraitTreeFlag
|
||||||
|
{
|
||||||
|
None = 0x0,
|
||||||
|
CannotRefund = 0x1,
|
||||||
|
HideSingleRankNumbers = 0x2
|
||||||
|
}
|
||||||
|
|
||||||
public enum UiMapFlag
|
public enum UiMapFlag
|
||||||
{
|
{
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ namespace Framework.Database
|
|||||||
|
|
||||||
PrepareStatement(CharStatements.SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?");
|
PrepareStatement(CharStatements.SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?");
|
||||||
PrepareStatement(CharStatements.SEL_CHARACTER_INVENTORY, "SELECT " + SelectItemInstanceContent + ", bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid WHERE ci.guid = ? ORDER BY (ii.flags & 0x80000) ASC, bag ASC, slot ASC");
|
PrepareStatement(CharStatements.SEL_CHARACTER_INVENTORY, "SELECT " + SelectItemInstanceContent + ", bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid WHERE ci.guid = ? ORDER BY (ii.flags & 0x80000) ASC, bag ASC, slot ASC");
|
||||||
PrepareStatement(CharStatements.SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button");
|
|
||||||
PrepareStatement(CharStatements.SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?");
|
PrepareStatement(CharStatements.SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?");
|
||||||
PrepareStatement(CharStatements.SEL_CHARACTER_SOCIALLIST, "SELECT cs.friend, c.account, cs.flags, cs.note FROM character_social cs JOIN characters c ON c.guid = cs.friend WHERE cs.guid = ? AND c.deleteinfos_name IS NULL LIMIT 255");
|
PrepareStatement(CharStatements.SEL_CHARACTER_SOCIALLIST, "SELECT cs.friend, c.account, cs.flags, cs.note FROM character_social cs JOIN characters c ON c.guid = cs.friend WHERE cs.guid = ? AND c.deleteinfos_name IS NULL LIMIT 255");
|
||||||
PrepareStatement(CharStatements.SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ, orientation FROM character_homebind WHERE guid = ?");
|
PrepareStatement(CharStatements.SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ, orientation FROM character_homebind WHERE guid = ?");
|
||||||
@@ -148,7 +147,7 @@ namespace Framework.Database
|
|||||||
PrepareStatement(CharStatements.DEL_CHARACTER_FAVORITE_AUCTIONS_BY_CHAR, "DELETE FROM character_favorite_auctions WHERE guid = ?");
|
PrepareStatement(CharStatements.DEL_CHARACTER_FAVORITE_AUCTIONS_BY_CHAR, "DELETE FROM character_favorite_auctions WHERE guid = ?");
|
||||||
PrepareStatement(CharStatements.SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?");
|
PrepareStatement(CharStatements.SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?");
|
||||||
|
|
||||||
PrepareStatement(CharStatements.SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button");
|
PrepareStatement(CharStatements.SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? AND traitConfigId = ? ORDER BY button");
|
||||||
PrepareStatement(CharStatements.SEL_MAILITEMS, "SELECT " + SelectItemInstanceContent + ", ii.owner_guid, m.id FROM mail_items mi INNER JOIN mail m ON mi.mail_id = m.id LEFT JOIN item_instance ii ON mi.item_guid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid WHERE m.receiver = ?");
|
PrepareStatement(CharStatements.SEL_MAILITEMS, "SELECT " + SelectItemInstanceContent + ", ii.owner_guid, m.id FROM mail_items mi INNER JOIN mail m ON mi.mail_id = m.id LEFT JOIN item_instance ii ON mi.item_guid = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid WHERE m.receiver = ?");
|
||||||
PrepareStatement(CharStatements.SEL_MAILITEMS_ARTIFACT, "SELECT a.itemGuid, a.xp, a.artifactAppearanceId, a.artifactTierId, ap.artifactPowerId, ap.purchasedRank FROM item_instance_artifact_powers ap LEFT JOIN item_instance_artifact a ON ap.itemGuid = a.itemGuid INNER JOIN mail_items mi ON a.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?");
|
PrepareStatement(CharStatements.SEL_MAILITEMS_ARTIFACT, "SELECT a.itemGuid, a.xp, a.artifactAppearanceId, a.artifactTierId, ap.artifactPowerId, ap.purchasedRank FROM item_instance_artifact_powers ap LEFT JOIN item_instance_artifact a ON ap.itemGuid = a.itemGuid INNER JOIN mail_items mi ON a.itemGuid = mi.item_guid INNER JOIN mail m ON mi.mail_id = m.id WHERE m.receiver = ?");
|
||||||
PrepareStatement(CharStatements.SEL_MAILITEMS_AZERITE, "SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel, " +
|
PrepareStatement(CharStatements.SEL_MAILITEMS_AZERITE, "SELECT iz.itemGuid, iz.xp, iz.level, iz.knowledgeLevel, " +
|
||||||
@@ -605,9 +604,10 @@ namespace Framework.Database
|
|||||||
PrepareStatement(CharStatements.DEL_CHAR_PVP_TALENT, "DELETE FROM character_pvp_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.DEL_CHAR_SKILLS, "DELETE FROM character_skills WHERE guid = ?");
|
||||||
PrepareStatement(CharStatements.UPD_CHAR_MONEY, "UPDATE characters SET money = ? 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 (?, ?, ?, ?, ?)");
|
PrepareStatement(CharStatements.INS_CHAR_ACTION, "INSERT INTO character_action (guid, spec, traitConfigId, button, action, type) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
PrepareStatement(CharStatements.UPD_CHAR_ACTION, "UPDATE character_action SET action = ?, type = ? WHERE guid = ? AND button = ? AND spec = ?");
|
PrepareStatement(CharStatements.UPD_CHAR_ACTION, "UPDATE character_action SET action = ?, type = ? WHERE guid = ? AND button = ? AND spec = ? AND traitConfigId = ?");
|
||||||
PrepareStatement(CharStatements.DEL_CHAR_ACTION_BY_BUTTON_SPEC, "DELETE FROM character_action WHERE guid = ? and button = ? and spec = ?");
|
PrepareStatement(CharStatements.DEL_CHAR_ACTION_BY_BUTTON_SPEC, "DELETE FROM character_action WHERE guid = ? and button = ? and spec = ? AND traitConfigId = ?");
|
||||||
|
PrepareStatement(CharStatements.DEL_CHAR_ACTION_BY_TRAIT_CONFIG, "DELETE FROM character_action WHERE guid = ? AND traitConfigId = ?");
|
||||||
PrepareStatement(CharStatements.DEL_CHAR_INVENTORY_BY_ITEM, "DELETE FROM character_inventory WHERE item = ?");
|
PrepareStatement(CharStatements.DEL_CHAR_INVENTORY_BY_ITEM, "DELETE FROM character_inventory WHERE item = ?");
|
||||||
PrepareStatement(CharStatements.DEL_CHAR_INVENTORY_BY_BAG_SLOT, "DELETE FROM character_inventory WHERE bag = ? AND slot = ? AND guid = ?");
|
PrepareStatement(CharStatements.DEL_CHAR_INVENTORY_BY_BAG_SLOT, "DELETE FROM character_inventory WHERE bag = ? AND slot = ? AND guid = ?");
|
||||||
PrepareStatement(CharStatements.UPD_MAIL, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?");
|
PrepareStatement(CharStatements.UPD_MAIL, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?");
|
||||||
@@ -639,6 +639,14 @@ namespace Framework.Database
|
|||||||
PrepareStatement(CharStatements.UPD_CHAR_LIST_SLOT, "UPDATE characters SET slot = ? WHERE guid = ? AND account = ?");
|
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.INS_CHAR_FISHINGSTEPS, "INSERT INTO character_fishingsteps (guid, fishingSteps) VALUES (?, ?)");
|
||||||
PrepareStatement(CharStatements.DEL_CHAR_FISHINGSTEPS, "DELETE FROM character_fishingsteps WHERE guid = ?");
|
PrepareStatement(CharStatements.DEL_CHAR_FISHINGSTEPS, "DELETE FROM character_fishingsteps WHERE guid = ?");
|
||||||
|
PrepareStatement(CharStatements.SEL_CHAR_TRAIT_ENTRIES, "SELECT traitConfigId, traitNodeId, traitNodeEntryId, `rank`, grantedRanks FROM character_trait_entry WHERE guid = ?");
|
||||||
|
PrepareStatement(CharStatements.INS_CHAR_TRAIT_ENTRIES, "INSERT INTO character_trait_entry (guid, traitConfigId, traitNodeId, traitNodeEntryId, `rank`, grantedRanks) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
PrepareStatement(CharStatements.DEL_CHAR_TRAIT_ENTRIES, "DELETE FROM character_trait_entry WHERE guid = ? AND traitConfigId = ?");
|
||||||
|
PrepareStatement(CharStatements.DEL_CHAR_TRAIT_ENTRIES_BY_CHAR, "DELETE FROM character_trait_entry WHERE guid = ?");
|
||||||
|
PrepareStatement(CharStatements.SEL_CHAR_TRAIT_CONFIGS, "SELECT traitConfigId, type, chrSpecializationId, combatConfigFlags, localIdentifier, skillLineId, traitSystemId, `name` FROM character_trait_config WHERE guid = ?");
|
||||||
|
PrepareStatement(CharStatements.INS_CHAR_TRAIT_CONFIGS, "INSERT INTO character_trait_config (guid, traitConfigId, type, chrSpecializationId, combatConfigFlags, localIdentifier, skillLineId, traitSystemId, `name`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
PrepareStatement(CharStatements.DEL_CHAR_TRAIT_CONFIGS, "DELETE FROM character_trait_config WHERE guid = ? AND traitConfigId = ?");
|
||||||
|
PrepareStatement(CharStatements.DEL_CHAR_TRAIT_CONFIGS_BY_CHAR, "DELETE FROM character_trait_config WHERE guid = ?");
|
||||||
|
|
||||||
// Void Storage
|
// Void Storage
|
||||||
PrepareStatement(CharStatements.SEL_CHAR_VOID_STORAGE, "SELECT itemId, itemEntry, slot, creatorGuid, randomBonusListId, fixedScalingLevel, artifactKnowledgeLevel, context, bonusListIDs FROM character_void_storage WHERE playerGuid = ?");
|
PrepareStatement(CharStatements.SEL_CHAR_VOID_STORAGE, "SELECT itemId, itemEntry, slot, creatorGuid, randomBonusListId, fixedScalingLevel, artifactKnowledgeLevel, context, bonusListIDs FROM character_void_storage WHERE playerGuid = ?");
|
||||||
@@ -821,7 +829,6 @@ namespace Framework.Database
|
|||||||
|
|
||||||
SEL_CHARACTER_REPUTATION,
|
SEL_CHARACTER_REPUTATION,
|
||||||
SEL_CHARACTER_INVENTORY,
|
SEL_CHARACTER_INVENTORY,
|
||||||
SEL_CHARACTER_ACTIONS,
|
|
||||||
SEL_CHARACTER_ACTIONS_SPEC,
|
SEL_CHARACTER_ACTIONS_SPEC,
|
||||||
SEL_MAIL_COUNT,
|
SEL_MAIL_COUNT,
|
||||||
SEL_CHARACTER_SOCIALLIST,
|
SEL_CHARACTER_SOCIALLIST,
|
||||||
@@ -1233,6 +1240,7 @@ namespace Framework.Database
|
|||||||
INS_CHAR_ACTION,
|
INS_CHAR_ACTION,
|
||||||
UPD_CHAR_ACTION,
|
UPD_CHAR_ACTION,
|
||||||
DEL_CHAR_ACTION_BY_BUTTON_SPEC,
|
DEL_CHAR_ACTION_BY_BUTTON_SPEC,
|
||||||
|
DEL_CHAR_ACTION_BY_TRAIT_CONFIG,
|
||||||
DEL_CHAR_INVENTORY_BY_ITEM,
|
DEL_CHAR_INVENTORY_BY_ITEM,
|
||||||
DEL_CHAR_INVENTORY_BY_BAG_SLOT,
|
DEL_CHAR_INVENTORY_BY_BAG_SLOT,
|
||||||
UPD_MAIL,
|
UPD_MAIL,
|
||||||
@@ -1262,6 +1270,14 @@ namespace Framework.Database
|
|||||||
UPD_CHAR_LIST_SLOT,
|
UPD_CHAR_LIST_SLOT,
|
||||||
INS_CHAR_FISHINGSTEPS,
|
INS_CHAR_FISHINGSTEPS,
|
||||||
DEL_CHAR_FISHINGSTEPS,
|
DEL_CHAR_FISHINGSTEPS,
|
||||||
|
SEL_CHAR_TRAIT_ENTRIES,
|
||||||
|
INS_CHAR_TRAIT_ENTRIES,
|
||||||
|
DEL_CHAR_TRAIT_ENTRIES,
|
||||||
|
DEL_CHAR_TRAIT_ENTRIES_BY_CHAR,
|
||||||
|
SEL_CHAR_TRAIT_CONFIGS,
|
||||||
|
INS_CHAR_TRAIT_CONFIGS,
|
||||||
|
DEL_CHAR_TRAIT_CONFIGS,
|
||||||
|
DEL_CHAR_TRAIT_CONFIGS_BY_CHAR,
|
||||||
|
|
||||||
SEL_CHAR_VOID_STORAGE,
|
SEL_CHAR_VOID_STORAGE,
|
||||||
REP_CHAR_VOID_STORAGE_ITEM,
|
REP_CHAR_VOID_STORAGE_ITEM,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2724,13 +2724,13 @@ namespace Game
|
|||||||
case UnitConditionVariable.IsEnemy:
|
case UnitConditionVariable.IsEnemy:
|
||||||
return (otherUnit && unit.GetReactionTo(otherUnit) <= ReputationRank.Hostile) ? 1 : 0;
|
return (otherUnit && unit.GetReactionTo(otherUnit) <= ReputationRank.Hostile) ? 1 : 0;
|
||||||
case UnitConditionVariable.IsSpecMelee:
|
case UnitConditionVariable.IsSpecMelee:
|
||||||
return (unit.IsPlayer() && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Flags.HasFlag(ChrSpecializationFlag.Melee)) ? 1 : 0;
|
return (unit.IsPlayer() && unit.ToPlayer().GetPrimarySpecialization() != 0 && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Flags.HasFlag(ChrSpecializationFlag.Melee)) ? 1 : 0;
|
||||||
case UnitConditionVariable.IsSpecTank:
|
case UnitConditionVariable.IsSpecTank:
|
||||||
return (unit.IsPlayer() && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Role == 0) ? 1 : 0;
|
return (unit.IsPlayer() && unit.ToPlayer().GetPrimarySpecialization() != 0 && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Role == 0) ? 1 : 0;
|
||||||
case UnitConditionVariable.IsSpecRanged:
|
case UnitConditionVariable.IsSpecRanged:
|
||||||
return (unit.IsPlayer() && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Flags.HasFlag(ChrSpecializationFlag.Ranged)) ? 1 : 0;
|
return (unit.IsPlayer() && unit.ToPlayer().GetPrimarySpecialization() != 0 && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Flags.HasFlag(ChrSpecializationFlag.Ranged)) ? 1 : 0;
|
||||||
case UnitConditionVariable.IsSpecHealer:
|
case UnitConditionVariable.IsSpecHealer:
|
||||||
return (unit.IsPlayer() && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Role == 1) ? 1 : 0;
|
return (unit.IsPlayer() && unit.ToPlayer().GetPrimarySpecialization() != 0 && CliDB.ChrSpecializationStorage.LookupByKey(unit.ToPlayer().GetPrimarySpecialization()).Role == 1) ? 1 : 0;
|
||||||
case UnitConditionVariable.IsPlayerControlledNPC:
|
case UnitConditionVariable.IsPlayerControlledNPC:
|
||||||
return unit.IsCreature() && unit.HasUnitFlag(UnitFlags.PlayerControlled) ? 1 : 0;
|
return unit.IsCreature() && unit.HasUnitFlag(UnitFlags.PlayerControlled) ? 1 : 0;
|
||||||
case UnitConditionVariable.IsDying:
|
case UnitConditionVariable.IsDying:
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ namespace Game.DataStorage
|
|||||||
SceneScriptTextStorage = ReadDB2<SceneScriptTextRecord>("SceneScriptText.db2", HotfixStatements.SEL_SCENE_SCRIPT_TEXT);
|
SceneScriptTextStorage = ReadDB2<SceneScriptTextRecord>("SceneScriptText.db2", HotfixStatements.SEL_SCENE_SCRIPT_TEXT);
|
||||||
SkillLineStorage = ReadDB2<SkillLineRecord>("SkillLine.db2", HotfixStatements.SEL_SKILL_LINE, HotfixStatements.SEL_SKILL_LINE_LOCALE);
|
SkillLineStorage = ReadDB2<SkillLineRecord>("SkillLine.db2", HotfixStatements.SEL_SKILL_LINE, HotfixStatements.SEL_SKILL_LINE_LOCALE);
|
||||||
SkillLineAbilityStorage = ReadDB2<SkillLineAbilityRecord>("SkillLineAbility.db2", HotfixStatements.SEL_SKILL_LINE_ABILITY);
|
SkillLineAbilityStorage = ReadDB2<SkillLineAbilityRecord>("SkillLineAbility.db2", HotfixStatements.SEL_SKILL_LINE_ABILITY);
|
||||||
|
SkillLineXTraitTreeStorage = ReadDB2<SkillLineXTraitTreeRecord>("SkillLineXTraitTree.db2", HotfixStatements.SEL_SKILL_LINE_X_TRAIT_TREE);
|
||||||
SkillRaceClassInfoStorage = ReadDB2<SkillRaceClassInfoRecord>("SkillRaceClassInfo.db2", HotfixStatements.SEL_SKILL_RACE_CLASS_INFO);
|
SkillRaceClassInfoStorage = ReadDB2<SkillRaceClassInfoRecord>("SkillRaceClassInfo.db2", HotfixStatements.SEL_SKILL_RACE_CLASS_INFO);
|
||||||
SoulbindConduitRankStorage = ReadDB2<SoulbindConduitRankRecord>("SoulbindConduitRank.db2", HotfixStatements.SEL_SOULBIND_CONDUIT_RANK);
|
SoulbindConduitRankStorage = ReadDB2<SoulbindConduitRankRecord>("SoulbindConduitRank.db2", HotfixStatements.SEL_SOULBIND_CONDUIT_RANK);
|
||||||
SoundKitStorage = ReadDB2<SoundKitRecord>("SoundKit.db2", HotfixStatements.SEL_SOUND_KIT);
|
SoundKitStorage = ReadDB2<SoundKitRecord>("SoundKit.db2", HotfixStatements.SEL_SOUND_KIT);
|
||||||
@@ -326,6 +327,29 @@ namespace Game.DataStorage
|
|||||||
TaxiPathNodeStorage = ReadDB2<TaxiPathNodeRecord>("TaxiPathNode.db2", HotfixStatements.SEL_TAXI_PATH_NODE);
|
TaxiPathNodeStorage = ReadDB2<TaxiPathNodeRecord>("TaxiPathNode.db2", HotfixStatements.SEL_TAXI_PATH_NODE);
|
||||||
TotemCategoryStorage = ReadDB2<TotemCategoryRecord>("TotemCategory.db2", HotfixStatements.SEL_TOTEM_CATEGORY, HotfixStatements.SEL_TOTEM_CATEGORY_LOCALE);
|
TotemCategoryStorage = ReadDB2<TotemCategoryRecord>("TotemCategory.db2", HotfixStatements.SEL_TOTEM_CATEGORY, HotfixStatements.SEL_TOTEM_CATEGORY_LOCALE);
|
||||||
ToyStorage = ReadDB2<ToyRecord>("Toy.db2", HotfixStatements.SEL_TOY, HotfixStatements.SEL_TOY_LOCALE);
|
ToyStorage = ReadDB2<ToyRecord>("Toy.db2", HotfixStatements.SEL_TOY, HotfixStatements.SEL_TOY_LOCALE);
|
||||||
|
TraitCondStorage = ReadDB2<TraitCondRecord>("TraitCond.db2", HotfixStatements.SEL_TRAIT_COND);
|
||||||
|
TraitCostStorage = ReadDB2<TraitCostRecord>("TraitCost.db2", HotfixStatements.SEL_TRAIT_COST);
|
||||||
|
TraitCurrencyStorage = ReadDB2<TraitCurrencyRecord>("TraitCurrency.db2", HotfixStatements.SEL_TRAIT_CURRENCY);
|
||||||
|
TraitCurrencySourceStorage = ReadDB2<TraitCurrencySourceRecord>("TraitCurrencySource.db2", HotfixStatements.SEL_TRAIT_CURRENCY_SOURCE, HotfixStatements.SEL_TRAIT_CURRENCY_SOURCE_LOCALE);
|
||||||
|
TraitDefinitionStorage = ReadDB2<TraitDefinitionRecord>("TraitDefinition.db2", HotfixStatements.SEL_TRAIT_DEFINITION, HotfixStatements.SEL_TRAIT_DEFINITION_LOCALE);
|
||||||
|
TraitDefinitionEffectPointsStorage = ReadDB2<TraitDefinitionEffectPointsRecord>("TraitDefinitionEffectPoints.db2", HotfixStatements.SEL_TRAIT_DEFINITION_EFFECT_POINTS);
|
||||||
|
TraitEdgeStorage = ReadDB2<TraitEdgeRecord>("TraitEdge.db2", HotfixStatements.SEL_TRAIT_EDGE);
|
||||||
|
TraitNodeStorage = ReadDB2<TraitNodeRecord>("TraitNode.db2", HotfixStatements.SEL_TRAIT_NODE);
|
||||||
|
TraitNodeEntryStorage = ReadDB2<TraitNodeEntryRecord>("TraitNodeEntry.db2", HotfixStatements.SEL_TRAIT_NODE_ENTRY);
|
||||||
|
TraitNodeEntryXTraitCondStorage = ReadDB2<TraitNodeEntryXTraitCondRecord>("TraitNodeEntryXTraitCond.db2", HotfixStatements.SEL_TRAIT_NODE_ENTRY_X_TRAIT_COND);
|
||||||
|
TraitNodeEntryXTraitCostStorage = ReadDB2<TraitNodeEntryXTraitCostRecord>("TraitNodeEntryXTraitCost.db2", HotfixStatements.SEL_TRAIT_NODE_ENTRY_X_TRAIT_COST);
|
||||||
|
TraitNodeGroupStorage = ReadDB2<TraitNodeGroupRecord>("TraitNodeGroup.db2", HotfixStatements.SEL_TRAIT_NODE_GROUP);
|
||||||
|
TraitNodeGroupXTraitCondStorage = ReadDB2<TraitNodeGroupXTraitCondRecord>("TraitNodeGroupXTraitCond.db2", HotfixStatements.SEL_TRAIT_NODE_GROUP_X_TRAIT_COND);
|
||||||
|
TraitNodeGroupXTraitCostStorage = ReadDB2<TraitNodeGroupXTraitCostRecord>("TraitNodeGroupXTraitCost.db2", HotfixStatements.SEL_TRAIT_NODE_GROUP_X_TRAIT_COST);
|
||||||
|
TraitNodeGroupXTraitNodeStorage = ReadDB2<TraitNodeGroupXTraitNodeRecord>("TraitNodeGroupXTraitNode.db2", HotfixStatements.SEL_TRAIT_NODE_GROUP_X_TRAIT_NODE);
|
||||||
|
TraitNodeXTraitCondStorage = ReadDB2<TraitNodeXTraitCondRecord>("TraitNodeXTraitCond.db2", HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_COND);
|
||||||
|
TraitNodeXTraitCostStorage = ReadDB2<TraitNodeXTraitCostRecord>("TraitNodeXTraitCost.db2", HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_COST);
|
||||||
|
TraitNodeXTraitNodeEntryStorage = ReadDB2<TraitNodeXTraitNodeEntryRecord>("TraitNodeXTraitNodeEntry.db2", HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_NODE_ENTRY);
|
||||||
|
TraitTreeStorage = ReadDB2<TraitTreeRecord>("TraitTree.db2", HotfixStatements.SEL_TRAIT_TREE);
|
||||||
|
TraitTreeLoadoutStorage = ReadDB2<TraitTreeLoadoutRecord>("TraitTreeLoadout.db2", HotfixStatements.SEL_TRAIT_TREE_LOADOUT);
|
||||||
|
TraitTreeLoadoutEntryStorage = ReadDB2<TraitTreeLoadoutEntryRecord>("TraitTreeLoadoutEntry.db2", HotfixStatements.SEL_TRAIT_TREE_LOADOUT_ENTRY);
|
||||||
|
TraitTreeXTraitCostStorage = ReadDB2<TraitTreeXTraitCostRecord>("TraitTreeXTraitCost.db2", HotfixStatements.SEL_TRAIT_TREE_X_TRAIT_COST);
|
||||||
|
TraitTreeXTraitCurrencyStorage = ReadDB2<TraitTreeXTraitCurrencyRecord>("TraitTreeXTraitCurrency.db2", HotfixStatements.SEL_TRAIT_TREE_X_TRAIT_CURRENCY);
|
||||||
TransmogHolidayStorage = ReadDB2<TransmogHolidayRecord>("TransmogHoliday.db2", HotfixStatements.SEL_TRANSMOG_HOLIDAY);
|
TransmogHolidayStorage = ReadDB2<TransmogHolidayRecord>("TransmogHoliday.db2", HotfixStatements.SEL_TRANSMOG_HOLIDAY);
|
||||||
TransmogIllusionStorage = ReadDB2<TransmogIllusionRecord>("TransmogIllusion.db2", HotfixStatements.SEL_TRANSMOG_ILLUSION);
|
TransmogIllusionStorage = ReadDB2<TransmogIllusionRecord>("TransmogIllusion.db2", HotfixStatements.SEL_TRANSMOG_ILLUSION);
|
||||||
TransmogSetStorage = ReadDB2<TransmogSetRecord>("TransmogSet.db2", HotfixStatements.SEL_TRANSMOG_SET, HotfixStatements.SEL_TRANSMOG_SET_LOCALE);
|
TransmogSetStorage = ReadDB2<TransmogSetRecord>("TransmogSet.db2", HotfixStatements.SEL_TRANSMOG_SET, HotfixStatements.SEL_TRANSMOG_SET_LOCALE);
|
||||||
@@ -674,6 +698,7 @@ namespace Game.DataStorage
|
|||||||
public static DB6Storage<SceneScriptTextRecord> SceneScriptTextStorage;
|
public static DB6Storage<SceneScriptTextRecord> SceneScriptTextStorage;
|
||||||
public static DB6Storage<SkillLineRecord> SkillLineStorage;
|
public static DB6Storage<SkillLineRecord> SkillLineStorage;
|
||||||
public static DB6Storage<SkillLineAbilityRecord> SkillLineAbilityStorage;
|
public static DB6Storage<SkillLineAbilityRecord> SkillLineAbilityStorage;
|
||||||
|
public static DB6Storage<SkillLineXTraitTreeRecord> SkillLineXTraitTreeStorage;
|
||||||
public static DB6Storage<SkillRaceClassInfoRecord> SkillRaceClassInfoStorage;
|
public static DB6Storage<SkillRaceClassInfoRecord> SkillRaceClassInfoStorage;
|
||||||
public static DB6Storage<SoulbindConduitRankRecord> SoulbindConduitRankStorage;
|
public static DB6Storage<SoulbindConduitRankRecord> SoulbindConduitRankStorage;
|
||||||
public static DB6Storage<SoundKitRecord> SoundKitStorage;
|
public static DB6Storage<SoundKitRecord> SoundKitStorage;
|
||||||
@@ -725,6 +750,29 @@ namespace Game.DataStorage
|
|||||||
public static DB6Storage<TaxiPathNodeRecord> TaxiPathNodeStorage;
|
public static DB6Storage<TaxiPathNodeRecord> TaxiPathNodeStorage;
|
||||||
public static DB6Storage<TotemCategoryRecord> TotemCategoryStorage;
|
public static DB6Storage<TotemCategoryRecord> TotemCategoryStorage;
|
||||||
public static DB6Storage<ToyRecord> ToyStorage;
|
public static DB6Storage<ToyRecord> ToyStorage;
|
||||||
|
public static DB6Storage<TraitCondRecord> TraitCondStorage;
|
||||||
|
public static DB6Storage<TraitCostRecord> TraitCostStorage;
|
||||||
|
public static DB6Storage<TraitCurrencyRecord> TraitCurrencyStorage;
|
||||||
|
public static DB6Storage<TraitCurrencySourceRecord> TraitCurrencySourceStorage;
|
||||||
|
public static DB6Storage<TraitDefinitionRecord> TraitDefinitionStorage;
|
||||||
|
public static DB6Storage<TraitDefinitionEffectPointsRecord> TraitDefinitionEffectPointsStorage;
|
||||||
|
public static DB6Storage<TraitEdgeRecord> TraitEdgeStorage;
|
||||||
|
public static DB6Storage<TraitNodeRecord> TraitNodeStorage;
|
||||||
|
public static DB6Storage<TraitNodeEntryRecord> TraitNodeEntryStorage;
|
||||||
|
public static DB6Storage<TraitNodeEntryXTraitCondRecord> TraitNodeEntryXTraitCondStorage;
|
||||||
|
public static DB6Storage<TraitNodeEntryXTraitCostRecord> TraitNodeEntryXTraitCostStorage;
|
||||||
|
public static DB6Storage<TraitNodeGroupRecord> TraitNodeGroupStorage;
|
||||||
|
public static DB6Storage<TraitNodeGroupXTraitCondRecord> TraitNodeGroupXTraitCondStorage;
|
||||||
|
public static DB6Storage<TraitNodeGroupXTraitCostRecord> TraitNodeGroupXTraitCostStorage;
|
||||||
|
public static DB6Storage<TraitNodeGroupXTraitNodeRecord> TraitNodeGroupXTraitNodeStorage;
|
||||||
|
public static DB6Storage<TraitNodeXTraitCondRecord> TraitNodeXTraitCondStorage;
|
||||||
|
public static DB6Storage<TraitNodeXTraitCostRecord> TraitNodeXTraitCostStorage;
|
||||||
|
public static DB6Storage<TraitNodeXTraitNodeEntryRecord> TraitNodeXTraitNodeEntryStorage;
|
||||||
|
public static DB6Storage<TraitTreeRecord> TraitTreeStorage;
|
||||||
|
public static DB6Storage<TraitTreeLoadoutRecord> TraitTreeLoadoutStorage;
|
||||||
|
public static DB6Storage<TraitTreeLoadoutEntryRecord> TraitTreeLoadoutEntryStorage;
|
||||||
|
public static DB6Storage<TraitTreeXTraitCostRecord> TraitTreeXTraitCostStorage;
|
||||||
|
public static DB6Storage<TraitTreeXTraitCurrencyRecord> TraitTreeXTraitCurrencyStorage;
|
||||||
public static DB6Storage<TransmogHolidayRecord> TransmogHolidayStorage;
|
public static DB6Storage<TransmogHolidayRecord> TransmogHolidayStorage;
|
||||||
public static DB6Storage<TransmogIllusionRecord> TransmogIllusionStorage;
|
public static DB6Storage<TransmogIllusionRecord> TransmogIllusionStorage;
|
||||||
public static DB6Storage<TransmogSetRecord> TransmogSetStorage;
|
public static DB6Storage<TransmogSetRecord> TransmogSetStorage;
|
||||||
@@ -874,7 +922,7 @@ namespace Game.DataStorage
|
|||||||
{
|
{
|
||||||
return row.Wins * row.Xp;
|
return row.Wins * row.Xp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float GetIlvlStatMultiplier(GtGenericMultByILvlRecord row, InventoryType invType)
|
public static float GetIlvlStatMultiplier(GtGenericMultByILvlRecord row, InventoryType invType)
|
||||||
{
|
{
|
||||||
switch (invType)
|
switch (invType)
|
||||||
|
|||||||
@@ -1985,19 +1985,24 @@ namespace Game.DataStorage
|
|||||||
public SkillRaceClassInfoRecord GetSkillRaceClassInfo(uint skill, Race race, Class class_)
|
public SkillRaceClassInfoRecord GetSkillRaceClassInfo(uint skill, Race race, Class class_)
|
||||||
{
|
{
|
||||||
var bounds = _skillRaceClassInfoBySkill.LookupByKey(skill);
|
var bounds = _skillRaceClassInfoBySkill.LookupByKey(skill);
|
||||||
foreach (var record in bounds)
|
foreach (var skllRaceClassInfo in bounds)
|
||||||
{
|
{
|
||||||
if (record.RaceMask != 0 && !Convert.ToBoolean(record.RaceMask & SharedConst.GetMaskForRace(race)))
|
if (skllRaceClassInfo.RaceMask != 0 && !Convert.ToBoolean(skllRaceClassInfo.RaceMask & SharedConst.GetMaskForRace(race)))
|
||||||
continue;
|
continue;
|
||||||
if (record.ClassMask != 0 && !Convert.ToBoolean(record.ClassMask & (1 << ((byte)class_ - 1))))
|
if (skllRaceClassInfo.ClassMask != 0 && !Convert.ToBoolean(skllRaceClassInfo.ClassMask & (1 << ((byte)class_ - 1))))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
return record;
|
return skllRaceClassInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<SkillRaceClassInfoRecord> GetSkillRaceClassInfo(uint skill)
|
||||||
|
{
|
||||||
|
return _skillRaceClassInfoBySkill.LookupByKey(skill);
|
||||||
|
}
|
||||||
|
|
||||||
public SoulbindConduitRankRecord GetSoulbindConduitRank(int soulbindConduitId, int rank)
|
public SoulbindConduitRankRecord GetSoulbindConduitRank(int soulbindConduitId, int rank)
|
||||||
{
|
{
|
||||||
return _soulbindConduitRanks.LookupByKey(Tuple.Create(soulbindConduitId, rank));
|
return _soulbindConduitRanks.LookupByKey(Tuple.Create(soulbindConduitId, rank));
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ namespace Game.DataStorage
|
|||||||
|
|
||||||
public sealed class ChrSpecializationRecord
|
public sealed class ChrSpecializationRecord
|
||||||
{
|
{
|
||||||
public string Name;
|
public LocalizedString Name;
|
||||||
public string FemaleName;
|
public string FemaleName;
|
||||||
public string Description;
|
public string Description;
|
||||||
public uint Id;
|
public uint Id;
|
||||||
|
|||||||
@@ -124,6 +124,14 @@ namespace Game.DataStorage
|
|||||||
public ushort SkillupSkillLineID;
|
public ushort SkillupSkillLineID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class SkillLineXTraitTreeRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int SkillLineID;
|
||||||
|
public int TraitTreeID;
|
||||||
|
public int OrderIndex;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class SkillRaceClassInfoRecord
|
public sealed class SkillRaceClassInfoRecord
|
||||||
{
|
{
|
||||||
public uint Id;
|
public uint Id;
|
||||||
|
|||||||
@@ -103,6 +103,222 @@ namespace Game.DataStorage
|
|||||||
public int RequiredTransmogHoliday;
|
public int RequiredTransmogHoliday;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class TraitCondRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int CondType;
|
||||||
|
public int TraitTreeID;
|
||||||
|
public int GrantedRanks;
|
||||||
|
public uint QuestID;
|
||||||
|
public uint AchievementID;
|
||||||
|
public int SpecSetID;
|
||||||
|
public int TraitNodeGroupID;
|
||||||
|
public int TraitNodeID;
|
||||||
|
public int TraitCurrencyID;
|
||||||
|
public int SpentAmountRequired;
|
||||||
|
public int Flags;
|
||||||
|
public int RequiredLevel;
|
||||||
|
public int FreeSharedStringID;
|
||||||
|
public int SpendMoreSharedStringID;
|
||||||
|
|
||||||
|
public TraitConditionType GetCondType() { return (TraitConditionType)CondType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitCostRecord
|
||||||
|
{
|
||||||
|
public string InternalName;
|
||||||
|
public uint Id;
|
||||||
|
public int Amount;
|
||||||
|
public int TraitCurrencyID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitCurrencyRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int Type;
|
||||||
|
public int CurrencyTypesID;
|
||||||
|
public int Flags;
|
||||||
|
public int Icon;
|
||||||
|
|
||||||
|
public TraitCurrencyType GetCurrencyType() { return (TraitCurrencyType)Type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitCurrencySourceRecord
|
||||||
|
{
|
||||||
|
public LocalizedString Requirement;
|
||||||
|
public uint Id;
|
||||||
|
public int TraitCurrencyID;
|
||||||
|
public int Amount;
|
||||||
|
public uint QuestID;
|
||||||
|
public uint AchievementID;
|
||||||
|
public uint PlayerLevel;
|
||||||
|
public int TraitNodeEntryID;
|
||||||
|
public int OrderIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitDefinitionRecord
|
||||||
|
{
|
||||||
|
public LocalizedString OverrideName;
|
||||||
|
public LocalizedString OverrideSubtext;
|
||||||
|
public LocalizedString OverrideDescription;
|
||||||
|
public uint Id;
|
||||||
|
public uint SpellID;
|
||||||
|
public int OverrideIcon;
|
||||||
|
public uint OverridesSpellID;
|
||||||
|
public uint VisibleSpellID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitDefinitionEffectPointsRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitDefinitionID;
|
||||||
|
public int EffectIndex;
|
||||||
|
public int OperationType;
|
||||||
|
public int CurveID;
|
||||||
|
|
||||||
|
public TraitPointsOperationType GetOperationType() { return (TraitPointsOperationType)OperationType; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitEdgeRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int VisualStyle;
|
||||||
|
public int LeftTraitNodeID;
|
||||||
|
public int RightTraitNodeID;
|
||||||
|
public int Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitTreeID;
|
||||||
|
public int PosX;
|
||||||
|
public int PosY;
|
||||||
|
public sbyte Type;
|
||||||
|
public int Flags;
|
||||||
|
|
||||||
|
public TraitNodeType GetNodeType() { return (TraitNodeType)Type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeEntryRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitDefinitionID;
|
||||||
|
public int MaxRanks;
|
||||||
|
public byte NodeRecordType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeEntryXTraitCondRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitCondID;
|
||||||
|
public uint TraitNodeEntryID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeEntryXTraitCostRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitNodeEntryID;
|
||||||
|
public int TraitCostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeGroupRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitTreeID;
|
||||||
|
public int Flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeGroupXTraitCondRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitCondID;
|
||||||
|
public int TraitNodeGroupID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeGroupXTraitCostRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitNodeGroupID;
|
||||||
|
public int TraitCostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeGroupXTraitNodeRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitNodeGroupID;
|
||||||
|
public int TraitNodeID;
|
||||||
|
public int Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeXTraitCondRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitCondID;
|
||||||
|
public int TraitNodeID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeXTraitCostRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public uint TraitNodeID;
|
||||||
|
public int TraitCostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitNodeXTraitNodeEntryRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitNodeID;
|
||||||
|
public int TraitNodeEntryID;
|
||||||
|
public int Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitTreeRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitSystemID;
|
||||||
|
public int Unused1000_1;
|
||||||
|
public int FirstTraitNodeID;
|
||||||
|
public int PlayerConditionID;
|
||||||
|
public int Flags;
|
||||||
|
public float Unused1000_2;
|
||||||
|
public float Unused1000_3;
|
||||||
|
|
||||||
|
public TraitTreeFlag GetFlags() { return (TraitTreeFlag)Flags; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitTreeLoadoutRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitTreeID;
|
||||||
|
public int ChrSpecializationID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitTreeLoadoutEntryRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int TraitTreeLoadoutID;
|
||||||
|
public int SelectedTraitNodeID;
|
||||||
|
public int SelectedTraitNodeEntryID;
|
||||||
|
public int NumPoints;
|
||||||
|
public int OrderIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitTreeXTraitCostRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public uint TraitTreeID;
|
||||||
|
public int TraitCostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TraitTreeXTraitCurrencyRecord
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public int Index;
|
||||||
|
public int TraitTreeID;
|
||||||
|
public int TraitCurrencyID;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class TransmogIllusionRecord
|
public sealed class TransmogIllusionRecord
|
||||||
{
|
{
|
||||||
public uint Id;
|
public uint Id;
|
||||||
@@ -161,5 +377,4 @@ namespace Game.DataStorage
|
|||||||
public uint TimeIndex;
|
public uint TimeIndex;
|
||||||
public uint GameObjectsID;
|
public uint GameObjectsID;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -141,7 +141,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public bool HasValue() { return _hasValue; }
|
public bool HasValue() { return _hasValue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateFieldArray<T> where T : new()
|
public class UpdateFieldArray<T> where T : new()
|
||||||
{
|
{
|
||||||
public T[] _values;
|
public T[] _values;
|
||||||
@@ -437,6 +437,12 @@ namespace Game.Entities
|
|||||||
return updateField;
|
return updateField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UpdateFieldString ModifyValue(UpdateFieldString updateField)
|
||||||
|
{
|
||||||
|
MarkChanged(updateField);
|
||||||
|
return updateField;
|
||||||
|
}
|
||||||
|
|
||||||
public ref U ModifyValue<U>(UpdateFieldArray<U> updateField, int index) where U : new()
|
public ref U ModifyValue<U>(UpdateFieldArray<U> updateField, int index) where U : new()
|
||||||
{
|
{
|
||||||
MarkChanged(updateField, index);
|
MarkChanged(updateField, index);
|
||||||
@@ -472,6 +478,12 @@ namespace Game.Entities
|
|||||||
_changesMask.Set(updateField.Bit);
|
_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()
|
public void MarkChanged<U>(UpdateFieldArray<U> updateField, int index) where U : new()
|
||||||
{
|
{
|
||||||
_changesMask.Set(updateField.Bit);
|
_changesMask.Set(updateField.Bit);
|
||||||
|
|||||||
@@ -824,7 +824,7 @@ namespace Game.Entities
|
|||||||
public abstract void BuildValuesCreate(WorldPacket data, Player target);
|
public abstract void BuildValuesCreate(WorldPacket data, Player target);
|
||||||
public abstract void BuildValuesUpdate(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()))
|
if (!newValue.Equals(updateField.GetValue()))
|
||||||
{
|
{
|
||||||
@@ -863,7 +863,7 @@ namespace Game.Entities
|
|||||||
SetUpdateFieldValue(ref value, (T)(value | (dynamic)flag));
|
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");
|
//static_assert(std::is_integral < T >::value, "SetUpdateFieldFlagValue must be used with integral types");
|
||||||
SetUpdateFieldValue(updateField, (T)(updateField.GetValue() & ~(dynamic)flag));
|
SetUpdateFieldValue(updateField, (T)(updateField.GetValue() & ~(dynamic)flag));
|
||||||
|
|||||||
@@ -1010,6 +1010,146 @@ namespace Game.Entities
|
|||||||
while (result.NextRow());
|
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)
|
void _LoadGlyphs(SQLResult result)
|
||||||
{
|
{
|
||||||
// SELECT talentGroup, glyphId from character_glyphs WHERE guid = ?
|
// SELECT talentGroup, glyphId from character_glyphs WHERE guid = ?
|
||||||
@@ -1863,6 +2003,23 @@ namespace Game.Entities
|
|||||||
|
|
||||||
void _SaveActions(SQLTransaction trans)
|
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;
|
PreparedStatement stmt;
|
||||||
|
|
||||||
foreach (var pair in m_actionButtons.ToList())
|
foreach (var pair in m_actionButtons.ToList())
|
||||||
@@ -1873,9 +2030,10 @@ namespace Game.Entities
|
|||||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_ACTION);
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_ACTION);
|
||||||
stmt.AddValue(0, GetGUID().GetCounter());
|
stmt.AddValue(0, GetGUID().GetCounter());
|
||||||
stmt.AddValue(1, GetActiveTalentGroup());
|
stmt.AddValue(1, GetActiveTalentGroup());
|
||||||
stmt.AddValue(2, pair.Key);
|
stmt.AddValue(2, traitConfigId);
|
||||||
stmt.AddValue(3, pair.Value.GetAction());
|
stmt.AddValue(3, pair.Key);
|
||||||
stmt.AddValue(4, (byte)pair.Value.GetButtonType());
|
stmt.AddValue(4, pair.Value.GetAction());
|
||||||
|
stmt.AddValue(5, (byte)pair.Value.GetButtonType());
|
||||||
trans.Append(stmt);
|
trans.Append(stmt);
|
||||||
|
|
||||||
pair.Value.uState = ActionButtonUpdateState.UnChanged;
|
pair.Value.uState = ActionButtonUpdateState.UnChanged;
|
||||||
@@ -1887,6 +2045,7 @@ namespace Game.Entities
|
|||||||
stmt.AddValue(2, GetGUID().GetCounter());
|
stmt.AddValue(2, GetGUID().GetCounter());
|
||||||
stmt.AddValue(3, pair.Key);
|
stmt.AddValue(3, pair.Key);
|
||||||
stmt.AddValue(4, GetActiveTalentGroup());
|
stmt.AddValue(4, GetActiveTalentGroup());
|
||||||
|
stmt.AddValue(5, traitConfigId);
|
||||||
trans.Append(stmt);
|
trans.Append(stmt);
|
||||||
|
|
||||||
pair.Value.uState = ActionButtonUpdateState.UnChanged;
|
pair.Value.uState = ActionButtonUpdateState.UnChanged;
|
||||||
@@ -1896,6 +2055,7 @@ namespace Game.Entities
|
|||||||
stmt.AddValue(0, GetGUID().GetCounter());
|
stmt.AddValue(0, GetGUID().GetCounter());
|
||||||
stmt.AddValue(1, pair.Key);
|
stmt.AddValue(1, pair.Key);
|
||||||
stmt.AddValue(2, GetActiveTalentGroup());
|
stmt.AddValue(2, GetActiveTalentGroup());
|
||||||
|
stmt.AddValue(3, traitConfigId);
|
||||||
trans.Append(stmt);
|
trans.Append(stmt);
|
||||||
|
|
||||||
m_actionButtons.Remove(pair.Key);
|
m_actionButtons.Remove(pair.Key);
|
||||||
@@ -2135,6 +2295,99 @@ namespace Game.Entities
|
|||||||
trans.Append(stmt);
|
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)
|
public void _SaveMail(SQLTransaction trans)
|
||||||
{
|
{
|
||||||
PreparedStatement stmt;
|
PreparedStatement stmt;
|
||||||
@@ -3074,6 +3327,8 @@ namespace Game.Entities
|
|||||||
LearnDefaultSkills();
|
LearnDefaultSkills();
|
||||||
LearnCustomSpells();
|
LearnCustomSpells();
|
||||||
|
|
||||||
|
_LoadTraits(holder.GetResult(PlayerLoginQueryLoad.TraitConfigs), holder.GetResult(PlayerLoginQueryLoad.TraitEntries)); // must be after loading spells
|
||||||
|
|
||||||
// must be before inventory (some items required reputation check)
|
// must be before inventory (some items required reputation check)
|
||||||
reputationMgr.LoadFromDB(holder.GetResult(PlayerLoginQueryLoad.Reputation));
|
reputationMgr.LoadFromDB(holder.GetResult(PlayerLoginQueryLoad.Reputation));
|
||||||
|
|
||||||
@@ -3086,7 +3341,7 @@ namespace Game.Entities
|
|||||||
// update items with duration and realtime
|
// update items with duration and realtime
|
||||||
UpdateItemDuration(time_diff, true);
|
UpdateItemDuration(time_diff, true);
|
||||||
|
|
||||||
_LoadActions(holder.GetResult(PlayerLoginQueryLoad.Actions));
|
StartLoadingActionButtons();
|
||||||
|
|
||||||
// unread mails and next delivery time, actual mails not loaded
|
// unread mails and next delivery time, actual mails not loaded
|
||||||
_LoadMail(holder.GetResult(PlayerLoginQueryLoad.Mails),
|
_LoadMail(holder.GetResult(PlayerLoginQueryLoad.Mails),
|
||||||
@@ -3633,6 +3888,7 @@ namespace Game.Entities
|
|||||||
_SaveMonthlyQuestStatus(characterTransaction);
|
_SaveMonthlyQuestStatus(characterTransaction);
|
||||||
_SaveGlyphs(characterTransaction);
|
_SaveGlyphs(characterTransaction);
|
||||||
_SaveTalents(characterTransaction);
|
_SaveTalents(characterTransaction);
|
||||||
|
_SaveTraits(characterTransaction);
|
||||||
_SaveSpells(characterTransaction);
|
_SaveSpells(characterTransaction);
|
||||||
GetSpellHistory().SaveToDB<Player>(characterTransaction);
|
GetSpellHistory().SaveToDB<Player>(characterTransaction);
|
||||||
_SaveActions(characterTransaction);
|
_SaveActions(characterTransaction);
|
||||||
@@ -4183,6 +4439,14 @@ namespace Game.Entities
|
|||||||
|
|
||||||
Garrison.DeleteFromDB(guid, trans);
|
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);
|
Global.CharacterCacheStorage.DeleteCharacterCacheEntry(playerGuid, name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,6 +257,8 @@ namespace Game.Entities
|
|||||||
uint m_PlayedTimeTotal;
|
uint m_PlayedTimeTotal;
|
||||||
uint m_PlayedTimeLevel;
|
uint m_PlayedTimeLevel;
|
||||||
|
|
||||||
|
Dictionary<int, PlayerSpellState> m_traitConfigStates = new();
|
||||||
|
|
||||||
Dictionary<byte, ActionButton> m_actionButtons = new();
|
Dictionary<byte, ActionButton> m_actionButtons = new();
|
||||||
ObjectGuid m_playerSharingQuest;
|
ObjectGuid m_playerSharingQuest;
|
||||||
uint m_sharedQuestId;
|
uint m_sharedQuestId;
|
||||||
|
|||||||
@@ -1024,13 +1024,17 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool HasSkill(SkillType skill)
|
public bool HasSkill(SkillType skill)
|
||||||
|
{
|
||||||
|
return HasSkill((uint)skill);
|
||||||
|
}
|
||||||
|
public bool HasSkill(uint skill)
|
||||||
{
|
{
|
||||||
if (skill == 0)
|
if (skill == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SkillInfo skillInfoField = m_activePlayerData.Skill;
|
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;
|
return skillStatusData != null && skillStatusData.State != SkillState.Deleted && skillInfoField.SkillRank[skillStatusData.Pos] != 0;
|
||||||
}
|
}
|
||||||
public void SetSkill(SkillType skill, uint step, uint newVal, uint maxVal)
|
public void SetSkill(SkillType skill, uint step, uint newVal, uint maxVal)
|
||||||
@@ -2046,14 +2050,14 @@ namespace Game.Entities
|
|||||||
SendPacket(new SendUnlearnSpells());
|
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);
|
PlayerSpell spell = m_spells.LookupByKey(spellId);
|
||||||
|
|
||||||
bool disabled = (spell != null) && spell.Disabled;
|
bool disabled = (spell != null) && spell.Disabled;
|
||||||
bool active = !disabled || spell.Active;
|
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)
|
// prevent duplicated entires in spell book, also not send if not in world (loading)
|
||||||
if (learning && IsInWorld)
|
if (learning && IsInWorld)
|
||||||
@@ -2061,6 +2065,7 @@ namespace Game.Entities
|
|||||||
LearnedSpells learnedSpells = new();
|
LearnedSpells learnedSpells = new();
|
||||||
LearnedSpellInfo learnedSpellInfo = new();
|
LearnedSpellInfo learnedSpellInfo = new();
|
||||||
learnedSpellInfo.SpellID = spellId;
|
learnedSpellInfo.SpellID = spellId;
|
||||||
|
learnedSpellInfo.TraitDefinitionID = traitDefinitionId;
|
||||||
learnedSpells.SuppressMessaging = suppressMessaging;
|
learnedSpells.SuppressMessaging = suppressMessaging;
|
||||||
learnedSpells.ClientLearnedSpellData.Add(learnedSpellInfo);
|
learnedSpells.ClientLearnedSpellData.Add(learnedSpellInfo);
|
||||||
SendPacket(learnedSpells);
|
SendPacket(learnedSpells);
|
||||||
@@ -2315,7 +2320,7 @@ namespace Game.Entities
|
|||||||
return null;
|
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);
|
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None);
|
||||||
if (spellInfo == null)
|
if (spellInfo == null)
|
||||||
@@ -2394,6 +2399,18 @@ namespace Game.Entities
|
|||||||
dependent_set = true;
|
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
|
// update active state for known spell
|
||||||
if (spell.Active != active && spell.State != PlayerSpellState.Removed && !spell.Disabled)
|
if (spell.Active != active && spell.State != PlayerSpellState.Removed && !spell.Disabled)
|
||||||
{
|
{
|
||||||
@@ -2475,6 +2492,8 @@ namespace Game.Entities
|
|||||||
newspell.Active = active;
|
newspell.Active = active;
|
||||||
newspell.Dependent = dependent;
|
newspell.Dependent = dependent;
|
||||||
newspell.Disabled = disabled;
|
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
|
// 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())
|
if (newspell.Active && !newspell.Disabled && spellInfo.IsRanked())
|
||||||
@@ -2524,26 +2543,73 @@ namespace Game.Entities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool castSpell = false;
|
||||||
|
|
||||||
// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
|
// 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
|
// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
|
||||||
if (!loading && spellInfo.HasAttribute(SpellCustomAttributes.IsTalent) && spellInfo.HasEffect(SpellEffectName.LearnSpell))
|
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)
|
// 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
|
// also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
|
||||||
else if (spellInfo.IsPassive())
|
else if (spellInfo.IsPassive())
|
||||||
{
|
castSpell = HandlePassiveSpellLearn(spellInfo);
|
||||||
if (HandlePassiveSpellLearn(spellInfo))
|
|
||||||
CastSpell(this, spellId, true);
|
|
||||||
}
|
|
||||||
else if (spellInfo.HasEffect(SpellEffectName.SkillStep))
|
else if (spellInfo.HasEffect(SpellEffectName.SkillStep))
|
||||||
{
|
castSpell = true;
|
||||||
CastSpell(this, spellId, true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (spellInfo.HasAttribute(SpellAttr1.CastWhenLearned))
|
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)
|
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
|
||||||
uint freeProfs = GetFreePrimaryProfessionPoints();
|
uint freeProfs = GetFreePrimaryProfessionPoints();
|
||||||
@@ -3525,5 +3591,6 @@ namespace Game.Entities
|
|||||||
public bool Active;
|
public bool Active;
|
||||||
public bool Dependent;
|
public bool Dependent;
|
||||||
public bool Disabled;
|
public bool Disabled;
|
||||||
|
public int? TraitDefinitionId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -349,6 +349,8 @@ namespace Game.Entities
|
|||||||
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
|
RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplyTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID, false);
|
||||||
|
|
||||||
// Remove spec specific spells
|
// Remove spec specific spells
|
||||||
RemoveSpecializationSpells();
|
RemoveSpecializationSpells();
|
||||||
|
|
||||||
@@ -357,6 +359,16 @@ namespace Game.Entities
|
|||||||
|
|
||||||
SetActiveTalentGroup(spec.OrderIndex);
|
SetActiveTalentGroup(spec.OrderIndex);
|
||||||
SetPrimarySpecialization(spec.Id);
|
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)
|
foreach (var talentInfo in CliDB.TalentStorage.Values)
|
||||||
{
|
{
|
||||||
@@ -399,20 +411,11 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplyTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID, true);
|
||||||
|
|
||||||
InitTalentForLevel();
|
InitTalentForLevel();
|
||||||
|
|
||||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACTIONS_SPEC);
|
StartLoadingActionButtons();
|
||||||
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);
|
|
||||||
}));
|
|
||||||
|
|
||||||
UpdateDisplayPower();
|
UpdateDisplayPower();
|
||||||
PowerType pw = GetPowerType();
|
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 Dictionary<uint, PlayerSpellState> GetTalentMap(uint spec) { return _specializationInfo.Talents[spec]; }
|
||||||
public List<uint> GetGlyphs(byte spec) { return _specializationInfo.Glyphs[spec]; }
|
public List<uint> GetGlyphs(byte spec) { return _specializationInfo.Glyphs[spec]; }
|
||||||
|
|
||||||
@@ -803,5 +848,357 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
return GetPvpTalentMap(activeTalentGroup).Contains(talentID);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//New shit
|
|
||||||
|
void SetActiveCombatTraitConfigID(int traitConfigId) { SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ActiveCombatTraitConfigID), (uint)traitConfigId); }
|
||||||
|
|
||||||
void InitPrimaryProfessions()
|
void InitPrimaryProfessions()
|
||||||
{
|
{
|
||||||
SetFreePrimaryProfessions(WorldConfig.GetUIntValue(WorldCfg.MaxPrimaryTradeSkill));
|
SetFreePrimaryProfessions(WorldConfig.GetUIntValue(WorldCfg.MaxPrimaryTradeSkill));
|
||||||
|
|||||||
@@ -2723,10 +2723,6 @@ namespace Game
|
|||||||
stmt.AddValue(0, lowGuid);
|
stmt.AddValue(0, lowGuid);
|
||||||
SetQuery(PlayerLoginQueryLoad.VoidStorage, stmt);
|
SetQuery(PlayerLoginQueryLoad.VoidStorage, stmt);
|
||||||
|
|
||||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_ACTIONS);
|
|
||||||
stmt.AddValue(0, lowGuid);
|
|
||||||
SetQuery(PlayerLoginQueryLoad.Actions, stmt);
|
|
||||||
|
|
||||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_MAIL);
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_MAIL);
|
||||||
stmt.AddValue(0, lowGuid);
|
stmt.AddValue(0, lowGuid);
|
||||||
SetQuery(PlayerLoginQueryLoad.Mails, stmt);
|
SetQuery(PlayerLoginQueryLoad.Mails, stmt);
|
||||||
@@ -2877,6 +2873,14 @@ namespace Game
|
|||||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES);
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES);
|
||||||
stmt.AddValue(0, lowGuid);
|
stmt.AddValue(0, lowGuid);
|
||||||
SetQuery(PlayerLoginQueryLoad.GarrisonFollowerAbilities, stmt);
|
SetQuery(PlayerLoginQueryLoad.GarrisonFollowerAbilities, stmt);
|
||||||
|
|
||||||
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_TRAIT_ENTRIES);
|
||||||
|
stmt.AddValue(0, lowGuid);
|
||||||
|
SetQuery(PlayerLoginQueryLoad.TraitEntries, stmt);
|
||||||
|
|
||||||
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHAR_TRAIT_CONFIGS);
|
||||||
|
stmt.AddValue(0, lowGuid);
|
||||||
|
SetQuery(PlayerLoginQueryLoad.TraitConfigs, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectGuid GetGuid() { return m_guid; }
|
public ObjectGuid GetGuid() { return m_guid; }
|
||||||
@@ -2938,7 +2942,6 @@ namespace Game
|
|||||||
AzeriteMilestonePowers,
|
AzeriteMilestonePowers,
|
||||||
AzeriteUnlockedEssences,
|
AzeriteUnlockedEssences,
|
||||||
AzeriteEmpowered,
|
AzeriteEmpowered,
|
||||||
Actions,
|
|
||||||
Mails,
|
Mails,
|
||||||
MailItems,
|
MailItems,
|
||||||
MailItemsArtifact,
|
MailItemsArtifact,
|
||||||
@@ -2980,6 +2983,8 @@ namespace Game
|
|||||||
GarrisonBuildings,
|
GarrisonBuildings,
|
||||||
GarrisonFollowers,
|
GarrisonFollowers,
|
||||||
GarrisonFollowerAbilities,
|
GarrisonFollowerAbilities,
|
||||||
|
TraitEntries,
|
||||||
|
TraitConfigs,
|
||||||
Max
|
Max
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Framework.Constants;
|
||||||
|
using Game.DataStorage;
|
||||||
|
using Game.Entities;
|
||||||
|
using Game.Networking;
|
||||||
|
using Game.Networking.Packets;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Game
|
||||||
|
{
|
||||||
|
public partial class WorldSession
|
||||||
|
{
|
||||||
|
[WorldPacketHandler(ClientOpcodes.TraitsCommitConfig)]
|
||||||
|
void HandleTraitsCommitConfig(TraitsCommitConfig traitsCommitConfig)
|
||||||
|
{
|
||||||
|
int configId = (int)(uint)traitsCommitConfig.Config.ID;
|
||||||
|
TraitConfig existingConfig = _player.GetTraitConfig(configId);
|
||||||
|
if (existingConfig == null)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedUnknown));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_player.IsInCombat())
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedAffectingCombat));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_player.GetBattleground() && _player.GetBattleground().GetStatus() == BattlegroundStatus.InProgress)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.InPvpMatch));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasRemovedEntries = false;
|
||||||
|
TraitConfigPacket newConfigState = new(existingConfig);
|
||||||
|
foreach (TraitEntryPacket newEntry in traitsCommitConfig.Config.Entries)
|
||||||
|
{
|
||||||
|
TraitEntryPacket traitEntry = newConfigState.Entries.Find(traitEntry => traitEntry.TraitNodeID == newEntry.TraitNodeID && traitEntry.TraitNodeEntryID == newEntry.TraitNodeEntryID);
|
||||||
|
if (traitEntry != null && traitEntry.Rank > newEntry.Rank)
|
||||||
|
{
|
||||||
|
TraitNodeRecord traitNode = CliDB.TraitNodeStorage.LookupByKey(newEntry.TraitNodeID);
|
||||||
|
if (traitNode == null)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedUnknown));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraitTreeRecord traitTree = CliDB.TraitTreeStorage.LookupByKey(traitNode.TraitTreeID);
|
||||||
|
if (traitTree == null)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedUnknown));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traitTree.GetFlags().HasFlag(TraitTreeFlag.CannotRefund))
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedCantRemoveTalent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraitNodeEntryRecord traitNodeEntry = CliDB.TraitNodeEntryStorage.LookupByKey(newEntry.TraitNodeEntryID);
|
||||||
|
if (traitNodeEntry == null)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedUnknown));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitNodeEntry.TraitDefinitionID);
|
||||||
|
if (traitDefinition == null)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)TalentLearnResult.FailedUnknown));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traitDefinition.SpellID != 0 && _player.GetSpellHistory().HasCooldown(traitDefinition.SpellID))
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, traitDefinition.SpellID, (int)TalentLearnResult.FailedCantRemoveTalent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traitDefinition.VisibleSpellID != 0 && _player.GetSpellHistory().HasCooldown((uint)traitDefinition.VisibleSpellID))
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, traitDefinition.VisibleSpellID, (int)TalentLearnResult.FailedCantRemoveTalent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRemovedEntries = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traitEntry != null)
|
||||||
|
{
|
||||||
|
if (newEntry.Rank != 0)
|
||||||
|
traitEntry.Rank = newEntry.Rank;
|
||||||
|
else
|
||||||
|
newConfigState.Entries.RemoveAll(TraitEntrytraitEntry => traitEntry.TraitNodeID == newEntry.TraitNodeID && traitEntry.TraitNodeEntryID == newEntry.TraitNodeEntryID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newConfigState.Entries.Add(newEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
TalentLearnResult validationResult = TraitMgr.ValidateConfig(newConfigState, _player, true);
|
||||||
|
if (validationResult != TalentLearnResult.LearnOk)
|
||||||
|
{
|
||||||
|
SendPacket(new TraitConfigCommitFailed(configId, 0, (int)validationResult));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needsCastTime = newConfigState.Type == TraitConfigType.Combat && hasRemovedEntries;
|
||||||
|
|
||||||
|
if (traitsCommitConfig.SavedLocalIdentifier != 0)
|
||||||
|
newConfigState.LocalIdentifier = traitsCommitConfig.SavedLocalIdentifier;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TraitConfig savedConfig = _player.GetTraitConfig(traitsCommitConfig.SavedLocalIdentifier);
|
||||||
|
if (savedConfig != null)
|
||||||
|
newConfigState.LocalIdentifier = savedConfig.LocalIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
_player.UpdateTraitConfig(newConfigState, traitsCommitConfig.SavedConfigID, needsCastTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.ClassTalentsRequestNewConfig)]
|
||||||
|
void HandleClassTalentsRequestNewConfig(ClassTalentsRequestNewConfig classTalentsRequestNewConfig)
|
||||||
|
{
|
||||||
|
if (classTalentsRequestNewConfig.Config.Type != TraitConfigType.Combat)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((classTalentsRequestNewConfig.Config.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) != (int)TraitCombatConfigFlags.None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long configCount = _player.m_activePlayerData.TraitConfigs._values.Count(traitConfig =>
|
||||||
|
{
|
||||||
|
return (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||||
|
&& ((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags & TraitCombatConfigFlags.ActiveForSpec) == TraitCombatConfigFlags.None;
|
||||||
|
});
|
||||||
|
if (configCount >= TraitMgr.MAX_COMBAT_TRAIT_CONFIGS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int findFreeLocalIdentifier()
|
||||||
|
{
|
||||||
|
int index = 1;
|
||||||
|
while (_player.m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||||
|
{
|
||||||
|
return (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||||
|
&& traitConfig.ChrSpecializationID == _player.GetPrimarySpecialization()
|
||||||
|
&& traitConfig.LocalIdentifier == index;
|
||||||
|
}) >= 0)
|
||||||
|
++index;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
classTalentsRequestNewConfig.Config.ChrSpecializationID = (int)_player.GetPrimarySpecialization();
|
||||||
|
classTalentsRequestNewConfig.Config.LocalIdentifier = findFreeLocalIdentifier();
|
||||||
|
|
||||||
|
foreach (TraitEntry grantedEntry in TraitMgr.GetGrantedTraitEntriesForConfig(classTalentsRequestNewConfig.Config, _player))
|
||||||
|
{
|
||||||
|
var newEntry = classTalentsRequestNewConfig.Config.Entries.Find(entry => { return entry.TraitNodeID == grantedEntry.TraitNodeID && entry.TraitNodeEntryID == grantedEntry.TraitNodeEntryID; });
|
||||||
|
if (newEntry == null)
|
||||||
|
{
|
||||||
|
newEntry = new();
|
||||||
|
classTalentsRequestNewConfig.Config.Entries.Add(newEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry.TraitNodeID = grantedEntry.TraitNodeID;
|
||||||
|
newEntry.TraitNodeEntryID = grantedEntry.TraitNodeEntryID;
|
||||||
|
newEntry.Rank = grantedEntry.Rank;
|
||||||
|
newEntry.GrantedRanks = grantedEntry.GrantedRanks;
|
||||||
|
|
||||||
|
TraitNodeEntryRecord traitNodeEntry = CliDB.TraitNodeEntryStorage.LookupByKey(grantedEntry.TraitNodeEntryID);
|
||||||
|
if (traitNodeEntry != null)
|
||||||
|
if (newEntry.Rank + newEntry.GrantedRanks > traitNodeEntry.MaxRanks)
|
||||||
|
newEntry.Rank = Math.Max(0, traitNodeEntry.MaxRanks - newEntry.GrantedRanks);
|
||||||
|
}
|
||||||
|
|
||||||
|
TalentLearnResult validationResult = TraitMgr.ValidateConfig(classTalentsRequestNewConfig.Config, _player);
|
||||||
|
if (validationResult != TalentLearnResult.LearnOk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_player.CreateTraitConfig(classTalentsRequestNewConfig.Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.ClassTalentsRenameConfig)]
|
||||||
|
void HandleClassTalentsRenameConfig(ClassTalentsRenameConfig classTalentsRenameConfig)
|
||||||
|
{
|
||||||
|
_player.RenameTraitConfig(classTalentsRenameConfig.ConfigID, classTalentsRenameConfig.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.ClassTalentsDeleteConfig)]
|
||||||
|
void HandleClassTalentsDeleteConfig(ClassTalentsDeleteConfig classTalentsDeleteConfig)
|
||||||
|
{
|
||||||
|
_player.DeleteTraitConfig(classTalentsDeleteConfig.ConfigID);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.ClassTalentsSetStarterBuildActive)]
|
||||||
|
void HandleClassTalentsSetStarterBuildActive(ClassTalentsSetStarterBuildActive classTalentsSetStarterBuildActive)
|
||||||
|
{
|
||||||
|
TraitConfig traitConfig = _player.GetTraitConfig(classTalentsSetStarterBuildActive.ConfigID);
|
||||||
|
if (traitConfig == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((TraitConfigType)(int)traitConfig.Type != TraitConfigType.Combat)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!((TraitCombatConfigFlags)(int)traitConfig.CombatConfigFlags).HasFlag(TraitCombatConfigFlags.ActiveForSpec))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (classTalentsSetStarterBuildActive.Active)
|
||||||
|
{
|
||||||
|
TraitConfigPacket newConfigState = new(traitConfig);
|
||||||
|
|
||||||
|
int freeLocalIdentifier = 1;
|
||||||
|
while (_player.m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig =>
|
||||||
|
{
|
||||||
|
return (TraitConfigType)(int)traitConfig.Type == TraitConfigType.Combat
|
||||||
|
&& traitConfig.ChrSpecializationID == _player.GetPrimarySpecialization()
|
||||||
|
&& traitConfig.LocalIdentifier == freeLocalIdentifier;
|
||||||
|
}) >= 0)
|
||||||
|
++freeLocalIdentifier;
|
||||||
|
|
||||||
|
TraitMgr.InitializeStarterBuildTraitConfig(newConfigState, _player);
|
||||||
|
newConfigState.CombatConfigFlags |= TraitCombatConfigFlags.StarterBuild;
|
||||||
|
newConfigState.LocalIdentifier = freeLocalIdentifier;
|
||||||
|
|
||||||
|
_player.UpdateTraitConfig(newConfigState, 0, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_player.SetTraitConfigUseStarterBuild(classTalentsSetStarterBuildActive.ConfigID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[WorldPacketHandler(ClientOpcodes.ClassTalentsSetUsesSharedActionBars)]
|
||||||
|
void HandleClassTalentsSetUsesSharedActionBars(ClassTalentsSetUsesSharedActionBars classTalentsSetUsesSharedActionBars)
|
||||||
|
{
|
||||||
|
_player.SetTraitConfigUseSharedActionBars(classTalentsSetUsesSharedActionBars.ConfigID, classTalentsSetUsesSharedActionBars.UsesShared,
|
||||||
|
classTalentsSetUsesSharedActionBars.IsLastSelectedSavedConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -339,12 +339,12 @@ namespace Game.Networking.Packets
|
|||||||
{
|
{
|
||||||
public int Level;
|
public int Level;
|
||||||
public int ChrSpecializationID;
|
public int ChrSpecializationID;
|
||||||
public TraitConfig Config;
|
public TraitConfigPacket Config;
|
||||||
|
|
||||||
public void Write(WorldPacket data)
|
public void Write(WorldPacket data)
|
||||||
{
|
{
|
||||||
data . WriteInt32(Level);
|
data.WriteInt32(Level);
|
||||||
data . WriteInt32(ChrSpecializationID);
|
data.WriteInt32(ChrSpecializationID);
|
||||||
Config.Write(data);
|
Config.Write(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Framework.Constants;
|
||||||
|
using Game.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Game.Networking.Packets
|
||||||
|
{
|
||||||
|
class TraitsCommitConfig : ClientPacket
|
||||||
|
{
|
||||||
|
public TraitConfigPacket Config = new();
|
||||||
|
public int SavedConfigID;
|
||||||
|
public int SavedLocalIdentifier;
|
||||||
|
|
||||||
|
public TraitsCommitConfig(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
Config.Read(_worldPacket);
|
||||||
|
SavedConfigID = _worldPacket.ReadInt32();
|
||||||
|
SavedLocalIdentifier = _worldPacket.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TraitConfigCommitFailed : ServerPacket
|
||||||
|
{
|
||||||
|
public int ConfigID;
|
||||||
|
public uint SpellID;
|
||||||
|
public int Reason;
|
||||||
|
|
||||||
|
public TraitConfigCommitFailed(int configId = 0, uint spellId = 0, int reason = 0) : base(ServerOpcodes.TraitConfigCommitFailed)
|
||||||
|
{
|
||||||
|
ConfigID = configId;
|
||||||
|
SpellID = spellId;
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write()
|
||||||
|
{
|
||||||
|
_worldPacket.WriteInt32(ConfigID);
|
||||||
|
_worldPacket.WriteUInt32(SpellID);
|
||||||
|
_worldPacket.WriteBits(Reason, 4);
|
||||||
|
_worldPacket.FlushBits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassTalentsRequestNewConfig : ClientPacket
|
||||||
|
{
|
||||||
|
public TraitConfigPacket Config = new();
|
||||||
|
|
||||||
|
public ClassTalentsRequestNewConfig(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
Config.Read(_worldPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassTalentsRenameConfig : ClientPacket
|
||||||
|
{
|
||||||
|
public int ConfigID;
|
||||||
|
public string Name;
|
||||||
|
|
||||||
|
public ClassTalentsRenameConfig(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
ConfigID = _worldPacket.ReadInt32();
|
||||||
|
uint nameLength = _worldPacket.ReadBits<uint>(9);
|
||||||
|
Name = _worldPacket.ReadString(nameLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassTalentsDeleteConfig : ClientPacket
|
||||||
|
{
|
||||||
|
public int ConfigID;
|
||||||
|
|
||||||
|
public ClassTalentsDeleteConfig(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
ConfigID = _worldPacket.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassTalentsSetStarterBuildActive : ClientPacket
|
||||||
|
{
|
||||||
|
public int ConfigID;
|
||||||
|
public bool Active;
|
||||||
|
|
||||||
|
public ClassTalentsSetStarterBuildActive(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
ConfigID = _worldPacket.ReadInt32();
|
||||||
|
Active = _worldPacket.HasBit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassTalentsSetUsesSharedActionBars : ClientPacket
|
||||||
|
{
|
||||||
|
public int ConfigID;
|
||||||
|
public bool UsesShared;
|
||||||
|
public bool IsLastSelectedSavedConfig;
|
||||||
|
|
||||||
|
public ClassTalentsSetUsesSharedActionBars(WorldPacket packet) : base(packet) { }
|
||||||
|
|
||||||
|
public override void Read()
|
||||||
|
{
|
||||||
|
ConfigID = _worldPacket.ReadInt32();
|
||||||
|
UsesShared = _worldPacket.HasBit();
|
||||||
|
IsLastSelectedSavedConfig = _worldPacket.HasBit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TraitEntryPacket
|
||||||
|
{
|
||||||
|
public int TraitNodeID;
|
||||||
|
public int TraitNodeEntryID;
|
||||||
|
public int Rank;
|
||||||
|
public int GrantedRanks;
|
||||||
|
|
||||||
|
public TraitEntryPacket() { }
|
||||||
|
public TraitEntryPacket(TraitEntry ufEntry)
|
||||||
|
{
|
||||||
|
TraitNodeID = ufEntry.TraitNodeID;
|
||||||
|
TraitNodeEntryID = ufEntry.TraitNodeEntryID;
|
||||||
|
Rank = ufEntry.Rank;
|
||||||
|
GrantedRanks = ufEntry.GrantedRanks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Read(WorldPacket data)
|
||||||
|
{
|
||||||
|
TraitNodeID = data.ReadInt32();
|
||||||
|
TraitNodeEntryID = data.ReadInt32();
|
||||||
|
Rank = data.ReadInt32();
|
||||||
|
GrantedRanks = data.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(WorldPacket data)
|
||||||
|
{
|
||||||
|
data.WriteInt32(TraitNodeID);
|
||||||
|
data.WriteInt32(TraitNodeEntryID);
|
||||||
|
data.WriteInt32(Rank);
|
||||||
|
data.WriteInt32(GrantedRanks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TraitConfigPacket
|
||||||
|
{
|
||||||
|
public int ID;
|
||||||
|
public TraitConfigType Type;
|
||||||
|
public int ChrSpecializationID = 0;
|
||||||
|
public TraitCombatConfigFlags CombatConfigFlags;
|
||||||
|
public int LocalIdentifier; // Local to specialization
|
||||||
|
public uint SkillLineID;
|
||||||
|
public int TraitSystemID;
|
||||||
|
public List<TraitEntryPacket> Entries = new();
|
||||||
|
public string Name = "";
|
||||||
|
|
||||||
|
public TraitConfigPacket() { }
|
||||||
|
public TraitConfigPacket(TraitConfig ufConfig)
|
||||||
|
{
|
||||||
|
ID = ufConfig.ID;
|
||||||
|
Type = (TraitConfigType)(int)ufConfig.Type;
|
||||||
|
ChrSpecializationID = ufConfig.ChrSpecializationID;
|
||||||
|
CombatConfigFlags = (TraitCombatConfigFlags)(int)ufConfig.CombatConfigFlags;
|
||||||
|
LocalIdentifier = ufConfig.LocalIdentifier;
|
||||||
|
SkillLineID = (uint)(int)ufConfig.SkillLineID;
|
||||||
|
TraitSystemID = ufConfig.TraitSystemID;
|
||||||
|
foreach (TraitEntry ufEntry in ufConfig.Entries)
|
||||||
|
Entries.Add(new TraitEntryPacket(ufEntry));
|
||||||
|
Name = ufConfig.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Read(WorldPacket data)
|
||||||
|
{
|
||||||
|
ID = data.ReadInt32();
|
||||||
|
Type = (TraitConfigType)data.ReadInt32();
|
||||||
|
var entriesCount = data.ReadInt32();
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case TraitConfigType.Combat:
|
||||||
|
ChrSpecializationID = data.ReadInt32();
|
||||||
|
CombatConfigFlags = (TraitCombatConfigFlags)data.ReadInt32();
|
||||||
|
LocalIdentifier = data.ReadInt32();
|
||||||
|
break;
|
||||||
|
case TraitConfigType.Profession:
|
||||||
|
SkillLineID = data.ReadUInt32();
|
||||||
|
break;
|
||||||
|
case TraitConfigType.Generic:
|
||||||
|
TraitSystemID = data.ReadInt32();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < entriesCount; ++i)
|
||||||
|
{
|
||||||
|
TraitEntryPacket traitEntry = new();
|
||||||
|
traitEntry.Read(data);
|
||||||
|
Entries.Add(traitEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint nameLength = data.ReadBits<uint>(9);
|
||||||
|
Name = data.ReadString(nameLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(WorldPacket data)
|
||||||
|
{
|
||||||
|
data.WriteInt32(ID);
|
||||||
|
data.WriteInt32((int)Type);
|
||||||
|
data.WriteInt32(Entries.Count);
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case TraitConfigType.Combat:
|
||||||
|
data.WriteInt32(ChrSpecializationID);
|
||||||
|
data.WriteInt32((int)CombatConfigFlags);
|
||||||
|
data.WriteInt32(LocalIdentifier);
|
||||||
|
break;
|
||||||
|
case TraitConfigType.Profession:
|
||||||
|
data.WriteUInt32(SkillLineID);
|
||||||
|
break;
|
||||||
|
case TraitConfigType.Generic:
|
||||||
|
data.WriteInt32(TraitSystemID);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TraitEntryPacket traitEntry in Entries)
|
||||||
|
traitEntry.Write(data);
|
||||||
|
|
||||||
|
data.WriteBits(Name.GetByteCount(), 9);
|
||||||
|
data.FlushBits();
|
||||||
|
|
||||||
|
data.WriteString(Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using Framework.Constants;
|
|
||||||
using Game.Entities;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Game.Networking.Packets
|
|
||||||
{
|
|
||||||
public struct TraitEntry
|
|
||||||
{
|
|
||||||
public int TraitNodeID;
|
|
||||||
public int TraitNodeEntryID;
|
|
||||||
public int Rank;
|
|
||||||
public int GrantedRanks;
|
|
||||||
|
|
||||||
public void Write(WorldPacket data)
|
|
||||||
{
|
|
||||||
data.WriteInt32(TraitNodeID);
|
|
||||||
data.WriteInt32(TraitNodeEntryID);
|
|
||||||
data.WriteInt32(Rank);
|
|
||||||
data.WriteInt32(GrantedRanks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TraitConfig
|
|
||||||
{
|
|
||||||
public int ID;
|
|
||||||
public TraitConfigType Type;
|
|
||||||
public int ChrSpecializationID = 0;
|
|
||||||
public TraitCombatConfigFlags CombatConfigFlags;
|
|
||||||
public int LocalIdentifier; // Local to specialization
|
|
||||||
public int SkillLineID;
|
|
||||||
public int TraitSystemID;
|
|
||||||
public List<TraitEntry> Entries = new();
|
|
||||||
public string Name = "";
|
|
||||||
|
|
||||||
public void Write(WorldPacket data)
|
|
||||||
{
|
|
||||||
data.WriteInt32(ID);
|
|
||||||
data.WriteInt32((int)Type);
|
|
||||||
data.WriteInt32(Entries.Count);
|
|
||||||
switch (Type)
|
|
||||||
{
|
|
||||||
case TraitConfigType.Combat:
|
|
||||||
data.WriteInt32(ChrSpecializationID);
|
|
||||||
data.WriteInt32((int)CombatConfigFlags);
|
|
||||||
data.WriteInt32(LocalIdentifier);
|
|
||||||
break;
|
|
||||||
case TraitConfigType.Profession:
|
|
||||||
data.WriteInt32(SkillLineID);
|
|
||||||
break;
|
|
||||||
case TraitConfigType.Generic:
|
|
||||||
data.WriteInt32(TraitSystemID);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (TraitEntry traitEntry in Entries)
|
|
||||||
traitEntry.Write(data);
|
|
||||||
|
|
||||||
data.WriteBits(Name.GetByteCount(), 9);
|
|
||||||
data.FlushBits();
|
|
||||||
|
|
||||||
data.WriteString(Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3394,7 +3394,14 @@ namespace Game.Spells
|
|||||||
Unit.ProcSkillsAndAuras(unitCaster, null, new ProcFlagsInit(ProcFlags.CastEnded), new ProcFlagsInit(), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, this, null, null);
|
Unit.ProcSkillsAndAuras(unitCaster, null, new ProcFlagsInit(ProcFlags.CastEnded), new ProcFlagsInit(), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, this, null, null);
|
||||||
|
|
||||||
if (!ok)
|
if (!ok)
|
||||||
|
{
|
||||||
|
// on failure (or manual cancel) send TraitConfigCommitFailed to revert talent UI saved config selection
|
||||||
|
if (m_caster.IsPlayer() && m_spellInfo.HasEffect(SpellEffectName.ChangeActiveCombatTraitConfig))
|
||||||
|
if (m_customArg is TraitConfig)
|
||||||
|
m_caster.ToPlayer().SendPacket(new TraitConfigCommitFailed((m_customArg as TraitConfig).ID));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (unitCaster.IsTypeId(TypeId.Unit) && unitCaster.ToCreature().IsSummon())
|
if (unitCaster.IsTypeId(TypeId.Unit) && unitCaster.ToCreature().IsSummon())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5676,6 +5676,41 @@ namespace Game.Spells
|
|||||||
for (int i = 0; i < damage; ++i)
|
for (int i = 0; i < damage; ++i)
|
||||||
unitTarget.GetSpellHistory().RestoreCharge((uint)effectInfo.MiscValue);
|
unitTarget.GetSpellHistory().RestoreCharge((uint)effectInfo.MiscValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SpellEffectHandler(SpellEffectName.CreateTraitTreeConfig)]
|
||||||
|
void EffectCreateTraitTreeConfig()
|
||||||
|
{
|
||||||
|
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Player target = unitTarget?.ToPlayer();
|
||||||
|
if (target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TraitConfigPacket newConfig = new();
|
||||||
|
newConfig.Type = TraitMgr.GetConfigTypeForTree(effectInfo.MiscValue);
|
||||||
|
if (newConfig.Type != TraitConfigType.Generic)
|
||||||
|
return;
|
||||||
|
|
||||||
|
newConfig.TraitSystemID = CliDB.TraitTreeStorage.LookupByKey(effectInfo.MiscValue).TraitSystemID;
|
||||||
|
target.CreateTraitConfig(newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SpellEffectHandler(SpellEffectName.ChangeActiveCombatTraitConfig)]
|
||||||
|
void EffectChangeActiveCombatTraitConfig()
|
||||||
|
{
|
||||||
|
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Player target = unitTarget?.ToPlayer();
|
||||||
|
if (target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_customArg is not TraitConfigPacket)
|
||||||
|
return;
|
||||||
|
|
||||||
|
target.UpdateTraitConfig(m_customArg as TraitConfigPacket, damage, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DispelableAura
|
public class DispelableAura
|
||||||
|
|||||||
@@ -0,0 +1,746 @@
|
|||||||
|
using Framework.Constants;
|
||||||
|
using Game.DataStorage;
|
||||||
|
using Game.Entities;
|
||||||
|
using Game.Networking.Packets;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Game
|
||||||
|
{
|
||||||
|
class TraitMgr
|
||||||
|
{
|
||||||
|
public static uint COMMIT_COMBAT_TRAIT_CONFIG_CHANGES_SPELL_ID = 384255u;
|
||||||
|
public static uint MAX_COMBAT_TRAIT_CONFIGS = 10u;
|
||||||
|
|
||||||
|
static Dictionary<int, NodeGroup> _traitGroups = new();
|
||||||
|
static Dictionary<int, Node> _traitNodes = new();
|
||||||
|
static Dictionary<int, Tree> _traitTrees = new();
|
||||||
|
static int[] _skillLinesByClass = new int[(int)Class.Max];
|
||||||
|
static MultiMap<int, Tree> _traitTreesBySkillLine = new();
|
||||||
|
static MultiMap<int, Tree> _traitTreesByTraitSystem = new();
|
||||||
|
static int _configIdGenerator;
|
||||||
|
static MultiMap<int, TraitCurrencySourceRecord> _traitCurrencySourcesByCurrency = new();
|
||||||
|
static MultiMap<int, TraitDefinitionEffectPointsRecord> _traitDefinitionEffectPointModifiers = new();
|
||||||
|
static MultiMap<int, TraitTreeLoadoutEntryRecord> _traitTreeLoadoutsByChrSpecialization = new();
|
||||||
|
|
||||||
|
public static void Load()
|
||||||
|
{
|
||||||
|
_configIdGenerator = int.MaxValue;
|
||||||
|
|
||||||
|
MultiMap<int, TraitCondRecord> nodeEntryConditions = new();
|
||||||
|
foreach (TraitNodeEntryXTraitCondRecord traitNodeEntryXTraitCondEntry in CliDB.TraitNodeEntryXTraitCondStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCondRecord traitCondEntry = CliDB.TraitCondStorage.LookupByKey(traitNodeEntryXTraitCondEntry.TraitCondID);
|
||||||
|
if (traitCondEntry != null)
|
||||||
|
nodeEntryConditions.Add((int)traitNodeEntryXTraitCondEntry.TraitNodeEntryID, traitCondEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, TraitCostRecord> nodeEntryCosts = new();
|
||||||
|
foreach (TraitNodeEntryXTraitCostRecord traitNodeEntryXTraitCostEntry in CliDB.TraitNodeEntryXTraitCostStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCostRecord traitCostEntry = CliDB.TraitCostStorage.LookupByKey(traitNodeEntryXTraitCostEntry.TraitCostID);
|
||||||
|
if (traitCostEntry != null)
|
||||||
|
nodeEntryCosts.Add(traitNodeEntryXTraitCostEntry.TraitNodeEntryID, traitCostEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, TraitCondRecord> nodeGroupConditions = new();
|
||||||
|
foreach (TraitNodeGroupXTraitCondRecord traitNodeGroupXTraitCondEntry in CliDB.TraitNodeGroupXTraitCondStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCondRecord traitCondEntry = CliDB.TraitCondStorage.LookupByKey(traitNodeGroupXTraitCondEntry.TraitCondID);
|
||||||
|
if (traitCondEntry != null)
|
||||||
|
nodeGroupConditions.Add(traitNodeGroupXTraitCondEntry.TraitNodeGroupID, traitCondEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, TraitCostRecord> nodeGroupCosts = new();
|
||||||
|
foreach (TraitNodeGroupXTraitCostRecord traitNodeGroupXTraitCostEntry in CliDB.TraitNodeGroupXTraitCostStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCostRecord traitCondEntry = CliDB.TraitCostStorage.LookupByKey(traitNodeGroupXTraitCostEntry.TraitCostID);
|
||||||
|
if (traitCondEntry != null)
|
||||||
|
nodeGroupCosts.Add(traitNodeGroupXTraitCostEntry.TraitNodeGroupID, traitCondEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, int> nodeGroups = new();
|
||||||
|
foreach (TraitNodeGroupXTraitNodeRecord traitNodeGroupXTraitNodeEntry in CliDB.TraitNodeGroupXTraitNodeStorage.Values)
|
||||||
|
nodeGroups.Add(traitNodeGroupXTraitNodeEntry.TraitNodeID, traitNodeGroupXTraitNodeEntry.TraitNodeGroupID);
|
||||||
|
|
||||||
|
MultiMap<int, TraitCondRecord> nodeConditions = new();
|
||||||
|
foreach (TraitNodeXTraitCondRecord traitNodeXTraitCondEntry in CliDB.TraitNodeXTraitCondStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCondRecord traitCondEntry = CliDB.TraitCondStorage.LookupByKey(traitNodeXTraitCondEntry.TraitCondID);
|
||||||
|
if (traitCondEntry != null)
|
||||||
|
nodeConditions.Add(traitNodeXTraitCondEntry.TraitNodeID, traitCondEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<uint, TraitCostRecord> nodeCosts = new();
|
||||||
|
foreach (TraitNodeXTraitCostRecord traitNodeXTraitCostEntry in CliDB.TraitNodeXTraitCostStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCostRecord traitCostEntry = CliDB.TraitCostStorage.LookupByKey(traitNodeXTraitCostEntry.TraitCostID);
|
||||||
|
if (traitCostEntry != null)
|
||||||
|
nodeCosts.Add(traitNodeXTraitCostEntry.TraitNodeID, traitCostEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, TraitNodeEntryRecord> nodeEntries = new();
|
||||||
|
foreach (TraitNodeXTraitNodeEntryRecord traitNodeXTraitNodeEntryEntry in CliDB.TraitNodeXTraitNodeEntryStorage.Values)
|
||||||
|
{
|
||||||
|
TraitNodeEntryRecord traitNodeEntryEntry = CliDB.TraitNodeEntryStorage.LookupByKey(traitNodeXTraitNodeEntryEntry.TraitNodeEntryID);
|
||||||
|
if (traitNodeEntryEntry != null)
|
||||||
|
nodeEntries.Add(traitNodeXTraitNodeEntryEntry.TraitNodeID, traitNodeEntryEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<uint, TraitCostRecord> treeCosts = new();
|
||||||
|
foreach (TraitTreeXTraitCostRecord traitTreeXTraitCostEntry in CliDB.TraitTreeXTraitCostStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCostRecord traitCostEntry = CliDB.TraitCostStorage.LookupByKey(traitTreeXTraitCostEntry.TraitCostID);
|
||||||
|
if (traitCostEntry != null)
|
||||||
|
treeCosts.Add(traitTreeXTraitCostEntry.TraitTreeID, traitCostEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, TraitCurrencyRecord> treeCurrencies = new();
|
||||||
|
foreach (TraitTreeXTraitCurrencyRecord traitTreeXTraitCurrencyEntry in CliDB.TraitTreeXTraitCurrencyStorage.Values)
|
||||||
|
{
|
||||||
|
TraitCurrencyRecord traitCurrencyEntry = CliDB.TraitCurrencyStorage.LookupByKey(traitTreeXTraitCurrencyEntry.TraitCurrencyID);
|
||||||
|
if (traitCurrencyEntry != null)
|
||||||
|
treeCurrencies.Add(traitTreeXTraitCurrencyEntry.TraitTreeID, traitCurrencyEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiMap<int, int> traitTreesIdsByTraitSystem = new();
|
||||||
|
foreach (TraitTreeRecord traitTree in CliDB.TraitTreeStorage.Values)
|
||||||
|
{
|
||||||
|
Tree tree = _traitTrees[(int)traitTree.Id];
|
||||||
|
tree.Data = traitTree;
|
||||||
|
|
||||||
|
var costs = treeCosts.LookupByKey(traitTree.Id);
|
||||||
|
if (costs != null)
|
||||||
|
tree.Costs = costs;
|
||||||
|
|
||||||
|
var currencies = treeCurrencies.LookupByKey(traitTree.Id);
|
||||||
|
if (currencies != null)
|
||||||
|
tree.Currencies = currencies;
|
||||||
|
|
||||||
|
if (traitTree.TraitSystemID != 0)
|
||||||
|
{
|
||||||
|
traitTreesIdsByTraitSystem.Add(traitTree.TraitSystemID, (int)traitTree.Id);
|
||||||
|
tree.ConfigType = TraitConfigType.Generic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TraitNodeGroupRecord traitNodeGroup in CliDB.TraitNodeGroupStorage.Values)
|
||||||
|
{
|
||||||
|
NodeGroup nodeGroup = _traitGroups[(int)traitNodeGroup.Id];
|
||||||
|
nodeGroup.Data = traitNodeGroup;
|
||||||
|
|
||||||
|
var conditions = nodeGroupConditions.LookupByKey(traitNodeGroup.Id);
|
||||||
|
if (conditions != null)
|
||||||
|
nodeGroup.Conditions = conditions;
|
||||||
|
|
||||||
|
var costs = nodeGroupCosts.LookupByKey(traitNodeGroup.Id);
|
||||||
|
if (costs != null)
|
||||||
|
nodeGroup.Costs = costs;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TraitNodeRecord traitNode in CliDB.TraitNodeStorage.Values)
|
||||||
|
{
|
||||||
|
Node node = _traitNodes[(int)traitNode.Id];
|
||||||
|
node.Data = traitNode;
|
||||||
|
|
||||||
|
Tree tree = _traitTrees.LookupByKey(traitNode.TraitTreeID);
|
||||||
|
if (tree != null)
|
||||||
|
tree.Nodes.Add(node);
|
||||||
|
|
||||||
|
foreach (var traitNodeEntry in nodeEntries.LookupByKey(traitNode.Id))
|
||||||
|
{
|
||||||
|
NodeEntry entry = new();
|
||||||
|
entry.Data = traitNodeEntry;
|
||||||
|
|
||||||
|
var conditions = nodeEntryConditions.LookupByKey(traitNodeEntry.Id);
|
||||||
|
if (conditions != null)
|
||||||
|
entry.Conditions = conditions;
|
||||||
|
|
||||||
|
var costs = nodeEntryCosts.LookupByKey(traitNodeEntry.Id);
|
||||||
|
if (costs != null)
|
||||||
|
entry.Costs = costs;
|
||||||
|
|
||||||
|
node.Entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var nodeGroupId in nodeGroups.LookupByKey(traitNode.Id))
|
||||||
|
{
|
||||||
|
NodeGroup nodeGroup = _traitGroups.LookupByKey(nodeGroupId);
|
||||||
|
if (nodeGroup == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nodeGroup.Nodes.Add(node);
|
||||||
|
node.Groups.Add(nodeGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
var conditions1 = nodeConditions.LookupByKey(traitNode.Id);
|
||||||
|
if (conditions1 != null)
|
||||||
|
node.Conditions = conditions1;
|
||||||
|
|
||||||
|
var costs1 = nodeCosts.LookupByKey(traitNode.Id);
|
||||||
|
if (costs1 != null)
|
||||||
|
node.Costs = costs1;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TraitEdgeRecord traitEdgeEntry in CliDB.TraitEdgeStorage.Values)
|
||||||
|
{
|
||||||
|
Node left = _traitNodes.LookupByKey(traitEdgeEntry.LeftTraitNodeID);
|
||||||
|
Node right = _traitNodes.LookupByKey(traitEdgeEntry.RightTraitNodeID);
|
||||||
|
if (left == null || right == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
right.ParentNodes.Add(Tuple.Create(left, (TraitEdgeType)traitEdgeEntry.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (SkillLineXTraitTreeRecord skillLineXTraitTreeEntry in CliDB.SkillLineXTraitTreeStorage.Values)
|
||||||
|
{
|
||||||
|
Tree tree = _traitTrees.LookupByKey(skillLineXTraitTreeEntry.TraitTreeID);
|
||||||
|
if (tree == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SkillLineRecord skillLineEntry = CliDB.SkillLineStorage.LookupByKey(skillLineXTraitTreeEntry.SkillLineID);
|
||||||
|
if (skillLineEntry == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_traitTreesBySkillLine[skillLineXTraitTreeEntry.SkillLineID].Add(tree);
|
||||||
|
if (skillLineEntry.CategoryID == SkillCategory.Class)
|
||||||
|
{
|
||||||
|
foreach (SkillRaceClassInfoRecord skillRaceClassInfo in Global.DB2Mgr.GetSkillRaceClassInfo(skillLineEntry.Id))
|
||||||
|
for (int i = 1; i < (int)Class.Max; ++i)
|
||||||
|
if ((skillRaceClassInfo.ClassMask & (1 << (i - 1))) != 0)
|
||||||
|
_skillLinesByClass[i] = skillLineXTraitTreeEntry.SkillLineID;
|
||||||
|
|
||||||
|
tree.ConfigType = TraitConfigType.Combat;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tree.ConfigType = TraitConfigType.Profession;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (traitSystemId, traitTreeId) in traitTreesIdsByTraitSystem)
|
||||||
|
{
|
||||||
|
Tree tree = _traitTrees.LookupByKey(traitTreeId);
|
||||||
|
if (tree != null)
|
||||||
|
_traitTreesByTraitSystem[traitSystemId].Add(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TraitCurrencySourceRecord traitCurrencySource in CliDB.TraitCurrencySourceStorage.Values)
|
||||||
|
_traitCurrencySourcesByCurrency[traitCurrencySource.TraitCurrencyID].Add(traitCurrencySource);
|
||||||
|
|
||||||
|
foreach (TraitDefinitionEffectPointsRecord traitDefinitionEffectPoints in CliDB.TraitDefinitionEffectPointsStorage.Values)
|
||||||
|
_traitDefinitionEffectPointModifiers[traitDefinitionEffectPoints.TraitDefinitionID].Add(traitDefinitionEffectPoints);
|
||||||
|
|
||||||
|
MultiMap<int, TraitTreeLoadoutEntryRecord> traitTreeLoadoutEntries = new();
|
||||||
|
foreach (TraitTreeLoadoutEntryRecord traitTreeLoadoutEntry in CliDB.TraitTreeLoadoutEntryStorage.Values)
|
||||||
|
traitTreeLoadoutEntries[traitTreeLoadoutEntry.TraitTreeLoadoutID].Add(traitTreeLoadoutEntry);
|
||||||
|
|
||||||
|
foreach (TraitTreeLoadoutRecord traitTreeLoadout in CliDB.TraitTreeLoadoutStorage.Values)
|
||||||
|
{
|
||||||
|
var entries = traitTreeLoadoutEntries.LookupByKey(traitTreeLoadout.Id);
|
||||||
|
if (entries != null)
|
||||||
|
{
|
||||||
|
entries.Sort((left, right) => { return left.OrderIndex.CompareTo(right.OrderIndex); });
|
||||||
|
// there should be only one loadout per spec, we take last one encountered
|
||||||
|
_traitTreeLoadoutsByChrSpecialization[traitTreeLoadout.ChrSpecializationID] = entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates new TraitConfig identifier.
|
||||||
|
* Because this only needs to be unique for each character we let it overflow
|
||||||
|
*/
|
||||||
|
public static int GenerateNewTraitConfigId()
|
||||||
|
{
|
||||||
|
if (_configIdGenerator == int.MaxValue)
|
||||||
|
_configIdGenerator = 0;
|
||||||
|
|
||||||
|
return ++_configIdGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TraitConfigType GetConfigTypeForTree(int traitTreeId)
|
||||||
|
{
|
||||||
|
Tree tree = _traitTrees.LookupByKey(traitTreeId);
|
||||||
|
if (tree == null)
|
||||||
|
return TraitConfigType.Invalid;
|
||||||
|
|
||||||
|
return tree.ConfigType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds relevant TraitTree identifiers
|
||||||
|
* @param traitConfig config data
|
||||||
|
* @return Trait tree data
|
||||||
|
*/
|
||||||
|
public static List<Tree> GetTreesForConfig(TraitConfigPacket traitConfig)
|
||||||
|
{
|
||||||
|
switch (traitConfig.Type)
|
||||||
|
{
|
||||||
|
case TraitConfigType.Combat:
|
||||||
|
ChrSpecializationRecord chrSpecializationEntry = CliDB.ChrSpecializationStorage.LookupByKey(traitConfig.ChrSpecializationID);
|
||||||
|
if (chrSpecializationEntry != null)
|
||||||
|
return _traitTreesBySkillLine.LookupByKey(_skillLinesByClass[chrSpecializationEntry.ClassID]);
|
||||||
|
break;
|
||||||
|
case TraitConfigType.Profession:
|
||||||
|
return _traitTreesBySkillLine.LookupByKey(traitConfig.SkillLineID);
|
||||||
|
case TraitConfigType.Generic:
|
||||||
|
return _traitTreesByTraitSystem.LookupByKey(traitConfig.TraitSystemID);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasEnoughCurrency(TraitEntryPacket entry, Dictionary<int, int> currencies)
|
||||||
|
{
|
||||||
|
int getCurrencyCount(int currencyId)
|
||||||
|
{
|
||||||
|
return currencies.LookupByKey(currencyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node node = _traitNodes.LookupByKey(entry.TraitNodeID);
|
||||||
|
foreach (NodeGroup group in node.Groups)
|
||||||
|
foreach (TraitCostRecord cost in group.Costs)
|
||||||
|
if (getCurrencyCount(cost.TraitCurrencyID) < cost.Amount * entry.Rank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var nodeEntryItr = node.Entries.Find(nodeEntry => nodeEntry.Data.Id == entry.TraitNodeEntryID);
|
||||||
|
if (nodeEntryItr != null)
|
||||||
|
foreach (TraitCostRecord cost in nodeEntryItr.Costs)
|
||||||
|
if (getCurrencyCount(cost.TraitCurrencyID) < cost.Amount * entry.Rank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (TraitCostRecord cost in node.Costs)
|
||||||
|
if (getCurrencyCount(cost.TraitCurrencyID) < cost.Amount * entry.Rank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Tree tree = _traitTrees.LookupByKey(node.Data.TraitTreeID);
|
||||||
|
if (tree != null)
|
||||||
|
foreach (TraitCostRecord cost in tree.Costs)
|
||||||
|
if (getCurrencyCount(cost.TraitCurrencyID) < cost.Amount * entry.Rank)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TakeCurrencyCost(TraitEntryPacket entry, Dictionary<int, int> currencies)
|
||||||
|
{
|
||||||
|
Node node = _traitNodes.LookupByKey(entry.TraitNodeID);
|
||||||
|
foreach (NodeGroup group in node.Groups)
|
||||||
|
foreach (TraitCostRecord cost in group.Costs)
|
||||||
|
currencies[cost.TraitCurrencyID] -= cost.Amount * entry.Rank;
|
||||||
|
|
||||||
|
var nodeEntryItr = node.Entries.Find(nodeEntry => nodeEntry.Data.Id == entry.TraitNodeEntryID);
|
||||||
|
if (nodeEntryItr != null)
|
||||||
|
foreach (TraitCostRecord cost in nodeEntryItr.Costs)
|
||||||
|
currencies[cost.TraitCurrencyID] -= cost.Amount * entry.Rank;
|
||||||
|
|
||||||
|
foreach (TraitCostRecord cost in node.Costs)
|
||||||
|
currencies[cost.TraitCurrencyID] -= cost.Amount * entry.Rank;
|
||||||
|
|
||||||
|
Tree tree = _traitTrees.LookupByKey(node.Data.TraitTreeID);
|
||||||
|
if (tree != null)
|
||||||
|
foreach (TraitCostRecord cost in tree.Costs)
|
||||||
|
currencies[cost.TraitCurrencyID] -= cost.Amount * entry.Rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FillOwnedCurrenciesMap(TraitConfigPacket traitConfig, Player player, Dictionary<int, int> currencies)
|
||||||
|
{
|
||||||
|
List<Tree> trees = GetTreesForConfig(traitConfig);
|
||||||
|
if (trees == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool hasTraitNodeEntry(int traitNodeEntryId)
|
||||||
|
{
|
||||||
|
return traitConfig.Entries.Any(traitEntry => traitEntry.TraitNodeEntryID == traitNodeEntryId && (traitEntry.Rank > 0 || traitEntry.GrantedRanks > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Tree tree in trees)
|
||||||
|
{
|
||||||
|
foreach (TraitCurrencyRecord currency in tree.Currencies)
|
||||||
|
{
|
||||||
|
switch (currency.GetCurrencyType())
|
||||||
|
{
|
||||||
|
case TraitCurrencyType.Gold:
|
||||||
|
{
|
||||||
|
int amount = currencies[(int)currency.Id];
|
||||||
|
if (player.GetMoney() > (ulong)(int.MaxValue - amount))
|
||||||
|
amount = int.MaxValue;
|
||||||
|
else
|
||||||
|
amount += (int)player.GetMoney();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TraitCurrencyType.CurrencyTypesBased:
|
||||||
|
currencies[(int)currency.Id] += (int)player.GetCurrency((uint)currency.CurrencyTypesID);
|
||||||
|
break;
|
||||||
|
case TraitCurrencyType.TraitSourced:
|
||||||
|
var currencySources = _traitCurrencySourcesByCurrency.LookupByKey(currency.Id);
|
||||||
|
if (currencySources != null)
|
||||||
|
{
|
||||||
|
foreach (TraitCurrencySourceRecord currencySource in currencySources)
|
||||||
|
{
|
||||||
|
if (currencySource.QuestID != 0 && !player.IsQuestRewarded(currencySource.QuestID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (currencySource.AchievementID != 0 && !player.HasAchieved(currencySource.AchievementID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (currencySource.PlayerLevel != 0 && player.GetLevel() < currencySource.PlayerLevel)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (currencySource.TraitNodeEntryID != 0 && !hasTraitNodeEntry(currencySource.TraitNodeEntryID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
currencies[currencySource.TraitCurrencyID] += currencySource.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FillSpentCurrenciesMap(TraitEntryPacket entry, Dictionary<int, int> cachedCurrencies)
|
||||||
|
{
|
||||||
|
Node node = _traitNodes.LookupByKey(entry.TraitNodeID);
|
||||||
|
foreach (NodeGroup group in node.Groups)
|
||||||
|
foreach (TraitCostRecord cost in group.Costs)
|
||||||
|
cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank;
|
||||||
|
|
||||||
|
var nodeEntryItr = node.Entries.Find(nodeEntry => nodeEntry.Data.Id == entry.TraitNodeEntryID);
|
||||||
|
if (nodeEntryItr != null)
|
||||||
|
foreach (TraitCostRecord cost in nodeEntryItr.Costs)
|
||||||
|
cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank;
|
||||||
|
|
||||||
|
foreach (TraitCostRecord cost in node.Costs)
|
||||||
|
cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank;
|
||||||
|
|
||||||
|
Tree tree = _traitTrees.LookupByKey(node.Data.TraitTreeID);
|
||||||
|
if (tree != null)
|
||||||
|
foreach (TraitCostRecord cost in tree.Costs)
|
||||||
|
cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FillSpentCurrenciesMap(TraitConfigPacket traitConfig, Dictionary<int, int> cachedCurrencies)
|
||||||
|
{
|
||||||
|
foreach (TraitEntryPacket entry in traitConfig.Entries)
|
||||||
|
FillSpentCurrenciesMap(entry, cachedCurrencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool MeetsTraitCondition(TraitConfigPacket traitConfig, Player player, TraitCondRecord condition, ref Dictionary<int, int> cachedCurrencies)
|
||||||
|
{
|
||||||
|
if (condition.QuestID != 0 && !player.IsQuestRewarded(condition.QuestID))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (condition.AchievementID != 0 && !player.HasAchieved(condition.AchievementID))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (condition.SpecSetID != 0 && !Global.DB2Mgr.IsSpecSetMember(condition.SpecSetID, player.GetPrimarySpecialization()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (condition.TraitCurrencyID != 0 && condition.SpentAmountRequired != 0)
|
||||||
|
{
|
||||||
|
if (cachedCurrencies == null)
|
||||||
|
{
|
||||||
|
cachedCurrencies = new();
|
||||||
|
FillSpentCurrenciesMap(traitConfig, cachedCurrencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.TraitNodeGroupID != 0)
|
||||||
|
{
|
||||||
|
cachedCurrencies.TryAdd(condition.TraitCurrencyID, 0);
|
||||||
|
if (cachedCurrencies[condition.TraitCurrencyID] < condition.SpentAmountRequired)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (condition.TraitNodeID != 0)
|
||||||
|
{
|
||||||
|
cachedCurrencies.TryAdd(condition.TraitCurrencyID, 0);
|
||||||
|
if (cachedCurrencies[condition.TraitCurrencyID] < condition.SpentAmountRequired)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition.RequiredLevel != 0 && player.GetLevel() < condition.RequiredLevel)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TraitEntry> GetGrantedTraitEntriesForConfig(TraitConfigPacket traitConfig, Player player)
|
||||||
|
{
|
||||||
|
List<TraitEntry> entries = new();
|
||||||
|
var trees = GetTreesForConfig(traitConfig);
|
||||||
|
if (trees == null)
|
||||||
|
return entries;
|
||||||
|
|
||||||
|
TraitEntry getOrCreateEntry(uint nodeId, uint entryId)
|
||||||
|
{
|
||||||
|
TraitEntry foundTraitEntry = entries.Find(traitEntry => traitEntry.TraitNodeID == nodeId && traitEntry.TraitNodeEntryID == entryId);
|
||||||
|
if (foundTraitEntry == null)
|
||||||
|
{
|
||||||
|
foundTraitEntry = new();
|
||||||
|
foundTraitEntry.TraitNodeID = (int)nodeId;
|
||||||
|
foundTraitEntry.TraitNodeEntryID = (int)entryId;
|
||||||
|
foundTraitEntry.Rank = 0;
|
||||||
|
foundTraitEntry.GrantedRanks = 0;
|
||||||
|
entries.Add(foundTraitEntry);
|
||||||
|
}
|
||||||
|
return foundTraitEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<int, int> cachedCurrencies = null;
|
||||||
|
|
||||||
|
foreach (Tree tree in trees)
|
||||||
|
{
|
||||||
|
foreach (Node node in tree.Nodes)
|
||||||
|
{
|
||||||
|
foreach (NodeEntry entry in node.Entries)
|
||||||
|
foreach (TraitCondRecord condition in entry.Conditions)
|
||||||
|
if (condition.GetCondType() == TraitConditionType.Granted && MeetsTraitCondition(traitConfig, player, condition, ref cachedCurrencies))
|
||||||
|
getOrCreateEntry(node.Data.Id, entry.Data.Id).GrantedRanks += condition.GrantedRanks;
|
||||||
|
|
||||||
|
foreach (TraitCondRecord condition in node.Conditions)
|
||||||
|
if (condition.GetCondType() == TraitConditionType.Granted && MeetsTraitCondition(traitConfig, player, condition, ref cachedCurrencies))
|
||||||
|
foreach (NodeEntry entry in node.Entries)
|
||||||
|
getOrCreateEntry(node.Data.Id, entry.Data.Id).GrantedRanks += condition.GrantedRanks;
|
||||||
|
|
||||||
|
foreach (NodeGroup group in node.Groups)
|
||||||
|
foreach (TraitCondRecord condition in group.Conditions)
|
||||||
|
if (condition.GetCondType() == TraitConditionType.Granted && MeetsTraitCondition(traitConfig, player, condition, ref cachedCurrencies))
|
||||||
|
foreach (NodeEntry entry in node.Entries)
|
||||||
|
getOrCreateEntry(node.Data.Id, entry.Data.Id).GrantedRanks += condition.GrantedRanks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsValidEntry(TraitEntryPacket traitEntry)
|
||||||
|
{
|
||||||
|
Node node = _traitNodes.LookupByKey(traitEntry.TraitNodeID);
|
||||||
|
if (node == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var entryItr = node.Entries.Find(entry => entry.Data.Id == traitEntry.TraitNodeEntryID);
|
||||||
|
if (entryItr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (entryItr.Data.MaxRanks < traitEntry.Rank + traitEntry.GrantedRanks)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TalentLearnResult ValidateConfig(TraitConfigPacket traitConfig, Player player, bool requireSpendingAllCurrencies = false)
|
||||||
|
{
|
||||||
|
int getNodeEntryCount(int traitNodeId)
|
||||||
|
{
|
||||||
|
return traitConfig.Entries.Count(traitEntry => traitEntry.TraitNodeID == traitNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraitEntryPacket getNodeEntry(uint traitNodeId, uint traitNodeEntryId)
|
||||||
|
{
|
||||||
|
return traitConfig.Entries.Find(traitEntry => traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNodeFullyFilled(Node node)
|
||||||
|
{
|
||||||
|
if (node.Data.GetNodeType() == TraitNodeType.Selection)
|
||||||
|
return node.Entries.Any(nodeEntry =>
|
||||||
|
{
|
||||||
|
TraitEntryPacket traitEntry = getNodeEntry(node.Data.Id, nodeEntry.Data.Id);
|
||||||
|
return traitEntry != null && (traitEntry.Rank + traitEntry.GrantedRanks) == nodeEntry.Data.MaxRanks;
|
||||||
|
});
|
||||||
|
|
||||||
|
return node.Entries.All(nodeEntry =>
|
||||||
|
{
|
||||||
|
TraitEntryPacket traitEntry = getNodeEntry(node.Data.Id, nodeEntry.Data.Id);
|
||||||
|
return traitEntry != null && (traitEntry.Rank + traitEntry.GrantedRanks) == nodeEntry.Data.MaxRanks;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Dictionary<int, int> spentCurrencies = new();
|
||||||
|
FillSpentCurrenciesMap(traitConfig, spentCurrencies);
|
||||||
|
|
||||||
|
foreach (TraitEntryPacket traitEntry in traitConfig.Entries)
|
||||||
|
{
|
||||||
|
if (!IsValidEntry(traitEntry))
|
||||||
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
|
Node node = _traitNodes.LookupByKey(traitEntry.TraitNodeID);
|
||||||
|
if (node.Data.GetNodeType() == TraitNodeType.Selection)
|
||||||
|
if (getNodeEntryCount(traitEntry.TraitNodeID) != 1)
|
||||||
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
|
foreach (NodeEntry entry in node.Entries)
|
||||||
|
foreach (TraitCondRecord condition in entry.Conditions)
|
||||||
|
if ((condition.GetCondType() == TraitConditionType.Available || condition.GetCondType() == TraitConditionType.Visible)
|
||||||
|
&& !MeetsTraitCondition(traitConfig, player, condition, ref spentCurrencies))
|
||||||
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
|
foreach (TraitCondRecord condition in node.Conditions)
|
||||||
|
if ((condition.GetCondType() == TraitConditionType.Available || condition.GetCondType() == TraitConditionType.Visible)
|
||||||
|
&& !MeetsTraitCondition(traitConfig, player, condition, ref spentCurrencies))
|
||||||
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
|
foreach (NodeGroup group in node.Groups)
|
||||||
|
foreach (TraitCondRecord condition in group.Conditions)
|
||||||
|
if ((condition.GetCondType() == TraitConditionType.Available || condition.GetCondType() == TraitConditionType.Visible)
|
||||||
|
&& !MeetsTraitCondition(traitConfig, player, condition, ref spentCurrencies))
|
||||||
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
|
if (!node.ParentNodes.Empty())
|
||||||
|
{
|
||||||
|
bool hasAnyParentTrait = false;
|
||||||
|
foreach (var (parentNode, edgeType) in node.ParentNodes)
|
||||||
|
{
|
||||||
|
if (!isNodeFullyFilled(parentNode))
|
||||||
|
{
|
||||||
|
if (edgeType == TraitEdgeType.RequiredForAvailability)
|
||||||
|
return TalentLearnResult.FailedNotEnoughTalentsInPrimaryTree;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAnyParentTrait = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAnyParentTrait)
|
||||||
|
return TalentLearnResult.FailedNotEnoughTalentsInPrimaryTree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<int, int> grantedCurrencies = new();
|
||||||
|
FillOwnedCurrenciesMap(traitConfig, player, grantedCurrencies);
|
||||||
|
|
||||||
|
foreach (var (traitCurrencyId, spentAmount) in spentCurrencies)
|
||||||
|
{
|
||||||
|
if (CliDB.TraitCurrencyStorage.LookupByKey(traitCurrencyId).GetCurrencyType() != TraitCurrencyType.TraitSourced)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (spentAmount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int grantedCount = grantedCurrencies.LookupByKey(traitCurrencyId);
|
||||||
|
if (grantedCount == 0 || grantedCount < spentAmount)
|
||||||
|
return TalentLearnResult.FailedNotEnoughTalentsInPrimaryTree;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requireSpendingAllCurrencies && traitConfig.Type == TraitConfigType.Combat)
|
||||||
|
{
|
||||||
|
foreach (var (traitCurrencyId, grantedAmount) in grantedCurrencies)
|
||||||
|
{
|
||||||
|
if (grantedAmount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int spentAmount = spentCurrencies.LookupByKey(traitCurrencyId);
|
||||||
|
if (spentAmount == 0 || spentAmount != grantedAmount)
|
||||||
|
return TalentLearnResult.UnspentTalentPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TalentLearnResult.LearnOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TraitDefinitionEffectPointsRecord> GetTraitDefinitionEffectPointModifiers(int traitDefinitionId)
|
||||||
|
{
|
||||||
|
return _traitDefinitionEffectPointModifiers.LookupByKey(traitDefinitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeStarterBuildTraitConfig(TraitConfigPacket traitConfig, Player player)
|
||||||
|
{
|
||||||
|
traitConfig.Entries.Clear();
|
||||||
|
var trees = GetTreesForConfig(traitConfig);
|
||||||
|
if (trees == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (TraitEntry grant in GetGrantedTraitEntriesForConfig(traitConfig, player))
|
||||||
|
{
|
||||||
|
TraitEntryPacket newEntry = new();
|
||||||
|
newEntry.TraitNodeID = grant.TraitNodeID;
|
||||||
|
newEntry.TraitNodeEntryID = grant.TraitNodeEntryID;
|
||||||
|
newEntry.GrantedRanks = grant.GrantedRanks;
|
||||||
|
traitConfig.Entries.Add(newEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<int, int> currencies = new();
|
||||||
|
FillOwnedCurrenciesMap(traitConfig, player, currencies);
|
||||||
|
|
||||||
|
var loadoutEntries = _traitTreeLoadoutsByChrSpecialization.LookupByKey(traitConfig.ChrSpecializationID);
|
||||||
|
if (loadoutEntries != null)
|
||||||
|
{
|
||||||
|
TraitEntryPacket findEntry(TraitConfigPacket config, int traitNodeId, int traitNodeEntryId)
|
||||||
|
{
|
||||||
|
return config.Entries.Find(traitEntry => traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TraitTreeLoadoutEntryRecord loadoutEntry in loadoutEntries)
|
||||||
|
{
|
||||||
|
int addedRanks = loadoutEntry.NumPoints;
|
||||||
|
Node node = _traitNodes.LookupByKey(loadoutEntry.SelectedTraitNodeID);
|
||||||
|
|
||||||
|
TraitEntryPacket newEntry = new();
|
||||||
|
newEntry.TraitNodeID = loadoutEntry.SelectedTraitNodeID;
|
||||||
|
newEntry.TraitNodeEntryID = loadoutEntry.SelectedTraitNodeEntryID;
|
||||||
|
if (newEntry.TraitNodeEntryID == 0)
|
||||||
|
newEntry.TraitNodeEntryID = (int)node.Entries[0].Data.Id;
|
||||||
|
|
||||||
|
TraitEntryPacket entryInConfig = findEntry(traitConfig, newEntry.TraitNodeID, newEntry.TraitNodeEntryID);
|
||||||
|
|
||||||
|
if (entryInConfig != null)
|
||||||
|
addedRanks -= entryInConfig.Rank;
|
||||||
|
|
||||||
|
newEntry.Rank = addedRanks;
|
||||||
|
|
||||||
|
if (!HasEnoughCurrency(newEntry, currencies))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (entryInConfig != null)
|
||||||
|
entryInConfig.Rank += addedRanks;
|
||||||
|
else
|
||||||
|
traitConfig.Entries.Add(newEntry);
|
||||||
|
|
||||||
|
TakeCurrencyCost(newEntry, currencies);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeEntry
|
||||||
|
{
|
||||||
|
public TraitNodeEntryRecord Data;
|
||||||
|
public List<TraitCondRecord> Conditions = new();
|
||||||
|
public List<TraitCostRecord> Costs = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public TraitNodeRecord Data;
|
||||||
|
public List<NodeEntry> Entries = new();
|
||||||
|
public List<NodeGroup> Groups = new();
|
||||||
|
public List<Tuple<Node, TraitEdgeType>> ParentNodes = new(); // TraitEdge::LeftTraitNodeID
|
||||||
|
public List<TraitCondRecord> Conditions = new();
|
||||||
|
public List<TraitCostRecord> Costs = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeGroup
|
||||||
|
{
|
||||||
|
public TraitNodeGroupRecord Data;
|
||||||
|
public List<TraitCondRecord> Conditions = new();
|
||||||
|
public List<TraitCostRecord> Costs = new();
|
||||||
|
public List<Node> Nodes = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tree
|
||||||
|
{
|
||||||
|
public TraitTreeRecord Data;
|
||||||
|
public List<Node> Nodes = new();
|
||||||
|
public List<TraitCostRecord> Costs = new();
|
||||||
|
public List<TraitCurrencyRecord> Currencies = new();
|
||||||
|
public TraitConfigType ConfigType;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user