diff options
author | Ovahlord <dreadkiller@gmx.de> | 2024-06-01 17:45:20 +0200 |
---|---|---|
committer | Ovahlord <dreadkiller@gmx.de> | 2024-06-01 19:32:24 +0200 |
commit | 57c36d758fabadd0385cde75b5b6abdcd8c493dd (patch) | |
tree | 5a9ef5b1822347cda950fc2c1cf9e0d900c8c769 | |
parent | d4c36db8aca9b3da8c589ac313c230763687f171 (diff) |
Core/Items: implement reforging items
23 files changed, 367 insertions, 198 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 13c47a14fdf..02bb7a2153f 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -2689,6 +2689,7 @@ CREATE TABLE `item_instance_modifiers` ( `itemGuid` bigint unsigned NOT NULL, `fixedScalingLevel` int unsigned DEFAULT '0', `artifactKnowledgeLevel` int unsigned DEFAULT '0', + `itemReforgeId` int unsigned DEFAULT '0', PRIMARY KEY (`itemGuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -3477,7 +3478,8 @@ INSERT INTO `updates` VALUES ('2024_03_13_00_characters.sql','6360F50059E5DB1F248FA8A270CDC4788A03A0EC','RELEASED','2024-03-13 17:10:46',0), ('2024_03_19_00_characters.sql','1D200630578074A4E3A373F891323DB867D00B02','RELEASED','2024-03-19 19:59:26',0), ('2024_04_09_00_characters.sql','07AC79B4E489B1CD073852EC57D12939C2A1D4B1','RELEASED','2024-04-09 12:54:11',0), -('2024_04_12_00_characters.sql','043E023F998DA77170C9D2D0162CAA340290B215','RELEASED','2024-04-12 00:23:51',0); +('2024_04_12_00_characters.sql','043E023F998DA77170C9D2D0162CAA340290B215','RELEASED','2024-04-12 00:23:51',0), +('2024_06_01_00_characters.sql','0440E0A8BE109101B390A640397D9B024C8E124B','RELEASED','2024-06-01 19:29:34',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/cata_classic/2024_06_01_00_characters.sql b/sql/updates/characters/cata_classic/2024_06_01_00_characters.sql new file mode 100644 index 00000000000..143e3e1e620 --- /dev/null +++ b/sql/updates/characters/cata_classic/2024_06_01_00_characters.sql @@ -0,0 +1,2 @@ +ALTER TABLE `item_instance_modifiers` +ADD COLUMN `itemReforgeId` int UNSIGNED NULL DEFAULT 0 AFTER `artifactKnowledgeLevel`; diff --git a/sql/updates/hotfixes/cata_classic/2024_06_01_00_hotfixes.sql b/sql/updates/hotfixes/cata_classic/2024_06_01_00_hotfixes.sql new file mode 100644 index 00000000000..504dea84a37 --- /dev/null +++ b/sql/updates/hotfixes/cata_classic/2024_06_01_00_hotfixes.sql @@ -0,0 +1,27 @@ +-- +-- Table structure for table `item_reforge` +-- + +DROP TABLE IF EXISTS `item_reforge`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `item_reforge` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `SourceStat` smallint(5) unsigned NOT NULL DEFAULT '0', + `SourceMultiplier` float NOT NULL DEFAULT '0', + `TargetStat` smallint(5) unsigned NOT NULL DEFAULT '0', + `TargetMultiplier` float NOT NULL DEFAULT '0', + `LegacyItemReforgeID` smallint(5) unsigned NOT NULL DEFAULT '0', + `VerifiedBuild` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `item_reforge` +-- + +LOCK TABLES `item_reforge` WRITE; +/*!40000 ALTER TABLE `item_reforge` DISABLE KEYS */; +/*!40000 ALTER TABLE `item_reforge` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/sql/updates/world/cata_classic/2024_06_01_00_world.sql b/sql/updates/world/cata_classic/2024_06_01_00_world.sql new file mode 100644 index 00000000000..cc92a1149be --- /dev/null +++ b/sql/updates/world/cata_classic/2024_06_01_00_world.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `npcflag`= (0x00400000 << 32) WHERE `entry` IN (54441, 54471); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 64fef7f49f4..4bfe2a327a1 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -29,7 +29,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "iit.spellItemEnchantmentAllSpecs, iit.spellItemEnchantmentSpec1, iit.spellItemEnchantmentSpec2, iit.spellItemEnchantmentSpec3, iit.spellItemEnchantmentSpec4, iit.spellItemEnchantmentSpec5, " \ "iit.secondaryItemModifiedAppearanceAllSpecs, iit.secondaryItemModifiedAppearanceSpec1, iit.secondaryItemModifiedAppearanceSpec2, iit.secondaryItemModifiedAppearanceSpec3, iit.secondaryItemModifiedAppearanceSpec4, iit.secondaryItemModifiedAppearanceSpec5, " \ "ig.gemItemId1, ig.gemBonuses1, ig.gemContext1, ig.gemScalingLevel1, ig.gemItemId2, ig.gemBonuses2, ig.gemContext2, ig.gemScalingLevel2, ig.gemItemId3, ig.gemBonuses3, ig.gemContext3, ig.gemScalingLevel3, " \ - "im.fixedScalingLevel, im.artifactKnowledgeLevel" + "im.fixedScalingLevel, im.artifactKnowledgeLevel, im.itemReforgeId" PrepareStatement(CHAR_DEL_POOL_QUEST_SAVE, "DELETE FROM pool_quest_save WHERE pool_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_POOL_QUEST_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC); @@ -213,7 +213,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_ITEM_INSTANCE_ARTIFACT_POWERS, "INSERT INTO item_instance_artifact_powers (itemGuid, artifactPowerId, purchasedRank) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT_POWERS, "DELETE FROM item_instance_artifact_powers WHERE itemGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_ARTIFACT_POWERS_BY_OWNER, "DELETE iiap FROM item_instance_artifact_powers iiap LEFT JOIN item_instance ii ON iiap.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_ITEM_INSTANCE_MODIFIERS, "INSERT INTO item_instance_modifiers (itemGuid, fixedScalingLevel, artifactKnowledgeLevel) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_ITEM_INSTANCE_MODIFIERS, "INSERT INTO item_instance_modifiers (itemGuid, fixedScalingLevel, artifactKnowledgeLevel, itemReforgeId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS, "DELETE FROM item_instance_modifiers WHERE itemGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS_BY_OWNER, "DELETE im FROM item_instance_modifiers im LEFT JOIN item_instance ii ON im.itemGuid = ii.guid WHERE ii.owner_guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GIFT_OWNER, "UPDATE character_gifts SET guid = ? WHERE item_guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 8c5fc8ec88d..7547ce06034 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -928,6 +928,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_ITEM_PRICE_BASE, "SELECT ID, ItemLevel, Armor, Weapon FROM item_price_base WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_PRICE_BASE, "SELECT MAX(ID) + 1 FROM item_price_base", CONNECTION_SYNCH); + // ItemReforge.db2 + PrepareStatement(HOTFIX_SEL_ITEM_REFORGE, "SELECT ID, SourceStat, SourceMultiplier, TargetStat, TargetMultiplier, LegacyItemReforgeID" + " FROM item_reforge WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_ITEM_REFORGE, "SELECT MAX(ID) + 1 FROM item_reforge", CONNECTION_SYNCH); + // ItemSearchName.db2 PrepareStatement(HOTFIX_SEL_ITEM_SEARCH_NAME, "SELECT AllowableRace, Display, ID, OverallQualityID, ExpansionID, MinFactionID, MinReputation, " "AllowableClass, RequiredLevel, RequiredSkill, RequiredSkillRank, RequiredAbility, ItemLevel, Flags1, Flags2, Flags3, Flags4" diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index b68a2f58aae..a479832b0da 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -541,6 +541,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_ITEM_PRICE_BASE, HOTFIX_SEL_ITEM_PRICE_BASE_MAX_ID, + HOTFIX_SEL_ITEM_REFORGE, + HOTFIX_SEL_ITEM_REFORGE_MAX_ID, + HOTFIX_SEL_ITEM_SEARCH_NAME, HOTFIX_SEL_ITEM_SEARCH_NAME_MAX_ID, HOTFIX_SEL_ITEM_SEARCH_NAME_LOCALE, diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index feabfc035fe..a74b2880537 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -2916,6 +2916,21 @@ struct ItemPriceBaseLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 4, &ItemPriceBaseMeta::Instance, HOTFIX_SEL_ITEM_PRICE_BASE }; }; +struct ItemReforgeLoadInfo +{ + static constexpr DB2FieldMeta Fields[6] = + { + { false, FT_INT, "ID" }, + { false, FT_SHORT, "SourceStat" }, + { false, FT_FLOAT, "SourceMultiplier" }, + { false, FT_SHORT, "TargetStat" }, + { false, FT_FLOAT, "TargetMultiplier" }, + { false, FT_SHORT, "LegacyItemReforgeID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 6, &ItemReforgeMeta::Instance, HOTFIX_SEL_ITEM_REFORGE }; +}; + struct ItemSearchNameLoadInfo { static constexpr DB2FieldMeta Fields[17] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 821c9651353..d97549502ab 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -195,6 +195,7 @@ DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore("It DB2Storage<ItemModifiedAppearanceExtraEntry> sItemModifiedAppearanceExtraStore("ItemModifiedAppearanceExtra.db2", &ItemModifiedAppearanceExtraLoadInfo::Instance); DB2Storage<ItemNameDescriptionEntry> sItemNameDescriptionStore("ItemNameDescription.db2", &ItemNameDescriptionLoadInfo::Instance); DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore("ItemPriceBase.db2", &ItemPriceBaseLoadInfo::Instance); +DB2Storage<ItemReforgeEntry> sItemReforgeStore("ItemReforge.db2", &ItemReforgeLoadInfo::Instance); DB2Storage<ItemSearchNameEntry> sItemSearchNameStore("ItemSearchName.db2", &ItemSearchNameLoadInfo::Instance); DB2Storage<ItemSetEntry> sItemSetStore("ItemSet.db2", &ItemSetLoadInfo::Instance); DB2Storage<ItemSetSpellEntry> sItemSetSpellStore("ItemSetSpell.db2", &ItemSetSpellLoadInfo::Instance); @@ -774,6 +775,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sItemModifiedAppearanceExtraStore); LOAD_DB2(sItemNameDescriptionStore); LOAD_DB2(sItemPriceBaseStore); + LOAD_DB2(sItemReforgeStore); LOAD_DB2(sItemSearchNameStore); LOAD_DB2(sItemSetStore); LOAD_DB2(sItemSetSpellStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index a5c9f4348a6..67f56064f54 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -159,6 +159,7 @@ TC_GAME_API extern DB2Storage<ItemLimitCategoryEntry> sItemLimitCa TC_GAME_API extern DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore; TC_GAME_API extern DB2Storage<ItemModifiedAppearanceExtraEntry> sItemModifiedAppearanceExtraStore; TC_GAME_API extern DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore; +TC_GAME_API extern DB2Storage<ItemReforgeEntry> sItemReforgeStore; TC_GAME_API extern DB2Storage<ItemSearchNameEntry> sItemSearchNameStore; TC_GAME_API extern DB2Storage<ItemSetEntry> sItemSetStore; TC_GAME_API extern DB2Storage<ItemSetSpellEntry> sItemSetSpellStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 242a1140cb6..380110ec140 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -2242,6 +2242,16 @@ struct ItemPriceBaseEntry float Weapon; }; +struct ItemReforgeEntry +{ + uint32 ID; + uint16 SourceStat; + float SourceMultiplier; + uint16 TargetStat; + float TargetMultiplier; + uint16 LegacyItemReforgeID; +}; + struct ItemSearchNameEntry { Trinity::RaceMask<int64> AllowableRace; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index a83b6ea5c5b..6e3581cf9b1 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -650,7 +650,8 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans) static ItemModifier const modifiersTable[] = { ITEM_MODIFIER_TIMEWALKER_LEVEL, - ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL + ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL, + ITEM_MODIFIER_REFORGE, }; stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_MODIFIERS); @@ -663,6 +664,7 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans) stmt->setUInt64(0, GetGUID().GetCounter()); stmt->setUInt32(1, GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL)); stmt->setUInt32(2, GetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL)); + stmt->setUInt32(3, GetModifier(ITEM_MODIFIER_REFORGE)); trans->Append(stmt); } @@ -737,8 +739,8 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie // secondaryItemModifiedAppearanceSpec3, secondaryItemModifiedAppearanceSpec4, secondaryItemModifiedAppearanceSpec5, // 38 39 40 41 42 43 44 45 46 47 48 49 // gemItemId1, gemBonuses1, gemContext1, gemScalingLevel1, gemItemId2, gemBonuses2, gemContext2, gemScalingLevel2, gemItemId3, gemBonuses3, gemContext3, gemScalingLevel3 - // 50 51 - // fixedScalingLevel, artifactKnowledgeLevel FROM item_instance + // 50 51 52 + // fixedScalingLevel, artifactKnowledgeLevel, itemReforgeId FROM item_instance // create item before any checks for store correct guid // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB @@ -861,6 +863,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie SetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL, fields[50].GetUInt32()); SetModifier(ITEM_MODIFIER_ARTIFACT_KNOWLEDGE_LEVEL, fields[51].GetUInt32()); + SetModifier(ITEM_MODIFIER_REFORGE, fields[52].GetUInt32()); // Enchants must be loaded after all other bonus/scaling data std::vector<std::string_view> enchantmentTokens = Trinity::Tokenize(fields[8].GetStringView(), ' ', false); @@ -1559,6 +1562,7 @@ Item* Item::CloneItem(uint32 count, Player const* player /*= nullptr*/) const newItem->SetExpiration(m_itemData->Expiration); newItem->SetBonuses(m_itemData->ItemBonusKey->BonusListIDs); newItem->SetFixedLevel(GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL)); + newItem->SetReforgeId(GetModifier(ITEM_MODIFIER_REFORGE)); // player CAN be NULL in which case we must not update random properties because that accesses player's item update queue if (player) newItem->SetItemRandomBonusList(m_randomBonusListId); @@ -2665,6 +2669,19 @@ void Item::SetFixedLevel(uint8 level) } } +void Item::SetReforgeId(uint32 itemReforceRecId) +{ + if (itemReforceRecId == GetModifier(ITEM_MODIFIER_REFORGE)) + return; + + Player* owner = GetOwner(); + if (!owner) + return; + + SetModifier(ITEM_MODIFIER_REFORGE, itemReforceRecId); + SetState(ITEM_CHANGED, owner); +} + int32 Item::GetRequiredLevel() const { int32 fixedLevel = GetModifier(ITEM_MODIFIER_TIMEWALKER_LEVEL); diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 10f5b38063e..0726a72ac08 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -324,6 +324,7 @@ class TC_GAME_API Item : public Object ItemDisenchantLootEntry const* GetDisenchantLoot(Player const* owner) const; static ItemDisenchantLootEntry const* GetDisenchantLoot(ItemTemplate const* itemTemplate, uint32 quality, uint32 itemLevel); void SetFixedLevel(uint8 level); + void SetReforgeId(uint32 itemReforceRecId); Trinity::IteratorPair<ItemEffectEntry const* const*> GetEffects() const { return { std::make_pair(&_bonusData.Effects[0], &_bonusData.Effects[0] + _bonusData.EffectCount) }; } // Item Refund system diff --git a/src/server/game/Entities/Item/ItemDefines.h b/src/server/game/Entities/Item/ItemDefines.h index 7df3f83b9f7..0632c1e113d 100644 --- a/src/server/game/Entities/Item/ItemDefines.h +++ b/src/server/game/Entities/Item/ItemDefines.h @@ -266,6 +266,7 @@ enum ItemModifier : uint16 ITEM_MODIFIER_CRAFTING_REAGENT_SLOT_12 = 55, ITEM_MODIFIER_CRAFTING_REAGENT_SLOT_13 = 56, ITEM_MODIFIER_CRAFTING_REAGENT_SLOT_14 = 57, + ITEM_MODIFIER_REFORGE = 58, MAX_ITEM_MODIFIERS }; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a881fb0a2bd..df583ff9f6f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7715,6 +7715,7 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply, bool updateItemA } ApplyArtifactPowers(item, apply); ApplyEnchantment(item, apply); + ApplyReforgedStats(item, apply); TC_LOG_DEBUG("entities.player.items", "Player::_ApplyItemMods: completed"); } @@ -8643,6 +8644,7 @@ void Player::_RemoveAllItemMods() ApplyItemEquipSpell(m_items[i], false); ApplyEnchantment(m_items[i], false); ApplyArtifactPowers(m_items[i], false); + ApplyReforgedStats(m_items[i], false); } } @@ -8699,6 +8701,7 @@ void Player::_ApplyAllItemMods() ApplyItemEquipSpell(m_items[i], true); ApplyArtifactPowers(m_items[i], true); ApplyEnchantment(m_items[i], true); + ApplyReforgedStats(m_items[i], true); } } @@ -13229,6 +13232,164 @@ void Player::ApplyEnchantment(Item* item, bool apply) ApplyEnchantment(item, EnchantmentSlot(slot), apply); } +void Player::ApplyItemModModifier(ItemModType modifier, int32 amount, bool apply) +{ + switch (modifier) + { + case ITEM_MOD_MANA: + HandleStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, float(amount), apply); + break; + case ITEM_MOD_HEALTH: + HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(amount), apply); + break; + case ITEM_MOD_AGILITY: + HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(amount), apply); + UpdateStatBuffMod(STAT_AGILITY); + break; + case ITEM_MOD_STRENGTH: + HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(amount), apply); + UpdateStatBuffMod(STAT_STRENGTH); + break; + case ITEM_MOD_INTELLECT: + HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(amount), apply); + UpdateStatBuffMod(STAT_INTELLECT); + break; + case ITEM_MOD_SPIRIT: + HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(amount), apply); + UpdateStatBuffMod(STAT_SPIRIT); + break; + case ITEM_MOD_STAMINA: + HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(amount), apply); + UpdateStatBuffMod(STAT_STAMINA); + break; + case ITEM_MOD_DEFENSE_SKILL_RATING: + ApplyRatingMod(CR_DEFENSE_SKILL, amount, apply); + break; + case ITEM_MOD_DODGE_RATING: + ApplyRatingMod(CR_DODGE, amount, apply); + break; + case ITEM_MOD_PARRY_RATING: + ApplyRatingMod(CR_PARRY, amount, apply); + break; + case ITEM_MOD_BLOCK_RATING: + ApplyRatingMod(CR_BLOCK, amount, apply); + break; + case ITEM_MOD_HIT_MELEE_RATING: + ApplyRatingMod(CR_HIT_MELEE, amount, apply); + break; + case ITEM_MOD_HIT_RANGED_RATING: + ApplyRatingMod(CR_HIT_RANGED, amount, apply); + break; + case ITEM_MOD_HIT_SPELL_RATING: + ApplyRatingMod(CR_HIT_SPELL, amount, apply); + break; + case ITEM_MOD_CRIT_MELEE_RATING: + ApplyRatingMod(CR_CRIT_MELEE, amount, apply); + break; + case ITEM_MOD_CRIT_RANGED_RATING: + ApplyRatingMod(CR_CRIT_RANGED, amount, apply); + break; + case ITEM_MOD_CRIT_SPELL_RATING: + ApplyRatingMod(CR_CRIT_SPELL, amount, apply); + break; + // Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used + // in Enchantments + // case ITEM_MOD_HIT_TAKEN_MELEE_RATING: + // ApplyRatingMod(CR_HIT_TAKEN_MELEE, amount, apply); + // break; + // case ITEM_MOD_HIT_TAKEN_RANGED_RATING: + // ApplyRatingMod(CR_HIT_TAKEN_RANGED, amount, apply); + // break; + // case ITEM_MOD_HIT_TAKEN_SPELL_RATING: + // ApplyRatingMod(CR_HIT_TAKEN_SPELL, amount, apply); + // break; + // case ITEM_MOD_CRIT_TAKEN_MELEE_RATING: + // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, amount, apply); + // break; + // case ITEM_MOD_CRIT_TAKEN_RANGED_RATING: + // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, amount, apply); + // break; + // case ITEM_MOD_CRIT_TAKEN_SPELL_RATING: + // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, amount, apply); + // break; + // case ITEM_MOD_HASTE_MELEE_RATING: + // ApplyRatingMod(CR_HASTE_MELEE, amount, apply); + // break; + // case ITEM_MOD_HASTE_RANGED_RATING: + // ApplyRatingMod(CR_HASTE_RANGED, amount, apply); + // break; + case ITEM_MOD_HASTE_SPELL_RATING: + ApplyRatingMod(CR_HASTE_SPELL, amount, apply); + break; + case ITEM_MOD_HIT_RATING: + ApplyRatingMod(CR_HIT_MELEE, amount, apply); + ApplyRatingMod(CR_HIT_RANGED, amount, apply); + ApplyRatingMod(CR_HIT_SPELL, amount, apply); + break; + case ITEM_MOD_CRIT_RATING: + ApplyRatingMod(CR_CRIT_MELEE, amount, apply); + ApplyRatingMod(CR_CRIT_RANGED, amount, apply); + ApplyRatingMod(CR_CRIT_SPELL, amount, apply); + break; + // case ITEM_MOD_HIT_TAKEN_RATING: // Unused since 3.3.5 + // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply); + // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply); + // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply); + // break; + // case ITEM_MOD_CRIT_TAKEN_RATING: // Unused since 3.3.5 + // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply); + // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply); + // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply); + // break; + case ITEM_MOD_RESILIENCE_RATING: + ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE, amount, apply); + break; + case ITEM_MOD_HASTE_RATING: + ApplyRatingMod(CR_HASTE_MELEE, amount, apply); + ApplyRatingMod(CR_HASTE_RANGED, amount, apply); + ApplyRatingMod(CR_HASTE_SPELL, amount, apply); + break; + case ITEM_MOD_EXPERTISE_RATING: + ApplyRatingMod(CR_EXPERTISE, amount, apply); + break; + case ITEM_MOD_ATTACK_POWER: + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(amount), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(amount), apply); + break; + case ITEM_MOD_RANGED_ATTACK_POWER: + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(amount), apply); + break; + case ITEM_MOD_MANA_REGENERATION: + ApplyManaRegenBonus(amount, apply); + break; + case ITEM_MOD_ARMOR_PENETRATION_RATING: + ApplyRatingMod(CR_ARMOR_PENETRATION, amount, apply); + break; + case ITEM_MOD_SPELL_POWER: + ApplySpellPowerBonus(amount, apply); + break; + case ITEM_MOD_HEALTH_REGEN: + ApplyHealthRegenBonus(amount, apply); + break; + case ITEM_MOD_SPELL_PENETRATION: + ApplySpellPenetrationBonus(amount, apply); + break; + case ITEM_MOD_BLOCK_VALUE: + HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(amount), apply); + break; + case ITEM_MOD_MASTERY_RATING: + ApplyRatingMod(CR_MASTERY, amount, apply); + break; + case ITEM_MOD_VERSATILITY: + ApplyRatingMod(CR_VERSATILITY_DAMAGE_DONE, amount, apply); + ApplyRatingMod(CR_VERSATILITY_HEALING_DONE, amount, apply); + ApplyRatingMod(CR_VERSATILITY_DAMAGE_TAKEN, amount, apply); + break; + default: + break; + } +} + void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool apply_dur, bool ignore_condition) { if (!item || !item->IsEquipped()) @@ -13311,194 +13472,8 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool case ITEM_ENCHANTMENT_TYPE_STAT: { enchant_amount = std::max(enchant_amount, 1u); - TC_LOG_DEBUG("entities.player.items", "Adding {} to stat nb {}", enchant_amount, enchant_spell_id); - switch (enchant_spell_id) - { - case ITEM_MOD_MANA: - TC_LOG_DEBUG("entities.player.items", "+ {} MANA", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply); - break; - case ITEM_MOD_HEALTH: - TC_LOG_DEBUG("entities.player.items", "+ {} HEALTH", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply); - break; - case ITEM_MOD_AGILITY: - TC_LOG_DEBUG("entities.player.items", "+ {} AGILITY", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_AGILITY); - break; - case ITEM_MOD_STRENGTH: - TC_LOG_DEBUG("entities.player.items", "+ {} STRENGTH", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_STRENGTH); - break; - case ITEM_MOD_INTELLECT: - TC_LOG_DEBUG("entities.player.items", "+ {} INTELLECT", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_INTELLECT); - break; - // case ITEM_MOD_SPIRIT: - TC_LOG_DEBUG("entities.player.items", "+ {} SPIRIT", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_SPIRIT); - break; - case ITEM_MOD_STAMINA: - TC_LOG_DEBUG("entities.player.items", "+ {} STAMINA", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_STAMINA); - break; - case ITEM_MOD_DEFENSE_SKILL_RATING: - ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} DEFENSE", enchant_amount); - break; - case ITEM_MOD_DODGE_RATING: - ApplyRatingMod(CR_DODGE, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} DODGE", enchant_amount); - break; - case ITEM_MOD_PARRY_RATING: - ApplyRatingMod(CR_PARRY, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} PARRY", enchant_amount); - break; - case ITEM_MOD_BLOCK_RATING: - ApplyRatingMod(CR_BLOCK, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} SHIELD_BLOCK", enchant_amount); - break; - case ITEM_MOD_HIT_MELEE_RATING: - ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} MELEE_HIT", enchant_amount); - break; - case ITEM_MOD_HIT_RANGED_RATING: - ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} RANGED_HIT", enchant_amount); - break; - case ITEM_MOD_HIT_SPELL_RATING: - ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} SPELL_HIT", enchant_amount); - break; - case ITEM_MOD_CRIT_MELEE_RATING: - ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} MELEE_CRIT", enchant_amount); - break; - case ITEM_MOD_CRIT_RANGED_RATING: - ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} RANGED_CRIT", enchant_amount); - break; - case ITEM_MOD_CRIT_SPELL_RATING: - ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} SPELL_CRIT", enchant_amount); - break; - // Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used - // in Enchantments - // case ITEM_MOD_HIT_TAKEN_MELEE_RATING: - // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply); - // break; - // case ITEM_MOD_HIT_TAKEN_RANGED_RATING: - // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply); - // break; - // case ITEM_MOD_HIT_TAKEN_SPELL_RATING: - // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply); - // break; - // case ITEM_MOD_CRIT_TAKEN_MELEE_RATING: - // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply); - // break; - // case ITEM_MOD_CRIT_TAKEN_RANGED_RATING: - // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply); - // break; - // case ITEM_MOD_CRIT_TAKEN_SPELL_RATING: - // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply); - // break; - // case ITEM_MOD_HASTE_MELEE_RATING: - // ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply); - // break; - // case ITEM_MOD_HASTE_RANGED_RATING: - // ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply); - // break; - case ITEM_MOD_HASTE_SPELL_RATING: - ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply); - break; - case ITEM_MOD_HIT_RATING: - ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply); - ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply); - ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} HIT", enchant_amount); - break; - case ITEM_MOD_CRIT_RATING: - ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply); - ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply); - ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} CRITICAL", enchant_amount); - break; - // case ITEM_MOD_HIT_TAKEN_RATING: // Unused since 3.3.5 - // ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply); - // ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply); - // ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply); - // break; - // case ITEM_MOD_CRIT_TAKEN_RATING: // Unused since 3.3.5 - // ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply); - // ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply); - // ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply); - // break; - case ITEM_MOD_RESILIENCE_RATING: - ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} RESILIENCE", enchant_amount); - break; - case ITEM_MOD_HASTE_RATING: - ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply); - ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply); - ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} HASTE", enchant_amount); - break; - case ITEM_MOD_EXPERTISE_RATING: - ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} EXPERTISE", enchant_amount); - break; - case ITEM_MOD_ATTACK_POWER: - HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply); - HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); - TC_LOG_DEBUG("entities.player.items", "+ {} ATTACK_POWER", enchant_amount); - break; - case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); - TC_LOG_DEBUG("entities.player.items", "+ {} RANGED_ATTACK_POWER", enchant_amount); - break; - case ITEM_MOD_MANA_REGENERATION: - ApplyManaRegenBonus(enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} MANA_REGENERATION", enchant_amount); - break; - case ITEM_MOD_ARMOR_PENETRATION_RATING: - ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} ARMOR PENETRATION", enchant_amount); - break; - case ITEM_MOD_SPELL_POWER: - ApplySpellPowerBonus(enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} SPELL_POWER", enchant_amount); - break; - case ITEM_MOD_HEALTH_REGEN: - ApplyHealthRegenBonus(enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} HEALTH_REGENERATION", enchant_amount); - break; - case ITEM_MOD_SPELL_PENETRATION: - ApplySpellPenetrationBonus(enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} SPELL_PENETRATION", enchant_amount); - break; - case ITEM_MOD_BLOCK_VALUE: - HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(enchant_amount), apply); - TC_LOG_DEBUG("entities.player.items", "+ {} BLOCK_VALUE", enchant_amount); - break; - case ITEM_MOD_MASTERY_RATING: - ApplyRatingMod(CR_MASTERY, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} MASTERY", enchant_amount); - break; - case ITEM_MOD_VERSATILITY: - ApplyRatingMod(CR_VERSATILITY_DAMAGE_DONE, enchant_amount, apply); - ApplyRatingMod(CR_VERSATILITY_HEALING_DONE, enchant_amount, apply); - ApplyRatingMod(CR_VERSATILITY_DAMAGE_TAKEN, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ {} VERSATILITY", enchant_amount); - break; - default: - break; - } + ApplyItemModModifier(static_cast<ItemModType>(enchant_spell_id), enchant_amount, apply); break; } case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon @@ -13548,6 +13523,37 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool } } +void Player::ApplyReforgedStats(Item* item, bool apply) +{ + // No reforged stats to remove or add + if (!item->GetModifier(ITEM_MODIFIER_REFORGE)) + return; + + ItemReforgeEntry const* itemReforge = sItemReforgeStore.LookupEntry(item->GetModifier(ITEM_MODIFIER_REFORGE)); + if (!itemReforge) + return; + + float sourceValue = [&]() + { + for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) + { + if (item->GetItemStatType(i) == static_cast<ItemModType>(itemReforge->SourceStat)) + return item->GetItemStatValue(i, this); + } + + return 0.0f; + }(); + + if (sourceValue == 0.0f) + return; + + float removeValue = sourceValue * itemReforge->SourceMultiplier; + float addValue = removeValue * itemReforge->TargetMultiplier; + + ApplyItemModModifier(static_cast<ItemModType>(itemReforge->SourceStat), -removeValue, apply); + ApplyItemModModifier(static_cast<ItemModType>(itemReforge->TargetStat), addValue, apply); +} + void Player::UpdateSkillEnchantments(uint16 skill_id, uint16 curr_value, uint16 new_value) { for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) @@ -18391,9 +18397,9 @@ void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult arti // secondaryItemModifiedAppearanceSpec3, secondaryItemModifiedAppearanceSpec4, secondaryItemModifiedAppearanceSpec5, // 38 39 40 41 42 43 44 45 46 47 48 49 // gemItemId1, gemBonuses1, gemContext1, gemScalingLevel1, gemItemId2, gemBonuses2, gemContext2, gemScalingLevel2, gemItemId3, gemBonuses3, gemContext3, gemScalingLevel3 - // 50 51 - // fixedScalingLevel, artifactKnowledgeLevel FROM item_instance - // 52 53 + // 50 51 52 + // fixedScalingLevel, artifactKnowledgeLevel, itemReforgeId FROM item_instance + // 53 54 // bag, slot // FROM character_inventory ci // JOIN item_instance ii ON ci.item = ii.guid @@ -18435,8 +18441,8 @@ void Player::_LoadInventory(PreparedQueryResult result, PreparedQueryResult arti addionalDataPtr->Artifact->ArtifactTierId, addionalDataPtr->Artifact->ArtifactPowers); } - ObjectGuid bagGuid = fields[52].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[52].GetUInt64()) : ObjectGuid::Empty; - uint8 slot = fields[53].GetUInt8(); + ObjectGuid bagGuid = fields[53].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[53].GetUInt64()) : ObjectGuid::Empty; + uint8 slot = fields[54].GetUInt8(); GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item); GetSession()->GetCollectionMgr()->AddItemAppearance(item); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 30cbdb2b762..dc0f5219b5d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -100,6 +100,7 @@ enum class InstanceResetMethod : uint8; enum class InstanceResetResult : uint8; enum InventoryType : uint8; enum ItemClass : uint8; +enum ItemModType; enum LootError : uint8; enum LootType : uint8; enum class MovementStopReason : uint8; @@ -1500,8 +1501,10 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void RemoveEnchantmentDurationsReferences(Item* item); void RemoveArenaEnchantments(EnchantmentSlot slot); void AddEnchantmentDuration(Item* item, EnchantmentSlot slot, uint32 duration); + void ApplyItemModModifier(ItemModType modifier, int32 amount, bool apply); void ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool apply_dur = true, bool ignore_condition = false); void ApplyEnchantment(Item* item, bool apply); + void ApplyReforgedStats(Item* item, bool apply); void UpdateSkillEnchantments(uint16 skill_id, uint16 curr_value, uint16 new_value); void SendEnchantmentDurations(); void AddItemDurations(Item* item); diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index 3db45d62329..4bbb68fbcb9 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -349,6 +349,7 @@ enum NPCFlags2 : uint32 UNIT_NPC_FLAG_2_ISLANDS_QUEUE = 0x00008000, // TITLE is islands queue UNIT_NPC_FLAG_2_SUPPRESS_NPC_SOUNDS_EXCEPT_END_OF_INTERACTION = 0x00010000, UNIT_NPC_FLAG_2_PERSONAL_TABARD_DESIGNER = 0x00200000, // TITLE is personal tabard designer + UNIT_NPC_FLAG_2_REFORGER = 0x00400000, // TITLE is reforger }; DEFINE_ENUM_FLAG(NPCFlags2); diff --git a/src/server/game/Entities/Unit/enuminfo_UnitDefines.cpp b/src/server/game/Entities/Unit/enuminfo_UnitDefines.cpp index 368b389789d..db67d97fc35 100644 --- a/src/server/game/Entities/Unit/enuminfo_UnitDefines.cpp +++ b/src/server/game/Entities/Unit/enuminfo_UnitDefines.cpp @@ -566,6 +566,7 @@ TC_API_EXPORT EnumText EnumUtils<NPCFlags2>::ToString(NPCFlags2 value) case UNIT_NPC_FLAG_2_ISLANDS_QUEUE: return { "UNIT_NPC_FLAG_2_ISLANDS_QUEUE", "is islands queue", "" }; case UNIT_NPC_FLAG_2_SUPPRESS_NPC_SOUNDS_EXCEPT_END_OF_INTERACTION: return { "UNIT_NPC_FLAG_2_SUPPRESS_NPC_SOUNDS_EXCEPT_END_OF_INTERACTION", "UNIT_NPC_FLAG_2_SUPPRESS_NPC_SOUNDS_EXCEPT_END_OF_INTERACTION", "" }; case UNIT_NPC_FLAG_2_PERSONAL_TABARD_DESIGNER: return { "UNIT_NPC_FLAG_2_PERSONAL_TABARD_DESIGNER", "is personal tabard designer", "" }; + case UNIT_NPC_FLAG_2_REFORGER: return { "UNIT_NPC_FLAG_2_REFORGER", "is reforger", "" }; default: throw std::out_of_range("value"); } } @@ -594,6 +595,7 @@ TC_API_EXPORT NPCFlags2 EnumUtils<NPCFlags2>::FromIndex(size_t index) case 13: return UNIT_NPC_FLAG_2_ISLANDS_QUEUE; case 14: return UNIT_NPC_FLAG_2_SUPPRESS_NPC_SOUNDS_EXCEPT_END_OF_INTERACTION; case 15: return UNIT_NPC_FLAG_2_PERSONAL_TABARD_DESIGNER; + case 16: return UNIT_NPC_FLAG_2_REFORGER; default: throw std::out_of_range("index"); } } @@ -619,6 +621,7 @@ TC_API_EXPORT size_t EnumUtils<NPCFlags2>::ToIndex(NPCFlags2 value) case UNIT_NPC_FLAG_2_ISLANDS_QUEUE: return 13; case UNIT_NPC_FLAG_2_SUPPRESS_NPC_SOUNDS_EXCEPT_END_OF_INTERACTION: return 14; case UNIT_NPC_FLAG_2_PERSONAL_TABARD_DESIGNER: return 15; + case UNIT_NPC_FLAG_2_REFORGER: return 16; default: throw std::out_of_range("value"); } } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index c95463666b1..7224191b9dd 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -1256,3 +1256,48 @@ void WorldSession::HandleRemoveNewItem(WorldPackets::Item::RemoveNewItem& remove item->SetState(ITEM_CHANGED, _player); } } + +void WorldSession::HandleReforgeItem(WorldPackets::Item::ReforgeItem& reforgeItem) +{ + if (!_player->GetNPCIfCanInteractWith(reforgeItem.ReforgerGUID, UNIT_NPC_FLAG_NONE, UNIT_NPC_FLAG_2_REFORGER)) + { + TC_LOG_DEBUG("network", "WorldSession::HandleReforgeItem - Reforger {} not found or player can't interact with it (missing UNIT_NPC_FLAG_2_REFORGER)!", reforgeItem.ReforgerGUID.ToString()); + return; + } + + Item* item = _player->GetItemByPos(reforgeItem.ContainerId, reforgeItem.SlotNum); + if (!item) + { + TC_LOG_DEBUG("network", "WorldSession::HandleReforgeItem: {} tried to reforge a non-existing item! Possible cheater or malformed packet.", GetPlayerInfo()); + return; + } + + // Items must at least be of item level 200 to be allowed to get reforged + if (item->GetItemLevel(_player) < 200) + return; + + if (reforgeItem.ItemReforgeRecId != 0) + { + if (!sItemReforgeStore.HasRecord(reforgeItem.ItemReforgeRecId)) + { + TC_LOG_DEBUG("network", "WorldSession::HandleReforgeItem: {} tried to reforge an item with a non-existing reforge entry! Possible cheater or malformed packet.", GetPlayerInfo()); + return; + } + + if (!_player->HasEnoughMoney(uint64(item->GetTemplate()->GetSellPrice()))) + { + _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, nullptr, 0, 0); + return; + } + else + _player->ModifyMoney(-int64(item->GetTemplate()->GetSellPrice())); + } + + if (item->IsEquipped()) + _player->ApplyReforgedStats(item, false); + + item->SetReforgeId(reforgeItem.ItemReforgeRecId); + + if (item->IsEquipped()) + _player->ApplyReforgedStats(item, true); +} diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp index 010ab6f5a07..45c2946e7aa 100644 --- a/src/server/game/Server/Packets/ItemPackets.cpp +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -361,3 +361,11 @@ void WorldPackets::Item::RemoveNewItem::Read() { _worldPacket >> ItemGuid; } + +void WorldPackets::Item::ReforgeItem::Read() +{ + _worldPacket >> ReforgerGUID; + _worldPacket >> ContainerId; + _worldPacket >> SlotNum; + _worldPacket >> ItemReforgeRecId; +} diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h index 23acbcd3af3..449af7b5009 100644 --- a/src/server/game/Server/Packets/ItemPackets.h +++ b/src/server/game/Server/Packets/ItemPackets.h @@ -532,6 +532,19 @@ namespace WorldPackets WorldPacket const* Write() override { return &_worldPacket; } }; + + class ReforgeItem final : public ClientPacket + { + public: + ReforgeItem(WorldPacket&& packet) : ClientPacket(CMSG_REFORGE_ITEM, std::move(packet)) { } + + void Read() override; + + ObjectGuid ReforgerGUID; + int32 ContainerId = 0; + int32 SlotNum = 0; + int32 ItemReforgeRecId = 0; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index fa521ba8a16..3a8ba9966ea 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -790,6 +790,7 @@ void OpcodeTable::InitializeClientOpcodes() DEFINE_HANDLER(CMSG_READY_CHECK_RESPONSE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleReadyCheckResponseOpcode); DEFINE_HANDLER(CMSG_READ_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleReadItem); DEFINE_HANDLER(CMSG_RECLAIM_CORPSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleReclaimCorpse); + DEFINE_HANDLER(CMSG_REFORGE_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleReforgeItem); DEFINE_HANDLER(CMSG_REMOVE_NEW_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRemoveNewItem); DEFINE_HANDLER(CMSG_REMOVE_RAF_RECRUIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_REORDER_CHARACTERS, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleReorderCharacters); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 7dc40044dc2..56279f6b266 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -442,6 +442,7 @@ namespace WorldPackets class SortBankBags; class SortReagentBankBags; struct ItemInstance; + class ReforgeItem; class RemoveNewItem; } @@ -1660,6 +1661,7 @@ class TC_GAME_API WorldSession void HandleSortBankBags(WorldPackets::Item::SortBankBags& sortBankBags); void HandleSortReagentBankBags(WorldPackets::Item::SortReagentBankBags& sortReagentBankBags); void HandleRemoveNewItem(WorldPackets::Item::RemoveNewItem& removeNewItem); + void HandleReforgeItem(WorldPackets::Item::ReforgeItem& reforgeItem); void HandleCancelTempEnchantmentOpcode(WorldPackets::Item::CancelTempEnchantment& cancelTempEnchantment); |