new tmog still not ready wait for sqls
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user