aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOvahlord <dreadkiller@gmx.de>2024-06-01 17:45:20 +0200
committerOvahlord <dreadkiller@gmx.de>2024-06-01 19:32:24 +0200
commit57c36d758fabadd0385cde75b5b6abdcd8c493dd (patch)
tree5a9ef5b1822347cda950fc2c1cf9e0d900c8c769
parentd4c36db8aca9b3da8c589ac313c230763687f171 (diff)
Core/Items: implement reforging items
-rw-r--r--sql/base/characters_database.sql4
-rw-r--r--sql/updates/characters/cata_classic/2024_06_01_00_characters.sql2
-rw-r--r--sql/updates/hotfixes/cata_classic/2024_06_01_00_hotfixes.sql27
-rw-r--r--sql/updates/world/cata_classic/2024_06_01_00_world.sql1
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp5
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h3
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h15
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp2
-rw-r--r--src/server/game/DataStores/DB2Stores.h1
-rw-r--r--src/server/game/DataStores/DB2Structure.h10
-rw-r--r--src/server/game/Entities/Item/Item.cpp23
-rw-r--r--src/server/game/Entities/Item/Item.h1
-rw-r--r--src/server/game/Entities/Item/ItemDefines.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp390
-rw-r--r--src/server/game/Entities/Player/Player.h3
-rw-r--r--src/server/game/Entities/Unit/UnitDefines.h1
-rw-r--r--src/server/game/Entities/Unit/enuminfo_UnitDefines.cpp3
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp45
-rw-r--r--src/server/game/Server/Packets/ItemPackets.cpp8
-rw-r--r--src/server/game/Server/Packets/ItemPackets.h13
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp1
-rw-r--r--src/server/game/Server/WorldSession.h2
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);