aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/base/characters_database.sql4
-rw-r--r--sql/updates/characters/master/2023_02_08_00_characters.sql1
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp6
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.cpp14
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp227
-rw-r--r--src/server/game/Entities/Player/Player.h7
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); }