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);  | 
