Core/Players: Added support for profession equipment slots

Port From (https://github.com/TrinityCore/TrinityCore/commit/aaa0db28801784e64a845d2df90a0e285062d3a8)
This commit is contained in:
hondacrx
2023-02-12 02:05:42 -05:00
parent 4003efb9c9
commit 913614754d
6 changed files with 247 additions and 66 deletions
+2 -1
View File
@@ -105,6 +105,7 @@ namespace Framework.Constants
public const byte End = 30;
public const byte Start = Profession1Tool;
public const byte MaxCount = Profession2Tool - Profession1Tool;
}
public struct InventorySlots
@@ -886,7 +887,7 @@ namespace Framework.Constants
Max = 1
}
public enum ItemSubclassPorfession
public enum ItemSubclassProfession
{
Blacksmithing = 0,
Leatherworking = 1,
@@ -124,7 +124,7 @@ namespace Framework.Database
PrepareStatement(CharStatements.SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyphId FROM character_glyphs WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_TALENTS, "SELECT talentId, talentGroup FROM character_talent WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_PVP_TALENTS, "SELECT talentId0, talentId1, talentId2, talentId3, talentGroup FROM character_pvp_talent WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_SKILLS, "SELECT skill, value, max, professionSlot FROM character_skills WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?");
PrepareStatement(CharStatements.SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1");
PrepareStatement(CharStatements.SEL_CHARACTER_QUESTSTATUSREW, "SELECT quest FROM character_queststatus_rewarded WHERE guid = ? AND active = 1");
@@ -611,8 +611,8 @@ namespace Framework.Database
PrepareStatement(CharStatements.UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST, "UPDATE character_queststatus_rewarded SET active = 0 WHERE quest = ? AND guid = ?");
PrepareStatement(CharStatements.DEL_INVALID_QUEST_PROGRESS_CRITERIA, "DELETE FROM character_queststatus_objectives_criteria WHERE questObjectiveId = ?");
PrepareStatement(CharStatements.DEL_CHAR_SKILL_BY_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?");
PrepareStatement(CharStatements.INS_CHAR_SKILLS, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)");
PrepareStatement(CharStatements.UPD_CHAR_SKILLS, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?");
PrepareStatement(CharStatements.INS_CHAR_SKILLS, "INSERT INTO character_skills (guid, skill, value, max, professionSlot) VALUES (?, ?, ?, ?)");
PrepareStatement(CharStatements.UPD_CHAR_SKILLS, "UPDATE character_skills SET value = ?, max = ?, professionSlot = ? WHERE guid = ? AND skill = ?");
PrepareStatement(CharStatements.INS_CHAR_SPELL, "INSERT INTO character_spell (guid, spell, active, disabled) VALUES (?, ?, ?, ?)");
PrepareStatement(CharStatements.DEL_CHAR_SPELL_FAVORITE, "DELETE FROM character_spell_favorite WHERE guid = ? AND spell = ?");
PrepareStatement(CharStatements.DEL_CHAR_SPELL_FAVORITE_BY_CHAR, "DELETE FROM character_spell_favorite WHERE guid = ?");
+26 -17
View File
@@ -63,23 +63,29 @@ namespace Game.Entities
return false;
}
static SkillType[] item_weapon_skills =
{
SkillType.Axes, SkillType.TwoHandedAxes, SkillType.Bows, SkillType.Guns, SkillType.Maces,
SkillType.TwoHandedMaces, SkillType.Polearms, SkillType.Swords, SkillType.TwoHandedSwords, SkillType.Warglaives,
SkillType.Staves, 0, 0, SkillType.FistWeapons, 0,
SkillType.Daggers, 0, 0, SkillType.Crossbows, SkillType.Wands,
SkillType.Fishing
};
static SkillType[] item_armor_skills =
{
0, SkillType.Cloth, SkillType.Leather, SkillType.Mail, SkillType.PlateMail, 0, SkillType.Shield, 0, 0, 0, 0, 0
};
static SkillType[] itemProfessionSkills =
{
SkillType.Blacksmithing, SkillType.Leatherworking, SkillType.Alchemy, SkillType.Herbalism, SkillType.Cooking,
SkillType.Mining, SkillType.Tailoring, SkillType.Engineering, SkillType.Enchanting, SkillType.Fishing,
SkillType.Skinning, SkillType.Jewelcrafting, SkillType.Inscription, SkillType.Archaeology
};
public SkillType GetSkill()
{
SkillType[] item_weapon_skills =
{
SkillType.Axes, SkillType.TwoHandedAxes, SkillType.Bows, SkillType.Guns, SkillType.Maces,
SkillType.TwoHandedMaces, SkillType.Polearms, SkillType.Swords, SkillType.TwoHandedSwords, SkillType.Warglaives,
SkillType.Staves, 0, 0, SkillType.FistWeapons, 0,
SkillType.Daggers, 0, 0, SkillType.Crossbows, SkillType.Wands,
SkillType.Fishing
};
SkillType[] item_armor_skills =
{
0, SkillType.Cloth, SkillType.Leather, SkillType.Mail, SkillType.PlateMail, 0, SkillType.Shield, 0, 0, 0, 0, 0
};
switch (GetClass())
{
case ItemClass.Weapon:
@@ -87,13 +93,16 @@ namespace Game.Entities
return 0;
else
return item_weapon_skills[GetSubClass()];
case ItemClass.Armor:
if (GetSubClass() >= (int)ItemSubClassArmor.Max)
return 0;
else
return item_armor_skills[GetSubClass()];
case ItemClass.Profession:
if (GetSubClass() >= (int)ItemSubclassProfession.Max)
return 0;
else
return itemProfessionSkills[GetSubClass()];
default:
return 0;
}
+21 -5
View File
@@ -335,6 +335,7 @@ namespace Game.Entities
Race race = GetRace();
uint count = 0;
Dictionary<uint, uint> loadedSkillValues = new();
List<ushort> loadedProfessionsWithoutSlot = new(); // fixup old characters
if (!result.IsEmpty())
{
do
@@ -348,6 +349,7 @@ namespace Game.Entities
var skill = result.Read<ushort>(0);
var value = result.Read<ushort>(1);
var max = result.Read<ushort>(2);
var professionSlot = result.Read<sbyte>(3);
SkillRaceClassInfoRecord rcEntry = Global.DB2Mgr.GetSkillRaceClassInfo(skill, race, GetClass());
if (rcEntry == null)
@@ -391,14 +393,14 @@ namespace Game.Entities
if (skillLine.ParentSkillLineID != 0 && skillLine.ParentTierIndex != 0)
{
int professionSlot = FindProfessionSlotFor(skill);
if (professionSlot != -1)
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, (int)professionSlot), skill);
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, professionSlot), skill);
else
loadedProfessionsWithoutSlot.Add(skill);
}
}
}
SetSkillLineId(skillStatusData.Pos, skill);
SetSkillStep(skillStatusData.Pos, step);
SetSkillRank(skillStatusData.Pos, value);
@@ -433,9 +435,20 @@ namespace Game.Entities
}
}
foreach (ushort skill in loadedProfessionsWithoutSlot)
{
int emptyProfessionSlot = FindEmptyProfessionSlotFor(skill);
if (emptyProfessionSlot != -1)
{
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, emptyProfessionSlot), skill);
mSkillStatus[skill].State = SkillState.Changed;
}
}
if (HasSkill(SkillType.FistWeapons))
SetSkill(SkillType.FistWeapons, 0, GetSkillValue(SkillType.Unarmed), GetMaxSkillValueForLevel());
}
void _LoadSpells(SQLResult result, SQLResult favoritesResult)
{
if (!result.IsEmpty())
@@ -1786,6 +1799,7 @@ namespace Game.Entities
ushort value = skillInfoField.SkillRank[pair.Value.Pos];
ushort max = skillInfoField.SkillMaxRank[pair.Value.Pos];
sbyte professionSlot = (sbyte)GetProfessionSlotFor(pair.Key);
switch (pair.Value.State)
{
@@ -1795,14 +1809,16 @@ namespace Game.Entities
stmt.AddValue(1, (ushort)pair.Key);
stmt.AddValue(2, value);
stmt.AddValue(3, max);
stmt.AddValue(4, professionSlot);
trans.Append(stmt);
break;
case SkillState.Changed:
stmt = CharacterDatabase.GetPreparedStatement(CharStatements.UPD_CHAR_SKILLS);
stmt.AddValue(0, value);
stmt.AddValue(1, max);
stmt.AddValue(2, GetGUID().GetCounter());
stmt.AddValue(3, (ushort)pair.Key);
stmt.AddValue(2, professionSlot);
stmt.AddValue(3, GetGUID().GetCounter());
stmt.AddValue(4, (ushort)pair.Key);
trans.Append(stmt);
break;
case SkillState.Deleted:
+102 -1
View File
@@ -4829,6 +4829,65 @@ namespace Game.Entities
slots[2] = InventorySlots.BagStart + 2;
slots[3] = InventorySlots.BagStart + 3;
break;
case InventoryType.ProfessionTool:
case InventoryType.ProfessionGear:
{
bool isProfessionTool = item.GetTemplate().GetInventoryType() == InventoryType.ProfessionTool;
// Validate item class
if (!(item.GetTemplate().GetClass() == ItemClass.Profession))
return ItemConst.NullSlot;
// Check if player has profession skill
uint itemSkill = (uint)item.GetTemplate().GetSkill();
if (!HasSkill(itemSkill))
return ItemConst.NullSlot;
switch ((ItemSubclassProfession)item.GetTemplate().GetSubClass())
{
case ItemSubclassProfession.Cooking:
slots[0] = isProfessionTool ? ProfessionSlots.CookingTool : ProfessionSlots.CookingGear1;
break;
case ItemSubclassProfession.Fishing:
{
// Fishing doesn't make use of gear slots (clientside)
if (!isProfessionTool)
return ItemConst.NullSlot;
slots[0] = ProfessionSlots.FishingTool;
break;
}
case ItemSubclassProfession.Blacksmithing :
case ItemSubclassProfession.Leatherworking :
case ItemSubclassProfession.Alchemy :
case ItemSubclassProfession.Herbalism :
case ItemSubclassProfession.Mining :
case ItemSubclassProfession.Tailoring :
case ItemSubclassProfession.Engineering :
case ItemSubclassProfession.Enchanting :
case ItemSubclassProfession.Skinning :
case ItemSubclassProfession.Jewelcrafting :
case ItemSubclassProfession.Inscription :
{
int professionSlot = GetProfessionSlotFor(itemSkill);
if (professionSlot == -1)
return ItemConst.NullSlot;
if (isProfessionTool)
slots[0] = (byte)(ProfessionSlots.Profession1Tool + professionSlot * ProfessionSlots.MaxCount);
else
{
slots[0] = (byte)(ProfessionSlots.Profession1Gear1 + professionSlot * ProfessionSlots.MaxCount);
slots[0] = (byte)(ProfessionSlots.Profession1Gear2 + professionSlot * ProfessionSlots.MaxCount);
}
break;
}
default:
return ItemConst.NullSlot;
}
break;
}
default:
return ItemConst.NullSlot;
}
@@ -4970,8 +5029,50 @@ namespace Game.Entities
if (!swap && GetItemByPos(InventorySlots.Bag0, eslot) != null)
return InventoryResult.NoSlotAvailable;
// if we are swapping 2 equiped items, CanEquipUniqueItem check
// should ignore the item we are trying to swap, and not the
// destination item. CanEquipUniqueItem should ignore destination
// item only when we are swapping weapon from bag
byte ignore = ItemConst.NullSlot;
switch (eslot)
{
case EquipmentSlot.MainHand:
ignore = EquipmentSlot.OffHand;
break;
case EquipmentSlot.OffHand:
ignore = EquipmentSlot.MainHand;
break;
case EquipmentSlot.Finger1:
ignore = EquipmentSlot.Finger2;
break;
case EquipmentSlot.Finger2:
ignore = EquipmentSlot.Finger1;
break;
case EquipmentSlot.Trinket1:
ignore = EquipmentSlot.Trinket2;
break;
case EquipmentSlot.Trinket2:
ignore = EquipmentSlot.Trinket1;
break;
case ProfessionSlots.Profession1Gear1:
ignore = ProfessionSlots.Profession1Gear2;
break;
case ProfessionSlots.Profession1Gear2:
ignore = ProfessionSlots.Profession1Gear1;
break;
case ProfessionSlots.Profession2Gear1:
ignore = ProfessionSlots.Profession2Gear2;
break;
case ProfessionSlots.Profession2Gear2:
ignore = ProfessionSlots.Profession2Gear1;
break;
}
if (ignore == ItemConst.NullSlot || pItem != GetItemByPos(InventorySlots.Bag0, ignore))
ignore = eslot;
// if swap ignore item (equipped also)
InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? eslot : ItemConst.NullSlot);
InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? ignore : ItemConst.NullSlot);
if (res2 != InventoryResult.Ok)
return res2;
+93 -39
View File
@@ -828,6 +828,11 @@ namespace Game.Entities
}
public void ModifySkillBonus(SkillType skillid, int val, bool talent)
{
ModifySkillBonus((uint)skillid, val, talent);
}
public void ModifySkillBonus(uint skillid, int val, bool talent)
{
SkillInfo skillInfoField = m_activePlayerData.Skill;
@@ -839,6 +844,12 @@ namespace Game.Entities
SetSkillPermBonus(skillStatusData.Pos, (ushort)(skillInfoField.SkillPermBonus[skillStatusData.Pos] + val));
else
SetSkillTempBonus(skillStatusData.Pos, (ushort)(skillInfoField.SkillTempBonus[skillStatusData.Pos] + val));
// Apply/Remove bonus to child skill lines
var childSkillLines = Global.DB2Mgr.GetSkillLinesForParentSkill(skillid);
if (childSkillLines != null)
foreach (var childSkillLine in childSkillLines)
ModifySkillBonus(childSkillLine.Id, val, talent);
}
public void StopCastingBindSight()
@@ -1029,13 +1040,34 @@ namespace Game.Entities
}
public void SetSkill(uint id, uint step, uint newVal, uint maxVal)
{
if (id == 0)
SkillLineRecord skillEntry = CliDB.SkillLineStorage.LookupByKey(id);
if (skillEntry == null)
{
Log.outError(LogFilter.Misc, $"Player::SetSkill: Skill (SkillID: {id}) not found in SkillLineStore for player '{GetName()}' ({GetGUID()})");
return;
}
ushort currVal;
var skillStatusData = mSkillStatus.LookupByKey(id);
SkillInfo skillInfoField = m_activePlayerData.Skill;
void refreshSkillBonusAuras()
{
// Temporary bonuses
foreach (AuraEffect effect in GetAuraEffectsByType(AuraType.ModSkill))
if (effect.GetMiscValue() == id)
effect.HandleEffect(this, AuraEffectHandleModes.Skill, true);
foreach (AuraEffect effect in GetAuraEffectsByType(AuraType.ModSkill2))
if (effect.GetMiscValue() == id)
effect.HandleEffect(this, AuraEffectHandleModes.Skill, true);
// Permanent bonuses
foreach (AuraEffect effect in GetAuraEffectsByType(AuraType.ModSkillTalent))
if (effect.GetMiscValue() == id)
effect.HandleEffect(this, AuraEffectHandleModes.Skill, true);
}
// Handle already stored skills
if (skillStatusData != null)
{
@@ -1067,16 +1099,56 @@ namespace Game.Entities
UpdateCriteria(CriteriaType.AchieveSkillStep, id);
// update skill state
if (skillStatusData.State == SkillState.Unchanged)
if (skillStatusData.State == SkillState.Unchanged || skillStatusData.State == SkillState.Deleted)
{
if (currVal == 0) // activated skill, mark as new to save into database
{
skillStatusData.State = SkillState.New;
// Set profession line
int freeProfessionSlot = FindEmptyProfessionSlotFor(id);
if (freeProfessionSlot != -1)
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, freeProfessionSlot), id);
refreshSkillBonusAuras();
}
else // updated skill, mark as changed to save into database
skillStatusData.State = SkillState.Changed;
}
}
else if (currVal != 0 && newVal == 0) // Deactivate skill line
{
// Try to store profession tools and accessories into the bag
// If we can't, we can't unlearn the profession
int professionSlot = GetProfessionSlotFor(id);
if (professionSlot != -1)
{
byte professionSlotStart = (byte)(ProfessionSlots.Profession1Tool + professionSlot * ProfessionSlots.MaxCount);
// Get all profession items equipped
for (byte slotOffset = 0; slotOffset < ProfessionSlots.MaxCount; ++slotOffset)
{
Item professionItem = GetItemByPos(InventorySlots.Bag0, (byte)(professionSlotStart + slotOffset));
if (professionItem != null)
{
// Store item in bag
List<ItemPosCount> professionItemDest = new();
if (CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, professionItemDest, professionItem, false) != InventoryResult.Ok)
{
SendPacket(new DisplayGameError(GameError.InvFull));
return;
}
RemoveItem(InventorySlots.Bag0, professionItem.GetSlot(), true);
StoreItem(professionItemDest, professionItem, true);
}
}
// Clear profession lines
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, professionSlot), 0u);
}
//remove enchantments needing this skill
UpdateSkillEnchantments(id, currVal, 0);
// clear skill fields
@@ -1088,10 +1160,8 @@ namespace Game.Entities
SetSkillPermBonus(skillStatusData.Pos, 0);
// mark as deleted so the next save will delete the data from the database
if (skillStatusData.State != SkillState.New)
skillStatusData.State = SkillState.Deleted;
else
skillStatusData.State = SkillState.Unchanged;
skillStatusData.State = SkillState.Deleted;
// remove all spells that related to this skill
List<SkillLineAbilityRecord> skillLineAbilities = Global.DB2Mgr.GetSkillLineAbilitiesBySkill(id);
@@ -1107,12 +1177,6 @@ namespace Game.Entities
SetSkill(childSkillLine.Id, 0, 0, 0);
}
}
// Clear profession lines
if (m_activePlayerData.ProfessionSkillLine[0] == id)
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, 0), 0u);
else if (m_activePlayerData.ProfessionSkillLine[1] == id)
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, 1), 0u);
}
}
else
@@ -1136,13 +1200,6 @@ namespace Game.Entities
return;
}
SkillLineRecord skillEntry = CliDB.SkillLineStorage.LookupByKey(id);
if (skillEntry == null)
{
Log.outError(LogFilter.Misc, $"Player.SetSkill: Skill (SkillID: {id}) not found in SkillLineStore for player '{GetName()}' ({GetGUID()})");
return;
}
if (skillEntry.ParentSkillLineID != 0)
{
if (skillEntry.ParentTierIndex > 0)
@@ -1168,12 +1225,9 @@ namespace Game.Entities
if (!HasSkill((SkillType)childSkillLine.Id))
SetSkill(childSkillLine.Id, 0, 0, 0);
if (skillEntry.CategoryID == SkillCategory.Profession)
{
int freeProfessionSlot = FindProfessionSlotFor(id);
if (freeProfessionSlot != -1)
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, freeProfessionSlot), id);
}
int freeProfessionSlot = FindEmptyProfessionSlotFor(id);
if (freeProfessionSlot != -1)
SetUpdateFieldValue(ref m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.ProfessionSkillLine, freeProfessionSlot), id);
}
if (skillStatusData == null)
@@ -1194,19 +1248,7 @@ namespace Game.Entities
if (newVal != 0)
{
// temporary bonuses
foreach (var auraEffect in GetAuraEffectsByType(AuraType.ModSkill))
if (auraEffect.GetMiscValue() == id)
auraEffect.HandleEffect(this, AuraEffectHandleModes.Skill, true);
foreach (var auraEffect in GetAuraEffectsByType(AuraType.ModSkill2))
if (auraEffect.GetMiscValue() == id)
auraEffect.HandleEffect(this, AuraEffectHandleModes.Skill, true);
// permanent bonuses
foreach (var auraEffect in GetAuraEffectsByType(AuraType.ModSkillTalent))
if (auraEffect.GetMiscValue() == id)
auraEffect.HandleEffect(this, AuraEffectHandleModes.Skill, true);
refreshSkillBonusAuras();
// Learn all spells for skill
LearnSkillRewardedSpells(id, newVal, GetRace());
@@ -1609,12 +1651,24 @@ namespace Game.Entities
}
}
int FindProfessionSlotFor(uint skillId)
int GetProfessionSlotFor(uint skillId)
{
for (var i = 0; i < m_activePlayerData.ProfessionSkillLine.GetSize(); ++i)
if (m_activePlayerData.ProfessionSkillLine[i] == skillId)
return i;
return -1;
}
int FindEmptyProfessionSlotFor(uint skillId)
{
SkillLineRecord skillEntry = CliDB.SkillLineStorage.LookupByKey(skillId);
if (skillEntry == null)
return -1;
if (skillEntry.ParentSkillLineID != 0 || skillEntry.CategoryID != SkillCategory.Profession)
return -1;
int index = 0;
// if there is no same profession, find any free slot
foreach (var b in m_activePlayerData.ProfessionSkillLine)