From a727a99e34a1a50b4b7589ab549751caa63282b0 Mon Sep 17 00:00:00 2001 From: Shauren Date: Thu, 25 Aug 2016 19:12:00 +0200 Subject: Core/Players: Implemented new glyph system --- .../Database/Implementation/CharacterDatabase.cpp | 3 + .../Database/Implementation/CharacterDatabase.h | 3 + .../Database/Implementation/HotfixDatabase.cpp | 6 ++ .../Database/Implementation/HotfixDatabase.h | 4 + src/server/game/DataStores/DB2Stores.cpp | 28 +++++++ src/server/game/DataStores/DB2Stores.h | 6 ++ src/server/game/DataStores/DB2Structure.h | 14 ++++ src/server/game/Entities/Player/Player.cpp | 89 +++++++++++++++++++++- src/server/game/Entities/Player/Player.h | 37 +++++---- src/server/game/Handlers/CharacterHandler.cpp | 4 + src/server/game/Server/Packets/TalentPackets.cpp | 19 +++++ src/server/game/Server/Packets/TalentPackets.h | 19 +++++ src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Spells/Spell.cpp | 58 +++++++++++++- src/server/game/Spells/Spell.h | 4 +- src/server/game/Spells/SpellEffects.cpp | 35 +++++++++ 16 files changed, 307 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 0380cd45c2f..364b98d545d 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -135,6 +135,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "appearance5, appearance6, appearance7, appearance8, appearance9, appearance10, appearance11, appearance12, appearance13, appearance14, appearance15, appearance16, " "appearance17, appearance18, mainHandEnchant, offHandEnchant FROM character_transmog_outfits WHERE guid = ? ORDER BY setindex", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyphId FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT spell, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC); @@ -583,6 +584,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFITS, "DELETE FROM character_transmog_outfits WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER, "DELETE FROM guild_eventlog WHERE PlayerGuid1 = ? OR PlayerGuid2 = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER, "DELETE FROM guild_bank_eventlog WHERE PlayerGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_GLYPHS, "DELETE FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_TALENT, "DELETE FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SKILLS, "DELETE FROM character_skills WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_MONEY, "UPDATE characters SET money = ? WHERE guid = ?", CONNECTION_ASYNC); @@ -611,6 +613,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "spellPower, resilience) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_BY_OWNER, "DELETE FROM petition WHERE ownerguid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER, "DELETE FROM petition_sign WHERE ownerguid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_GLYPHS, "INSERT INTO character_glyphs VALUES(?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, "DELETE FROM character_talent WHERE guid = ? and spell = ? and talentGroup = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, talentGroup) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index a8c26cdc316..c94cbdedd04 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -111,6 +111,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_EQUIPMENTSETS, CHAR_SEL_CHARACTER_TRANSMOG_OUTFITS, CHAR_SEL_CHARACTER_BGDATA, + CHAR_SEL_CHARACTER_GLYPHS, CHAR_SEL_CHARACTER_TALENTS, CHAR_SEL_CHARACTER_SKILLS, CHAR_SEL_CHARACTER_RANDOMBG, @@ -487,6 +488,7 @@ enum CharacterDatabaseStatements CHAR_DEL_CHAR_TRANSMOG_OUTFITS, CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER, CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER, + CHAR_DEL_CHAR_GLYPHS, CHAR_DEL_CHAR_TALENT, CHAR_DEL_CHAR_SKILLS, CHAR_UPD_CHAR_MONEY, @@ -513,6 +515,7 @@ enum CharacterDatabaseStatements CHAR_INS_CHAR_STATS, CHAR_DEL_PETITION_BY_OWNER, CHAR_DEL_PETITION_SIGNATURE_BY_OWNER, + CHAR_INS_CHAR_GLYPHS, CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, CHAR_INS_CHAR_TALENT, CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index b62d54626f6..66fd71cd5f9 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -344,10 +344,16 @@ void HotfixDatabaseConnection::DoPrepareStatements() // GemProperties.db2 PrepareStatement(HOTFIX_SEL_GEM_PROPERTIES, "SELECT ID, Type, EnchantID, MinItemLevel FROM gem_properties ORDER BY ID DESC", CONNECTION_SYNCH); + // GlyphBindableSpell.db2 + PrepareStatement(HOTFIX_SEL_GLYPH_BINDABLE_SPELL, "SELECT ID, SpellID, GlyphPropertiesID FROM glyph_bindable_spell ORDER BY ID DESC", CONNECTION_SYNCH); + // GlyphProperties.db2 PrepareStatement(HOTFIX_SEL_GLYPH_PROPERTIES, "SELECT ID, SpellID, SpellIconID, Type, GlyphExclusiveCategoryID FROM glyph_properties" " ORDER BY ID DESC", CONNECTION_SYNCH); + // GlyphRequiredSpec.db2 + PrepareStatement(HOTFIX_SEL_GLYPH_REQUIRED_SPEC, "SELECT ID, GlyphPropertiesID, ChrSpecializationID FROM glyph_required_spec ORDER BY ID DESC", CONNECTION_SYNCH); + // GuildColorBackground.db2 PrepareStatement(HOTFIX_SEL_GUILD_COLOR_BACKGROUND, "SELECT ID, Red, Green, Blue FROM guild_color_background ORDER BY ID DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index f209c0d569b..4519255a299 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -196,8 +196,12 @@ enum HotfixDatabaseStatements HOTFIX_SEL_GEM_PROPERTIES, + HOTFIX_SEL_GLYPH_BINDABLE_SPELL, + HOTFIX_SEL_GLYPH_PROPERTIES, + HOTFIX_SEL_GLYPH_REQUIRED_SPEC, + HOTFIX_SEL_GUILD_COLOR_BACKGROUND, HOTFIX_SEL_GUILD_COLOR_BORDER, 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 sGarrPlotInstanceStore("GarrPlot DB2Storage sGarrSiteLevelStore("GarrSiteLevel.db2", GarrSiteLevelMeta::Instance(), HOTFIX_SEL_GARR_SITE_LEVEL); DB2Storage sGarrSiteLevelPlotInstStore("GarrSiteLevelPlotInst.db2", GarrSiteLevelPlotInstMeta::Instance(), HOTFIX_SEL_GARR_SITE_LEVEL_PLOT_INST); DB2Storage sGemPropertiesStore("GemProperties.db2", GemPropertiesMeta::Instance(), HOTFIX_SEL_GEM_PROPERTIES); +DB2Storage sGlyphBindableSpellStore("GlyphBindableSpell.db2", GlyphBindableSpellMeta::Instance(), HOTFIX_SEL_GLYPH_BINDABLE_SPELL); DB2Storage sGlyphPropertiesStore("GlyphProperties.db2", GlyphPropertiesMeta::Instance(), HOTFIX_SEL_GLYPH_PROPERTIES); +DB2Storage sGlyphRequiredSpecStore("GlyphRequiredSpec.db2", GlyphRequiredSpecMeta::Instance(), HOTFIX_SEL_GLYPH_REQUIRED_SPEC); DB2Storage sGuildColorBackgroundStore("GuildColorBackground.db2", GuildColorBackgroundMeta::Instance(), HOTFIX_SEL_GUILD_COLOR_BACKGROUND); DB2Storage sGuildColorBorderStore("GuildColorBorder.db2", GuildColorBorderMeta::Instance(), HOTFIX_SEL_GUILD_COLOR_BORDER); DB2Storage 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 const* DB2Manager::GetGlyphBindableSpells(uint32 glyphPropertiesId) const +{ + auto itr = _glyphBindableSpells.find(glyphPropertiesId); + if (itr != _glyphBindableSpells.end()) + return &itr->second; + + return nullptr; +} + +std::vector 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, EmotesTextSoundEntry const*> EmotesTextSoundContainer; typedef std::unordered_map> FactionTeamContainer; typedef std::unordered_map HeirloomItemsContainer; + typedef std::unordered_map> GlyphBindableSpellsContainer; + typedef std::unordered_map> GlyphRequiredSpecsContainer; typedef std::vector ItemBonusList; typedef std::unordered_map ItemBonusListContainer; typedef std::unordered_map ItemBonusListLevelDeltaContainer; @@ -291,6 +293,8 @@ public: EmotesTextSoundEntry const* GetTextSoundEmoteFor(uint32 emote, uint8 race, uint8 gender, uint8 class_) const; std::vector const* GetFactionTeamList(uint32 faction) const; HeirloomEntry const* GetHeirloomByItemId(uint32 itemId) const; + std::vector const* GetGlyphBindableSpells(uint32 glyphPropertiesId) const; + std::vector const* GetGlyphRequiredSpecs(uint32 glyphPropertiesId) const; ItemBonusList const* GetItemBonusList(uint32 bonusListId) const; uint32 GetItemBonusListForItemLevelDelta(int16 delta) const; std::set 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(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 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 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 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 @@ -1767,14 +1769,14 @@ class TC_GAME_API Player : public Unit, public GridObject 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 // 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 const& GetGlyphs(uint8 spec) const { return _specializationInfo.Glyphs[spec]; } + std::vector& 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 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 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 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 std::unordered_map /*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 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 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 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 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 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& glyphs = player->GetGlyphs(player->GetActiveTalentGroup()); + std::size_t replacedGlyph = glyphs.size(); + for (std::size_t i = 0; i < glyphs.size(); ++i) + { + if (std::vector 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*/) -- cgit v1.2.3