diff --git a/sql/characters.sql b/sql/characters.sql index 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.sql new file mode 100644 index 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.cpp index 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.cpp index 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(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); @@ -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; 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); @@ -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 settings diff --git a/src/game/Player.h b/src/game/Player.h index 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 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;