knightonline/server/GameServer/MerchantHandler.cpp

1175 lines
30 KiB
C++

#include "stdafx.h"
#include <boost\foreach.hpp>
using std::string;
enum MerchantOpenResponseCodes
{
MERCHANT_OPEN_SUCCESS = 1,
MERCHANT_OPEN_NO_SESSION = -1,
MERCHANT_OPEN_DEAD = -2,
MERCHANT_OPEN_TRADING = -3,
MERCHANT_OPEN_MERCHANTING = -4,
MERCHANT_OPEN_INVALID_ZONE = -5,
MERCHANT_OPEN_SHOPPING = -6,
MERCHANT_OPEN_UNDERLEVELED = 30
};
void CUser::MerchantProcess(Packet & pkt)
{
uint8 opcode = pkt.read<uint8>();
if (isTrading()
|| isStoreOpen()
|| isDead())
CancelMerchant();
switch (opcode)
{
// Regular merchants
case MERCHANT_OPEN:
MerchantOpen();
break;
case MERCHANT_CLOSE:
MerchantClose();
break;
case MERCHANT_ITEM_ADD:
MerchantItemAdd(pkt);
break;
case MERCHANT_ITEM_CANCEL:
MerchantItemCancel(pkt);
break;
case MERCHANT_ITEM_LIST:
MerchantItemList(pkt);
break;
case MERCHANT_ITEM_BUY:
MerchantItemBuy(pkt);
break;
case MERCHANT_INSERT:
MerchantInsert(pkt);
break;
case MERCHANT_TRADE_CANCEL:
CancelMerchant();
break;
// Buying merchants
case MERCHANT_BUY_OPEN:
BuyingMerchantOpen(pkt);
break;
case MERCHANT_BUY_CLOSE:
BuyingMerchantClose();
break;
case MERCHANT_BUY_LIST:
BuyingMerchantList(pkt);
break;
case MERCHANT_BUY_INSERT:
BuyingMerchantInsert(pkt);
break;
case MERCHANT_BUY_BUY: // seeya!
BuyingMerchantBuy(pkt);
break;
case MERCHANT_MENISIA_LIST:
OfficialList(pkt);
break;
}
}
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() != ZONE_MORADON
|| isMining())
errorCode = MERCHANT_OPEN_INVALID_ZONE;
else if (GetLevel() < 20)
errorCode = MERCHANT_OPEN_UNDERLEVELED;
else if (isBuyingMerchant()){
BuyingMerchantClose();
return;
}else if(uint32(m_bLastMerchantTime+55) > uint32(UNIXTIME))
errorCode = MERCHANT_OPEN_TRADING;
else if (isMerchanting() || m_bMerchantStatex)
errorCode = MERCHANT_OPEN_MERCHANTING;
else
{
errorCode = MERCHANT_OPEN_SUCCESS;
m_bMerchantStatex = 1;
m_bLastMerchantTime = uint32(UNIXTIME);
}
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()
{
GiveMerchantItems(); // Give back to the user that which hasn't been sold, if any.
m_bMerchantState = MERCHANT_STATE_NONE;
m_bMerchantStatex = 0;
Packet result(WIZ_MERCHANT, uint8(MERCHANT_CLOSE));
result << GetSocketID();
SendToRegion(&result,nullptr,GetEventRoom());
string errorMessage = string_format(_T("MERCHANT_CLOSE uId-%s- Z-%d- X-%d- Y-%d-"),
GetName().c_str(),GetZoneID(), uint16(GetX()), uint16(GetZ()));
g_pMain->WriteMerchantUserLogFile(errorMessage);
_MERCHANT_LIST * pList = g_pMain->m_MerchantListArray.GetData(GetID());
if(!pList)
return;
else
g_pMain->m_MerchantListArray.DeleteData(GetID());
}
void CUser::MerchantItemAdd(Packet & pkt)
{
Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_ADD));
uint32 nGold, nItemID;
uint16 sCount;
uint16 bResult = 0;
uint8 bSrcPos, // It sends the "actual" inventory slot (SLOT_MAX -> INVENTORY_MAX-SLOT_MAX), so need to allow for it.
bDstPos,
bMode; // Might be a flag for normal / "premium" merchant mode, once skills are implemented take another look at this.
pkt >> nItemID >> sCount >> nGold >> bSrcPos >> bDstPos >> bMode;
// TODO: Implement the possible error codes for these various error cases.
if (bSrcPos >= HAVE_MAX
|| bDstPos >= MAX_MERCH_ITEMS
|| !m_bMerchantStatex
|| isMerchanting())
return;
bSrcPos += SLOT_MAX;
_ITEM_DATA *pSrcItem = GetItem(bSrcPos);
_ITEM_TABLE * pTable = g_pMain->GetItemPtr(nItemID);
if (pTable == nullptr)
return;
if ((nItemID >= ITEM_NO_TRADE && nItemID < ITEM_NO_TRADE_MAX) // Cannot be traded, sold or stored.
|| pTable->m_bRace == RACE_UNTRADEABLE) // Cannot be traded or sold.
goto fail_return;
if (pSrcItem == nullptr)
return;
if (pSrcItem->nNum != nItemID
|| pSrcItem->sCount < sCount
|| pSrcItem->isRented()
|| pSrcItem->isSealed()
|| pSrcItem->isBound()
|| pSrcItem->nExpirationTime != 0
|| pSrcItem->isDuplicate())
goto fail_return;
_MERCH_DATA *pMerch = &m_arSellMerchantItems[bDstPos];
if(pMerch == nullptr)
return;
if (pSrcItem->IsSelling)
goto fail_return;
pMerch->nNum = nItemID;
pMerch->nPrice = nGold;
pMerch->sCount = sCount; // Selling Count
pMerch->bCount = pSrcItem->sCount; // Original Count ( INVENTORY )
pMerch->sDuration = pSrcItem->sDuration;
pMerch->nSerialNum = pSrcItem->nSerialNum; // NOTE: Stackable items will have an issue with this.
pMerch->bOriginalSlot = bSrcPos;
pMerch->IsSoldOut = false;
pSrcItem->IsSelling = true;
bResult = 1;
// Take the user's item.
//memset(pSrcItem, 0, sizeof(_ITEM_DATA));
result << bResult
<< nItemID << sCount << pMerch->sDuration << nGold
<< bSrcPos << bDstPos;
Send(&result);
fail_return:
result << bResult
<< nItemID
<< sCount
<< pSrcItem->sDuration
<< nGold
<< bSrcPos
<< bDstPos;
Send(&result);
}
void CUser::MerchantItemCancel(Packet & pkt)
{
Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_CANCEL));
_MERCH_DATA *pMerch = nullptr;
_ITEM_DATA *pItem = nullptr;
if(isMerchanting() || !m_bMerchantStatex)
return;
uint8 bSrcPos = pkt.read<uint8>();
/* if (this == nullptr)
result << int16(-1);*/
// Invalid source position
if (bSrcPos >= MAX_MERCH_ITEMS)
result << int16(-2);
// There's no item in that list..?
else if ((pMerch = &m_arSellMerchantItems[bSrcPos])->nNum == 0)
result << int16(-3);
// Check to make sure we've got a valid stack
else if (pMerch->bOriginalSlot > HAVE_MAX || (pItem = &m_sItemArray[pMerch->bOriginalSlot])->nNum != pMerch->nNum)
result << int16(-3); // custom error
else
{
pItem->nNum = pMerch->nNum;
pItem->sCount = pMerch->bCount;
pItem->sDuration = pMerch->sDuration;
pItem->IsSelling = false;
pItem->nSerialNum = pMerch->nSerialNum; // NOTE: Stackable items will have an issue with this.
string errorMessage = string_format(_T("MERCHANT_ITEM_CANCEL uId-%s- %d,%d,%d"),
GetName().c_str(), pMerch->nNum, pMerch->bCount, pMerch->sDuration);
g_pMain->WriteMerchantUserLogFile(errorMessage);
memset(pMerch, 0, sizeof(pMerch));
result << int16(1) << bSrcPos;
}
Send(&result);
}
void CUser::MerchantItemList(Packet & pkt)
{
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| m_bMerchantStatex)
return;
if (m_sMerchantsSocketID >= 0)
RemoveFromMerchantLookers(); //This check should never be hit...
uint16 uid = pkt.read<uint16>();
if(uid == GetID())
return;
CUser *pMerchant = g_pMain->GetUserPtr(uid);
CBot *pMerchantBot = g_pMain->m_arBotArray.GetData(uid);
if ((pMerchant == nullptr
|| !pMerchant->isMerchanting())
&& (pMerchantBot == nullptr || !pMerchantBot->isMerchanting())
|| pMerchant->m_bMerchantState != MERCHANT_STATE_SELLING)
return;
m_sMerchantsSocketID = uid;
if(pMerchant != nullptr)
{
if (pMerchant->m_arMerchantLookers.size() != 0) {
Packet result(WIZ_MERCHANT, uint8(35));
result << uint8(7);
Send(&result);
return;
}
pMerchant->m_arMerchantLookers.push_front(GetSocketID());
Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_LIST));
result << uint16(1) << uint16(uid);
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
_MERCH_DATA *pMerch = &pMerchant->m_arSellMerchantItems[i];
result << pMerch->nNum << pMerch->sCount
<< pMerch->sDuration << pMerch->nPrice;
pMerchant->SetMerchantSpecialItemData(pMerch,result);
}
Send(&result);
}
else if(pMerchantBot != nullptr)
{
if (pMerchantBot->m_arMerchantLookers.size() != 0) {
Packet result(WIZ_MERCHANT, uint8(35));
result << uint8(7);
Send(&result);
return;
}
pMerchantBot->m_arMerchantLookers.push_front(GetSocketID());
Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_LIST));
result << uint16(1) << uint16(uid);
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
_MERCH_DATA *pMerch = &pMerchantBot->m_arSellMerchantItems[i];
result << pMerch->nNum << pMerch->sCount
<< pMerch->sDuration << pMerch->nPrice << uint32(0);
}
Send(&result);
}
}
void CUser::MerchantItemBuy(Packet & pkt)
{
uint32 itemid, req_gold;
uint16 item_count, leftover_count;
uint8 item_slot, dest_slot;
CUser *pMerchant = g_pMain->GetUserPtr(m_sMerchantsSocketID),
*pUser = g_pMain->GetUserPtr(GetID());
CBot *pMerchantBot = g_pMain->m_arBotArray.GetData(m_sMerchantsSocketID);
if (pMerchant == nullptr && pMerchantBot == nullptr)
return;
pkt >> itemid >> item_count >> item_slot >> dest_slot;
// Make sure the slots are correct and that we're actually buying at least 1 item.
if (item_slot >= MAX_MERCH_ITEMS
|| dest_slot >= HAVE_MAX
|| item_count == 0)
return;
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| m_bMerchantStatex)
return;
_MERCH_DATA *pMerch;
// Grab pointers to the items.
if(m_sMerchantsSocketID > MAX_USER)
pMerch = &pMerchantBot->m_arSellMerchantItems[item_slot];
else
pMerch = &pMerchant->m_arSellMerchantItems[item_slot];
_ITEM_DATA *pItem = GetItem(SLOT_MAX + dest_slot);
_ITEM_DATA *pItemRob;
if(m_sMerchantsSocketID < MAX_USER)
pItemRob = &pMerchant->m_sItemArray[pMerch->bOriginalSlot];
// Make sure the merchant actually has that item in that slot
// and that they have enough
if (pMerch->nNum != itemid
|| pMerch->sCount < item_count)
return;
// If it's not stackable, and we're specifying something other than 1
// we really don't care to handle this request...
_ITEM_TABLE *proto = g_pMain->GetItemPtr(itemid);
if (proto == nullptr
|| !proto->m_bCountable && item_count != 1)
return;
// Do we have enough coins?
req_gold = pMerch->nPrice * item_count;
if (m_iGold < req_gold)
return;
// If the slot's not empty
if (pItem->nNum != 0
// and we already have an item that isn't the same item
// or it's the same item but the item's not stackable...
&& (pItem->nNum != itemid || !proto->m_bCountable))
return;
leftover_count = pMerch->sCount - item_count;
if(m_sMerchantsSocketID < MAX_USER)
pMerchant->GoldChange(GetSocketID(), req_gold);
else
GoldLose(req_gold);
pItem->nNum = itemid;
pItem->sCount += item_count;
pItem->sDuration = pMerch->sDuration;
pItem->nSerialNum = pMerch->nSerialNum;
string errorMessage = string_format(_T("MERCHANT_ITEM_BUY buyerId-%s- sellerId-%s- Gold-%d- I-%d-, C-%d-"),
GetName().c_str(),m_sMerchantsSocketID < MAX_USER ? pMerchant->GetName().c_str() : pMerchantBot->GetName().c_str(), req_gold, itemid, item_count);
g_pMain->WriteMerchantUserLogFile(errorMessage);
pMerch->sCount -= item_count;
pMerch->bCount -= item_count;
if (pMerch->sCount == 0 && pMerch->bCount == 0 && m_sMerchantsSocketID < MAX_USER)
memset(pItemRob, 0, sizeof(_ITEM_DATA));
else if(m_sMerchantsSocketID < MAX_USER)
pItemRob->sCount = pMerch->sCount;
SendStackChangeSpecial(itemid, pItem->sCount, pItem->sDuration, dest_slot,
(pItem->sCount == item_count),0,pItem); // is it a new item?
if(m_sMerchantsSocketID < MAX_USER)
pMerchant->SendStackChange(itemid, pMerch->bCount, pMerch->sDuration,
pMerch->bOriginalSlot- SLOT_MAX);
if (pMerch->sCount == 0 && pMerch->bCount == 0)
memset(pMerch, 0, sizeof(_MERCH_DATA));
else if (pMerch->sCount == 0) // Countable item protect.
pMerch->IsSoldOut = true;
Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_PURCHASED));
if(m_sMerchantsSocketID < MAX_USER)
{
result << itemid << GetName();
pMerchant->Send(&result);
}
result.clear();
result << uint8(MERCHANT_ITEM_BUY) << uint16(1)
<< itemid << leftover_count
<< item_slot << dest_slot;
Send(&result);
if (item_slot < 4 && leftover_count == 0)
{
result.Initialize(WIZ_MERCHANT_INOUT);
result << uint8(2) << m_sMerchantsSocketID << uint8(1) << uint8(0) << item_slot;
if(m_sMerchantsSocketID < MAX_USER)
pMerchant->SendToRegion(&result,nullptr,pMerchant->GetEventRoom());
else
pMerchantBot->SendToRegion(&result);
}
if(m_sMerchantsSocketID < MAX_USER)
g_pMain->MerchantUserInOutForMe(pMerchant);
g_pMain->MerchantUserInOutForMe(pUser);
int nItemsRemaining = 0;
if(m_sMerchantsSocketID < MAX_USER)
{
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
if (pMerchant->m_arSellMerchantItems[i].nNum != 0 && !pMerchant->m_arSellMerchantItems[i].IsSoldOut)
nItemsRemaining++;
}
if (nItemsRemaining == 0)
pMerchant->MerchantClose();
}
else
{
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
if (pMerchantBot->m_arSellMerchantItems[i].nNum != 0 && !pMerchantBot->m_arSellMerchantItems[i].IsSoldOut)
nItemsRemaining++;
}
if (nItemsRemaining == 0)
{
pMerchantBot->LastWarpTime = UNIXTIME + 60;
pMerchantBot->m_bMerchantState = MERCHANT_STATE_NONE;
pMerchantBot->m_bMerchantStatex = 0;
Packet result35(WIZ_MERCHANT, uint8(MERCHANT_CLOSE));
result35 << pMerchantBot->GetID();
pMerchantBot->SendToRegion(&result35);
}
}
}
void CUser::MerchantInsert(Packet & pkt)
{
string advertMessage; // check here maybe to make sure they're not using it otherwise?
pkt >> advertMessage;
uint16 bResult = 0;
if (advertMessage.size() > MAX_MERCH_MESSAGE)
return;
uint8 Count = 0, ListCount = 0, i = 1, TotalCount = 0;
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| !m_bMerchantStatex)
return;
uint8 MerchantItemleri = 0;
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
if (m_arSellMerchantItems[i].nNum != 0)
MerchantItemleri++;
}
if (MerchantItemleri == 0)
return;
m_bMerchantState = MERCHANT_STATE_SELLING;
bResult = 1;
Packet result(WIZ_MERCHANT, uint8(MERCHANT_INSERT));
if(isGM())
{
CBot * myBot = nullptr;
uint16 ID = g_pMain->SpawnBot(3600,GetZoneID(),GetX(),GetY(),GetZ(),5,35);
if(ID)
{
myBot = g_pMain->m_arBotArray.GetData(ID);
if(myBot == nullptr)
return;
std::string asdasd = advertMessage;
if(!asdasd.empty())
myBot->MerchantChat = string_format("%s(Location:%d,%d)",asdasd.c_str(),myBot->GetSPosX()/10,myBot->GetSPosZ()/10);
else
myBot->MerchantChat.clear();
myBot->m_iLoyalty = uint32(UNIXTIME) - 110;
_MERCH_DATA * pMerch, * pMerchMe;
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
pMerch = &myBot->m_arSellMerchantItems[i];
pMerchMe = &m_arSellMerchantItems[i];
pMerch->nNum = pMerchMe->nNum;
pMerch->nPrice = pMerchMe->nPrice;
pMerch->sCount = pMerchMe->sCount; // Selling Count
pMerch->bCount = pMerchMe->bCount; // Original Count ( INVENTORY )
pMerch->sDuration = pMerchMe->sDuration;
pMerch->nSerialNum = pMerchMe->nSerialNum; // NOTE: Stackable items will have an issue with this.
pMerch->bOriginalSlot = pMerchMe->bOriginalSlot;
pMerch->IsSoldOut = false;
bResult = 1;
}
MerchantClose();
myBot->m_bPremiumMerchant = m_bPremiumMerchant;
myBot->m_bMerchantState = MERCHANT_STATE_SELLING;
result << bResult << advertMessage << myBot->GetID()
<< m_bPremiumMerchant;
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
result << myBot->m_arSellMerchantItems[i].nNum;
if(bResult == 1)
{
_MERCHANT_LIST * pList = new _MERCHANT_LIST;
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
pList->ItemID[i] = myBot->m_arSellMerchantItems[i].nNum;
pList->Price[i] = myBot->m_arSellMerchantItems[i].nPrice;
pList->strUserID = myBot->GetID();
pList->strUserName = myBot->m_strUserID;
pList->Type = 0;
}
g_pMain->m_MerchantListArray.PutData(pList->strUserID, pList);
}
myBot->SendToRegion(&result);
return;
}
}
if(bResult == 1)
{
_MERCHANT_LIST * pList = new _MERCHANT_LIST;
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
pList->ItemID[i] = m_arSellMerchantItems[i].nNum;
pList->Price[i] = m_arSellMerchantItems[i].nPrice;
pList->strUserID = GetID();
pList->strUserName = m_strUserID;
pList->Type = 0;
}
g_pMain->m_MerchantListArray.PutData(pList->strUserID, pList);
}
result << bResult << advertMessage << GetSocketID()
<< m_bPremiumMerchant;
string errorMessage = string_format(_T("MERCHANT_INSERT Msg-%s- uId-%s- %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d"),
advertMessage.c_str(),GetName().c_str(),m_arSellMerchantItems[0].nNum,m_arSellMerchantItems[1].nNum,m_arSellMerchantItems[2].nNum,m_arSellMerchantItems[3].nNum,
m_arSellMerchantItems[4].nNum,m_arSellMerchantItems[5].nNum,m_arSellMerchantItems[6].nNum,m_arSellMerchantItems[7].nNum,m_arSellMerchantItems[8].nNum,m_arSellMerchantItems[9].nNum,
m_arSellMerchantItems[10].nNum,m_arSellMerchantItems[11].nNum);
g_pMain->WriteMerchantUserLogFile(errorMessage);
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
result << m_arSellMerchantItems[i].nNum;
SendToRegion(&result,nullptr,GetEventRoom());
}
void CUser::GiveMerchantItems()
{
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
_MERCH_DATA *pMerch = &m_arSellMerchantItems[i];
if (pMerch->nNum == 0)
continue;
_ITEM_DATA *pItem = &m_sItemArray[pMerch->bOriginalSlot];
pItem->nNum = pMerch->nNum;
pItem->nSerialNum = pMerch->nSerialNum;
pItem->sCount = pMerch->bCount;
pItem->sDuration = pMerch->sDuration;
pItem->IsSelling = false;
// NOTE: Don't need to update the client, the client doesn't see any change.
}
// remove the items from the array now that they've been restored to the user
memset(&m_arSellMerchantItems, 0, sizeof(m_arSellMerchantItems));
}
void CUser::CancelMerchant()
{
if (m_sMerchantsSocketID < 0)
return;
CUser *pMerchant = g_pMain->GetUserPtr(GetID());
if(pMerchant == nullptr)
return;
RemoveFromMerchantLookers();
memset(&m_arSellMerchantItems, 0, sizeof(m_arSellMerchantItems));
Packet result(WIZ_MERCHANT, uint8(MERCHANT_TRADE_CANCEL));
result << uint16(1);
Send(&result);
}
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() != ZONE_MORADON && GetZoneID() != ZONE_MORADONM2) || isMining())
errorCode = MERCHANT_OPEN_INVALID_ZONE;
else if (GetLevel() < 20)
errorCode = MERCHANT_OPEN_UNDERLEVELED;
else if (isMerchanting() || m_bMerchantStatex)
errorCode = MERCHANT_OPEN_MERCHANTING;
else if(uint32(m_bLastMerchantTime+55) > uint32(UNIXTIME))
errorCode = MERCHANT_OPEN_TRADING;
else {
errorCode = MERCHANT_OPEN_SUCCESS;
m_bMerchantStatex = 1;
m_bLastMerchantTime = uint32(UNIXTIME);
}
memset(&m_arBuyMerchantItems, 0, sizeof(m_arBuyMerchantItems));
Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_OPEN));
result << errorCode;
Send(&result);
if (errorCode == MERCHANT_OPEN_MERCHANTING)
BuyingMerchantClose();
}
void CUser::BuyingMerchantClose()
{
if (isMerchanting() && !isBuyingMerchant()){
MerchantClose();
return;
}
if (isMerchanting())
m_bMerchantState = MERCHANT_STATE_NONE;
else if (m_sMerchantsSocketID >= 0)
RemoveFromMerchantLookers();
else
return;
m_bMerchantStatex = 0;
memset(&m_arBuyMerchantItems, 0, sizeof(m_arBuyMerchantItems));
Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_CLOSE));
result << GetSocketID();
SendToRegion(&result,nullptr,GetEventRoom());
_MERCHANT_LIST * pList = g_pMain->m_MerchantListArray.GetData(GetID());
if(!pList)
return;
else
g_pMain->m_MerchantListArray.DeleteData(GetID());
}
void CUser::BuyingMerchantInsert(Packet & pkt)
{
uint8 amount_of_items;
uint32 itemid, buying_price;
uint32 totalamount = 0;
uint16 item_count;
_ITEM_TABLE *pItem = nullptr;
uint8 bResult = 0;
pkt >> amount_of_items;
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| !m_bMerchantStatex)
return;
uint8 MerchantItemleri = 0;
for (int i = 0; i < amount_of_items; i++)
{
pkt >> itemid >> item_count >> buying_price;
pItem = g_pMain->m_ItemtableArray.GetData(itemid);
if (pItem == nullptr){
BuyingMerchantClose();
return;
}
m_arBuyMerchantItems[i].nNum = itemid;
m_arBuyMerchantItems[i].sCount = item_count;
m_arBuyMerchantItems[i].nPrice = buying_price;
m_arBuyMerchantItems[i].sDuration = pItem->m_sDuration;
totalamount += buying_price;
MerchantItemleri++;
}
if (MerchantItemleri == 0)
return;
if (!hasCoins(totalamount))
return;
m_bMerchantState = MERCHANT_STATE_BUYING;
bResult = 1;
Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_INSERT));
result << bResult;
Send(&result);
if(bResult == 1)
{
_MERCHANT_LIST * pList = new _MERCHANT_LIST;
for (int i = 0; i < amount_of_items; i++)
{
pList->ItemID[i] = m_arBuyMerchantItems[i].nNum;
pList->Price[i] = m_arBuyMerchantItems[i].nPrice;
pList->strUserID = GetID();
pList->strUserName = m_strUserID;
pList->Type = 1;
}
g_pMain->m_MerchantListArray.PutData(pList->strUserID, pList);
}
BuyingMerchantInsertRegion();
}
void CUser::BuyingMerchantInsertRegion()
{
Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_REGION_INSERT));
result << GetSocketID();
for (int i = 0; i < 4; i++)
{
result << m_arBuyMerchantItems[i].nNum;
}
SendToRegion(&result,nullptr,GetEventRoom());
}
void CUser::BuyingMerchantList(Packet & pkt)
{
if (m_sMerchantsSocketID >= 0)
RemoveFromMerchantLookers(); //This check should never be hit...
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| m_bMerchantStatex)
return;
uint16 uid = pkt.read<uint16>();
if(uid == GetID())
return;
CUser *pMerchant = g_pMain->GetUserPtr(uid);
if (pMerchant == nullptr
|| !pMerchant->isMerchanting()
|| pMerchant->m_bMerchantState != MERCHANT_STATE_BUYING)
return;
m_sMerchantsSocketID = uid;
if (pMerchant->m_arMerchantLookers.size() != 0) {
Packet result(WIZ_MERCHANT, uint8(35));
result << uint8(7);
Send(&result);
return;
}
pMerchant->m_arMerchantLookers.push_front(GetSocketID());
Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_LIST));
result << uint8(1) << uint16(uid);
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
_MERCH_DATA *pMerch = &pMerchant->m_arBuyMerchantItems[i];
result << pMerch->nNum << pMerch->sCount
<< pMerch->sDuration << pMerch->nPrice;
}
Send(&result);
}
void CUser::BuyingMerchantBuy(Packet & pkt)
{
uint32 nPrice;
uint16 sStackSize, sRemainingStackSize;
uint8 bSellerSrcSlot, bMerchantListSlot;
CUser *pMerchant = g_pMain->GetUserPtr(m_sMerchantsSocketID);
if (pMerchant == nullptr)
return;
pkt >> bSellerSrcSlot >> bMerchantListSlot >> sStackSize;
if (bSellerSrcSlot >= HAVE_MAX
|| bMerchantListSlot >= MAX_MERCH_ITEMS)
return;
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| m_bMerchantStatex)
return;
_MERCH_DATA *pWantedItem = &pMerchant->m_arBuyMerchantItems[bMerchantListSlot];
_ITEM_DATA *pSellerItem = GetItem(SLOT_MAX + bSellerSrcSlot);
// Make sure the merchant actually has that item in that slot
// and that they want enough, and the selling user has enough
if (pWantedItem == nullptr
|| pSellerItem == nullptr
|| pWantedItem->nNum != pSellerItem->nNum
|| pWantedItem->sCount < sStackSize
|| pSellerItem->sCount < sStackSize
|| pSellerItem->nExpirationTime > 0
// For scrolls, this will ensure you can only sell a full stack of scrolls.
// For everything else, this will ensure you cannot sell items that need repair.
|| pSellerItem->sDuration != pWantedItem->sDuration
|| pSellerItem->isDuplicate()
|| sStackSize <= 0)
return;
// If it's not stackable, and we're specifying something other than 1
// we really don't care to handle this request...
_ITEM_TABLE *proto = g_pMain->GetItemPtr(pWantedItem->nNum);
if (proto == nullptr
|| !proto->m_bCountable && sStackSize != 1)
return;
// Do they have enough coins?
nPrice = pWantedItem->nPrice * sStackSize;
if (!pMerchant->hasCoins(nPrice))
return;
// Now find the buyer a home for their item
int8 bDstPos = pMerchant->FindSlotForItem(pWantedItem->nNum, sStackSize);
if (bDstPos < 0)
return;
_ITEM_DATA *pMerchantItem = pMerchant->GetItem(bDstPos);
// Take coins off the buying merchant
if (!pMerchant->GoldLose(nPrice))
return;
// and give them all to me, me, me!
GoldGain(nPrice);
// Get the remaining stack size after purchase.
sRemainingStackSize = pSellerItem->sCount - sStackSize;
// Now we give the buying merchant their wares.
pMerchantItem->nNum = pSellerItem->nNum;
pMerchantItem->sDuration = pSellerItem->sDuration;
pSellerItem->sCount -= sStackSize;
pMerchantItem->sCount += sStackSize;
// Update how many items the buyer still needs.
pWantedItem->sCount -= sStackSize;
// If the buyer needs no more, remove this item from the wanted list.
if (pWantedItem->sCount == 0)
memset(pWantedItem, 0, sizeof(_MERCH_DATA));
// If the seller's all out, remove their item.
if (pSellerItem->sCount == 0)
memset(pSellerItem, 0, sizeof(_ITEM_DATA));
// TODO : Proper checks for the removal of the items in the array, we're now assuming everything gets bought
// Update players
SendStackChange(pSellerItem->nNum, pSellerItem->sCount, pSellerItem->sDuration, bSellerSrcSlot);
pMerchant->SendStackChange(pMerchantItem->nNum, pMerchantItem->sCount, pMerchantItem->sDuration, bDstPos - SLOT_MAX,
pMerchantItem->sCount == sStackSize); // if the buying merchant only has what they wanted, it's a new item.
// (otherwise it was a stackable item that was merged into an existing slot)
Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_BOUGHT));
result << bMerchantListSlot << uint16(0) << GetName();
pMerchant->Send(&result);
result.clear();
result << uint8(MERCHANT_BUY_SOLD) << uint8(1) << bMerchantListSlot << pWantedItem->sCount << bSellerSrcSlot << pSellerItem->sCount;
Send(&result);
result.clear();
result << uint8(MERCHANT_BUY_BUY) << uint8(1);
Send(&result);
if (bMerchantListSlot < 4 && pWantedItem->sCount == 0)
{
result.Initialize(WIZ_MERCHANT_INOUT);
result << uint8(2) << m_sMerchantsSocketID << uint8(1) << uint8(0) << bMerchantListSlot;
pMerchant->SendToRegion(&result,nullptr,pMerchant->GetEventRoom());
}
int nItemsRemaining = 0;
for (int i = 0; i < MAX_MERCH_ITEMS; i++)
{
if (pMerchant->m_arBuyMerchantItems[i].nNum != 0)
nItemsRemaining++;
}
if (nItemsRemaining == 0)
pMerchant->BuyingMerchantClose();
}
void CUser::RemoveFromMerchantLookers()
{
CUser *pPreviousMerchantUser = g_pMain->GetUserPtr(m_sMerchantsSocketID);
CBot *pPreviousMerchantBot = g_pMain->m_arBotArray.GetData(m_sMerchantsSocketID);
if (pPreviousMerchantUser == nullptr && pPreviousMerchantBot == nullptr)
return;
if ( m_sMerchantsSocketID < MAX_USER)
pPreviousMerchantUser->m_arMerchantLookers.remove(GetSocketID());
else
pPreviousMerchantBot->m_arMerchantLookers.remove(GetSocketID());
m_sMerchantsSocketID = -1;
}
void CUser::OfficialList(Packet & pkt)
{
if (GetZoneID() < ZONE_MORADON || GetZoneID() > ZONE_MORADONM2)
{
Packet result(WIZ_MERCHANT, uint8(MERCHANT_MENISIA_LIST));
result << uint8(1) << uint8(0) << uint8(3);
Send(&result);
return;
}
uint8 opcode = pkt.read<uint8>();
switch (opcode)
{
case 1:
ListSend(pkt);
break;
case 2:
ListMoveProcess(pkt);
break;
}
}
void CUser::ListSend(Packet & pkt)
{
uint32 RecvItemID = pkt.read<uint32>();
if (RecvItemID != ITEM_MENICIAS_LIST)
return;
if (CheckExistItem(RecvItemID))
{
uint8 nCount = 0;
Packet result(WIZ_MERCHANT,uint8(MERCHANT_MENISIA_LIST));
result << uint8(0x01) << uint8(0x01) << nCount;
Send(&result);
int16 nSize = g_pMain->m_MerchantListArray.GetSize();
if(nSize == 0)
{
uint8 nCount = 0;
Packet result2(WIZ_MERCHANT,uint8(MERCHANT_MENISIA_LIST));
result2 << uint8(0x01) << uint8(0x02) << nCount;
Send(&result2);
return;
}
while (nSize > 0)
{
nCount++;
result.clear();
result << uint8(MERCHANT_MENISIA_LIST) << uint8(1) << uint8(2) << nCount;
if(nSize > 50)
result << uint8(50);
else
result << uint8(nSize);
result.SByte();
foreach_stlmap(itr, g_pMain->m_MerchantListArray)
{
result << itr->second->strUserID << itr->second->strUserName << itr->second->Type;
for(int i = 0; i < 12; i++)
{
if(itr->second->ItemID[i] == 3452816845)
result << uint32(0) << uint32(0);
else
{
result << itr->second->ItemID[i] << itr->second->Price[i];
}
}
}
Send(&result);
nSize = nSize - 50;
}
}
else
{
Packet result(WIZ_MERCHANT,uint8(MERCHANT_MENISIA_LIST));
result << uint8(0x01) << uint8(0x00) << uint8(0x00);
Send(&result);
}
}
void CUser::ListMoveProcess(Packet & pkt)
{
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| m_bMerchantStatex)
{
Packet result(WIZ_MERCHANT, uint8(MERCHANT_MENISIA_LIST));
result << uint8(1) << uint8(0) << uint8(3);
Send(&result);
return;
}
uint16 TargetID = pkt.read<uint16>();
CUser *pUser = g_pMain->GetUserPtr(TargetID);
CBot * pBot = g_pMain->m_arBotArray.GetData(TargetID);
if (!pUser)
{
if(pBot)
{
ZoneChange(pBot->m_bZone, pBot->GetX(), pBot->GetZ());
}
else
{
Packet error(WIZ_MERCHANT, uint8(MERCHANT_MENISIA_LIST));
error << uint8(2) << uint8(0) << uint8(0);
Send(&error);
return;
}
}
else if(!pBot)
{
if(pUser)
{
ZoneChange(pUser->GetZoneID(), pUser->GetX(), pUser->GetZ());
}
else
{
Packet error(WIZ_MERCHANT, uint8(MERCHANT_MENISIA_LIST));
error << uint8(2) << uint8(0) << uint8(0);
Send(&error);
return;
}
}
}