Core/Transmog: Update transmog system to 12.0

This commit is contained in:
Shauren
2026-03-21 13:41:42 +01:00
parent ec3065b354
commit db8552567d
43 changed files with 2883 additions and 290 deletions
+26 -1
View File
@@ -459,6 +459,30 @@ LOCK TABLES `battlenet_account_transmog_illusions` WRITE;
/*!40000 ALTER TABLE `battlenet_account_transmog_illusions` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `battlenet_account_transmog_outfits`
--
DROP TABLE IF EXISTS `battlenet_account_transmog_outfits`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `battlenet_account_transmog_outfits` (
`battlenetAccountId` int unsigned NOT NULL,
`transmogOutfitId` int NOT NULL DEFAULT '0',
PRIMARY KEY (`battlenetAccountId`,`blobIndex`),
CONSTRAINT `fk_battlenet_account_transmog_outfits` FOREIGN KEY (`battlenetAccountId`) REFERENCES `battlenet_accounts` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `battlenet_account_transmog_outfits`
--
LOCK TABLES `battlenet_account_transmog_outfits` WRITE;
/*!40000 ALTER TABLE `battlenet_account_transmog_outfits` DISABLE KEYS */;
/*!40000 ALTER TABLE `battlenet_account_transmog_outfits` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `battlenet_account_warband_scenes`
--
@@ -4147,7 +4171,8 @@ INSERT INTO `updates` VALUES
('2026_03_10_00_auth.sql','4BB4EBC719F80F7BEA667E6DA49815B358956FEA','RELEASED','2026-03-10 20:42:13',0),
('2026_03_14_00_auth.sql','4CE4AF3D9E1BF173832B25F4D1A730A93ED016FA','RELEASED','2026-03-14 12:03:14',0),
('2026_03_18_00_auth.sql','7A755DADF9AAC9016FD01B26ACEC714FAB5E3269','RELEASED','2026-03-18 12:36:48',0),
('2026_03_20_00_auth.sql','9083386953291DA3D0634E90D40863F7625FF37E','RELEASED','2026-03-20 01:11:00',0);
('2026_03_20_00_auth.sql','9083386953291DA3D0634E90D40863F7625FF37E','RELEASED','2026-03-20 01:11:00',0),
('2026_03_21_00_auth.sql','6D47EFD7FBCD159F5BC5B5A6020E61824280E92A','RELEASED','2026-03-21 00:08:26',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
+87 -1
View File
@@ -1891,6 +1891,89 @@ LOCK TABLES `character_trait_entry` WRITE;
/*!40000 ALTER TABLE `character_trait_entry` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `character_transmog_outfit`
--
DROP TABLE IF EXISTS `character_transmog_outfit`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `character_transmog_outfit` (
`guid` bigint NOT NULL,
`transmogOutfitId` int NOT NULL,
`name` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`icon` int NOT NULL,
`situationsEnabled` tinyint(1) NOT NULL,
PRIMARY KEY (`guid`,`transmogOutfitId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `character_transmog_outfit`
--
LOCK TABLES `character_transmog_outfit` WRITE;
/*!40000 ALTER TABLE `character_transmog_outfit` DISABLE KEYS */;
/*!40000 ALTER TABLE `character_transmog_outfit` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `character_transmog_outfit_situation`
--
DROP TABLE IF EXISTS `character_transmog_outfit_situation`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `character_transmog_outfit_situation` (
`guid` bigint NOT NULL,
`transmogOutfitId` int NOT NULL,
`situationID` int NOT NULL,
`specID` int NOT NULL,
`loadoutID` int NOT NULL,
`equipmentSetID` int NOT NULL,
PRIMARY KEY (`guid`,`transmogOutfitId`,`situationID`,`specID`,`loadoutID`,`equipmentSetID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `character_transmog_outfit_situation`
--
LOCK TABLES `character_transmog_outfit_situation` WRITE;
/*!40000 ALTER TABLE `character_transmog_outfit_situation` DISABLE KEYS */;
/*!40000 ALTER TABLE `character_transmog_outfit_situation` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `character_transmog_outfit_slot`
--
DROP TABLE IF EXISTS `character_transmog_outfit_slot`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `character_transmog_outfit_slot` (
`guid` bigint NOT NULL,
`transmogOutfitId` int NOT NULL,
`slot` tinyint NOT NULL,
`slotOption` tinyint NOT NULL,
`itemModifiedAppearanceID` int NOT NULL,
`appearanceDisplayType` tinyint NOT NULL,
`spellItemEnchantmentID` int NOT NULL,
`illusionDisplayType` tinyint NOT NULL,
`flags` int NOT NULL,
PRIMARY KEY (`guid`,`transmogOutfitId`,`slot`,`slotOption`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `character_transmog_outfit_slot`
--
LOCK TABLES `character_transmog_outfit_slot` WRITE;
/*!40000 ALTER TABLE `character_transmog_outfit_slot` DISABLE KEYS */;
/*!40000 ALTER TABLE `character_transmog_outfit_slot` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `character_transmog_outfits`
--
@@ -2043,6 +2126,8 @@ CREATE TABLE `characters` (
`personalTabardBorderStyle` int NOT NULL DEFAULT '-1',
`personalTabardBorderColor` int NOT NULL DEFAULT '-1',
`personalTabardBackgroundColor` int NOT NULL DEFAULT '-1',
`transmogOutfitEquippedId` int NOT NULL DEFAULT '2',
`transmogOutfitLocked` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`guid`),
UNIQUE KEY `idx_name` (`name`),
KEY `idx_account` (`account`),
@@ -3832,7 +3917,8 @@ INSERT INTO `updates` VALUES
('2025_11_25_00_characters.sql','A0C04B2404B1832421402F78436DDC4AA18EBAD8','ARCHIVED','2025-11-25 22:28:32',0),
('2026_01_14_00_characters.sql','FF5D889A41BBD9F9827489DEC08BCA9DB457933E','ARCHIVED','2026-01-14 23:40:33',0),
('2026_01_28_00_characters.sql','807B6622970E81089806C3B45C6C7A32EF531BCA','ARCHIVED','2026-01-25 21:53:51',0),
('2026_02_06_00_characters.sql','90735F4481A137E79B8371F291008CF6051657AC','ARCHIVED','2026-02-06 12:45:48',0);
('2026_02_06_00_characters.sql','90735F4481A137E79B8371F291008CF6051657AC','ARCHIVED','2026-02-06 12:45:48',0),
('2026_03_21_00_characters.sql','87963F4E341B195D4B4C4514A3119092DF127431','RELEASED','2026-03-21 00:42:13',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
@@ -0,0 +1,10 @@
--
-- Table structure for table `battlenet_account_transmog_outfits`
--
DROP TABLE IF EXISTS `battlenet_account_transmog_outfits`;
CREATE TABLE `battlenet_account_transmog_outfits` (
`battlenetAccountId` int unsigned NOT NULL,
`transmogOutfitId` int NOT NULL DEFAULT '0',
PRIMARY KEY (`battlenetAccountId`,`transmogOutfitId`),
CONSTRAINT `fk_battlenet_account_transmog_outfits` FOREIGN KEY (`battlenetAccountId`) REFERENCES `battlenet_accounts` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -0,0 +1,47 @@
ALTER TABLE `characters`
ADD `transmogOutfitEquippedId` int NOT NULL DEFAULT 2 AFTER `personalTabardBackgroundColor`,
ADD `transmogOutfitLocked` tinyint(1) NOT NULL DEFAULT 0 AFTER `transmogOutfitEquippedId`;
--
-- Table structure for table `character_transmog_outfit`
--
DROP TABLE IF EXISTS `character_transmog_outfit`;
CREATE TABLE `character_transmog_outfit` (
`guid` bigint NOT NULL,
`transmogOutfitId` int NOT NULL,
`name` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`icon` int NOT NULL,
`situationsEnabled` tinyint(1) NOT NULL,
PRIMARY KEY (`guid`,`transmogOutfitId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `character_transmog_outfit_situation`
--
DROP TABLE IF EXISTS `character_transmog_outfit_situation`;
CREATE TABLE `character_transmog_outfit_situation` (
`guid` bigint NOT NULL,
`transmogOutfitId` int NOT NULL,
`situationID` int NOT NULL,
`specID` int NOT NULL,
`loadoutID` int NOT NULL,
`equipmentSetID` int NOT NULL,
PRIMARY KEY (`guid`,`transmogOutfitId`,`situationID`,`specID`,`loadoutID`,`equipmentSetID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `character_transmog_outfit_slot`
--
DROP TABLE IF EXISTS `character_transmog_outfit_slot`;
CREATE TABLE `character_transmog_outfit_slot` (
`guid` bigint NOT NULL,
`transmogOutfitId` int NOT NULL,
`slot` tinyint NOT NULL,
`slotOption` tinyint NOT NULL,
`itemModifiedAppearanceID` int NOT NULL,
`appearanceDisplayType` tinyint NOT NULL,
`spellItemEnchantmentID` int NOT NULL,
`illusionDisplayType` tinyint NOT NULL,
`flags` int NOT NULL,
PRIMARY KEY (`guid`,`transmogOutfitId`,`slot`,`slotOption`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -0,0 +1,190 @@
--
-- Table structure for table `transmog_outfit_entry`
--
DROP TABLE IF EXISTS `transmog_outfit_entry`;
CREATE TABLE `transmog_outfit_entry` (
`Cost` bigint unsigned NOT NULL DEFAULT '0',
`Name` text,
`ID` int unsigned NOT NULL DEFAULT '0',
`OrderIndex` int NOT NULL DEFAULT '0',
`Source` tinyint unsigned NOT NULL DEFAULT '0',
`Flags` int NOT NULL DEFAULT '0',
`SetType` tinyint unsigned NOT NULL DEFAULT '0',
`OverrideCostModifier` float NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `transmog_outfit_entry_locale`
--
DROP TABLE IF EXISTS `transmog_outfit_entry_locale`;
CREATE TABLE `transmog_outfit_entry_locale` (
`ID` int unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Name_lang` text,
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST COLUMNS(locale)
(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB);
--
-- Table structure for table `transmog_outfit_slot_info`
--
DROP TABLE IF EXISTS `transmog_outfit_slot_info`;
CREATE TABLE `transmog_outfit_slot_info` (
`InventorySlotName` text,
`ID` int unsigned NOT NULL DEFAULT '0',
`TransmogOutfitSlotEnum` tinyint NOT NULL DEFAULT '0',
`InventorySlotEnum` int NOT NULL DEFAULT '0',
`Flags` int NOT NULL DEFAULT '0',
`Unused1200` tinyint unsigned NOT NULL DEFAULT '0',
`TransmogCollectionType` tinyint unsigned NOT NULL DEFAULT '0',
`SecondarySlotID` int NOT NULL DEFAULT '0',
`InventorySlotID` int NOT NULL DEFAULT '0',
`UnassignedAtlasID` int NOT NULL DEFAULT '0',
`UnassignedDisplayAtlasID` int NOT NULL DEFAULT '0',
`ItemCostMultiplier` float NOT NULL DEFAULT '0',
`IllusionCostMultiplier` float NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `transmog_outfit_slot_option`
--
DROP TABLE IF EXISTS `transmog_outfit_slot_option`;
CREATE TABLE `transmog_outfit_slot_option` (
`ID` int unsigned NOT NULL DEFAULT '0',
`Name` text,
`OptionEnum` tinyint unsigned NOT NULL DEFAULT '0',
`TransmogOutfitSlotInfoID` int unsigned NOT NULL DEFAULT '0',
`Flags` int NOT NULL DEFAULT '0',
`SecondaryOptionID` int NOT NULL DEFAULT '0',
`ItemCostMultiplier` float NOT NULL DEFAULT '0',
`IllusionCostMultiplier` float NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `transmog_outfit_slot_option_locale`
--
DROP TABLE IF EXISTS `transmog_outfit_slot_option_locale`;
CREATE TABLE `transmog_outfit_slot_option_locale` (
`ID` int unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Name_lang` text,
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST COLUMNS(locale)
(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB);
--
-- Table structure for table `transmog_situation`
--
DROP TABLE IF EXISTS `transmog_situation`;
CREATE TABLE `transmog_situation` (
`Name` text,
`ID` int unsigned NOT NULL DEFAULT '0',
`SituationEnum` tinyint NOT NULL DEFAULT '0',
`Flags` int NOT NULL DEFAULT '0',
`TransmogSituationGroupID` int unsigned NOT NULL DEFAULT '0',
`OrderIndex` int NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `transmog_situation_locale`
--
DROP TABLE IF EXISTS `transmog_situation_locale`;
CREATE TABLE `transmog_situation_locale` (
`ID` int unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Name_lang` text,
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST COLUMNS(locale)
(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB);
--
-- Table structure for table `transmog_situation_group`
--
DROP TABLE IF EXISTS `transmog_situation_group`;
CREATE TABLE `transmog_situation_group` (
`ID` int unsigned NOT NULL DEFAULT '0',
`TransmogSituationTriggerID` int unsigned NOT NULL DEFAULT '0',
`OrderIndex` int NOT NULL DEFAULT '0',
`Flags` int NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `transmog_situation_trigger`
--
DROP TABLE IF EXISTS `transmog_situation_trigger`;
CREATE TABLE `transmog_situation_trigger` (
`Name` text,
`Description` text,
`ID` int unsigned NOT NULL DEFAULT '0',
`TriggerEnum` tinyint unsigned NOT NULL DEFAULT '0',
`Flags` int NOT NULL DEFAULT '0',
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `transmog_situation_trigger_locale`
--
DROP TABLE IF EXISTS `transmog_situation_trigger_locale`;
CREATE TABLE `transmog_situation_trigger_locale` (
`ID` int unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Name_lang` text,
`Description_lang` text,
`VerifiedBuild` int NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST COLUMNS(locale)
(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB);
@@ -99,7 +99,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, "
"health, power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, "
"raidDifficulty, legacyRaidDifficulty, fishingSteps, honor, honorLevel, honorRestState, honorRestBonus, numRespecs, "
"personalTabardEmblemStyle, personalTabardEmblemColor, personalTabardBorderStyle, personalTabardBorderColor, personalTabardBackgroundColor "
"personalTabardEmblemStyle, personalTabardEmblemColor, personalTabardBorderStyle, personalTabardBorderColor, personalTabardBackgroundColor, transmogOutfitEquippedId, transmogOutfitLocked "
"FROM characters c LEFT JOIN character_fishingsteps cfs ON c.guid = cfs.guid WHERE c.guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_CUSTOMIZATIONS, "SELECT chrCustomizationOptionID, chrCustomizationChoiceID FROM character_customizations WHERE guid = ? ORDER BY chrCustomizationOptionID", CONNECTION_ASYNC);
@@ -150,6 +150,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFITS, "SELECT setguid, setindex, name, iconname, ignore_mask, appearance0, appearance1, appearance2, appearance3, appearance4, "
"appearance5, appearance6, appearance7, appearance8, appearance9, appearance10, appearance11, appearance12, appearance13, appearance14, appearance15, appearance16, "
"appearance17, appearance18, mainHandEnchant, offHandEnchant FROM character_transmog_outfits WHERE guid = ? ORDER BY setindex", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT, "SELECT transmogOutfitId, name, icon, situationsEnabled FROM character_transmog_outfit WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT_SITUATION, "SELECT transmogOutfitId, situationID, specID, loadoutID, equipmentSetID FROM character_transmog_outfit_situation WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT_SLOT, "SELECT transmogOutfitId, slot, slotOption, itemModifiedAppearanceID, appearanceDisplayType, spellItemEnchantmentID, illusionDisplayType, flags FROM character_transmog_outfit_slot WHERE guid = ? ORDER BY transmogOutfitId, slot, slotOption", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell, queueId FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyphId FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT talentId, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC);
@@ -360,6 +363,12 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"appearance16, appearance17, appearance18, mainHandEnchant, offHandEnchant) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_TRANSMOG_OUTFIT, "DELETE FROM character_transmog_outfits WHERE setguid=?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_TRANSMOG_OUTFIT_2, "INSERT INTO character_transmog_outfit (guid, transmogOutfitId, name, icon, situationsEnabled) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_TRANSMOG_OUTFIT_2, "DELETE FROM character_transmog_outfit WHERE guid = ? AND transmogOutfitId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_TRANSMOG_OUTFIT_SITUATION, "INSERT INTO character_transmog_outfit_situation (guid, transmogOutfitId, situationID, specID, loadoutID, equipmentSetID) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_TRANSMOG_OUTFIT_SITUATION, "DELETE FROM character_transmog_outfit_situation WHERE guid = ? AND transmogOutfitId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_TRANSMOG_OUTFIT_SLOT, "INSERT INTO character_transmog_outfit_slot (guid, transmogOutfitId, slot, slotOption, itemModifiedAppearanceID, appearanceDisplayType, spellItemEnchantmentID, illusionDisplayType, flags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_TRANSMOG_OUTFIT_SLOT, "DELETE FROM character_transmog_outfit_slot WHERE guid = ? AND transmogOutfitId = ?", CONNECTION_ASYNC);
// Auras
PrepareStatement(CHAR_INS_AURA, "INSERT INTO character_aura (guid, casterGuid, itemGuid, spell, effectMask, recalculateMask, difficulty, stackCount, maxDuration, remainTime, remainCharges, castItemId, castItemLevel) "
@@ -481,17 +490,19 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"taximask, createTime, createMode, cinematic, "
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, primarySpecialization, "
"extra_flags, summonedPetNumber, at_login, "
"death_expire_time, taxi_path, totalKills, "
"todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, "
"power4, power5, power6, power7, power8, power9, power10, latency, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, lastLoginBuild) VALUES "
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
"death_expire_time, taxi_path, totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, "
"power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, "
"latency, activeTalentGroup, lootSpecId, exploredZones, equipmentCache, knownTitles, actionBars, lastLoginBuild, "
"personalTabardEmblemStyle, personalTabardEmblemColor, personalTabardBorderStyle, personalTabardBorderColor, personalTabardBackgroundColor, transmogOutfitEquippedId, transmogOutfitLocked) VALUES "
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,inventorySlots=?,inventoryBagFlags=?,bagSlotFlags1=?,bagSlotFlags2=?,bagSlotFlags3=?,bagSlotFlags4=?,bagSlotFlags5=?,"
"bankSlots=?,bankTabs=?,bankBagFlags=?,restState=?,playerFlags=?,playerFlagsEx=?,"
"map=?,instance_id=?,dungeonDifficulty=?,raidDifficulty=?,legacyRaidDifficulty=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?,"
"logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,numRespecs=?,primarySpecialization=?,extra_flags=?,summonedPetNumber=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
"totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?,"
"watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,power8=?,power9=?,power10=?,latency=?,activeTalentGroup=?,lootSpecId=?,exploredZones=?,"
"equipmentCache=?,knownTitles=?,actionBars=?,online=?,honor=?,honorLevel=?,honorRestState=?,honorRestBonus=?,lastLoginBuild=? WHERE guid=?", CONNECTION_ASYNC);
"equipmentCache=?,knownTitles=?,actionBars=?,online=?,honor=?,honorLevel=?,honorRestState=?,honorRestBonus=?,lastLoginBuild=?,"
"personalTabardEmblemStyle=?,personalTabardEmblemColor=?,personalTabardBorderStyle=?,personalTabardBorderColor=?,personalTabardBackgroundColor=?,transmogOutfitEquippedId=?,transmogOutfitLocked=? WHERE guid=?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG, "UPDATE characters SET at_login = at_login | ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
@@ -626,6 +637,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENTS, "DELETE FROM character_achievement WHERE guid = ? AND achievement NOT IN (456,457,458,459,460,461,462,463,464,465,466,467,1400,1402,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424,1425,1426,1427,1463,3117,3259,4078,4576,4998,4999,5000,5001,5002,5003,5004,5005,5006,5007,5008,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396,6433,6523,6524,6743,6744,6745,6746,6747,6748,6749,6750,6751,6752,6829,6859,6860,6861,6862,6863,6864,6865,6866,6867,6868,6869,6870,6871,6872,6873)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_EQUIPMENTSETS, "DELETE FROM character_equipmentsets WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFITS, "DELETE FROM character_transmog_outfits WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFIT_BY_CHAR, "DELETE FROM character_transmog_outfit WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFIT_SITUATION_BY_CHAR, "DELETE FROM character_transmog_outfit_slot WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFIT_SLOT_BY_CHAR, "DELETE FROM character_transmog_outfit_situation WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER, "DELETE FROM guild_eventlog WHERE PlayerGuid1 = ? OR PlayerGuid2 = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER, "DELETE FROM guild_bank_eventlog WHERE PlayerGuid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_GLYPHS, "DELETE FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC);
@@ -107,6 +107,9 @@ enum CharacterDatabaseStatements : uint32
CHAR_SEL_CHARACTER_CRITERIAPROGRESS,
CHAR_SEL_CHARACTER_EQUIPMENTSETS,
CHAR_SEL_CHARACTER_TRANSMOG_OUTFITS,
CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT,
CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT_SITUATION,
CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT_SLOT,
CHAR_SEL_CHARACTER_BGDATA,
CHAR_SEL_CHARACTER_GLYPHS,
CHAR_SEL_CHARACTER_TALENTS,
@@ -270,6 +273,13 @@ enum CharacterDatabaseStatements : uint32
CHAR_INS_TRANSMOG_OUTFIT,
CHAR_DEL_TRANSMOG_OUTFIT,
CHAR_INS_TRANSMOG_OUTFIT_2,
CHAR_DEL_TRANSMOG_OUTFIT_2,
CHAR_INS_TRANSMOG_OUTFIT_SITUATION,
CHAR_DEL_TRANSMOG_OUTFIT_SITUATION,
CHAR_INS_TRANSMOG_OUTFIT_SLOT,
CHAR_DEL_TRANSMOG_OUTFIT_SLOT,
CHAR_INS_AURA,
CHAR_INS_AURA_EFFECT,
@@ -496,6 +506,9 @@ enum CharacterDatabaseStatements : uint32
CHAR_DEL_CHAR_ACHIEVEMENTS,
CHAR_DEL_CHAR_EQUIPMENTSETS,
CHAR_DEL_CHAR_TRANSMOG_OUTFITS,
CHAR_DEL_CHAR_TRANSMOG_OUTFIT_BY_CHAR,
CHAR_DEL_CHAR_TRANSMOG_OUTFIT_SITUATION_BY_CHAR,
CHAR_DEL_CHAR_TRANSMOG_OUTFIT_SLOT_BY_CHAR,
CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER,
CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER,
CHAR_DEL_CHAR_GLYPHS,
@@ -1997,6 +1997,26 @@ void HotfixDatabaseConnection::DoPrepareStatements()
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_ILLUSION, "SELECT MAX(ID) + 1 FROM transmog_illusion", CONNECTION_SYNCH);
// TransmogOutfitEntry.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY, "SELECT Cost, Name, ID, OrderIndex, Source, Flags, SetType, OverrideCostModifier"
" FROM transmog_outfit_entry WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY, "SELECT MAX(ID) + 1 FROM transmog_outfit_entry", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY, "SELECT ID, Name_lang FROM transmog_outfit_entry_locale WHERE (`VerifiedBuild` > 0) = ?"
" AND locale = ?", CONNECTION_SYNCH);
// TransmogOutfitSlotInfo.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_INFO, "SELECT InventorySlotName, ID, TransmogOutfitSlotEnum, InventorySlotEnum, Flags, "
"Unused1200, TransmogCollectionType, SecondarySlotID, InventorySlotID, UnassignedAtlasID, UnassignedDisplayAtlasID, ItemCostMultiplier, "
"IllusionCostMultiplier FROM transmog_outfit_slot_info WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_INFO, "SELECT MAX(ID) + 1 FROM transmog_outfit_slot_info", CONNECTION_SYNCH);
// TransmogOutfitSlotOption.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION, "SELECT ID, Name, OptionEnum, TransmogOutfitSlotInfoID, Flags, SecondaryOptionID, "
"ItemCostMultiplier, IllusionCostMultiplier FROM transmog_outfit_slot_option WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION, "SELECT MAX(ID) + 1 FROM transmog_outfit_slot_option", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION, "SELECT ID, Name_lang FROM transmog_outfit_slot_option_locale"
" WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH);
// TransmogSet.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_SET, "SELECT Name, ID, ClassMask, TrackingQuestID, Flags, TransmogSetGroupID, ItemNameDescriptionID, "
"ParentTransmogSetID, Unknown810, ExpansionID, PatchID, UiOrder, PlayerConditionID FROM transmog_set WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
@@ -2014,6 +2034,25 @@ void HotfixDatabaseConnection::DoPrepareStatements()
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_SET_ITEM, "SELECT MAX(ID) + 1 FROM transmog_set_item", CONNECTION_SYNCH);
// TransmogSituation.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_SITUATION, "SELECT Name, ID, SituationEnum, Flags, TransmogSituationGroupID, OrderIndex"
" FROM transmog_situation WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_SITUATION, "SELECT MAX(ID) + 1 FROM transmog_situation", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_TRANSMOG_SITUATION, "SELECT ID, Name_lang FROM transmog_situation_locale WHERE (`VerifiedBuild` > 0) = ?"
" AND locale = ?", CONNECTION_SYNCH);
// TransmogSituationGroup.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_SITUATION_GROUP, "SELECT ID, TransmogSituationTriggerID, OrderIndex, Flags FROM transmog_situation_group"
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_SITUATION_GROUP, "SELECT MAX(ID) + 1 FROM transmog_situation_group", CONNECTION_SYNCH);
// TransmogSituationTrigger.db2
PrepareStatement(HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER, "SELECT Name, Description, ID, TriggerEnum, Flags FROM transmog_situation_trigger"
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER, "SELECT MAX(ID) + 1 FROM transmog_situation_trigger", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER, "SELECT ID, Name_lang, Description_lang FROM transmog_situation_trigger_locale"
" WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH);
// TransportAnimation.db2
PrepareStatement(HOTFIX_SEL_TRANSPORT_ANIMATION, "SELECT ID, PosX, PosY, PosZ, SequenceID, TimeIndex, TransportID FROM transport_animation"
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
@@ -1155,6 +1155,17 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_TRANSMOG_ILLUSION,
HOTFIX_SEL_TRANSMOG_ILLUSION_MAX_ID,
HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY,
HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY_MAX_ID,
HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY_LOCALE,
HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_INFO,
HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_INFO_MAX_ID,
HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION,
HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION_MAX_ID,
HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION_LOCALE,
HOTFIX_SEL_TRANSMOG_SET,
HOTFIX_SEL_TRANSMOG_SET_MAX_ID,
HOTFIX_SEL_TRANSMOG_SET_LOCALE,
@@ -1166,6 +1177,17 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_TRANSMOG_SET_ITEM,
HOTFIX_SEL_TRANSMOG_SET_ITEM_MAX_ID,
HOTFIX_SEL_TRANSMOG_SITUATION,
HOTFIX_SEL_TRANSMOG_SITUATION_MAX_ID,
HOTFIX_SEL_TRANSMOG_SITUATION_LOCALE,
HOTFIX_SEL_TRANSMOG_SITUATION_GROUP,
HOTFIX_SEL_TRANSMOG_SITUATION_GROUP_MAX_ID,
HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER,
HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER_MAX_ID,
HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER_LOCALE,
HOTFIX_SEL_TRANSPORT_ANIMATION,
HOTFIX_SEL_TRANSPORT_ANIMATION_MAX_ID,
@@ -191,6 +191,8 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_BNET_TRANSMOG_ILLUSIONS, "SELECT blobIndex, illusionMask FROM battlenet_account_transmog_illusions WHERE battlenetAccountId = ? ORDER BY blobIndex DESC", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_BNET_TRANSMOG_ILLUSIONS, "INSERT INTO battlenet_account_transmog_illusions (battlenetAccountId, blobIndex, illusionMask) VALUES (?, ?, ?) "
"ON DUPLICATE KEY UPDATE illusionMask = illusionMask | VALUES(illusionMask)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_BNET_TRANSMOG_OUTFITS, "SELECT transmogOutfitId FROM battlenet_account_transmog_outfits WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_BNET_TRANSMOG_OUTFITS, "INSERT IGNORE INTO battlenet_account_transmog_outfits (battlenetAccountId, transmogOutfitId) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_BNET_WARBAND_SCENES, "SELECT warbandSceneId, isFavorite, hasFanfare FROM battlenet_account_warband_scenes WHERE battlenetAccountId = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_BNET_WARBAND_SCENE, "INSERT INTO battlenet_account_warband_scenes (battlenetAccountId, warbandSceneId, isFavorite, hasFanfare) VALUES (?, ?, ?, ?) "
"ON DUPLICATE KEY UPDATE isFavorite = VALUES(isFavorite)", CONNECTION_ASYNC);
@@ -176,6 +176,8 @@ enum LoginDatabaseStatements : uint32
LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE,
LOGIN_SEL_BNET_TRANSMOG_ILLUSIONS,
LOGIN_INS_BNET_TRANSMOG_ILLUSIONS,
LOGIN_SEL_BNET_TRANSMOG_OUTFITS,
LOGIN_INS_BNET_TRANSMOG_OUTFITS,
LOGIN_SEL_BNET_WARBAND_SCENES,
LOGIN_INS_BNET_WARBAND_SCENE,
LOGIN_UPD_BNET_WARBAND_SCENE,
@@ -3227,10 +3227,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6
{
itemSubclass = itemTemplate->GetSubClass();
if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = sDB2Manager.GetItemModifiedAppearance(visibleItem.ItemID, visibleItem.ItemAppearanceModID))
if (ItemModifiedAppearanceExtraEntry const* itemModifiedAppearaceExtra = sItemModifiedAppearanceExtraStore.LookupEntry(itemModifiedAppearance->ID))
if (itemModifiedAppearaceExtra->DisplayWeaponSubclassID > 0)
itemSubclass = itemModifiedAppearaceExtra->DisplayWeaponSubclassID;
if (ItemModifiedAppearanceExtraEntry const* itemModifiedAppearaceExtra = sItemModifiedAppearanceExtraStore.LookupEntry(visibleItem.ItemModifiedAppearanceID))
if (itemModifiedAppearaceExtra->DisplayWeaponSubclassID > 0)
itemSubclass = itemModifiedAppearaceExtra->DisplayWeaponSubclassID;
}
}
if (itemSubclass != reqValue)
@@ -3247,10 +3246,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6
{
itemSubclass = itemTemplate->GetSubClass();
if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = sDB2Manager.GetItemModifiedAppearance(visibleItem.ItemID, visibleItem.ItemAppearanceModID))
if (ItemModifiedAppearanceExtraEntry const* itemModifiedAppearaceExtra = sItemModifiedAppearanceExtraStore.LookupEntry(itemModifiedAppearance->ID))
if (itemModifiedAppearaceExtra->DisplayWeaponSubclassID > 0)
itemSubclass = itemModifiedAppearaceExtra->DisplayWeaponSubclassID;
if (ItemModifiedAppearanceExtraEntry const* itemModifiedAppearaceExtra = sItemModifiedAppearanceExtraStore.LookupEntry(visibleItem.ItemModifiedAppearanceID))
if (itemModifiedAppearaceExtra->DisplayWeaponSubclassID > 0)
itemSubclass = itemModifiedAppearaceExtra->DisplayWeaponSubclassID;
}
}
if (itemSubclass != reqValue)
+2 -1
View File
@@ -22,6 +22,7 @@
#include "ObjectMgr.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "TransmogMgr.h"
static constexpr char HYPERLINK_DATA_DELIMITER = ':';
@@ -527,7 +528,7 @@ bool Trinity::Hyperlinks::LinkTags::transmogillusion::StoreTo(SpellItemEnchantme
if (!t.TryConsumeTo(spellItemEnchantmentId))
return false;
return !!(val = sSpellItemEnchantmentStore.LookupEntry(spellItemEnchantmentId))
&& sDB2Manager.GetTransmogIllusionForEnchantment(spellItemEnchantmentId) && t.IsEmpty();
&& TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(spellItemEnchantmentId) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::transmogset::StoreTo(TransmogSetEntry const*& val, std::string_view text)
+98
View File
@@ -6579,6 +6579,62 @@ struct TransmogIllusionLoadInfo
static constexpr DB2LoadInfo Instance{ Fields, 5, &TransmogIllusionMeta::Instance, HOTFIX_SEL_TRANSMOG_ILLUSION };
};
struct TransmogOutfitEntryLoadInfo
{
static constexpr DB2FieldMeta Fields[8] =
{
{ .IsSigned = false, .Type = FT_LONG, .Name = "Cost" },
{ .IsSigned = false, .Type = FT_STRING, .Name = "Name" },
{ .IsSigned = false, .Type = FT_INT, .Name = "ID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "OrderIndex" },
{ .IsSigned = false, .Type = FT_BYTE, .Name = "Source" },
{ .IsSigned = true, .Type = FT_INT, .Name = "Flags" },
{ .IsSigned = false, .Type = FT_BYTE, .Name = "SetType" },
{ .IsSigned = false, .Type = FT_FLOAT, .Name = "OverrideCostModifier" },
};
static constexpr DB2LoadInfo Instance{ Fields, 8, &TransmogOutfitEntryMeta::Instance, HOTFIX_SEL_TRANSMOG_OUTFIT_ENTRY };
};
struct TransmogOutfitSlotInfoLoadInfo
{
static constexpr DB2FieldMeta Fields[13] =
{
{ .IsSigned = false, .Type = FT_STRING_NOT_LOCALIZED, .Name = "InventorySlotName" },
{ .IsSigned = false, .Type = FT_INT, .Name = "ID" },
{ .IsSigned = true, .Type = FT_BYTE, .Name = "TransmogOutfitSlotEnum" },
{ .IsSigned = true, .Type = FT_INT, .Name = "InventorySlotEnum" },
{ .IsSigned = true, .Type = FT_INT, .Name = "Flags" },
{ .IsSigned = false, .Type = FT_BYTE, .Name = "Unused1200" },
{ .IsSigned = false, .Type = FT_BYTE, .Name = "TransmogCollectionType" },
{ .IsSigned = true, .Type = FT_INT, .Name = "SecondarySlotID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "InventorySlotID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "UnassignedAtlasID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "UnassignedDisplayAtlasID" },
{ .IsSigned = false, .Type = FT_FLOAT, .Name = "ItemCostMultiplier" },
{ .IsSigned = false, .Type = FT_FLOAT, .Name = "IllusionCostMultiplier" },
};
static constexpr DB2LoadInfo Instance{ Fields, 13, &TransmogOutfitSlotInfoMeta::Instance, HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_INFO };
};
struct TransmogOutfitSlotOptionLoadInfo
{
static constexpr DB2FieldMeta Fields[8] =
{
{ .IsSigned = false, .Type = FT_INT, .Name = "ID" },
{ .IsSigned = false, .Type = FT_STRING, .Name = "Name" },
{ .IsSigned = false, .Type = FT_BYTE, .Name = "OptionEnum" },
{ .IsSigned = false, .Type = FT_INT, .Name = "TransmogOutfitSlotInfoID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "Flags" },
{ .IsSigned = true, .Type = FT_INT, .Name = "SecondaryOptionID" },
{ .IsSigned = false, .Type = FT_FLOAT, .Name = "ItemCostMultiplier" },
{ .IsSigned = false, .Type = FT_FLOAT, .Name = "IllusionCostMultiplier" },
};
static constexpr DB2LoadInfo Instance{ Fields, 8, &TransmogOutfitSlotOptionMeta::Instance, HOTFIX_SEL_TRANSMOG_OUTFIT_SLOT_OPTION };
};
struct TransmogSetLoadInfo
{
static constexpr DB2FieldMeta Fields[13] =
@@ -6625,6 +6681,48 @@ struct TransmogSetItemLoadInfo
static constexpr DB2LoadInfo Instance{ Fields, 4, &TransmogSetItemMeta::Instance, HOTFIX_SEL_TRANSMOG_SET_ITEM };
};
struct TransmogSituationLoadInfo
{
static constexpr DB2FieldMeta Fields[6] =
{
{ .IsSigned = false, .Type = FT_STRING, .Name = "Name" },
{ .IsSigned = false, .Type = FT_INT, .Name = "ID" },
{ .IsSigned = true, .Type = FT_BYTE, .Name = "SituationEnum" },
{ .IsSigned = true, .Type = FT_INT, .Name = "Flags" },
{ .IsSigned = false, .Type = FT_INT, .Name = "TransmogSituationGroupID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "OrderIndex" },
};
static constexpr DB2LoadInfo Instance{ Fields, 6, &TransmogSituationMeta::Instance, HOTFIX_SEL_TRANSMOG_SITUATION };
};
struct TransmogSituationGroupLoadInfo
{
static constexpr DB2FieldMeta Fields[4] =
{
{ .IsSigned = false, .Type = FT_INT, .Name = "ID" },
{ .IsSigned = false, .Type = FT_INT, .Name = "TransmogSituationTriggerID" },
{ .IsSigned = true, .Type = FT_INT, .Name = "OrderIndex" },
{ .IsSigned = true, .Type = FT_INT, .Name = "Flags" },
};
static constexpr DB2LoadInfo Instance{ Fields, 4, &TransmogSituationGroupMeta::Instance, HOTFIX_SEL_TRANSMOG_SITUATION_GROUP };
};
struct TransmogSituationTriggerLoadInfo
{
static constexpr DB2FieldMeta Fields[5] =
{
{ .IsSigned = false, .Type = FT_STRING, .Name = "Name" },
{ .IsSigned = false, .Type = FT_STRING, .Name = "Description" },
{ .IsSigned = false, .Type = FT_INT, .Name = "ID" },
{ .IsSigned = false, .Type = FT_BYTE, .Name = "TriggerEnum" },
{ .IsSigned = true, .Type = FT_INT, .Name = "Flags" },
};
static constexpr DB2LoadInfo Instance{ Fields, 5, &TransmogSituationTriggerMeta::Instance, HOTFIX_SEL_TRANSMOG_SITUATION_TRIGGER };
};
struct TransportAnimationLoadInfo
{
static constexpr DB2FieldMeta Fields[7] =
+12 -70
View File
@@ -382,6 +382,12 @@ DB2Storage<TraitTreeXTraitCostEntry> sTraitTreeXTraitCostStore("Trait
DB2Storage<TraitTreeXTraitCurrencyEntry> sTraitTreeXTraitCurrencyStore("TraitTreeXTraitCurrency.db2", &TraitTreeXTraitCurrencyLoadInfo::Instance);
DB2Storage<TransmogHolidayEntry> sTransmogHolidayStore("TransmogHoliday.db2", &TransmogHolidayLoadInfo::Instance);
DB2Storage<TransmogIllusionEntry> sTransmogIllusionStore("TransmogIllusion.db2", &TransmogIllusionLoadInfo::Instance);
DB2Storage<TransmogOutfitEntryEntry> sTransmogOutfitEntryStore("TransmogOutfitEntry.db2", &TransmogOutfitEntryLoadInfo::Instance);
DB2Storage<TransmogOutfitSlotInfoEntry> sTransmogOutfitSlotInfoStore("TransmogOutfitSlotInfo.db2", &TransmogOutfitSlotInfoLoadInfo::Instance);
DB2Storage<TransmogOutfitSlotOptionEntry> sTransmogOutfitSlotOptionInfoStore("TransmogOutfitSlotOption.db2", &TransmogOutfitSlotOptionLoadInfo::Instance);
DB2Storage<TransmogSituationEntry> sTransmogSituationStore("TransmogSituation.db2", &TransmogSituationLoadInfo::Instance);
DB2Storage<TransmogSituationGroupEntry> sTransmogSituationGroupStore("TransmogSituationGroup.db2", &TransmogSituationGroupLoadInfo::Instance);
DB2Storage<TransmogSituationTriggerEntry> sTransmogSituationTriggerStore("TransmogSituationTrigger.db2", &TransmogSituationTriggerLoadInfo::Instance);
DB2Storage<TransmogSetEntry> sTransmogSetStore("TransmogSet.db2", &TransmogSetLoadInfo::Instance);
DB2Storage<TransmogSetGroupEntry> sTransmogSetGroupStore("TransmogSetGroup.db2", &TransmogSetGroupLoadInfo::Instance);
DB2Storage<TransmogSetItemEntry> sTransmogSetItemStore("TransmogSetItem.db2", &TransmogSetItemLoadInfo::Instance);
@@ -426,7 +432,6 @@ typedef std::unordered_map<uint32 /*glyphPropertiesId*/, std::vector<ChrSpeciali
typedef std::unordered_map<uint32 /*itemId*/, ItemChildEquipmentEntry const*> ItemChildEquipmentContainer;
typedef std::array<ItemClassEntry const*, 21> ItemClassByOldEnumContainer;
typedef std::unordered_map<uint32, std::vector<ItemLimitCategoryConditionEntry const*>> ItemLimitCategoryConditionContainer;
typedef std::unordered_map<uint32 /*itemId | appearanceMod << 24*/, ItemModifiedAppearanceEntry const*> ItemModifiedAppearanceByItemContainer;
typedef std::unordered_map<uint32, std::vector<ItemSetSpellEntry const*>> ItemSetSpellContainer;
typedef std::unordered_map<uint32, std::vector<ItemSpecOverrideEntry const*>> ItemSpecOverridesContainer;
typedef std::unordered_map<uint32, std::unordered_map<uint32, MapDifficultyEntry const*>> MapDifficultyContainer;
@@ -505,7 +510,6 @@ namespace
ItemClassByOldEnumContainer _itemClassByOldEnum;
std::unordered_set<uint32> _itemsWithCurrencyCost;
ItemLimitCategoryConditionContainer _itemCategoryConditions;
ItemModifiedAppearanceByItemContainer _itemModifiedAppearancesByItem;
ItemSetSpellContainer _itemSetSpells;
ItemSpecOverridesContainer _itemSpecOverrides;
std::vector<JournalTierEntry const*> _journalTiersByIndex;
@@ -536,9 +540,6 @@ namespace
TalentsByPosition _talentsByPosition;
std::unordered_map<std::pair<uint32, uint32>, TaxiPathEntry const*> _taxiPaths;
ToyItemIdsContainer _toys;
std::unordered_map<uint32, TransmogIllusionEntry const*> _transmogIllusionsByEnchantmentId;
std::unordered_map<uint32, std::vector<TransmogSetEntry const*>> _transmogSetsByItemModifiedAppearance;
std::unordered_map<uint32, std::vector<TransmogSetItemEntry const*>> _transmogSetItemsByTransmogSet;
std::unordered_map<int32, UiMapBounds> _uiMapBounds;
std::unordered_multimap<int32, UiMapAssignmentEntry const*> _uiMapAssignmentByMap[MAX_UI_MAP_SYSTEM];
std::unordered_multimap<int32, UiMapAssignmentEntry const*> _uiMapAssignmentByArea[MAX_UI_MAP_SYSTEM];
@@ -1013,6 +1014,12 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
LOAD_DB2(sTraitTreeXTraitCurrencyStore);
LOAD_DB2(sTransmogHolidayStore);
LOAD_DB2(sTransmogIllusionStore);
LOAD_DB2(sTransmogOutfitEntryStore);
LOAD_DB2(sTransmogOutfitSlotInfoStore);
LOAD_DB2(sTransmogOutfitSlotOptionInfoStore);
LOAD_DB2(sTransmogSituationStore);
LOAD_DB2(sTransmogSituationGroupStore);
LOAD_DB2(sTransmogSituationTriggerStore);
LOAD_DB2(sTransmogSetStore);
LOAD_DB2(sTransmogSetGroupStore);
LOAD_DB2(sTransmogSetItemStore);
@@ -1359,12 +1366,6 @@ void DB2Manager::IndexLoadedStores()
for (ItemLimitCategoryConditionEntry const* condition : sItemLimitCategoryConditionStore)
_itemCategoryConditions[condition->ParentItemLimitCategoryID].push_back(condition);
for (ItemModifiedAppearanceEntry const* appearanceMod : sItemModifiedAppearanceStore)
{
ASSERT(appearanceMod->ItemID <= 0xFFFFFF);
_itemModifiedAppearancesByItem[appearanceMod->ItemID | (appearanceMod->ItemAppearanceModifierID << 24)] = appearanceMod;
}
for (ItemSetSpellEntry const* itemSetSpell : sItemSetSpellStore)
_itemSetSpells[itemSetSpell->ItemSetID].push_back(itemSetSpell);
@@ -1576,19 +1577,6 @@ void DB2Manager::IndexLoadedStores()
for (ToyEntry const* toy : sToyStore)
_toys.insert(toy->ItemID);
for (TransmogIllusionEntry const* transmogIllusion : sTransmogIllusionStore)
_transmogIllusionsByEnchantmentId[transmogIllusion->SpellItemEnchantmentID] = transmogIllusion;
for (TransmogSetItemEntry const* transmogSetItem : sTransmogSetItemStore)
{
TransmogSetEntry const* set = sTransmogSetStore.LookupEntry(transmogSetItem->TransmogSetID);
if (!set)
continue;
_transmogSetsByItemModifiedAppearance[transmogSetItem->ItemModifiedAppearanceID].push_back(set);
_transmogSetItemsByTransmogSet[transmogSetItem->TransmogSetID].push_back(transmogSetItem);
}
std::unordered_multimap<int32, UiMapAssignmentEntry const*> uiMapAssignmentByUiMap;
for (UiMapAssignmentEntry const* uiMapAssignment : sUiMapAssignmentStore)
{
@@ -2649,37 +2637,6 @@ std::vector<ItemLimitCategoryConditionEntry const*> const* DB2Manager::GetItemLi
return Trinity::Containers::MapGetValuePtr(_itemCategoryConditions, categoryId);
}
uint32 DB2Manager::GetItemDisplayId(uint32 itemId, uint32 appearanceModId) const
{
if (ItemModifiedAppearanceEntry const* modifiedAppearance = GetItemModifiedAppearance(itemId, appearanceModId))
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(modifiedAppearance->ItemAppearanceID))
return itemAppearance->ItemDisplayInfoID;
return 0;
}
ItemModifiedAppearanceEntry const* DB2Manager::GetItemModifiedAppearance(uint32 itemId, uint32 appearanceModId) const
{
auto itr = _itemModifiedAppearancesByItem.find(itemId | (appearanceModId << 24));
if (itr != _itemModifiedAppearancesByItem.end())
return itr->second;
// Fall back to unmodified appearance
if (appearanceModId)
{
itr = _itemModifiedAppearancesByItem.find(itemId);
if (itr != _itemModifiedAppearancesByItem.end())
return itr->second;
}
return nullptr;
}
ItemModifiedAppearanceEntry const* DB2Manager::GetDefaultItemModifiedAppearance(uint32 itemId) const
{
return Trinity::Containers::MapGetValuePtr(_itemModifiedAppearancesByItem, itemId);
}
std::vector<ItemSetSpellEntry const*> const* DB2Manager::GetItemSetSpells(uint32 itemSetId) const
{
return Trinity::Containers::MapGetValuePtr(_itemSetSpells, itemSetId);
@@ -3116,21 +3073,6 @@ bool DB2Manager::IsToyItem(uint32 toy) const
return _toys.count(toy) > 0;
}
TransmogIllusionEntry const* DB2Manager::GetTransmogIllusionForEnchantment(uint32 spellItemEnchantmentId) const
{
return Trinity::Containers::MapGetValuePtr(_transmogIllusionsByEnchantmentId, spellItemEnchantmentId);
}
std::vector<TransmogSetEntry const*> const* DB2Manager::GetTransmogSetsForItemModifiedAppearance(uint32 itemModifiedAppearanceId) const
{
return Trinity::Containers::MapGetValuePtr(_transmogSetsByItemModifiedAppearance, itemModifiedAppearanceId);
}
std::vector<TransmogSetItemEntry const*> const* DB2Manager::GetTransmogSetItems(uint32 transmogSetId) const
{
return Trinity::Containers::MapGetValuePtr(_transmogSetItemsByTransmogSet, transmogSetId);
}
struct UiMapAssignmentStatus
{
UiMapAssignmentEntry const* UiMapAssignment = nullptr;
+6 -6
View File
@@ -306,6 +306,12 @@ TC_GAME_API extern DB2Storage<TraitTreeXTraitCostEntry> sTraitTreeXT
TC_GAME_API extern DB2Storage<TraitTreeXTraitCurrencyEntry> sTraitTreeXTraitCurrencyStore;
TC_GAME_API extern DB2Storage<TransmogHolidayEntry> sTransmogHolidayStore;
TC_GAME_API extern DB2Storage<TransmogIllusionEntry> sTransmogIllusionStore;
TC_GAME_API extern DB2Storage<TransmogOutfitEntryEntry> sTransmogOutfitEntryStore;
TC_GAME_API extern DB2Storage<TransmogOutfitSlotInfoEntry> sTransmogOutfitSlotInfoStore;
TC_GAME_API extern DB2Storage<TransmogOutfitSlotOptionEntry> sTransmogOutfitSlotOptionInfoStore;
TC_GAME_API extern DB2Storage<TransmogSituationEntry> sTransmogSituationStore;
TC_GAME_API extern DB2Storage<TransmogSituationGroupEntry> sTransmogSituationGroupStore;
TC_GAME_API extern DB2Storage<TransmogSituationTriggerEntry> sTransmogSituationTriggerStore;
TC_GAME_API extern DB2Storage<TransmogSetEntry> sTransmogSetStore;
TC_GAME_API extern DB2Storage<TransmogSetGroupEntry> sTransmogSetGroupStore;
TC_GAME_API extern DB2Storage<TransmogSetItemEntry> sTransmogSetItemStore;
@@ -499,9 +505,6 @@ public:
ItemClassEntry const* GetItemClassByOldEnum(uint32 itemClass) const;
bool HasItemCurrencyCost(uint32 itemId) const;
std::vector<ItemLimitCategoryConditionEntry const*> const* GetItemLimitCategoryConditions(uint32 categoryId) const;
uint32 GetItemDisplayId(uint32 itemId, uint32 appearanceModId) const;
ItemModifiedAppearanceEntry const* GetItemModifiedAppearance(uint32 itemId, uint32 appearanceModId) const;
ItemModifiedAppearanceEntry const* GetDefaultItemModifiedAppearance(uint32 itemId) const;
std::vector<ItemSetSpellEntry const*> const* GetItemSetSpells(uint32 itemSetId) const;
std::vector<ItemSpecOverrideEntry const*> const* GetItemSpecOverrides(uint32 itemId) const;
JournalTierEntry const* GetJournalTier(uint32 index) const;
@@ -548,9 +551,6 @@ public:
TaxiPathEntry const* GetTaxiPath(uint32 from, uint32 to) const;
static bool IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId, bool requireAllTotems = true);
bool IsToyItem(uint32 toy) const;
TransmogIllusionEntry const* GetTransmogIllusionForEnchantment(uint32 spellItemEnchantmentId) const;
std::vector<TransmogSetEntry const*> const* GetTransmogSetsForItemModifiedAppearance(uint32 itemModifiedAppearanceId) const;
std::vector<TransmogSetItemEntry const*> const* GetTransmogSetItems(uint32 transmogSetId) const;
static bool GetUiMapPosition(float x, float y, float z, int32 mapId, int32 areaId, int32 wmoDoodadPlacementId, int32 wmoGroupId, UiMapSystem system, bool local,
uint32* uiMapId = nullptr, DBCPosition2D* newPos = nullptr);
bool Zone2MapCoordinates(uint32 areaId, float& x, float& y) const;
+84
View File
@@ -4696,6 +4696,57 @@ struct TransmogIllusionEntry
EnumFlag<TransmogIllusionFlags> GetFlags() const { return static_cast<TransmogIllusionFlags>(Flags); }
};
struct TransmogOutfitEntryEntry
{
uint64 Cost;
LocalizedString Name;
uint32 ID;
int32 OrderIndex;
uint8 Source;
int32 Flags;
uint8 SetType;
float OverrideCostModifier;
TransmogOutfitEntrySource GetSource() const { return static_cast<TransmogOutfitEntrySource>(Source); }
bool HasFlag(TransmogOutfitEntryFlags flag) const { return EnumFlag(static_cast<TransmogOutfitEntryFlags>(Flags)).HasFlag(flag); }
TransmogOutfitSetType GetSetType() const { return static_cast<TransmogOutfitSetType>(SetType); }
};
struct TransmogOutfitSlotInfoEntry
{
char const* InventorySlotName;
uint32 ID;
int8 TransmogOutfitSlotEnum;
int32 InventorySlotEnum;
int32 Flags;
uint8 Unused1200;
uint8 TransmogCollectionType;
int32 SecondarySlotID;
int32 InventorySlotID;
int32 UnassignedAtlasID;
int32 UnassignedDisplayAtlasID;
float ItemCostMultiplier;
float IllusionCostMultiplier;
TransmogOutfitSlot GetSlot() const { return static_cast<TransmogOutfitSlot>(TransmogOutfitSlotEnum); }
bool HasFlag(TransmogOutfitSlotFlags flag) const { return EnumFlag(static_cast<TransmogOutfitSlotFlags>(Flags)).HasFlag(flag); }
};
struct TransmogOutfitSlotOptionEntry
{
uint32 ID;
LocalizedString Name;
uint8 OptionEnum;
uint32 TransmogOutfitSlotInfoID;
int32 Flags;
int32 SecondaryOptionID;
float ItemCostMultiplier;
float IllusionCostMultiplier;
TransmogOutfitSlotOption GetOption() const { return static_cast<TransmogOutfitSlotOption>(OptionEnum); }
bool HasFlag(TransmogOutfitSlotOptionFlags flag) const { return EnumFlag(static_cast<TransmogOutfitSlotOptionFlags>(Flags)).HasFlag(flag); }
};
struct TransmogSetEntry
{
LocalizedString Name;
@@ -4727,6 +4778,39 @@ struct TransmogSetItemEntry
int32 Flags;
};
struct TransmogSituationEntry
{
LocalizedString Name;
uint32 ID;
int8 SituationEnum;
int32 Flags;
uint32 TransmogSituationGroupID;
int32 OrderIndex;
TransmogSituation GetSituation() const { return static_cast<TransmogSituation>(SituationEnum); }
bool HasFlag(TransmogSituationFlags flag) const { return EnumFlag(static_cast<TransmogSituationFlags>(Flags)).HasFlag(flag); }
};
struct TransmogSituationGroupEntry
{
uint32 ID;
uint32 TransmogSituationTriggerID;
int32 OrderIndex;
int32 Flags;
};
struct TransmogSituationTriggerEntry
{
LocalizedString Name;
LocalizedString Description;
uint32 ID;
uint8 TriggerEnum;
int32 Flags;
TransmogSituationTrigger GetTrigger() const { return static_cast<TransmogSituationTrigger>(TriggerEnum); }
bool HasFlag(TransmogSituationTriggerFlags flag) const { return EnumFlag(static_cast<TransmogSituationTriggerFlags>(Flags)).HasFlag(flag); }
};
struct TransportAnimationEntry
{
uint32 ID;
+173
View File
@@ -2520,6 +2520,179 @@ enum class TransmogIllusionFlags : int32
DEFINE_ENUM_FLAG(TransmogIllusionFlags);
enum class TransmogOutfitDisplayType : uint8
{
Unassigned = 0,
Assigned = 1,
Equipped = 2,
Hidden = 3,
Disabled = 4,
Max
};
enum class TransmogOutfitEquipAction : uint8
{
Equip = 0,
EquipAndLock = 1,
Remove = 2,
RemoveAndLock = 3,
Unlock = 4,
Lock = 5,
};
enum class TransmogOutfitEntryFlags : int32
{
AutomaticallyAwardedOnLogin = 0x01,
UseOverrideName = 0x02,
OnlyAvailableDuringEvent = 0x04,
SortedToTopOfList = 0x08,
UseOverrideCostModifier = 0x10,
IsDefaultEquipped = 0x20
};
DEFINE_ENUM_FLAG(TransmogOutfitEntryFlags);
enum class TransmogOutfitEntrySource : uint8
{
StampedSource = 0,
AutomaticallyAwarded = 1,
PlayerPurchased = 2,
Max
};
enum class TransmogOutfitSetType : uint8
{
Equipped = 0,
Outfit = 1,
CustomSet = 2
};
enum class TransmogOutfitSlot : int8
{
Head = 0,
ShoulderRight = 1,
ShoulderLeft = 2,
Back = 3,
Chest = 4,
Tabard = 5,
Body = 6,
Wrist = 7,
Hand = 8,
Waist = 9,
Legs = 10,
Feet = 11,
WeaponMainHand = 12,
WeaponOffHand = 13,
WeaponRanged = 14,
Max
};
enum class TransmogOutfitSlotFlags : int32
{
CannotBeHidden = 0x01,
CanHaveIllusions = 0x02,
IsSecondarySlot = 0x04
};
DEFINE_ENUM_FLAG(TransmogOutfitSlotFlags);
enum class TransmogOutfitSlotOption : uint8
{
None = 0,
OneHandedWeapon = 1,
TwoHandedWeapon = 2,
RangedWeapon = 3,
OffHand = 4,
Shield = 5,
DeprecatedReuseMe = 6,
FuryTwoHandedWeapon = 7,
ArtifactSpecOne = 8,
ArtifactSpecTwo = 9,
ArtifactSpecThree = 10,
ArtifactSpecFour = 11,
Max
};
enum class TransmogOutfitSlotOptionFlags : int32
{
IllusionNotAllowed = 0x01,
DynamicOptionName = 0x02,
DisablesOffhandSlot = 0x04
};
DEFINE_ENUM_FLAG(TransmogOutfitSlotOptionFlags);
enum class TransmogSituation : int8
{
AllSpecs = 0,
Spec = 1,
AllLocations = 2,
LocationRested = 3,
LocationHouse = 4,
LocationCharacterSelect = 5,
LocationWorld = 6,
LocationDelves = 7,
LocationDungeons = 8,
LocationRaids = 9,
LocationArenas = 10,
LocationBattlegrounds = 11,
AllMovement = 12,
MovementUnmounted = 13,
MovementSwimming = 14,
MovementGroundMount = 15,
MovementFlyingMount = 16,
AllEquipmentSets = 17,
EquipmentSets = 18,
AllRacialForms = 19,
FormNative = 20,
FormNonNative = 21,
Max
};
enum class TransmogSituationFlags : int32
{
IsPlayerFacing = 0x01,
SpecUseTalentLoadout = 0x02,
AllSituation = 0x04,
DefaultsToOn = 0x08,
DynamicallyNamed = 0x10,
NoneSituation = 0x20,
DisabledSituation = 0x40
};
DEFINE_ENUM_FLAG(TransmogSituationFlags);
enum class TransmogSituationTrigger : uint8
{
None = 0,
Manual = 1,
TransmogUpdate = 2,
Location = 3,
Movement = 4,
Specialization = 5,
EquipmentSet = 6,
Forms = 7,
EventOutfit = 8,
Max
};
enum class TransmogSituationTriggerFlags : int32
{
CanLockOutfit = 0x01,
CanChangeLockedOutfit = 0x02,
IsPlayerFacing = 0x04,
SituationsAreExclusive = 0x08,
DisabledTrigger = 0x10
};
DEFINE_ENUM_FLAG(TransmogSituationTriggerFlags);
// SummonProperties.dbc, col 1
enum SummonPropGroup
{
+10 -50
View File
@@ -46,6 +46,7 @@
#include "SpellMgr.h"
#include "StringConvert.h"
#include "TradeData.h"
#include "TransmogMgr.h"
#include "UpdateData.h"
#include "World.h"
#include "WorldSession.h"
@@ -2010,51 +2011,6 @@ bool Item::IsValidTransmogrificationTarget() const
return true;
}
enum class ItemTransmogrificationWeaponCategory : uint8
{
// Two-handed
MELEE_2H,
RANGED,
// One-handed
AXE_MACE_SWORD_1H,
DAGGER,
INVALID
};
static ItemTransmogrificationWeaponCategory GetTransmogrificationWeaponCategory(ItemTemplate const* proto)
{
if (proto->GetClass() == ITEM_CLASS_WEAPON)
{
switch (proto->GetSubClass())
{
case ITEM_SUBCLASS_WEAPON_AXE2:
case ITEM_SUBCLASS_WEAPON_MACE2:
case ITEM_SUBCLASS_WEAPON_SWORD2:
case ITEM_SUBCLASS_WEAPON_STAFF:
case ITEM_SUBCLASS_WEAPON_POLEARM:
return ItemTransmogrificationWeaponCategory::MELEE_2H;
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_GUN:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
return ItemTransmogrificationWeaponCategory::RANGED;
case ITEM_SUBCLASS_WEAPON_AXE:
case ITEM_SUBCLASS_WEAPON_MACE:
case ITEM_SUBCLASS_WEAPON_SWORD:
case ITEM_SUBCLASS_WEAPON_WARGLAIVES:
case ITEM_SUBCLASS_WEAPON_FIST_WEAPON:
return ItemTransmogrificationWeaponCategory::AXE_MACE_SWORD_1H;
case ITEM_SUBCLASS_WEAPON_DAGGER:
return ItemTransmogrificationWeaponCategory::DAGGER;
default:
break;
}
}
return ItemTransmogrificationWeaponCategory::INVALID;
}
int32 const ItemTransmogrificationSlots[MAX_INVTYPE] =
{
-1, // INVTYPE_NON_EQUIP
@@ -2124,7 +2080,7 @@ bool Item::CanTransmogrifyItemWithItem(Item const* item, ItemModifiedAppearanceE
switch (source->GetClass())
{
case ITEM_CLASS_WEAPON:
if (GetTransmogrificationWeaponCategory(source) != GetTransmogrificationWeaponCategory(target))
if (source->GetWeaponTransmogOutfitSlotOption() != target->GetWeaponTransmogOutfitSlotOption())
return false;
break;
case ITEM_CLASS_ARMOR:
@@ -2495,16 +2451,20 @@ uint32 Item::GetDisplayId(Player const* owner) const
if (!itemModifiedAppearanceId)
itemModifiedAppearanceId = GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS);
if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId))
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(transmog->ItemAppearanceID))
ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId);
if (!itemModifiedAppearance)
itemModifiedAppearance = GetItemModifiedAppearance();
if (itemModifiedAppearance)
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(itemModifiedAppearance->ItemAppearanceID))
return itemAppearance->ItemDisplayInfoID;
return sDB2Manager.GetItemDisplayId(GetEntry(), GetAppearanceModId());
return 0;
}
ItemModifiedAppearanceEntry const* Item::GetItemModifiedAppearance() const
{
return sDB2Manager.GetItemModifiedAppearance(GetEntry(), _bonusData.AppearanceModID);
return TransmogMgr::GetItemModifiedAppearance(GetEntry(), _bonusData.AppearanceModID);
}
uint32 Item::GetModifier(ItemModifier modifier) const
@@ -297,3 +297,49 @@ std::size_t ItemTemplate::CalculateItemSpecBit(ChrSpecializationEntry const* spe
{
return (spec->ClassID - 1) * MAX_SPECIALIZATIONS + spec->OrderIndex;
}
TransmogOutfitSlotOption ItemTemplate::GetWeaponTransmogOutfitSlotOption() const
{
switch (GetClass())
{
case ITEM_CLASS_WEAPON:
switch (GetSubClass())
{
case ITEM_SUBCLASS_WEAPON_AXE2:
case ITEM_SUBCLASS_WEAPON_MACE2:
case ITEM_SUBCLASS_WEAPON_SWORD2:
case ITEM_SUBCLASS_WEAPON_STAFF:
case ITEM_SUBCLASS_WEAPON_POLEARM:
return TransmogOutfitSlotOption::TwoHandedWeapon;
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_GUN:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
return TransmogOutfitSlotOption::RangedWeapon;
case ITEM_SUBCLASS_WEAPON_AXE:
case ITEM_SUBCLASS_WEAPON_MACE:
case ITEM_SUBCLASS_WEAPON_SWORD:
case ITEM_SUBCLASS_WEAPON_WARGLAIVES:
case ITEM_SUBCLASS_WEAPON_FIST_WEAPON:
case ITEM_SUBCLASS_WEAPON_DAGGER:
return TransmogOutfitSlotOption::OneHandedWeapon;
default:
break;
}
break;
case ITEM_CLASS_ARMOR:
switch (GetInventoryType())
{
case INVTYPE_SHIELD:
return TransmogOutfitSlotOption::Shield;
case INVTYPE_HOLDABLE:
return TransmogOutfitSlotOption::OffHand;
default:
break;
}
break;
default:
break;
}
return TransmogOutfitSlotOption::None;
}
@@ -967,6 +967,7 @@ struct TC_GAME_API ItemTemplate
void GetDamage(uint32 itemLevel, float& minDamage, float& maxDamage) const;
bool IsUsableByLootSpecialization(Player const* player, bool alwaysAllowBoundToAccount) const;
static std::size_t CalculateItemSpecBit(ChrSpecializationEntry const* spec);
TransmogOutfitSlotOption GetWeaponTransmogOutfitSlotOption() const;
};
#endif
@@ -882,7 +882,7 @@ void VisibleItem::WriteCreate(ByteBuffer& data, Player const* receiver, Unit con
data << uint16(ItemAppearanceModID);
data << uint16(ItemVisual);
data << uint32(ItemModifiedAppearanceID);
data << uint8(Field_18);
data << uint8(TransmogSlotOption);
data.WriteBit(HasTransmog);
data.WriteBit(HasIllusion);
data.FlushBits();
@@ -936,7 +936,7 @@ void VisibleItem::WriteUpdate(bool ignoreChangesMask, ByteBuffer& data, Player c
}
if (changesMask[9])
{
data << uint8(Field_18);
data << uint8(TransmogSlotOption);
}
}
data.FlushBits();
@@ -952,7 +952,7 @@ void VisibleItem::ClearChangesMask()
Base::ClearChangesMask(ItemAppearanceModID);
Base::ClearChangesMask(ItemVisual);
Base::ClearChangesMask(ItemModifiedAppearanceID);
Base::ClearChangesMask(Field_18);
Base::ClearChangesMask(TransmogSlotOption);
_changesMask.ResetAll();
}
@@ -5444,7 +5444,7 @@ void ActivePlayerData::WriteCreate(EnumFlag<UpdateFieldFlag> fieldVisibilityFlag
data << uint32(HouseThemes.size());
data << uint32(HouseRoomComponentTextures.size());
data << uint32(HouseTypes.size());
data << uint32(Field_1980.size());
data << uint32(UnlockedTransmogOutfits.size());
data << uint32(CharacterRestrictions.size());
data << uint32(SpellPctModByLabel.size());
data << uint32(SpellFlatModByLabel.size());
@@ -5563,9 +5563,9 @@ void ActivePlayerData::WriteCreate(EnumFlag<UpdateFieldFlag> fieldVisibilityFlag
{
data << uint32(HouseTypes[i]);
}
for (uint32 i = 0; i < Field_1980.size(); ++i)
for (uint32 i = 0; i < UnlockedTransmogOutfits.size(); ++i)
{
data << int32(Field_1980[i]);
data << int32(UnlockedTransmogOutfits[i]);
}
for (uint32 i = 0; i < SpellPctModByLabel.size(); ++i)
{
@@ -5979,9 +5979,9 @@ void ActivePlayerData::WriteUpdate(Mask const& changesMask, ByteBuffer& data, Pl
if (changesMask[29])
{
if (!ignoreNestedChangesMask)
Field_1980.WriteUpdateMask(data);
UnlockedTransmogOutfits.WriteUpdateMask(data);
else
WriteCompleteDynamicFieldUpdateMask(Field_1980.size(), data);
WriteCompleteDynamicFieldUpdateMask(UnlockedTransmogOutfits.size(), data);
}
if (changesMask[30])
{
@@ -6321,11 +6321,11 @@ void ActivePlayerData::WriteUpdate(Mask const& changesMask, ByteBuffer& data, Pl
}
if (changesMask[29])
{
for (uint32 i = 0; i < Field_1980.size(); ++i)
for (uint32 i = 0; i < UnlockedTransmogOutfits.size(); ++i)
{
if (Field_1980.HasChanged(i) || ignoreNestedChangesMask)
if (UnlockedTransmogOutfits.HasChanged(i) || ignoreNestedChangesMask)
{
data << int32(Field_1980[i]);
data << int32(UnlockedTransmogOutfits[i]);
}
}
}
@@ -7158,7 +7158,7 @@ void ActivePlayerData::ClearChangesMask()
Base::ClearChangesMask(HouseThemes);
Base::ClearChangesMask(HouseRoomComponentTextures);
Base::ClearChangesMask(HouseTypes);
Base::ClearChangesMask(Field_1980);
Base::ClearChangesMask(UnlockedTransmogOutfits);
Base::ClearChangesMask(SpellPctModByLabel);
Base::ClearChangesMask(SpellFlatModByLabel);
Base::ClearChangesMask(SpellPctModPVPByLabel);
@@ -262,7 +262,7 @@ struct VisibleItem : public IsUpdateFieldStructureTag, public HasChangesMask<10>
UpdateField<uint16, 0, 6> ItemAppearanceModID;
UpdateField<uint16, 0, 7> ItemVisual;
UpdateField<uint32, 0, 8> ItemModifiedAppearanceID;
UpdateField<uint8, 0, 9> Field_18;
UpdateField<uint8, 0, 9> TransmogSlotOption;
using OwnerObject = Unit;
void WriteCreate(ByteBuffer& data, Player const* receiver, Unit const* owner) const;
@@ -1311,7 +1311,7 @@ struct ActivePlayerData : public IsUpdateFieldStructureTag, public HasChangesMas
DynamicUpdateField<uint32, 0, 26> HouseThemes;
DynamicUpdateField<uint32, 0, 27> HouseRoomComponentTextures;
DynamicUpdateField<uint32, 0, 28> HouseTypes;
DynamicUpdateField<int32, 0, 29> Field_1980;
DynamicUpdateField<int32, 0, 29> UnlockedTransmogOutfits;
DynamicUpdateField<UF::SpellPctModByLabel, 0, 31> SpellPctModByLabel;
DynamicUpdateField<UF::SpellFlatModByLabel, 32, 33> SpellFlatModByLabel;
DynamicUpdateField<UF::SpellPctPVPModByLabel, 32, 34> SpellPctModPVPByLabel;
@@ -26,6 +26,7 @@
#include "ObjectMgr.h"
#include "Player.h"
#include "Timer.h"
#include "TransmogMgr.h"
#include "TransmogrificationPackets.h"
#include "WorldSession.h"
#include <boost/dynamic_bitset.hpp>
@@ -95,12 +96,36 @@ namespace
}
}
CollectionMgr::CollectionMgr(WorldSession* owner) : _owner(owner), _appearances(std::make_unique<boost::dynamic_bitset<uint32>>()), _transmogIllusions(std::make_unique<boost::dynamic_bitset<uint32>>())
CollectionMgr::CollectionMgr(WorldSession* owner) : _owner(owner),
_appearances(std::make_unique<boost::dynamic_bitset<uint32>>()),
_transmogIllusions(std::make_unique<boost::dynamic_bitset<uint32>>())
{
}
CollectionMgr::~CollectionMgr() = default;
void CollectionMgr::LoadCharacterData()
{
LoadToys();
LoadHeirlooms();
LoadMounts();
LoadItemAppearances();
LoadTransmogIllusions();
LoadTransmogOutfits();
LoadWarbandScenes();
}
void CollectionMgr::SaveToDB(LoginDatabaseTransaction trans)
{
SaveAccountToys(trans);
SaveAccountHeirlooms(trans);
SaveAccountMounts(trans);
SaveAccountItemAppearances(trans);
SaveAccountTransmogIllusions(trans);
SaveAccountTransmogOutfits(trans);
SaveAccountWarbandScenes(trans);
}
void CollectionMgr::LoadToys()
{
for (auto const& [itemId, flags] : _toys)
@@ -529,7 +554,7 @@ void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearan
for (uint32 hiddenItem : hiddenAppearanceItems)
{
ItemModifiedAppearanceEntry const* hiddenAppearance = sDB2Manager.GetItemModifiedAppearance(hiddenItem, 0);
ItemModifiedAppearanceEntry const* hiddenAppearance = TransmogMgr::GetDefaultItemModifiedAppearance(hiddenItem);
ASSERT(hiddenAppearance);
if (_appearances->size() <= hiddenAppearance->ID)
_appearances->resize(hiddenAppearance->ID + 1);
@@ -603,7 +628,7 @@ void CollectionMgr::AddItemAppearance(Item* item)
void CollectionMgr::AddItemAppearance(uint32 itemId, uint32 appearanceModId /*= 0*/)
{
ItemModifiedAppearanceEntry const* itemModifiedAppearance = sDB2Manager.GetItemModifiedAppearance(itemId, appearanceModId);
ItemModifiedAppearanceEntry const* itemModifiedAppearance = TransmogMgr::GetItemModifiedAppearance(itemId, appearanceModId);
if (!CanAddAppearance(itemModifiedAppearance))
return;
@@ -612,11 +637,7 @@ void CollectionMgr::AddItemAppearance(uint32 itemId, uint32 appearanceModId /*=
void CollectionMgr::AddTransmogSet(uint32 transmogSetId)
{
std::vector<TransmogSetItemEntry const*> const* items = sDB2Manager.GetTransmogSetItems(transmogSetId);
if (!items)
return;
for (TransmogSetItemEntry const* item : *items)
for (TransmogSetItemEntry const* item : TransmogMgr::GetTransmogSetItems(transmogSetId))
{
ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(item->ItemModifiedAppearanceID);
if (!itemModifiedAppearance)
@@ -628,13 +649,13 @@ void CollectionMgr::AddTransmogSet(uint32 transmogSetId)
bool CollectionMgr::IsSetCompleted(uint32 transmogSetId) const
{
std::vector<TransmogSetItemEntry const*> const* transmogSetItems = sDB2Manager.GetTransmogSetItems(transmogSetId);
if (!transmogSetItems)
std::span<TransmogSetItemEntry const* const> transmogSetItems = TransmogMgr::GetTransmogSetItems(transmogSetId);
if (transmogSetItems.empty())
return false;
std::array<int8, EQUIPMENT_SLOT_END> knownPieces;
knownPieces.fill(-1);
for (TransmogSetItemEntry const* transmogSetItem : *transmogSetItems)
for (TransmogSetItemEntry const* transmogSetItem : transmogSetItems)
{
ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(transmogSetItem->ItemModifiedAppearanceID);
if (!itemModifiedAppearance)
@@ -756,18 +777,14 @@ void CollectionMgr::AddItemAppearance(ItemModifiedAppearanceEntry const* itemMod
owner->UpdateCriteria(CriteriaType::LearnAnyTransmogInSlot, transmogSlot, itemModifiedAppearance->ID);
}
if (std::vector<TransmogSetEntry const*> const* sets = sDB2Manager.GetTransmogSetsForItemModifiedAppearance(itemModifiedAppearance->ID))
for (TransmogSetEntry const* set : TransmogMgr::GetTransmogSetsForItemModifiedAppearance(itemModifiedAppearance->ID))
{
for (TransmogSetEntry const* set : *sets)
if (IsSetCompleted(set->ID))
{
if (IsSetCompleted(set->ID))
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(set->TrackingQuestID))
owner->RewardQuest(quest, LootItemType::Item, 0, owner, false);
if (Quest const* quest = sObjectMgr->GetQuestTemplate(set->TrackingQuestID))
owner->RewardQuest(quest, LootItemType::Item, 0, owner, false);
owner->UpdateCriteria(CriteriaType::CollectTransmogSetFromGroup, set->TransmogSetGroupID);
}
owner->UpdateCriteria(CriteriaType::CollectTransmogSetFromGroup, set->TransmogSetGroupID);
}
}
}
@@ -966,6 +983,55 @@ bool CollectionMgr::HasTransmogIllusion(uint32 transmogIllusionId) const
return transmogIllusionId < _transmogIllusions->size() && _transmogIllusions->test(transmogIllusionId);
}
void CollectionMgr::LoadTransmogOutfits()
{
_owner->GetPlayer()->AddUnlockedTransmogOutfits(_transmogOutfits);
}
void CollectionMgr::LoadAccountTransmogOutfits(PreparedQueryResult unlockedTransmogOutfits)
{
if (unlockedTransmogOutfits)
{
do
{
Field* fields = unlockedTransmogOutfits->Fetch();
int32 transmogOutfitId = fields[0].GetInt32();
if (!sTransmogOutfitEntryStore.HasRecord(transmogOutfitId))
continue;
_transmogOutfits.insert(transmogOutfitId);
} while (unlockedTransmogOutfits->NextRow());
}
for (TransmogOutfitEntryEntry const* transmogOutfitEntry : TransmogMgr::GetAutomaticallyUnlockedOutfits())
_transmogOutfits.insert(transmogOutfitEntry->ID);
}
void CollectionMgr::SaveAccountTransmogOutfits(LoginDatabaseTransaction trans)
{
for (int32 transmogOutfitId : _transmogOutfits)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_TRANSMOG_OUTFITS);
stmt->setUInt32(0, _owner->GetBattlenetAccountId());
stmt->setInt32(1, transmogOutfitId);
trans->Append(stmt);
}
}
void CollectionMgr::AddTransmogOutfit(int32 transmogOutfitId)
{
if (_transmogOutfits.insert(transmogOutfitId).second)
_owner->GetPlayer()->AddUnlockedTransmogOutfit(transmogOutfitId);
}
bool CollectionMgr::HasTransmogOutfit(int32 transmogOutfitId) const
{
return _transmogOutfits.contains(transmogOutfitId);
}
void CollectionMgr::LoadWarbandScenes()
{
Player* owner = _owner->GetPlayer();
@@ -21,6 +21,7 @@
#include "Define.h"
#include "DatabaseEnvFwd.h"
#include "EnumFlag.h"
#include "FlatSet.h"
#include "ObjectGuid.h"
#include <boost/dynamic_bitset_fwd.hpp>
#include <map>
@@ -117,6 +118,9 @@ public:
static void LoadMountDefinitions();
static void LoadWarbandSceneDefinitions();
void LoadCharacterData();
void SaveToDB(LoginDatabaseTransaction trans);
// Account-wide toys
void LoadToys();
void LoadAccountToys(PreparedQueryResult result);
@@ -179,6 +183,12 @@ public:
void AddTransmogIllusion(uint32 transmogIllusionId);
bool HasTransmogIllusion(uint32 transmogIllusionId) const;
void LoadTransmogOutfits();
void LoadAccountTransmogOutfits(PreparedQueryResult unlockedTransmogOutfits);
void SaveAccountTransmogOutfits(LoginDatabaseTransaction trans);
void AddTransmogOutfit(int32 transmogOutfitId);
bool HasTransmogOutfit(int32 transmogOutfitId) const;
// Warband Scenes
void LoadWarbandScenes();
void LoadAccountWarbandScenes(PreparedQueryResult knownWarbandScenes);
@@ -204,6 +214,7 @@ private:
std::unordered_map<uint32, std::unordered_set<ObjectGuid>> _temporaryAppearances;
std::unordered_map<uint32, CollectionItemState> _favoriteAppearances;
std::unique_ptr<boost::dynamic_bitset<uint32>> _transmogIllusions;
Trinity::Containers::FlatSet<int32> _transmogOutfits;
WarbandSceneCollectionContainer _warbandScenes;
};
+683 -85
View File
@@ -131,6 +131,8 @@
#include "TradeData.h"
#include "TraitMgr.h"
#include "TraitPacketsCommon.h"
#include "TransmogMgr.h"
#include "TransmogrificationPackets.h"
#include "Transport.h"
#include "UpdateData.h"
#include "Util.h"
@@ -559,6 +561,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac
GetThreatManager().Initialize();
EquipTransmogOutfit(0, TransmogSituationTrigger::Manual, false);
return true;
}
@@ -4146,6 +4150,18 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt64(0, guid);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFIT_SITUATION_BY_CHAR);
stmt->setUInt64(0, guid);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFIT_SLOT_BY_CHAR);
stmt->setUInt64(0, guid);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_TRANSMOG_OUTFIT_BY_CHAR);
stmt->setUInt64(0, guid);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER);
stmt->setUInt64(0, guid);
stmt->setUInt64(1, guid);
@@ -4521,19 +4537,10 @@ Corpse* Player::CreateCorpse()
corpse->SetFactionTemplate(sChrRacesStore.AssertEntry(GetRace())->FactionID);
for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
{
if (m_items[i])
{
uint32 itemDisplayId = m_items[i]->GetDisplayId(this);
uint32 itemInventoryType;
if (ItemEntry const* itemEntry = sItemStore.LookupEntry(m_items[i]->GetVisibleEntry(this)))
itemInventoryType = itemEntry->InventoryType;
else
itemInventoryType = m_items[i]->GetTemplate()->GetInventoryType();
corpse->SetItem(i, itemDisplayId | (itemInventoryType << 24));
}
}
if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(m_playerData->VisibleItems[i].ItemModifiedAppearanceID))
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(itemModifiedAppearance->ItemAppearanceID))
if (ItemEntry const* item = sItemStore.LookupEntry(itemModifiedAppearance->ItemID))
corpse->SetItem(i, itemAppearance->ItemDisplayInfoID | int32(item->InventoryType) << 24);
// register for player, but not show
GetMap()->AddCorpse(corpse);
@@ -6176,6 +6183,7 @@ bool Player::IsActionButtonDataValid(uint8 button, uint64 action, uint8 type) co
case ACTION_BUTTON_MACRO:
case ACTION_BUTTON_EQSET:
case ACTION_BUTTON_DROPDOWN:
case ACTION_BUTTON_OUTFIT:
break;
default:
TC_LOG_ERROR("entities.player", "Player::IsActionButtonDataValid: Unknown action type {}", type);
@@ -11825,25 +11833,123 @@ void Player::QuickEquipItem(uint16 pos, Item* pItem)
}
}
void Player::SetVisibleItemSlot(uint8 slot, Item* pItem)
void Player::SetVisibleItemSlot(uint8 slot, Item const* item)
{
auto itemField = m_values.ModifyValue(&Player::m_playerData).ModifyValue(&UF::PlayerData::VisibleItems, slot);
if (pItem)
auto setVisibleItemSlot = [this](uint32 slot, int32 itemId, int32 secondaryItemModifiedAppearanceId, int32 conditionalItemAppearanceId,
uint16 itemAppearanceModId, uint16 itemVisual, uint32 itemModifiedAppearanceId, TransmogOutfitSlotOption transmogSlotOption,
bool hasTransmog, bool hasIllusion)
{
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemID), pItem->GetVisibleEntry(this));
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::SecondaryItemModifiedAppearanceID), pItem->GetVisibleSecondaryModifiedAppearanceId(this));
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemAppearanceModID), pItem->GetVisibleAppearanceModId(this));
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemVisual), pItem->GetVisibleItemVisual(this));
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemModifiedAppearanceID), pItem->GetVisibleModifiedAppearanceId(this));
auto itemField = m_values.ModifyValue(&Player::m_playerData).ModifyValue(&UF::PlayerData::VisibleItems, slot);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemID), itemId);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::SecondaryItemModifiedAppearanceID), secondaryItemModifiedAppearanceId);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ConditionalItemAppearanceID), conditionalItemAppearanceId);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemAppearanceModID), itemAppearanceModId);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemVisual), itemVisual);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemModifiedAppearanceID), itemModifiedAppearanceId);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::TransmogSlotOption), AsUnderlyingType(transmogSlotOption));
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::HasTransmog), hasTransmog);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::HasIllusion), hasIllusion);
auto transmogMetadata = m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::TransmogMetadata);
if (slot == EQUIPMENT_SLOT_MAINHAND)
SetUpdateFieldValue(transmogMetadata.ModifyValue(&UF::TransmogOutfitMetadata::StampedOptionMainHand), AsUnderlyingType(transmogSlotOption));
else if (slot == EQUIPMENT_SLOT_OFFHAND)
SetUpdateFieldValue(transmogMetadata.ModifyValue(&UF::TransmogOutfitMetadata::StampedOptionOffHand), AsUnderlyingType(transmogSlotOption));
};
if (item)
{
TransmogOutfitSlotOption transmogSlotOption = item->GetTemplate()->GetWeaponTransmogOutfitSlotOption();
if (transmogSlotOption == TransmogOutfitSlotOption::TwoHandedWeapon && CanTitanGrip())
transmogSlotOption = TransmogOutfitSlotOption::FuryTwoHandedWeapon;
int32 itemId = item->GetVisibleEntry(this);
int32 secondaryItemModifiedAppearanceId = item->GetVisibleSecondaryModifiedAppearanceId(this);
int32 conditionalItemAppearanceId = 0;
uint16 itemAppearanceModId = item->GetVisibleAppearanceModId(this);
uint16 itemVisual = item->GetVisibleItemVisual(this);
uint32 itemModifiedAppearanceId = item->GetVisibleModifiedAppearanceId(this);
bool hasTransmog = false;
bool hasIllusion = false;
if (!m_activePlayerData->ViewedOutfit->Slots.empty())
{
TransmogMgr::TransmogOutfitSlotAndOptionInfo const* slotInfo = TransmogMgr::GetSlotAndOption(EquipmentSlots(slot), transmogSlotOption);
if (transmogSlotOption != TransmogOutfitSlotOption::None)
{
// check if artifact override is active
TransmogOutfitSlotOption artifactOption = static_cast<TransmogOutfitSlotOption>(AsUnderlyingType(TransmogOutfitSlotOption::ArtifactSpecOne) + GetPrimarySpecializationEntry()->OrderIndex);
TransmogMgr::TransmogOutfitSlotAndOptionInfo const* artifactSlotInfo = TransmogMgr::GetSlotAndOption(EquipmentSlots(slot), artifactOption);
if (artifactSlotInfo && static_cast<TransmogOutfitDisplayType>(*m_activePlayerData->ViewedOutfit->Slots[artifactSlotInfo->SlotIndex].AppearanceDisplayType) == TransmogOutfitDisplayType::Assigned)
{
transmogSlotOption = artifactOption;
slotInfo = artifactSlotInfo;
}
}
if (slotInfo)
{
auto isTransmogDisplayed = [](TransmogOutfitDisplayType displayType)
{
return displayType == TransmogOutfitDisplayType::Assigned || displayType == TransmogOutfitDisplayType::Hidden;
};
UF::TransmogOutfitSlotData const& transmogOutfitItem = m_activePlayerData->ViewedOutfit->Slots[slotInfo->SlotIndex];
if (isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*transmogOutfitItem.AppearanceDisplayType)))
{
if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(transmogOutfitItem.ItemModifiedAppearanceID))
{
itemId = itemModifiedAppearance->ItemID;
itemAppearanceModId = itemModifiedAppearance->ItemAppearanceModifierID;
itemModifiedAppearanceId = itemModifiedAppearance->ID;
}
hasTransmog = true;
}
if (TransmogOutfitSlotInfoEntry const* secondarySlot = sTransmogOutfitSlotInfoStore.LookupEntry(slotInfo->Slot->SecondarySlotID))
{
if (TransmogMgr::TransmogOutfitSlotAndOptionInfo const* secondarySlotInfo = TransmogMgr::GetSlotAndOption(secondarySlot->GetSlot(), transmogSlotOption))
{
UF::TransmogOutfitSlotData const& secondaryTransmogOutfitItem = m_activePlayerData->ViewedOutfit->Slots[secondarySlotInfo->SlotIndex];
if (isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*secondaryTransmogOutfitItem.AppearanceDisplayType))
|| static_cast<TransmogOutfitDisplayType>(*secondaryTransmogOutfitItem.AppearanceDisplayType) == TransmogOutfitDisplayType::Equipped)
{
secondaryItemModifiedAppearanceId = secondaryTransmogOutfitItem.ItemModifiedAppearanceID;
hasTransmog = true;
}
}
}
if (TransmogOutfitSlotOptionEntry const* secondarySlotOption = sTransmogOutfitSlotOptionInfoStore.LookupEntry(slotInfo->SlotOption ? slotInfo->SlotOption->SecondaryOptionID : 0))
{
if (TransmogMgr::TransmogOutfitSlotAndOptionInfo const* secondarySlotInfo = TransmogMgr::GetSlotAndOption(slotInfo->Slot->GetSlot(), secondarySlotOption->GetOption()))
{
UF::TransmogOutfitSlotData const& secondaryTransmogOutfitItem = m_activePlayerData->ViewedOutfit->Slots[secondarySlotInfo->SlotIndex];
if (isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*secondaryTransmogOutfitItem.AppearanceDisplayType))
|| static_cast<TransmogOutfitDisplayType>(*secondaryTransmogOutfitItem.AppearanceDisplayType) == TransmogOutfitDisplayType::Equipped)
{
secondaryItemModifiedAppearanceId = secondaryTransmogOutfitItem.ItemModifiedAppearanceID;
hasTransmog = true;
}
}
}
if (isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*transmogOutfitItem.IllusionDisplayType)))
{
if (SpellItemEnchantmentEntry const* spellItemEnchantment = sSpellItemEnchantmentStore.LookupEntry(transmogOutfitItem.SpellItemEnchantmentID))
itemVisual = spellItemEnchantment->ItemVisual;
hasIllusion = true;
}
}
}
setVisibleItemSlot(slot, itemId, secondaryItemModifiedAppearanceId, conditionalItemAppearanceId, itemAppearanceModId,
itemVisual, itemModifiedAppearanceId, transmogSlotOption, hasTransmog, hasIllusion);
}
else
{
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemID), 0);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::SecondaryItemModifiedAppearanceID), 0);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemAppearanceModID), 0);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemVisual), 0);
SetUpdateFieldValue(itemField.ModifyValue(&UF::VisibleItem::ItemModifiedAppearanceID), 0);
}
setVisibleItemSlot(slot, 0, 0, 0, 0, 0, 0, TransmogOutfitSlotOption::None, false, false);
}
void Player::VisualizeItem(uint8 slot, Item* pItem)
@@ -17579,7 +17685,7 @@ void Player::_LoadEquipmentSets(PreparedQueryResult result)
} while (result->NextRow());
}
void Player::_LoadTransmogOutfits(PreparedQueryResult result)
void Player::_LoadTransmogCustomSets(PreparedQueryResult result)
{
// 0 1 2 3 4 5 6 7 8 9
//SELECT setguid, setindex, name, iconname, ignore_mask, appearance0, appearance1, appearance2, appearance3, appearance4,
@@ -17616,6 +17722,153 @@ void Player::_LoadTransmogOutfits(PreparedQueryResult result)
} while (result->NextRow());
}
void Player::_LoadTransmogOutfits(PreparedQueryResult setsResult, PreparedQueryResult situationsResult, PreparedQueryResult slotsResult,
int32 equippedTransmogOutfitId, bool locked)
{
struct SetData
{
std::vector<WorldPackets::Transmogrification::TransmogOutfitSituationInfo> situations;
std::vector<WorldPackets::Transmogrification::TransmogOutfitSlotData> slots;
};
std::unordered_map<uint32, SetData> sets;
// SELECT transmogOutfitId, situationID, specID, loadoutID, equipmentSetID FROM character_transmog_outfit_situation WHERE guid = ?
if (situationsResult)
{
do
{
DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (transmogOutfitId)(situationID)(specID)(loadoutID)(equipmentSetID)) fields { *situationsResult };
uint32 transmogOutfitId = fields.transmogOutfitId().GetUInt32();
WorldPackets::Transmogrification::TransmogOutfitSituationInfo& situation = sets[transmogOutfitId].situations.emplace_back();
situation.SituationID = fields.situationID().GetUInt32();
situation.SpecID = fields.specID().GetUInt32();
situation.LoadoutID = fields.loadoutID().GetUInt32();
situation.EquipmentSetID = fields.equipmentSetID().GetUInt32();
} while (situationsResult->NextRow());
}
// SELECT transmogOutfitId, slot, slotOption, itemModifiedAppearanceID, appearanceDisplayType, spellItemEnchantmentID, illusionDisplayType, flags FROM character_transmog_outfit_slot guid = ?
if (slotsResult)
{
do
{
DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (transmogOutfitId)(slot)(slotOption)(itemModifiedAppearanceID)(appearanceDisplayType)
(spellItemEnchantmentID)(illusionDisplayType)(flags)) fields { *slotsResult };
uint32 transmogOutfitId = fields.transmogOutfitId().GetUInt32();
WorldPackets::Transmogrification::TransmogOutfitSlotData& slot = sets[transmogOutfitId].slots.emplace_back();
slot.Slot = static_cast<TransmogOutfitSlot>(fields.slot().GetInt8());
slot.SlotOption = static_cast<TransmogOutfitSlotOption>(fields.slotOption().GetUInt8());
slot.ItemModifiedAppearanceID = fields.itemModifiedAppearanceID().GetUInt32();
slot.AppearanceDisplayType = static_cast<TransmogOutfitDisplayType>(fields.appearanceDisplayType().GetUInt8());
slot.SpellItemEnchantmentID = fields.spellItemEnchantmentID().GetUInt32();
slot.IllusionDisplayType = static_cast<TransmogOutfitDisplayType>(fields.illusionDisplayType().GetUInt8());
slot.Flags = fields.flags().GetUInt32();
} while (slotsResult->NextRow());
}
auto activePlayerData = m_values.ModifyValue(&Player::m_activePlayerData);
// SELECT transmogOutfitId, name, icon, situationsEnabled FROM character_transmog_outfit WHERE guid = ?
if (setsResult)
{
do
{
DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (transmogOutfitId)(name)(icon)(situationsEnabled)) fields { *setsResult };
uint32 transmogOutfitId = fields.transmogOutfitId().GetUInt32();
TransmogOutfitEntryEntry const* transmogOutfitEntry = sTransmogOutfitEntryStore.LookupEntry(transmogOutfitId);
if (!transmogOutfitEntry || transmogOutfitEntry->GetSetType() == TransmogOutfitSetType::CustomSet)
continue;
if (transmogOutfitEntry->HasFlag(TransmogOutfitEntryFlags::OnlyAvailableDuringEvent) && !IsHolidayActive(HOLIDAY_TRIAL_OF_STYLE))
continue;
WorldPackets::Transmogrification::TransmogOutfitDataInfo outfitInfo;
outfitInfo.SetType = transmogOutfitEntry->GetSetType();
outfitInfo.Name = fields.name().GetString();
outfitInfo.Icon = fields.icon().GetUInt32();
outfitInfo.SituationsEnabled = fields.situationsEnabled().GetBool();
auto transmogOutfit = activePlayerData.ModifyValue(&UF::ActivePlayerData::TransmogOutfits, transmogOutfitId);
InitializeNewTransmogOutfit(transmogOutfit, transmogOutfitId, outfitInfo);
auto setData = sets.extract(transmogOutfitId);
if (!setData)
continue;
auto situations = transmogOutfit.ModifyValue(&UF::TransmogOutfitData::Situations);
ClearDynamicUpdateFieldValues(situations);
for (WorldPackets::Transmogrification::TransmogOutfitSituationInfo const& situationInfo : setData.mapped().situations)
{
auto situation = AddDynamicUpdateFieldValue(situations);
situation.ModifyValue(&UF::TransmogOutfitSituationInfo::SituationID).SetValue(situationInfo.SituationID);
situation.ModifyValue(&UF::TransmogOutfitSituationInfo::SpecID).SetValue(situationInfo.SpecID);
situation.ModifyValue(&UF::TransmogOutfitSituationInfo::LoadoutID).SetValue(situationInfo.LoadoutID);
situation.ModifyValue(&UF::TransmogOutfitSituationInfo::EquipmentSetID).SetValue(situationInfo.EquipmentSetID);
}
for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slotData : setData.mapped().slots)
{
TransmogMgr::TransmogOutfitSlotAndOptionInfo const* slotInfo = TransmogMgr::GetSlotAndOption(slotData.Slot, slotData.SlotOption);
if (!slotInfo)
continue;
auto slot = transmogOutfit.ModifyValue(&UF::TransmogOutfitData::Slots, slotInfo->SlotIndex);
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::Slot), AsUnderlyingType(slotData.Slot));
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::SlotOption), AsUnderlyingType(slotData.SlotOption));
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::ItemModifiedAppearanceID), slotData.ItemModifiedAppearanceID);
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::AppearanceDisplayType), AsUnderlyingType(slotData.AppearanceDisplayType));
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::SpellItemEnchantmentID), slotData.SpellItemEnchantmentID);
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::IllusionDisplayType), AsUnderlyingType(slotData.IllusionDisplayType));
SetUpdateFieldValue(slot.ModifyValue(&UF::TransmogOutfitSlotData::Flags), slotData.Flags);
}
} while (setsResult->NextRow());
}
for (int32 transmogOutfitId : m_activePlayerData->UnlockedTransmogOutfits)
{
TransmogOutfitEntryEntry const* transmogOutfitEntry = sTransmogOutfitEntryStore.AssertEntry(transmogOutfitId);
if (transmogOutfitEntry->HasFlag(TransmogOutfitEntryFlags::OnlyAvailableDuringEvent) && !IsHolidayActive(HOLIDAY_TRIAL_OF_STYLE))
continue;
WorldPackets::Transmogrification::TransmogOutfitDataInfo outfitData;
outfitData.SetType = transmogOutfitEntry->GetSetType();
outfitData.SituationsEnabled = false;
outfitData.Icon = TransmogMgr::DefaultOutfitIcon;
outfitData.Name = TransmogMgr::DefaultOutfitName[GetSession()->GetSessionDbcLocale()];
if (transmogOutfitEntry->Name.Str[GetSession()->GetSessionDbcLocale()][0])
outfitData.Name = transmogOutfitEntry->Name.Str[GetSession()->GetSessionDbcLocale()];
switch (transmogOutfitEntry->GetSetType())
{
case TransmogOutfitSetType::Equipped:
{
outfitData.SituationsEnabled = true;
auto equippedOutfit = activePlayerData.ModifyValue(&UF::ActivePlayerData::ViewedOutfit);
InitializeNewTransmogOutfit(equippedOutfit, transmogOutfitId, outfitData);
break;
}
case TransmogOutfitSetType::Outfit:
if (!m_activePlayerData->TransmogOutfits.Get(transmogOutfitId))
CreateTransmogOutfit(transmogOutfitId, outfitData);
break;
case TransmogOutfitSetType::CustomSet:
default:
break;
}
}
EquipTransmogOutfit(equippedTransmogOutfitId, TransmogSituationTrigger::Manual, locked);
}
void Player::_LoadBGData(PreparedQueryResult result)
{
if (!result)
@@ -17717,7 +17970,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
// "totalKills, todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, "
// "health, power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, instance_id, activeTalentGroup, lootSpecId, exploredZones, knownTitles, actionBars, "
// "raidDifficulty, legacyRaidDifficulty, fishingSteps, honor, honorLevel, honorRestState, honorRestBonus, numRespecs, "
// "personalTabardEmblemStyle, personalTabardEmblemColor, personalTabardBorderStyle, personalTabardBorderColor, personalTabardBackgroundColor "
// "personalTabardEmblemStyle, personalTabardEmblemColor, personalTabardBorderStyle, personalTabardBorderColor, personalTabardBackgroundColor, transmogOutfitEquippedId, transmogOutfitLocked "
// "FROM characters c LEFT JOIN character_fishingsteps cfs ON c.guid = cfs.guid WHERE c.guid = ?", CONNECTION_ASYNC);
ObjectGuid::LowType guid;
@@ -17795,6 +18048,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
int32 personalTabardBorderStyle;
int32 personalTabardBorderColor;
int32 personalTabardBackgroundColor;
int32 transmogOutfitEquippedId;
bool transmogOutfitLocked;
explicit PlayerLoadData(Field const* fields)
{
@@ -17876,6 +18131,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
personalTabardBorderStyle = fields[i++].GetInt32();
personalTabardBorderColor = fields[i++].GetInt32();
personalTabardBackgroundColor = fields[i++].GetInt32();
transmogOutfitEquippedId = fields[i++].GetInt32();
transmogOutfitLocked = fields[i++].GetBool();
}
} fields(result->Fetch());
@@ -18379,12 +18636,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
_LoadTalents(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadPvpTalents(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PVP_TALENTS));
_LoadSpells(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_FAVORITES));
GetSession()->GetCollectionMgr()->LoadToys();
GetSession()->GetCollectionMgr()->LoadHeirlooms();
GetSession()->GetCollectionMgr()->LoadMounts();
GetSession()->GetCollectionMgr()->LoadItemAppearances();
GetSession()->GetCollectionMgr()->LoadTransmogIllusions();
GetSession()->GetCollectionMgr()->LoadWarbandScenes();
GetSession()->GetCollectionMgr()->LoadCharacterData();
LearnSpecializationSpells();
@@ -18560,6 +18812,9 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
}
}
if (extraflags & PLAYER_EXTRA_FLAG_FREE_TRANSMOG_CLAIMED)
SetPlayerLocalFlag(PLAYER_LOCAL_FLAG_FREE_TRANSMOG_CLAIMED);
InitPvP();
// RaF stuff.
@@ -18569,7 +18824,11 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
_LoadDeclinedNames(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
_LoadEquipmentSets(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS));
_LoadTransmogOutfits(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFITS));
_LoadTransmogCustomSets(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFITS));
_LoadTransmogOutfits(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT),
holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT_SITUATION),
holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT_SLOT),
fields.transmogOutfitEquippedId, fields.transmogOutfitLocked);
_LoadCUFProfiles(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES));
@@ -20314,27 +20573,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
}
stmt->setString(index++, ss.str());
ss.str("");
// cache equipment...
for (uint32 i = 0; i < REAGENT_BAG_SLOT_END; ++i)
{
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
ss << uint32(item->GetTemplate()->GetInventoryType()) << ' ' << item->GetDisplayId(this) << ' ';
if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId(this)))
ss << enchant->ItemVisual;
else
ss << '0';
ss << ' '
<< uint32(sItemStore.AssertEntry(item->GetVisibleEntry(this))->SubclassID) << ' '
<< uint32(item->GetVisibleSecondaryModifiedAppearanceId(this)) << ' ';
}
else
ss << "0 0 0 0 0 ";
}
stmt->setString(index++, ss.str());
stmt->setString(index++, GetCharacterSelectOutfit());
ss.str("");
for (uint32 i = 0; i < m_activePlayerData->KnownTitles.size(); ++i)
@@ -20349,6 +20588,14 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
stmt->setUInt32(index++, ClientBuild::GetMinorMajorBugfixVersionForBuild(currentRealm->Build));
else
stmt->setUInt32(index++, 0);
stmt->setInt32(index++, m_playerData->PersonalTabard->EmblemStyle);
stmt->setInt32(index++, m_playerData->PersonalTabard->EmblemColor);
stmt->setInt32(index++, m_playerData->PersonalTabard->BorderStyle);
stmt->setInt32(index++, m_playerData->PersonalTabard->BorderColor);
stmt->setInt32(index++, m_playerData->PersonalTabard->BackgroundColor);
stmt->setInt32(index++, m_activePlayerData->TransmogMetadata->TransmogOutfitID);
stmt->setBool(index++, m_activePlayerData->TransmogMetadata->Locked);
}
else
{
@@ -20473,27 +20720,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
}
stmt->setString(index++, ss.str());
ss.str("");
// cache equipment...
for (uint32 i = 0; i < REAGENT_BAG_SLOT_END; ++i)
{
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
ss << uint32(item->GetTemplate()->GetInventoryType()) << ' ' << item->GetDisplayId(this) << ' ';
if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId(this)))
ss << enchant->ItemVisual;
else
ss << '0';
ss << ' '
<< uint32(sItemStore.AssertEntry(item->GetVisibleEntry(this))->SubclassID) << ' '
<< uint32(item->GetVisibleSecondaryModifiedAppearanceId(this)) << ' ';
}
else
ss << "0 0 0 0 0 ";
}
stmt->setString(index++, ss.str());
stmt->setString(index++, GetCharacterSelectOutfit());
ss.str("");
for (uint32 i = 0; i < m_activePlayerData->KnownTitles.size(); ++i)
@@ -20515,6 +20742,14 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
else
stmt->setUInt32(index++, 0);
stmt->setInt32(index++, m_playerData->PersonalTabard->EmblemStyle);
stmt->setInt32(index++, m_playerData->PersonalTabard->EmblemColor);
stmt->setInt32(index++, m_playerData->PersonalTabard->BorderStyle);
stmt->setInt32(index++, m_playerData->PersonalTabard->BorderColor);
stmt->setInt32(index++, m_playerData->PersonalTabard->BackgroundColor);
stmt->setInt32(index++, m_activePlayerData->TransmogMetadata->TransmogOutfitID);
stmt->setBool(index++, m_activePlayerData->TransmogMetadata->Locked);
// Index
stmt->setUInt64(index, GetGUID().GetCounter());
}
@@ -20554,6 +20789,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
m_reputationMgr->SaveToDB(trans);
m_questObjectiveCriteriaMgr->SaveToDB(trans);
_SaveEquipmentSets(trans);
_SaveTransmogOutfits(trans);
GetSession()->SaveTutorialsData(trans); // changed only while character in game
_SaveInstanceTimeRestrictions(trans);
_SaveCurrency(trans);
@@ -20569,13 +20805,8 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
_SaveStats(trans);
// TODO: Move this out
GetSession()->GetCollectionMgr()->SaveAccountToys(loginTransaction);
GetSession()->GetCollectionMgr()->SaveToDB(loginTransaction);
GetSession()->GetBattlePetMgr()->SaveToDB(loginTransaction);
GetSession()->GetCollectionMgr()->SaveAccountHeirlooms(loginTransaction);
GetSession()->GetCollectionMgr()->SaveAccountMounts(loginTransaction);
GetSession()->GetCollectionMgr()->SaveAccountItemAppearances(loginTransaction);
GetSession()->GetCollectionMgr()->SaveAccountTransmogIllusions(loginTransaction);
GetSession()->GetCollectionMgr()->SaveAccountWarbandScenes(loginTransaction);
GetSession()->SavePlayerDataAccount(loginTransaction);
Battlenet::RealmHandle currentRealmId = sRealmList->GetCurrentRealmId();
@@ -28047,6 +28278,71 @@ void Player::_SaveEquipmentSets(CharacterDatabaseTransaction trans)
}
}
void Player::_SaveTransmogOutfits(CharacterDatabaseTransaction trans)
{
CharacterDatabasePreparedStatement* stmt = nullptr;
ObjectGuid::LowType guid = GetGUID().GetCounter();
for (uint32 transmogOutfitId : m_changedTransmogOutfits)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TRANSMOG_OUTFIT_SLOT);
stmt->setUInt64(0, guid);
stmt->setUInt32(1, transmogOutfitId);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TRANSMOG_OUTFIT_SITUATION);
stmt->setUInt64(0, guid);
stmt->setUInt32(1, transmogOutfitId);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TRANSMOG_OUTFIT_2);
stmt->setUInt64(0, guid);
stmt->setUInt32(1, transmogOutfitId);
trans->Append(stmt);
UF::TransmogOutfitData const* transmogOutfit = m_activePlayerData->TransmogOutfits.Get(transmogOutfitId);
if (!transmogOutfit)
continue;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_TRANSMOG_OUTFIT_2);
stmt->setUInt64(0, guid);
stmt->setUInt32(1, transmogOutfitId);
stmt->setString(2, *transmogOutfit->OutfitInfo->Name);
stmt->setUInt32(3, *transmogOutfit->OutfitInfo->Icon);
stmt->setBool(4, *transmogOutfit->OutfitInfo->SituationsEnabled);
trans->Append(stmt);
for (UF::TransmogOutfitSituationInfo const& situation : transmogOutfit->Situations)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_TRANSMOG_OUTFIT_SITUATION);
stmt->setUInt64(0, guid);
stmt->setUInt32(1, transmogOutfitId);
stmt->setUInt32(2, *situation.SituationID);
stmt->setUInt32(3, *situation.SpecID);
stmt->setUInt32(4, *situation.LoadoutID);
stmt->setUInt32(5, *situation.EquipmentSetID);
trans->Append(stmt);
}
for (UF::TransmogOutfitSlotData const& slot : transmogOutfit->Slots)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_TRANSMOG_OUTFIT_SLOT);
stmt->setUInt64(0, guid);
stmt->setUInt32(1, transmogOutfitId);
stmt->setInt8(2, *slot.Slot);
stmt->setUInt8(3, *slot.SlotOption);
stmt->setUInt32(4, *slot.ItemModifiedAppearanceID);
stmt->setUInt8(5, *slot.AppearanceDisplayType);
stmt->setUInt32(6, *slot.SpellItemEnchantmentID);
stmt->setUInt8(7, *slot.IllusionDisplayType);
stmt->setUInt32(8, *slot.Flags);
trans->Append(stmt);
}
}
m_changedTransmogOutfits.clear();
}
void Player::_SaveBGData(CharacterDatabaseTransaction trans)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_BGDATA);
@@ -30891,6 +31187,308 @@ void Player::UpdateWarModeAuras()
}
}
void Player::AddUnlockedTransmogOutfits(std::span<int32 const> transmogOutfitIds)
{
auto unlockedTransmogOutfits = m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::UnlockedTransmogOutfits);
for (int32 transmogOutfitId : transmogOutfitIds)
AddDynamicUpdateFieldValue(unlockedTransmogOutfits) = transmogOutfitId;
}
void Player::CreateTransmogOutfit(uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const& outfitData)
{
auto outfit = m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::TransmogOutfits, id);
InitializeNewTransmogOutfit(outfit, id, outfitData);
m_changedTransmogOutfits.insert(id);
}
void Player::InitializeNewTransmogOutfit(UF::MutableFieldReference<UF::TransmogOutfitData, false> outfit,
uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const& outfitData)
{
SetUpdateFieldValue(outfit.ModifyValue(&UF::TransmogOutfitData::Id), id);
auto outfitInfo = outfit.ModifyValue(&UF::TransmogOutfitData::OutfitInfo);
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::SetType), AsUnderlyingType(outfitData.SetType));
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::SituationsEnabled), outfitData.SituationsEnabled);
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::Icon), outfitData.Icon);
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::Name), std::string(outfitData.Name));
auto situations = outfit.ModifyValue(&UF::TransmogOutfitData::Situations);
for (TransmogSituationEntry const* defaultSituation : TransmogMgr::GetDefaultSituations())
{
auto situation = AddDynamicUpdateFieldValue(situations);
situation.ModifyValue(&UF::TransmogOutfitSituationInfo::SituationID).SetValue(defaultSituation->ID);
}
auto slots = outfit.ModifyValue(&UF::TransmogOutfitData::Slots);
for (TransmogMgr::TransmogOutfitSlotAndOptionInfo const& slotInfo : TransmogMgr::GetAllSlots())
{
auto slot = AddDynamicUpdateFieldValue(slots);
slot.ModifyValue(&UF::TransmogOutfitSlotData::Slot).SetValue(AsUnderlyingType(slotInfo.Slot->GetSlot()));
if (slotInfo.SlotOption)
{
slot.ModifyValue(&UF::TransmogOutfitSlotData::SlotOption).SetValue(AsUnderlyingType(slotInfo.SlotOption->GetOption()));
switch (slotInfo.SlotOption->GetOption())
{
case TransmogOutfitSlotOption::ArtifactSpecOne:
case TransmogOutfitSlotOption::ArtifactSpecTwo:
case TransmogOutfitSlotOption::ArtifactSpecThree:
case TransmogOutfitSlotOption::ArtifactSpecFour:
// artifacts are disabled by default
slot.ModifyValue(&UF::TransmogOutfitSlotData::AppearanceDisplayType).SetValue(AsUnderlyingType(TransmogOutfitDisplayType::Disabled));
slot.ModifyValue(&UF::TransmogOutfitSlotData::IllusionDisplayType).SetValue(AsUnderlyingType(TransmogOutfitDisplayType::Disabled));
break;
default:
break;
}
}
}
}
bool Player::UpdateTransmogOutfit(uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const& outfitData)
{
if (!m_activePlayerData->TransmogOutfits.Get(id))
return false;
auto outfitInfo = m_values.ModifyValue(&Player::m_activePlayerData)
.ModifyValue(&UF::ActivePlayerData::TransmogOutfits, id)
.ModifyValue(&UF::TransmogOutfitData::OutfitInfo);
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::SetType), AsUnderlyingType(outfitData.SetType));
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::SituationsEnabled), outfitData.SituationsEnabled);
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::Icon), outfitData.Icon);
SetUpdateFieldValue(outfitInfo.ModifyValue(&UF::TransmogOutfitDataInfo::Name), std::string(outfitData.Name));
m_changedTransmogOutfits.insert(id);
return true;
}
void Player::UpdateTransmogOutfitSituations(uint32 id, bool situationsEnabled, std::span<WorldPackets::Transmogrification::TransmogOutfitSituationInfo const> situations)
{
auto outfit = m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::TransmogOutfits, id);
SetUpdateFieldValue(outfit.ModifyValue(&UF::TransmogOutfitData::OutfitInfo).ModifyValue(&UF::TransmogOutfitDataInfo::SituationsEnabled), situationsEnabled);
auto outfitSituations = outfit.ModifyValue(&UF::TransmogOutfitData::Situations);
ClearDynamicUpdateFieldValues(outfitSituations);
for (WorldPackets::Transmogrification::TransmogOutfitSituationInfo const& situation : situations)
{
auto outfitSituation = AddDynamicUpdateFieldValue(outfitSituations);
outfitSituation.ModifyValue(&UF::TransmogOutfitSituationInfo::SituationID).SetValue(situation.SituationID);
outfitSituation.ModifyValue(&UF::TransmogOutfitSituationInfo::SpecID).SetValue(situation.SpecID);
outfitSituation.ModifyValue(&UF::TransmogOutfitSituationInfo::LoadoutID).SetValue(situation.LoadoutID);
outfitSituation.ModifyValue(&UF::TransmogOutfitSituationInfo::EquipmentSetID).SetValue(situation.EquipmentSetID);
}
m_changedTransmogOutfits.insert(id);
}
void Player::UpdateTransmogOutfitSlots(uint32 id, std::span<WorldPackets::Transmogrification::TransmogOutfitSlotData const> slots)
{
UF::TransmogOutfitData const* transmogOutfit = m_activePlayerData->TransmogOutfits.Get(id);
if (!transmogOutfit)
return;
auto outfit = m_values.ModifyValue(&Player::m_activePlayerData)
.ModifyValue(&UF::ActivePlayerData::TransmogOutfits, id);
uint32 outfitSlotIndex = 0;
for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slot : slots)
{
while (std::pair(*transmogOutfit->Slots[outfitSlotIndex].Slot, *transmogOutfit->Slots[outfitSlotIndex].SlotOption) < std::pair(AsUnderlyingType(slot.Slot), AsUnderlyingType(slot.SlotOption)))
if (++outfitSlotIndex >= transmogOutfit->Slots.size())
return;
auto viewedOutfitSlot = outfit.ModifyValue(&UF::TransmogOutfitData::Slots, outfitSlotIndex);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::Slot), AsUnderlyingType(slot.Slot));
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::SlotOption), AsUnderlyingType(slot.SlotOption));
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::ItemModifiedAppearanceID), slot.ItemModifiedAppearanceID);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::AppearanceDisplayType), AsUnderlyingType(slot.AppearanceDisplayType));
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::SpellItemEnchantmentID), slot.SpellItemEnchantmentID);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::IllusionDisplayType), AsUnderlyingType(slot.IllusionDisplayType));
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::Flags), slot.Flags);
}
m_changedTransmogOutfits.insert(id);
}
void Player::EquipTransmogOutfit(uint32 id, TransmogSituationTrigger trigger, Optional<bool> locked)
{
auto activePlayerData = m_values.ModifyValue(&Player::m_activePlayerData);
auto transmogMetadata = activePlayerData.ModifyValue(&UF::ActivePlayerData::TransmogMetadata);
SetUpdateFieldValue(transmogMetadata.ModifyValue(&UF::TransmogOutfitMetadata::TransmogOutfitID), id);
SetUpdateFieldValue(transmogMetadata.ModifyValue(&UF::TransmogOutfitMetadata::SituationTrigger), AsUnderlyingType(trigger));
if (locked.has_value())
SetUpdateFieldValue(transmogMetadata.ModifyValue(&UF::TransmogOutfitMetadata::Locked), *locked);
auto viewedOutfit = activePlayerData.ModifyValue(&UF::ActivePlayerData::ViewedOutfit);
if (UF::TransmogOutfitData const* transmogOutfit = m_activePlayerData->TransmogOutfits.Get(id))
{
for (UF::TransmogOutfitSlotData const& slot : transmogOutfit->Slots)
{
uint32 slotIndex = TransmogMgr::GetSlotAndOption(static_cast<TransmogOutfitSlot>(*slot.Slot), static_cast<TransmogOutfitSlotOption>(*slot.SlotOption))->SlotIndex;
auto viewedOutfitSlot = viewedOutfit.ModifyValue(&UF::TransmogOutfitData::Slots, slotIndex);
if (static_cast<TransmogOutfitDisplayType>(*slot.AppearanceDisplayType) != TransmogOutfitDisplayType::Unassigned)
{
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::ItemModifiedAppearanceID), slot.ItemModifiedAppearanceID);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::AppearanceDisplayType), slot.AppearanceDisplayType);
}
if (static_cast<TransmogOutfitDisplayType>(*slot.IllusionDisplayType) != TransmogOutfitDisplayType::Unassigned)
{
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::SpellItemEnchantmentID), slot.SpellItemEnchantmentID);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::IllusionDisplayType), slot.IllusionDisplayType);
}
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::Flags), slot.Flags);
}
}
else
{
for (TransmogMgr::TransmogOutfitSlotAndOptionInfo const& slotInfo : TransmogMgr::GetAllSlots())
{
auto viewedOutfitSlot = viewedOutfit.ModifyValue(&UF::TransmogOutfitData::Slots, slotInfo.SlotIndex);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::Slot), AsUnderlyingType(slotInfo.Slot->GetSlot()));
TransmogOutfitSlotOption slotOption = slotInfo.SlotOption ? slotInfo.SlotOption->GetOption() : TransmogOutfitSlotOption::None;
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::SlotOption), AsUnderlyingType(slotOption));
if (Item const* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slotInfo.Slot->InventorySlotEnum))
{
if (item->GetTemplate()->GetWeaponTransmogOutfitSlotOption() == slotOption)
{
if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance())
{
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::ItemModifiedAppearanceID), itemModifiedAppearance->ID);
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::AppearanceDisplayType), AsUnderlyingType(TransmogOutfitDisplayType::Equipped));
}
}
}
switch (slotOption)
{
case TransmogOutfitSlotOption::ArtifactSpecOne:
case TransmogOutfitSlotOption::ArtifactSpecTwo:
case TransmogOutfitSlotOption::ArtifactSpecThree:
case TransmogOutfitSlotOption::ArtifactSpecFour:
// artifacts are disabled by default
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::AppearanceDisplayType), AsUnderlyingType(TransmogOutfitDisplayType::Disabled));
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::IllusionDisplayType), AsUnderlyingType(TransmogOutfitDisplayType::Disabled));
break;
case TransmogOutfitSlotOption::None:
break;
default:
SetUpdateFieldValue(viewedOutfitSlot.ModifyValue(&UF::TransmogOutfitSlotData::IllusionDisplayType), AsUnderlyingType(TransmogOutfitDisplayType::Equipped));
break;
}
}
}
for (uint8 equipSlot = EQUIPMENT_SLOT_START; equipSlot < EQUIPMENT_SLOT_END; ++equipSlot)
SetVisibleItemSlot(equipSlot, GetItemByPos(INVENTORY_SLOT_BAG_0, equipSlot));
}
std::string Player::GetCharacterSelectOutfit() const
{
std::vector<UF::TransmogOutfitData const*> outfits;
for (auto const& [_, transmogOutfit] : m_activePlayerData->TransmogOutfits)
{
if (!transmogOutfit.value.OutfitInfo->SituationsEnabled)
continue;
bool isCharacterSelect = transmogOutfit.value.Situations.FindIndexIf([](UF::TransmogOutfitSituationInfo const& situation)
{
return sTransmogSituationStore.AssertEntry(situation.SituationID)->GetSituation() == TransmogSituation::LocationCharacterSelect;
}) >= 0;
if (!isCharacterSelect)
continue;
outfits.push_back(&transmogOutfit.value);
}
UF::TransmogOutfitData const* outfit = &*m_activePlayerData->ViewedOutfit;
if (!outfits.empty())
outfit = Trinity::Containers::SelectRandomContainerElement(outfits);
auto isTransmogDisplayed = [](TransmogOutfitDisplayType displayType)
{
return displayType == TransmogOutfitDisplayType::Assigned || displayType == TransmogOutfitDisplayType::Hidden;
};
std::string result;
for (EquipmentSlots i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i = EquipmentSlots(i + 1))
{
TransmogOutfitSlotOption transmogSlotOption = TransmogOutfitSlotOption::None;
switch (i)
{
case EQUIPMENT_SLOT_MAINHAND:
transmogSlotOption = static_cast<TransmogOutfitSlotOption>(m_activePlayerData->TransmogMetadata->StampedOptionMainHand);
break;
case EQUIPMENT_SLOT_OFFHAND:
transmogSlotOption = static_cast<TransmogOutfitSlotOption>(m_activePlayerData->TransmogMetadata->StampedOptionOffHand);
break;
default:
break;
}
if (TransmogMgr::TransmogOutfitSlotAndOptionInfo const* slotInfo = TransmogMgr::GetSlotAndOption(i, transmogSlotOption))
{
UF::TransmogOutfitSlotData const& transmogOutfitSlot = outfit->Slots[slotInfo->SlotIndex];
uint32 itemModifiedAppearanceId = transmogOutfitSlot.ItemModifiedAppearanceID;
if (!isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*transmogOutfitSlot.AppearanceDisplayType)))
itemModifiedAppearanceId = m_activePlayerData->ViewedOutfit->Slots[slotInfo->SlotIndex].ItemModifiedAppearanceID;
uint32 spellItemEnchantmentId = transmogOutfitSlot.SpellItemEnchantmentID;
if (!isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*transmogOutfitSlot.IllusionDisplayType)))
spellItemEnchantmentId = m_activePlayerData->ViewedOutfit->Slots[slotInfo->SlotIndex].SpellItemEnchantmentID;
InventoryType inventoryType = INVTYPE_NON_EQUIP;
int32 displayId = 0;
uint16 itemVisual = 0;
uint8 subClass = 0;
uint32 secondaryItemModifiedAppearanceId = 0;
if (ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId))
{
if (ItemEntry const* item = sItemStore.LookupEntry(itemModifiedAppearance->ItemID))
{
subClass = item->SubclassID;
inventoryType = static_cast<InventoryType>(item->InventoryType);
}
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(itemModifiedAppearance->ItemAppearanceID))
displayId = itemAppearance->ItemDisplayInfoID;
}
if (SpellItemEnchantmentEntry const* spellItemEnchantment = sSpellItemEnchantmentStore.LookupEntry(spellItemEnchantmentId))
itemVisual = spellItemEnchantment->ItemVisual;
if (TransmogOutfitSlotInfoEntry const* secondarySlot = sTransmogOutfitSlotInfoStore.LookupEntry(slotInfo->Slot->SecondarySlotID))
{
if (TransmogMgr::TransmogOutfitSlotAndOptionInfo const* secondarySlotInfo = TransmogMgr::GetSlotAndOption(secondarySlot->GetSlot(), transmogSlotOption))
{
UF::TransmogOutfitSlotData const& secondaryTransmogOutfitSlot = outfit->Slots[secondarySlotInfo->SlotIndex];
secondaryItemModifiedAppearanceId = secondaryTransmogOutfitSlot.ItemModifiedAppearanceID;
if (!isTransmogDisplayed(static_cast<TransmogOutfitDisplayType>(*secondaryTransmogOutfitSlot.AppearanceDisplayType)))
secondaryItemModifiedAppearanceId = m_activePlayerData->ViewedOutfit->Slots[secondarySlotInfo->SlotIndex].ItemModifiedAppearanceID;
}
}
Trinity::StringFormatTo(std::back_inserter(result), "{} {} {} {} {} ", inventoryType, displayId, itemVisual, subClass, secondaryItemModifiedAppearanceId);
}
else
result += "0 0 0 0 0 "sv;
}
for (uint32 i = EQUIPMENT_SLOT_END; i < REAGENT_BAG_SLOT_END; ++i)
result += "0 0 0 0 0 "sv;
return result;
}
void Player::OnPhaseChange()
{
Unit::OnPhaseChange();
+35 -3
View File
@@ -135,6 +135,13 @@ namespace WorldPackets
struct TraitConfig;
struct TraitEntry;
}
namespace Transmogrification
{
struct TransmogOutfitDataInfo;
struct TransmogOutfitSituationInfo;
struct TransmogOutfitSlotData;
}
}
TC_GAME_API uint32 GetBagSize(Bag const* bag);
@@ -334,7 +341,8 @@ enum ActionButtonType
ACTION_BUTTON_CMACRO = ACTION_BUTTON_C | ACTION_BUTTON_MACRO,
ACTION_BUTTON_COMPANION = 0x50,
ACTION_BUTTON_MOUNT = 0x60,
ACTION_BUTTON_ITEM = 0x80
ACTION_BUTTON_ITEM = 0x80,
ACTION_BUTTON_OUTFIT = 0x90
};
enum class HonorGainSource : uint8
@@ -548,6 +556,7 @@ enum PlayerLocalFlags
PLAYER_LOCAL_FLAG_CHARACTER_BANK_DISABLED = 0x00080000,
PLAYER_LOCAL_FLAG_CHARACTER_BANK_CONVERSION_FAILED = 0x00100000,
PLAYER_LOCAL_FLAG_ACCOUNT_BANK_DISABLED = 0x00200000,
PLAYER_LOCAL_FLAG_FREE_TRANSMOG_CLAIMED = 0x00400000,
};
DEFINE_ENUM_FLAG(PlayerLocalFlags);
@@ -586,6 +595,8 @@ enum PlayerExtraFlags
PLAYER_EXTRA_HAS_RACE_CHANGED = 0x0200,
PLAYER_EXTRA_GRANTED_LEVELS_FROM_RAF = 0x0400,
PLAYER_EXTRA_LEVEL_BOOSTED = 0x0800,
PLAYER_EXTRA_FLAG_FREE_TRANSMOG_CLAIMED = 0x1000,
};
// 2^n values
@@ -937,6 +948,9 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS,
PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS,
PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS,
PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT,
PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT_SITUATION,
PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT_SLOT,
PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFITS,
PLAYER_LOGIN_QUERY_LOAD_BG_DATA,
PLAYER_LOGIN_QUERY_LOAD_GLYPHS,
@@ -1264,6 +1278,8 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void SetBeenGrantedLevelsFromRaF() { m_ExtraFlags |= PLAYER_EXTRA_GRANTED_LEVELS_FROM_RAF; }
bool HasLevelBoosted() const { return (m_ExtraFlags & PLAYER_EXTRA_LEVEL_BOOSTED) != 0; }
void SetHasLevelBoosted() { m_ExtraFlags |= PLAYER_EXTRA_LEVEL_BOOSTED; }
bool HasClaimedFreeTransmog() const { return (m_ExtraFlags & PLAYER_EXTRA_FLAG_FREE_TRANSMOG_CLAIMED) != 0; }
void SetHasClaimedFreeTransmog() { m_ExtraFlags |= PLAYER_EXTRA_FLAG_FREE_TRANSMOG_CLAIMED; }
uint32 GetXP() const { return m_activePlayerData->XP; }
uint32 GetXPForNextLevel() const { return m_activePlayerData->NextLevelXP; }
@@ -1550,7 +1566,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void ApplyEquipCooldown(Item* pItem);
void QuickEquipItem(uint16 pos, Item* pItem);
void VisualizeItem(uint8 slot, Item* pItem);
void SetVisibleItemSlot(uint8 slot, Item* pItem);
void SetVisibleItemSlot(uint8 slot, Item const* item);
Item* BankItem(ItemPosCountVec const& dest, Item* pItem, bool update);
void RemoveItem(uint8 bag, uint8 slot, bool update);
void MoveItemFromInventory(uint8 bag, uint8 slot, bool update);
@@ -2987,6 +3003,18 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
bool CanEnableWarModeInArea() const;
void UpdateWarModeAuras();
void AddUnlockedTransmogOutfits(std::span<int32 const> transmogOutfitIds);
void AddUnlockedTransmogOutfit(int32 transmogOutfitIds) { AddUnlockedTransmogOutfits(std::span(&transmogOutfitIds, 1)); }
void CreateTransmogOutfit(uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const& outfitData);
void InitializeNewTransmogOutfit(UF::MutableFieldReference<UF::TransmogOutfitData, false> outfit,
uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const& outfitData);
bool UpdateTransmogOutfit(uint32 id, WorldPackets::Transmogrification::TransmogOutfitDataInfo const& outfitData);
void UpdateTransmogOutfitSituations(uint32 id, bool situationsEnabled, std::span<WorldPackets::Transmogrification::TransmogOutfitSituationInfo const> situations);
void UpdateTransmogOutfitSlots(uint32 id, std::span<WorldPackets::Transmogrification::TransmogOutfitSlotData const> slots);
void EquipTransmogOutfit(uint32 id, TransmogSituationTrigger trigger, Optional<bool> locked);
std::string GetCharacterSelectOutfit() const;
std::string GetDebugInfo() const override;
UF::UpdateField<UF::PlayerData, int32(WowCS::EntityFragment::CGObject), TYPEID_PLAYER> m_playerData;
@@ -3075,7 +3103,9 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void _LoadDeclinedNames(PreparedQueryResult result);
void _LoadArenaTeamInfo(PreparedQueryResult result);
void _LoadEquipmentSets(PreparedQueryResult result);
void _LoadTransmogOutfits(PreparedQueryResult result);
void _LoadTransmogCustomSets(PreparedQueryResult result);
void _LoadTransmogOutfits(PreparedQueryResult setsResult, PreparedQueryResult situationsResult, PreparedQueryResult slotsResult,
int32 equippedTransmogOutfitId, bool locked);
void _LoadBGData(PreparedQueryResult result);
void _LoadGlyphs(PreparedQueryResult result);
void _LoadTalents(PreparedQueryResult result);
@@ -3106,6 +3136,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void _SaveSpells(CharacterDatabaseTransaction trans);
void _SaveStoredAuraTeleportLocations(CharacterDatabaseTransaction trans);
void _SaveEquipmentSets(CharacterDatabaseTransaction trans);
void _SaveTransmogOutfits(CharacterDatabaseTransaction trans);
void _SaveBGData(CharacterDatabaseTransaction trans);
void _SaveGlyphs(CharacterDatabaseTransaction trans) const;
void _SaveTalents(CharacterDatabaseTransaction trans);
@@ -3254,6 +3285,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
std::unique_ptr<Runes> m_runes;
EquipmentSetContainer _equipmentSets;
std::set<uint32> m_changedTransmogOutfits;
bool CanNeverSee(WorldObject const* obj, bool ignorePhaseShift = false) const override;
bool CanAlwaysSee(WorldObject const* obj) const override;
+3 -2
View File
@@ -70,6 +70,7 @@
#include "TerrainMgr.h"
#include "ThreadPool.h"
#include "Timer.h"
#include "TransmogMgr.h"
#include "TransportMgr.h"
#include "VMapFactory.h"
#include "VMapManager.h"
@@ -1509,12 +1510,12 @@ void ObjectMgr::LoadEquipmentTemplates()
}
// AppearanceModId 0 is always valid
if (equipmentInfo.Items[i].AppearanceModId && !sDB2Manager.GetItemModifiedAppearance(equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId))
if (equipmentInfo.Items[i].AppearanceModId && !TransmogMgr::GetItemModifiedAppearance(equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId))
{
TC_LOG_ERROR("sql.sql", "Unknown item appearance for (ID={}, AppearanceModID={}) pair in creature_equip_template.ItemID{} creature_equip_template.AppearanceModID{} "
"for CreatureID = {} and ID={}, forced to default.",
equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId, i + 1, i + 1, entry, id);
if (ItemModifiedAppearanceEntry const* defaultAppearance = sDB2Manager.GetDefaultItemModifiedAppearance(equipmentInfo.Items[i].ItemId))
if (ItemModifiedAppearanceEntry const* defaultAppearance = TransmogMgr::GetDefaultItemModifiedAppearance(equipmentInfo.Items[i].ItemId))
equipmentInfo.Items[i].AppearanceModId = defaultAppearance->ItemAppearanceModifierID;
else
equipmentInfo.Items[i].AppearanceModId = 0;
+14 -4
View File
@@ -61,6 +61,7 @@
#include "SocialMgr.h"
#include "StringConvert.h"
#include "SystemPackets.h"
#include "TransmogMgr.h"
#include "Util.h"
#include "World.h"
#include <boost/circular_buffer.hpp>
@@ -257,6 +258,18 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFITS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT_SITUATION);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT_SITUATION, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TRANSMOG_OUTFIT_SLOT);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TRANSMOG_OUTFIT_SLOT, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CUF_PROFILES);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES, stmt);
@@ -2047,10 +2060,7 @@ void WorldSession::HandleEquipmentSetSave(WorldPackets::EquipmentSet::SaveEquipm
auto validateIllusion = [this](uint32 enchantId) -> bool
{
SpellItemEnchantmentEntry const* illusion = sSpellItemEnchantmentStore.LookupEntry(enchantId);
if (!illusion)
return false;
if (!illusion->ItemVisual || !illusion->GetFlags().HasFlag(SpellItemEnchantmentFlags::AllowTransmog))
if (!illusion || !TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(enchantId))
return false;
if (!ConditionMgr::IsPlayerMeetingCondition(_player, illusion->TransmogUseConditionID))
@@ -23,6 +23,7 @@
#include "NPCPackets.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "TransmogMgr.h"
#include "TransmogrificationPackets.h"
void WorldSession::HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems& transmogrifyItems)
@@ -128,7 +129,7 @@ void WorldSession::HandleTransmogrifyItems(WorldPackets::Transmogrification::Tra
return;
}
TransmogIllusionEntry const* illusion = sDB2Manager.GetTransmogIllusionForEnchantment(transmogItem.SpellItemEnchantmentID);
TransmogIllusionEntry const* illusion = TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(transmogItem.SpellItemEnchantmentID);
if (!illusion)
{
TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - {}, Name: {} tried to transmogrify illusion using invalid enchant ({}).", player->GetGUID().ToString(), player->GetName(), transmogItem.SpellItemEnchantmentID);
@@ -326,6 +327,283 @@ void WorldSession::HandleTransmogrifyItems(WorldPackets::Transmogrification::Tra
}
}
void WorldSession::HandleTransmogOutfitNew(WorldPackets::Transmogrification::TransmogOutfitNew const& transmogOutfitNew)
{
if (!_player->GetNPCIfCanInteractWith(transmogOutfitNew.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER, UNIT_NPC_FLAG_2_NONE))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
GetPlayerInfo(), transmogOutfitNew.Npc);
return;
}
_player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting);
if (transmogOutfitNew.Source != TransmogOutfitEntrySource::PlayerPurchased)
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - source {} not allowed.",
GetPlayerInfo(), transmogOutfitNew.Source);
return;
}
if (transmogOutfitNew.Info.SetType != TransmogOutfitSetType::Outfit)
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - set type {} not allowed.",
GetPlayerInfo(), transmogOutfitNew.Info.SetType);
return;
}
TransmogOutfitEntryEntry const* transmogOutfitEntry = TransmogMgr::GetNextOutfitToUnlock(transmogOutfitNew.Source, _player);
if (!transmogOutfitEntry)
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - no next unlockable outfit entry found for source {}.",
GetPlayerInfo(), transmogOutfitNew.Source);
return;
}
if (!_player->HasEnoughMoney(transmogOutfitEntry->Cost))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - not enough money.",
GetPlayerInfo());
return;
}
GetCollectionMgr()->AddTransmogOutfit(transmogOutfitEntry->ID);
_player->CreateTransmogOutfit(transmogOutfitEntry->ID, transmogOutfitNew.Info);
_player->ModifyMoney(-int64(transmogOutfitEntry->Cost));
WorldPackets::Transmogrification::TransmogOutfitNewEntryAdded transmogOutfitNewEntryAdded;
transmogOutfitNewEntryAdded.TransmogOutfitID = transmogOutfitEntry->ID;
SendPacket(transmogOutfitNewEntryAdded.Write());
}
void WorldSession::HandleTransmogOutfitUpdateInfo(WorldPackets::Transmogrification::TransmogOutfitUpdateInfo const& transmogOutfitUpdateInfo)
{
if (!_player->GetNPCIfCanInteractWith(transmogOutfitUpdateInfo.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER, UNIT_NPC_FLAG_2_NONE))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
GetPlayerInfo(), transmogOutfitUpdateInfo.Npc);
return;
}
_player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting);
if (!_player->UpdateTransmogOutfit(transmogOutfitUpdateInfo.OutfitID, transmogOutfitUpdateInfo.Info))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateInfo - player does not have outfit {}.",
GetPlayerInfo(), transmogOutfitUpdateInfo.OutfitID);
return;
}
// SMSG_UPDATE_OBJECT must be received by client before transmog packet for UI to properly update
Player::ValuesUpdateForPlayerWithMaskSender sendUpdateObject(_player);
sendUpdateObject.ActivePlayerMask.MarkChanged(&UF::ActivePlayerData::TransmogOutfits);
sendUpdateObject(_player);
WorldPackets::Transmogrification::TransmogOutfitInfoUpdated transmogOutfitInfoUpdated;
transmogOutfitInfoUpdated.TransmogOutfitID = transmogOutfitUpdateInfo.OutfitID;
transmogOutfitInfoUpdated.OutfitInfo = &transmogOutfitUpdateInfo.Info;
SendPacket(transmogOutfitInfoUpdated.Write());
}
void WorldSession::HandleTransmogOutfitUpdateSituations(WorldPackets::Transmogrification::TransmogOutfitUpdateSituations const& transmogOutfitUpdateSituations)
{
if (!_player->GetNPCIfCanInteractWith(transmogOutfitUpdateSituations.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER, UNIT_NPC_FLAG_2_NONE))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
GetPlayerInfo(), transmogOutfitUpdateSituations.Npc);
return;
}
_player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting);
if (!_player->m_activePlayerData->TransmogOutfits.Get(transmogOutfitUpdateSituations.OutfitID))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSituations - player does not have outfit {}.",
GetPlayerInfo(), transmogOutfitUpdateSituations.OutfitID);
return;
}
if (!TransmogMgr::ValidateSituations(transmogOutfitUpdateSituations.Situations))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSituations - player sent invalid situations.",
GetPlayerInfo());
return;
}
_player->UpdateTransmogOutfitSituations(transmogOutfitUpdateSituations.OutfitID, transmogOutfitUpdateSituations.SituationsEnabled,
transmogOutfitUpdateSituations.Situations);
// SMSG_UPDATE_OBJECT must be received by client before transmog packet for UI to properly update
Player::ValuesUpdateForPlayerWithMaskSender sendUpdateObject(_player);
sendUpdateObject.ActivePlayerMask.MarkChanged(&UF::ActivePlayerData::TransmogOutfits);
sendUpdateObject(_player);
WorldPackets::Transmogrification::TransmogOutfitSituationsUpdated transmogOutfitSituationsUpdated;
transmogOutfitSituationsUpdated.TransmogOutfitID = transmogOutfitUpdateSituations.OutfitID;
transmogOutfitSituationsUpdated.SituationsEnabled = transmogOutfitUpdateSituations.SituationsEnabled;
transmogOutfitSituationsUpdated.Situations = transmogOutfitUpdateSituations.Situations;
SendPacket(transmogOutfitSituationsUpdated.Write());
}
void WorldSession::HandleTransmogOutfitUpdateSlots(WorldPackets::Transmogrification::TransmogOutfitUpdateSlots const& transmogOutfitUpdateSlots)
{
if (!_player->GetNPCIfCanInteractWith(transmogOutfitUpdateSlots.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER, UNIT_NPC_FLAG_2_NONE))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitNew - {} not found or player can't interact with it.",
GetPlayerInfo(), transmogOutfitUpdateSlots.Npc);
return;
}
_player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting);
UF::TransmogOutfitData const* transmogOutfit = _player->m_activePlayerData->TransmogOutfits.Get(transmogOutfitUpdateSlots.OutfitID);
if (!transmogOutfit)
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player does not have outfit {}.",
GetPlayerInfo(), transmogOutfitUpdateSlots.OutfitID);
return;
}
if (!TransmogMgr::ValidateSlots(transmogOutfitUpdateSlots.Slots))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player sent invalid slots.",
GetPlayerInfo());
return;
}
std::vector<uint32> bindAppearances;
for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slot : transmogOutfitUpdateSlots.Slots)
{
if (slot.ItemModifiedAppearanceID)
{
auto [hasAppearance, isTemporary] = GetCollectionMgr()->HasItemAppearance(slot.ItemModifiedAppearanceID);
if (!hasAppearance)
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player does not have appearance {} in collection.",
GetPlayerInfo(), slot.ItemModifiedAppearanceID);
return;
}
if (isTemporary)
bindAppearances.push_back(slot.ItemModifiedAppearanceID);
}
if (slot.SpellItemEnchantmentID && !GetCollectionMgr()->HasTransmogIllusion(TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(slot.SpellItemEnchantmentID)->ID))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player does not have enchant {} in illusion collection.",
GetPlayerInfo(), slot.SpellItemEnchantmentID);
return;
}
}
if (transmogOutfitUpdateSlots.UseAvailableDiscount && _player->HasPlayerLocalFlag(PLAYER_LOCAL_FLAG_FREE_TRANSMOG_CLAIMED))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player has already claimed free transmog before.",
GetPlayerInfo());
return;
}
// calculate cost
float baseCost = 0;
if (uint32 curveId = sDB2Manager.GetGlobalCurveId(GlobalCurve::TransmogCost))
baseCost = sDB2Manager.GetCurveValueAt(curveId, std::max<int32>(_player->GetLevel(), _player->m_activePlayerData->MaxLevel));
float costMultiplier = 1.0f;
TransmogOutfitEntryEntry const* transmogOutfitEntry = sTransmogOutfitEntryStore.AssertEntry(transmogOutfitUpdateSlots.OutfitID);
if (transmogOutfitEntry->HasFlag(TransmogOutfitEntryFlags::UseOverrideCostModifier))
costMultiplier *= transmogOutfitEntry->OverrideCostModifier;
if (_player->HasAuraType(SPELL_AURA_MOD_TRANSMOG_OUTFIT_UPDATE_COST))
costMultiplier *= _player->m_activePlayerData->TransmogMetadata->CostMod;
if (sChrRacesStore.AssertEntry(_player->GetRace())->GetFlags().HasFlag(ChrRacesFlag::VoidVendorDiscount))
costMultiplier *= 0.5f;
uint64 cost = 0;
if (!transmogOutfitUpdateSlots.UseAvailableDiscount)
{
auto oldSlotItr = transmogOutfit->Slots.begin();
auto oldSlotEnd = transmogOutfit->Slots.end();
for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slot : transmogOutfitUpdateSlots.Slots)
{
oldSlotItr = std::ranges::find(oldSlotItr, oldSlotEnd,
std::pair(AsUnderlyingType(slot.Slot), AsUnderlyingType(slot.SlotOption)),
[](UF::TransmogOutfitSlotData const& slotData) { return std::pair(*slotData.Slot, *slotData.SlotOption); });
auto [slotEntry, slotOptionEntry, _] = *TransmogMgr::GetSlotAndOption(slot.Slot, slot.SlotOption);
if (slot.AppearanceDisplayType == TransmogOutfitDisplayType::Assigned && oldSlotItr->ItemModifiedAppearanceID != slot.ItemModifiedAppearanceID)
{
if (slotEntry)
cost = static_cast<uint64>(std::floor(baseCost * slotEntry->ItemCostMultiplier)) + cost;
if (slotOptionEntry)
cost = static_cast<uint64>(std::floor(baseCost * slotOptionEntry->ItemCostMultiplier)) + cost;
}
if (slot.IllusionDisplayType == TransmogOutfitDisplayType::Assigned && oldSlotItr->SpellItemEnchantmentID != slot.SpellItemEnchantmentID)
{
if (slotEntry)
cost = static_cast<uint64>(std::floor(baseCost * slotEntry->IllusionCostMultiplier)) + cost;
if (slotOptionEntry)
cost = static_cast<uint64>(std::floor(baseCost * slotOptionEntry->IllusionCostMultiplier)) + cost;
}
++oldSlotItr;
}
cost = static_cast<uint64>(std::clamp(costMultiplier, 0.0f, 1.0f) * cost);
if (cost != transmogOutfitUpdateSlots.Cost)
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - player sent invalid cost {}.",
GetPlayerInfo(), transmogOutfitUpdateSlots.Cost);
return;
}
if (!_player->HasEnoughMoney(cost))
{
TC_LOG_ERROR("entities.player.cheat", "{} WorldSession::HandleTransmogOutfitUpdateSlots - not enough money.",
GetPlayerInfo());
return;
}
}
else
{
_player->SetPlayerLocalFlag(PLAYER_LOCAL_FLAG_FREE_TRANSMOG_CLAIMED);
_player->SetHasClaimedFreeTransmog();
}
_player->ModifyMoney(-int64(cost));
_player->UpdateTransmogOutfitSlots(transmogOutfitUpdateSlots.OutfitID, transmogOutfitUpdateSlots.Slots);
if (transmogOutfitUpdateSlots.OutfitID == _player->m_activePlayerData->TransmogMetadata->TransmogOutfitID)
_player->EquipTransmogOutfit(transmogOutfitUpdateSlots.OutfitID, TransmogSituationTrigger::TransmogUpdate, {});
WorldPackets::Transmogrification::TransmogOutfitSlotsUpdated transmogOutfitSlotsUpdated;
transmogOutfitSlotsUpdated.TransmogOutfitID = transmogOutfitUpdateSlots.OutfitID;
transmogOutfitSlotsUpdated.Slots = transmogOutfitUpdateSlots.Slots;
SendPacket(transmogOutfitSlotsUpdated.Write());
for (uint32 itemModifedAppearanceId : bindAppearances)
{
std::unordered_set<ObjectGuid> itemsProvidingAppearance = GetCollectionMgr()->GetItemsProvidingTemporaryAppearance(itemModifedAppearanceId);
for (ObjectGuid const& itemGuid : itemsProvidingAppearance)
{
if (Item* item = _player->GetItemByGuid(itemGuid))
{
item->SetNotRefundable(_player);
item->ClearSoulboundTradeable(_player);
GetCollectionMgr()->AddItemAppearance(item);
}
}
}
}
void WorldSession::SendOpenTransmogrifier(ObjectGuid const& guid)
{
WorldPackets::NPC::NPCInteractionOpenResult npcInteraction;
@@ -37,9 +37,164 @@ void TransmogrifyItems::Read()
for (TransmogrifyItem& item : Items)
_worldPacket >> item;
_worldPacket.ResetBitPos();
_worldPacket >> Bits<1>(CurrentSpecOnly);
}
ByteBuffer& operator>>(ByteBuffer& data, TransmogOutfitDataInfo& transmogOutfitDataInfo)
{
data.ResetBitPos();
data >> As<uint8>(transmogOutfitDataInfo.SetType);
data >> transmogOutfitDataInfo.Icon;
data >> SizedString::BitsSize<8>(transmogOutfitDataInfo.Name);
data >> Bits<1>(transmogOutfitDataInfo.SituationsEnabled);
data >> SizedString::Data(transmogOutfitDataInfo.Name);
return data;
}
ByteBuffer& operator<<(ByteBuffer& data, TransmogOutfitDataInfo const& transmogOutfitDataInfo)
{
data << As<uint8>(transmogOutfitDataInfo.SetType);
data << uint32(transmogOutfitDataInfo.Icon);
data << SizedString::BitsSize<8>(transmogOutfitDataInfo.Name);
data << Bits<1>(transmogOutfitDataInfo.SituationsEnabled);
data.FlushBits();
data << SizedString::Data(transmogOutfitDataInfo.Name);
return data;
}
void TransmogOutfitNew::Read()
{
_worldPacket >> Npc;
_worldPacket >> As<uint8>(Source);
_worldPacket >> Info;
}
WorldPacket const* TransmogOutfitNewEntryAdded::Write()
{
_worldPacket << uint32(TransmogOutfitID);
return &_worldPacket;
}
void TransmogOutfitUpdateInfo::Read()
{
_worldPacket >> OutfitID;
_worldPacket >> Npc;
_worldPacket >> Info;
}
WorldPacket const* TransmogOutfitInfoUpdated::Write()
{
_worldPacket << uint32(TransmogOutfitID);
_worldPacket << *OutfitInfo;
return &_worldPacket;
}
ByteBuffer& operator>>(ByteBuffer& data, TransmogOutfitSituationInfo& transmogOutfitSituationInfo)
{
data >> transmogOutfitSituationInfo.SituationID;
data >> transmogOutfitSituationInfo.SpecID;
data >> transmogOutfitSituationInfo.LoadoutID;
data >> transmogOutfitSituationInfo.EquipmentSetID;
return data;
}
ByteBuffer& operator<<(ByteBuffer& data, TransmogOutfitSituationInfo const& transmogOutfitSituationInfo)
{
data << uint32(transmogOutfitSituationInfo.SituationID);
data << uint32(transmogOutfitSituationInfo.SpecID);
data << uint32(transmogOutfitSituationInfo.LoadoutID);
data << uint32(transmogOutfitSituationInfo.EquipmentSetID);
return data;
}
void TransmogOutfitUpdateSituations::Read()
{
_worldPacket >> OutfitID;
_worldPacket >> Npc;
_worldPacket >> Size<uint32>(Situations);
for (TransmogOutfitSituationInfo& situation : Situations)
_worldPacket >> situation;
_worldPacket.ResetBitPos();
_worldPacket >> Bits<1>(SituationsEnabled);
}
WorldPacket const* TransmogOutfitSituationsUpdated::Write()
{
_worldPacket << uint32(TransmogOutfitID);
_worldPacket << Size<uint32>(Situations);
for (TransmogOutfitSituationInfo const& situation : Situations)
_worldPacket << situation;
_worldPacket << Bits<1>(SituationsEnabled);
_worldPacket.FlushBits();
return &_worldPacket;
}
ByteBuffer& operator>>(ByteBuffer& data, TransmogOutfitSlotData& transmogOutfitSlotData)
{
data >> As<int8>(transmogOutfitSlotData.Slot);
data >> As<uint8>(transmogOutfitSlotData.SlotOption);
data >> transmogOutfitSlotData.ItemModifiedAppearanceID;
data >> As<uint8>(transmogOutfitSlotData.AppearanceDisplayType);
data >> transmogOutfitSlotData.SpellItemEnchantmentID;
data >> As<uint8>(transmogOutfitSlotData.IllusionDisplayType);
data >> transmogOutfitSlotData.Flags;
return data;
}
ByteBuffer& operator<<(ByteBuffer& data, TransmogOutfitSlotData const& transmogOutfitSlotData)
{
data << As<int8>(transmogOutfitSlotData.Slot);
data << As<uint8>(transmogOutfitSlotData.SlotOption);
data << uint32(transmogOutfitSlotData.ItemModifiedAppearanceID);
data << As<uint8>(transmogOutfitSlotData.AppearanceDisplayType);
data << uint32(transmogOutfitSlotData.SpellItemEnchantmentID);
data << As<uint8>(transmogOutfitSlotData.IllusionDisplayType);
data << uint32(transmogOutfitSlotData.Flags);
return data;
}
void TransmogOutfitUpdateSlots::Read()
{
_worldPacket >> OutfitID;
_worldPacket >> Size<uint32>(Slots);
_worldPacket >> Npc;
_worldPacket >> Cost;
for (TransmogOutfitSlotData& slot : Slots)
_worldPacket >> slot;
_worldPacket.ResetBitPos();
_worldPacket >> Bits<1>(UseAvailableDiscount);
std::ranges::sort(Slots, std::ranges::less(), [](TransmogOutfitSlotData const& slot) { return std::pair(slot.Slot, slot.SlotOption); });
}
WorldPacket const* TransmogOutfitSlotsUpdated::Write()
{
_worldPacket << uint32(TransmogOutfitID);
_worldPacket << Size<uint32>(Slots);
for (TransmogOutfitSlotData const& slot : Slots)
_worldPacket << slot;
return &_worldPacket;
}
WorldPacket const* AccountTransmogUpdate::Write()
{
_worldPacket << Bits<1>(IsFullUpdate);
@@ -22,6 +22,12 @@
#include "ObjectGuid.h"
#include "PacketUtilities.h"
enum class TransmogOutfitDisplayType : uint8;
enum class TransmogOutfitEntrySource : uint8;
enum class TransmogOutfitSetType : uint8;
enum class TransmogOutfitSlot : int8;
enum class TransmogOutfitSlotOption : uint8;
namespace WorldPackets
{
namespace Transmogrification
@@ -51,6 +57,128 @@ namespace WorldPackets
bool CurrentSpecOnly = false;
};
struct TransmogOutfitDataInfo
{
TransmogOutfitSetType SetType = { };
bool SituationsEnabled = false;
uint32 Icon = 0;
std::string_view Name;
};
class TransmogOutfitNew final : public ClientPacket
{
public:
explicit TransmogOutfitNew(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOG_OUTFIT_NEW, std::move(packet)) { }
void Read() override;
ObjectGuid Npc;
TransmogOutfitDataInfo Info;
TransmogOutfitEntrySource Source = { };
};
class TransmogOutfitNewEntryAdded final : public ServerPacket
{
public:
explicit TransmogOutfitNewEntryAdded() : ServerPacket(SMSG_TRANSMOG_OUTFIT_NEW_ENTRY_ADDED, 4) { }
WorldPacket const* Write() override;
uint32 TransmogOutfitID = 0;
};
class TransmogOutfitUpdateInfo final : public ClientPacket
{
public:
explicit TransmogOutfitUpdateInfo(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOG_OUTFIT_UPDATE_INFO, std::move(packet)) { }
void Read() override;
uint32 OutfitID = 0;
ObjectGuid Npc;
TransmogOutfitDataInfo Info;
};
class TransmogOutfitInfoUpdated final : public ServerPacket
{
public:
explicit TransmogOutfitInfoUpdated() : ServerPacket(SMSG_TRANSMOG_OUTFIT_INFO_UPDATED, 4 + 1 + 4 + 1 + 1 + 128) { }
WorldPacket const* Write() override;
uint32 TransmogOutfitID = 0;
TransmogOutfitDataInfo const* OutfitInfo = nullptr;
};
struct TransmogOutfitSituationInfo
{
uint32 SituationID = 0;
uint32 SpecID = 0;
uint32 LoadoutID = 0;
uint32 EquipmentSetID = 0;
};
class TransmogOutfitUpdateSituations final : public ClientPacket
{
public:
explicit TransmogOutfitUpdateSituations(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOG_OUTFIT_UPDATE_SITUATIONS, std::move(packet)) { }
void Read() override;
uint32 OutfitID = 0;
ObjectGuid Npc;
bool SituationsEnabled = false;
Array<TransmogOutfitSituationInfo, 100> Situations;
};
class TransmogOutfitSituationsUpdated final : public ServerPacket
{
public:
explicit TransmogOutfitSituationsUpdated() : ServerPacket(SMSG_TRANSMOG_OUTFIT_SITUATIONS_UPDATED, 4 + 4 + 10 * (4 + 4 + 4 + 4) + 1) { }
WorldPacket const* Write() override;
uint32 TransmogOutfitID = 0;
bool SituationsEnabled = false;
std::span<TransmogOutfitSituationInfo const> Situations;
};
struct TransmogOutfitSlotData
{
TransmogOutfitSlot Slot = { };
TransmogOutfitSlotOption SlotOption = { };
TransmogOutfitDisplayType AppearanceDisplayType = { };
TransmogOutfitDisplayType IllusionDisplayType = { };
uint32 ItemModifiedAppearanceID = 0;
uint32 SpellItemEnchantmentID = 0;
uint32 Flags = 0;
};
class TransmogOutfitUpdateSlots final : public ClientPacket
{
public:
explicit TransmogOutfitUpdateSlots(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOG_OUTFIT_UPDATE_SLOTS, std::move(packet)) { }
void Read() override;
uint32 OutfitID = 0;
Array<TransmogOutfitSlotData, 30> Slots;
ObjectGuid Npc;
uint64 Cost = 0;
bool UseAvailableDiscount = false;
};
class TransmogOutfitSlotsUpdated final : public ServerPacket
{
public:
explicit TransmogOutfitSlotsUpdated() : ServerPacket(SMSG_TRANSMOG_OUTFIT_SLOTS_UPDATED, 4 + 4 + 30 * (1 + 1 + 4 + 1 + 4 + 1 + 4)) { }
WorldPacket const* Write() override;
uint32 TransmogOutfitID = 0;
std::span<TransmogOutfitSlotData const> Slots;
};
class AccountTransmogUpdate final : public ServerPacket
{
public:
+8 -8
View File
@@ -1105,10 +1105,10 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_TRAITS_TALENT_TEST_UNLEARN_SPELLS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRANSFER_CURRENCY_FROM_ACCOUNT_CHARACTER, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRANSMOGRIFY_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTransmogrifyItems);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_NEW, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_UPDATE_INFO, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_UPDATE_SITUATIONS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_UPDATE_SLOTS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_NEW, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTransmogOutfitNew);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_UPDATE_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTransmogOutfitUpdateInfo);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_UPDATE_SITUATIONS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTransmogOutfitUpdateSituations);
DEFINE_HANDLER(CMSG_TRANSMOG_OUTFIT_UPDATE_SLOTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTransmogOutfitUpdateSlots);
DEFINE_HANDLER(CMSG_TURN_IN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTurnInPetition);
DEFINE_HANDLER(CMSG_TUTORIAL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlag);
DEFINE_HANDLER(CMSG_UI_MAP_QUEST_LINES_REQUEST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleUiMapQuestLinesRequest);
@@ -2454,10 +2454,10 @@ void OpcodeTable::InitializeServerOpcodes()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRAIT_CONFIG_COMMIT_FAILED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSFER_ABORTED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSFER_PENDING, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_INFO_UPDATED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_NEW_ENTRY_ADDED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SITUATIONS_UPDATED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SLOTS_UPDATED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_INFO_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_NEW_ENTRY_ADDED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SITUATIONS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_OUTFIT_SLOTS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TREASURE_PICKER_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TREASURE_PUNCH_LIST_ITEMS_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER, CONNECTION_TYPE_REALM);
+7 -5
View File
@@ -1136,11 +1136,7 @@ bool WorldSession::IsAddonRegistered(std::string_view prefix) const
if (!_filterAddonMessages) // if we have hit the softcap (64) nothing should be filtered
return true;
if (_registeredAddonPrefixes.empty())
return false;
std::vector<std::string>::const_iterator itr = std::find(_registeredAddonPrefixes.begin(), _registeredAddonPrefixes.end(), prefix);
return itr != _registeredAddonPrefixes.end();
return advstd::ranges::contains(_registeredAddonPrefixes, prefix);
}
void WorldSession::HandleUnregisterAllAddonPrefixesOpcode(WorldPackets::Chat::ChatUnregisterAllAddonPrefixes& /*packet*/) // empty packet
@@ -1259,6 +1255,7 @@ public:
ITEM_APPEARANCES,
ITEM_FAVORITE_APPEARANCES,
TRANSMOG_ILLUSIONS,
TRANSMOG_OUTFITS,
WARBAND_SCENES,
PLAYER_DATA_ELEMENTS_ACCOUNT,
PLAYER_DATA_FLAGS_ACCOUNT,
@@ -1309,6 +1306,10 @@ public:
stmt->setUInt32(0, battlenetAccountId);
ok = SetPreparedQuery(TRANSMOG_ILLUSIONS, stmt) && ok;
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_TRANSMOG_OUTFITS);
stmt->setUInt32(0, battlenetAccountId);
ok = SetPreparedQuery(TRANSMOG_OUTFITS, stmt) && ok;
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_WARBAND_SCENES);
stmt->setUInt32(0, battlenetAccountId);
ok = SetPreparedQuery(WARBAND_SCENES, stmt) && ok;
@@ -1373,6 +1374,7 @@ void WorldSession::InitializeSessionCallback(LoginDatabaseQueryHolder const& hol
_collectionMgr->LoadAccountMounts(holder.GetPreparedResult(AccountInfoQueryHolder::MOUNTS));
_collectionMgr->LoadAccountItemAppearances(holder.GetPreparedResult(AccountInfoQueryHolder::ITEM_APPEARANCES), holder.GetPreparedResult(AccountInfoQueryHolder::ITEM_FAVORITE_APPEARANCES));
_collectionMgr->LoadAccountTransmogIllusions(holder.GetPreparedResult(AccountInfoQueryHolder::TRANSMOG_ILLUSIONS));
_collectionMgr->LoadAccountTransmogOutfits(holder.GetPreparedResult(AccountInfoQueryHolder::TRANSMOG_OUTFITS));
_collectionMgr->LoadAccountWarbandScenes(holder.GetPreparedResult(AccountInfoQueryHolder::WARBAND_SCENES));
LoadPlayerDataAccount(holder.GetPreparedResult(AccountInfoQueryHolder::PLAYER_DATA_ELEMENTS_ACCOUNT), holder.GetPreparedResult(AccountInfoQueryHolder::PLAYER_DATA_FLAGS_ACCOUNT));
+8
View File
@@ -819,6 +819,10 @@ namespace WorldPackets
namespace Transmogrification
{
class TransmogrifyItems;
class TransmogOutfitNew;
class TransmogOutfitUpdateInfo;
class TransmogOutfitUpdateSituations;
class TransmogOutfitUpdateSlots;
}
namespace Vehicle
@@ -1790,6 +1794,10 @@ class TC_GAME_API WorldSession
// Transmogrification
void HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems& transmogrifyItems);
void HandleTransmogOutfitNew(WorldPackets::Transmogrification::TransmogOutfitNew const& transmogOutfitNew);
void HandleTransmogOutfitUpdateInfo(WorldPackets::Transmogrification::TransmogOutfitUpdateInfo const& transmogOutfitUpdateInfo);
void HandleTransmogOutfitUpdateSituations(WorldPackets::Transmogrification::TransmogOutfitUpdateSituations const& transmogOutfitUpdateSituations);
void HandleTransmogOutfitUpdateSlots(WorldPackets::Transmogrification::TransmogOutfitUpdateSlots const& transmogOutfitUpdateSlots);
// Miscellaneous
void HandleSpellClick(WorldPackets::Spells::SpellClick& spellClick);
@@ -735,7 +735,7 @@ enum AuraType : uint32
SPELL_AURA_ADD_FLAT_PVP_MODIFIER_BY_SPELL_LABEL = 648,
SPELL_AURA_ADD_PCT_PVP_MODIFIER_BY_SPELL_LABEL = 649,
SPELL_AURA_650 = 650,
SPELL_AURA_651 = 651,
SPELL_AURA_ENABLE_EVENT_TRANSMOG_OUTFIT = 651,
SPELL_AURA_652 = 652,
SPELL_AURA_653 = 653,
SPELL_AURA_654 = 654,
@@ -720,7 +720,7 @@ NonDefaultConstructible<pAuraEffectHandler> AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //648 SPELL_AURA_ADD_FLAT_PVP_MODIFIER_BY_SPELL_LABEL
&AuraEffect::HandleNULL, //649 SPELL_AURA_ADD_PCT_PVP_MODIFIER_BY_SPELL_LABEL
&AuraEffect::HandleNULL, //650
&AuraEffect::HandleNULL, //651
&AuraEffect::HandleNULL, //651 SPELL_AURA_ENABLE_EVENT_TRANSMOG_OUTFIT
&AuraEffect::HandleNULL, //652
&AuraEffect::HandleNULL, //653
&AuraEffect::HandleNULL, //654
+9 -2
View File
@@ -61,6 +61,7 @@
#include "TemporarySummon.h"
#include "TradeData.h"
#include "TraitPackets.h"
#include "TransmogMgr.h"
#include "UniqueTrackablePtr.h"
#include "Util.h"
#include "VMapFactory.h"
@@ -4973,7 +4974,10 @@ int32 Spell::GetSpellCastDataAmmo()
switch (itemEntry->SubclassID)
{
case ITEM_SUBCLASS_WEAPON_THROWN:
ammoDisplayID = sDB2Manager.GetItemDisplayId(item_id, unitCaster->GetVirtualItemAppearanceMod(i));
if (ItemModifiedAppearanceEntry const* modifiedAppearance = TransmogMgr::GetItemModifiedAppearance(item_id, unitCaster->GetVirtualItemAppearanceMod(i)))
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(modifiedAppearance->ItemAppearanceID))
ammoDisplayID = itemAppearance->ItemDisplayInfoID;
ammoInventoryType = itemEntry->InventoryType;
break;
case ITEM_SUBCLASS_WEAPON_BOW:
@@ -4986,7 +4990,10 @@ int32 Spell::GetSpellCastDataAmmo()
ammoInventoryType = INVTYPE_AMMO;
break;
default:
nonRangedAmmoDisplayID = sDB2Manager.GetItemDisplayId(item_id, unitCaster->GetVirtualItemAppearanceMod(i));
if (ItemModifiedAppearanceEntry const* modifiedAppearance = TransmogMgr::GetItemModifiedAppearance(item_id, unitCaster->GetVirtualItemAppearanceMod(i)))
if (ItemAppearanceEntry const* itemAppearance = sItemAppearanceStore.LookupEntry(modifiedAppearance->ItemAppearanceID))
nonRangedAmmoDisplayID = itemAppearance->ItemDisplayInfoID;
nonRangedAmmoInventoryType = itemEntry->InventoryType;
break;
}
+9
View File
@@ -464,6 +464,7 @@ class TC_GAME_API Spell
void EffectSetPlayerDataElementCharacter();
void EffectSetPlayerDataFlagAccount();
void EffectSetPlayerDataFlagCharacter();
void EffectEquipTransmogOutfit();
typedef std::unordered_set<Aura*> UsedSpellMods;
@@ -632,6 +633,14 @@ class TC_GAME_API Spell
// SPELL_EFFECT_UPGRADE_HEIRLOOM
uint32 ItemId;
// SPELL_EFFECT_EQUIP_TRANSMOG_OUTFIT
struct
{
uint32 EquipAction;
uint32 TransmogOutfitId;
uint32 SituationTrigger;
} EquipTransmogOutfit;
struct
{
uint32 Data[3];
+28 -1
View File
@@ -436,7 +436,7 @@ NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EF
&Spell::EffectNULL, //344 SPELL_EFFECT_344
&Spell::EffectNULL, //345 SPELL_EFFECT_ASSIST_ACTION
&Spell::EffectNULL, //346 SPELL_EFFECT_346
&Spell::EffectNULL, //347 SPELL_EFFECT_EQUIP_TRANSMOG_OUTFIT
&Spell::EffectEquipTransmogOutfit, //347 SPELL_EFFECT_EQUIP_TRANSMOG_OUTFIT
&Spell::EffectNULL, //348 SPELL_EFFECT_GIVE_HOUSE_LEVEL
&Spell::EffectNULL, //349 SPELL_EFFECT_LEARN_HOUSE_ROOM
&Spell::EffectNULL, //350 SPELL_EFFECT_LEARN_HOUSE_EXTERIOR_COMPONENT
@@ -6338,3 +6338,30 @@ void Spell::EffectSetPlayerDataFlagCharacter()
target->SetDataFlagCharacter(effectInfo->MiscValue, damage != 0);
}
void Spell::EffectEquipTransmogOutfit()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* target = Object::ToPlayer(unitTarget);
if (!target)
return;
Optional<bool> locked;
switch (static_cast<TransmogOutfitEquipAction>(m_misc.EquipTransmogOutfit.EquipAction))
{
case TransmogOutfitEquipAction::EquipAndLock:
case TransmogOutfitEquipAction::RemoveAndLock:
case TransmogOutfitEquipAction::Lock:
locked = true;
break;
case TransmogOutfitEquipAction::Unlock:
locked = false;
break;
default:
break;
}
target->EquipTransmogOutfit(m_misc.EquipTransmogOutfit.TransmogOutfitId, static_cast<TransmogSituationTrigger>(m_misc.EquipTransmogOutfit.SituationTrigger), locked);
}
+416
View File
@@ -0,0 +1,416 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TransmogMgr.h"
#include "DB2Stores.h"
#include "FlatSet.h"
#include "MapUtils.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "TransmogrificationPackets.h"
#include "Util.h"
namespace
{
struct TransmogOutfitSlotOptionInfo
{
TransmogOutfitSlotOptionEntry const* Data = nullptr;
int32 SlotIndex = -1;
};
struct TransmogOutfitSlotInfo
{
TransmogOutfitSlotInfoEntry const* Data = nullptr;
std::variant<int32, std::unique_ptr<TransmogOutfitSlotOptionInfo[]>> SlotIndexOrOptions;
int32 GetSlotIndex(TransmogOutfitSlotOption slotOption) const
{
switch (SlotIndexOrOptions.index())
{
case 0:
return std::get<0>(SlotIndexOrOptions);
case 1:
return std::get<1>(SlotIndexOrOptions)[AsUnderlyingType(slotOption)].SlotIndex;
default:
return -1;
}
}
};
std::unordered_map<std::pair<uint32 /*itemId*/, uint32 /*appearanceMod*/>, ItemModifiedAppearanceEntry const*> ItemModifiedAppearancesByItem;
std::unordered_map<uint32, TransmogIllusionEntry const*> TransmogIllusionBySpellItemEnchantment;
std::unordered_map<uint32, Trinity::Containers::FlatSet<TransmogSetEntry const*>> TransmogSetsByItemModifiedAppearance;
std::vector<TransmogSetItemEntry const*> TransmogSetItemsByTransmogSet;
std::array<std::vector<TransmogOutfitEntryEntry const*>, AsUnderlyingType(TransmogOutfitEntrySource::Max)> TransmogOutfitsBySource;
std::vector<TransmogOutfitEntryEntry const*> TransmogOutfitsAutomaticallyCreated;
std::vector<TransmogMgr::TransmogOutfitSlotAndOptionInfo> AllSlots;
std::array<TransmogOutfitSlotInfo, AsUnderlyingType(TransmogOutfitSlot::Max)> SlotInfoByOutfitSlot;
std::array<TransmogOutfitSlotInfo const*, EQUIPMENT_SLOT_END> SlotInfoByInvSlot;
std::vector<TransmogSituationEntry const*> DefaultSituations;
constexpr bool IsArtifactTransmogOutfitSlotOption(TransmogOutfitSlotOption option)
{
return option == TransmogOutfitSlotOption::ArtifactSpecOne
|| option == TransmogOutfitSlotOption::ArtifactSpecTwo
|| option == TransmogOutfitSlotOption::ArtifactSpecThree
|| option == TransmogOutfitSlotOption::ArtifactSpecFour;
}
bool IsValidTransmogOutfitSlotForItem(ItemTemplate const* item, TransmogOutfitSlot slot, TransmogOutfitSlotOption option)
{
if (IsArtifactTransmogOutfitSlotOption(option))
if (ArtifactEntry const* artifact = sArtifactStore.LookupEntry(item->GetArtifactID()))
if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(artifact->ChrSpecializationID))
if ((int8(option) - int8(TransmogOutfitSlotOption::ArtifactSpecOne)) != specialization->OrderIndex)
return false;
switch (item->GetInventoryType())
{
case INVTYPE_HEAD:
return slot == TransmogOutfitSlot::Head;
case INVTYPE_SHOULDERS:
return slot == TransmogOutfitSlot::ShoulderLeft || slot == TransmogOutfitSlot::ShoulderRight;
case INVTYPE_BODY:
return slot == TransmogOutfitSlot::Body;
case INVTYPE_CHEST:
case INVTYPE_ROBE:
return slot == TransmogOutfitSlot::Chest;
case INVTYPE_WAIST:
return slot == TransmogOutfitSlot::Waist;
case INVTYPE_LEGS:
return slot == TransmogOutfitSlot::Legs;
case INVTYPE_FEET:
return slot == TransmogOutfitSlot::Feet;
case INVTYPE_WRISTS:
return slot == TransmogOutfitSlot::Wrist;
case INVTYPE_HANDS:
return slot == TransmogOutfitSlot::Hand;
case INVTYPE_WEAPON:
case INVTYPE_WEAPONMAINHAND:
case INVTYPE_WEAPONOFFHAND:
return slot == TransmogOutfitSlot::WeaponMainHand || slot == TransmogOutfitSlot::WeaponOffHand || IsArtifactTransmogOutfitSlotOption(option);
case INVTYPE_SHIELD:
case INVTYPE_HOLDABLE:
return slot == TransmogOutfitSlot::WeaponOffHand || IsArtifactTransmogOutfitSlotOption(option);
case INVTYPE_RANGED:
return (slot == TransmogOutfitSlot::WeaponMainHand && option == TransmogOutfitSlotOption::RangedWeapon) || IsArtifactTransmogOutfitSlotOption(option);
case INVTYPE_CLOAK:
return slot == TransmogOutfitSlot::Back;
case INVTYPE_2HWEAPON:
return slot == TransmogOutfitSlot::WeaponMainHand || (slot == TransmogOutfitSlot::WeaponOffHand && option == TransmogOutfitSlotOption::FuryTwoHandedWeapon) || IsArtifactTransmogOutfitSlotOption(option);
case INVTYPE_TABARD:
return slot == TransmogOutfitSlot::Tabard;
case INVTYPE_RANGEDRIGHT:
return slot == (item->GetSubClass() == ITEM_SUBCLASS_WEAPON_WAND ? TransmogOutfitSlot::WeaponMainHand : TransmogOutfitSlot::WeaponRanged) || IsArtifactTransmogOutfitSlotOption(option);
default:
break;
}
return false;
}
}
void TransmogMgr::Load()
{
for (ItemModifiedAppearanceEntry const* appearanceMod : sItemModifiedAppearanceStore)
ItemModifiedAppearancesByItem[{ appearanceMod->ItemID, appearanceMod->ItemAppearanceModifierID }] = appearanceMod;
for (TransmogIllusionEntry const* transmogIllusion : sTransmogIllusionStore)
TransmogIllusionBySpellItemEnchantment[transmogIllusion->SpellItemEnchantmentID] = transmogIllusion;
for (TransmogSetItemEntry const* transmogSetItem : sTransmogSetItemStore)
{
TransmogSetEntry const* set = sTransmogSetStore.LookupEntry(transmogSetItem->TransmogSetID);
if (!set)
continue;
TransmogSetsByItemModifiedAppearance[transmogSetItem->ItemModifiedAppearanceID].insert(set);
TransmogSetItemsByTransmogSet.push_back(transmogSetItem);
}
std::ranges::sort(TransmogSetItemsByTransmogSet, {}, &TransmogSetItemEntry::TransmogSetID);
for (TransmogOutfitEntryEntry const* transmogOutfitEntry : sTransmogOutfitEntryStore)
{
if (transmogOutfitEntry->HasFlag(TransmogOutfitEntryFlags::AutomaticallyAwardedOnLogin))
TransmogOutfitsAutomaticallyCreated.push_back(transmogOutfitEntry);
if (transmogOutfitEntry->GetSetType() == TransmogOutfitSetType::Outfit)
TransmogOutfitsBySource[AsUnderlyingType(transmogOutfitEntry->GetSource())].push_back(transmogOutfitEntry);
}
for (std::vector<TransmogOutfitEntryEntry const*>& transmogOutfitEntries : TransmogOutfitsBySource)
std::ranges::sort(transmogOutfitEntries, {}, &TransmogOutfitEntryEntry::OrderIndex);
for (TransmogOutfitSlotInfoEntry const* transmogOutfitSlot : sTransmogOutfitSlotInfoStore)
{
ASSERT(transmogOutfitSlot->GetSlot() < TransmogOutfitSlot::Max);
TransmogOutfitSlotInfo* slot = &SlotInfoByOutfitSlot[AsUnderlyingType(transmogOutfitSlot->GetSlot())];
slot->Data = transmogOutfitSlot;
if (!transmogOutfitSlot->HasFlag(TransmogOutfitSlotFlags::IsSecondarySlot))
{
ASSERT(transmogOutfitSlot->InventorySlotEnum < EQUIPMENT_SLOT_END);
SlotInfoByInvSlot[transmogOutfitSlot->InventorySlotEnum] = slot;
}
}
for (TransmogOutfitSlotOptionEntry const* transmogOutfitSlotOption : sTransmogOutfitSlotOptionInfoStore)
{
ASSERT(transmogOutfitSlotOption->GetOption() < TransmogOutfitSlotOption::Max);
TransmogOutfitSlotInfoEntry const* transmogOutfitSlot = sTransmogOutfitSlotInfoStore.AssertEntry(transmogOutfitSlotOption->TransmogOutfitSlotInfoID);
TransmogOutfitSlotInfo& slotInfo = SlotInfoByOutfitSlot[AsUnderlyingType(transmogOutfitSlot->GetSlot())];
if (!std::holds_alternative<std::unique_ptr<TransmogOutfitSlotOptionInfo[]>>(slotInfo.SlotIndexOrOptions))
slotInfo.SlotIndexOrOptions.emplace<std::unique_ptr<TransmogOutfitSlotOptionInfo[]>>(new TransmogOutfitSlotOptionInfo[AsUnderlyingType(TransmogOutfitSlotOption::Max)]);
std::get<std::unique_ptr<TransmogOutfitSlotOptionInfo[]>>(slotInfo.SlotIndexOrOptions)[AsUnderlyingType(transmogOutfitSlotOption->GetOption())].Data = transmogOutfitSlotOption;
}
for (TransmogOutfitSlotInfo& slotInfo : SlotInfoByOutfitSlot)
{
if (!slotInfo.Data)
continue;
TransmogOutfitSlotAndOptionInfo& slot = AllSlots.emplace_back();
slot.Slot = slotInfo.Data;
slot.SlotIndex = AllSlots.size() - 1;
if (std::holds_alternative<std::unique_ptr<TransmogOutfitSlotOptionInfo[]>>(slotInfo.SlotIndexOrOptions))
{
// if slot has options, keep adding transmog slots for every option
auto options = std::span<TransmogOutfitSlotOptionInfo, AsUnderlyingType(TransmogOutfitSlotOption::Max)>(
&std::get<std::unique_ptr<TransmogOutfitSlotOptionInfo[]>>(slotInfo.SlotIndexOrOptions)[0],
AsUnderlyingType(TransmogOutfitSlotOption::Max));
auto optionItr = std::ranges::find_if(options,
[](TransmogOutfitSlotOptionEntry const* option) { return option != nullptr; },
&TransmogOutfitSlotOptionInfo::Data);
slot.SlotOption = optionItr->Data;
optionItr->SlotIndex = AllSlots.size() - 1;
while (++optionItr != options.end())
{
if (!optionItr->Data)
continue;
TransmogOutfitSlotAndOptionInfo& newSlot = AllSlots.emplace_back();
newSlot.Slot = slotInfo.Data;
newSlot.SlotOption = optionItr->Data;
newSlot.SlotIndex = AllSlots.size() - 1;
optionItr->SlotIndex = AllSlots.size() - 1;
}
}
if (std::holds_alternative<int32>(slotInfo.SlotIndexOrOptions))
std::get<int32>(slotInfo.SlotIndexOrOptions) = AllSlots.size() - 1;
}
for (TransmogSituationEntry const* transmogSituation : sTransmogSituationStore)
if (transmogSituation->HasFlag(TransmogSituationFlags::DefaultsToOn))
DefaultSituations.push_back(transmogSituation);
}
ItemModifiedAppearanceEntry const* TransmogMgr::GetItemModifiedAppearance(uint32 itemId, uint32 appearanceModId)
{
auto itr = ItemModifiedAppearancesByItem.find({ itemId, appearanceModId });
if (itr != ItemModifiedAppearancesByItem.end())
return itr->second;
// Fall back to unmodified appearance
if (appearanceModId)
return GetDefaultItemModifiedAppearance(itemId);
return nullptr;
}
ItemModifiedAppearanceEntry const* TransmogMgr::GetDefaultItemModifiedAppearance(uint32 itemId)
{
return Trinity::Containers::MapGetValuePtr(ItemModifiedAppearancesByItem, { itemId, 0 });
}
TransmogIllusionEntry const* TransmogMgr::GetTransmogIllusionForSpellItemEnchantment(uint32 spellItemEnchantmentId)
{
return Trinity::Containers::MapGetValuePtr(TransmogIllusionBySpellItemEnchantment, spellItemEnchantmentId);
}
std::span<TransmogSetEntry const* const> TransmogMgr::GetTransmogSetsForItemModifiedAppearance(uint32 itemModifiedAppearanceId)
{
std::span<TransmogSetEntry const* const> result;
auto itr = TransmogSetsByItemModifiedAppearance.find(itemModifiedAppearanceId);
if (itr != TransmogSetsByItemModifiedAppearance.end())
result = itr->second;
return result;
}
std::span<TransmogSetItemEntry const* const> TransmogMgr::GetTransmogSetItems(uint32 transmogSetId)
{
return std::ranges::equal_range(TransmogSetItemsByTransmogSet, transmogSetId, {}, &TransmogSetItemEntry::TransmogSetID);
}
std::span<TransmogOutfitEntryEntry const* const> TransmogMgr::GetAutomaticallyUnlockedOutfits()
{
return TransmogOutfitsAutomaticallyCreated;
}
std::span<TransmogMgr::TransmogOutfitSlotAndOptionInfo const> TransmogMgr::GetAllSlots()
{
return AllSlots;
}
TransmogMgr::TransmogOutfitSlotAndOptionInfo const* TransmogMgr::GetSlotAndOption(TransmogOutfitSlot slot, TransmogOutfitSlotOption slotOption)
{
int32 slotIndex = SlotInfoByOutfitSlot[AsUnderlyingType(slot)].GetSlotIndex(slotOption);
if (slotIndex >= 0)
return &AllSlots[slotIndex];
return nullptr;
}
TransmogMgr::TransmogOutfitSlotAndOptionInfo const* TransmogMgr::GetSlotAndOption(EquipmentSlots inventorySlot, TransmogOutfitSlotOption slotOption)
{
if (TransmogOutfitSlotInfo const* slotInfo = SlotInfoByInvSlot[inventorySlot])
if (int32 slotIndex = slotInfo->GetSlotIndex(slotOption); slotIndex >= 0)
return &AllSlots[slotIndex];
return nullptr;
}
std::span<TransmogSituationEntry const* const> TransmogMgr::GetDefaultSituations()
{
return DefaultSituations;
}
TransmogOutfitEntryEntry const* TransmogMgr::GetNextOutfitToUnlock(TransmogOutfitEntrySource source, Player const* player)
{
if (source >= TransmogOutfitEntrySource::Max)
return nullptr;
TransmogOutfitEntryEntry const* lastOwnedOutfit = nullptr;
for (auto const& [id, transmogOutfit] : player->m_activePlayerData->TransmogOutfits)
{
TransmogOutfitEntryEntry const* transmogOutfitEntry = sTransmogOutfitEntryStore.LookupEntry(transmogOutfit.value.Id);
if (!transmogOutfitEntry || transmogOutfitEntry->GetSource() != source)
continue;
if (!lastOwnedOutfit || transmogOutfitEntry->OrderIndex > lastOwnedOutfit->OrderIndex)
lastOwnedOutfit = transmogOutfitEntry;
}
if (!lastOwnedOutfit)
return TransmogOutfitsBySource[AsUnderlyingType(source)].front();
auto itr = std::ranges::find(TransmogOutfitsBySource[AsUnderlyingType(source)], lastOwnedOutfit) + 1;
if (itr != TransmogOutfitsBySource[AsUnderlyingType(source)].end())
return *itr;
return nullptr;
}
bool TransmogMgr::ValidateSituations(std::span<WorldPackets::Transmogrification::TransmogOutfitSituationInfo const> situations)
{
struct SituationTriggerStatus
{
uint8 AllSituationCount = 0;
uint8 NoneSituationCount = 0;
uint8 RegularSituationCount = 0;
};
std::array<SituationTriggerStatus, AsUnderlyingType(TransmogSituationTrigger::Max)> statusByTrigger;
for (WorldPackets::Transmogrification::TransmogOutfitSituationInfo const& situation : situations)
{
TransmogSituationEntry const* transmogSituation = sTransmogSituationStore.LookupEntry(situation.SituationID);
if (!transmogSituation)
return false;
TransmogSituationGroupEntry const* transmogSituationGroup = sTransmogSituationGroupStore.LookupEntry(transmogSituation->TransmogSituationGroupID);
if (!transmogSituationGroup)
return false;
TransmogSituationTriggerEntry const* transmogSituationTrigger = sTransmogSituationTriggerStore.LookupEntry(transmogSituationGroup->TransmogSituationTriggerID);
if (!transmogSituationTrigger)
return false;
SituationTriggerStatus& triggers = statusByTrigger[AsUnderlyingType(transmogSituationTrigger->GetTrigger())];
uint8* count = nullptr;
if (transmogSituation->HasFlag(TransmogSituationFlags::AllSituation))
count = &triggers.AllSituationCount;
else if (transmogSituation->HasFlag(TransmogSituationFlags::NoneSituation))
count = &triggers.NoneSituationCount;
else
count = &triggers.RegularSituationCount;
*count += 1;
if (transmogSituationTrigger->HasFlag(TransmogSituationTriggerFlags::SituationsAreExclusive) && *count > 1)
return false;
}
for (SituationTriggerStatus const& triggers : statusByTrigger)
if ((triggers.AllSituationCount > 0) + (triggers.NoneSituationCount > 0) + (triggers.RegularSituationCount > 0) > 1) // only 1 group can be active
return false;
return true;
}
bool TransmogMgr::ValidateSlots(std::span<WorldPackets::Transmogrification::TransmogOutfitSlotData const> slots)
{
for (WorldPackets::Transmogrification::TransmogOutfitSlotData const& slot : slots)
{
if (slot.Slot >= TransmogOutfitSlot::Max)
return false;
if (slot.SlotOption >= TransmogOutfitSlotOption::Max)
return false;
if (slot.AppearanceDisplayType >= TransmogOutfitDisplayType::Max)
return false;
if (slot.IllusionDisplayType >= TransmogOutfitDisplayType::Max)
return false;
if (!GetSlotAndOption(slot.Slot, slot.SlotOption))
return false;
if (slot.ItemModifiedAppearanceID)
{
ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(slot.ItemModifiedAppearanceID);
if (!itemModifiedAppearance)
return false;
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID);
if (!itemTemplate)
return false;
if (!IsValidTransmogOutfitSlotForItem(itemTemplate, slot.Slot, slot.SlotOption))
return false;
TransmogOutfitSlotOption appearanceSlotOption = itemTemplate->GetWeaponTransmogOutfitSlotOption();
if (appearanceSlotOption != slot.SlotOption
&& (slot.SlotOption != TransmogOutfitSlotOption::FuryTwoHandedWeapon
|| appearanceSlotOption != TransmogOutfitSlotOption::TwoHandedWeapon))
return false;
}
if (slot.SpellItemEnchantmentID && !TransmogIllusionBySpellItemEnchantment.contains(slot.SpellItemEnchantmentID))
return false;
}
return true;
}
+88
View File
@@ -0,0 +1,88 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYCORE_TRANSMOG_MGR_H
#define TRINITYCORE_TRANSMOG_MGR_H
#include "Common.h"
#include <span>
class Player;
struct ItemModifiedAppearanceEntry;
struct TransmogIllusionEntry;
struct TransmogOutfitEntryEntry;
struct TransmogOutfitSlotInfoEntry;
struct TransmogOutfitSlotOptionEntry;
struct TransmogSetEntry;
struct TransmogSetItemEntry;
struct TransmogSituationEntry;
enum EquipmentSlots : uint8;
enum class TransmogOutfitEntrySource : uint8;
enum class TransmogOutfitSlot : int8;
enum class TransmogOutfitSlotOption : uint8;
namespace WorldPackets::Transmogrification
{
struct TransmogOutfitSituationInfo;
struct TransmogOutfitSlotData;
}
namespace TransmogMgr
{
struct TransmogOutfitSlotAndOptionInfo
{
TransmogOutfitSlotInfoEntry const* Slot = nullptr;
TransmogOutfitSlotOptionEntry const* SlotOption = nullptr;
uint32 SlotIndex = 0;
};
inline constexpr std::array<std::string_view, TOTAL_LOCALES> DefaultOutfitName =
{
"Outfit"sv,
"\354\235\230\354\203\201"sv,
"Tenue"sv,
"Outfit"sv,
"\345\244\226\350\247\202\346\226\271\346\241\210"sv,
"\346\234\215\350\243\235"sv,
"Atuendo"sv,
"Indumentaria"sv,
"\320\241\320\275\320\260\321\200\321\217\320\266\320\265\320\275\320\270\320\265"sv,
""sv,
"Roupa"sv,
"Completo"sv
};
inline constexpr uint32 DefaultOutfitIcon = 134400;
void Load();
ItemModifiedAppearanceEntry const* GetItemModifiedAppearance(uint32 itemId, uint32 appearanceModId);
ItemModifiedAppearanceEntry const* GetDefaultItemModifiedAppearance(uint32 itemId);
TransmogIllusionEntry const* GetTransmogIllusionForSpellItemEnchantment(uint32 spellItemEnchantmentId);
std::span<TransmogSetEntry const* const> GetTransmogSetsForItemModifiedAppearance(uint32 itemModifiedAppearanceId);
std::span<TransmogSetItemEntry const* const> GetTransmogSetItems(uint32 transmogSetId);
std::span<TransmogOutfitEntryEntry const* const> GetAutomaticallyUnlockedOutfits();
std::span<TransmogOutfitSlotAndOptionInfo const> GetAllSlots();
TransmogOutfitSlotAndOptionInfo const* GetSlotAndOption(TransmogOutfitSlot slot, TransmogOutfitSlotOption slotOption);
TransmogOutfitSlotAndOptionInfo const* GetSlotAndOption(EquipmentSlots inventorySlot, TransmogOutfitSlotOption slotOption);
std::span<TransmogSituationEntry const* const> GetDefaultSituations();
TransmogOutfitEntryEntry const* GetNextOutfitToUnlock(TransmogOutfitEntrySource source, Player const* player);
bool ValidateSituations(std::span<WorldPackets::Transmogrification::TransmogOutfitSituationInfo const> situations);
bool ValidateSlots(std::span<WorldPackets::Transmogrification::TransmogOutfitSlotData const> slots);
}
#endif // TRINITYCORE_TRANSMOG_MGR_H
+4
View File
@@ -96,6 +96,7 @@
#include "TaxiPathGraph.h"
#include "TerrainMgr.h"
#include "TraitMgr.h"
#include "TransmogMgr.h"
#include "TransportMgr.h"
#include "Unit.h"
#include "UpdateTime.h"
@@ -2066,6 +2067,9 @@ bool World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading phase names...");
sObjectMgr->LoadPhaseNames();
TC_LOG_INFO("server.loading", "Loading transmog data...");
TransmogMgr::Load();
uint32 startupDuration = GetMSTimeDiffToNow(startupBegin);
TC_LOG_INFO("server.worldserver", "World initialized in {} minutes {} seconds", startupDuration / 60000, startupDuration % 60000 / 1000);