diff options
-rw-r--r-- | sql/base/characters_database.sql | 4 | ||||
-rw-r--r-- | sql/updates/characters/master/2023_02_08_00_characters.sql | 1 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Entities/Item/ItemTemplate.cpp | 14 | ||||
-rw-r--r-- | src/server/game/Entities/Item/ItemTemplate.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 227 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 7 |
7 files changed, 207 insertions, 54 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index ae7ff4dfbb4..16bf4837b27 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1511,6 +1511,7 @@ CREATE TABLE `character_skills` ( `skill` smallint unsigned NOT NULL, `value` smallint unsigned NOT NULL, `max` smallint unsigned NOT NULL, + `professionSlot` tinyint NOT NULL DEFAULT -1, PRIMARY KEY (`guid`,`skill`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Player System'; /*!40101 SET character_set_client = @saved_cs_client */; @@ -3696,7 +3697,8 @@ INSERT INTO `updates` VALUES ('2022_12_30_00_characters.sql','5F90C2BFFBB8F6CE0A3327A2CAABCD5CA3C2BA60','ARCHIVED','2022-12-30 22:50:16',0), ('2023_01_28_00_characters.sql','0280F79FD6EC93FFB3CC67B6499CEDA49D582BFC','ARCHIVED','2023-01-28 00:11:03',0), ('2023_01_29_00_characters.sql','24FA9E0F616BF77AC588A25A8A8699903A19A5FE','ARCHIVED','2023-01-29 16:31:12',0), -('2023_02_03_00_characters.sql','A04BA4386B3D5C60407D22CA4BF9A4A6258AA39D','ARCHIVED','2023-02-03 01:13:52',0); +('2023_02_03_00_characters.sql','A04BA4386B3D5C60407D22CA4BF9A4A6258AA39D','ARCHIVED','2023-02-03 01:13:52',0), +('2023_02_08_00_characters.sql','C9DF607CCE99540F613F5E25E17090176C995C7C','RELEASED','2023-02-08 21:41:17',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/master/2023_02_08_00_characters.sql b/sql/updates/characters/master/2023_02_08_00_characters.sql new file mode 100644 index 00000000000..f4f6b459fc8 --- /dev/null +++ b/sql/updates/characters/master/2023_02_08_00_characters.sql @@ -0,0 +1 @@ +ALTER TABLE `character_skills` ADD `professionSlot` TINYINT NOT NULL DEFAULT -1 AFTER `max`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index edd46912ba0..c609bc8a620 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -141,7 +141,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyphId FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT talentId, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_PVP_TALENTS, "SELECT talentId0, talentId1, talentId2, talentId3, talentGroup FROM character_pvp_talent WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max, professionSlot FROM character_skills WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUSREW, "SELECT quest FROM character_queststatus_rewarded WHERE guid = ? AND active = 1", CONNECTION_ASYNC); @@ -636,8 +636,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST, "UPDATE character_queststatus_rewarded SET active = 0 WHERE quest = ? AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INVALID_QUEST_PROGRESS_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria WHERE questObjectiveId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SKILL_BY_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_SKILLS, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_SKILLS, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_SKILLS, "INSERT INTO character_skills (guid, skill, value, max, professionSlot) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_SKILLS, "UPDATE character_skills SET value = ?, max = ?, professionSlot = ? WHERE guid = ? AND skill = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_SPELL, "INSERT INTO character_spell (guid, spell, active, disabled) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SPELL_FAVORITE, "DELETE FROM character_spell_favorite WHERE guid = ? AND spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SPELL_FAVORITE_BY_CHAR, "DELETE FROM character_spell_favorite WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/game/Entities/Item/ItemTemplate.cpp b/src/server/game/Entities/Item/ItemTemplate.cpp index fe85585f1af..22ab691840d 100644 --- a/src/server/game/Entities/Item/ItemTemplate.cpp +++ b/src/server/game/Entities/Item/ItemTemplate.cpp @@ -98,6 +98,13 @@ uint32 ItemTemplate::GetSkill() const 0, SKILL_CLOTH, SKILL_LEATHER, SKILL_MAIL, SKILL_PLATE_MAIL, 0, SKILL_SHIELD, 0, 0, 0, 0 }; + static uint32 const itemProfessionSkills[MAX_ITEM_SUBCLASS_PROFESSION] = + { + SKILL_BLACKSMITHING, SKILL_LEATHERWORKING, SKILL_ALCHEMY, SKILL_HERBALISM, SKILL_COOKING, + SKILL_MINING, SKILL_TAILORING, SKILL_ENGINEERING, SKILL_ENCHANTING, SKILL_FISHING, + SKILL_SKINNING, SKILL_JEWELCRAFTING, SKILL_INSCRIPTION, SKILL_ARCHAEOLOGY + }; + switch (GetClass()) { case ITEM_CLASS_WEAPON: @@ -105,13 +112,16 @@ uint32 ItemTemplate::GetSkill() const return 0; else return itemWeaponSkills[GetSubClass()]; - case ITEM_CLASS_ARMOR: if (GetSubClass() >= MAX_ITEM_SUBCLASS_ARMOR) return 0; else return itemArmorSkills[GetSubClass()]; - + case ITEM_CLASS_PROFESSION: + if (GetSubClass() >= MAX_ITEM_SUBCLASS_PROFESSION) + return 0; + else + return itemProfessionSkills[GetSubClass()]; default: return 0; } diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 0a036d1c36d..8a8d86c9440 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -695,7 +695,7 @@ enum ItemSubclassWowToken #define MAX_ITEM_SUBCLASS_WOW_TOKEN 1 -enum ItemSubclassPorfession +enum ItemSubclassProfession { ITEM_SUBCLASS_PROFESSION_BLACKSMITHING = 0, ITEM_SUBCLASS_PROFESSION_LEATHERWORKING = 1, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9b044c87bde..cf928b99560 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -5719,6 +5719,11 @@ void Player::ModifySkillBonus(uint32 skillid, int32 val, bool talent) SetSkillPermBonus(itr->second.pos, m_activePlayerData->Skill->SkillPermBonus[itr->second.pos] + val); else SetSkillTempBonus(itr->second.pos, m_activePlayerData->Skill->SkillTempBonus[itr->second.pos] + val); + + // Apply/Remove bonus to child skill lines + if (std::vector<SkillLineEntry const*> const* childSkillLines = sDB2Manager.GetSkillLinesForParentSkill(skillid)) + for (SkillLineEntry const* childSkillLine : *childSkillLines) + ModifySkillBonus(childSkillLine->ID, val, talent); } void Player::UpdateSkillsForLevel() @@ -5771,12 +5776,34 @@ void Player::InitializeSkillFields() // To "remove" a skill line, set it's values to zero void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) { - if (!id) + SkillLineEntry const* skillEntry = sSkillLineStore.LookupEntry(id); + if (!skillEntry) + { + TC_LOG_ERROR("misc", "Player::SetSkill: Skill (SkillID: {}) not found in SkillLineStore for player '{}' ({})", + id, GetName(), GetGUID().ToString()); return; + } uint16 currVal; SkillStatusMap::iterator itr = mSkillStatus.find(id); + auto refreshSkillBonusAuras = [&] + { + // Temporary bonuses + for (AuraEffect* effect : GetAuraEffectsByType(SPELL_AURA_MOD_SKILL)) + if (effect->GetMiscValue() == int32(id)) + effect->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true); + + for (AuraEffect* effect : GetAuraEffectsByType(SPELL_AURA_MOD_SKILL_2)) + if (effect->GetMiscValue() == int32(id)) + effect->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true); + + // Permanent bonuses + for (AuraEffect* effect : GetAuraEffectsByType(SPELL_AURA_MOD_SKILL_TALENT)) + if (effect->GetMiscValue() == int32(id)) + effect->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true); + }; + // Handle already stored skills if (itr != mSkillStatus.end()) { @@ -5808,16 +5835,55 @@ void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) UpdateCriteria(CriteriaType::AchieveSkillStep, id); // update skill state - if (itr->second.uState == SKILL_UNCHANGED) + if (itr->second.uState == SKILL_UNCHANGED || itr->second.uState == SKILL_DELETED) { if (currVal == 0) // activated skill, mark as new to save into database + { itr->second.uState = SKILL_NEW; + + // Set profession line + int32 freeProfessionSlot = FindEmptyProfessionSlotFor(id); + if (freeProfessionSlot != -1) + SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, freeProfessionSlot), id); + + refreshSkillBonusAuras(); + } else // updated skill, mark as changed to save into database itr->second.uState = SKILL_CHANGED; } } else if (currVal && !newVal) // Deactivate skill line { + // Try to store profession tools and accessories into the bag + // If we can't, we can't unlearn the profession + int32 professionSlot = GetProfessionSlotFor(id); + if (professionSlot != -1) + { + uint8 professionSlotStart = PROFESSION_SLOT_PROFESSION1_TOOL + professionSlot * PROFESSION_SLOT_MAX_COUNT; + + // Get all profession items equipped + for (uint8 slotOffset = 0; slotOffset < PROFESSION_SLOT_MAX_COUNT; ++slotOffset) + { + if (Item* professionItem = GetItemByPos(INVENTORY_SLOT_BAG_0, professionSlotStart + slotOffset)) + { + // Store item in bag + ItemPosCountVec professionItemDest; + + if (CanStoreItem(NULL_BAG, NULL_SLOT, professionItemDest, professionItem, false) != EQUIP_ERR_OK) + { + SendDirectMessage(WorldPackets::Misc::DisplayGameError(GameError::ERR_INV_FULL).Write()); + return; + } + + RemoveItem(INVENTORY_SLOT_BAG_0, professionItem->GetSlot(), true); + StoreItem(professionItemDest, professionItem, true); + } + } + + // Clear profession lines + SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, professionSlot), 0); + } + //remove enchantments needing this skill UpdateSkillEnchantments(id, currVal, 0); // clear skill fields @@ -5829,10 +5895,7 @@ void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) SetSkillPermBonus(itr->second.pos, 0); // mark as deleted so the next save will delete the data from the database - if (itr->second.uState != SKILL_NEW) - itr->second.uState = SKILL_DELETED; - else - itr->second.uState = SKILL_UNCHANGED; + itr->second.uState = SKILL_DELETED; // remove all spells that related to this skill if (std::vector<SkillLineAbilityEntry const*> const* skillLineAbilities = sDB2Manager.GetSkillLineAbilitiesBySkill(id)) @@ -5842,12 +5905,6 @@ void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) if (std::vector<SkillLineEntry const*> const* childSkillLines = sDB2Manager.GetSkillLinesForParentSkill(id)) for (SkillLineEntry const* childSkillLine : *childSkillLines) SetSkill(childSkillLine->ID, 0, 0, 0); - - // Clear profession lines - if (m_activePlayerData->ProfessionSkillLine[0] == int32(id)) - SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, 0), 0); - else if (m_activePlayerData->ProfessionSkillLine[1] == int32(id)) - SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, 1), 0); } } else @@ -5871,14 +5928,6 @@ void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) return; } - SkillLineEntry const* skillEntry = sSkillLineStore.LookupEntry(id); - if (!skillEntry) - { - TC_LOG_ERROR("misc", "Player::SetSkill: Skill (SkillID: {}) not found in SkillLineStore for player '{}' ({})", - id, GetName(), GetGUID().ToString()); - return; - } - if (skillEntry->ParentSkillLineID) { if (skillEntry->ParentTierIndex > 0) @@ -5901,12 +5950,9 @@ void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) if (!HasSkill(childSkillLine->ID)) SetSkill(childSkillLine->ID, 0, 0, 0); - if (skillEntry->CategoryID == SKILL_CATEGORY_PROFESSION) - { - int32 freeProfessionSlot = FindProfessionSlotFor(id); - if (freeProfessionSlot != -1) - SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, freeProfessionSlot), id); - } + int32 freeProfessionSlot = FindEmptyProfessionSlotFor(id); + if (freeProfessionSlot != -1) + SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, freeProfessionSlot), id); } if (itr == mSkillStatus.end()) @@ -5927,19 +5973,7 @@ void Player::SetSkill(uint32 id, uint16 step, uint16 newVal, uint16 maxVal) if (newVal) { - // temporary bonuses - for (AuraEffect* effect : GetAuraEffectsByType(SPELL_AURA_MOD_SKILL)) - if (effect->GetMiscValue() == int32(id)) - effect->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true); - - for (AuraEffect* effect : GetAuraEffectsByType(SPELL_AURA_MOD_SKILL_2)) - if (effect->GetMiscValue() == int32(id)) - effect->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true); - - // permanent bonuses - for (AuraEffect* effect : GetAuraEffectsByType(SPELL_AURA_MOD_SKILL_TALENT)) - if (effect->GetMiscValue() == int32(id)) - effect->HandleEffect(this, AURA_EFFECT_HANDLE_SKILL, true); + refreshSkillBonusAuras(); // Learn all spells for skill LearnSkillRewardedSpells(id, newVal, Races(GetRace())); @@ -9154,6 +9188,65 @@ uint8 Player::FindEquipSlot(Item const* item, uint32 slot, bool swap) const slots[2] = INVENTORY_SLOT_BAG_START + 2; slots[3] = INVENTORY_SLOT_BAG_START + 3; break; + case INVTYPE_PROFESSION_TOOL: + case INVTYPE_PROFESSION_GEAR: + { + bool isProfessionTool = item->GetTemplate()->GetInventoryType() == INVTYPE_PROFESSION_TOOL; + + // Validate item class + if (!(item->GetTemplate()->GetClass() == ITEM_CLASS_PROFESSION)) + return NULL_SLOT; + + // Check if player has profession skill + uint32 itemSkill = item->GetTemplate()->GetSkill(); + if (!HasSkill(itemSkill)) + return NULL_SLOT; + + switch (item->GetTemplate()->GetSubClass()) + { + case ITEM_SUBCLASS_PROFESSION_COOKING: + slots[0] = isProfessionTool ? PROFESSION_SLOT_COOKING_TOOL : PROFESSION_SLOT_COOKING_GEAR1; + break; + case ITEM_SUBCLASS_PROFESSION_FISHING: + { + // Fishing doesn't make use of gear slots (clientside) + if (!isProfessionTool) + return NULL_SLOT; + + slots[0] = PROFESSION_SLOT_FISHING_TOOL; + break; + } + case ITEM_SUBCLASS_PROFESSION_BLACKSMITHING: + case ITEM_SUBCLASS_PROFESSION_LEATHERWORKING: + case ITEM_SUBCLASS_PROFESSION_ALCHEMY: + case ITEM_SUBCLASS_PROFESSION_HERBALISM: + case ITEM_SUBCLASS_PROFESSION_MINING: + case ITEM_SUBCLASS_PROFESSION_TAILORING: + case ITEM_SUBCLASS_PROFESSION_ENGINEERING: + case ITEM_SUBCLASS_PROFESSION_ENCHANTING: + case ITEM_SUBCLASS_PROFESSION_SKINNING: + case ITEM_SUBCLASS_PROFESSION_JEWELCRAFTING: + case ITEM_SUBCLASS_PROFESSION_INSCRIPTION: + { + int32 professionSlot = GetProfessionSlotFor(itemSkill); + if (professionSlot == -1) + return NULL_SLOT; + + if (isProfessionTool) + slots[0] = PROFESSION_SLOT_PROFESSION1_TOOL + professionSlot * PROFESSION_SLOT_MAX_COUNT; + else + { + slots[0] = PROFESSION_SLOT_PROFESSION1_GEAR1 + professionSlot * PROFESSION_SLOT_MAX_COUNT; + slots[0] = PROFESSION_SLOT_PROFESSION1_GEAR2 + professionSlot * PROFESSION_SLOT_MAX_COUNT; + } + + break; + } + default: + return NULL_SLOT; + } + break; + } default: return NULL_SLOT; } @@ -10775,6 +10868,18 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool case EQUIPMENT_SLOT_TRINKET2: ignore = EQUIPMENT_SLOT_TRINKET1; break; + case PROFESSION_SLOT_PROFESSION1_GEAR1: + ignore = PROFESSION_SLOT_PROFESSION1_GEAR2; + break; + case PROFESSION_SLOT_PROFESSION1_GEAR2: + ignore = PROFESSION_SLOT_PROFESSION1_GEAR1; + break; + case PROFESSION_SLOT_PROFESSION2_GEAR1: + ignore = PROFESSION_SLOT_PROFESSION2_GEAR2; + break; + case PROFESSION_SLOT_PROFESSION2_GEAR2: + ignore = PROFESSION_SLOT_PROFESSION2_GEAR1; + break; } if (ignore == uint8(NULL_SLOT) || pItem != GetItemByPos(INVENTORY_SLOT_BAG_0, ignore)) @@ -20564,6 +20669,7 @@ void Player::_SaveSkills(CharacterDatabaseTransaction trans) uint16 value = m_activePlayerData->Skill->SkillRank[itr->second.pos]; uint16 max = m_activePlayerData->Skill->SkillMaxRank[itr->second.pos]; + int8 professionSlot = int8(GetProfessionSlotFor(itr->first)); switch (itr->second.uState) { @@ -20573,14 +20679,16 @@ void Player::_SaveSkills(CharacterDatabaseTransaction trans) stmt->setUInt16(1, uint16(itr->first)); stmt->setUInt16(2, value); stmt->setUInt16(3, max); + stmt->setInt8(4, professionSlot); trans->Append(stmt); break; case SKILL_CHANGED: stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_SKILLS); stmt->setUInt16(0, value); stmt->setUInt16(1, max); - stmt->setUInt64(2, GetGUID().GetCounter()); - stmt->setUInt16(3, uint16(itr->first)); + stmt->setInt8(2, professionSlot); + stmt->setUInt64(3, GetGUID().GetCounter()); + stmt->setUInt16(4, uint16(itr->first)); trans->Append(stmt); break; case SKILL_DELETED: @@ -24162,12 +24270,28 @@ void Player::LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue, Races r } } -int32 Player::FindProfessionSlotFor(uint32 skillId) const +int32 Player::GetProfessionSlotFor(uint32 skillId) const +{ + int32 const* professionsBegin = m_activePlayerData->ProfessionSkillLine.begin(); + int32 const* professionsEnd = m_activePlayerData->ProfessionSkillLine.end(); + + // Find profession slot + auto professionSlot = std::find(professionsBegin, professionsEnd, int32(skillId)); + if (professionSlot == professionsEnd) + return -1; + + return std::distance(professionsBegin, professionSlot); +} + +int32 Player::FindEmptyProfessionSlotFor(uint32 skillId) const { SkillLineEntry const* skillEntry = sSkillLineStore.LookupEntry(skillId); if (!skillEntry) return -1; + if (skillEntry->ParentSkillLineID || skillEntry->CategoryID != SKILL_CATEGORY_PROFESSION) + return -1; + int32 const* professionsBegin = m_activePlayerData->ProfessionSkillLine.begin(); int32 const* professionsEnd = m_activePlayerData->ProfessionSkillLine.end(); @@ -25794,12 +25918,13 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot* void Player::_LoadSkills(PreparedQueryResult result) { - // 0 1 2 - // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '{}'", GUID_LOPART(m_guid)); + // 0 1 2 3 + // SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max, professionSlot FROM character_skills WHERE guid = '{}'", GUID_LOPART(m_guid)); Races race = Races(GetRace()); uint32 count = 0; std::unordered_map<uint32, uint32> loadedSkillValues; + std::vector<uint16> loadedProfessionsWithoutSlot; // fixup old characters if (result) { do @@ -25815,6 +25940,7 @@ void Player::_LoadSkills(PreparedQueryResult result) uint16 skill = fields[0].GetUInt16(); uint16 value = fields[1].GetUInt16(); uint16 max = fields[2].GetUInt16(); + int8 professionSlot = fields[3].GetInt8(); SkillRaceClassInfoEntry const* rcEntry = sDB2Manager.GetSkillRaceClassInfo(skill, race, GetClass()); if (!rcEntry) @@ -25859,9 +25985,10 @@ void Player::_LoadSkills(PreparedQueryResult result) if (!skillLine->ParentSkillLineID) { - int32 professionSlot = FindProfessionSlotFor(skill); if (professionSlot != -1) SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, professionSlot), skill); + else + loadedProfessionsWithoutSlot.push_back(skill); } } } @@ -25897,6 +26024,16 @@ void Player::_LoadSkills(PreparedQueryResult result) } } + for (uint16 skill : loadedProfessionsWithoutSlot) + { + int32 emptyProfessionSlot = FindEmptyProfessionSlotFor(skill); + if (emptyProfessionSlot != -1) + { + SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ProfessionSkillLine, emptyProfessionSlot), skill); + mSkillStatus.at(skill).uState = SKILL_CHANGED; + } + } + if (HasSkill(SKILL_FIST_WEAPONS)) SetSkill(SKILL_FIST_WEAPONS, 0, GetSkillValue(SKILL_UNARMED), GetMaxSkillValueForLevel()); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index cf26c304388..7cff3ee493e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -696,7 +696,9 @@ enum ProfessionSlots : uint8 PROFESSION_SLOT_FISHING_GEAR2 = 29, PROFESSION_SLOT_END, - PROFESSION_SLOT_START = PROFESSION_SLOT_PROFESSION1_TOOL + PROFESSION_SLOT_START = PROFESSION_SLOT_PROFESSION1_TOOL, + + PROFESSION_SLOT_MAX_COUNT = PROFESSION_SLOT_PROFESSION2_TOOL - PROFESSION_SLOT_PROFESSION1_TOOL }; enum InventorySlots : uint8 // 4 slots @@ -2204,7 +2206,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint16 GetSkillStep(uint32 skill) const; // 0...6 bool HasSkill(uint32 skill) const; void LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue, Races race); - int32 FindProfessionSlotFor(uint32 skillId) const; + int32 GetProfessionSlotFor(uint32 skillId) const; + int32 FindEmptyProfessionSlotFor(uint32 skillId) const; void SetSkillLineId(uint32 pos, uint16 skillLineId) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Skill).ModifyValue(&UF::SkillInfo::SkillLineID, pos), skillLineId); } void SetSkillStep(uint32 pos, uint16 step) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Skill).ModifyValue(&UF::SkillInfo::SkillStep, pos), step); }; void SetSkillRank(uint32 pos, uint16 rank) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Skill).ModifyValue(&UF::SkillInfo::SkillRank, pos), rank); } |