rendered paste bodydiff --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;