From 4a6da1ad32d2d4965af549e4c95d937f98dffe45 Mon Sep 17 00:00:00 2001 From: hunuza Date: Wed, 25 Nov 2009 18:21:14 +0100 Subject: [PATCH] [8874] Extract player skills from data blob. Thanks to vladimir for the update query. Please make sure to make BACKUPs before you update your database. The update might take a while depending on the size of your database. Signed-off-by: hunuza --- sql/characters.sql | 24 +- .../8874_01_characters_character_skills.sql | 50 +++ sql/updates/Makefile.am | 2 + src/game/CharacterHandler.cpp | 1 + src/game/Player.cpp | 414 +++++++++++------- src/game/Player.h | 27 +- src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 8 files changed, 356 insertions(+), 166 deletions(-) create mode 100644 sql/updates/8874_01_characters_character_skills.sql diff --git a/sql/characters.sql b/sql/characters.sql index c47458548..bbc42f893 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_8843_01_characters` bit(1) default NULL + `required_8874_01_characters_character_skills` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -690,6 +690,28 @@ LOCK TABLES `character_reputation` WRITE; /*!40000 ALTER TABLE `character_reputation` ENABLE KEYS */; 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/8874_01_characters_character_skills.sql b/sql/updates/8874_01_characters_character_skills.sql new file mode 100644 index 000000000..5e28b4837 --- /dev/null +++ b/sql/updates/8874_01_characters_character_skills.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; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 99d8c9f85..42b2aa99d 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -174,6 +174,7 @@ pkgdata_DATA = \ 8863_01_mangos_spell_proc_event.sql \ 8873_01_mangos_spell_proc_event.sql \ 8873_02_mangos_spell_learn_spell.sql \ + 8874_01_characters_character_skills.sql \ README ## Additional files to include when running 'make dist' @@ -328,4 +329,5 @@ EXTRA_DIST = \ 8863_01_mangos_spell_proc_event.sql \ 8873_01_mangos_spell_proc_event.sql \ 8873_02_mangos_spell_learn_spell.sql \ + 8874_01_characters_character_skills.sql \ README diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 69cd034c5..7e2417593 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -85,6 +85,7 @@ bool LoginQueryHolder::Initialize() 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_LOADBGDATA, "SELECT instance_id, team, join_x, join_y, join_z, join_o, join_map, taxi_start, taxi_end, mount_spell FROM character_battleground_data 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 2b62149f2..e86da1093 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -5026,15 +5026,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); @@ -5047,7 +5044,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; } @@ -5156,13 +5155,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); @@ -5177,7 +5176,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)) @@ -5275,19 +5276,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() @@ -5297,10 +5299,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) @@ -5309,37 +5313,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(); @@ -5353,15 +5372,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(isecond.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); @@ -5369,9 +5389,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; jsecond.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); @@ -5424,15 +5459,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 @@ -5440,78 +5471,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 @@ -5519,15 +5543,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 @@ -5535,15 +5555,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 @@ -14487,7 +14503,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(); @@ -15751,6 +15767,7 @@ void Player::SaveToDB() _SaveSpellCooldowns(); _SaveActions(); _SaveAuras(); + _SaveSkills(); m_achievementMgr.SaveToDB(); m_reputationMgr.SaveToDB(); _SaveEquipmentSets(); @@ -16031,6 +16048,46 @@ 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();) @@ -19882,43 +19939,78 @@ void Player::learnSpellHighRank(uint32 spellid) sSpellMgr.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); - - // set correct unlearn bit - uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF; - if(!id) continue; - - SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id); - if(!pSkill) 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)) + do { - 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: - break; - } + Field *fields = result->Fetch(); - uint32 vskill = SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))); - learnSkillRewardedSpells(id, vskill); + uint16 skill = fields[0].GetUInt16(); + uint16 value = fields[1].GetUInt16(); + uint16 max = fields[2].GetUInt16(); + + SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill); + if(!pSkill) + { + sLog.outError("Character %u has skill %u that does not exist.", GetGUIDLow(), skill); + continue; + } + + // 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; + } + + // 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; + } + + 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 c3b64691d..ac33568fe 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -563,6 +563,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 SkillStatusMap; + class Quest; class Spell; class Item; @@ -884,7 +903,8 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS = 20, PLAYER_LOGIN_QUERY_LOADBGDATA = 21, PLAYER_LOGIN_QUERY_LOADACCOUNTDATA = 22, - MAX_PLAYER_LOGIN_QUERY = 23 + PLAYER_LOGIN_QUERY_LOADSKILLS = 23, + MAX_PLAYER_LOGIN_QUERY = 24 }; enum PlayerDelayedOperations @@ -2296,7 +2316,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); @@ -2315,6 +2335,7 @@ class MANGOS_DLL_SPEC Player : public Unit void _SaveMail(); void _SaveQuestStatus(); void _SaveDailyQuestStatus(); + void _SaveSkills(); void _SaveSpells(); void _SaveEquipmentSets(); void _SaveBGData(); @@ -2362,6 +2383,8 @@ class MANGOS_DLL_SPEC Player : public Unit QuestStatusMap mQuestStatus; + SkillStatusMap mSkillStatus; + uint32 m_GuildIdInvited; uint32 m_ArenaTeamIdInvited; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 4a0847810..a53caa3e2 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "8873" + #define REVISION_NR "8874" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 4836f289f..2843f48b9 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ - #define REVISION_DB_CHARACTERS "required_8843_01_characters" + #define REVISION_DB_CHARACTERS "required_8874_01_characters_character_skills" #define REVISION_DB_MANGOS "required_8873_02_mangos_spell_learn_spell" #define REVISION_DB_REALMD "required_8728_01_realmd_account" #endif // __REVISION_SQL_H__