diff options
-rw-r--r-- | sql/base/characters_database.sql | 1 | ||||
-rw-r--r-- | sql/updates/characters/2012_01_29_00_.characters_characters.sql | 1 | ||||
-rwxr-xr-x | src/server/game/Chat/Commands/Level3.cpp | 2 | ||||
-rwxr-xr-x | src/server/game/DataStores/DBCStores.cpp | 27 | ||||
-rwxr-xr-x | src/server/game/DataStores/DBCStores.h | 2 | ||||
-rwxr-xr-x | src/server/game/DataStores/DBCStructure.h | 14 | ||||
-rwxr-xr-x | src/server/game/DataStores/DBCfmt.h | 2 | ||||
-rwxr-xr-x | src/server/game/Entities/Player/Player.cpp | 414 | ||||
-rwxr-xr-x | src/server/game/Entities/Player/Player.h | 98 | ||||
-rwxr-xr-x | src/server/game/Miscellaneous/SharedDefines.h | 5 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp | 4 | ||||
-rwxr-xr-x | src/server/game/Server/Protocol/Handlers/SkillHandler.cpp | 28 | ||||
-rwxr-xr-x | src/server/game/Server/Protocol/Handlers/SpellHandler.cpp | 1 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 12 | ||||
-rwxr-xr-x | src/server/game/Spells/SpellEffects.cpp | 16 | ||||
-rwxr-xr-x | src/server/shared/Database/Implementation/CharacterDatabase.cpp | 8 |
16 files changed, 390 insertions, 245 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index bce0a211b3c..5cd0db50dcb 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1127,6 +1127,7 @@ CREATE TABLE `characters` ( `rest_bonus` float NOT NULL DEFAULT '0', `resettalents_cost` int(10) unsigned NOT NULL DEFAULT '0', `resettalents_time` int(10) unsigned NOT NULL DEFAULT '0', + `talentTree` smallint(5) unsigned NOT NULL DEFAULT '0', `trans_x` float NOT NULL DEFAULT '0', `trans_y` float NOT NULL DEFAULT '0', `trans_z` float NOT NULL DEFAULT '0', diff --git a/sql/updates/characters/2012_01_29_00_.characters_characters.sql b/sql/updates/characters/2012_01_29_00_.characters_characters.sql new file mode 100644 index 00000000000..baaf37ac467 --- /dev/null +++ b/sql/updates/characters/2012_01_29_00_.characters_characters.sql @@ -0,0 +1 @@ +ALTER TABLE `characters` ADD `talentTree` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `resettalents_time`; diff --git a/src/server/game/Chat/Commands/Level3.cpp b/src/server/game/Chat/Commands/Level3.cpp index ee07c034b15..df35a8ed906 100755 --- a/src/server/game/Chat/Commands/Level3.cpp +++ b/src/server/game/Chat/Commands/Level3.cpp @@ -2627,7 +2627,7 @@ bool ChatHandler::HandleResetTalentsCommand(const char* args) if (target) { - target->resetTalents(true); + target->ResetTalents(true); target->SendTalentsInfoData(false); ChatHandler(target).SendSysMessage(LANG_RESET_TALENTS); if (!m_session || m_session->GetPlayer() != target) diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 0a7b1776bc9..98b21355504 100755 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -145,6 +145,7 @@ DBCStorage <MountTypeEntry> sMountTypeStore(MountTypefmt); DBCStorage <NameGenEntry> sNameGenStore(NameGenfmt); NameGenVectorArraysMap sGenNameVectoArraysMap; +DBCStorage <NumTalentsAtLevelEntry> sNumTalentsAtLevelStore(NumTalentsAtLevelfmt); DBCStorage <OverrideSpellDataEntry> sOverrideSpellDataStore(OverrideSpellDatafmt); @@ -203,6 +204,9 @@ DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt); DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt); TalentSpellPosMap sTalentSpellPosMap; DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt); +DBCStorage <TalentTreePrimarySpellsEntry> sTalentTreePrimarySpellsStore(TalentTreePrimarySpellsfmt); +typedef std::map<uint32, std::vector<uint32> > TalentTreePrimarySpellsMap; +TalentTreePrimarySpellsMap sTalentTreePrimarySpellsMap; // store absolute bit position for first rank for talent inspect static uint32 sTalentTabPages[MAX_CLASSES][3]; @@ -428,6 +432,7 @@ void LoadDBCStores(const std::string& dataPath) if (NameGenEntry const* entry = sNameGenStore.LookupEntry(i)) sGenNameVectoArraysMap[entry->race].stringVectorArray[entry->gender].push_back(std::string(entry->name)); sNameGenStore.Clear(); + LoadDBC(availableDbcLocales, bad_dbc_files, sNumTalentsAtLevelStore, dbcPath, "NumTalentsAtLevel.dbc");//14545 LoadDBC(availableDbcLocales, bad_dbc_files, sMovieStore, dbcPath, "Movie.dbc");//14545 @@ -584,13 +589,18 @@ void LoadDBCStores(const std::string& dataPath) continue; // store class talent tab pages - uint32 cls = 1; - for (uint32 m=1; !(m & talentTabInfo->ClassMask) && cls < MAX_CLASSES; m <<= 1, ++cls) {} - - sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId; + for (uint32 cls = 1; cls < MAX_CLASSES; ++cls) + if (talentTabInfo->ClassMask & (1 << (cls - 1))) + sTalentTabPages[cls][talentTabInfo->tabpage] = talentTabId; } } + LoadDBC(availableDbcLocales, bad_dbc_files, sTalentTreePrimarySpellsStore, dbcPath, "TalentTreePrimarySpells.dbc"); + for (uint32 i = 0; i < sTalentTreePrimarySpellsStore.GetNumRows(); ++i) + if (TalentTreePrimarySpellsEntry const* talentSpell = sTalentTreePrimarySpellsStore.LookupEntry(i)) + sTalentTreePrimarySpellsMap[talentSpell->TalentTree].push_back(talentSpell->SpellId); + sTalentTreePrimarySpellsStore.Clear(); + LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiNodesStore, dbcPath, "TaxiNodes.dbc");//14545 LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiPathStore, dbcPath, "TaxiPath.dbc");//14545 for (uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) @@ -985,6 +995,15 @@ uint32 const* GetTalentTabPages(uint8 cls) return sTalentTabPages[cls]; } +std::vector<uint32> const* GetTalentTreePrimarySpells(uint32 talentTree) +{ + TalentTreePrimarySpellsMap::const_iterator itr = sTalentTreePrimarySpellsMap.find(talentTree); + if (itr == sTalentTreePrimarySpellsMap.end()) + return NULL; + + return &itr->second; +} + uint32 ScalingStatValuesEntry::GetStatMultiplier(uint32 inventoryType) const { if (inventoryType < MAX_INVTYPE) diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 5fa8d83fc4b..f77f6195aea 100755 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -65,6 +65,7 @@ MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty); MapDifficulty const* GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty); uint32 const* /*[MAX_TALENT_TABS]*/ GetTalentTabPages(uint8 cls); +std::vector<uint32> const* GetTalentTreePrimarySpells(uint32 talentTree); PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level); PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id); @@ -143,6 +144,7 @@ extern DBCStorage <MapEntry> sMapStore; extern DBCStorage <MountCapabilityEntry> sMountCapabilityStore; extern DBCStorage <MountTypeEntry> sMountTypeStore; extern DBCStorage <NameGenEntry> sNameGenStore; +extern DBCStorage <NumTalentsAtLevelEntry> sNumTalentsAtLevelStore; extern DBCStorage <PhaseEntry> sPhaseStore; //extern DBCStorage <MapDifficultyEntry> sMapDifficultyStore; -- use GetMapDifficultyData insteed extern MapDifficultyMap sMapDifficultyMap; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index d57b6d68115..37ae10f52cb 100755 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1432,6 +1432,12 @@ struct NameGenEntry uint32 gender; }; +struct NumTalentsAtLevelEntry +{ + //uint32 Level; // 0 index + float Talents; // 1 talent count +}; + #define MAX_OVERRIDE_SPELL 10 struct OverrideSpellDataEntry @@ -2022,6 +2028,14 @@ struct TalentTabEntry //uint32 spellIds[2]; // 9-10 passive mastery bonus spells? }; +struct TalentTreePrimarySpellsEntry +{ + //uint32 Id; // 0 index + uint32 TalentTree; // 1 entry from TalentTab.dbc + uint32 SpellId; // 2 spell id to learn + //uint32 Flags; // 3 some kind of flags +}; + struct TaxiNodesEntry { uint32 ID; // 0 m_ID diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 59af1d0e7c6..81f6e1a75a0 100755 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -98,6 +98,7 @@ const char MovieEntryfmt[]="nxxx"; const char MountCapabilityfmt[]="niiiiiii"; const char MountTypefmt[]="niiiiiiiiiiiiiiiiiiiiiiii"; const char NameGenfmt[] = "dsii"; +const char NumTalentsAtLevelfmt[]="df"; const char OverrideSpellDatafmt[]="niiiiiiiiiixx"; const char QuestSortEntryfmt[]="nx"; const char QuestXPfmt[]="niiiiiiiiii"; @@ -144,6 +145,7 @@ const char StableSlotPricesfmt[] = "ni"; const char SummonPropertiesfmt[] = "niiiii"; const char TalentEntryfmt[]="niiiiiiiiixxixxxxxx"; const char TalentTabEntryfmt[]="nxxiiixxxxx"; +const char TalentTreePrimarySpellsfmt[]="diix"; const char TaxiNodesEntryfmt[]="nifffsiixxx"; const char TaxiPathEntryfmt[]="niii"; const char TaxiPathNodeEntryfmt[]="diiifffiiii"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index cdb9ddbcd9b..22bd1846bb5 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -663,9 +663,6 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep m_comboTarget = 0; m_comboPoints = 0; - m_usedTalentCount = 0; - m_questRewardTalentCount = 0; - m_regenTimer = 0; m_regenTimerCount = 0; m_weaponChangeTimer = 0; @@ -766,8 +763,6 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep unReadMails = 0; m_nextMailDelivereTime = 0; - m_resetTalentsCost = 0; - m_resetTalentsTime = 0; m_itemUpdateQueueBlocked = false; for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) @@ -783,19 +778,7 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL; m_lastPotionId = 0; - - m_activeSpec = 0; - m_specsCount = 1; - - m_freeTalentPoints = 0; - - for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) - { - for (uint8 g = 0; g < MAX_GLYPH_SLOT_INDEX; ++g) - m_Glyphs[i][g] = 0; - - m_talents[i] = new PlayerTalentMap(); - } + _talentMgr = new PlayerTalentInfo(); for (uint8 i = 0; i < BASEMOD_END; ++i) { @@ -857,7 +840,7 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep SetPendingBind(0, 0); } -Player::~Player () +Player::~Player() { // it must be unloaded already in PlayerLogout and accessed only for loggined player //m_social = NULL; @@ -869,12 +852,7 @@ Player::~Player () for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) delete itr->second; - for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) - { - for (PlayerTalentMap::const_iterator itr = m_talents[i]->begin(); itr != m_talents[i]->end(); ++itr) - delete itr->second; - delete m_talents[i]; - } + 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) @@ -2128,7 +2106,7 @@ void Player::SendSetFlyPacket(bool apply) data.WriteByteSeq(bytes[6]); data.WriteByteSeq(bytes[2]); } - else + else { data.WriteBit(bytes[1]); data.WriteBit(bytes[6]); @@ -3189,33 +3167,33 @@ void Player::InitTalentForLevel() if (level < 10) { // Remove all talent points - if (m_usedTalentCount > 0) // Free any used talents + if (GetUsedTalentCount() > 0) // Free any used talents { - resetTalents(true); + ResetTalents(true); SetFreeTalentPoints(0); } } else { - if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL) || m_specsCount == 0) + if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL) || GetSpecsCount() == 0) { - m_specsCount = 1; - m_activeSpec = 0; + SetSpecsCount(1); + SetActiveSpec(0); } uint32 talentPointsForLevel = CalculateTalentsPoints(); // if used more that have then reset - if (m_usedTalentCount > talentPointsForLevel) + if (GetUsedTalentCount() > talentPointsForLevel) { if (!AccountMgr::IsAdminAccount(GetSession()->GetSecurity())) - resetTalents(true); + ResetTalents(true); else SetFreeTalentPoints(0); } // else update amount of free points else - SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount); + SetFreeTalentPoints(talentPointsForLevel - GetUsedTalentCount()); } if (!GetSession()->PlayerLoading()) @@ -3560,8 +3538,8 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) return false; } - PlayerTalentMap::iterator itr = m_talents[spec]->find(spellId); - if (itr != m_talents[spec]->end()) + PlayerTalentMap::iterator itr = GetTalentMap(spec)->find(spellId); + if (itr != GetTalentMap(spec)->end()) itr->second->state = PLAYERSPELL_UNCHANGED; else if (TalentSpellPos const* talentPos = GetTalentSpellPos(spellId)) { @@ -3574,8 +3552,8 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) if (!rankSpellId || rankSpellId == spellId) continue; - itr = m_talents[spec]->find(rankSpellId); - if (itr != m_talents[spec]->end()) + itr = GetTalentMap(spec)->find(rankSpellId); + if (itr != GetTalentMap(spec)->end()) itr->second->state = PLAYERSPELL_REMOVED; } } @@ -3586,7 +3564,7 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) newtalent->state = state; newtalent->spec = spec; - (*m_talents[spec])[spellId] = newtalent; + (*GetTalentMap(spec))[spellId] = newtalent; return true; } return false; @@ -3860,7 +3838,7 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent } // update used talent points count - m_usedTalentCount += talentCost; + SetUsedTalentCount(GetUsedTalentCount() + talentCost); // update free primary prof.points (if any, can be none in case GM .learn prof. learning) if (uint32 freeProfs = GetFreePrimaryProfessionPoints()) @@ -4096,10 +4074,10 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) uint32 talentCosts = GetTalentSpellCost(spell_id); if (talentCosts > 0 && giveTalentPoints) { - if (talentCosts < m_usedTalentCount) - m_usedTalentCount -= talentCosts; + if (talentCosts < GetUsedTalentCount()) + SetUsedTalentCount(GetUsedTalentCount() - talentCosts); else - m_usedTalentCount = 0; + SetUsedTalentCount(0); } // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning) @@ -4442,31 +4420,31 @@ void Player::_SaveSpellCooldowns(SQLTransaction& trans) trans->Append(ss.str().c_str()); } -uint32 Player::resetTalentsCost() const +uint32 Player::GetNextResetTalentsCost() const { // The first time reset costs 1 gold - if (m_resetTalentsCost < 1*GOLD) + if (GetTalentResetCost() < 1*GOLD) return 1*GOLD; // then 5 gold - else if (m_resetTalentsCost < 5*GOLD) + else if (GetTalentResetCost() < 5*GOLD) return 5*GOLD; // After that it increases in increments of 5 gold - else if (m_resetTalentsCost < 10*GOLD) + else if (GetTalentResetCost() < 10*GOLD) return 10*GOLD; else { - uint64 months = (sWorld->GetGameTime() - m_resetTalentsTime)/MONTH; + uint64 months = (sWorld->GetGameTime() - GetTalentResetTime())/MONTH; if (months > 0) { // This cost will be reduced by a rate of 5 gold per month - int32 new_cost = int32(m_resetTalentsCost - 5*GOLD*months); + int32 new_cost = int32(GetTalentResetCost() - 5*GOLD*months); // to a minimum of 10 gold. return (new_cost < 10*GOLD ? 10*GOLD : new_cost); } else { // After that it increases in increments of 5 gold - int32 new_cost = m_resetTalentsCost + 5*GOLD; + int32 new_cost = GetTalentResetCost() + 5*GOLD; // until it hits a cap of 50 gold. if (new_cost > 50*GOLD) new_cost = 50*GOLD; @@ -4475,7 +4453,7 @@ uint32 Player::resetTalentsCost() const } } -bool Player::resetTalents(bool no_cost) +bool Player::ResetTalents(bool no_cost) { sScriptMgr->OnPlayerTalentsReset(this, no_cost); @@ -4485,7 +4463,7 @@ bool Player::resetTalents(bool no_cost) uint32 talentPointsForLevel = CalculateTalentsPoints(); - if (m_usedTalentCount == 0) + if (!GetUsedTalentCount()) { SetFreeTalentPoints(talentPointsForLevel); return false; @@ -4495,7 +4473,7 @@ bool Player::resetTalents(bool no_cost) if (!no_cost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST)) { - cost = resetTalentsCost(); + cost = GetNextResetTalentsCost(); if (!HasEnoughMoney(cost)) { @@ -4535,27 +4513,37 @@ bool Player::resetTalents(bool no_cost) if (_spellEntry->Effects[i].TriggerSpell > 0 && _spellEntry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL) removeSpell(_spellEntry->Effects[i].TriggerSpell, true); // and remove any spells that the talent teaches // if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted - PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(talentInfo->RankID[rank]); - if (plrTalent != m_talents[m_activeSpec]->end()) + PlayerTalentMap::iterator plrTalent = GetTalentMap(GetActiveSpec())->find(talentInfo->RankID[rank]); + if (plrTalent != GetTalentMap(GetActiveSpec())->end()) plrTalent->second->state = PLAYERSPELL_REMOVED; } } + // Remove spec specific spells + for (uint32 i = 0; i < MAX_TALENT_TABS; ++i) + { + std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(GetTalentTabPages(getClass())[i]); + if (specSpells) + for (size_t i = 0; i < specSpells->size(); ++i) + removeSpell(specSpells->at(i), true); + } + + SetPrimaryTalentTree(0); + SetFreeTalentPoints(talentPointsForLevel); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); _SaveTalents(trans); _SaveSpells(trans); CharacterDatabase.CommitTransaction(trans); - SetFreeTalentPoints(talentPointsForLevel); - if (!no_cost) { ModifyMoney(-(int32)cost); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS, 1); - m_resetTalentsCost = cost; - m_resetTalentsTime = time(NULL); + SetTalentResetCost(cost); + SetTalentResetTime(time(NULL)); } /* when prev line will dropped use next line @@ -4757,8 +4745,8 @@ bool Player::HasSpell(uint32 spell) const bool Player::HasTalent(uint32 spell, uint8 spec) const { - PlayerTalentMap::const_iterator itr = m_talents[spec]->find(spell); - return (itr != m_talents[spec]->end() && itr->second->state != PLAYERSPELL_REMOVED); + PlayerTalentMap::const_iterator itr = GetTalentMap(spec)->find(spell); + return (itr != GetTalentMap(spec)->end() && itr->second->state != PLAYERSPELL_REMOVED); } bool Player::HasActiveSpell(uint32 spell) const @@ -6707,7 +6695,7 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const void Player::SendActionButtons(uint32 state) const { - sLog->outDetail("Sending Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec); + sLog->outDetail("Sending Action Buttons for '%u' spec '%u'", GetGUIDLow(), GetActiveSpec()); WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4)); data << uint8(state); @@ -6730,7 +6718,7 @@ void Player::SendActionButtons(uint32 state) const } GetSession()->SendPacket(&data); - sLog->outDetail("Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(), m_activeSpec); + sLog->outDetail("Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(), GetActiveSpec()); } bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) @@ -9795,7 +9783,7 @@ void Player::SendTalentWipeConfirm(uint64 guid) { WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8+4)); data << uint64(guid); - uint32 cost = sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST) ? 0 : resetTalentsCost(); + uint32 cost = sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST) ? 0 : GetNextResetTalentsCost(); data << cost; GetSession()->SendPacket(&data); } @@ -15019,9 +15007,9 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, SetTitle(titleEntry); } - if (quest->GetBonusTalents()) + if (uint32 talents = quest->GetBonusTalents()) { - m_questRewardTalentCount+=quest->GetBonusTalents(); + AddQuestRewardedTalentCount(talents); InitTalentForLevel(); } @@ -16593,13 +16581,13 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) //QueryResult* result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, " // 12 13 14 15 16 17 18 19 20 21 22 23 24 //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, " - // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 - //"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " - // 39 40 41 42 43 44 45 46 + // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 + //"resettalents_time, talentTree, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " + // 40 41 42 43 44 45 46 47 //"conquestPoints, totalHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " - // 47 48 49 50 51 52 53 54 55 56 57 + // 48 49 50 51 52 53 54 55 56 57 58 //"health, power1, power2, power3, power4, power5, instance_id, speccount, activespec, exploredZones, equipmentCache, " - // 58 59 60 61 + // 59 60 61 62 //"knownTitles, actionBars, grantableLevels, guildId FROM characters WHERE guid = '%u'", guid); PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADFROM); @@ -16665,8 +16653,8 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); - _LoadIntoDataField(fields[56].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); - _LoadIntoDataField(fields[58].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); + _LoadIntoDataField(fields[57].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); + _LoadIntoDataField(fields[59].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); @@ -16682,12 +16670,12 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32()); SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32()); - SetUInt32Value(PLAYER_BYTES_3, (fields[46].GetUInt16() & 0xFFFE) | fields[5].GetUInt8()); + SetUInt32Value(PLAYER_BYTES_3, (fields[47].GetUInt16() & 0xFFFE) | fields[5].GetUInt8()); SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32()); - SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[45].GetUInt32()); + SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[46].GetUInt32()); // set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise) - SetByteValue(PLAYER_FIELD_BYTES, 2, fields[59].GetUInt8()); + SetByteValue(PLAYER_FIELD_BYTES, 2, fields[60].GetUInt8()); InitDisplayIds(); @@ -16715,21 +16703,21 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) InitPrimaryProfessions(); // to max set before any spell loaded // init saved position, and fix it later if problematic - uint32 transGUID = uint32(fields[30].GetUInt64()); // field type is uint64 but lowguid is saved + uint32 transGUID = uint32(fields[31].GetUInt64()); // field type is uint64 but lowguid is saved Relocate(fields[12].GetFloat(), fields[13].GetFloat(), fields[14].GetFloat(), fields[16].GetFloat()); uint32 mapId = fields[15].GetUInt16(); - uint32 instanceId = fields[53].GetUInt8(); + uint32 instanceId = fields[54].GetUInt8(); - uint32 dungeonDiff = fields[38].GetUInt32() & 0x0F; + uint32 dungeonDiff = fields[39].GetUInt32() & 0x0F; if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY) dungeonDiff = DUNGEON_DIFFICULTY_NORMAL; - uint32 raidDiff = (fields[38].GetUInt8() >> 4) & 0x0F; + uint32 raidDiff = (fields[39].GetUInt8() >> 4) & 0x0F; if (raidDiff >= MAX_RAID_DIFFICULTY) raidDiff = RAID_DIFFICULTY_10MAN_NORMAL; SetDungeonDifficulty(Difficulty(dungeonDiff)); // may be changed in _LoadGroup SetRaidDifficulty(Difficulty(raidDiff)); // may be changed in _LoadGroup - std::string taxi_nodes = fields[37].GetString(); + std::string taxi_nodes = fields[38].GetString(); #define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); } @@ -16753,11 +16741,11 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0); } - SetCurrency(CURRENCY_TYPE_CONQUEST_POINTS, fields[39].GetUInt32()); - SetCurrency(CURRENCY_TYPE_HONOR_POINTS, fields[40].GetUInt32()); - SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, fields[41].GetUInt32()); - SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[42].GetUInt16()); - SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[43].GetUInt16()); + SetCurrency(CURRENCY_TYPE_CONQUEST_POINTS, fields[40].GetUInt32()); + SetCurrency(CURRENCY_TYPE_HONOR_POINTS, fields[41].GetUInt32()); + SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, fields[42].GetUInt32()); + SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[43].GetUInt16()); + SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[44].GetUInt16()); _LoadBoundInstances(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES)); _LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES)); @@ -16818,7 +16806,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) else if (transGUID) { m_movementInfo.t_guid = MAKE_NEW_GUID(transGUID, 0, HIGHGUID_MO_TRANSPORT); - m_movementInfo.t_pos.Relocate(fields[26].GetFloat(), fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat()); + m_movementInfo.t_pos.Relocate(fields[27].GetFloat(), fields[28].GetFloat(), fields[29].GetFloat(), fields[30].GetFloat()); if (!Trinity::IsValidMapCoord( GetPositionX()+m_movementInfo.t_pos.m_positionX, GetPositionY()+m_movementInfo.t_pos.m_positionY, @@ -16996,28 +16984,28 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) m_Played_time[PLAYED_TIME_TOTAL]= fields[19].GetUInt32(); m_Played_time[PLAYED_TIME_LEVEL]= fields[20].GetUInt32(); - m_resetTalentsCost = fields[24].GetUInt32(); - m_resetTalentsTime = time_t(fields[25].GetUInt32()); + SetTalentResetCost(fields[24].GetUInt32()); + SetTalentResetTime(time_t(fields[25].GetUInt32())); m_taxi.LoadTaxiMask(fields[17].GetCString()); // must be before InitTaxiNodesForLevel - uint32 extraflags = fields[31].GetUInt16(); + uint32 extraflags = fields[32].GetUInt16(); - m_stableSlots = fields[32].GetUInt8(); + m_stableSlots = fields[33].GetUInt8(); if (m_stableSlots > MAX_PET_STABLES) { sLog->outError("Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots)); m_stableSlots = MAX_PET_STABLES; } - m_atLoginFlags = fields[33].GetUInt16(); + m_atLoginFlags = fields[34].GetUInt16(); // Honor system // Update Honor kills data m_lastHonorUpdateTime = logoutTime; UpdateHonorFields(); - m_deathExpireTime = time_t(fields[36].GetUInt32()); + m_deathExpireTime = time_t(fields[37].GetUInt32()); if (m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP) m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1; @@ -17077,14 +17065,14 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) //mails are loaded only when needed ;-) - when player in game click on mailbox. //_LoadMail(); - m_specsCount = fields[54].GetUInt8(); - m_activeSpec = fields[55].GetUInt8(); + SetSpecsCount(fields[55].GetUInt8()); + SetActiveSpec(fields[56].GetUInt8()); // sanity check - if (m_specsCount > MAX_TALENT_SPECS || m_activeSpec > MAX_TALENT_SPEC || m_specsCount < MIN_TALENT_SPECS) + if (GetSpecsCount() > MAX_TALENT_SPECS || GetActiveSpec() > MAX_TALENT_SPEC || GetSpecsCount() < MIN_TALENT_SPECS) { - m_activeSpec = 0; - sLog->outError("Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName(), GetGUIDLow(), m_specsCount, m_activeSpec); + SetActiveSpec(0); + sLog->outError("Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName(), GetGUIDLow(), GetSpecsCount(), GetActiveSpec()); } _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADTALENTS)); @@ -17126,7 +17114,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded - uint32 curTitle = fields[44].GetUInt32(); + uint32 curTitle = fields[45].GetUInt32(); if (curTitle && !HasTitle(curTitle)) curTitle = 0; @@ -17149,14 +17137,21 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) UpdateAllStats(); // restore remembered power/health values (but not more max values) - uint32 savedHealth = fields[47].GetUInt32(); + uint32 savedHealth = fields[48].GetUInt32(); SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth); for (uint8 i = 0; i < MAX_STORED_POWERS; ++i) { - uint32 savedPower = fields[48+i].GetUInt32(); + uint32 savedPower = fields[49+i].GetUInt32(); SetPower(Powers(i), savedPower > GetMaxPower(Powers(i)) ? GetMaxPower(Powers(i)) : savedPower); } + // must be after loading spells and talents + uint32 talentTree = uint32(fields[26].GetUInt16()); + if (sTalentTabStore.LookupEntry(talentTree)) + SetPrimaryTalentTree(talentTree); + else + SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); // invalid tree, reset talents + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "The value of player %s after load item and aura is: ", m_name.c_str()); outDebugValues(); @@ -17209,14 +17204,14 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) } // RaF stuff. - m_grantableLevels = fields[60].GetUInt32(); + m_grantableLevels = fields[61].GetUInt32(); if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0)) SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND); if (m_grantableLevels > 0) SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01); - m_guildId = fields[61].GetUInt32(); + m_guildId = fields[62].GetUInt32(); _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES)); @@ -17380,7 +17375,7 @@ void Player::_LoadGlyphAuras() { for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) { - if (uint32 glyph = GetGlyph(i)) + if (uint32 glyph = GetGlyph(GetActiveSpec(), i)) { if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph)) { @@ -17886,8 +17881,8 @@ void Player::_LoadQuestStatusRewarded(PreparedQueryResult result) SetTitle(titleEntry); } - if (quest->GetBonusTalents()) - m_questRewardTalentCount += quest->GetBonusTalents(); + if (uint32 talents = quest->GetBonusTalents()) + AddQuestRewardedTalentCount(talents); } m_RewardedQuests.insert(quest_id); @@ -18531,8 +18526,9 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt8(index++, (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0)); //save, far from tavern/city //save, but in tavern/city - stmt->setUInt32(index++, m_resetTalentsCost); - stmt->setUInt32(index++, m_resetTalentsTime); + stmt->setUInt32(index++, GetTalentResetCost()); + stmt->setUInt32(index++, GetTalentResetTime()); + stmt->setUInt16(index++, uint16(GetPrimaryTalentTree())); stmt->setUInt16(index++, (uint16)m_ExtraFlags); stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); @@ -18558,8 +18554,8 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetSession()->GetLatency()); - stmt->setUInt8(index++, m_specsCount); - stmt->setUInt8(index++, m_activeSpec); + stmt->setUInt8(index++, GetSpecsCount()); + stmt->setUInt8(index++, GetActiveSpec()); ss.str(""); for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i) @@ -18637,8 +18633,9 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt8(index++, (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0)); //save, far from tavern/city //save, but in tavern/city - stmt->setUInt32(index++, m_resetTalentsCost); - stmt->setUInt32(index++, m_resetTalentsTime); + stmt->setUInt32(index++, GetTalentResetCost()); + stmt->setUInt32(index++, GetTalentResetTime()); + stmt->setUInt16(index++, uint16(GetPrimaryTalentTree())); stmt->setUInt16(index++, (uint16)m_ExtraFlags); stmt->setUInt8(index++, m_stableSlots); stmt->setUInt16(index++, (uint16)m_atLoginFlags); @@ -18664,8 +18661,8 @@ void Player::SaveToDB(bool create /*=false*/) stmt->setUInt32(index++, GetSession()->GetLatency()); - stmt->setUInt8(index++, m_specsCount); - stmt->setUInt8(index++, m_activeSpec); + stmt->setUInt8(index++, GetSpecsCount()); + stmt->setUInt8(index++, GetActiveSpec()); ss.str(""); for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i) @@ -18760,18 +18757,18 @@ void Player::_SaveActions(SQLTransaction& trans) { case ACTIONBUTTON_NEW: trans->PAppend("INSERT INTO character_action (guid, spec, button, action, type) VALUES ('%u', '%u', '%u', '%u', '%u')", - GetGUIDLow(), m_activeSpec, (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType()); + GetGUIDLow(), GetActiveSpec(), (uint32)itr->first, (uint32)itr->second.GetAction(), (uint32)itr->second.GetType()); itr->second.uState = ACTIONBUTTON_UNCHANGED; ++itr; break; case ACTIONBUTTON_CHANGED: trans->PAppend("UPDATE character_action SET action = '%u', type = '%u' WHERE guid = '%u' AND button = '%u' AND spec = '%u'", - (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first, m_activeSpec); + (uint32)itr->second.GetAction(), (uint32)itr->second.GetType(), GetGUIDLow(), (uint32)itr->first, GetActiveSpec()); itr->second.uState = ACTIONBUTTON_UNCHANGED; ++itr; break; case ACTIONBUTTON_DELETED: - trans->PAppend("DELETE FROM character_action WHERE guid = '%u' and button = '%u' and spec = '%u'", GetGUIDLow(), (uint32)itr->first, m_activeSpec); + trans->PAppend("DELETE FROM character_action WHERE guid = '%u' and button = '%u' and spec = '%u'", GetGUIDLow(), (uint32)itr->first, GetActiveSpec()); m_actionButtons.erase(itr++); break; default: @@ -23229,27 +23226,22 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n void Player::InitGlyphsForLevel() { - for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i) + uint32 slot = 0; + for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows() && slot < MAX_GLYPH_SLOT_INDEX; ++i) if (GlyphSlotEntry const* gs = sGlyphSlotStore.LookupEntry(i)) - if (gs->Order) - SetGlyphSlot(gs->Order - 1, gs->Id); + SetGlyphSlot(slot++, gs->Id); uint8 level = getLevel(); - uint32 value = 0; + uint32 slotMask = 0; - // 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level - if (level >= 15) - value |= (0x01 | 0x02); - if (level >= 30) - value |= 0x08; + if (level >= 25) + slotMask |= 0x01 | 0x02 | 0x40; if (level >= 50) - value |= 0x04; - if (level >= 70) - value |= 0x10; - if (level >= 80) - value |= 0x20; + slotMask |= 0x04 | 0x08 | 0x80; + if (level >= 75) + slotMask |= 0x10 | 0x20 | 0x100; - SetUInt32Value(PLAYER_GLYPHS_ENABLED, value); + SetUInt32Value(PLAYER_GLYPHS_ENABLED, slotMask); } bool Player::isTotalImmune() @@ -23573,16 +23565,30 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) uint32 Player::CalculateTalentsPoints() const { - uint32 base_talent = getLevel() < 10 ? 0 : getLevel()-9; + // this dbc file has entries only up to level 100 + NumTalentsAtLevelEntry const* count = sNumTalentsAtLevelStore.LookupEntry(std::min<uint32>(getLevel(), 100)); + if (!count) + return 0; + + float baseForLevel = count->Talents; if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609) - return uint32(base_talent * sWorld->getRate(RATE_TALENT)); + return uint32(baseForLevel * sWorld->getRate(RATE_TALENT)); + + // Death Knight starting level + // hardcoded here - number of quest awarded talents is equal to number of talents any other class would have at level 55 + if (getLevel() < 55) + return 0; + + NumTalentsAtLevelEntry const* dkBase = sNumTalentsAtLevelStore.LookupEntry(55); + if (!dkBase) + return 0; - uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55; - talentPointsForLevel += m_questRewardTalentCount; + float talentPointsForLevel = count->Talents - dkBase->Talents; + talentPointsForLevel += float(GetQuestRewardedTalentCount()); - if (talentPointsForLevel > base_talent) - talentPointsForLevel = base_talent; + if (talentPointsForLevel > baseForLevel) + talentPointsForLevel = baseForLevel; return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT)); } @@ -23851,29 +23857,29 @@ void Player::CompletedAchievement(AchievementEntry const* entry) GetAchievementMgr().CompletedAchievement(entry); } -void Player::LearnTalent(uint32 talentId, uint32 talentRank) +bool Player::LearnTalent(uint32 talentId, uint32 talentRank) { uint32 CurTalentPoints = GetFreeTalentPoints(); if (CurTalentPoints == 0) - return; + return false; if (talentRank >= MAX_TALENT_RANK) - return; + return false; TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); if (!talentInfo) - return; + return false; TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); if (!talentTabInfo) - return; + return false; // prevent learn talent for different class (cheating) if ((getClassMask() & talentTabInfo->ClassMask) == 0) - return; + return false; // find current max talent rank (0~5) uint8 curtalent_maxrank = 0; // 0 = not learned any rank @@ -23888,11 +23894,11 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank) // we already have same or higher talent rank learned if (curtalent_maxrank >= (talentRank + 1)) - return; + return false; // check if we have enough talent points if (CurTalentPoints < (talentRank - curtalent_maxrank + 1)) - return; + return false; // Check if it requires another talent if (talentInfo->DependsOn > 0) @@ -23907,33 +23913,32 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank) hasEnoughRank = true; } if (!hasEnoughRank) - return; + return false; } } // Find out how many points we have in this field uint32 spentPoints = 0; - + uint32 primaryTreeTalents = 0; uint32 tTab = talentInfo->TalentTab; - if (talentInfo->Row > 0) + bool isMainTree = GetPrimaryTalentTree() == tTab || !GetPrimaryTalentTree(); + + if (talentInfo->Row > 0 || !isMainTree) { - uint32 numRows = sTalentStore.GetNumRows(); - for (uint32 i = 0; i < numRows; i++) // Loop through all talents. + for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) // Loop through all talents. { - // Someday, someone needs to revamp - const TalentEntry* tmpTalent = sTalentStore.LookupEntry(i); - if (tmpTalent) // the way talents are tracked + if (TalentEntry const* tmpTalent = sTalentStore.LookupEntry(i)) // Someday, someone needs to revamp the way talents are tracked { - if (tmpTalent->TalentTab == tTab) + for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++) { - for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++) + if (tmpTalent->RankID[rank] != 0) { - if (tmpTalent->RankID[rank] != 0) + if (HasSpell(tmpTalent->RankID[rank])) { - if (HasSpell(tmpTalent->RankID[rank])) - { + if (tmpTalent->TalentTab == tTab) spentPoints += (rank + 1); - } + if (tmpTalent->TalentTab == GetPrimaryTalentTree()) + primaryTreeTalents += (rank + 1); } } } @@ -23943,28 +23948,43 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank) // not have required min points spent in talent tree if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK)) - return; + return false; + + // player has not spent 31 talents in main tree before attempting to learn other tree's talents + if (!isMainTree && primaryTreeTalents < REQ_PRIMARY_TREE_TALENTS) + return false; // spell not set in talent.dbc uint32 spellid = talentInfo->RankID[talentRank]; if (spellid == 0) { sLog->outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); - return; + return false; } // already known if (HasSpell(spellid)) - return; + return false; // learn! (other talent ranks will unlearned at learning) learnSpell(spellid, false); - AddTalent(spellid, m_activeSpec, true); + AddTalent(spellid, GetActiveSpec(), true); + + sLog->outDetail("TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, GetActiveSpec()); - sLog->outDetail("TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec); + // set talent tree for player + if (!GetPrimaryTalentTree()) + { + SetPrimaryTalentTree(talentInfo->TalentTab); + std::vector<uint32> const* specSpells = GetTalentTreePrimarySpells(talentInfo->TalentTab); + if (specSpells) + for (size_t i = 0; i < specSpells->size(); ++i) + learnSpell(specSpells->at(i), false); + } // update free talent points SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1)); + return true; } void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank) @@ -24167,23 +24187,26 @@ bool Player::canSeeSpellClickOn(Creature const* c) const void Player::BuildPlayerTalentsInfoData(WorldPacket* data) { *data << uint32(GetFreeTalentPoints()); // unspentTalentPoints - *data << uint8(m_specsCount); // talent group count (0, 1 or 2) - *data << uint8(m_activeSpec); // talent group index (0 or 1) + *data << uint8(GetSpecsCount()); // talent group count (0, 1 or 2) + *data << uint8(GetActiveSpec()); // talent group index (0 or 1) - if (m_specsCount) + if (GetSpecsCount()) { - if (m_specsCount > MAX_TALENT_SPECS) - m_specsCount = MAX_TALENT_SPECS; + if (GetSpecsCount() > MAX_TALENT_SPECS) + SetSpecsCount(MAX_TALENT_SPECS); // loop through all specs (only 1 for now) - for (uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx) + for (uint32 specIdx = 0; specIdx < GetSpecsCount(); ++specIdx) { + size_t specPos = data->wpos(); + *data << uint32(0); uint8 talentIdCount = 0; size_t pos = data->wpos(); *data << uint8(talentIdCount); // [PH], talentIdCount // find class talent tabs (all players have 3 talent tabs) uint32 const* talentTabIds = GetTalentTabPages(getClass()); + uint32 talentCounts[3] = { 0, 0, 0 }; for (uint8 i = 0; i < MAX_TALENT_TABS; ++i) { @@ -24217,16 +24240,29 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket* data) *data << uint32(talentInfo->TalentID); // Talent.dbc *data << uint8(curtalent_maxrank); // talentMaxRank (0-4) + talentCounts[i] += curtalent_maxrank + 1; ++talentIdCount; } } + if (talentIdCount) + { + uint32 maxTalentsSpec = 0; + if (talentCounts[1] > talentCounts[maxTalentsSpec]) + maxTalentsSpec = 1; + + if (talentCounts[2] > talentCounts[maxTalentsSpec]) + maxTalentsSpec = 2; + + data->put<uint32>(specPos, talentTabIds[maxTalentsSpec]); + } + data->put<uint8>(pos, talentIdCount); // put real count *data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) - *data << uint16(m_Glyphs[specIdx][i]); // GlyphProperties.dbc + *data << uint16(GetGlyph(specIdx, i)); // GlyphProperties.dbc } } } @@ -24558,15 +24594,11 @@ void Player::_LoadGlyphs(PreparedQueryResult result) Field* fields = result->Fetch(); uint8 spec = fields[0].GetUInt8(); - if (spec >= m_specsCount) + if (spec >= GetSpecsCount()) continue; - m_Glyphs[spec][0] = fields[1].GetUInt16(); - m_Glyphs[spec][1] = fields[2].GetUInt16(); - m_Glyphs[spec][2] = fields[3].GetUInt16(); - m_Glyphs[spec][3] = fields[4].GetUInt16(); - m_Glyphs[spec][4] = fields[5].GetUInt16(); - m_Glyphs[spec][5] = fields[6].GetUInt16(); + for (uint8 i = 0; i < 6; ++i) + _talentMgr->SpecInfo[spec].Glyphs[i] = fields[i + 1].GetUInt16(); } while (result->NextRow()); } @@ -24574,10 +24606,10 @@ void Player::_LoadGlyphs(PreparedQueryResult result) void Player::_SaveGlyphs(SQLTransaction& trans) { trans->PAppend("DELETE FROM character_glyphs WHERE guid='%u'", GetGUIDLow()); - for (uint8 spec = 0; spec < m_specsCount; ++spec) + for (uint8 spec = 0; spec < GetSpecsCount(); ++spec) { trans->PAppend("INSERT INTO character_glyphs VALUES('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", - GetGUIDLow(), spec, m_Glyphs[spec][0], m_Glyphs[spec][1], m_Glyphs[spec][2], m_Glyphs[spec][3], m_Glyphs[spec][4], m_Glyphs[spec][5]); + GetGUIDLow(), spec, GetGlyph(spec, 0), GetGlyph(spec, 1), GetGlyph(spec, 2), GetGlyph(spec, 3), GetGlyph(spec, 4), GetGlyph(spec, 5)); } } @@ -24596,7 +24628,7 @@ void Player::_SaveTalents(SQLTransaction& trans) { for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) { - for (PlayerTalentMap::iterator itr = m_talents[i]->begin(); itr != m_talents[i]->end();) + for (PlayerTalentMap::iterator itr = GetTalentMap(i)->begin(); itr != GetTalentMap(i)->end();) { if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED) trans->PAppend("DELETE FROM character_talent WHERE guid = '%u' and spell = '%u' and spec = '%u'", GetGUIDLow(), itr->first, itr->second->spec); @@ -24607,7 +24639,7 @@ void Player::_SaveTalents(SQLTransaction& trans) if (itr->second->state == PLAYERSPELL_REMOVED) { delete itr->second; - m_talents[i]->erase(itr++); + GetTalentMap(i)->erase(itr++); } else { @@ -24624,7 +24656,7 @@ void Player::UpdateSpecCount(uint8 count) if (curCount == count) return; - if (m_activeSpec >= count) + if (GetActiveSpec() >= count) ActivateSpec(0); SQLTransaction trans = CharacterDatabase.BeginTransaction(); @@ -24641,8 +24673,8 @@ void Player::UpdateSpecCount(uint8 count) else if (count < curCount) { _SaveActions(trans); - trans->PAppend("DELETE FROM character_action WHERE spec<>'%u' AND guid='%u'", m_activeSpec, GetGUIDLow()); - m_activeSpec = 0; + trans->PAppend("DELETE FROM character_action WHERE spec<>'%u' AND guid='%u'", GetActiveSpec(), GetGUIDLow()); + SetActiveSpec(0); } CharacterDatabase.CommitTransaction(trans); @@ -24723,7 +24755,7 @@ void Player::ActivateSpec(uint8 spec) // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) // remove secondary glyph - if (uint32 oldglyph = m_Glyphs[m_activeSpec][slot]) + if (uint32 oldglyph = GetGlyph(GetActiveSpec(), slot)) if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph)) RemoveAurasDueToSpell(old_gp->SpellId); @@ -24753,7 +24785,7 @@ void Player::ActivateSpec(uint8 spec) if (talentInfo->RankID[rank] == 0) continue; // if the talent can be found in the newly activated PlayerTalentMap - if (HasTalent(talentInfo->RankID[rank], m_activeSpec)) + if (HasTalent(talentInfo->RankID[rank], GetActiveSpec())) { learnSpell(talentInfo->RankID[rank], false); // add the talent to the PlayerSpellMap spentTalents += (rank + 1); // increment the spentTalents count @@ -24764,7 +24796,7 @@ void Player::ActivateSpec(uint8 spec) // set glyphs for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) { - uint32 glyph = m_Glyphs[m_activeSpec][slot]; + uint32 glyph = GetGlyph(GetActiveSpec(), slot); // apply primary glyph if (glyph) @@ -24774,13 +24806,13 @@ void Player::ActivateSpec(uint8 spec) SetGlyph(slot, glyph); } - m_usedTalentCount = spentTalents; + SetUsedTalentCount(spentTalents); InitTalentForLevel(); { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC); stmt->setUInt32(0, GetGUIDLow()); - stmt->setUInt8(1, m_activeSpec); + stmt->setUInt8(1, GetActiveSpec()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) _LoadActions(result); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index a594f2c02ef..73c8c010e76 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1086,6 +1086,49 @@ private: uint32 _xp; }; +struct PlayerTalentInfo +{ + PlayerTalentInfo() : + FreeTalentPoints(0), UsedTalentCount(0), QuestRewardedTalentCount(0), + ResetTalentsCost(0), ResetTalentsTime(0), TalentTree(0), + ActiveSpec(0), SpecsCount(1) + { + for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) + { + memset(SpecInfo[i].Glyphs, 0, MAX_GLYPH_SLOT_INDEX * sizeof(uint32)); + SpecInfo[i].Talents = new PlayerTalentMap(); + } + } + + ~PlayerTalentInfo() + { + for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) + { + for (PlayerTalentMap::const_iterator itr = SpecInfo[i].Talents->begin(); itr != SpecInfo[i].Talents->end(); ++itr) + delete itr->second; + delete SpecInfo[i].Talents; + } + } + + struct TalentSpecInfo + { + PlayerTalentMap* Talents; + uint32 Glyphs[MAX_GLYPH_SLOT_INDEX]; + } SpecInfo[MAX_TALENT_SPECS]; + + uint32 FreeTalentPoints; + uint32 UsedTalentCount; + uint32 QuestRewardedTalentCount; + uint32 ResetTalentsCost; + time_t ResetTalentsTime; + uint32 TalentTree; + uint8 ActiveSpec; + uint8 SpecsCount; + +private: + PlayerTalentInfo(PlayerTalentInfo const&); +}; + class Player : public Unit, public GridObject<Player> { friend class WorldSession; @@ -1683,39 +1726,54 @@ class Player : public Unit, public GridObject<Player> void SetReputation(uint32 factionentry, uint32 value); uint32 GetReputation(uint32 factionentry); std::string GetGuildName(); - uint32 GetFreeTalentPoints() const { return m_freeTalentPoints; } - void SetFreeTalentPoints(uint32 points) { m_freeTalentPoints = points; } - bool resetTalents(bool no_cost = false); - uint32 resetTalentsCost() const; + + // Talents + uint32 GetFreeTalentPoints() const { return _talentMgr->FreeTalentPoints; } + void SetFreeTalentPoints(uint32 points) { _talentMgr->FreeTalentPoints = points; } + uint32 GetUsedTalentCount() const { return _talentMgr->UsedTalentCount; } + void SetUsedTalentCount(uint32 talents) { _talentMgr->UsedTalentCount = talents; } + uint32 GetQuestRewardedTalentCount() const { return _talentMgr->QuestRewardedTalentCount; } + void AddQuestRewardedTalentCount(uint32 points) { _talentMgr->QuestRewardedTalentCount += points; } + uint32 GetTalentResetCost() const { return _talentMgr->ResetTalentsCost; } + void SetTalentResetCost(uint32 cost) { _talentMgr->ResetTalentsCost = cost; } + uint32 GetTalentResetTime() const { return _talentMgr->ResetTalentsTime; } + void SetTalentResetTime(time_t time_) { _talentMgr->ResetTalentsTime = time_; } + uint32 GetPrimaryTalentTree() const { return _talentMgr->TalentTree; } + void SetPrimaryTalentTree(uint32 tree) { _talentMgr->TalentTree = tree; } + uint8 GetActiveSpec() const { return _talentMgr->ActiveSpec; } + void SetActiveSpec(uint8 spec){ _talentMgr->ActiveSpec = spec; } + uint8 GetSpecsCount() const { return _talentMgr->SpecsCount; } + void SetSpecsCount(uint8 count) { _talentMgr->SpecsCount = count; } + + bool ResetTalents(bool no_cost = false); + uint32 GetNextResetTalentsCost() const; void InitTalentForLevel(); void BuildPlayerTalentsInfoData(WorldPacket* data); void BuildPetTalentsInfoData(WorldPacket* data); void SendTalentsInfoData(bool pet); - void LearnTalent(uint32 talentId, uint32 talentRank); + bool LearnTalent(uint32 talentId, uint32 talentRank); void LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank); - bool AddTalent(uint32 spellId, uint8 spec, bool learning); bool HasTalent(uint32 spell_id, uint8 spec) const; - uint32 CalculateTalentsPoints() const; // Dual Spec void UpdateSpecCount(uint8 count); - uint32 GetActiveSpec() { return m_activeSpec; } - void SetActiveSpec(uint8 spec){ m_activeSpec = spec; } - uint8 GetSpecsCount() { return m_specsCount; } - void SetSpecsCount(uint8 count) { m_specsCount = count; } void ActivateSpec(uint8 spec); void InitGlyphsForLevel(); void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); } - uint32 GetGlyphSlot(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); } + uint32 GetGlyphSlot(uint8 slot) const { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); } void SetGlyph(uint8 slot, uint32 glyph) { - m_Glyphs[m_activeSpec][slot] = glyph; + _talentMgr->SpecInfo[GetActiveSpec()].Glyphs[slot] = glyph; SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph); } - uint32 GetGlyph(uint8 slot) { return m_Glyphs[m_activeSpec][slot]; } + uint32 GetGlyph(uint8 spec, uint8 slot) const { return _talentMgr->SpecInfo[spec].Glyphs[slot]; } + + PlayerTalentMap const* GetTalentMap(uint8 spec) const { return _talentMgr->SpecInfo[spec].Talents; } + PlayerTalentMap* GetTalentMap(uint8 spec) { return _talentMgr->SpecInfo[spec].Talents; } + ActionButtonList const& GetActionButtons() const { return m_actionButtons; } uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS); } void SetFreePrimaryProfessions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS, profs); } @@ -2698,17 +2756,11 @@ class Player : public Unit, public GridObject<Player> PlayerMails m_mail; PlayerSpellMap m_spells; - PlayerTalentMap* m_talents[MAX_TALENT_SPECS]; uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use GlobalCooldownMgr m_GlobalCooldownMgr; - uint8 m_activeSpec; - uint8 m_specsCount; - - uint32 m_freeTalentPoints; - - uint32 m_Glyphs[MAX_TALENT_SPECS][MAX_GLYPH_SLOT_INDEX]; + PlayerTalentInfo* _talentMgr; ActionButtonList m_actionButtons; @@ -2781,10 +2833,6 @@ class Player : public Unit, public GridObject<Player> float m_rest_bonus; RestType rest_type; ////////////////////Rest System///////////////////// - uint32 m_resetTalentsCost; - time_t m_resetTalentsTime; - uint32 m_usedTalentCount; - uint32 m_questRewardTalentCount; // Social PlayerSocial *m_social; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 8fac594520e..323f31c9bb4 100755 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -564,7 +564,8 @@ enum SpellAttr7 #define MAX_TALENT_SPEC 1 #define MIN_TALENT_SPECS 1 #define MAX_TALENT_SPECS 2 -#define MAX_GLYPH_SLOT_INDEX 6 +#define MAX_GLYPH_SLOT_INDEX 9 +#define REQ_PRIMARY_TREE_TALENTS 31 // Custom values enum SpellClickUserTypes @@ -842,7 +843,7 @@ enum SpellEffects SPELL_EFFECT_171 = 171, // Summons gamebject SPELL_EFFECT_172 = 172, // Aoe ressurection SPELL_EFFECT_173 = 173, // Guild tab unlocked (guild perk) - SPELL_EFFECT_174 = 174, // + SPELL_EFFECT_174 = 174, // SPELL_EFFECT_175 = 175, // Unused (4.2.2) SPELL_EFFECT_176 = 176, // Some kind of sanctuary effect (Vanish) SPELL_EFFECT_177 = 177, diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp index 6479c6b6386..62b9fe0b582 100644 --- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp @@ -1070,7 +1070,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) { - pCurrChar->resetTalents(true); + pCurrChar->ResetTalents(true); pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state SendNotification(LANG_RESET_TALENTS); } @@ -1428,7 +1428,7 @@ void WorldSession::HandleRemoveGlyph(WorldPacket & recv_data) return; } - if (uint32 glyph = _player->GetGlyph(slot)) + if (uint32 glyph = _player->GetGlyph(_player->GetActiveSpec(), slot)) { if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyph)) { diff --git a/src/server/game/Server/Protocol/Handlers/SkillHandler.cpp b/src/server/game/Server/Protocol/Handlers/SkillHandler.cpp index 96234762a15..09d8b42c0ab 100755 --- a/src/server/game/Server/Protocol/Handlers/SkillHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/SkillHandler.cpp @@ -31,15 +31,31 @@ void WorldSession::HandleLearnTalentOpcode(WorldPacket & recv_data) uint32 talentId, requestedRank; recv_data >> talentId >> requestedRank; - _player->LearnTalent(talentId, requestedRank); - _player->SendTalentsInfoData(false); + if (_player->LearnTalent(talentId, requestedRank)) + _player->SendTalentsInfoData(false); } void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) { sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LEARN_PREVIEW_TALENTS"); + int32 tabPage; uint32 talentsCount; + recvPacket >> tabPage; // talent tree + + // prevent cheating (selecting new tree with points already in another) + if (tabPage >= 0) // -1 if player already has specialization + { + if (TalentTabEntry const* talentTabEntry = sTalentTabStore.LookupEntry(_player->GetPrimaryTalentTree())) + { + if (talentTabEntry->tabpage != tabPage) + { + recvPacket.rfinish(); + return; + } + } + } + recvPacket >> talentsCount; uint32 talentId, talentRank; @@ -48,7 +64,11 @@ void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket) { recvPacket >> talentId >> talentRank; - _player->LearnTalent(talentId, talentRank); + if (!_player->LearnTalent(talentId, talentRank)) + { + recvPacket.rfinish(); + break; + } } _player->SendTalentsInfoData(false); @@ -71,7 +91,7 @@ void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket & recv_data) if (GetPlayer()->HasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - if (!_player->resetTalents()) + if (!_player->ResetTalents()) { WorldPacket data(MSG_TALENT_WIPE_CONFIRM, 8+4); //you have not any talent data << uint64(0); diff --git a/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp b/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp index e15fc8b3871..09ba46654ee 100755 --- a/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp @@ -405,6 +405,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) Spell* spell = new Spell(mover, spellInfo, TRIGGERED_NONE, 0, false); spell->m_cast_count = castCount; // set count of casts + spell->m_glyphIndex = glyphIndex; spell->prepare(&targets); } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index e1aaaf1dbef..3b202a159ec 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -654,7 +654,7 @@ void InitOpcodes() //DEFINE_OPCODE_HANDLER(SMSG_PERIODICAURALOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SPELLDAMAGESHIELD, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SPELLNONMELEEDAMAGELOG, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //DEFINE_OPCODE_HANDLER(CMSG_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnTalentOpcode ); + DEFINE_OPCODE_HANDLER(CMSG_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnTalentOpcode ); //DEFINE_OPCODE_HANDLER(SMSG_RESURRECT_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(CMSG_TOGGLE_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTogglePvP ); DEFINE_OPCODE_HANDLER(SMSG_ZONE_UNDER_ATTACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); @@ -1209,7 +1209,7 @@ void InitOpcodes() //DEFINE_OPCODE_HANDLER(CMSG_REQUEST_VEHICLE_PREV_SEAT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle); //DEFINE_OPCODE_HANDLER(CMSG_REQUEST_VEHICLE_NEXT_SEAT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle); //DEFINE_OPCODE_HANDLER(CMSG_REQUEST_VEHICLE_SWITCH_SEAT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle); - //DEFINE_OPCODE_HANDLER(CMSG_PET_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetLearnTalent ); + DEFINE_OPCODE_HANDLER(CMSG_PET_LEARN_TALENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetLearnTalent ); //DEFINE_OPCODE_HANDLER(CMSG_PET_UNLEARN_TALENTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //DEFINE_OPCODE_HANDLER(SMSG_SET_PHASE_SHIFT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); //DEFINE_OPCODE_HANDLER(SMSG_ALL_ACHIEVEMENT_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); @@ -1281,8 +1281,8 @@ void InitOpcodes() //DEFINE_OPCODE_HANDLER(SMSG_SET_PROJECTILE_POSITION, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_TALENTS_INFO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_TALENTS_INVOLUNTARILY_RESET, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - //DEFINE_OPCODE_HANDLER(CMSG_LEARN_PREVIEW_TALENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalents ); - //DEFINE_OPCODE_HANDLER(CMSG_LEARN_PREVIEW_TALENTS_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalentsPet ); + DEFINE_OPCODE_HANDLER(CMSG_LEARN_PREVIEW_TALENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalents ); + DEFINE_OPCODE_HANDLER(CMSG_LEARN_PREVIEW_TALENTS_PET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLearnPreviewTalentsPet ); //DEFINE_OPCODE_HANDLER(CMSG_SET_ACTIVE_TALENT_GROUP_OBSOLETE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //DEFINE_OPCODE_HANDLER(CMSG_GM_GRANT_ACHIEVEMENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //DEFINE_OPCODE_HANDLER(CMSG_GM_REMOVE_ACHIEVEMENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); @@ -1378,11 +1378,11 @@ void InitOpcodes() DEFINE_OPCODE_HANDLER(SMSG_VERIFY_CONNECTIVITY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(CMSG_VERIFY_CONNECTIVITY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess ); //DEFINE_OPCODE_HANDLER(CMSG_LOG_DISCONNECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess ); - DEFINE_OPCODE_HANDLER(CMSG_RETURN_TO_GRAVEYARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReturnToGraveyard ); + DEFINE_OPCODE_HANDLER(CMSG_RETURN_TO_GRAVEYARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReturnToGraveyard ); DEFINE_OPCODE_HANDLER(CMSG_RANDOMIZE_CHAR_NAME, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleRandomizeCharNameOpcode ); DEFINE_OPCODE_HANDLER(SMSG_RANDOMIZE_CHAR_NAME, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_PLAYER_MOVE, STATUS_AUTHED, PROCESS_THREADSAFE, &WorldSession::Handle_ServerSide ); //DEFINE_OPCODE_HANDLER(CMSG_BATTLEFIELD_REQUEST_SCORE_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); Need to send the response - + #undef DEFINE_OPCODE_HANDLER }; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 86bc68e631d..d932b536a78 100755 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -5710,12 +5710,16 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) switch (m_glyphIndex) { case 0: - case 1: minLevel = 15; break; - case 2: minLevel = 50; break; - case 3: minLevel = 30; break; - case 4: minLevel = 70; break; - case 5: minLevel = 80; break; + case 1: + case 6: minLevel = 25; break; + case 2: + case 3: + case 7: minLevel = 50; break; + case 4: + case 5: + case 8: minLevel = 75; break; } + if (minLevel && m_caster->getLevel() < minLevel) { SendCastResult(SPELL_FAILED_GLYPH_SOCKET_LOCKED); @@ -5737,7 +5741,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) } // remove old glyph - if (uint32 oldglyph = player->GetGlyph(m_glyphIndex)) + if (uint32 oldglyph = player->GetGlyph(player->GetActiveSpec(), m_glyphIndex)) { if (GlyphPropertiesEntry const* old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph)) { diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index fef3c9f9ccf..b0054aca5a5 100755 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -56,7 +56,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Start LoginQueryHolder content PREPARE_STATEMENT(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, " "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, " - "resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " + "resettalents_time, talentTree, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, " "conquestPoints, totalHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, " "health, power1, power2, power3, power4, power5, instance_id, speccount, activespec, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels, guildId FROM characters WHERE guid = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_ASYNC) @@ -333,15 +333,15 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, " "map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, " "taximask, cinematic, " - "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, " + "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, talentTree, " "extra_flags, stable_slots, at_login, zone, " "death_expire_time, taxi_path, conquestPoints, totalHonorPoints, totalKills, " "todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, " "power4, power5, latency, speccount, activespec, exploredZones, equipmentCache, knownTitles, actionBars, grantableLevels) VALUES " - "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,playerBytes=?,playerBytes2=?,playerFlags=?," "map=?,instance_id=?,instance_mode_mask=?,position_x=?,position_y=?,position_z=?,orientation=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?," - "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," + "logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,talentTree=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?," "conquestPoints=?,totalHonorPoints=?,totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?," "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,latency=?,speccount=?,activespec=?,exploredZones=?," "equipmentCache=?,knownTitles=?,actionBars=?,grantableLevels=?,online=? WHERE guid=?", CONNECTION_ASYNC); |