diff options
| author | Kudlaty <none@none> | 2009-12-14 20:47:43 +0100 | 
|---|---|---|
| committer | Kudlaty <none@none> | 2009-12-14 20:47:43 +0100 | 
| commit | 379cbb72af7869950e04d59b9903ab0dd2618a68 (patch) | |
| tree | b2d2f724d74e2ed605f685b2c8317118d0fb8f9e | |
| parent | 45d74f8e05b0442a08713c12287f6becda34f892 (diff) | |
Mangos merge: [8874] Extract player skills from data blob. #526
Converted by Azazel.
--HG--
branch : trunk
| -rw-r--r-- | sql/characters.sql | 22 | ||||
| -rw-r--r-- | sql/updates/6509_characters_mangos_8874.sql | 50 | ||||
| -rw-r--r-- | src/game/CharacterHandler.cpp | 1 | ||||
| -rw-r--r-- | src/game/Player.cpp | 472 | ||||
| -rw-r--r-- | src/game/Player.h | 27 | ||||
| -rw-r--r-- | src/game/PlayerDump.cpp | 3 | 
6 files changed, 383 insertions, 192 deletions
diff --git a/sql/characters.sql b/sql/characters.sql index 80b164ba143..e138dafb6fe 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -820,6 +820,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/6509_characters_mangos_8874.sql b/sql/updates/6509_characters_mangos_8874.sql new file mode 100644 index 00000000000..20c373b0665 --- /dev/null +++ b/sql/updates/6509_characters_mangos_8874.sql @@ -0,0 +1,50 @@ +-- ALTER TABLE character_db_version CHANGE COLUMN required_8843_01_characters required_8874_01_characters_character_skills bit;
 +
 +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;
