/* Buying merchants: 1.7XX only */ void CUser::BuyingMerchantOpen(Packet & pkt) { int16 errorCode = 0; if (isDead()) errorCode = MERCHANT_OPEN_DEAD; else if (isStoreOpen()) errorCode = MERCHANT_OPEN_SHOPPING; else if (isTrading()) errorCode = MERCHANT_OPEN_TRADING; else if (GetZoneID() > 21 || GetZoneID() <= ELMORAD) errorCode = MERCHANT_OPEN_INVALID_ZONE; else if (GetLevel() < 30) errorCode = MERCHANT_OPEN_UNDERLEVELED; else if (isMerchanting()) errorCode = MERCHANT_OPEN_MERCHANTING; else errorCode = MERCHANT_OPEN_SUCCESS; Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_OPEN)); result << errorCode; Send(&result); if (errorCode == MERCHANT_OPEN_MERCHANTING) BuyingMerchantClose(); memset(&m_arMerchantItems, 0, sizeof(m_arMerchantItems)); }
/* Regular merchants */ void CUser::MerchantOpen() { int16 errorCode = 0; if (isDead()) errorCode = MERCHANT_OPEN_DEAD; else if (isStoreOpen()) errorCode = MERCHANT_OPEN_SHOPPING; else if (isTrading()) errorCode = MERCHANT_OPEN_TRADING; else if (GetZoneID() > 21 || GetZoneID() <= ELMORAD) errorCode = MERCHANT_OPEN_INVALID_ZONE; else if (GetLevel() < 30) errorCode = MERCHANT_OPEN_UNDERLEVELED; else if (isMerchanting()) errorCode = MERCHANT_OPEN_MERCHANTING; else errorCode = MERCHANT_OPEN_SUCCESS; Packet result(WIZ_MERCHANT, uint8(MERCHANT_OPEN)); result << errorCode; Send(&result); // If we're already merchanting, user may be desynced // so we need to close our current merchant first. if (errorCode == MERCHANT_OPEN_MERCHANTING) MerchantClose(); }
void CUser::MerchantClose() { if (!isMerchanting()) return; m_bMerchantState = MERCHANT_STATE_NONE; GiveMerchantItems(); // Give back to the user that which hasn't been sold, if any. Packet result(WIZ_MERCHANT, uint8(MERCHANT_CLOSE)); result << GetSocketID(); SendToRegion(&result); }
void CUser::BuyingMerchantClose() { if (isMerchanting()) m_bMerchantState = MERCHANT_STATE_NONE; else if (m_sMerchantsSocketID >= 0) RemoveFromMerchantLookers(); else return; Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_CLOSE)); result << GetSocketID(); SendToRegion(&result); }
void CUser::Chat(Packet & pkt) { Packet result; uint16 sessID; uint8 type = pkt.read<uint8>(), bNation; string chatstr, finalstr, strSender, * strMessage; bool isAnnouncement = false; if (isMuted()) return; pkt >> chatstr; if (chatstr.empty() || chatstr.size() > 128) return; // Process GM commands if (isGM() && ProcessChatCommand(chatstr)) return; #if 0 // Removed this - all it seems to do is cause chat to break for GMs (is it 19xx+ only?) if( isGM() && type == GENERAL_CHAT) type = 0x14; #endif // Handle GM notice & announcement commands if (type == PUBLIC_CHAT || type == ANNOUNCEMENT_CHAT) { // Trying to use a GM command without authorisation? Bad player! if (!isGM()) return; if (type == ANNOUNCEMENT_CHAT) type = WAR_SYSTEM_CHAT; // This is horrible, but we'll live with it for now. // Pull the notice string (#### NOTICE : %s ####) from the database. // Format the chat string around it, so our chat data is within the notice g_pMain->GetServerResource(IDP_ANNOUNCEMENT, &finalstr, chatstr.c_str()); isAnnouncement = true; } if (isAnnouncement) { // GM notice/announcements show no name, so don't bother setting it. strMessage = &finalstr; // use the formatted message from the user bNation = KARUS; // arbitrary nation sessID = -1; } else { strMessage = &chatstr; // use the raw message from the user strSender = GetName(); // everything else uses a name, so set it bNation = GetNation(); sessID = GetSocketID(); } ChatPacket::Construct(&result, type, strMessage, &strSender, bNation, sessID); switch (type) { case GENERAL_CHAT: g_pMain->Send_NearRegion(&result, GetMap(), GetRegionX(), GetRegionZ(), GetX(), GetZ()); break; case PRIVATE_CHAT: { CUser *pUser = g_pMain->GetUserPtr(m_sPrivateChatUser); if (pUser != nullptr) pUser->Send(&result); } break; case PARTY_CHAT: if (isInParty()) g_pMain->Send_PartyMember(m_sPartyIndex, &result); break; case SHOUT_CHAT: if (m_sMp < (m_iMaxMp / 5)) break; // Characters under level 35 require 3,000 coins to shout. if (!isGM() && GetLevel() < 35 && !GoldLose(SHOUT_COIN_REQUIREMENT)) break; MSpChange(-(m_iMaxMp / 5)); SendToRegion(&result); break; case KNIGHTS_CHAT: if (isInClan()) g_pMain->Send_KnightsMember(GetClanID(), &result); break; case PUBLIC_CHAT: case ANNOUNCEMENT_CHAT: if (isGM()) g_pMain->Send_All(&result); break; case COMMAND_CHAT: if (GetFame() == COMMAND_CAPTAIN) g_pMain->Send_CommandChat(&result, m_bNation, this); break; case MERCHANT_CHAT: if (isMerchanting()) SendToRegion(&result); break; case ALLIANCE_CHAT: if (isInClan()) { CKnights *pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights != nullptr && pKnights->isInAlliance()) g_pMain->Send_KnightsAlliance(pKnights->GetAllianceID(), &result); } break; case WAR_SYSTEM_CHAT: if (isGM()) g_pMain->Send_All(&result); break; } }
void CUser::ItemMove(Packet & pkt) { _ITEM_TABLE *pTable; _ITEM_DATA *pSrcItem, *pDstItem, tmpItem; uint32 nItemID; uint8 dir, bSrcPos, bDstPos; pkt >> dir >> nItemID >> bSrcPos >> bDstPos; if (isTrading() || isMerchanting()) goto fail_return; pTable = g_pMain->GetItemPtr(nItemID); if (pTable == nullptr // || dir == ITEM_INVEN_SLOT && ((pTable->m_sWeight + m_sItemWeight) > m_sMaxWeight)) // || dir > ITEM_MBAG_TO_MBAG || bSrcPos >= SLOT_MAX+HAVE_MAX+COSP_MAX+MBAG_MAX || bDstPos >= SLOT_MAX+HAVE_MAX+COSP_MAX+MBAG_MAX || ((dir == ITEM_INVEN_SLOT || dir == ITEM_SLOT_SLOT) && (bDstPos > SLOT_MAX || !ItemEquipAvailable(pTable))) || (dir == ITEM_SLOT_INVEN && bSrcPos > SLOT_MAX) || (dir == ITEM_INVEN_SLOT && bDstPos == RESERVED) || (dir == ITEM_SLOT_INVEN && bDstPos == RESERVED)) goto fail_return; switch (dir) { case ITEM_MBAG_TO_MBAG: if (bDstPos >= MBAG_TOTAL || bSrcPos >= MBAG_TOTAL // We also need to make sure that if we're setting an item in a magic bag, we need to actually // have a magic back to put the item in! || (INVENTORY_MBAG+bDstPos < INVENTORY_MBAG2 && m_sItemArray[BAG1].nNum == 0) || (INVENTORY_MBAG+bDstPos >= INVENTORY_MBAG2 && m_sItemArray[BAG2].nNum == 0) // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_MBAG + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_MBAG + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_MBAG + bDstPos]; break; case ITEM_MBAG_TO_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= MBAG_TOTAL // We also need to make sure that if we're taking an item from a magic bag, we need to actually // have a magic back to take it from! || (INVENTORY_MBAG+bSrcPos < INVENTORY_MBAG2 && m_sItemArray[BAG1].nNum == 0) || (INVENTORY_MBAG+bSrcPos >= INVENTORY_MBAG2 && m_sItemArray[BAG2].nNum == 0) // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_MBAG + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_MBAG + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_INVEN_TO_MBAG: if (bDstPos >= MBAG_TOTAL || bSrcPos >= HAVE_MAX // We also need to make sure that if we're adding an item to a magic bag, we need to actually // have a magic back to put the item in! || (INVENTORY_MBAG + bDstPos < INVENTORY_MBAG2 && m_sItemArray[BAG1].nNum == 0) || (INVENTORY_MBAG + bDstPos >= INVENTORY_MBAG2 && m_sItemArray[BAG2].nNum == 0) // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_MBAG + bDstPos]; break; case ITEM_COSP_TO_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= COSP_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_COSP + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_COSP + bSrcPos]; pDstItem = &m_sItemArray[SLOT_MAX + bDstPos]; break; case ITEM_INVEN_TO_COSP: if (bDstPos >= COSP_MAX+MBAG_COUNT || bSrcPos >= HAVE_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[SLOT_MAX + bSrcPos].nNum || !IsValidSlotPos(pTable, bDstPos)) goto fail_return; pSrcItem = &m_sItemArray[SLOT_MAX + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_COSP + bDstPos]; // If we're setting a magic bag... if (bDstPos == COSP_BAG1 || bDstPos == COSP_BAG2) { // Can't replace existing magic bag. if (pDstItem->nNum != 0 // Can't set any old item in the bag slot, it must be a bag. || pTable->m_bSlot != ItemSlotBag) goto fail_return; } break; case ITEM_INVEN_SLOT: if (bDstPos >= SLOT_MAX || bSrcPos >= HAVE_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum // Ensure the item is able to be equipped in that slot || !IsValidSlotPos(pTable, bDstPos)) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[bDstPos]; break; case ITEM_SLOT_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= SLOT_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_INVEN_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= HAVE_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_SLOT_SLOT: if (bDstPos >= SLOT_MAX || bSrcPos >= SLOT_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[bSrcPos].nNum // Ensure the item is able to be equipped in that slot || !IsValidSlotPos(pTable, bDstPos)) goto fail_return; pSrcItem = &m_sItemArray[bSrcPos]; pDstItem = &m_sItemArray[bDstPos]; break; default: return; } // If there's an item already in the target slot already, we need to just swap the items if (pDstItem->nNum != 0) { memcpy(&tmpItem, pDstItem, sizeof(_ITEM_DATA)); // Temporarily store the target item memcpy(pDstItem, pSrcItem, sizeof(_ITEM_DATA)); // Replace the target item with the source memcpy(pSrcItem, &tmpItem, sizeof(_ITEM_DATA)); // Now replace the source with the old target (swapping them) } // Since there's no way to move a partial stack using this handler, just overwrite the destination. else { memcpy(pDstItem, pSrcItem, sizeof(_ITEM_DATA)); // Shift the item over memset(pSrcItem, 0, sizeof(_ITEM_DATA)); // Clear out the source item's data } // If we're equipping a 2-handed weapon, we need to ensure that if there's // an item in the other weapon slot (2-handed or not), it's unequipped. if (dir == ITEM_INVEN_SLOT) { uint8 bOtherWeaponSlot = 0; if (bDstPos == LEFTHAND && pTable->m_bSlot == ItemSlot2HLeftHand) bOtherWeaponSlot = RIGHTHAND; else if (bDstPos == RIGHTHAND && pTable->is2Handed()) bOtherWeaponSlot = LEFTHAND; if (bOtherWeaponSlot != 0) { _ITEM_DATA * pUnequipItem = GetItem(bOtherWeaponSlot); if (pUnequipItem->nNum != 0) { // Place item where item that we just equipped was. memcpy(pSrcItem, pUnequipItem, sizeof(_ITEM_DATA)); memset(pUnequipItem, 0, sizeof(_ITEM_DATA)); UserLookChange(bOtherWeaponSlot, 0, 0); } } } // If equipping/de-equipping an item if (dir == ITEM_INVEN_SLOT || dir == ITEM_SLOT_INVEN // or moving an item to/from our cospre item slots || dir == ITEM_INVEN_TO_COSP || dir == ITEM_COSP_TO_INVEN || dir == ITEM_SLOT_SLOT) { // Re-update item stats SetUserAbility(false); } SendItemMove(1); SendItemWeight(); // Update everyone else, so that they can see your shiny new items (you didn't take them off did you!? DID YOU!?) switch (dir) { case ITEM_INVEN_SLOT: case ITEM_INVEN_TO_COSP: UserLookChange(bDstPos, nItemID, pDstItem->sDuration); break; case ITEM_SLOT_INVEN: case ITEM_COSP_TO_INVEN: UserLookChange(bSrcPos, 0, 0); break; case ITEM_SLOT_SLOT: UserLookChange(bSrcPos, pSrcItem->nNum, pSrcItem->sDuration); UserLookChange(bDstPos, pDstItem->nNum, pDstItem->sDuration); break; } Send2AI_UserUpdateInfo(); return; fail_return: SendItemMove(0); }
void CUser::Chat(Packet & pkt) { Packet result(WIZ_CHAT); uint8 type = pkt.read<uint8>(); char finalstr[1024] = ""; std::string buff, chatstr; bool isAnnouncement = false; if (isMuted()) return; pkt >> chatstr; if (chatstr.empty() || chatstr.size() > 128) return; // Process GM commands if (isGM() && ProcessChatCommand(chatstr)) return; #if 0 // Removed this - all it seems to do is cause chat to break for GMs (is it 19xx+ only?) if( isGM() && type == GENERAL_CHAT) type = 0x14; #endif uint8 bNation = GetNation(); uint16 sessID = GetSocketID(); // Handle GM notice & announcement commands if (type == PUBLIC_CHAT || type == ANNOUNCEMENT_CHAT) { // Trying to use a GM command without authorisation? Bad player! if (!isGM()) return; if (type == ANNOUNCEMENT_CHAT) type = WAR_SYSTEM_CHAT; // This is horrible, but we'll live with it for now. // Pull the notice string (#### NOTICE : %s ####) from the database. CString noticeText = g_pMain->GetServerResource(IDP_ANNOUNCEMENT); // Format the chat string around it, so our chat data is within the notice sprintf_s(finalstr, sizeof(finalstr), noticeText, chatstr.c_str()); bNation = KARUS; // arbitrary nation sessID = -1; isAnnouncement = true; } result.SByte(); result << type << bNation << sessID; if (isAnnouncement) { result << uint8(0); // GM notice/announcements show no name (so specify length of 0) result.DByte(); result << finalstr; // now tack on the formatted message from the user } else { result << m_pUserData.m_id; // everything else provides a name result.DByte(); result << chatstr; // now tack on the chat message from the user } switch (type) { case GENERAL_CHAT: g_pMain->Send_NearRegion(&result, GetMap(), GetRegionX(), GetRegionZ(), GetX(), GetZ()); break; case PRIVATE_CHAT: { if (m_sPrivateChatUser == GetSocketID()) break; CUser *pUser = g_pMain->GetUserPtr(m_sPrivateChatUser); if (pUser != NULL) pUser->Send(&result); } break; case PARTY_CHAT: if (isInParty()) g_pMain->Send_PartyMember(m_sPartyIndex, &result); break; case SHOUT_CHAT: if (m_pUserData.m_sMp < (m_iMaxMp / 5)) break; // Characters under level 35 require 3,000 coins to shout. if (!isGM() && GetLevel() < 35 && !GoldLose(SHOUT_COIN_REQUIREMENT)) break; MSpChange(-(m_iMaxMp / 5)); SendToRegion(&result); break; case KNIGHTS_CHAT: if (isInClan()) g_pMain->Send_KnightsMember(GetClanID(), &result); break; case PUBLIC_CHAT: case ANNOUNCEMENT_CHAT: if (isGM()) g_pMain->Send_All(&result); break; case COMMAND_CHAT: if (getFame() == COMMAND_CAPTAIN) g_pMain->Send_CommandChat(&result, m_pUserData.m_bNation, this); break; case MERCHANT_CHAT: if (isMerchanting()) SendToRegion(&result); break; case WAR_SYSTEM_CHAT: if (isGM()) g_pMain->Send_All(&result); break; } }
void CUser::ItemMove(Packet & pkt) { _ITEM_DATA *pSrcItem, *pDstItem, tmpItem; uint32 nItemID; uint8 dir, bSrcPos, bDstPos; pkt >> dir >> nItemID >> bSrcPos >> bDstPos; if (isTrading() || isMerchanting()) goto fail_return; _ITEM_TABLE *pTable = g_pMain->GetItemPtr(nItemID); if (pTable == NULL // || dir == ITEM_INVEN_SLOT && ((pTable->m_sWeight + m_sItemWeight) > m_sMaxWeight)) // || dir > ITEM_MBAG_TO_MBAG || bSrcPos >= SLOT_MAX+HAVE_MAX+COSP_MAX+MBAG_MAX || bDstPos >= SLOT_MAX+HAVE_MAX+COSP_MAX+MBAG_MAX || ((dir == ITEM_INVEN_SLOT || dir == ITEM_SLOT_SLOT) && (bDstPos > SLOT_MAX || !ItemEquipAvailable(pTable))) || (dir == ITEM_SLOT_INVEN && bSrcPos > SLOT_MAX) || (dir == ITEM_INVEN_SLOT && bDstPos == RESERVED) || (dir == ITEM_SLOT_INVEN && bDstPos == RESERVED)) goto fail_return; switch (dir) { case ITEM_MBAG_TO_MBAG: if (bDstPos >= MBAG_TOTAL || bSrcPos >= MBAG_TOTAL // We also need to make sure that if we're setting an item in a magic bag, we need to actually // have a magic back to put the item in! || (INVENTORY_MBAG+bDstPos < INVENTORY_MBAG2 && m_sItemArray[BAG1].nNum == 0) || (INVENTORY_MBAG+bDstPos >= INVENTORY_MBAG2 && m_sItemArray[BAG2].nNum == 0) // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_MBAG + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_MBAG + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_MBAG + bDstPos]; break; case ITEM_MBAG_TO_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= MBAG_TOTAL // We also need to make sure that if we're taking an item from a magic bag, we need to actually // have a magic back to take it from! || (INVENTORY_MBAG+bSrcPos < INVENTORY_MBAG2 && m_sItemArray[BAG1].nNum == 0) || (INVENTORY_MBAG+bSrcPos >= INVENTORY_MBAG2 && m_sItemArray[BAG2].nNum == 0) // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_MBAG + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_MBAG + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_INVEN_TO_MBAG: if (bDstPos >= MBAG_TOTAL || bSrcPos >= HAVE_MAX // We also need to make sure that if we're adding an item to a magic bag, we need to actually // have a magic back to put the item in! || (INVENTORY_MBAG + bDstPos < INVENTORY_MBAG2 && m_sItemArray[BAG1].nNum == 0) || (INVENTORY_MBAG + bDstPos >= INVENTORY_MBAG2 && m_sItemArray[BAG2].nNum == 0) // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_MBAG + bDstPos]; break; case ITEM_COSP_TO_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= COSP_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_COSP + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_COSP + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_INVEN_TO_COSP: // TO-DO: Update IsValidSlotPos() for cospre items? if (bDstPos >= COSP_MAX || bSrcPos >= HAVE_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_COSP + bDstPos]; break; case ITEM_INVEN_SLOT: if (bDstPos >= SLOT_MAX || bSrcPos >= HAVE_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum // Ensure the item is able to be equipped in that slot || !IsValidSlotPos(pTable, bDstPos)) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[bDstPos]; break; case ITEM_SLOT_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= SLOT_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_INVEN_INVEN: if (bDstPos >= HAVE_MAX || bSrcPos >= HAVE_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[INVENTORY_INVENT + bSrcPos].nNum) goto fail_return; pSrcItem = &m_sItemArray[INVENTORY_INVENT + bSrcPos]; pDstItem = &m_sItemArray[INVENTORY_INVENT + bDstPos]; break; case ITEM_SLOT_SLOT: if (bDstPos >= SLOT_MAX || bSrcPos >= SLOT_MAX // Make sure that the item actually exists there. || nItemID != m_sItemArray[bSrcPos].nNum // Ensure the item is able to be equipped in that slot || !IsValidSlotPos(pTable, bDstPos)) goto fail_return; pSrcItem = &m_sItemArray[bSrcPos]; pDstItem = &m_sItemArray[bDstPos]; break; default: return; } // If there's an item already in the target slot already, we need to just swap the items if (pDstItem->nNum != 0) { memcpy(&tmpItem, pDstItem, sizeof(_ITEM_DATA)); // Temporarily store the target item memcpy(pDstItem, pSrcItem, sizeof(_ITEM_DATA)); // Replace the target item with the source memcpy(pSrcItem, &tmpItem, sizeof(_ITEM_DATA)); // Now replace the source with the old target (swapping them) } // Since there's no way to move a partial stack using this handler, just overwrite the destination. else { memcpy(pDstItem, pSrcItem, sizeof(_ITEM_DATA)); // Shift the item over memset(pSrcItem, 0, sizeof(_ITEM_DATA)); // Clear out the source item's data } // If equipping/de-equipping an item if (dir == ITEM_INVEN_SLOT || dir == ITEM_SLOT_INVEN // or moving an item to/from our cospre item slots || dir == ITEM_INVEN_TO_COSP || dir == ITEM_COSP_TO_INVEN || dir == ITEM_SLOT_SLOT) { // Re-update item stats SetSlotItemValue(); SetUserAbility(false); } SendItemMove(1); SendItemWeight(); // Update everyone else, so that they can see your shiny new items (you didn't take them off did you!? DID YOU!?) if (dir == ITEM_INVEN_SLOT) UserLookChange(bDstPos, nItemID, pDstItem->sDuration); if (dir == ITEM_SLOT_INVEN) UserLookChange(bSrcPos, 0, 0); if (dir == ITEM_SLOT_SLOT) { UserLookChange(bSrcPos, pSrcItem->nNum, pSrcItem->sDuration); UserLookChange(bDstPos, pDstItem->nNum, pDstItem->sDuration); } if (dir == ITEM_INVEN_TO_COSP) UserLookChange(INVENTORY_COSP + bDstPos, nItemID, pDstItem->sDuration); if (dir == ITEM_COSP_TO_INVEN) UserLookChange(INVENTORY_INVENT + bSrcPos, 0, 0); Send2AI_UserUpdateInfo(); return; fail_return: SendItemMove(0); }