new tmog still not ready wait for sqls

This commit is contained in:
luis
2026-03-22 18:57:22 -03:00
parent aed90f22e5
commit c9dab6ac93
14 changed files with 1152 additions and 24 deletions
+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_DEBUG("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"
#ifdef ELUNA
@@ -263,6 +264,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);
@@ -2257,10 +2270,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)
@@ -136,7 +137,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);
@@ -334,6 +335,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)); 1Code has comments.Press enter to view.
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
@@ -1152,10 +1152,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_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTransferCurrencyFromAccountCharacter);
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);
@@ -2528,10 +2528,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
@@ -1180,11 +1180,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
@@ -1303,6 +1299,7 @@ public:
ITEM_APPEARANCES,
ITEM_FAVORITE_APPEARANCES,
TRANSMOG_ILLUSIONS,
TRANSMOG_OUTFITS,
WARBAND_SCENES,
PLAYER_DATA_ELEMENTS_ACCOUNT,
PLAYER_DATA_FLAGS_ACCOUNT,
@@ -1353,6 +1350,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;
@@ -1417,6 +1418,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
@@ -1050,6 +1050,10 @@ namespace WorldPackets
namespace Transmogrification
{
class TransmogrifyItems;
class TransmogOutfitNew;
class TransmogOutfitUpdateInfo;
class TransmogOutfitUpdateSituations;
class TransmogOutfitUpdateSlots;
}
namespace Vehicle
@@ -2222,6 +2226,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,
+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"
@@ -4993,7 +4994,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:
@@ -5006,7 +5010,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
@@ -481,6 +481,7 @@ class TC_GAME_API Spell
void EffectSetPlayerDataElementCharacter();
void EffectSetPlayerDataFlagAccount();
void EffectSetPlayerDataFlagCharacter();
void EffectEquipTransmogOutfit();
//WowCommunity
void EffectGiveHouseLevel();
void EffectCollectHousingDecor();
@@ -661,6 +662,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];
+27 -1
View File
@@ -452,7 +452,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::EffectGiveHouseLevel, //348 SPELL_EFFECT_GIVE_HOUSE_LEVEL
&Spell::EffectLearnHouseRoom, //349 SPELL_EFFECT_LEARN_HOUSE_ROOM
&Spell::EffectLearnHouseExteriorComponent, //350 SPELL_EFFECT_LEARN_HOUSE_EXTERIOR_COMPONENT
@@ -6360,6 +6360,32 @@ 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);
}
void Spell::EffectRestoreGarrisonTroopVitality()
{
+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