\ No newline at end of file diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 14a69e2b732..ca2561f4771 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -92,6 +92,7 @@ bool LoginQueryHolder::Initialize()      res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS,          "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid));      res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS,         "SELECT spell, spec FROM character_talent WHERE guid='%u'", GUID_LOPART(m_guid));      res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA,     "SELECT type, time, data FROM character_account_data WHERE guid='%u'", 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.cpp index 26443e9c379..e5613cbb703 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -4252,6 +4252,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC      CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid = '%u'",guid);      CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",guid);      CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'",guid); +    CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'",guid);      CharacterDatabase.CommitTransaction();      //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); @@ -5294,15 +5295,12 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)      if(skill_id == SKILL_FIST_WEAPONS)          skill_id = SKILL_UNARMED; -    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); @@ -5315,7 +5313,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;      } @@ -5424,13 +5424,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); @@ -5445,7 +5445,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)) @@ -5544,19 +5546,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); +    SkillStatusMap::const_iterator itr = mSkillStatus.find(skillid); +    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) +        return; -            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)); -            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() @@ -5566,53 +5569,67 @@ 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))) -        { -            uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; +    for(SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr) +    { +        if(itr->second.uState == SKILL_DELETED) +            continue; -            SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill); -            if (!pSkill) -                continue; +        uint32 pskill = itr->first; +        SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill); +        if (!pSkill) +            continue; -            if (GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL) -                continue; +        if (GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL) +            continue; -            uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)); -            uint32 max = SKILL_MAX(data); -            uint32 val = SKILL_VALUE(data); +        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) +        /// update only level dependent max skill values +        if (max != 1) +        { +            /// maximize skill always +            if (alwaysMaxSkill) +            { +                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              { -                /// miximize 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(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))) -        { -            uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; -            if (IsProfessionOrRidingSkill(pskill)) -                continue; -            uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)); - -            uint32 max = SKILL_MAX(data); +    for(SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr) +    { +        if(itr->second.uState == SKILL_DELETED) +            continue; -            if (max > 1) -                SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(max,max)); +        uint32 pskill = itr->first; +        if (IsProfessionOrRidingSkill(pskill)) +            continue; +        uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos); +        uint32 data = GetUInt32Value(valueIndex); +        uint32 max = SKILL_MAX(data); -            if (pskill == SKILL_DEFENSE) -                UpdateDefenseBonusesMod(); +        if (max > 1) +        { +            SetUInt32Value(valueIndex,MAKE_SKILL_VALUE(max,max)); +            if(itr->second.uState != SKILL_NEW) +                itr->second.uState = SKILL_CHANGED;          } +        if (pskill == SKILL_DEFENSE) +            UpdateDefenseBonusesMod(); +    }  }  // This functions sets a skill line value (and adds if doesn't exist yet) @@ -5622,16 +5639,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); @@ -5639,9 +5656,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) @@ -5652,56 +5675,62 @@ 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); +            if (!pSkill)              { -                SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id); -                if (!pSkill) -                { -                    sLog.outError("Skill not found in SkillLineStore: skill #%u", id); -                    return; -                } -                // 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)); -                SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal)); -                GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,id); -                GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL,id); - -                // apply skill bonuses -                SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0); - -                // temporary bonuses -                AuraEffectList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL); -                for (AuraEffectList::const_iterator j = mModSkill.begin(); j != mModSkill.end(); ++j) -                    if ((*j)->GetMiscValue() == int32(id)) -                        (*j)->ApplyModifier(true); - -                // permanent bonuses -                AuraEffectList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT); -                for (AuraEffectList::const_iterator j = mModSkillTalent.begin(); j != mModSkillTalent.end(); ++j) -                    if ((*j)->GetMiscValue() == int32(id)) -                        (*j)->ApplyModifier(true); - -                // Learn all spells for skill -                learnSkillRewardedSpells(id, currVal); +                sLog.outError("Skill not found in SkillLineStore: skill #%u", id);                  return;              } +            // 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)); +            SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,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); + +            // temporary bonuses +            AuraEffectList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL); +            for (AuraEffectList::const_iterator j = mModSkill.begin(); j != mModSkill.end(); ++j) +                if ((*j)->GetMiscValue() == int32(id)) +                    (*j)->ApplyModifier(true); + +            // permanent bonuses +            AuraEffectList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT); +            for (AuraEffectList::const_iterator j = mModSkillTalent.begin(); j != mModSkillTalent.end(); ++j) +                if ((*j)->GetMiscValue() == int32(id)) +                    (*j)->ApplyModifier(true); + +            // Learn all spells for skill +            learnSkillRewardedSpells(id, currVal); +            return;          } +    }  }  bool Player::HasSkill(uint32 skill) const  { -    if (!skill) +    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; +    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); +    return (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED);  }  uint16 Player::GetSkillValue(uint32 skill) const @@ -5709,19 +5738,16 @@ 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 @@ -5729,60 +5755,54 @@ 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)); +    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); +    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) +        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; +    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) +    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))); +    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); +    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) +        return 0; -    return 0; +    return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));  }  uint16 Player::GetBaseSkillValue(uint32 skill) const  { -    if (!skill) +    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; +    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) +    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))); +    SkillStatusMap::const_iterator itr = mSkillStatus.find(skill); +    if(itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) +        return 0; -    return 0; +    return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));  }  int16 Player::GetSkillPermBonusValue(uint32 skill) const @@ -5790,11 +5810,11 @@ int16 Player::GetSkillPermBonusValue(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_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 @@ -5802,11 +5822,11 @@ int16 Player::GetSkillTempBonusValue(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_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::SendActionButtons(uint32 state) const @@ -15145,7 +15165,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(); @@ -16445,6 +16465,7 @@ void Player::SaveToDB()      _SaveSpellCooldowns();      _SaveActions();      _SaveAuras(); +    _SaveSkills();      m_achievementMgr.SaveToDB();      m_reputationMgr.SaveToDB();      _SaveEquipmentSets(); @@ -16693,6 +16714,45 @@ 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) +        { +            ++itr; +            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();) @@ -21003,43 +21063,77 @@ void Player::learnSpellHighRank(uint32 spellid)          learnSpellHighRank(next);  } -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; +            } +            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; +            } -        // 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: +            // 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 settings diff --git a/src/game/Player.h b/src/game/Player.h index 56e3cb97b6c..162e2e29b00 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -573,6 +573,25 @@ enum QuestSlotStateMask      QUEST_STATE_FAIL     = 0x0002  }; +enum SkillUpdateState +{ +    SKILL_UNCHANGED     = 0, +    SKILL_CHANGED       = 1, +    SKILL_NEW           = 2, +    SKILL_DELETED       = 3 +}; + +struct SkillStatusData +{ +    SkillStatusData(uint8 _pos, SkillUpdateState _uState) : pos(_pos), uState(_uState) +    { +    } +    uint8 pos; +    SkillUpdateState uState; +}; + +typedef UNORDERED_MAP<uint32, SkillStatusData> SkillStatusMap; +  class Quest;  class Spell;  class Item; @@ -799,7 +818,8 @@ enum PlayerLoginQueryIndex      PLAYER_LOGIN_QUERY_LOADGLYPHS               = 22,      PLAYER_LOGIN_QUERY_LOADTALENTS              = 23,      PLAYER_LOGIN_QUERY_LOADACCOUNTDATA          = 24, -    MAX_PLAYER_LOGIN_QUERY                      = 25 +    PLAYER_LOGIN_QUERY_LOADSKILLS               = 25, +    MAX_PLAYER_LOGIN_QUERY                      = 26  };  enum PlayerDelayedOperations @@ -2271,7 +2291,7 @@ Spell * m_spellModTakingSpell;          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); @@ -2292,6 +2312,7 @@ Spell * m_spellModTakingSpell;          void _SaveMail();          void _SaveQuestStatus();          void _SaveDailyQuestStatus(); +        void _SaveSkills();          void _SaveSpells();          void _SaveEquipmentSets();          void _SaveBGData(); @@ -2340,6 +2361,8 @@ Spell * m_spellModTakingSpell;          QuestStatusMap mQuestStatus; +        SkillStatusMap mSkillStatus; +          uint32 m_GuildIdInvited;          uint32 m_ArenaTeamIdInvited; diff --git a/src/game/PlayerDump.cpp b/src/game/PlayerDump.cpp index 4a34b7f5102..75833364396 100644 --- a/src/game/PlayerDump.cpp +++ b/src/game/PlayerDump.cpp @@ -26,7 +26,7 @@  #include "ObjectMgr.h"  // Character Dump tables -#define DUMP_TABLE_COUNT 26 +#define DUMP_TABLE_COUNT 27  struct DumpTable  { @@ -47,6 +47,7 @@ static DumpTable dumpTables[DUMP_TABLE_COUNT] =      { "character_action",                 DTT_CHAR_TABLE },      { "character_aura",                   DTT_CHAR_TABLE },      { "character_homebind",               DTT_CHAR_TABLE }, +    { "character_skills",                 DTT_CHAR_TABLE },      { "character_account_data",           DTT_CHAR_TABLE },      { "character_glyphs",                 DTT_CHAR_TABLE },      { "character_talent",                 DTT_CHAR_TABLE },  | 
