aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp28
-rw-r--r--src/server/game/DataStores/DB2Stores.h6
-rw-r--r--src/server/game/DataStores/DB2Structure.h14
-rw-r--r--src/server/game/Entities/Player/Player.cpp89
-rw-r--r--src/server/game/Entities/Player/Player.h37
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp4
-rw-r--r--src/server/game/Server/Packets/TalentPackets.cpp19
-rw-r--r--src/server/game/Server/Packets/TalentPackets.h19
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/game/Spells/Spell.cpp58
-rw-r--r--src/server/game/Spells/Spell.h4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp35
12 files changed, 291 insertions, 24 deletions
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index a893bcc5ece..506dccd1f23 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -91,7 +91,9 @@ DB2Storage<GarrPlotInstanceEntry> sGarrPlotInstanceStore("GarrPlot
DB2Storage<GarrSiteLevelEntry> sGarrSiteLevelStore("GarrSiteLevel.db2", GarrSiteLevelMeta::Instance(), HOTFIX_SEL_GARR_SITE_LEVEL);
DB2Storage<GarrSiteLevelPlotInstEntry> sGarrSiteLevelPlotInstStore("GarrSiteLevelPlotInst.db2", GarrSiteLevelPlotInstMeta::Instance(), HOTFIX_SEL_GARR_SITE_LEVEL_PLOT_INST);
DB2Storage<GemPropertiesEntry> sGemPropertiesStore("GemProperties.db2", GemPropertiesMeta::Instance(), HOTFIX_SEL_GEM_PROPERTIES);
+DB2Storage<GlyphBindableSpellEntry> sGlyphBindableSpellStore("GlyphBindableSpell.db2", GlyphBindableSpellMeta::Instance(), HOTFIX_SEL_GLYPH_BINDABLE_SPELL);
DB2Storage<GlyphPropertiesEntry> sGlyphPropertiesStore("GlyphProperties.db2", GlyphPropertiesMeta::Instance(), HOTFIX_SEL_GLYPH_PROPERTIES);
+DB2Storage<GlyphRequiredSpecEntry> sGlyphRequiredSpecStore("GlyphRequiredSpec.db2", GlyphRequiredSpecMeta::Instance(), HOTFIX_SEL_GLYPH_REQUIRED_SPEC);
DB2Storage<GuildColorBackgroundEntry> sGuildColorBackgroundStore("GuildColorBackground.db2", GuildColorBackgroundMeta::Instance(), HOTFIX_SEL_GUILD_COLOR_BACKGROUND);
DB2Storage<GuildColorBorderEntry> sGuildColorBorderStore("GuildColorBorder.db2", GuildColorBorderMeta::Instance(), HOTFIX_SEL_GUILD_COLOR_BORDER);
DB2Storage<GuildColorEmblemEntry> sGuildColorEmblemStore("GuildColorEmblem.db2", GuildColorEmblemMeta::Instance(), HOTFIX_SEL_GUILD_COLOR_EMBLEM);
@@ -362,7 +364,9 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sGarrSiteLevelStore);
LOAD_DB2(sGarrSiteLevelPlotInstStore);
LOAD_DB2(sGemPropertiesStore);
+ LOAD_DB2(sGlyphBindableSpellStore);
LOAD_DB2(sGlyphPropertiesStore);
+ LOAD_DB2(sGlyphRequiredSpecStore);
LOAD_DB2(sGuildColorBackgroundStore);
LOAD_DB2(sGuildColorBorderStore);
LOAD_DB2(sGuildColorEmblemStore);
@@ -627,6 +631,12 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
for (HeirloomEntry const* heirloom : sHeirloomStore)
_heirlooms[heirloom->ItemID] = heirloom;
+ for (GlyphBindableSpellEntry const* glyphBindableSpell : sGlyphBindableSpellStore)
+ _glyphBindableSpells[glyphBindableSpell->GlyphPropertiesID].push_back(glyphBindableSpell->SpellID);
+
+ for (GlyphRequiredSpecEntry const* glyphRequiredSpec : sGlyphRequiredSpecStore)
+ _glyphRequiredSpecs[glyphRequiredSpec->GlyphPropertiesID].push_back(glyphRequiredSpec->ChrSpecializationID);
+
for (ItemBonusEntry const* bonus : sItemBonusStore)
_itemBonusLists[bonus->BonusListID].push_back(bonus);
@@ -1245,6 +1255,24 @@ HeirloomEntry const* DB2Manager::GetHeirloomByItemId(uint32 itemId) const
return nullptr;
}
+std::vector<uint32> const* DB2Manager::GetGlyphBindableSpells(uint32 glyphPropertiesId) const
+{
+ auto itr = _glyphBindableSpells.find(glyphPropertiesId);
+ if (itr != _glyphBindableSpells.end())
+ return &itr->second;
+
+ return nullptr;
+}
+
+std::vector<uint32> const* DB2Manager::GetGlyphRequiredSpecs(uint32 glyphPropertiesId) const
+{
+ auto itr = _glyphRequiredSpecs.find(glyphPropertiesId);
+ if (itr != _glyphRequiredSpecs.end())
+ return &itr->second;
+
+ return nullptr;
+}
+
DB2Manager::ItemBonusList const* DB2Manager::GetItemBonusList(uint32 bonusListId) const
{
auto itr = _itemBonusLists.find(bonusListId);
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index b8e5569ec9b..e435554be18 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -237,6 +237,8 @@ public:
typedef std::map<std::tuple<uint32, uint8, uint8, uint8>, EmotesTextSoundEntry const*> EmotesTextSoundContainer;
typedef std::unordered_map<uint32, std::vector<uint32>> FactionTeamContainer;
typedef std::unordered_map<uint32, HeirloomEntry const*> HeirloomItemsContainer;
+ typedef std::unordered_map<uint32 /*glyphPropertiesId*/, std::vector<uint32>> GlyphBindableSpellsContainer;
+ typedef std::unordered_map<uint32 /*glyphPropertiesId*/, std::vector<uint32>> GlyphRequiredSpecsContainer;
typedef std::vector<ItemBonusEntry const*> ItemBonusList;
typedef std::unordered_map<uint32 /*bonusListId*/, ItemBonusList> ItemBonusListContainer;
typedef std::unordered_map<int16, uint32> ItemBonusListLevelDeltaContainer;
@@ -291,6 +293,8 @@ public:
EmotesTextSoundEntry const* GetTextSoundEmoteFor(uint32 emote, uint8 race, uint8 gender, uint8 class_) const;
std::vector<uint32> const* GetFactionTeamList(uint32 faction) const;
HeirloomEntry const* GetHeirloomByItemId(uint32 itemId) const;
+ std::vector<uint32> const* GetGlyphBindableSpells(uint32 glyphPropertiesId) const;
+ std::vector<uint32> const* GetGlyphRequiredSpecs(uint32 glyphPropertiesId) const;
ItemBonusList const* GetItemBonusList(uint32 bonusListId) const;
uint32 GetItemBonusListForItemLevelDelta(int16 delta) const;
std::set<uint32> GetItemBonusTree(uint32 itemId, uint32 itemBonusTreeMod) const;
@@ -348,6 +352,8 @@ private:
EmotesTextSoundContainer _emoteTextSounds;
FactionTeamContainer _factionTeams;
HeirloomItemsContainer _heirlooms;
+ GlyphBindableSpellsContainer _glyphBindableSpells;
+ GlyphRequiredSpecsContainer _glyphRequiredSpecs;
ItemBonusListContainer _itemBonusLists;
ItemBonusListLevelDeltaContainer _itemLevelDeltaToBonusListContainer;
ItemBonusTreeContainer _itemBonusTrees;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 627b2f6b916..319d69818f0 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -1108,6 +1108,13 @@ struct GemPropertiesEntry
uint16 MinItemLevel;
};
+struct GlyphBindableSpellEntry
+{
+ uint32 ID;
+ uint32 SpellID;
+ uint16 GlyphPropertiesID;
+};
+
struct GlyphPropertiesEntry
{
uint32 ID;
@@ -1117,6 +1124,13 @@ struct GlyphPropertiesEntry
uint8 GlyphExclusiveCategoryID;
};
+struct GlyphRequiredSpecEntry
+{
+ uint32 ID;
+ uint16 GlyphPropertiesID;
+ uint16 ChrSpecializationID;
+};
+
struct GuildColorBackgroundEntry
{
uint32 ID;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 68a4f60e72f..491397a5a70 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -256,7 +256,6 @@ Player::Player(WorldSession* session) : Unit(true)
m_prevMapDifficulty = DIFFICULTY_NORMAL_RAID;
m_lastPotionId = 0;
- _talentMgr = new PlayerTalentInfo();
for (uint8 i = 0; i < BASEMOD_END; ++i)
{
@@ -352,8 +351,6 @@ Player::~Player()
for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
delete itr->second;
- delete _talentMgr;
-
//all mailed items should be deleted, also all mail should be deallocated
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
delete *itr;
@@ -4077,6 +4074,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
trans->Append(stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
+ stmt->setUInt64(0, guid);
+ trans->Append(stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY);
stmt->setUInt64(0, guid);
trans->Append(stmt);
@@ -17638,7 +17639,9 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
LearnSpecializationSpells();
+ _LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
_LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURA_EFFECTS), time_diff);
+ _LoadGlyphAuras();
// add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
m_deathState = DEAD;
@@ -18002,6 +18005,12 @@ void Player::_LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effe
}
}
+void Player::_LoadGlyphAuras()
+{
+ for (uint32 glyphId : GetGlyphs(GetActiveTalentGroup()))
+ CastSpell(this, sGlyphPropertiesStore.AssertEntry(glyphId)->SpellID, true);
+}
+
void Player::LoadCorpse(PreparedQueryResult result)
{
if (IsAlive() || HasAtLoginFlag(AT_LOGIN_RESURRECT))
@@ -19657,6 +19666,7 @@ void Player::SaveToDB(bool create /*=false*/)
_SaveWeeklyQuestStatus(trans);
_SaveSeasonalQuestStatus(trans);
_SaveMonthlyQuestStatus(trans);
+ _SaveGlyphs(trans);
_SaveTalents(trans);
_SaveSpells(trans);
GetSpellHistory()->SaveToDB<Player>(trans);
@@ -23078,6 +23088,17 @@ void Player::SendInitialPacketsBeforeAddToMap()
GetSpellHistory()->WritePacket(&sendSpellCharges);
SendDirectMessage(sendSpellCharges.Write());
+ WorldPackets::Talent::ActiveGlyphs activeGlyphs;
+ activeGlyphs.Glyphs.reserve(GetGlyphs(GetActiveTalentGroup()).size());
+ for (uint32 glyphId : GetGlyphs(GetActiveTalentGroup()))
+ if (std::vector<uint32> const* bindableSpells = sDB2Manager.GetGlyphBindableSpells(glyphId))
+ for (uint32 bindableSpell : *bindableSpells)
+ if (HasSpell(bindableSpell) && m_overrideSpells.find(bindableSpell) == m_overrideSpells.end())
+ activeGlyphs.Glyphs.emplace_back(uint32(bindableSpell), uint16(glyphId));
+
+ activeGlyphs.IsFullUpdate = true;
+ SendDirectMessage(activeGlyphs.Write());
+
/// SMSG_ACTION_BUTTONS
SendInitialActionButtons();
@@ -25882,6 +25903,51 @@ void Player::SetMap(Map* map)
m_mapRef.link(map, this);
}
+void Player::_LoadGlyphs(PreparedQueryResult result)
+{
+ // SELECT talentGroup, glyphId from character_glyphs WHERE guid = ?
+ if (!result)
+ return;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint8 spec = fields[0].GetUInt8();
+ if (spec >= MAX_SPECIALIZATIONS || !sDB2Manager.GetChrSpecializationByIndex(getClass(), spec))
+ continue;
+
+ uint16 glyphId = fields[1].GetUInt16();
+ if (!sGlyphPropertiesStore.LookupEntry(glyphId))
+ continue;
+
+ GetGlyphs(spec).push_back(glyphId);
+
+ } while (result->NextRow());
+}
+
+void Player::_SaveGlyphs(SQLTransaction& trans) const
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
+ stmt->setUInt64(0, GetGUID().GetCounter());
+ trans->Append(stmt);
+
+ for (uint8 spec = 0; spec < MAX_SPECIALIZATIONS; ++spec)
+ {
+ for (uint32 glyphId : GetGlyphs(spec))
+ {
+ uint8 index = 0;
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GLYPHS);
+ stmt->setUInt64(index++, GetGUID().GetCounter());
+ stmt->setUInt8(index++, spec);
+ stmt->setUInt16(index++, uint16(glyphId));
+
+ trans->Append(stmt);
+ }
+ }
+}
+
void Player::_LoadTalents(PreparedQueryResult result)
{
// SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
@@ -25985,6 +26051,9 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec)
// Remove spec specific spells
RemoveSpecializationSpells();
+ for (uint32 glyphId : GetGlyphs(GetActiveTalentGroup()))
+ RemoveAurasDueToSpell(sGlyphPropertiesStore.AssertEntry(glyphId)->SpellID);
+
SetActiveTalentGroup(spec->OrderIndex);
SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, spec->ID);
if (!GetPrimarySpecialization())
@@ -26041,6 +26110,20 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec)
for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
if (Item* equippedItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
SetVisibleItemSlot(i, equippedItem);
+
+ for (uint32 glyphId : GetGlyphs(spec->OrderIndex))
+ CastSpell(this, sGlyphPropertiesStore.AssertEntry(glyphId)->SpellID, true);
+
+ WorldPackets::Talent::ActiveGlyphs activeGlyphs;
+ activeGlyphs.Glyphs.reserve(GetGlyphs(spec->OrderIndex).size());
+ for (uint32 glyphId : GetGlyphs(spec->OrderIndex))
+ if (std::vector<uint32> const* bindableSpells = sDB2Manager.GetGlyphBindableSpells(glyphId))
+ for (uint32 bindableSpell : *bindableSpells)
+ if (HasSpell(bindableSpell) && m_overrideSpells.find(bindableSpell) == m_overrideSpells.end())
+ activeGlyphs.Glyphs.emplace_back(uint32(bindableSpell), uint16(glyphId));
+
+ activeGlyphs.IsFullUpdate = true;
+ SendDirectMessage(activeGlyphs.Write());
}
void Player::ResetTimeSync()
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index fd044cad8c2..1a6481d296a 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -972,6 +972,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS,
PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFITS,
PLAYER_LOGIN_QUERY_LOAD_BG_DATA,
+ PLAYER_LOGIN_QUERY_LOAD_GLYPHS,
PLAYER_LOGIN_QUERY_LOAD_TALENTS,
PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA,
PLAYER_LOGIN_QUERY_LOAD_SKILLS,
@@ -1173,21 +1174,22 @@ static uint32 const DefaultTalentRowLevels[MAX_TALENT_TIERS] = { 15, 30, 45, 60,
static uint32 const DKTalentRowLevels[MAX_TALENT_TIERS] = { 57, 58, 59, 60, 75, 90, 100 };
static uint32 const DHTalentRowLevels[MAX_TALENT_TIERS] = { 99, 100, 102, 104, 106, 108, 110 };
-struct TC_GAME_API PlayerTalentInfo
+struct TC_GAME_API SpecializationInfo
{
- PlayerTalentInfo() : ResetTalentsCost(0), ResetTalentsTime(0), PrimarySpecialization(0), ActiveGroup(0)
+ SpecializationInfo() : ResetTalentsCost(0), ResetTalentsTime(0), PrimarySpecialization(0), ActiveGroup(0)
{
}
PlayerTalentMap Talents[MAX_SPECIALIZATIONS];
+ std::vector<uint32> Glyphs[MAX_SPECIALIZATIONS];
uint32 ResetTalentsCost;
time_t ResetTalentsTime;
uint32 PrimarySpecialization;
uint8 ActiveGroup;
private:
- PlayerTalentInfo(PlayerTalentInfo const&) = delete;
- PlayerTalentInfo& operator=(PlayerTalentInfo const&) = delete;
+ SpecializationInfo(SpecializationInfo const&) = delete;
+ SpecializationInfo& operator=(SpecializationInfo const&) = delete;
};
class TC_GAME_API Player : public Unit, public GridObject<Player>
@@ -1767,14 +1769,14 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
uint32 GetLootSpecId() const { return GetUInt32Value(PLAYER_FIELD_LOOT_SPEC_ID); }
// Talents
- uint32 GetTalentResetCost() const { return _talentMgr->ResetTalentsCost; }
- void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; }
- time_t GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; }
- void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; }
- uint32 GetPrimarySpecialization() const { return _talentMgr->PrimarySpecialization; }
- void SetPrimarySpecialization(uint32 spec) { _talentMgr->PrimarySpecialization = spec; }
- uint8 GetActiveTalentGroup() const { return _talentMgr->ActiveGroup; }
- void SetActiveTalentGroup(uint8 group){ _talentMgr->ActiveGroup = group; }
+ uint32 GetTalentResetCost() const { return _specializationInfo.ResetTalentsCost; }
+ void SetTalentResetCost(uint32 cost) { _specializationInfo.ResetTalentsCost = cost; }
+ time_t GetTalentResetTime() const { return _specializationInfo.ResetTalentsTime; }
+ void SetTalentResetTime(time_t time_) { _specializationInfo.ResetTalentsTime = time_; }
+ uint32 GetPrimarySpecialization() const { return _specializationInfo.PrimarySpecialization; }
+ void SetPrimarySpecialization(uint32 spec) { _specializationInfo.PrimarySpecialization = spec; }
+ uint8 GetActiveTalentGroup() const { return _specializationInfo.ActiveGroup; }
+ void SetActiveTalentGroup(uint8 group){ _specializationInfo.ActiveGroup = group; }
uint32 GetDefaultSpecId() const;
bool ResetTalents(bool noCost = false);
@@ -1791,8 +1793,10 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
// Dual Spec
void ActivateTalentGroup(ChrSpecializationEntry const* spec);
- PlayerTalentMap const* GetTalentMap(uint8 spec) const { return &_talentMgr->Talents[spec]; }
- PlayerTalentMap* GetTalentMap(uint8 spec) { return &_talentMgr->Talents[spec]; }
+ PlayerTalentMap const* GetTalentMap(uint8 spec) const { return &_specializationInfo.Talents[spec]; }
+ PlayerTalentMap* GetTalentMap(uint8 spec) { return &_specializationInfo.Talents[spec]; }
+ std::vector<uint32> const& GetGlyphs(uint8 spec) const { return _specializationInfo.Glyphs[spec]; }
+ std::vector<uint32>& GetGlyphs(uint8 spec) { return _specializationInfo.Glyphs[spec]; }
ActionButtonList const& GetActionButtons() const { return m_actionButtons; }
uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS); }
@@ -2522,6 +2526,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadActions(PreparedQueryResult result);
void _LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effectResult, uint32 timediff);
+ void _LoadGlyphAuras();
void _LoadBoundInstances(PreparedQueryResult result);
void _LoadInventory(PreparedQueryResult result, PreparedQueryResult artifactsResult, uint32 timeDiff);
void _LoadVoidStorage(PreparedQueryResult result);
@@ -2545,6 +2550,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadEquipmentSets(PreparedQueryResult result);
void _LoadTransmogOutfits(PreparedQueryResult result);
void _LoadBGData(PreparedQueryResult result);
+ void _LoadGlyphs(PreparedQueryResult result);
void _LoadTalents(PreparedQueryResult result);
void _LoadInstanceTimeRestrictions(PreparedQueryResult result);
void _LoadCurrency(PreparedQueryResult result);
@@ -2568,6 +2574,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _SaveSpells(SQLTransaction& trans);
void _SaveEquipmentSets(SQLTransaction& trans);
void _SaveBGData(SQLTransaction& trans);
+ void _SaveGlyphs(SQLTransaction& trans) const;
void _SaveTalents(SQLTransaction& trans);
void _SaveStats(SQLTransaction& trans) const;
void _SaveInstanceTimeRestrictions(SQLTransaction& trans);
@@ -2648,7 +2655,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
std::unordered_map<uint32 /*overridenSpellId*/, std::unordered_set<uint32> /*newSpellId*/> m_overrideSpells;
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
- PlayerTalentInfo* _talentMgr;
+ SpecializationInfo _specializationInfo;
ActionButtonList m_actionButtons;
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index efbd3bcdcef..10f601f91b8 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -207,6 +207,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BG_DATA, stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GLYPHS);
+ stmt->setUInt64(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GLYPHS, stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TALENTS);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TALENTS, stmt);
diff --git a/src/server/game/Server/Packets/TalentPackets.cpp b/src/server/game/Server/Packets/TalentPackets.cpp
index 1069b8a89f7..c6f3b95a02f 100644
--- a/src/server/game/Server/Packets/TalentPackets.cpp
+++ b/src/server/game/Server/Packets/TalentPackets.cpp
@@ -70,3 +70,22 @@ WorldPacket const* WorldPackets::Talent::LearnTalentsFailed::Write()
return &_worldPacket;
}
+
+ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Talent::GlyphBinding const& glyphBinding)
+{
+ data << uint32(glyphBinding.SpellID);
+ data << uint16(glyphBinding.GlyphID);
+ return data;
+}
+
+WorldPacket const* WorldPackets::Talent::ActiveGlyphs::Write()
+{
+ _worldPacket << uint32(Glyphs.size());
+ for (GlyphBinding const& glyph : Glyphs)
+ _worldPacket << glyph;
+
+ _worldPacket.WriteBit(IsFullUpdate);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/TalentPackets.h b/src/server/game/Server/Packets/TalentPackets.h
index 57893bb94a4..92c2e1efdce 100644
--- a/src/server/game/Server/Packets/TalentPackets.h
+++ b/src/server/game/Server/Packets/TalentPackets.h
@@ -93,6 +93,25 @@ namespace WorldPackets
int32 SpellID = 0;
std::vector<uint16> Talents;
};
+
+ struct GlyphBinding
+ {
+ GlyphBinding(uint32 spellId = 0, uint16 glyphId = 0) : SpellID(spellId), GlyphID(glyphId) { }
+
+ uint32 SpellID;
+ uint16 GlyphID;
+ };
+
+ class ActiveGlyphs final : public ServerPacket
+ {
+ public:
+ ActiveGlyphs() : ServerPacket(SMSG_ACTIVE_GLYPHS) { }
+
+ WorldPacket const* Write() override;
+
+ std::vector<GlyphBinding> Glyphs;
+ bool IsFullUpdate = false;
+ };
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 9a6d7adba71..96ce155de11 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -837,7 +837,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_DELETED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_EARNED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTIVATE_TAXI_REPLY, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTIVE_GLYPHS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTIVE_GLYPHS, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADD_BATTLENET_FRIEND_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADD_ITEM_PASSIVE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADD_LOSS_OF_CONTROL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index b304491ba31..d6fa46bb267 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -5100,10 +5100,60 @@ SpellCastResult Spell::CheckCast(bool strict)
}
case SPELL_EFFECT_APPLY_GLYPH:
{
- uint32 glyphId = effect->MiscValue;
- if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyphId))
- if (m_caster->HasAura(gp->SpellID))
- return SPELL_FAILED_UNIQUE_GLYPH;
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_GLYPH_NO_SPEC;
+
+ Player* caster = m_caster->ToPlayer();
+ if (!caster->HasSpell(m_misc.SpellId))
+ return SPELL_FAILED_NOT_KNOWN;
+
+ if (uint32 glyphId = effect->MiscValue)
+ {
+ GlyphPropertiesEntry const* glyphProperties = sGlyphPropertiesStore.LookupEntry(glyphId);
+ if (!glyphProperties)
+ return SPELL_FAILED_INVALID_GLYPH;
+
+ std::vector<uint32> const* glyphBindableSpells = sDB2Manager.GetGlyphBindableSpells(glyphId);
+ if (!glyphBindableSpells)
+ return SPELL_FAILED_INVALID_GLYPH;
+
+ if (std::find(glyphBindableSpells->begin(), glyphBindableSpells->end(), m_misc.SpellId) == glyphBindableSpells->end())
+ return SPELL_FAILED_INVALID_GLYPH;
+
+ if (std::vector<uint32> const* glyphRequiredSpecs = sDB2Manager.GetGlyphRequiredSpecs(glyphId))
+ {
+ if (!caster->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID))
+ return SPELL_FAILED_GLYPH_NO_SPEC;
+
+ if (std::find(glyphRequiredSpecs->begin(), glyphRequiredSpecs->end(), caster->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)) == glyphRequiredSpecs->end())
+ return SPELL_FAILED_GLYPH_INVALID_SPEC;
+ }
+
+ uint32 replacedGlyph = 0;
+ for (uint32 activeGlyphId : caster->GetGlyphs(caster->GetActiveTalentGroup()))
+ {
+ if (std::vector<uint32> const* activeGlyphBindableSpells = sDB2Manager.GetGlyphBindableSpells(activeGlyphId))
+ {
+ if (std::find(activeGlyphBindableSpells->begin(), activeGlyphBindableSpells->end(), m_misc.SpellId) != activeGlyphBindableSpells->end())
+ {
+ replacedGlyph = activeGlyphId;
+ break;
+ }
+ }
+ }
+
+ for (uint32 activeGlyphId : caster->GetGlyphs(caster->GetActiveTalentGroup()))
+ {
+ if (activeGlyphId == replacedGlyph)
+ continue;
+
+ if (activeGlyphId == glyphId)
+ return SPELL_FAILED_UNIQUE_GLYPH;
+
+ if (sGlyphPropertiesStore.AssertEntry(activeGlyphId)->GlyphExclusiveCategoryID == glyphProperties->GlyphExclusiveCategoryID)
+ return SPELL_FAILED_GLYPH_EXCLUSIVE_CATEGORY;
+ }
+ }
break;
}
case SPELL_EFFECT_FEED_PET:
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 33e3107dc03..1fa5fa6aa08 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -570,7 +570,9 @@ class TC_GAME_API Spell
{
// Alternate names for this value
uint32 TalentId;
- uint32 GlyphSlot;
+
+ // SPELL_EFFECT_APPLY_GLYPH
+ uint32 SpellId;
// SPELL_EFFECT_TALENT_SPEC_SELECT
uint32 SpecializationId;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index e2656495467..0df4abea54f 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -61,6 +61,7 @@
#include "DuelPackets.h"
#include "MiscPackets.h"
#include "SpellPackets.h"
+#include "TalentPackets.h"
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
{
@@ -3923,6 +3924,40 @@ void Spell::EffectApplyGlyph(SpellEffIndex /*effIndex*/)
Player* player = m_caster->ToPlayer();
if (!player)
return;
+
+ std::vector<uint32>& glyphs = player->GetGlyphs(player->GetActiveTalentGroup());
+ std::size_t replacedGlyph = glyphs.size();
+ for (std::size_t i = 0; i < glyphs.size(); ++i)
+ {
+ if (std::vector<uint32> const* activeGlyphBindableSpells = sDB2Manager.GetGlyphBindableSpells(glyphs[i]))
+ {
+ if (std::find(activeGlyphBindableSpells->begin(), activeGlyphBindableSpells->end(), m_misc.SpellId) != activeGlyphBindableSpells->end())
+ {
+ replacedGlyph = i;
+ player->RemoveAurasDueToSpell(sGlyphPropertiesStore.AssertEntry(glyphs[i])->SpellID);
+ break;
+ }
+ }
+ }
+
+ uint32 glyphId = effectInfo->MiscValue;
+ if (replacedGlyph < glyphs.size())
+ {
+ if (glyphId)
+ glyphs[replacedGlyph] = glyphId;
+ else
+ glyphs.erase(glyphs.begin() + replacedGlyph);
+ }
+ else if (glyphId)
+ glyphs.push_back(glyphId);
+
+ if (GlyphPropertiesEntry const* glyphProperties = sGlyphPropertiesStore.LookupEntry(glyphId))
+ player->CastSpell(player, glyphProperties->SpellID, true);
+
+ WorldPackets::Talent::ActiveGlyphs activeGlyphs;
+ activeGlyphs.Glyphs.emplace_back(m_misc.SpellId, uint16(glyphId));
+ activeGlyphs.IsFullUpdate = false;
+ player->SendDirectMessage(activeGlyphs.Write());
}
void Spell::EffectEnchantHeldItem(SpellEffIndex /*effIndex*/)