All pastes #1571623 Raw Copy code Copy link Edit

Unnamed

public diff v1 · immutable
#1571623 ·published 2009-09-19 08:37 UTC
rendered paste body
diff --git a/sql/characters.sql b/sql/characters.sqlindex 0858b64..d6dcb6e 100644--- a/sql/characters.sql+++ b/sql/characters.sql@@ -647,6 +647,28 @@ LOCK TABLES `character_reputation` WRITE; UNLOCK TABLES;  --+-- Table structure for table `character_skills`+--++DROP TABLE IF EXISTS `character_skills`;+CREATE TABLE `character_skills` (+  `guid` int(11) unsigned NOT NULL COMMENT 'Global Unique Identifier',+  `skill` mediumint(9) unsigned NOT NULL,+  `value` mediumint(9) unsigned NOT NULL,+  `max` mediumint(9) unsigned NOT NULL,+  PRIMARY KEY  (`guid`,`skill`)+) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Player System';++--+-- Dumping data for table `character_skills`+--++LOCK TABLES `character_skills` WRITE;+/*!40000 ALTER TABLE `character_skills` DISABLE KEYS */;+/*!40000 ALTER TABLE `character_skills` ENABLE KEYS */;+UNLOCK TABLES;++-- -- Table structure for table `character_social` -- diff --git a/sql/updates/9999_01_characters_character_skills.sql b/sql/updates/9999_01_characters_character_skills.sqlnew file mode 100644index 0000000..ce6dd5a--- /dev/null+++ b/sql/updates/9999_01_characters_character_skills.sql@@ -0,0 +1,48 @@+DROP TABLE IF EXISTS `character_skills`;+CREATE TABLE `character_skills` (+  `guid` int(11) unsigned NOT NULL COMMENT 'Global Unique Identifier',+  `skill` mediumint(9) unsigned NOT NULL,+  `value` int(11) unsigned NOT NULL,+  `max` mediumint(9) unsigned NOT NULL,+  i mediumint(9),+  PRIMARY KEY  (`guid`,`skill`,`i`)+) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Player System';+++DROP TABLE IF EXISTS temp_skills;+CREATE TABLE temp_skills (+  i int(11) unsigned NOT NULL,+  PRIMARY KEY (i)+);++INSERT INTO temp_skills VALUES+( 0),( 1),( 2),( 3),( 4),( 5),( 6),( 7),( 8),( 9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),+(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(36),(37),(38),(39),+(40),(41),(42),(43),(44),(45),(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),+(60),(61),(62),(63),(64),(65),(66),(67),(68),(69),(70),(71),(72),(73),(74),(75),(76),(77),(78),(79),+(80),(81),(82),(83),(84),(85),(86),(87),(88),(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),+(100),(101),(102),(103),(104),(105),(106),(107),(108),(109),(110),(111),(112),(113),(114),(115),(116),(117),(118),(119),+(120),(121),(122),(123),(124),(125),(126),(127);++INSERT INTO character_skills SELECT+guid,+((SUBSTRING(data, length(SUBSTRING_INDEX(data, ' ', 610+3*i))+2, length(SUBSTRING_INDEX(data, ' ', 610+3*i+1))- length(SUBSTRING_INDEX(data, ' ', 610+3*i)) - 1)) & 0xFFFF) as skill,+(SUBSTRING(data, length(SUBSTRING_INDEX(data, ' ', 610+3*i+1))+2, length(SUBSTRING_INDEX(data, ' ', 610+3*i+2))- length(SUBSTRING_INDEX(data, ' ', 610+3*i+1)) - 1)) as value,+(0) as max,+i+FROM characters, temp_skills;++DELETE FROM character_skills WHERE skill = 0;+DROP TABLE IF EXISTS temp_skills;++UPDATE character_skills+  SET max = ((value & 0xFFFF0000) >> 16);++UPDATE character_skills+  SET value = (value & 0xFFFF);++ALTER IGNORE TABLE character_skills+  CHANGE COLUMN value value mediumint(9) unsigned NOT NULL,+  DROP PRIMARY KEY,+  ADD  PRIMARY KEY (guid,skill),+  DROP COLUMN i;diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cppindex ce10794..c494157 100644--- a/src/game/CharacterHandler.cpp+++ b/src/game/CharacterHandler.cpp@@ -82,6 +82,7 @@ bool LoginQueryHolder::Initialize()     res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS,    "SELECT achievement, date FROM character_achievement WHERE guid = '%u'", GUID_LOPART(m_guid));     res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid));     res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS,   "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));+    res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS,          "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));      return res; }diff --git a/src/game/Player.cpp b/src/game/Player.cppindex 044739e..74fc8ae 100644--- a/src/game/Player.cpp+++ b/src/game/Player.cpp@@ -4995,15 +4995,12 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)     if(!skill_id)         return false; -    uint16 i=0;-    for (; i < PLAYER_MAX_SKILLS; ++i)-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill_id)-            break;--    if(i>=PLAYER_MAX_SKILLS)+    SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)         return false; -    uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));+    uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);+    uint32 data = GetUInt32Value(valueIndex);     uint32 value = SKILL_VALUE(data);     uint32 max = SKILL_MAX(data); @@ -5016,7 +5013,9 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)         if(new_value > max)             new_value = max; -        SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max));+        SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(new_value,max));+        if(itr->second.uState != SKILL_NEW)+            itr->second.uState = SKILL_CHANGED;         GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,skill_id);         return true;     }@@ -5126,13 +5125,13 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)         return false;     } -    uint16 i=0;-    for (; i < PLAYER_MAX_SKILLS; ++i)-        if ( SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_INDEX(i))) == SkillId ) break;-    if ( i >= PLAYER_MAX_SKILLS )+    SkillStatusMap::iterator itr = mSkillStatus.find(SkillId);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)         return false; -    uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));+    uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);++    uint32 data = GetUInt32Value(valueIndex);     uint16 SkillValue = SKILL_VALUE(data);     uint16 MaxValue   = SKILL_MAX(data); @@ -5147,7 +5146,9 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)         if(new_value > MaxValue)             new_value = MaxValue; -        SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,MaxValue));+        SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(new_value,MaxValue));+        if(itr->second.uState != SKILL_NEW)+            itr->second.uState = SKILL_CHANGED;         for(uint32* bsl = &bonusSkillLevels[0]; *bsl; ++bsl)         {             if((SkillValue < *bsl && new_value >= *bsl))@@ -5245,19 +5246,20 @@ void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool de  void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent) {-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skillid)-    {-        uint32 bonus_val = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));-        int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);-        int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);--        if(talent)                                          // permanent bonus stored in high part-            SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus,perm_bonus+val));-        else                                                // temporary/item bonus stored in low part-            SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus+val,perm_bonus));+    SkillStatusMap::const_iterator itr = mSkillStatus.find(skillid);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)         return;-    }++    uint32 bonusIndex = PLAYER_SKILL_BONUS_INDEX(itr->second.pos);++    uint32 bonus_val = GetUInt32Value(bonusIndex);+    int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);+    int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);++    if(talent)                                          // permanent bonus stored in high part+        SetUInt32Value(bonusIndex,MAKE_SKILL_BONUS(temp_bonus,perm_bonus+val));+    else                                                // temporary/item bonus stored in low part+        SetUInt32Value(bonusIndex,MAKE_SKILL_BONUS(temp_bonus+val,perm_bonus)); }  void Player::UpdateSkillsForLevel()@@ -5267,10 +5269,12 @@ void Player::UpdateSkillsForLevel()      bool alwaysMaxSkill = sWorld.getConfig(CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL); -    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-        if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))+    for(SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)     {-        uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;+        if(itr->second.uState == SKILL_DELETED)+            continue;++        uint32 pskill = itr->first;          SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill);         if(!pSkill)@@ -5279,37 +5283,52 @@ void Player::UpdateSkillsForLevel()         if(GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL)             continue; -        uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));+        uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);+        uint32 data = GetUInt32Value(valueIndex);         uint32 max = SKILL_MAX(data);         uint32 val = SKILL_VALUE(data);          /// update only level dependent max skill values         if(max!=1)         {-            /// miximize skill always+            /// maximize skill always             if(alwaysMaxSkill)-                SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(maxSkill,maxSkill));-            /// update max skill value if current max skill not maximized-            else if(max != maxconfskill)-                SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(val,maxSkill));+            {+                SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(maxSkill,maxSkill));+                if(itr->second.uState != SKILL_NEW)+                    itr->second.uState = SKILL_CHANGED;+            }+            else if(max != maxconfskill)                    /// update max skill value if current max skill not maximized+            {+                SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(val,maxSkill));+                if(itr->second.uState != SKILL_NEW)+                    itr->second.uState = SKILL_CHANGED;+            }         }     } }  void Player::UpdateSkillsToMaxSkillsForLevel() {-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-        if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))+    for(SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)     {-        uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;+        if(itr->second.uState == SKILL_DELETED)+            continue;++        uint32 pskill = itr->first;         if( IsProfessionOrRidingSkill(pskill))             continue;-        uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));+        uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);+        uint32 data = GetUInt32Value(valueIndex);          uint32 max = SKILL_MAX(data);          if(max > 1)-            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(max,max));+        {+            SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(max,max));+            if(itr->second.uState != SKILL_NEW)+                itr->second.uState = SKILL_CHANGED;+        }          if(pskill == SKILL_DEFENSE)             UpdateDefenseBonusesMod();@@ -5323,15 +5342,16 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)     if(!id)         return; -    uint16 i=0;-    for (; i < PLAYER_MAX_SKILLS; ++i)-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == id) break;+    SkillStatusMap::iterator itr = mSkillStatus.find(id); -    if(i<PLAYER_MAX_SKILLS)                                 //has skill+    //has skill+    if(itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED)     {         if(currVal)         {-            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));+            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos),MAKE_SKILL_VALUE(currVal,maxVal));+            if(itr->second.uState != SKILL_NEW)+                itr->second.uState = SKILL_CHANGED;             learnSkillRewardedSpells(id, currVal);             GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,id);             GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL,id);@@ -5339,9 +5359,15 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)         else                                                //remove         {             // clear skill fields-            SetUInt32Value(PLAYER_SKILL_INDEX(i),0);-            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),0);-            SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);+            SetUInt32Value(PLAYER_SKILL_INDEX(itr->second.pos),0);+            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos),0);+            SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos),0);++            // mark as deleted or simply remove from map if not saved yet+            if(itr->second.uState != SKILL_NEW)+                itr->second.uState = SKILL_DELETED;+            else+                mSkillStatus.erase(itr);              // remove all spells that related to this skill             for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)@@ -5352,7 +5378,7 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)     }     else if(currVal)                                        //add     {-        for (i=0; i < PLAYER_MAX_SKILLS; ++i)+        for (int i=0; i < PLAYER_MAX_SKILLS; ++i)             if (!GetUInt32Value(PLAYER_SKILL_INDEX(i)))         {             SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);@@ -5370,6 +5396,15 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)             GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,id);             GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL,id); +            // insert new entry or update if not deleted old entry yet+            if(itr != mSkillStatus.end())+            {+                itr->second.pos = i;+                itr->second.uState = SKILL_CHANGED;+            }+            else+                mSkillStatus.insert(SkillStatusMap::value_type(id, SkillStatusData(i, SKILL_NEW)));+             // apply skill bonuses             SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); @@ -5394,15 +5429,11 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)  bool Player::HasSkill(uint32 skill) const {-    if(!skill)return false;-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            return true;-        }-    }-    return false;+    if(!skill)+        return false;++    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    return (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED); }  uint16 Player::GetSkillValue(uint32 skill) const@@ -5410,78 +5441,71 @@ uint16 Player::GetSkillValue(uint32 skill) const     if(!skill)         return 0; -    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));+    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0; -            int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));-            result += SKILL_TEMP_BONUS(bonus);-            result += SKILL_PERM_BONUS(bonus);-            return result < 0 ? 0 : result;-        }-    }-    return 0;+    uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos));++    int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));+    result += SKILL_TEMP_BONUS(bonus);+    result += SKILL_PERM_BONUS(bonus);+    return result < 0 ? 0 : result; }  uint16 Player::GetMaxSkillValue(uint32 skill) const {-    if(!skill)return 0;-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));+    if(!skill)+        return 0; -            int32 result = int32(SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));-            result += SKILL_TEMP_BONUS(bonus);-            result += SKILL_PERM_BONUS(bonus);-            return result < 0 ? 0 : result;-        }-    }-    return 0;+    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0;++    uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos));++    int32 result = int32(SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));+    result += SKILL_TEMP_BONUS(bonus);+    result += SKILL_PERM_BONUS(bonus);+    return result < 0 ? 0 : result; }  uint16 Player::GetPureMaxSkillValue(uint32 skill) const {-    if(!skill)return 0;-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));-        }-    }-    return 0;+    if(!skill)+        return 0;++    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0;++    return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))); }  uint16 Player::GetBaseSkillValue(uint32 skill) const {-    if(!skill)return 0;-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));-            result +=  SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));-            return result < 0 ? 0 : result;-        }-    }-    return 0;+    if(!skill)+        return 0;++    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0;++    int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));+    result +=  SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));+    return result < 0 ? 0 : result; }  uint16 Player::GetPureSkillValue(uint32 skill) const {-    if(!skill)return 0;-    for (uint16 i=0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));-        }-    }-    return 0;+    if(!skill)+        return 0;++    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0;++    return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))); }  int16 Player::GetSkillPermBonusValue(uint32 skill) const@@ -5489,15 +5513,11 @@ int16 Player::GetSkillPermBonusValue(uint32 skill) const     if(!skill)         return 0; -    for (int i = 0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            return SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));-        }-    }+    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0; -    return 0;+    return SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos))); }  int16 Player::GetSkillTempBonusValue(uint32 skill) const@@ -5505,15 +5525,11 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const     if(!skill)         return 0; -    for (int i = 0; i < PLAYER_MAX_SKILLS; ++i)-    {-        if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)-        {-            return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));-        }-    }+    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);+    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)+        return 0; -    return 0;+    return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos))); }  void Player::SendInitialActionButtons() const@@ -14380,7 +14396,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )     SetUInt32Value(PLAYER_TRACK_CREATURES, 0 );     SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 ); -    _LoadSkills();+    _LoadSkills(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSKILLS));      // make sure the unit is considered out of combat for proper loading     ClearInCombat();@@ -15620,6 +15636,7 @@ void Player::SaveToDB()     _SaveSpellCooldowns();     _SaveActions();     _SaveAuras();+    _SaveSkills();     m_achievementMgr.SaveToDB();     m_reputationMgr.SaveToDB();     _SaveEquipmentSets();@@ -15884,6 +15901,43 @@ void Player::_SaveDailyQuestStatus()                 GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx),uint64(m_lastDailyQuestTime)); } ++void Player::_SaveSkills()+{+    // we don't need transactions here.+    for( SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); )+    {+        if(itr->second.uState == SKILL_UNCHANGED)+            continue;++        if(itr->second.uState == SKILL_DELETED)+        {+            CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), itr->first );+            mSkillStatus.erase(itr++);+            continue;+        }++        uint32 valueData = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos));+        uint16 value = SKILL_VALUE(valueData);+        uint16 max = SKILL_MAX(valueData);++        switch (itr->second.uState)+        {+            case SKILL_NEW:+                CharacterDatabase.PExecute("INSERT INTO character_skills (guid, skill, value, max) VALUES ('%u', '%u', '%u', '%u')",+                    GetGUIDLow(), itr->first, value, max);+                break;+            case SKILL_CHANGED:+                CharacterDatabase.PExecute("UPDATE character_skills SET value = '%u',max = '%u'WHERE guid = '%u' AND skill = '%u' ",+                    value, max, GetGUIDLow(), itr->first );+                break;+        };+        itr->second.uState = SKILL_UNCHANGED;++        ++itr;+    }+}+ void Player::_SaveSpells() {     for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)@@ -19584,43 +19638,78 @@ void Player::learnSpellHighRank(uint32 spellid)     spellmgr.doForHighRanks(spellid,worker); } -void Player::_LoadSkills()+void Player::_LoadSkills(QueryResult *result) {-    // Note: skill data itself loaded from `data` field. This is only cleanup part of load+    //                                                           0      1      2+    // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS,          "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid)); -    // reset skill modifiers and set correct unlearn flags-    for (uint32 i = 0; i < PLAYER_MAX_SKILLS; ++i)+    uint32 count = 0;+    if (result)     {-        SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);+        do+        {+            Field *fields = result->Fetch(); -        // set correct unlearn bit-        uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;-        if(!id) continue;+            uint16 skill    = fields[0].GetUInt16();+            uint16 value    = fields[1].GetUInt16();+            uint16 max      = fields[2].GetUInt16(); -        SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);-        if(!pSkill) continue;+            SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill);+            if(!pSkill)+            {+                sLog.outError("Character %u has skill %u that does not exist.", GetGUIDLow(), skill);+                continue;+            } -        // enable unlearn button for primary professions only-        if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)-            SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));-        else-            SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));+            // set fixed skill ranges+            switch(GetSkillRangeType(pSkill,false))+            {+                case SKILL_RANGE_LANGUAGE:                      // 300..300+                    value = max = 300;+                    break;+                case SKILL_RANGE_MONO:                          // 1..1, grey monolite bar+                    value = max = 1;+                    break;+                default:+                    break;+            } -        // set fixed skill ranges-        switch(GetSkillRangeType(pSkill,false))-        {-            case SKILL_RANGE_LANGUAGE:                      // 300..300-                SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(300,300));-                break;-            case SKILL_RANGE_MONO:                          // 1..1, grey monolite bar-                SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(1,1));-                break;-            default:+            if(value == 0)+            {+                sLog.outError("Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill);+                CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ", GetGUIDLow(), skill );+                continue;+            }++            // enable unlearn button for primary professions only+            if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)+                SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill,1));+            else+                SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill,0));++            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count),MAKE_SKILL_VALUE(value, max));+            SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count),0);++            mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED)));++            learnSkillRewardedSpells(skill, value);++            ++count;++            if(count >= PLAYER_MAX_SKILLS)                      // client limit+            {+                sLog.outError("Character %u has more than %u skills.", PLAYER_MAX_SKILLS);                 break;-        }+            }+        } while (result->NextRow());+        delete result;+    } -        uint32 vskill = SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));-        learnSkillRewardedSpells(id, vskill);+    for (; count < PLAYER_MAX_SKILLS; ++count)+    {+        SetUInt32Value(PLAYER_SKILL_INDEX(count), 0);+        SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count),0);+        SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count),0);     }      // special settingsdiff --git a/src/game/Player.h b/src/game/Player.hindex d31a575..97a9d22 100644--- a/src/game/Player.h+++ b/src/game/Player.h@@ -564,6 +564,25 @@ enum QuestSlotStateMask     QUEST_STATE_FAIL     = 0x0002 }; +enum SkillUpdateState+{+    SKILL_UNCHANGED     = 0,+    SKILL_CHANGED       = 1,+    SKILL_NEW           = 2,+    SKILL_DELETED       = 3+};++struct SkillStatusData+{+    SkillStatusData(uint32 _pos, SkillUpdateState _uState) : pos(_pos), uState(_uState)+    {+    }+    uint32 pos;+    SkillUpdateState uState;+};++typedef UNORDERED_MAP<uint32, SkillStatusData> SkillStatusMap;+ class Quest; class Spell; class Item;@@ -866,7 +885,8 @@ enum PlayerLoginQueryIndex     PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS         = 18,     PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS     = 19,     PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS        = 20,-    MAX_PLAYER_LOGIN_QUERY                      = 21+    PLAYER_LOGIN_QUERY_LOADSKILLS               = 21,+    MAX_PLAYER_LOGIN_QUERY                      = 22 };  enum PlayerDelayedOperations@@ -2242,7 +2262,7 @@ class MANGOS_DLL_SPEC Player : public Unit         void _LoadQuestStatus(QueryResult *result);         void _LoadDailyQuestStatus(QueryResult *result);         void _LoadGroup(QueryResult *result);-        void _LoadSkills();+        void _LoadSkills(QueryResult *result);         void _LoadSpells(QueryResult *result);         void _LoadFriendList(QueryResult *result);         bool _LoadHomeBind(QueryResult *result);@@ -2260,6 +2280,7 @@ class MANGOS_DLL_SPEC Player : public Unit         void _SaveMail();         void _SaveQuestStatus();         void _SaveDailyQuestStatus();+        void _SaveSkills();         void _SaveSpells();         void _SaveEquipmentSets(); @@ -2305,6 +2326,8 @@ class MANGOS_DLL_SPEC Player : public Unit          QuestStatusMap mQuestStatus; +        SkillStatusMap mSkillStatus;+         uint32 m_GuildIdInvited;         uint32 m_ArenaTeamIdInvited;