updated gitignore to include modules
This commit is contained in:
921
modules/mod-transmog/src/Transmogrification.cpp
Normal file
921
modules/mod-transmog/src/Transmogrification.cpp
Normal file
@@ -0,0 +1,921 @@
|
||||
#include "Transmogrification.h"
|
||||
#include "ItemTemplate.h"
|
||||
|
||||
Transmogrification* Transmogrification::instance()
|
||||
{
|
||||
static Transmogrification instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
#ifdef PRESETS
|
||||
void Transmogrification::PresetTransmog(Player* player, Item* itemTransmogrified, uint32 fakeEntry, uint8 slot)
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::PresetTransmog");
|
||||
|
||||
if (!EnableSets)
|
||||
return;
|
||||
if (!player || !itemTransmogrified)
|
||||
return;
|
||||
if (slot >= EQUIPMENT_SLOT_END)
|
||||
return;
|
||||
if (fakeEntry != HIDDEN_ITEM_ID && (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), sObjectMgr->GetItemTemplate(fakeEntry))))
|
||||
return;
|
||||
|
||||
// [AZTH] Custom
|
||||
if (GetFakeEntry(itemTransmogrified->GetGUID()))
|
||||
DeleteFakeEntry(player, slot, itemTransmogrified);
|
||||
|
||||
SetFakeEntry(player, fakeEntry, slot, itemTransmogrified); // newEntry
|
||||
|
||||
|
||||
itemTransmogrified->UpdatePlayedTime(player);
|
||||
|
||||
itemTransmogrified->SetOwnerGUID(player->GetGUID());
|
||||
itemTransmogrified->SetNotRefundable(player);
|
||||
itemTransmogrified->ClearSoulboundTradeable(player);
|
||||
}
|
||||
|
||||
void Transmogrification::LoadPlayerSets(ObjectGuid pGUID)
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::LoadPlayerSets");
|
||||
|
||||
for (presetData::iterator it = presetById[pGUID].begin(); it != presetById[pGUID].end(); ++it)
|
||||
it->second.clear();
|
||||
|
||||
presetById[pGUID].clear();
|
||||
|
||||
presetByName[pGUID].clear();
|
||||
|
||||
QueryResult result = CharacterDatabase.Query("SELECT `PresetID`, `SetName`, `SetData` FROM `custom_transmogrification_sets` WHERE Owner = {}", pGUID.GetCounter());
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
uint8 PresetID = (*result)[0].Get<uint8>();
|
||||
std::string SetName = (*result)[1].Get<std::string>();
|
||||
std::istringstream SetData((*result)[2].Get<std::string>());
|
||||
while (SetData.good())
|
||||
{
|
||||
uint32 slot;
|
||||
uint32 entry;
|
||||
SetData >> slot >> entry;
|
||||
if (SetData.fail())
|
||||
break;
|
||||
if (slot >= EQUIPMENT_SLOT_END)
|
||||
{
|
||||
LOG_ERROR("module", "Item entry (FakeEntry: {}, player: {}, slot: {}, presetId: {}) has invalid slot, ignoring.", entry, pGUID.ToString(), slot, PresetID);
|
||||
continue;
|
||||
}
|
||||
if (entry == HIDDEN_ITEM_ID || sObjectMgr->GetItemTemplate(entry))
|
||||
presetById[pGUID][PresetID][slot] = entry; // Transmogrification::Preset(presetName, fakeEntry);
|
||||
}
|
||||
|
||||
if (!presetById[pGUID][PresetID].empty())
|
||||
{
|
||||
presetByName[pGUID][PresetID] = SetName;
|
||||
// load all presets anyways
|
||||
//if (presetByName[pGUID].size() >= GetMaxSets())
|
||||
// break;
|
||||
}
|
||||
else // should be deleted on startup, so this never runs (shouldnt..)
|
||||
{
|
||||
presetById[pGUID].erase(PresetID);
|
||||
CharacterDatabase.Execute("DELETE FROM `custom_transmogrification_sets` WHERE Owner = {} AND PresetID = {}", pGUID.GetCounter(), PresetID);
|
||||
}
|
||||
} while (result->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
bool Transmogrification::GetEnableSets() const
|
||||
{
|
||||
return EnableSets;
|
||||
}
|
||||
uint8 Transmogrification::GetMaxSets() const
|
||||
{
|
||||
return MaxSets;
|
||||
}
|
||||
float Transmogrification::GetSetCostModifier() const
|
||||
{
|
||||
return SetCostModifier;
|
||||
}
|
||||
int32 Transmogrification::GetSetCopperCost() const
|
||||
{
|
||||
return SetCopperCost;
|
||||
}
|
||||
|
||||
void Transmogrification::UnloadPlayerSets(ObjectGuid pGUID)
|
||||
{
|
||||
for (presetData::iterator it = presetById[pGUID].begin(); it != presetById[pGUID].end(); ++it)
|
||||
it->second.clear();
|
||||
presetById[pGUID].clear();
|
||||
|
||||
presetByName[pGUID].clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* Transmogrification::GetSlotName(uint8 slot, WorldSession* /*session*/) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::GetSlotName");
|
||||
|
||||
switch (slot)
|
||||
{
|
||||
case EQUIPMENT_SLOT_HEAD: return "Head";// session->GetAcoreString(LANG_SLOT_NAME_HEAD);
|
||||
case EQUIPMENT_SLOT_SHOULDERS: return "Shoulders";// session->GetAcoreString(LANG_SLOT_NAME_SHOULDERS);
|
||||
case EQUIPMENT_SLOT_BODY: return "Shirt";// session->GetAcoreString(LANG_SLOT_NAME_BODY);
|
||||
case EQUIPMENT_SLOT_CHEST: return "Chest";// session->GetAcoreString(LANG_SLOT_NAME_CHEST);
|
||||
case EQUIPMENT_SLOT_WAIST: return "Waist";// session->GetAcoreString(LANG_SLOT_NAME_WAIST);
|
||||
case EQUIPMENT_SLOT_LEGS: return "Legs";// session->GetAcoreString(LANG_SLOT_NAME_LEGS);
|
||||
case EQUIPMENT_SLOT_FEET: return "Feet";// session->GetAcoreString(LANG_SLOT_NAME_FEET);
|
||||
case EQUIPMENT_SLOT_WRISTS: return "Wrists";// session->GetAcoreString(LANG_SLOT_NAME_WRISTS);
|
||||
case EQUIPMENT_SLOT_HANDS: return "Hands";// session->GetAcoreString(LANG_SLOT_NAME_HANDS);
|
||||
case EQUIPMENT_SLOT_BACK: return "Back";// session->GetAcoreString(LANG_SLOT_NAME_BACK);
|
||||
case EQUIPMENT_SLOT_MAINHAND: return "Main hand";// session->GetAcoreString(LANG_SLOT_NAME_MAINHAND);
|
||||
case EQUIPMENT_SLOT_OFFHAND: return "Off hand";// session->GetAcoreString(LANG_SLOT_NAME_OFFHAND);
|
||||
case EQUIPMENT_SLOT_RANGED: return "Ranged";// session->GetAcoreString(LANG_SLOT_NAME_RANGED);
|
||||
case EQUIPMENT_SLOT_TABARD: return "Tabard";// session->GetAcoreString(LANG_SLOT_NAME_TABARD);
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Transmogrification::GetItemIcon(uint32 entry, uint32 width, uint32 height, int x, int y) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::GetItemIcon");
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "|TInterface";
|
||||
const ItemTemplate* temp = sObjectMgr->GetItemTemplate(entry);
|
||||
const ItemDisplayInfoEntry* dispInfo = NULL;
|
||||
if (temp)
|
||||
{
|
||||
dispInfo = sItemDisplayInfoStore.LookupEntry(temp->DisplayInfoID);
|
||||
if (dispInfo)
|
||||
ss << "/ICONS/" << dispInfo->inventoryIcon;
|
||||
}
|
||||
if (!dispInfo)
|
||||
ss << "/InventoryItems/WoWUnknownItem01";
|
||||
ss << ":" << width << ":" << height << ":" << x << ":" << y << "|t";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Transmogrification::GetSlotIcon(uint8 slot, uint32 width, uint32 height, int x, int y) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::GetSlotIcon");
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "|TInterface/PaperDoll/";
|
||||
switch (slot)
|
||||
{
|
||||
case EQUIPMENT_SLOT_HEAD: ss << "UI-PaperDoll-Slot-Head"; break;
|
||||
case EQUIPMENT_SLOT_SHOULDERS: ss << "UI-PaperDoll-Slot-Shoulder"; break;
|
||||
case EQUIPMENT_SLOT_BODY: ss << "UI-PaperDoll-Slot-Shirt"; break;
|
||||
case EQUIPMENT_SLOT_CHEST: ss << "UI-PaperDoll-Slot-Chest"; break;
|
||||
case EQUIPMENT_SLOT_WAIST: ss << "UI-PaperDoll-Slot-Waist"; break;
|
||||
case EQUIPMENT_SLOT_LEGS: ss << "UI-PaperDoll-Slot-Legs"; break;
|
||||
case EQUIPMENT_SLOT_FEET: ss << "UI-PaperDoll-Slot-Feet"; break;
|
||||
case EQUIPMENT_SLOT_WRISTS: ss << "UI-PaperDoll-Slot-Wrists"; break;
|
||||
case EQUIPMENT_SLOT_HANDS: ss << "UI-PaperDoll-Slot-Hands"; break;
|
||||
case EQUIPMENT_SLOT_BACK: ss << "UI-PaperDoll-Slot-Chest"; break;
|
||||
case EQUIPMENT_SLOT_MAINHAND: ss << "UI-PaperDoll-Slot-MainHand"; break;
|
||||
case EQUIPMENT_SLOT_OFFHAND: ss << "UI-PaperDoll-Slot-SecondaryHand"; break;
|
||||
case EQUIPMENT_SLOT_RANGED: ss << "UI-PaperDoll-Slot-Ranged"; break;
|
||||
case EQUIPMENT_SLOT_TABARD: ss << "UI-PaperDoll-Slot-Tabard"; break;
|
||||
default: ss << "UI-Backpack-EmptySlot";
|
||||
}
|
||||
ss << ":" << width << ":" << height << ":" << x << ":" << y << "|t";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Transmogrification::GetItemLink(Item* item, WorldSession* session) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::GetItemLink");
|
||||
|
||||
int loc_idx = session->GetSessionDbLocaleIndex();
|
||||
const ItemTemplate* temp = item->GetTemplate();
|
||||
std::string name = temp->Name1;
|
||||
if (ItemLocale const* il = sObjectMgr->GetItemLocale(temp->ItemId))
|
||||
ObjectMgr::GetLocaleString(il->Name, loc_idx, name);
|
||||
|
||||
if (int32 itemRandPropId = item->GetItemRandomPropertyId())
|
||||
{
|
||||
std::array<char const*, 16> const* suffix = nullptr;
|
||||
if (itemRandPropId < 0)
|
||||
{
|
||||
if (const ItemRandomSuffixEntry* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-itemRandPropId))
|
||||
suffix = &itemRandEntry->Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (const ItemRandomPropertiesEntry* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(itemRandPropId))
|
||||
suffix = &itemRandEntry->Name;
|
||||
}
|
||||
if (suffix)
|
||||
{
|
||||
std::string_view test((*suffix)[(name != temp->Name1) ? loc_idx : DEFAULT_LOCALE]);
|
||||
if (!test.empty())
|
||||
{
|
||||
name += ' ';
|
||||
name += test;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "|c" << std::hex << ItemQualityColors[temp->Quality] << std::dec <<
|
||||
"|Hitem:" << temp->ItemId << ":" <<
|
||||
item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT) << ":" <<
|
||||
item->GetEnchantmentId(SOCK_ENCHANTMENT_SLOT) << ":" <<
|
||||
item->GetEnchantmentId(SOCK_ENCHANTMENT_SLOT_2) << ":" <<
|
||||
item->GetEnchantmentId(SOCK_ENCHANTMENT_SLOT_3) << ":" <<
|
||||
item->GetEnchantmentId(BONUS_ENCHANTMENT_SLOT) << ":" <<
|
||||
item->GetItemRandomPropertyId() << ":" << item->GetItemSuffixFactor() << ":" <<
|
||||
// (uint32)item->GetOwner()->getLevel() << "|h[" << name << "]|h|r";
|
||||
(uint32)0 << "|h[" << name << "]|h|r";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string Transmogrification::GetItemLink(uint32 entry, WorldSession* session) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::GetItemLink");
|
||||
|
||||
if (entry == HIDDEN_ITEM_ID)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "(Hidden)";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
const ItemTemplate* temp = sObjectMgr->GetItemTemplate(entry);
|
||||
int loc_idx = session->GetSessionDbLocaleIndex();
|
||||
std::string name = temp->Name1;
|
||||
if (ItemLocale const* il = sObjectMgr->GetItemLocale(entry))
|
||||
ObjectMgr::GetLocaleString(il->Name, loc_idx, name);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "|c" << std::hex << ItemQualityColors[temp->Quality] << std::dec <<
|
||||
"|Hitem:" << entry << ":0:0:0:0:0:0:0:0:0|h[" << name << "]|h|r";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
uint32 Transmogrification::GetFakeEntry(ObjectGuid itemGUID) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::GetFakeEntry");
|
||||
|
||||
transmogData::const_iterator itr = dataMap.find(itemGUID);
|
||||
if (itr == dataMap.end()) return 0;
|
||||
transmogMap::const_iterator itr2 = entryMap.find(itr->second);
|
||||
if (itr2 == entryMap.end()) return 0;
|
||||
transmog2Data::const_iterator itr3 = itr2->second.find(itemGUID);
|
||||
if (itr3 == itr2->second.end()) return 0;
|
||||
return itr3->second;
|
||||
}
|
||||
|
||||
void Transmogrification::UpdateItem(Player* player, Item* item) const
|
||||
{
|
||||
LOG_DEBUG("module", "Transmogrification::UpdateItem");
|
||||
|
||||
if (item->IsEquipped())
|
||||
{
|
||||
player->SetVisibleItemSlot(item->GetSlot(), item);
|
||||
if (player->IsInWorld())
|
||||
item->SendUpdateToPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
void Transmogrification::DeleteFakeEntry(Player* player, uint8 /*slot*/, Item* itemTransmogrified, CharacterDatabaseTransaction* trans /*= nullptr*/)
|
||||
{
|
||||
//if (!GetFakeEntry(item))
|
||||
// return false;
|
||||
DeleteFakeFromDB(itemTransmogrified->GetGUID().GetCounter(), trans);
|
||||
UpdateItem(player, itemTransmogrified);
|
||||
}
|
||||
|
||||
void Transmogrification::SetFakeEntry(Player* player, uint32 newEntry, uint8 /*slot*/, Item* itemTransmogrified)
|
||||
{
|
||||
ObjectGuid itemGUID = itemTransmogrified->GetGUID();
|
||||
entryMap[player->GetGUID()][itemGUID] = newEntry;
|
||||
dataMap[itemGUID] = player->GetGUID();
|
||||
CharacterDatabase.Execute("REPLACE INTO custom_transmogrification (GUID, FakeEntry, Owner) VALUES ({}, {}, {})", itemGUID.GetCounter(), newEntry, player->GetGUID().GetCounter());
|
||||
UpdateItem(player, itemTransmogrified);
|
||||
}
|
||||
|
||||
bool Transmogrification::AddCollectedAppearance(uint32 accountId, uint32 itemId)
|
||||
{
|
||||
if (collectionCache.find(accountId) == collectionCache.end())
|
||||
{
|
||||
collectionCache.insert({accountId, {itemId}});
|
||||
return true;
|
||||
}
|
||||
if (std::find(collectionCache[accountId].begin(), collectionCache[accountId].end(), itemId) == collectionCache[accountId].end())
|
||||
{
|
||||
collectionCache[accountId].push_back(itemId);
|
||||
std::sort(collectionCache[accountId].begin(), collectionCache[accountId].end());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost) {
|
||||
if (itemEntry == UINT_MAX) // Hidden transmog
|
||||
{
|
||||
return Transmogrify(player, nullptr, slot, no_cost, true);
|
||||
}
|
||||
Item* itemTransmogrifier = Item::CreateItem(itemEntry, 1, 0);
|
||||
return Transmogrify(player, itemTransmogrifier, slot, no_cost, false);
|
||||
}
|
||||
|
||||
TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost) {
|
||||
Item* itemTransmogrifier = NULL;
|
||||
// guid of the transmogrifier item, if it's not 0
|
||||
if (itemGUID)
|
||||
{
|
||||
itemTransmogrifier = player->GetItemByGuid(itemGUID);
|
||||
if (!itemTransmogrifier)
|
||||
{
|
||||
//TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: {}, name: {}) tried to transmogrify with an invalid item (lowguid: {}).", player->GetGUIDLow(), player->GetName(), GUID_LOPART(itemGUID));
|
||||
return LANG_ERR_TRANSMOG_MISSING_SRC_ITEM;
|
||||
}
|
||||
}
|
||||
return Transmogrify(player, itemTransmogrifier, slot, no_cost, false);
|
||||
}
|
||||
|
||||
TransmogAcoreStrings Transmogrification::Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost, bool hidden_transmog)
|
||||
{
|
||||
int32 cost = 0;
|
||||
// slot of the transmogrified item
|
||||
if (slot >= EQUIPMENT_SLOT_END)
|
||||
{
|
||||
// TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: {}, name: {}) tried to transmogrify an item (lowguid: {}) with a wrong slot ({}) when transmogrifying items.", player->GetGUIDLow(), player->GetName(), GUID_LOPART(itemGUID), slot);
|
||||
return LANG_ERR_TRANSMOG_INVALID_SLOT;
|
||||
}
|
||||
|
||||
// transmogrified item
|
||||
Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
if (!itemTransmogrified)
|
||||
{
|
||||
//TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: {}, name: {}) tried to transmogrify an invalid item in a valid slot (slot: {}).", player->GetGUIDLow(), player->GetName(), slot);
|
||||
return LANG_ERR_TRANSMOG_MISSING_DEST_ITEM;
|
||||
}
|
||||
|
||||
if (hidden_transmog)
|
||||
{
|
||||
SetFakeEntry(player, HIDDEN_ITEM_ID, slot, itemTransmogrified); // newEntry
|
||||
return LANG_ERR_TRANSMOG_OK;
|
||||
}
|
||||
|
||||
if (!itemTransmogrifier) // reset look newEntry
|
||||
{
|
||||
// Custom
|
||||
DeleteFakeEntry(player, slot, itemTransmogrified);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier->GetTemplate()))
|
||||
{
|
||||
//TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: {}, name: {}) failed CanTransmogrifyItemWithItem ({} with {}).", player->GetGUIDLow(), player->GetName(), itemTransmogrified->GetEntry(), itemTransmogrifier->GetEntry());
|
||||
return LANG_ERR_TRANSMOG_INVALID_ITEMS;
|
||||
}
|
||||
|
||||
if (!no_cost)
|
||||
{
|
||||
if (RequireToken)
|
||||
{
|
||||
if (player->HasItemCount(TokenEntry, TokenAmount))
|
||||
player->DestroyItemCount(TokenEntry, TokenAmount, true);
|
||||
else
|
||||
return LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS;
|
||||
}
|
||||
|
||||
cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
|
||||
cost *= ScaledCostModifier;
|
||||
cost += CopperCost;
|
||||
|
||||
if (cost) // 0 cost if reverting look
|
||||
{
|
||||
if (cost < 0)
|
||||
LOG_DEBUG("module", "Transmogrification::Transmogrify - {} ({}) transmogrification invalid cost (non negative, amount {}). Transmogrified {} with {}",
|
||||
player->GetName(), player->GetGUID().ToString(), -cost, itemTransmogrified->GetEntry(), itemTransmogrifier->GetEntry());
|
||||
else
|
||||
{
|
||||
if (!player->HasEnoughMoney(cost))
|
||||
return LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY;
|
||||
player->ModifyMoney(-cost, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom
|
||||
SetFakeEntry(player, itemTransmogrifier->GetEntry(), slot, itemTransmogrified); // newEntry
|
||||
|
||||
itemTransmogrified->UpdatePlayedTime(player);
|
||||
|
||||
itemTransmogrified->SetOwnerGUID(player->GetGUID());
|
||||
itemTransmogrified->SetNotRefundable(player);
|
||||
itemTransmogrified->ClearSoulboundTradeable(player);
|
||||
|
||||
if (itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_USE)
|
||||
itemTransmogrifier->SetBinding(true);
|
||||
|
||||
itemTransmogrifier->SetOwnerGUID(player->GetGUID());
|
||||
itemTransmogrifier->SetNotRefundable(player);
|
||||
itemTransmogrifier->ClearSoulboundTradeable(player);
|
||||
}
|
||||
|
||||
return LANG_ERR_TRANSMOG_OK;
|
||||
}
|
||||
|
||||
bool Transmogrification::CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* target, ItemTemplate const* source) const
|
||||
{
|
||||
|
||||
if (!target || !source)
|
||||
return false;
|
||||
|
||||
if (source->ItemId == target->ItemId)
|
||||
return false;
|
||||
|
||||
if (source->DisplayInfoID == target->DisplayInfoID)
|
||||
return false;
|
||||
|
||||
if (source->Class != target->Class)
|
||||
return false;
|
||||
|
||||
if (source->InventoryType == INVTYPE_BAG ||
|
||||
source->InventoryType == INVTYPE_RELIC ||
|
||||
// source->InventoryType == INVTYPE_BODY ||
|
||||
source->InventoryType == INVTYPE_FINGER ||
|
||||
source->InventoryType == INVTYPE_TRINKET ||
|
||||
source->InventoryType == INVTYPE_AMMO ||
|
||||
source->InventoryType == INVTYPE_QUIVER)
|
||||
return false;
|
||||
|
||||
if (target->InventoryType == INVTYPE_BAG ||
|
||||
target->InventoryType == INVTYPE_RELIC ||
|
||||
// target->InventoryType == INVTYPE_BODY ||
|
||||
target->InventoryType == INVTYPE_FINGER ||
|
||||
target->InventoryType == INVTYPE_TRINKET ||
|
||||
target->InventoryType == INVTYPE_AMMO ||
|
||||
target->InventoryType == INVTYPE_QUIVER)
|
||||
return false;
|
||||
|
||||
if (!SuitableForTransmogrification(player, target) || !SuitableForTransmogrification(player, source))
|
||||
return false;
|
||||
|
||||
if (IsRangedWeapon(source->Class, source->SubClass) != IsRangedWeapon(target->Class, target->SubClass))
|
||||
return false;
|
||||
|
||||
if (source->SubClass != target->SubClass && !IsRangedWeapon(target->Class, target->SubClass))
|
||||
{
|
||||
if (!IsAllowed(source->ItemId))
|
||||
{
|
||||
if (source->Class == ITEM_CLASS_ARMOR && !AllowMixedArmorTypes)
|
||||
return false;
|
||||
if (source->Class == ITEM_CLASS_WEAPON && !AllowMixedWeaponTypes)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (source->InventoryType != target->InventoryType)
|
||||
{
|
||||
if (source->Class == ITEM_CLASS_WEAPON && !(IsRangedWeapon(target->Class, target->SubClass) ||
|
||||
(
|
||||
// [AZTH] Yehonal: fixed weapon check
|
||||
(target->InventoryType == INVTYPE_WEAPON || target->InventoryType == INVTYPE_2HWEAPON || target->InventoryType == INVTYPE_WEAPONMAINHAND || target->InventoryType == INVTYPE_WEAPONOFFHAND)
|
||||
&& (source->InventoryType == INVTYPE_WEAPON || source->InventoryType == INVTYPE_2HWEAPON || source->InventoryType == INVTYPE_WEAPONMAINHAND || source->InventoryType == INVTYPE_WEAPONOFFHAND)
|
||||
)
|
||||
))
|
||||
return false;
|
||||
if (source->Class == ITEM_CLASS_ARMOR &&
|
||||
!((source->InventoryType == INVTYPE_CHEST || source->InventoryType == INVTYPE_ROBE) &&
|
||||
(target->InventoryType == INVTYPE_CHEST || target->InventoryType == INVTYPE_ROBE)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transmogrification::SuitableForTransmogrification(Player* player, ItemTemplate const* proto) const
|
||||
{
|
||||
// ItemTemplate const* proto = item->GetTemplate();
|
||||
if (!player || !proto)
|
||||
return false;
|
||||
|
||||
if (proto->Class != ITEM_CLASS_ARMOR &&
|
||||
proto->Class != ITEM_CLASS_WEAPON)
|
||||
return false;
|
||||
|
||||
// Skip all checks for allowed items
|
||||
if (IsAllowed(proto->ItemId))
|
||||
return true;
|
||||
|
||||
if (!IsItemTransmogrifiable(proto))
|
||||
return false;
|
||||
|
||||
//[AZTH] Yehonal
|
||||
if (proto->SubClass > 0 && player->GetSkillValue(proto->GetSkill()) == 0)
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_ARMOR && !AllowMixedArmorTypes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (proto->Class == ITEM_CLASS_WEAPON && !AllowMixedWeaponTypes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && player->GetTeamId() != TEAM_HORDE)
|
||||
return false;
|
||||
|
||||
if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && player->GetTeamId() != TEAM_ALLIANCE)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqClass && (proto->AllowableClass & player->getClassMask()) == 0)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqRace && (proto->AllowableRace & player->getRaceMask()) == 0)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqSkill && proto->RequiredSkill != 0)
|
||||
{
|
||||
if (player->GetSkillValue(proto->RequiredSkill) == 0)
|
||||
return false;
|
||||
|
||||
if (player->GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IgnoreReqLevel && player->getLevel() < proto->RequiredLevel)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqSpell && proto->RequiredSpell != 0 && !player->HasSpell(proto->RequiredSpell))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transmogrification::SuitableForTransmogrification(ObjectGuid guid, ItemTemplate const* proto) const
|
||||
{
|
||||
if (!guid || !proto)
|
||||
return false;
|
||||
|
||||
if (proto->Class != ITEM_CLASS_ARMOR &&
|
||||
proto->Class != ITEM_CLASS_WEAPON)
|
||||
return false;
|
||||
|
||||
// Skip all checks for allowed items
|
||||
if (IsAllowed(proto->ItemId))
|
||||
return true;
|
||||
|
||||
if (!IsItemTransmogrifiable(proto))
|
||||
return false;
|
||||
|
||||
auto playerGuid = guid.GetCounter();
|
||||
CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid);
|
||||
if (!playerData)
|
||||
return false;
|
||||
|
||||
uint8 playerRace = playerData->Race;
|
||||
uint8 playerLevel = playerData->Level;
|
||||
uint32 playerRaceMask = 1 << (playerRace - 1);
|
||||
uint32 playerClassMask = 1 << (playerData->Class - 1);
|
||||
TeamId playerTeamId = Player::TeamIdForRace(playerRace);
|
||||
|
||||
std::unordered_map<uint32, uint32> playerSkillValues;
|
||||
if (QueryResult resultSkills = CharacterDatabase.Query("SELECT `skill`, `value` FROM `character_skills` WHERE `guid` = {}", playerGuid))
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = resultSkills->Fetch();
|
||||
uint16 skill = fields[0].Get<uint16>();
|
||||
uint16 value = fields[1].Get<uint16>();
|
||||
playerSkillValues[skill] = value;
|
||||
} while (resultSkills->NextRow());
|
||||
}
|
||||
else {
|
||||
LOG_ERROR("module", "Transmogification could not find skills for player with guid {} in database.", playerGuid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (proto->SubClass > 0 && playerSkillValues[proto->GetSkill()] == 0)
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_ARMOR && !AllowMixedArmorTypes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (proto->Class == ITEM_CLASS_WEAPON && !AllowMixedWeaponTypes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && playerTeamId != TEAM_HORDE)
|
||||
return false;
|
||||
|
||||
if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && playerTeamId != TEAM_ALLIANCE)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqClass && (proto->AllowableClass & playerClassMask) == 0)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqRace && (proto->AllowableRace & playerRaceMask) == 0)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqSkill && proto->RequiredSkill != 0)
|
||||
{
|
||||
if (playerSkillValues[proto->RequiredSkill] == 0)
|
||||
return false;
|
||||
|
||||
if (playerSkillValues[proto->RequiredSkill] < proto->RequiredSkillRank)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IgnoreReqLevel && playerLevel < proto->RequiredLevel)
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqSpell && proto->RequiredSpell != 0 && !(CharacterDatabase.Query("SELECT `spell` FROM `character_spell` WHERE `guid` = {} and `spell` = {}", playerGuid, proto->RequiredSpell)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transmogrification::IsItemTransmogrifiable(ItemTemplate const* proto) const
|
||||
{
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
if (IsNotAllowed(proto->ItemId))
|
||||
return false;
|
||||
|
||||
if (!AllowFishingPoles && proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
|
||||
return false;
|
||||
|
||||
if (!IsAllowedQuality(proto->Quality)) // (proto->Quality == ITEM_QUALITY_LEGENDARY)
|
||||
return false;
|
||||
|
||||
// If World Event is not active, prevent using event dependant items
|
||||
if (!IgnoreReqEvent && proto->HolidayId && !IsHolidayActive((HolidayIds)proto->HolidayId))
|
||||
return false;
|
||||
|
||||
if (!IgnoreReqStats)
|
||||
{
|
||||
if (!proto->RandomProperty && !proto->RandomSuffix
|
||||
/*[AZTH] Yehonal: we should transmorg also items without stats*/
|
||||
&& proto->StatsCount > 0)
|
||||
{
|
||||
bool found = false;
|
||||
for (uint8 i = 0; i < proto->StatsCount; ++i)
|
||||
{
|
||||
if (proto->ItemStat[i].ItemStatValue != 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 Transmogrification::GetSpecialPrice(ItemTemplate const* proto) const
|
||||
{
|
||||
uint32 cost = proto->SellPrice < 10000 ? 10000 : proto->SellPrice;
|
||||
return cost;
|
||||
}
|
||||
bool Transmogrification::IsRangedWeapon(uint32 Class, uint32 SubClass) const
|
||||
{
|
||||
return Class == ITEM_CLASS_WEAPON && (
|
||||
SubClass == ITEM_SUBCLASS_WEAPON_BOW ||
|
||||
SubClass == ITEM_SUBCLASS_WEAPON_GUN ||
|
||||
SubClass == ITEM_SUBCLASS_WEAPON_CROSSBOW);
|
||||
}
|
||||
|
||||
bool Transmogrification::IsAllowed(uint32 entry) const
|
||||
{
|
||||
return Allowed.find(entry) != Allowed.end();
|
||||
}
|
||||
|
||||
bool Transmogrification::IsNotAllowed(uint32 entry) const
|
||||
{
|
||||
return NotAllowed.find(entry) != NotAllowed.end();
|
||||
}
|
||||
|
||||
bool Transmogrification::IsAllowedQuality(uint32 quality) const
|
||||
{
|
||||
switch (quality)
|
||||
{
|
||||
case ITEM_QUALITY_POOR: return AllowPoor;
|
||||
case ITEM_QUALITY_NORMAL: return AllowCommon;
|
||||
case ITEM_QUALITY_UNCOMMON: return AllowUncommon;
|
||||
case ITEM_QUALITY_RARE: return AllowRare;
|
||||
case ITEM_QUALITY_EPIC: return AllowEpic;
|
||||
case ITEM_QUALITY_LEGENDARY: return AllowLegendary;
|
||||
case ITEM_QUALITY_ARTIFACT: return AllowArtifact;
|
||||
case ITEM_QUALITY_HEIRLOOM: return AllowHeirloom;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Transmogrification::CanNeverTransmog(ItemTemplate const* itemTemplate)
|
||||
{
|
||||
return (itemTemplate->InventoryType == INVTYPE_BAG ||
|
||||
itemTemplate->InventoryType == INVTYPE_RELIC ||
|
||||
itemTemplate->InventoryType == INVTYPE_FINGER ||
|
||||
itemTemplate->InventoryType == INVTYPE_TRINKET ||
|
||||
itemTemplate->InventoryType == INVTYPE_AMMO ||
|
||||
itemTemplate->InventoryType == INVTYPE_QUIVER);
|
||||
}
|
||||
|
||||
void Transmogrification::LoadConfig(bool reload)
|
||||
{
|
||||
#ifdef PRESETS
|
||||
EnableSetInfo = sConfigMgr->GetOption<bool>("Transmogrification.EnableSetInfo", true);
|
||||
SetNpcText = sConfigMgr->GetOption<uint32>("Transmogrification.SetNpcText", 601084);
|
||||
|
||||
EnableSets = sConfigMgr->GetOption<bool>("Transmogrification.EnableSets", true);
|
||||
MaxSets = sConfigMgr->GetOption<uint8>("Transmogrification.MaxSets", 10);
|
||||
SetCostModifier = sConfigMgr->GetOption<float>("Transmogrification.SetCostModifier", 3.0f);
|
||||
SetCopperCost = sConfigMgr->GetOption<int32>("Transmogrification.SetCopperCost", 0);
|
||||
|
||||
if (MaxSets > MAX_OPTIONS)
|
||||
MaxSets = MAX_OPTIONS;
|
||||
|
||||
if (reload) // dont store presets for nothing
|
||||
{
|
||||
SessionMap const& sessions = sWorld->GetAllSessions();
|
||||
for (SessionMap::const_iterator it = sessions.begin(); it != sessions.end(); ++it)
|
||||
{
|
||||
if (Player* player = it->second->GetPlayer())
|
||||
{
|
||||
// skipping session check
|
||||
UnloadPlayerSets(player->GetGUID());
|
||||
if (GetEnableSets())
|
||||
LoadPlayerSets(player->GetGUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
EnableTransmogInfo = sConfigMgr->GetOption<bool>("Transmogrification.EnableTransmogInfo", true);
|
||||
TransmogNpcText = uint32(sConfigMgr->GetOption<uint32>("Transmogrification.TransmogNpcText", 601083));
|
||||
|
||||
std::istringstream issAllowed(sConfigMgr->GetOption<std::string>("Transmogrification.Allowed", ""));
|
||||
std::istringstream issNotAllowed(sConfigMgr->GetOption<std::string>("Transmogrification.NotAllowed", ""));
|
||||
while (issAllowed.good())
|
||||
{
|
||||
uint32 entry;
|
||||
issAllowed >> entry;
|
||||
if (issAllowed.fail())
|
||||
break;
|
||||
Allowed.insert(entry);
|
||||
}
|
||||
while (issNotAllowed.good())
|
||||
{
|
||||
uint32 entry;
|
||||
issNotAllowed >> entry;
|
||||
if (issNotAllowed.fail())
|
||||
break;
|
||||
NotAllowed.insert(entry);
|
||||
}
|
||||
|
||||
ScaledCostModifier = sConfigMgr->GetOption<float>("Transmogrification.ScaledCostModifier", 1.0f);
|
||||
CopperCost = sConfigMgr->GetOption<uint32>("Transmogrification.CopperCost", 0);
|
||||
|
||||
RequireToken = sConfigMgr->GetOption<bool>("Transmogrification.RequireToken", false);
|
||||
TokenEntry = sConfigMgr->GetOption<uint32>("Transmogrification.TokenEntry", 49426);
|
||||
TokenAmount = sConfigMgr->GetOption<uint32>("Transmogrification.TokenAmount", 1);
|
||||
|
||||
AllowPoor = sConfigMgr->GetOption<bool>("Transmogrification.AllowPoor", false);
|
||||
AllowCommon = sConfigMgr->GetOption<bool>("Transmogrification.AllowCommon", false);
|
||||
AllowUncommon = sConfigMgr->GetOption<bool>("Transmogrification.AllowUncommon", true);
|
||||
AllowRare = sConfigMgr->GetOption<bool>("Transmogrification.AllowRare", true);
|
||||
AllowEpic = sConfigMgr->GetOption<bool>("Transmogrification.AllowEpic", true);
|
||||
AllowLegendary = sConfigMgr->GetOption<bool>("Transmogrification.AllowLegendary", false);
|
||||
AllowArtifact = sConfigMgr->GetOption<bool>("Transmogrification.AllowArtifact", false);
|
||||
AllowHeirloom = sConfigMgr->GetOption<bool>("Transmogrification.AllowHeirloom", true);
|
||||
AllowTradeable = sConfigMgr->GetOption<bool>("Transmogrification.AllowTradeable", false);
|
||||
|
||||
AllowMixedArmorTypes = sConfigMgr->GetOption<bool>("Transmogrification.AllowMixedArmorTypes", false);
|
||||
AllowMixedWeaponTypes = sConfigMgr->GetOption<bool>("Transmogrification.AllowMixedWeaponTypes", false);
|
||||
AllowFishingPoles = sConfigMgr->GetOption<bool>("Transmogrification.AllowFishingPoles", false);
|
||||
|
||||
IgnoreReqRace = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqRace", false);
|
||||
IgnoreReqClass = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqClass", false);
|
||||
IgnoreReqSkill = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqSkill", false);
|
||||
IgnoreReqSpell = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqSpell", false);
|
||||
IgnoreReqLevel = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqLevel", false);
|
||||
IgnoreReqEvent = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqEvent", false);
|
||||
IgnoreReqStats = sConfigMgr->GetOption<bool>("Transmogrification.IgnoreReqStats", false);
|
||||
UseCollectionSystem = sConfigMgr->GetOption<bool>("Transmogrification.UseCollectionSystem", true);
|
||||
AllowHiddenTransmog = sConfigMgr->GetOption<bool>("Transmogrification.AllowHiddenTransmog", true);
|
||||
TrackUnusableItems = sConfigMgr->GetOption<bool>("Transmogrification.TrackUnusableItems", true);
|
||||
RetroActiveAppearances = sConfigMgr->GetOption<bool>("Transmogrification.RetroActiveAppearances", true);
|
||||
ResetRetroActiveAppearances = sConfigMgr->GetOption<bool>("Transmogrification.ResetRetroActiveAppearancesFlag", false);
|
||||
|
||||
IsTransmogEnabled = sConfigMgr->GetOption<bool>("Transmogrification.Enable", true);
|
||||
|
||||
if (!sObjectMgr->GetItemTemplate(TokenEntry))
|
||||
{
|
||||
TokenEntry = 49426;
|
||||
}
|
||||
}
|
||||
|
||||
void Transmogrification::DeleteFakeFromDB(ObjectGuid::LowType itemLowGuid, CharacterDatabaseTransaction* trans /*= nullptr*/)
|
||||
{
|
||||
ObjectGuid itemGUID = ObjectGuid::Create<HighGuid::Item>(itemLowGuid);
|
||||
|
||||
if (dataMap.find(itemGUID) != dataMap.end())
|
||||
{
|
||||
if (entryMap.find(dataMap[itemGUID]) != entryMap.end())
|
||||
entryMap[dataMap[itemGUID]].erase(itemGUID);
|
||||
dataMap.erase(itemGUID);
|
||||
}
|
||||
if (trans)
|
||||
(*trans)->Append("DELETE FROM custom_transmogrification WHERE GUID = {}", itemLowGuid);
|
||||
else
|
||||
CharacterDatabase.Execute("DELETE FROM custom_transmogrification WHERE GUID = {}", itemGUID.GetCounter());
|
||||
}
|
||||
|
||||
bool Transmogrification::GetEnableTransmogInfo() const
|
||||
{
|
||||
return EnableTransmogInfo;
|
||||
}
|
||||
uint32 Transmogrification::GetTransmogNpcText() const
|
||||
{
|
||||
return TransmogNpcText;
|
||||
}
|
||||
bool Transmogrification::GetEnableSetInfo() const
|
||||
{
|
||||
return EnableSetInfo;
|
||||
}
|
||||
uint32 Transmogrification::GetSetNpcText() const
|
||||
{
|
||||
return SetNpcText;
|
||||
}
|
||||
float Transmogrification::GetScaledCostModifier() const
|
||||
{
|
||||
return ScaledCostModifier;
|
||||
}
|
||||
int32 Transmogrification::GetCopperCost() const
|
||||
{
|
||||
return CopperCost;
|
||||
}
|
||||
bool Transmogrification::GetRequireToken() const
|
||||
{
|
||||
return RequireToken;
|
||||
}
|
||||
uint32 Transmogrification::GetTokenEntry() const
|
||||
{
|
||||
return TokenEntry;
|
||||
}
|
||||
uint32 Transmogrification::GetTokenAmount() const
|
||||
{
|
||||
return TokenAmount;
|
||||
}
|
||||
bool Transmogrification::GetAllowMixedArmorTypes() const
|
||||
{
|
||||
return AllowMixedArmorTypes;
|
||||
};
|
||||
bool Transmogrification::GetAllowMixedWeaponTypes() const
|
||||
{
|
||||
return AllowMixedWeaponTypes;
|
||||
};
|
||||
bool Transmogrification::GetUseCollectionSystem() const
|
||||
{
|
||||
return UseCollectionSystem;
|
||||
};
|
||||
|
||||
bool Transmogrification::GetAllowHiddenTransmog() const
|
||||
{
|
||||
return AllowHiddenTransmog;
|
||||
}
|
||||
|
||||
bool Transmogrification::GetAllowTradeable() const
|
||||
{
|
||||
return AllowTradeable;
|
||||
}
|
||||
|
||||
bool Transmogrification::GetTrackUnusableItems() const
|
||||
{
|
||||
return TrackUnusableItems;
|
||||
}
|
||||
|
||||
bool Transmogrification::EnableRetroActiveAppearances() const
|
||||
{
|
||||
return RetroActiveAppearances;
|
||||
}
|
||||
|
||||
bool Transmogrification::EnableResetRetroActiveAppearances() const
|
||||
{
|
||||
return ResetRetroActiveAppearances;
|
||||
}
|
||||
|
||||
bool Transmogrification::IsEnabled() const
|
||||
{
|
||||
return IsTransmogEnabled;
|
||||
};
|
||||
202
modules/mod-transmog/src/Transmogrification.h
Normal file
202
modules/mod-transmog/src/Transmogrification.h
Normal file
@@ -0,0 +1,202 @@
|
||||
#ifndef DEF_TRANSMOGRIFICATION_H
|
||||
#define DEF_TRANSMOGRIFICATION_H
|
||||
|
||||
#include "Player.h"
|
||||
#include "Config.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedGossip.h"
|
||||
#include "GameEventMgr.h"
|
||||
#include "Item.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Chat.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "QuestDef.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define PRESETS // comment this line to disable preset feature totally
|
||||
#define HIDDEN_ITEM_ID 1 // used for hidden transmog - do not use a valid equipment ID
|
||||
#define MAX_OPTIONS 25 // do not alter
|
||||
#define MAX_SEARCH_STRING_LENGTH 50
|
||||
|
||||
class Item;
|
||||
class Player;
|
||||
class WorldSession;
|
||||
struct ItemTemplate;
|
||||
|
||||
enum TransmogSettings
|
||||
{
|
||||
SETTING_HIDE_TRANSMOG = 0,
|
||||
SETTING_RETROACTIVE_CHECK = 1
|
||||
};
|
||||
|
||||
enum TransmogAcoreStrings // Language.h might have same entries, appears when executing SQL, change if needed
|
||||
{
|
||||
LANG_ERR_TRANSMOG_OK = 11100, // change this
|
||||
LANG_ERR_TRANSMOG_INVALID_SLOT,
|
||||
LANG_ERR_TRANSMOG_INVALID_SRC_ENTRY,
|
||||
LANG_ERR_TRANSMOG_MISSING_SRC_ITEM,
|
||||
LANG_ERR_TRANSMOG_MISSING_DEST_ITEM,
|
||||
LANG_ERR_TRANSMOG_INVALID_ITEMS,
|
||||
LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY,
|
||||
LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS,
|
||||
|
||||
LANG_ERR_UNTRANSMOG_OK,
|
||||
LANG_ERR_UNTRANSMOG_NO_TRANSMOGS,
|
||||
|
||||
#ifdef PRESETS
|
||||
LANG_PRESET_ERR_INVALID_NAME,
|
||||
#endif
|
||||
LANG_CMD_TRANSMOG_SHOW = 11111,
|
||||
LANG_CMD_TRANSMOG_HIDE = 11112,
|
||||
LANG_CMD_TRANSMOG_ADD_UNSUITABLE = 11113,
|
||||
LANG_CMD_TRANSMOG_ADD_FORBIDDEN = 11114,
|
||||
LANG_CMD_TRANSMOG_BEGIN_SYNC = 11115,
|
||||
LANG_CMD_TRANSMOG_COMPLETE_SYNC = 11116,
|
||||
};
|
||||
|
||||
class Transmogrification
|
||||
{
|
||||
public:
|
||||
static Transmogrification* instance();
|
||||
|
||||
typedef std::unordered_map<ObjectGuid, ObjectGuid> transmogData;
|
||||
typedef std::unordered_map<ObjectGuid, uint32> transmog2Data;
|
||||
typedef std::unordered_map<ObjectGuid, transmog2Data> transmogMap;
|
||||
typedef std::unordered_map<uint32, std::vector<uint32>> collectionCacheMap;
|
||||
typedef std::unordered_map<uint32, std::string> searchStringMap;
|
||||
transmogMap entryMap; // entryMap[pGUID][iGUID] = entry
|
||||
transmogData dataMap; // dataMap[iGUID] = pGUID
|
||||
collectionCacheMap collectionCache;
|
||||
|
||||
#ifdef PRESETS
|
||||
bool EnableSetInfo;
|
||||
uint32 SetNpcText;
|
||||
|
||||
typedef std::map<uint8, uint32> slotMap;
|
||||
typedef std::map<uint8, slotMap> presetData;
|
||||
typedef std::unordered_map<ObjectGuid, presetData> presetDataMap;
|
||||
presetDataMap presetById; // presetById[pGUID][presetID][slot] = entry
|
||||
typedef std::map<uint8, std::string> presetIdMap;
|
||||
typedef std::unordered_map<ObjectGuid, presetIdMap> presetNameMap;
|
||||
presetNameMap presetByName; // presetByName[pGUID][presetID] = presetName
|
||||
searchStringMap searchStringByPlayer;
|
||||
|
||||
void PresetTransmog(Player* player, Item* itemTransmogrified, uint32 fakeEntry, uint8 slot);
|
||||
|
||||
bool EnableSets;
|
||||
uint8 MaxSets;
|
||||
float SetCostModifier;
|
||||
int32 SetCopperCost;
|
||||
|
||||
bool GetEnableSets() const;
|
||||
uint8 GetMaxSets() const;
|
||||
float GetSetCostModifier() const;
|
||||
int32 GetSetCopperCost() const;
|
||||
|
||||
void LoadPlayerSets(ObjectGuid pGUID);
|
||||
void UnloadPlayerSets(ObjectGuid pGUID);
|
||||
#endif
|
||||
|
||||
bool EnableTransmogInfo;
|
||||
uint32 TransmogNpcText;
|
||||
|
||||
// Use IsAllowed() and IsNotAllowed()
|
||||
// these are thread unsafe, but assumed to be static data so it should be safe
|
||||
std::set<uint32> Allowed;
|
||||
std::set<uint32> NotAllowed;
|
||||
|
||||
float ScaledCostModifier;
|
||||
int32 CopperCost;
|
||||
|
||||
bool RequireToken;
|
||||
uint32 TokenEntry;
|
||||
uint32 TokenAmount;
|
||||
|
||||
bool AllowPoor;
|
||||
bool AllowCommon;
|
||||
bool AllowUncommon;
|
||||
bool AllowRare;
|
||||
bool AllowEpic;
|
||||
bool AllowLegendary;
|
||||
bool AllowArtifact;
|
||||
bool AllowHeirloom;
|
||||
bool AllowTradeable;
|
||||
|
||||
bool AllowMixedArmorTypes;
|
||||
bool AllowMixedWeaponTypes;
|
||||
bool AllowFishingPoles;
|
||||
|
||||
bool IgnoreReqRace;
|
||||
bool IgnoreReqClass;
|
||||
bool IgnoreReqSkill;
|
||||
bool IgnoreReqSpell;
|
||||
bool IgnoreReqLevel;
|
||||
bool IgnoreReqEvent;
|
||||
bool IgnoreReqStats;
|
||||
|
||||
bool UseCollectionSystem;
|
||||
bool AllowHiddenTransmog;
|
||||
bool TrackUnusableItems;
|
||||
bool RetroActiveAppearances;
|
||||
bool ResetRetroActiveAppearances;
|
||||
|
||||
bool IsTransmogEnabled;
|
||||
|
||||
bool IsAllowed(uint32 entry) const;
|
||||
bool IsNotAllowed(uint32 entry) const;
|
||||
bool IsAllowedQuality(uint32 quality) const;
|
||||
bool IsRangedWeapon(uint32 Class, uint32 SubClass) const;
|
||||
bool CanNeverTransmog(ItemTemplate const* itemTemplate);
|
||||
|
||||
void LoadConfig(bool reload); // thread unsafe
|
||||
|
||||
std::string GetItemIcon(uint32 entry, uint32 width, uint32 height, int x, int y) const;
|
||||
std::string GetSlotIcon(uint8 slot, uint32 width, uint32 height, int x, int y) const;
|
||||
const char * GetSlotName(uint8 slot, WorldSession* session) const;
|
||||
std::string GetItemLink(Item* item, WorldSession* session) const;
|
||||
std::string GetItemLink(uint32 entry, WorldSession* session) const;
|
||||
uint32 GetFakeEntry(ObjectGuid itemGUID) const;
|
||||
void UpdateItem(Player* player, Item* item) const;
|
||||
void DeleteFakeEntry(Player* player, uint8 slot, Item* itemTransmogrified, CharacterDatabaseTransaction* trans = nullptr);
|
||||
void SetFakeEntry(Player* player, uint32 newEntry, uint8 slot, Item* itemTransmogrified);
|
||||
bool AddCollectedAppearance(uint32 accountId, uint32 itemId);
|
||||
|
||||
TransmogAcoreStrings Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost = false);
|
||||
TransmogAcoreStrings Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost = false);
|
||||
TransmogAcoreStrings Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost = false, bool hidden_transmog = false);
|
||||
bool CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* destination, ItemTemplate const* source) const;
|
||||
bool SuitableForTransmogrification(Player* player, ItemTemplate const* proto) const;
|
||||
bool SuitableForTransmogrification(ObjectGuid guid, ItemTemplate const* proto) const;
|
||||
bool IsItemTransmogrifiable(ItemTemplate const* proto) const;
|
||||
uint32 GetSpecialPrice(ItemTemplate const* proto) const;
|
||||
|
||||
void DeleteFakeFromDB(ObjectGuid::LowType itemLowGuid, CharacterDatabaseTransaction* trans = nullptr);
|
||||
float GetScaledCostModifier() const;
|
||||
int32 GetCopperCost() const;
|
||||
|
||||
bool GetRequireToken() const;
|
||||
uint32 GetTokenEntry() const;
|
||||
uint32 GetTokenAmount() const;
|
||||
|
||||
bool GetAllowMixedArmorTypes() const;
|
||||
bool GetAllowMixedWeaponTypes() const;
|
||||
|
||||
// Config
|
||||
bool GetEnableTransmogInfo() const;
|
||||
uint32 GetTransmogNpcText() const;
|
||||
bool GetEnableSetInfo() const;
|
||||
uint32 GetSetNpcText() const;
|
||||
bool GetAllowTradeable() const;
|
||||
|
||||
bool GetUseCollectionSystem() const;
|
||||
bool GetAllowHiddenTransmog() const;
|
||||
bool GetTrackUnusableItems() const;
|
||||
bool EnableRetroActiveAppearances() const;
|
||||
bool EnableResetRetroActiveAppearances() const;
|
||||
[[nodiscard]] bool IsEnabled() const;
|
||||
};
|
||||
#define sTransmogrification Transmogrification::instance()
|
||||
|
||||
#endif
|
||||
292
modules/mod-transmog/src/cs_transmog.cpp
Normal file
292
modules/mod-transmog/src/cs_transmog.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore 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 Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 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 Affero 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 "Chat.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Transmogrification.h"
|
||||
#include "Tokenize.h"
|
||||
|
||||
using namespace Acore::ChatCommands;
|
||||
|
||||
class transmog_commandscript : public CommandScript
|
||||
{
|
||||
public:
|
||||
transmog_commandscript() : CommandScript("transmog_commandscript") { }
|
||||
|
||||
ChatCommandTable GetCommands() const override
|
||||
{
|
||||
static ChatCommandTable addCollectionTable =
|
||||
{
|
||||
{ "set", HandleAddTransmogItemSet, SEC_MODERATOR, Console::Yes },
|
||||
{ "", HandleAddTransmogItem, SEC_MODERATOR, Console::Yes },
|
||||
};
|
||||
|
||||
static ChatCommandTable transmogTable =
|
||||
{
|
||||
{ "add", addCollectionTable },
|
||||
{ "", HandleDisableTransMogVisual, SEC_PLAYER, Console::No },
|
||||
{ "sync", HandleSyncTransMogCommand, SEC_PLAYER, Console::No },
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
{
|
||||
{ "transmog", transmogTable },
|
||||
};
|
||||
|
||||
return commandTable;
|
||||
}
|
||||
|
||||
static bool HandleSyncTransMogCommand(ChatHandler* handler)
|
||||
{
|
||||
Player* player = handler->GetPlayer();
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
handler->SendSysMessage(LANG_CMD_TRANSMOG_BEGIN_SYNC);
|
||||
for (uint32 itemId : sTransmogrification->collectionCache[accountId])
|
||||
{
|
||||
handler->PSendSysMessage("TRANSMOG_SYNC:%u", itemId);
|
||||
}
|
||||
handler->SendSysMessage(LANG_CMD_TRANSMOG_COMPLETE_SYNC);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleDisableTransMogVisual(ChatHandler* handler, bool hide)
|
||||
{
|
||||
Player* player = handler->GetPlayer();
|
||||
|
||||
if (hide)
|
||||
{
|
||||
player->UpdatePlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG, 0);
|
||||
handler->SendSysMessage(LANG_CMD_TRANSMOG_SHOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
player->UpdatePlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG, 1);
|
||||
handler->SendSysMessage(LANG_CMD_TRANSMOG_HIDE);
|
||||
}
|
||||
|
||||
player->UpdateObjectVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleAddTransmogItem(ChatHandler* handler, Optional<PlayerIdentifier> player, ItemTemplate const* itemTemplate)
|
||||
{
|
||||
if (!sTransmogrification->GetUseCollectionSystem())
|
||||
return true;
|
||||
|
||||
if (!sObjectMgr->GetItemTemplate(itemTemplate->ItemId))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemTemplate->ItemId);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player)
|
||||
{
|
||||
player = PlayerIdentifier::FromTargetOrSelf(handler);
|
||||
}
|
||||
|
||||
if (!player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* target = player->GetConnectedPlayer();
|
||||
bool isNotConsole = handler->GetSession();
|
||||
bool suitableForTransmog;
|
||||
|
||||
if (target)
|
||||
{
|
||||
suitableForTransmog = sTransmogrification->SuitableForTransmogrification(target, itemTemplate);
|
||||
}
|
||||
else
|
||||
{
|
||||
suitableForTransmog = sTransmogrification->SuitableForTransmogrification(player->GetGUID(), itemTemplate);
|
||||
}
|
||||
|
||||
if (!sTransmogrification->GetTrackUnusableItems() && !suitableForTransmog)
|
||||
{
|
||||
handler->SendSysMessage(LANG_CMD_TRANSMOG_ADD_UNSUITABLE);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemTemplate->Class != ITEM_CLASS_ARMOR && itemTemplate->Class != ITEM_CLASS_WEAPON)
|
||||
{
|
||||
handler->SendSysMessage(LANG_CMD_TRANSMOG_ADD_FORBIDDEN);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto guid = player->GetGUID();
|
||||
uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid);
|
||||
uint32 itemId = itemTemplate->ItemId;
|
||||
|
||||
std::stringstream tempStream;
|
||||
tempStream << std::hex << ItemQualityColors[itemTemplate->Quality];
|
||||
std::string itemQuality = tempStream.str();
|
||||
std::string itemName = itemTemplate->Name1;
|
||||
std::string playerName = player->GetName();
|
||||
std::string nameLink = handler->playerLink(playerName);
|
||||
|
||||
if (sTransmogrification->AddCollectedAppearance(accountId, itemId))
|
||||
{
|
||||
// Notify target of new item in appearance collection
|
||||
if (target && !(target->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value) && !sTransmogrification->CanNeverTransmog(itemTemplate))
|
||||
{
|
||||
ChatHandler(target->GetSession()).PSendSysMessage(R"(|c%s|Hitem:%u:0:0:0:0:0:0:0:0|h[%s]|h|r has been added to your appearance collection.)", itemQuality.c_str(), itemId, itemName.c_str());
|
||||
}
|
||||
|
||||
// Feedback of successful command execution to GM
|
||||
if (isNotConsole && target != handler->GetPlayer())
|
||||
{
|
||||
handler->PSendSysMessage(R"(|c%s|Hitem:%u:0:0:0:0:0:0:0:0|h[%s]|h|r has been added to the appearance collection of Player %s.)", itemQuality.c_str(), itemId, itemName.c_str(), nameLink);
|
||||
}
|
||||
|
||||
CharacterDatabase.Execute("INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Feedback of failed command execution to GM
|
||||
if (isNotConsole)
|
||||
{
|
||||
handler->PSendSysMessage(R"(Player %s already has item |c%s|Hitem:%u:0:0:0:0:0:0:0:0|h[%s]|h|r in the appearance collection.)", nameLink, itemQuality.c_str(), itemId, itemName.c_str());
|
||||
handler->SetSentErrorMessage(true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleAddTransmogItemSet(ChatHandler* handler, Optional<PlayerIdentifier> player, Variant<Hyperlink<itemset>, uint32> itemSetId)
|
||||
{
|
||||
if (!sTransmogrification->GetUseCollectionSystem())
|
||||
return true;
|
||||
|
||||
if (!*itemSetId)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND, uint32(itemSetId));
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player)
|
||||
{
|
||||
player = PlayerIdentifier::FromTargetOrSelf(handler);
|
||||
}
|
||||
|
||||
if (!player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* target = player->GetConnectedPlayer();
|
||||
ItemSetEntry const* set = sItemSetStore.LookupEntry(uint32(itemSetId));
|
||||
bool isNotConsole = handler->GetSession();
|
||||
|
||||
if (!set)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND, uint32(itemSetId));
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto guid = player->GetGUID();
|
||||
CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid);
|
||||
if (!playerData)
|
||||
return false;
|
||||
|
||||
bool added = false;
|
||||
uint32 error = 0;
|
||||
uint32 itemId;
|
||||
uint32 accountId = playerData->AccountId;
|
||||
|
||||
for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i)
|
||||
{
|
||||
itemId = set->itemId[i];
|
||||
if (itemId)
|
||||
{
|
||||
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (itemTemplate)
|
||||
{
|
||||
if (!sTransmogrification->GetTrackUnusableItems() && (
|
||||
(target && !sTransmogrification->SuitableForTransmogrification(target, itemTemplate)) ||
|
||||
!sTransmogrification->SuitableForTransmogrification(guid, itemTemplate)
|
||||
))
|
||||
{
|
||||
error = LANG_CMD_TRANSMOG_ADD_UNSUITABLE;
|
||||
continue;
|
||||
}
|
||||
if (itemTemplate->Class != ITEM_CLASS_ARMOR && itemTemplate->Class != ITEM_CLASS_WEAPON)
|
||||
{
|
||||
error = LANG_CMD_TRANSMOG_ADD_FORBIDDEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sTransmogrification->AddCollectedAppearance(accountId, itemId))
|
||||
{
|
||||
CharacterDatabase.Execute("INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!added && error > 0)
|
||||
{
|
||||
handler->SendSysMessage(error);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
int locale = handler->GetSessionDbcLocale();
|
||||
std::string setName = set->name[locale];
|
||||
std::string nameLink = handler->playerLink(player->GetName());
|
||||
|
||||
// Feedback of command execution to GM
|
||||
if (isNotConsole)
|
||||
{
|
||||
// Failed command execution
|
||||
if (!added)
|
||||
{
|
||||
handler->PSendSysMessage("Player %s already has ItemSet |cffffffff|Hitemset:%d|h[%s %s]|h|r in the appearance collection.", nameLink, uint32(itemSetId), setName.c_str(), localeNames[locale]);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Successful command execution
|
||||
if (target != handler->GetPlayer())
|
||||
{
|
||||
handler->PSendSysMessage("ItemSet |cffffffff|Hitemset:%d|h[%s %s]|h|r has been added to the appearance collection of Player %s.", uint32(itemSetId), setName.c_str(), localeNames[locale], nameLink);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify target of new item in appearance collection
|
||||
if (target && !(target->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value))
|
||||
{
|
||||
ChatHandler(target->GetSession()).PSendSysMessage("ItemSet |cffffffff|Hitemset:%d|h[%s %s]|h|r has been added to your appearance collection.", uint32(itemSetId), setName.c_str(), localeNames[locale]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_transmog_commandscript()
|
||||
{
|
||||
new transmog_commandscript();
|
||||
}
|
||||
15
modules/mod-transmog/src/transmog_loader.cpp
Normal file
15
modules/mod-transmog/src/transmog_loader.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
|
||||
*/
|
||||
|
||||
// From SC
|
||||
void AddSC_Transmog();
|
||||
void AddSC_transmog_commandscript();
|
||||
|
||||
// Add all
|
||||
void Addmod_transmogScripts()
|
||||
{
|
||||
AddSC_Transmog();
|
||||
AddSC_transmog_commandscript();
|
||||
}
|
||||
809
modules/mod-transmog/src/transmog_scripts.cpp
Normal file
809
modules/mod-transmog/src/transmog_scripts.cpp
Normal file
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
5.0
|
||||
Transmogrification 3.3.5a - Gossip menu
|
||||
By Rochet2
|
||||
|
||||
ScriptName for NPC:
|
||||
Creature_Transmogrify
|
||||
|
||||
TODO:
|
||||
Make DB saving even better (Deleting)? What about coding?
|
||||
|
||||
Fix the cost formula
|
||||
-- Too much data handling, use default costs
|
||||
|
||||
Are the qualities right?
|
||||
Blizzard might have changed the quality requirements.
|
||||
(TC handles it with stat checks)
|
||||
|
||||
Cant transmogrify rediculus items // Foereaper: would be fun to stab people with a fish
|
||||
-- Cant think of any good way to handle this easily, could rip flagged items from cata DB
|
||||
*/
|
||||
|
||||
#include "Transmogrification.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ItemTemplate.h"
|
||||
|
||||
#define sT sTransmogrification
|
||||
#define GTS session->GetAcoreString // dropped translation support, no one using?
|
||||
|
||||
class npc_transmogrifier : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_transmogrifier() : CreatureScript("npc_transmogrifier") { }
|
||||
|
||||
struct npc_transmogrifierAI : ScriptedAI
|
||||
{
|
||||
npc_transmogrifierAI(Creature* creature) : ScriptedAI(creature) { };
|
||||
|
||||
bool CanBeSeen(Player const* player) override
|
||||
{
|
||||
Player* target = ObjectAccessor::FindConnectedPlayer(player->GetGUID());
|
||||
return sTransmogrification->IsEnabled() && !target->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value;
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new npc_transmogrifierAI(creature);
|
||||
}
|
||||
|
||||
bool OnGossipHello(Player* player, Creature* creature) override
|
||||
{
|
||||
WorldSession* session = player->GetSession();
|
||||
|
||||
// Clear the search string for the player
|
||||
sT->searchStringByPlayer.erase(player->GetGUID().GetCounter());
|
||||
|
||||
if (sT->GetEnableTransmogInfo())
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Book_11:30:30:-18:0|tHow does transmogrification work?", EQUIPMENT_SLOT_END + 9, 0);
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (const char* slotName = sT->GetSlotName(slot, session))
|
||||
{
|
||||
Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
uint32 entry = newItem ? sT->GetFakeEntry(newItem->GetGUID()) : 0;
|
||||
std::string icon = entry ? sT->GetItemIcon(entry, 30, 30, -18, 0) : sT->GetSlotIcon(slot, 30, 30, -18, 0);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, icon + std::string(slotName), EQUIPMENT_SLOT_END, slot);
|
||||
}
|
||||
}
|
||||
#ifdef PRESETS
|
||||
if (sT->GetEnableSets())
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/RAIDFRAME/UI-RAIDFRAME-MAINASSIST:30:30:-18:0|tManage sets", EQUIPMENT_SLOT_END + 4, 0);
|
||||
#endif
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Enchant_Disenchant:30:30:-18:0|tRemove all transmogrifications", EQUIPMENT_SLOT_END + 2, 0, "Remove transmogrifications from all equipped items?", 0, false);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", EQUIPMENT_SLOT_END + 1, 0);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override
|
||||
{
|
||||
player->PlayerTalkClass->ClearMenus();
|
||||
WorldSession* session = player->GetSession();
|
||||
// Next page
|
||||
if (sender > EQUIPMENT_SLOT_END + 10)
|
||||
{
|
||||
ShowTransmogItems(player, creature, action, sender);
|
||||
return true;
|
||||
}
|
||||
switch (sender)
|
||||
{
|
||||
case EQUIPMENT_SLOT_END: // Show items you can use
|
||||
ShowTransmogItems(player, creature, action, sender);
|
||||
break;
|
||||
case EQUIPMENT_SLOT_END + 1: // Main menu
|
||||
OnGossipHello(player, creature);
|
||||
break;
|
||||
case EQUIPMENT_SLOT_END + 2: // Remove Transmogrifications
|
||||
{
|
||||
bool removed = false;
|
||||
auto trans = CharacterDatabase.BeginTransaction();
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
{
|
||||
if (!sT->GetFakeEntry(newItem->GetGUID()))
|
||||
continue;
|
||||
sT->DeleteFakeEntry(player, slot, newItem, &trans);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
{
|
||||
session->SendAreaTriggerMessage("%s", GTS(LANG_ERR_UNTRANSMOG_OK));
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
else
|
||||
session->SendNotification(LANG_ERR_UNTRANSMOG_NO_TRANSMOGS);
|
||||
OnGossipHello(player, creature);
|
||||
} break;
|
||||
case EQUIPMENT_SLOT_END + 3: // Remove Transmogrification from single item
|
||||
{
|
||||
if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action))
|
||||
{
|
||||
if (sT->GetFakeEntry(newItem->GetGUID()))
|
||||
{
|
||||
sT->DeleteFakeEntry(player, action, newItem);
|
||||
session->SendAreaTriggerMessage("%s", GTS(LANG_ERR_UNTRANSMOG_OK));
|
||||
}
|
||||
else
|
||||
session->SendNotification(LANG_ERR_UNTRANSMOG_NO_TRANSMOGS);
|
||||
}
|
||||
OnGossipSelect(player, creature, EQUIPMENT_SLOT_END, action);
|
||||
} break;
|
||||
#ifdef PRESETS
|
||||
case EQUIPMENT_SLOT_END + 4: // Presets menu
|
||||
{
|
||||
if (!sT->GetEnableSets())
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
if (sT->GetEnableSetInfo())
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Book_11:30:30:-18:0|tHow do sets work?", EQUIPMENT_SLOT_END + 10, 0);
|
||||
for (Transmogrification::presetIdMap::const_iterator it = sT->presetByName[player->GetGUID()].begin(); it != sT->presetByName[player->GetGUID()].end(); ++it)
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Statue_02:30:30:-18:0|t" + it->second, EQUIPMENT_SLOT_END + 6, it->first);
|
||||
|
||||
if (sT->presetByName[player->GetGUID()].size() < sT->GetMaxSets())
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/GuildBankFrame/UI-GuildBankFrame-NewTab:30:30:-18:0|tSave set", EQUIPMENT_SLOT_END + 8, 0);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 1, 0);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
} break;
|
||||
case EQUIPMENT_SLOT_END + 5: // Use preset
|
||||
{
|
||||
if (!sT->GetEnableSets())
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
// action = presetID
|
||||
for (Transmogrification::slotMap::const_iterator it = sT->presetById[player->GetGUID()][action].begin(); it != sT->presetById[player->GetGUID()][action].end(); ++it)
|
||||
{
|
||||
if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, it->first))
|
||||
sT->PresetTransmog(player, item, it->second, it->first);
|
||||
}
|
||||
OnGossipSelect(player, creature, EQUIPMENT_SLOT_END + 6, action);
|
||||
} break;
|
||||
case EQUIPMENT_SLOT_END + 6: // view preset
|
||||
{
|
||||
if (!sT->GetEnableSets())
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
// action = presetID
|
||||
for (Transmogrification::slotMap::const_iterator it = sT->presetById[player->GetGUID()][action].begin(); it != sT->presetById[player->GetGUID()][action].end(); ++it)
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(it->second, 30, 30, -18, 0) + sT->GetItemLink(it->second, session), sender, action);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Statue_02:30:30:-18:0|tUse this set", EQUIPMENT_SLOT_END + 5, action, "Using this set for transmogrify will bind transmogrified items to you and make them non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + sT->presetByName[player->GetGUID()][action], 0, false);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-LeaveItem-Opaque:30:30:-18:0|tDelete set", EQUIPMENT_SLOT_END + 7, action, "Are you sure you want to delete " + sT->presetByName[player->GetGUID()][action] + "?", 0, false);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 4, 0);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
} break;
|
||||
case EQUIPMENT_SLOT_END + 7: // Delete preset
|
||||
{
|
||||
if (!sT->GetEnableSets())
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
// action = presetID
|
||||
CharacterDatabase.Execute("DELETE FROM `custom_transmogrification_sets` WHERE Owner = {} AND PresetID = {}", player->GetGUID().GetCounter(), action);
|
||||
sT->presetById[player->GetGUID()][action].clear();
|
||||
sT->presetById[player->GetGUID()].erase(action);
|
||||
sT->presetByName[player->GetGUID()].erase(action);
|
||||
|
||||
OnGossipSelect(player, creature, EQUIPMENT_SLOT_END + 4, 0);
|
||||
} break;
|
||||
case EQUIPMENT_SLOT_END + 8: // Save preset
|
||||
{
|
||||
if (!sT->GetEnableSets() || sT->presetByName[player->GetGUID()].size() >= sT->GetMaxSets())
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
uint32 cost = 0;
|
||||
bool canSave = false;
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (!sT->GetSlotName(slot, session))
|
||||
continue;
|
||||
if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
{
|
||||
uint32 entry = sT->GetFakeEntry(newItem->GetGUID());
|
||||
if (!entry)
|
||||
continue;
|
||||
const ItemTemplate* temp = sObjectMgr->GetItemTemplate(entry);
|
||||
if (!temp)
|
||||
continue;
|
||||
if (!sT->SuitableForTransmogrification(player, temp)) // no need to check?
|
||||
continue;
|
||||
cost += sT->GetSpecialPrice(temp);
|
||||
canSave = true;
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(entry, 30, 30, -18, 0) + sT->GetItemLink(entry, session), EQUIPMENT_SLOT_END + 8, 0);
|
||||
}
|
||||
}
|
||||
if (canSave)
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/GuildBankFrame/UI-GuildBankFrame-NewTab:30:30:-18:0|tSave set", 0, 0, "Insert set name", cost*sT->GetSetCostModifier() + sT->GetSetCopperCost(), true);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", sender, action);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 4, 0);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
} break;
|
||||
case EQUIPMENT_SLOT_END + 10: // Set info
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 4, 0);
|
||||
SendGossipMenuFor(player, sT->GetSetNpcText(), creature->GetGUID());
|
||||
} break;
|
||||
#endif
|
||||
case EQUIPMENT_SLOT_END + 9: // Transmog info
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 1, 0);
|
||||
SendGossipMenuFor(player, sT->GetTransmogNpcText(), creature->GetGUID());
|
||||
} break;
|
||||
default: // Transmogrify
|
||||
{
|
||||
if (!sender && !action)
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
// sender = slot, action = display
|
||||
if (sT->GetUseCollectionSystem())
|
||||
{
|
||||
TransmogAcoreStrings res = sT->Transmogrify(player, action, sender);
|
||||
if (res == LANG_ERR_TRANSMOG_OK)
|
||||
session->SendAreaTriggerMessage("%s",GTS(LANG_ERR_TRANSMOG_OK));
|
||||
else
|
||||
session->SendNotification(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
TransmogAcoreStrings res = sT->Transmogrify(player, ObjectGuid::Create<HighGuid::Item>(action), sender);
|
||||
if (res == LANG_ERR_TRANSMOG_OK)
|
||||
session->SendAreaTriggerMessage("%s",GTS(LANG_ERR_TRANSMOG_OK));
|
||||
else
|
||||
session->SendNotification(res);
|
||||
}
|
||||
// OnGossipSelect(player, creature, EQUIPMENT_SLOT_END, sender);
|
||||
// ShowTransmogItems(player, creature, sender);
|
||||
CloseGossipMenuFor(player); // Wait for SetMoney to get fixed, issue #10053
|
||||
} break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef PRESETS
|
||||
bool OnGossipSelectCode(Player* player, Creature* creature, uint32 sender, uint32 action, const char* code) override
|
||||
{
|
||||
player->PlayerTalkClass->ClearMenus();
|
||||
if (sender)
|
||||
{
|
||||
// "sender" is an equipment slot for a search - execute the search
|
||||
std::string searchString(code);
|
||||
if (searchString.length() > MAX_SEARCH_STRING_LENGTH)
|
||||
searchString = searchString.substr(0, MAX_SEARCH_STRING_LENGTH);
|
||||
sT->searchStringByPlayer.erase(player->GetGUID().GetCounter());
|
||||
sT->searchStringByPlayer.insert({player->GetGUID().GetCounter(), searchString});
|
||||
OnGossipSelect(player, creature, EQUIPMENT_SLOT_END, sender - 1);
|
||||
return true;
|
||||
}
|
||||
if (action)
|
||||
return true; // should never happen
|
||||
if (!sT->GetEnableSets())
|
||||
{
|
||||
OnGossipHello(player, creature);
|
||||
return true;
|
||||
}
|
||||
std::string name(code);
|
||||
if (name.find('"') != std::string::npos || name.find('\\') != std::string::npos)
|
||||
player->GetSession()->SendNotification(LANG_PRESET_ERR_INVALID_NAME);
|
||||
else
|
||||
{
|
||||
for (uint8 presetID = 0; presetID < sT->GetMaxSets(); ++presetID) // should never reach over max
|
||||
{
|
||||
if (sT->presetByName[player->GetGUID()].find(presetID) != sT->presetByName[player->GetGUID()].end())
|
||||
continue; // Just remember never to use presetByName[pGUID][presetID] when finding etc!
|
||||
|
||||
int32 cost = 0;
|
||||
std::map<uint8, uint32> items;
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (!sT->GetSlotName(slot, player->GetSession()))
|
||||
continue;
|
||||
if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
{
|
||||
uint32 entry = sT->GetFakeEntry(newItem->GetGUID());
|
||||
if (!entry)
|
||||
continue;
|
||||
if (entry != HIDDEN_ITEM_ID)
|
||||
{
|
||||
const ItemTemplate* temp = sObjectMgr->GetItemTemplate(entry);
|
||||
if (!temp)
|
||||
continue;
|
||||
if (!sT->SuitableForTransmogrification(player, temp))
|
||||
continue;
|
||||
cost += sT->GetSpecialPrice(temp);
|
||||
}
|
||||
items[slot] = entry;
|
||||
}
|
||||
}
|
||||
if (items.empty())
|
||||
break; // no transmogrified items were found to be saved
|
||||
cost *= sT->GetSetCostModifier();
|
||||
cost += sT->GetSetCopperCost();
|
||||
if (!player->HasEnoughMoney(cost))
|
||||
{
|
||||
player->GetSession()->SendNotification(LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY);
|
||||
break;
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
for (std::map<uint8, uint32>::iterator it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
ss << uint32(it->first) << ' ' << it->second << ' ';
|
||||
sT->presetById[player->GetGUID()][presetID][it->first] = it->second;
|
||||
}
|
||||
sT->presetByName[player->GetGUID()][presetID] = name; // Make sure code doesnt mess up SQL!
|
||||
CharacterDatabase.Execute("REPLACE INTO `custom_transmogrification_sets` (`Owner`, `PresetID`, `SetName`, `SetData`) VALUES ({}, {}, \"{}\", \"{}\")", player->GetGUID().GetCounter(), uint32(presetID), name, ss.str());
|
||||
if (cost)
|
||||
player->ModifyMoney(-cost);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//OnGossipSelect(player, creature, EQUIPMENT_SLOT_END+4, 0);
|
||||
CloseGossipMenuFor(player); // Wait for SetMoney to get fixed, issue #10053
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ShowTransmogItems(Player* player, Creature* creature, uint8 slot, uint16 gossipPageNumber) // Only checks bags while can use an item from anywhere in inventory
|
||||
{
|
||||
WorldSession* session = player->GetSession();
|
||||
Item* oldItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
bool sendGossip = true;
|
||||
bool hasSearchString;
|
||||
if (oldItem)
|
||||
{
|
||||
uint32 price = sT->GetSpecialPrice(oldItem->GetTemplate());
|
||||
price *= sT->GetScaledCostModifier();
|
||||
price += sT->GetCopperCost();
|
||||
std::ostringstream ss;
|
||||
ss << std::endl;
|
||||
if (sT->GetRequireToken())
|
||||
ss << std::endl << std::endl << sT->GetTokenAmount() << " x " << sT->GetItemLink(sT->GetTokenEntry(), session);
|
||||
std::string lineEnd = ss.str();
|
||||
|
||||
if (sT->GetUseCollectionSystem())
|
||||
{
|
||||
sendGossip = false;
|
||||
|
||||
uint16 pageNumber = 0;
|
||||
uint32 startValue = 0;
|
||||
uint32 endValue = MAX_OPTIONS - 4;
|
||||
bool lastPage = false;
|
||||
if (gossipPageNumber > EQUIPMENT_SLOT_END + 10)
|
||||
{
|
||||
pageNumber = gossipPageNumber - EQUIPMENT_SLOT_END - 10;
|
||||
startValue = (pageNumber * (MAX_OPTIONS - 2));
|
||||
endValue = (pageNumber + 1) * (MAX_OPTIONS - 2) - 1;
|
||||
}
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
if (sT->collectionCache.find(accountId) != sT->collectionCache.end())
|
||||
{
|
||||
std::unordered_map<uint32, std::string>::iterator searchStringIterator = sT->searchStringByPlayer.find(player->GetGUID().GetCounter());
|
||||
hasSearchString = !(searchStringIterator == sT->searchStringByPlayer.end());
|
||||
std::string searchDisplayValue(hasSearchString ? searchStringIterator->second : "Search....");
|
||||
// Offset values to add Search gossip item
|
||||
if (pageNumber == 0)
|
||||
{
|
||||
if (hasSearchString)
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(30620, 30, 30, -18, 0) + "Searching for: " + searchDisplayValue, slot + 1, 0, "Search for what item?", 0, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(30620, 30, 30, -18, 0) + "Search....", slot + 1, 0, "Search for what item?", 0, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
startValue--;
|
||||
}
|
||||
std::vector<Item*> allowedItems;
|
||||
if (sT->GetAllowHiddenTransmog())
|
||||
{
|
||||
// Offset the start and end values to make space for invisible item entry
|
||||
endValue--;
|
||||
if (pageNumber != 0)
|
||||
{
|
||||
startValue--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add invisible item entry
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "Hide Slot", slot, UINT_MAX, "You are hiding the item in this slot.\nDo you wish to continue?\n\n" + lineEnd, 0, false);
|
||||
}
|
||||
}
|
||||
for (uint32 newItemEntryId : sT->collectionCache[accountId]) {
|
||||
if (!sObjectMgr->GetItemTemplate(newItemEntryId))
|
||||
continue;
|
||||
Item* newItem = Item::CreateItem(newItemEntryId, 1, 0);
|
||||
if (!newItem)
|
||||
continue;
|
||||
if (!sT->CanTransmogrifyItemWithItem(player, oldItem->GetTemplate(), newItem->GetTemplate()))
|
||||
continue;
|
||||
if (sT->GetFakeEntry(oldItem->GetGUID()) == newItem->GetEntry())
|
||||
continue;
|
||||
if (hasSearchString && newItem->GetTemplate()->Name1.find(searchDisplayValue) == std::string::npos)
|
||||
continue;
|
||||
allowedItems.push_back(newItem);
|
||||
}
|
||||
for (uint32 i = startValue; i <= endValue; i++)
|
||||
{
|
||||
if (allowedItems.empty() || i > allowedItems.size() - 1)
|
||||
{
|
||||
lastPage = true;
|
||||
break;
|
||||
}
|
||||
Item* newItem = allowedItems.at(i);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(newItem->GetEntry(), 30, 30, -18, 0) + sT->GetItemLink(newItem, session), slot, newItem->GetEntry(), "Using this item for transmogrify will bind it to you and make it non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + sT->GetItemIcon(newItem->GetEntry(), 40, 40, -15, -10) + sT->GetItemLink(newItem, session) + lineEnd, price, false);
|
||||
}
|
||||
}
|
||||
if (gossipPageNumber == EQUIPMENT_SLOT_END + 11)
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", EQUIPMENT_SLOT_END, slot);
|
||||
if (!lastPage)
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", gossipPageNumber + 1, slot);
|
||||
}
|
||||
}
|
||||
else if (gossipPageNumber > EQUIPMENT_SLOT_END + 11)
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", gossipPageNumber - 1, slot);
|
||||
if (!lastPage)
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", gossipPageNumber + 1, slot);
|
||||
}
|
||||
}
|
||||
else if (!lastPage)
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", EQUIPMENT_SLOT_END + 11, slot);
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Enchant_Disenchant:30:30:-18:0|tRemove transmogrification", EQUIPMENT_SLOT_END + 3, slot, "Remove transmogrification from the slot?", 0, false);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", EQUIPMENT_SLOT_END, slot);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 1, 0);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 limit = 0;
|
||||
for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
if (limit > MAX_OPTIONS)
|
||||
break;
|
||||
Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
|
||||
if (!newItem)
|
||||
continue;
|
||||
if (!sT->CanTransmogrifyItemWithItem(player, oldItem->GetTemplate(), newItem->GetTemplate()))
|
||||
continue;
|
||||
if (sT->GetFakeEntry(oldItem->GetGUID()) == newItem->GetEntry())
|
||||
continue;
|
||||
++limit;
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(newItem->GetEntry(), 30, 30, -18, 0) + sT->GetItemLink(newItem, session), slot, newItem->GetGUID().GetCounter(), "Using this item for transmogrify will bind it to you and make it non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + sT->GetItemIcon(newItem->GetEntry(), 40, 40, -15, -10) + sT->GetItemLink(newItem, session) + lineEnd, price, false);
|
||||
}
|
||||
|
||||
for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
||||
{
|
||||
Bag* bag = player->GetBagByPos(i);
|
||||
if (!bag)
|
||||
continue;
|
||||
for (uint32 j = 0; j < bag->GetBagSize(); ++j)
|
||||
{
|
||||
if (limit > MAX_OPTIONS)
|
||||
break;
|
||||
Item* newItem = player->GetItemByPos(i, j);
|
||||
if (!newItem)
|
||||
continue;
|
||||
if (!sT->CanTransmogrifyItemWithItem(player, oldItem->GetTemplate(), newItem->GetTemplate()))
|
||||
continue;
|
||||
if (sT->GetFakeEntry(oldItem->GetGUID()) == newItem->GetEntry())
|
||||
continue;
|
||||
++limit;
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sT->GetItemIcon(newItem->GetEntry(), 30, 30, -18, 0) + sT->GetItemLink(newItem, session), slot, newItem->GetGUID().GetCounter(), "Using this item for transmogrify will bind it to you and make it non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + sT->GetItemIcon(newItem->GetEntry(), 40, 40, -15, -10) + sT->GetItemLink(newItem, session) + ss.str(), price, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sendGossip) {
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Enchant_Disenchant:30:30:-18:0|tRemove transmogrification", EQUIPMENT_SLOT_END + 3, slot, "Remove transmogrification from the slot?", 0, false);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", EQUIPMENT_SLOT_END, slot);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", EQUIPMENT_SLOT_END + 1, 0);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PS_Transmogrification : public PlayerScript
|
||||
{
|
||||
private:
|
||||
void AddToDatabase(Player* player, Item* item)
|
||||
{
|
||||
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE) && !sTransmogrification->GetAllowTradeable())
|
||||
return;
|
||||
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE))
|
||||
return;
|
||||
ItemTemplate const* itemTemplate = item->GetTemplate();
|
||||
AddToDatabase(player, itemTemplate);
|
||||
}
|
||||
|
||||
void AddToDatabase(Player* player, ItemTemplate const* itemTemplate)
|
||||
{
|
||||
if (!sT->GetTrackUnusableItems() && !sT->SuitableForTransmogrification(player, itemTemplate))
|
||||
return;
|
||||
if (itemTemplate->Class != ITEM_CLASS_ARMOR && itemTemplate->Class != ITEM_CLASS_WEAPON)
|
||||
return;
|
||||
uint32 itemId = itemTemplate->ItemId;
|
||||
uint32 accountId = player->GetSession()->GetAccountId();
|
||||
std::string itemName = itemTemplate -> Name1;
|
||||
std::stringstream tempStream;
|
||||
tempStream << std::hex << ItemQualityColors[itemTemplate->Quality];
|
||||
std::string itemQuality = tempStream.str();
|
||||
bool showChatMessage = !(player->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value) && !sT->CanNeverTransmog(itemTemplate);
|
||||
if (sT->AddCollectedAppearance(accountId, itemId))
|
||||
{
|
||||
if (showChatMessage)
|
||||
ChatHandler(player->GetSession()).PSendSysMessage( R"(|c%s|Hitem:%u:0:0:0:0:0:0:0:0|h[%s]|h|r has been added to your appearance collection.)", itemQuality.c_str(), itemId, itemName.c_str());
|
||||
CharacterDatabase.Execute( "INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckRetroActiveQuestAppearances(Player* player)
|
||||
{
|
||||
const RewardedQuestSet& rewQuests = player->getRewardedQuests();
|
||||
for (RewardedQuestSet::const_iterator itr = rewQuests.begin(); itr != rewQuests.end(); ++itr)
|
||||
{
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(*itr);
|
||||
OnPlayerCompleteQuest(player, quest);
|
||||
}
|
||||
player->UpdatePlayerSetting("mod-transmog", SETTING_RETROACTIVE_CHECK, 1);
|
||||
}
|
||||
public:
|
||||
PS_Transmogrification() : PlayerScript("Player_Transmogrify") { }
|
||||
|
||||
void OnEquip(Player* player, Item* it, uint8 /*bag*/, uint8 /*slot*/, bool /*update*/) override
|
||||
{
|
||||
if (!sT->GetUseCollectionSystem())
|
||||
return;
|
||||
AddToDatabase(player, it);
|
||||
}
|
||||
|
||||
void OnLootItem(Player* player, Item* item, uint32 /*count*/, ObjectGuid /*lootguid*/) override
|
||||
{
|
||||
if (!sT->GetUseCollectionSystem() || !item)
|
||||
return;
|
||||
if (item->GetTemplate()->Bonding == ItemBondingType::BIND_WHEN_PICKED_UP || item->IsSoulBound())
|
||||
{
|
||||
AddToDatabase(player, item);
|
||||
}
|
||||
}
|
||||
|
||||
void OnCreateItem(Player* player, Item* item, uint32 /*count*/) override
|
||||
{
|
||||
if (!sT->GetUseCollectionSystem())
|
||||
return;
|
||||
if (item->GetTemplate()->Bonding == ItemBondingType::BIND_WHEN_PICKED_UP || item->IsSoulBound())
|
||||
{
|
||||
AddToDatabase(player, item);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAfterStoreOrEquipNewItem(Player* player, uint32 /*vendorslot*/, Item* item, uint8 /*count*/, uint8 /*bag*/, uint8 /*slot*/, ItemTemplate const* /*pProto*/, Creature* /*pVendor*/, VendorItem const* /*crItem*/, bool /*bStore*/) override
|
||||
{
|
||||
if (!sT->GetUseCollectionSystem())
|
||||
return;
|
||||
if (item->GetTemplate()->Bonding == ItemBondingType::BIND_WHEN_PICKED_UP || item->IsSoulBound())
|
||||
{
|
||||
AddToDatabase(player, item);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerCompleteQuest(Player* player, Quest const* quest) override
|
||||
{
|
||||
if (!sT->GetUseCollectionSystem() || !quest)
|
||||
return;
|
||||
for (uint8 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
|
||||
{
|
||||
uint32 itemId = uint32(quest->RewardChoiceItemId[i]);
|
||||
if (!itemId)
|
||||
continue;
|
||||
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
|
||||
AddToDatabase(player, itemTemplate);
|
||||
}
|
||||
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
|
||||
{
|
||||
uint32 itemId = uint32(quest->RewardItemId[i]);
|
||||
if (!itemId)
|
||||
continue;
|
||||
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
|
||||
AddToDatabase(player, itemTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAfterSetVisibleItemSlot(Player* player, uint8 slot, Item *item) override
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (uint32 entry = sT->GetFakeEntry(item->GetGUID()))
|
||||
{
|
||||
player->SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), entry);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAfterMoveItemFromInventory(Player* /*player*/, Item* it, uint8 /*bag*/, uint8 /*slot*/, bool /*update*/) override
|
||||
{
|
||||
sT->DeleteFakeFromDB(it->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
void OnLogin(Player* player) override
|
||||
{
|
||||
if (sT->EnableResetRetroActiveAppearances())
|
||||
{
|
||||
player->UpdatePlayerSetting("mod-transmog", SETTING_RETROACTIVE_CHECK, 0);
|
||||
}
|
||||
if (sT->EnableRetroActiveAppearances() && !(player->GetPlayerSetting("mod-transmog", SETTING_RETROACTIVE_CHECK).value))
|
||||
{
|
||||
CheckRetroActiveQuestAppearances(player);
|
||||
}
|
||||
ObjectGuid playerGUID = player->GetGUID();
|
||||
sT->entryMap.erase(playerGUID);
|
||||
QueryResult result = CharacterDatabase.Query("SELECT GUID, FakeEntry FROM custom_transmogrification WHERE Owner = {}", player->GetGUID().GetCounter());
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
ObjectGuid itemGUID = ObjectGuid::Create<HighGuid::Item>((*result)[0].Get<uint32>());
|
||||
uint32 fakeEntry = (*result)[1].Get<uint32>();
|
||||
if (fakeEntry == HIDDEN_ITEM_ID || sObjectMgr->GetItemTemplate(fakeEntry))
|
||||
{
|
||||
sT->dataMap[itemGUID] = playerGUID;
|
||||
sT->entryMap[playerGUID][itemGUID] = fakeEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
//sLog->outError(LOG_FILTER_SQL, "Item entry (Entry: {}, itemGUID: {}, playerGUID: {}) does not exist, ignoring.", fakeEntry, GUID_LOPART(itemGUID), player->GetGUIDLow());
|
||||
// CharacterDatabase.Execute("DELETE FROM custom_transmogrification WHERE FakeEntry = {}", fakeEntry);
|
||||
}
|
||||
} while (result->NextRow());
|
||||
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
player->SetVisibleItemSlot(slot, item);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PRESETS
|
||||
if (sT->GetEnableSets())
|
||||
sT->LoadPlayerSets(playerGUID);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnLogout(Player* player) override
|
||||
{
|
||||
ObjectGuid pGUID = player->GetGUID();
|
||||
for (Transmogrification::transmog2Data::const_iterator it = sT->entryMap[pGUID].begin(); it != sT->entryMap[pGUID].end(); ++it)
|
||||
sT->dataMap.erase(it->first);
|
||||
sT->entryMap.erase(pGUID);
|
||||
|
||||
#ifdef PRESETS
|
||||
if (sT->GetEnableSets())
|
||||
sT->UnloadPlayerSets(pGUID);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class WS_Transmogrification : public WorldScript
|
||||
{
|
||||
public:
|
||||
WS_Transmogrification() : WorldScript("WS_Transmogrification") { }
|
||||
|
||||
void OnAfterConfigLoad(bool reload) override
|
||||
{
|
||||
sT->LoadConfig(reload);
|
||||
if (sT->GetUseCollectionSystem())
|
||||
{
|
||||
LOG_INFO("module", "Loading transmog appearance collection cache....");
|
||||
uint32 collectedAppearanceCount = 0;
|
||||
QueryResult result = CharacterDatabase.Query("SELECT account_id, item_template_id FROM custom_unlocked_appearances");
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
uint32 accountId = (*result)[0].Get<uint32>();
|
||||
uint32 itemId = (*result)[1].Get<uint32>();
|
||||
if (sT->AddCollectedAppearance(accountId, itemId))
|
||||
{
|
||||
collectedAppearanceCount++;
|
||||
}
|
||||
} while (result->NextRow());
|
||||
}
|
||||
LOG_INFO("module", "Loaded {} collected appearances into cache", collectedAppearanceCount);
|
||||
}
|
||||
}
|
||||
|
||||
void OnStartup() override
|
||||
{
|
||||
sT->LoadConfig(false);
|
||||
//sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Deleting non-existing transmogrification entries...");
|
||||
CharacterDatabase.Execute("DELETE FROM custom_transmogrification WHERE NOT EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_transmogrification.GUID)");
|
||||
|
||||
#ifdef PRESETS
|
||||
// Clean even if disabled
|
||||
// Dont delete even if player has more presets than should
|
||||
CharacterDatabase.Execute("DELETE FROM `custom_transmogrification_sets` WHERE NOT EXISTS(SELECT 1 FROM characters WHERE characters.guid = custom_transmogrification_sets.Owner)");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class global_transmog_script : public GlobalScript
|
||||
{
|
||||
public:
|
||||
global_transmog_script() : GlobalScript("global_transmog_script") { }
|
||||
|
||||
void OnItemDelFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) override
|
||||
{
|
||||
sT->DeleteFakeFromDB(itemGuid, &trans);
|
||||
}
|
||||
|
||||
void OnMirrorImageDisplayItem(const Item *item, uint32 &display) override
|
||||
{
|
||||
if (uint32 entry = sTransmogrification->GetFakeEntry(item->GetGUID()))
|
||||
{
|
||||
if (entry == HIDDEN_ITEM_ID)
|
||||
{
|
||||
display = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
display=uint32(sObjectMgr->GetItemTemplate(entry)->DisplayInfoID);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class unit_transmog_script : public UnitScript
|
||||
{
|
||||
public:
|
||||
unit_transmog_script() : UnitScript("unit_transmog_script") { }
|
||||
|
||||
bool OnBuildValuesUpdate(Unit const* unit, uint8 /*updateType*/, ByteBuffer& fieldBuffer, Player* target, uint16 index) override
|
||||
{
|
||||
if (unit->IsPlayer() && index >= PLAYER_VISIBLE_ITEM_1_ENTRYID && index <= PLAYER_VISIBLE_ITEM_19_ENTRYID && (index & 1))
|
||||
{
|
||||
if (Item* item = unit->ToPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, ((index - PLAYER_VISIBLE_ITEM_1_ENTRYID) / 2U)))
|
||||
{
|
||||
if (!sTransmogrification->IsEnabled() || target->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value)
|
||||
{
|
||||
fieldBuffer << item->GetEntry();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_Transmog()
|
||||
{
|
||||
new global_transmog_script();
|
||||
new unit_transmog_script();
|
||||
new npc_transmogrifier();
|
||||
new PS_Transmogrification();
|
||||
new WS_Transmogrification();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user