void WorldSession::HandleSocketOpcode(WorldPacket& recvData)
{
    sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SOCKET_GEMS");

    uint64 item_guid;
    uint64 gem_guids[MAX_GEM_SOCKETS];

    recvData >> item_guid;
    if (!item_guid)
        return;

    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
        recvData >> gem_guids[i];

    //cheat -> tried to socket same gem multiple times
    if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) ||
        (gem_guids[1] && (gem_guids[1] == gem_guids[2])))
        return;

    Item* itemTarget = _player->GetItemByGuid(item_guid);
    if (!itemTarget)                                         //missing item to socket
        return;

    ItemTemplate const* itemProto = itemTarget->GetTemplate();
    if (!itemProto)
        return;

    //this slot is excepted when applying / removing meta gem bonus
    uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT);

    Item* Gems[MAX_GEM_SOCKETS];
    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
        Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : NULL;

    GemPropertiesEntry const* GemProps[MAX_GEM_SOCKETS];
    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)                //get geminfo from dbc storage
        GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetTemplate()->GemProperties) : NULL;

    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)                //check for hack maybe
    {
        if (!GemProps[i])
            continue;

        // tried to put gem in socket where no socket exists (take care about prismatic sockets)
        if (!itemProto->Socket[i].Color)
        {
            // no prismatic socket
            if (!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
                return;

            // not first not-colored (not normaly used) socket
            if (i != 0 && !itemProto->Socket[i-1].Color && (i+1 >= MAX_GEM_SOCKETS || itemProto->Socket[i+1].Color))
                return;

            // ok, this is first not colored socket for item with prismatic socket
        }

        // tried to put normal gem in meta socket
        if (itemProto->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META)
            return;

        // tried to put meta gem in normal socket
        if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META)
            return;
    }

    uint32 GemEnchants[MAX_GEM_SOCKETS];
    uint32 OldEnchants[MAX_GEM_SOCKETS];
    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)                //get new and old enchantments
    {
        GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
        OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
    }

    // check unique-equipped conditions
    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
    {
        if (!Gems[i])
            continue;

        // continue check for case when attempt add 2 similar unique equipped gems in one item.
        ItemTemplate const* iGemProto = Gems[i]->GetTemplate();

        // unique item (for new and already placed bit removed enchantments
        if (iGemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED)
        {
            for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
            {
                if (i == j)                                    // skip self
                    continue;

                if (Gems[j])
                {
                    if (iGemProto->ItemId == Gems[j]->GetEntry())
                    {
                        _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
                        return;
                    }
                }
                else if (OldEnchants[j])
                {
                    if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
                    {
                        if (iGemProto->ItemId == enchantEntry->GemID)
                        {
                            _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
                            return;
                        }
                    }
                }
            }
        }

        // unique limit type item
        int32 limit_newcount = 0;
        if (iGemProto->ItemLimitCategory)
        {
            if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory))
            {
                // NOTE: limitEntry->mode is not checked because if item has limit then it is applied in equip case
                for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
                {
                    if (Gems[j])
                    {
                        // new gem
                        if (iGemProto->ItemLimitCategory == Gems[j]->GetTemplate()->ItemLimitCategory)
                            ++limit_newcount;
                    }
                    else if (OldEnchants[j])
                    {
                        // existing gem
                        if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
                            if (ItemTemplate const* jProto = sObjectMgr->GetItemTemplate(enchantEntry->GemID))
                                if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory)
                                    ++limit_newcount;
                    }
                }

                if (limit_newcount > 0 && uint32(limit_newcount) > limitEntry->maxCount)
                {
                    _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
                    return;
                }
            }
        }

        // for equipped item check all equipment for duplicate equipped gems
        if (itemTarget->IsEquipped())
        {
            if (InventoryResult res = _player->CanEquipUniqueItem(Gems[i], slot, std::max(limit_newcount, 0)))
            {
                _player->SendEquipError(res, itemTarget, NULL);
                return;
            }
        }
    }

    bool SocketBonusActivated = itemTarget->GemsFitSockets();    //save state of socketbonus
    _player->ToggleMetaGemsActive(slot, false);             //turn off all metagems (except for the target item)

    //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met

    //remove ALL enchants
    for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
        _player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), false);

    for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
    {
        if (GemEnchants[i])
        {
            itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i], 0, 0);
            if (Item* guidItem = _player->GetItemByGuid(gem_guids[i]))
                _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true);
        }
    }

    for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
        _player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), true);

    bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
    if (SocketBonusActivated ^ SocketBonusToBeActivated)     //if there was a change...
    {
        _player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, false);
        itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetTemplate()->socketBonus : 0), 0, 0);
        _player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, true);
        //it is not displayed, client has an inbuilt system to determine if the bonus is activated
    }

    _player->ToggleMetaGemsActive(slot, true);              //turn on all metagems (except for target item)

    _player->RemoveTradeableItem(itemTarget);
    itemTarget->ClearSoulboundTradeable(_player);           // clear tradeable flag
}
TransmogTrinityStrings Transmogrification::Transmogrify(Player* player, uint64 itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost)
{
    int32 cost = 0;
    // slot of the transmogrified item
    if (slot >= EQUIPMENT_SLOT_END)
    {
        TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify an item (lowguid: %u) with a wrong slot (%u) when transmogrifying items.", player->GetGUIDLow(), player->GetName().c_str(), GUID_LOPART(itemGUID), slot);
        return LANG_ERR_TRANSMOG_INVALID_SLOT;
    }

    /* // GET FROM itemTransmogrifier
    // entry of the transmogrifier item, if it's not 0
    if (newEntry)
    {
    ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newEntry);
    if (!proto)
    {
    TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify to an invalid item (entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), newEntry);
    return LANG_ERR_TRANSMOG_INVALID_SRC_ENTRY;
    }
    }
    */

    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: %u, name: %s) tried to transmogrify with an invalid item (lowguid: %u).", player->GetGUIDLow(), player->GetName().c_str(), GUID_LOPART(itemGUID));
            return LANG_ERR_TRANSMOG_MISSING_SRC_ITEM;
        }
    }

    // transmogrified item
    Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
    if (!itemTransmogrified)
    {
        TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUIDLow(), player->GetName().c_str(), slot);
        return LANG_ERR_TRANSMOG_MISSING_DEST_ITEM;
    }

    // uint16 tempDest;
    //// has to be able to equip item transmogrified item
    //if (!player->CanEquipItem(slot, tempDest, itemTransmogrified, true, true))
    //{
    //    TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) can't equip the item to be transmogrified (slot: %u, entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), slot, itemTransmogrified->GetEntry());
    //    return;
    //}
    //
    //// has to be able to equip item transmogrifier item
    //if (!player->CanEquipItem(slot, tempDest, itemTransmogrifier, true, true))
    //{
    //    TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) can't equip the transmogrifier item (slot: %u, entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), slot, itemTransmogrifier->GetEntry());
    //    return;
    //}

    if (!itemTransmogrifier) // reset look newEntry
    {
        // itemTransmogrified->ClearEnchantment(TRANSMOGRIFY_ENCHANTMENT_SLOT);
        // player->SetVisibleItemSlot(slot, itemTransmogrified);

        // Custom
        DeleteFakeEntry(player, slot, itemTransmogrified);
    }
    else
    {
        if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier->GetTemplate()))
        {
            TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetGUIDLow(), player->GetName().c_str(), itemTransmogrified->GetEntry(), itemTransmogrifier->GetEntry());
            return LANG_ERR_TRANSMOG_INVALID_ITEMS;
        }

        if (!no_cost)
        {
            cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
            cost *= GetScaledCostModifier();
            cost += GetCopperCost();
            if (!player->HasEnoughMoney(cost))
                return LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY;

            if (GetRequireToken())
            {
                if (player->HasItemCount(GetTokenEntry(), GetTokenAmount()))
                    player->DestroyItemCount(GetTokenEntry(), GetTokenAmount(), true);
                else
                    return LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS;
            }
        }

        // All okay, proceed
        // itemTransmogrified->SetEnchantment(TRANSMOGRIFY_ENCHANTMENT_SLOT, newEntry, 0, 0);
        // player->SetVisibleItemSlot(slot, itemTransmogrified);

        // 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);

        //cost += GetSpecialPrice(itemTransmogrified->GetTemplate());
        //cost *= GetScaledCostModifier();
        //cost += sT->GetCopperCost();
    }

    // trusting the client, if it got here it has to have enough money
    // ... unless client was modified
    if (cost) // 0 cost if reverting look
        player->ModifyMoney(-1*cost, false);
    return LANG_ERR_TRANSMOG_OK;
}
TransmogTrinityStrings Transmogrification::Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, bool no_cost)
{
    TC_LOG_DEBUG("custom.transmog", "Transmogrification::Transmogrify");

    // slot of the transmogrified item
    if (slot >= EQUIPMENT_SLOT_END)
    {
        TC_LOG_DEBUG("custom.transmog", "Transmogrification::Transmogrify - %s (%s) tried to transmogrify an %s with a wrong slot (%u) when transmogrifying items.", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemGUID.ToString().c_str(), slot);
        return LANG_ERR_TRANSMOG_INVALID_SLOT;
    }

    Item* itemTransmogrifier = NULL;
    // guid of the transmogrifier item, if it's not 0
    if (!itemGUID.IsEmpty())
    {
        itemTransmogrifier = player->GetItemByGuid(itemGUID);
        if (!itemTransmogrifier)
        {
            TC_LOG_DEBUG("custom.transmog", "Transmogrification::Transmogrify - %s (%s) tried to transmogrify with an invalid %s.", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemGUID.ToString().c_str());
            return LANG_ERR_TRANSMOG_MISSING_SRC_ITEM;
        }
    }

    // transmogrified item
    Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
    if (!itemTransmogrified)
    {
        TC_LOG_DEBUG("custom.transmog", "Transmogrification::Transmogrify - %s (%s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetName().c_str(), player->GetGUID().ToString().c_str(), slot);
        return LANG_ERR_TRANSMOG_MISSING_DEST_ITEM;
    }

    if (!itemTransmogrifier) // reset look newEntry
    {
        DeleteFakeEntry(player, itemTransmogrified);
    }
    else
    {
        if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier->GetTemplate()))
        {
            TC_LOG_DEBUG("custom.transmog", "Transmogrification::Transmogrify - %s (%s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetName().c_str(), player->GetGUID().ToString().c_str(), 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;
            }

            int32 cost = 0;
            cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
            cost *= ScaledCostModifier;
            cost += CopperCost;

            if (cost) // 0 cost if reverting look
            {
                if (cost < 0)
                    TC_LOG_DEBUG("custom.transmog", "Transmogrification::Transmogrify - %s (%s) transmogrification invalid cost (non negative, amount %i). Transmogrified %u with %u", player->GetName().c_str(), player->GetGUID().ToString().c_str(), -cost, itemTransmogrified->GetEntry(), itemTransmogrifier->GetEntry());
                else
                {
                    if (!player->HasEnoughMoney(cost))
                        return LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY;
                    player->ModifyMoney(-cost, false);
                }
            }
        }

        SetFakeEntry(player, itemTransmogrified, itemTransmogrifier->GetEntry());

        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;
}
void TransmogDisplayVendorMgr::HandleTransmogrify(Player* player, Creature* /*creature*/, uint32 vendorslot, uint32 itemEntry, bool no_cost)
{
    TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify");

    SelectionStore::Selection selection;
    if (!selectionStore.GetSelection(player->GetGUID().GetCounter(), selection))
        return; // cheat, no slot selected

    const char* slotname = TransmogDisplayVendorMgr::getSlotName(selection.slot, player->GetSession());
    if (!slotname)
        return;
    uint8 slot = selection.slot;

    // slot of the transmogrified item
    if (slot >= EQUIPMENT_SLOT_END)
    {
        TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) tried to transmogrify item %u with a wrong slot (%u) when transmogrifying items.", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemEntry, slot);
        return; // LANG_ERR_TRANSMOG_INVALID_SLOT
    }

    const ItemTemplate* itemTransmogrifier = NULL;
    // guid of the transmogrifier item, if it's not 0
    if (itemEntry)
    {
        itemTransmogrifier = sObjectMgr->GetItemTemplate(itemEntry);
        if (!itemTransmogrifier)
        {
            TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) tried to transmogrify with an invalid item entry %u.", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemEntry);
            return; // LANG_ERR_TRANSMOG_MISSING_SRC_ITEM
        }
    }

    // transmogrified item
    Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
    if (!itemTransmogrified)
    {
        TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetName().c_str(), player->GetGUID().ToString().c_str(), slot);
        player->GetSession()->SendNotification("No item in %s slot", slotname);
        return; // LANG_ERR_TRANSMOG_MISSING_DEST_ITEM
    }

    if (!itemTransmogrifier) // reset look newEntry
    {
        DeleteFakeEntry(player, itemTransmogrified);
    }
    else
    {
        if (!CanTransmogrifyItemWithItem(player, itemTransmogrified->GetTemplate(), itemTransmogrifier))
        {
            TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetName().c_str(), player->GetGUID().ToString().c_str(), itemTransmogrified->GetEntry(), itemTransmogrifier->ItemId);
            player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
            return; // LANG_ERR_TRANSMOG_INVALID_ITEMS
        }

        if (uint32 fakeEntry = GetFakeEntry(itemTransmogrified))
        {
            if (const ItemTemplate* fakeItemTemplate = sObjectMgr->GetItemTemplate(fakeEntry))
            {
                if (fakeItemTemplate->DisplayInfoID == itemTransmogrifier->DisplayInfoID)
                {
                    player->GetSession()->SendNotification("%s already transmogrified with %s", slotname, getItemName(itemTransmogrifier, player->GetSession()).c_str());
                    return;
                }
            }
        }

        // {{entry}, {entry}, ...}
        std::list<uint32> L;
        uint32 counter = 0;
        bool over = false;
        if (itemTransmogrified->GetTemplate()->Class != ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedArmorTypes)
        {
            for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_ARMOR; ++i)
            {
                const EntryVector* oM = optionMap[MAX_ITEM_SUBCLASS_WEAPON + i][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
                if (!oM)
                    continue;
                if (!over && counter + oM->size() < selection.offset)
                {
                    counter += oM->size();
                }
                else
                {
                    over = true;
                    L.insert(L.end(), oM->begin(), oM->end());
                }
            }
        }
        else if (itemTransmogrified->GetTemplate()->Class == ITEM_CLASS_WEAPON && TransmogDisplayVendorMgr::AllowMixedWeaponTypes)
        {
            for (uint32 i = 0; i < MAX_ITEM_SUBCLASS_WEAPON; ++i)
            {
                const EntryVector* oM = optionMap[i][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
                if (!oM)
                    continue;
                if (!over && counter + oM->size() < selection.offset)
                {
                    counter += oM->size();
                }
                else
                {
                    over = true;
                    L.insert(L.end(), oM->begin(), oM->end());
                }
            }
        }
        else
        {
            const EntryVector* oM = optionMap[(itemTransmogrified->GetTemplate()->Class != ITEM_CLASS_WEAPON ? MAX_ITEM_SUBCLASS_WEAPON : 0) + itemTransmogrified->GetTemplate()->SubClass][getCorrectInvType(itemTransmogrified->GetTemplate()->InventoryType)][selection.quality];
            if (oM)
            {
                if (!over && counter + oM->size() < selection.offset)
                {
                    counter += oM->size();
                }
                else
                {
                    over = true;
                    L.insert(L.end(), oM->begin(), oM->end());
                }
            }
        }
        std::list<uint32>::const_iterator it = L.begin();
        std::advance(it, (selection.offset - counter) + vendorslot);
        if (it == L.end() || (*it) != itemEntry)
        {
            player->GetSession()->SendNotification("Equipped item is not suitable for selected transmogrification");
            return; // either cheat or changed items (not found in correct place in transmog vendor view)
        }

        if (!no_cost)
        {
            if (RequireToken)
            {
                if (player->HasItemCount(TokenEntry, TokenAmount))
                {
                    player->DestroyItemCount(TokenEntry, TokenAmount, true);
                }
                else
                {
                    player->GetSession()->SendNotification("You do not have enough %ss", getItemName(sObjectMgr->GetItemTemplate(TransmogDisplayVendorMgr::TokenEntry), player->GetSession()).c_str());
                    return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS
                }
            }

            int32 cost = 0;
            cost = GetSpecialPrice(itemTransmogrified->GetTemplate());
            cost *= ScaledCostModifier;
            cost += CopperCost;

            if (cost) // 0 cost if reverting look
            {
                if (cost < 0)
                {
                    TC_LOG_DEBUG("custom.transmog", "TransmogDisplayVendorMgr::HandleTransmogrify - %s (%s) transmogrification invalid cost (non negative, amount %i). Transmogrified %u with %u", player->GetName().c_str(), player->GetGUID().ToString().c_str(), -cost, itemTransmogrified->GetEntry(), itemTransmogrifier->ItemId);
                }
                else
                {
                    if (!player->HasEnoughMoney(cost))
                    {
                        player->GetSession()->SendNotification("You do not have enough money");
                        return; // LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY
                    }
                    player->ModifyMoney(-cost, false);
                }
            }

            SetFakeEntry(player, itemTransmogrified, itemTransmogrifier->ItemId);

            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);
        }

        player->PlayDirectSound(3337);
        player->GetSession()->SendAreaTriggerMessage("%s transmogrified", slotname);
        //return LANG_ERR_TRANSMOG_OK;
    }
}
Beispiel #5
0
void WorldSession::HandleSocketGems(WorldPackets::Item::SocketGems& socketGems)
{
    if (!socketGems.ItemGuid)
        return;

    //cheat -> tried to socket same gem multiple times
    if ((!socketGems.GemItem[0].IsEmpty() && (socketGems.GemItem[0] == socketGems.GemItem[1] || socketGems.GemItem[0] == socketGems.GemItem[2])) ||
        (!socketGems.GemItem[1].IsEmpty() && (socketGems.GemItem[1] == socketGems.GemItem[2])))
        return;

    Item* itemTarget = _player->GetItemByGuid(socketGems.ItemGuid);
    if (!itemTarget)                                         //missing item to socket
        return;

    ItemTemplate const* itemProto = itemTarget->GetTemplate();
    if (!itemProto)
        return;

    //this slot is excepted when applying / removing meta gem bonus
    uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT);

    Item* gems[MAX_GEM_SOCKETS];
    memset(gems, 0, sizeof(gems));
    ItemDynamicFieldGems gemData[MAX_GEM_SOCKETS];
    memset(gemData, 0, sizeof(gemData));
    GemPropertiesEntry const* gemProperties[MAX_GEM_SOCKETS];
    memset(gemProperties, 0, sizeof(gemProperties));
    ItemDynamicFieldGems const* oldGemData[MAX_GEM_SOCKETS];
    memset(oldGemData, 0, sizeof(oldGemData));
    for (uint32 i = 0; i < MAX_GEM_SOCKETS; ++i)
    {
        if (Item* gem = _player->GetItemByGuid(socketGems.GemItem[i]))
        {
            gems[i] = gem;
            gemData[i].ItemId = gem->GetEntry();
            gemData[i].Context = gem->GetUInt32Value(ITEM_FIELD_CONTEXT);
            for (std::size_t b = 0; b < gem->GetDynamicValues(ITEM_DYNAMIC_FIELD_BONUSLIST_IDS).size() && b < 16; ++b)
                gemData[i].BonusListIDs[b] = gem->GetDynamicValue(ITEM_DYNAMIC_FIELD_BONUSLIST_IDS, b);

            gemProperties[i] = sGemPropertiesStore.LookupEntry(gem->GetTemplate()->GetGemProperties());
        }

        oldGemData[i] = itemTarget->GetGem(i);
    }

    // Find first prismatic socket
    uint32 firstPrismatic = 0;
    while (firstPrismatic < MAX_GEM_SOCKETS && itemTarget->GetSocketColor(firstPrismatic))
        ++firstPrismatic;

    for (uint32 i = 0; i < MAX_GEM_SOCKETS; ++i)                //check for hack maybe
    {
        if (!gemProperties[i])
            continue;

        // tried to put gem in socket where no socket exists (take care about prismatic sockets)
        if (!itemTarget->GetSocketColor(i))
        {
            // no prismatic socket
            if (!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
                return;

            if (i != firstPrismatic)
                return;
        }

        // Gem must match socket color
        if (SocketColorToGemTypeMask[itemTarget->GetSocketColor(i)] != gemProperties[i]->Type)
        {
            // unless its red, blue, yellow or prismatic
            if (!(SocketColorToGemTypeMask[itemTarget->GetSocketColor(i)] & SOCKET_COLOR_PRISMATIC) || !(gemProperties[i]->Type & SOCKET_COLOR_PRISMATIC))
                return;
        }
    }

    // check unique-equipped conditions
    for (uint32 i = 0; i < MAX_GEM_SOCKETS; ++i)
    {
        if (!gems[i])
            continue;

        // continue check for case when attempt add 2 similar unique equipped gems in one item.
        ItemTemplate const* iGemProto = gems[i]->GetTemplate();

        // unique item (for new and already placed bit removed enchantments
        if (iGemProto->GetFlags() & ITEM_FLAG_UNIQUE_EQUIPPABLE)
        {
            for (uint32 j = 0; j < MAX_GEM_SOCKETS; ++j)
            {
                if (i == j)                                    // skip self
                    continue;

                if (gems[j])
                {
                    if (iGemProto->GetId() == gems[j]->GetEntry())
                    {
                        _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
                        return;
                    }
                }
                else if (oldGemData[j])
                {
                    if (iGemProto->GetId() == oldGemData[j]->ItemId)
                    {
                        _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
                        return;
                    }
                }
            }
        }

        // unique limit type item
        int32 limit_newcount = 0;
        if (iGemProto->GetItemLimitCategory())
        {
            if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->GetItemLimitCategory()))
            {
                // NOTE: limitEntry->mode is not checked because if item has limit then it is applied in equip case
                for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
                {
                    if (gems[j])
                    {
                        // new gem
                        if (iGemProto->GetItemLimitCategory() == gems[j]->GetTemplate()->GetItemLimitCategory())
                            ++limit_newcount;
                    }
                    else if (oldGemData[j])
                    {
                        // existing gem
                        if (ItemTemplate const* jProto = sObjectMgr->GetItemTemplate(oldGemData[j]->ItemId))
                            if (iGemProto->GetItemLimitCategory() == jProto->GetItemLimitCategory())
                                ++limit_newcount;
                    }
                }

                if (limit_newcount > 0 && uint32(limit_newcount) > limitEntry->Quantity)
                {
                    _player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
                    return;
                }
            }
        }

        // for equipped item check all equipment for duplicate equipped gems
        if (itemTarget->IsEquipped())
        {
            if (InventoryResult res = _player->CanEquipUniqueItem(gems[i], slot, std::max(limit_newcount, 0)))
            {
                _player->SendEquipError(res, itemTarget, NULL);
                return;
            }
        }
    }

    bool SocketBonusActivated = itemTarget->GemsFitSockets();    //save state of socketbonus
    _player->ToggleMetaGemsActive(slot, false);             //turn off all metagems (except for the target item)

    //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met

    //remove ALL mods - gem can change item level
    if (itemTarget->IsEquipped())
        _player->_ApplyItemMods(itemTarget, itemTarget->GetSlot(), false);

    for (uint16 i = 0; i < MAX_GEM_SOCKETS; ++i)
    {
        if (gems[i])
        {
            uint32 gemScalingLevel = _player->getLevel();
            if (uint32 fixedLevel = gems[i]->GetModifier(ITEM_MODIFIER_SCALING_STAT_DISTRIBUTION_FIXED_LEVEL))
                gemScalingLevel = fixedLevel;

            itemTarget->SetGem(i, &gemData[i], gemScalingLevel);

            if (gemProperties[i] && gemProperties[i]->EnchantID)
                itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + i), gemProperties[i]->EnchantID, 0, 0, _player->GetGUID());

            uint32 gemCount = 1;
            _player->DestroyItemCount(gems[i], gemCount, true);
        }
    }

    if (itemTarget->IsEquipped())
        _player->_ApplyItemMods(itemTarget, itemTarget->GetSlot(), true);

    if (Item* childItem = _player->GetChildItemByGuid(itemTarget->GetChildItem()))
    {
        if (childItem->IsEquipped())
            _player->_ApplyItemMods(childItem, childItem->GetSlot(), false);
        childItem->CopyArtifactDataFromParent(itemTarget);
        if (childItem->IsEquipped())
            _player->_ApplyItemMods(childItem, childItem->GetSlot(), true);
    }

    bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
    if (SocketBonusActivated ^ SocketBonusToBeActivated)     //if there was a change...
    {
        _player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, false);
        itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetTemplate()->GetSocketBonus() : 0), 0, 0, _player->GetGUID());
        _player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, true);
        //it is not displayed, client has an inbuilt system to determine if the bonus is activated
    }

    _player->ToggleMetaGemsActive(slot, true);              //turn on all metagems (except for target item)

    _player->RemoveTradeableItem(itemTarget);
    itemTarget->ClearSoulboundTradeable(_player);           // clear tradeable flag

    itemTarget->SendUpdateSockets();
}
void WorldSession::HandleTransmogrifyItems(WorldPackets::Item::TransmogrifyItems& transmogrifyItems)
{
    Player* player = GetPlayer();
    // Validate
    if (!player->GetNPCIfCanInteractWith(transmogrifyItems.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER))
    {
        TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s not found or player can't interact with it.", transmogrifyItems.Npc.ToString().c_str());
        return;
    }

    int64 cost = 0;
    std::unordered_map<Item*, Item*> transmogItems;
    std::unordered_map<Item*, std::pair<VoidStorageItem*, BonusData>> transmogVoidItems;
    std::vector<Item*> resetAppearanceItems;

    for (WorldPackets::Item::TransmogrifyItem const& transmogItem : transmogrifyItems.Items)
    {
        // slot of the transmogrified item
        if (transmogItem.Slot >= EQUIPMENT_SLOT_END)
        {
            TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify wrong slot (%u) when transmogrifying items.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
            return;
        }

        // transmogrified item
        Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot);
        if (!itemTransmogrified)
        {
            TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
            return;
        }

        WorldPackets::Item::ItemInstance itemInstance;
        BonusData const* bonus = nullptr;
        if (transmogItem.SrcItemGUID)
        {
            // guid of the transmogrifier item
            Item* itemTransmogrifier = player->GetItemByGuid(*transmogItem.SrcItemGUID);
            if (!itemTransmogrifier)
            {
                TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str());
                return;
            }

            itemInstance.Initialize(itemTransmogrifier);
            bonus = itemTransmogrifier->GetBonus();
            transmogItems[itemTransmogrified] = itemTransmogrifier;
        }
        else if (transmogItem.SrcVoidItemGUID)
        {
            // guid of the transmogrifier item
            uint8 slot;
            VoidStorageItem* itemTransmogrifier = player->GetVoidStorageItem(transmogItem.SrcVoidItemGUID->GetCounter(), slot);
            if (!itemTransmogrifier)
            {
                TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid void storage item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcVoidItemGUID->ToString().c_str());
                return;
            }

            itemInstance.Initialize(itemTransmogrifier);
            std::pair<VoidStorageItem*, BonusData>& transmogData = transmogVoidItems[itemTransmogrified];
            transmogData.first = itemTransmogrifier;
            transmogData.second.Initialize(itemInstance);
            bonus = &transmogData.second;
        }
        else
        {
            resetAppearanceItems.push_back(itemTransmogrified);
            continue;
        }

        // entry of transmogrifier and from packet
        if (itemInstance != transmogItem.Item)
        {
            TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid item instance data for %s.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str());
            return;
        }

        // validity of the transmogrification items
        if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, transmogItem.Item, bonus))
        {
            TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), itemTransmogrified->GetEntry(), transmogItem.Item.ItemID);
            return;
        }

        // add cost
        cost += itemTransmogrified->GetSpecialPrice();
    }

    if (cost) // 0 cost if reverting look
    {
        if (!player->HasEnoughMoney(cost))
            return;
        player->ModifyMoney(-cost);
    }

    // Everything is fine, proceed
    for (auto& transmogPair : transmogItems)
    {
        Item* transmogrified = transmogPair.first;
        Item* transmogrifier = transmogPair.second;

        transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogrifier->GetEntry());
        transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, transmogrifier->GetAppearanceModId());
        player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);

        transmogrified->SetNotRefundable(player);
        transmogrified->ClearSoulboundTradeable(player);

        transmogrifier->SetNotRefundable(player);
        transmogrifier->ClearSoulboundTradeable(player);

        if (transmogrifier->GetTemplate()->GetBonding() == BIND_WHEN_EQUIPED || transmogrifier->GetTemplate()->GetBonding() == BIND_WHEN_USE)
            transmogrifier->SetBinding(true);
    }

    for (auto& transmogVoirPair : transmogVoidItems)
    {
        Item* transmogrified = transmogVoirPair.first;
        VoidStorageItem* transmogrifier = transmogVoirPair.second.first;
        BonusData& bonus = transmogVoirPair.second.second;

        transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogrifier->ItemEntry);
        transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, bonus.AppearanceModID);
        player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);

        transmogrified->SetNotRefundable(player);
        transmogrified->ClearSoulboundTradeable(player);
    }

    for (Item* item : resetAppearanceItems)
    {
        item->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, 0);
        item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, 0);
        player->SetVisibleItemSlot(item->GetSlot(), item);
    }
}