523 lines
13 KiB
C++
523 lines
13 KiB
C++
#include "stdafx.h"
|
|
#include "DBAgent.h"
|
|
|
|
using std::string;
|
|
|
|
#define ITEM_SEAL_PRICE 1000000
|
|
|
|
enum
|
|
{
|
|
SEAL_TYPE_SEAL = 1,
|
|
SEAL_TYPE_UNSEAL = 2,
|
|
SEAL_TYPE_KROWAZ = 3,
|
|
SEAL_TYPE_OLD_ITEM = 4
|
|
};
|
|
|
|
enum SealErrorCodes
|
|
{
|
|
SealErrorNone = 0, // no error, success!
|
|
SealErrorFailed = 2, // "Seal Failed."
|
|
SealErrorNeedCoins = 3, // "Not enough coins."
|
|
SealErrorInvalidCode = 4, // "Invalid Citizen Registry Number" (i.e. invalid code/password)
|
|
SealErrorPremiumOnly = 5, // "Only available to premium users"
|
|
SealErrorFailed2 = 6, // "Seal Failed."
|
|
SealErrorTooSoon = 7, // "Please try again. You may not repeat this function instantly."
|
|
};
|
|
|
|
/**
|
|
* @brief Packet handler for the item sealing system.
|
|
*
|
|
* @param pkt The packet.
|
|
*/
|
|
void CUser::ItemSealProcess(Packet & pkt)
|
|
{
|
|
if (isDead()
|
|
|| isTrading()
|
|
|| isMerchanting()
|
|
|| isStoreOpen()
|
|
|| isSellingMerchant()
|
|
|| isBuyingMerchant()
|
|
|| isMining()
|
|
|| m_bMerchantStatex)
|
|
return;
|
|
|
|
// Seal type
|
|
uint8 opcode = pkt.read<uint8>();
|
|
|
|
Packet result(WIZ_ITEM_UPGRADE, uint8(ITEM_SEAL));
|
|
result << opcode;
|
|
|
|
switch (opcode)
|
|
{
|
|
// Used when sealing an item.
|
|
case SEAL_TYPE_SEAL:
|
|
{
|
|
string strPasswd;
|
|
uint32 nItemID;
|
|
int16 unk0; // set to -1 in this case
|
|
uint8 bSrcPos, bResponse = SealErrorNone;
|
|
pkt >> unk0 >> nItemID >> bSrcPos >> strPasswd;
|
|
|
|
/*
|
|
Most of these checks are handled client-side, so we shouldn't need to provide error messages.
|
|
Also, item sealing requires certain premium types (gold, platinum, etc) - need to double-check
|
|
these before implementing this check.
|
|
*/
|
|
|
|
// is this a valid position? (need to check if it can be taken from new slots)
|
|
if (bSrcPos >= HAVE_MAX
|
|
// does the item exist where the client says it does?
|
|
|| GetItem(SLOT_MAX + bSrcPos)->nNum != nItemID
|
|
// i ain't be allowin' no stealth items to be sealed!
|
|
|| GetItem(SLOT_MAX + bSrcPos)->nSerialNum == 0
|
|
|| GetItem(SLOT_MAX + bSrcPos)->nExpirationTime > 0)
|
|
bResponse = SealErrorFailed;
|
|
// is the password valid by client limits?
|
|
else if (strPasswd.empty() || strPasswd.length() > 8)
|
|
bResponse = SealErrorInvalidCode;
|
|
// do we have enough coins?
|
|
else if (!hasCoins(ITEM_SEAL_PRICE))
|
|
bResponse = SealErrorNeedCoins;
|
|
|
|
_ITEM_TABLE* pItem = g_pMain->m_ItemtableArray.GetData(nItemID);
|
|
|
|
if(pItem == nullptr)
|
|
return;
|
|
|
|
// If no error, pass it along to the database.
|
|
if (bResponse == SealErrorNone)
|
|
{
|
|
result << nItemID << bSrcPos << strPasswd << bResponse;
|
|
g_pMain->AddDatabaseRequest(result, this);
|
|
}
|
|
// If there's an error, tell the client.
|
|
// From memory though, there was no need -- it handled all of these conditions itself
|
|
// so there was no need to differentiate (just ignore the packet). Need to check this.
|
|
else
|
|
{
|
|
result << bResponse;
|
|
Send(&result);
|
|
}
|
|
} break;
|
|
|
|
// Used when unsealing an item.
|
|
case SEAL_TYPE_UNSEAL:
|
|
{
|
|
string strPasswd;
|
|
uint32 nItemID;
|
|
int16 unk0; // set to -1 in this case
|
|
uint8 bSrcPos, bResponse = SealErrorNone;
|
|
pkt >> unk0 >> nItemID >> bSrcPos >> strPasswd;
|
|
|
|
if (bSrcPos >= HAVE_MAX
|
|
|| GetItem(SLOT_MAX+bSrcPos)->bFlag != ITEM_FLAG_SEALED
|
|
|| GetItem(SLOT_MAX+bSrcPos)->nNum != nItemID)
|
|
bResponse = SealErrorFailed;
|
|
else if (strPasswd.empty() || strPasswd.length() > 8)
|
|
bResponse = SealErrorInvalidCode;
|
|
|
|
// If no error, pass it along to the database.
|
|
if (bResponse == SealErrorNone)
|
|
{
|
|
result << nItemID << bSrcPos << strPasswd << bResponse;
|
|
g_pMain->AddDatabaseRequest(result, this);
|
|
}
|
|
// If there's an error, tell the client.
|
|
// From memory though, there was no need -- it handled all of these conditions itself
|
|
// so there was no need to differentiate (just ignore the packet). Need to check this.
|
|
else
|
|
{
|
|
result << bResponse;
|
|
Send(&result);
|
|
}
|
|
} break;
|
|
|
|
// Used when binding a Krowaz item (used to take it from not bound -> bound)
|
|
case SEAL_TYPE_KROWAZ:
|
|
{
|
|
string strPasswd = "0"; //Dummy, not actually used.
|
|
uint32 nItemID;
|
|
uint8 bSrcPos = 0 , unk3, bResponse = SealErrorNone;
|
|
uint16 unk1, unk2;
|
|
pkt >> unk1 >> nItemID >> bSrcPos >> unk3 >> unk2;
|
|
|
|
if (bSrcPos >= HAVE_MAX
|
|
|| GetItem(SLOT_MAX+bSrcPos)->bFlag != ITEM_FLAG_NONE
|
|
|| GetItem(SLOT_MAX+bSrcPos)->nNum != nItemID)
|
|
bResponse = SealErrorFailed;
|
|
|
|
if (bResponse == SealErrorNone)
|
|
{
|
|
result << nItemID << bSrcPos << strPasswd << bResponse;
|
|
g_pMain->AddDatabaseRequest(result, this);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Used when binding a Old item (used to take it from bound -> not bound)
|
|
case SEAL_TYPE_OLD_ITEM:
|
|
{
|
|
string strPasswd, strPasswdA;
|
|
uint32 nItemID;
|
|
int16 unk0;
|
|
uint8 bSrcPos, bResponse = SealErrorNone;
|
|
pkt >> unk0 >> nItemID >> bSrcPos >> strPasswd;
|
|
|
|
|
|
if (bSrcPos >= HAVE_MAX
|
|
|| GetItem(SLOT_MAX+bSrcPos)->nNum != nItemID)
|
|
bResponse = SealErrorFailed;
|
|
|
|
if (bResponse == SealErrorNone)
|
|
{
|
|
_ITEM_TABLE * pTable = g_pMain->m_ItemtableArray.GetData(nItemID);
|
|
if (pTable == nullptr)
|
|
return;
|
|
|
|
if (pTable->m_bKind == 93)
|
|
RobItem(810890000, 10);
|
|
else if (pTable->m_bKind >= 210 && pTable->m_bKind <= 240)
|
|
RobItem(810890000, 70);
|
|
|
|
result << nItemID << bSrcPos << strPasswd << bResponse;
|
|
g_pMain->AddDatabaseRequest(result, this);
|
|
}
|
|
else
|
|
{
|
|
result << bResponse;
|
|
Send(&result);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CUser::SealItem(uint8 bSealType, uint8 bSrcPos)
|
|
{
|
|
_ITEM_DATA * pItem = GetItem(SLOT_MAX + bSrcPos);
|
|
if (pItem == nullptr)
|
|
return;
|
|
|
|
switch (bSealType)
|
|
{
|
|
case SEAL_TYPE_SEAL:
|
|
pItem->bFlag = ITEM_FLAG_SEALED;
|
|
GoldLose(ITEM_SEAL_PRICE);
|
|
break;
|
|
|
|
case SEAL_TYPE_UNSEAL:
|
|
pItem->bFlag = 0;
|
|
break;
|
|
|
|
case SEAL_TYPE_KROWAZ:
|
|
pItem->bFlag = ITEM_FLAG_BOUND;
|
|
break;
|
|
|
|
case SEAL_TYPE_OLD_ITEM:
|
|
pItem->bFlag = ITEM_FLAG_NOT_BOUND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CUser::SendCharacterSealInfo(Packet & pkt)
|
|
{
|
|
Packet result(WIZ_ITEM_UPGRADE,uint8(9));
|
|
uint32 SpecialID;
|
|
uint64 Serial;
|
|
pkt >> SpecialID;
|
|
|
|
Serial = g_DBAgent.GetSerialByID(SpecialID);
|
|
|
|
if(Serial == 0)
|
|
goto fail_return;
|
|
|
|
_CYPHERRING_DATA * pRingData = g_pMain->GetCypherRingPtr(Serial);
|
|
|
|
if(pRingData == nullptr)
|
|
goto fail_return;
|
|
|
|
|
|
result << uint8(4) << uint8(1);
|
|
g_DBAgent.LoadCharSeal(pRingData->UserName,result);
|
|
Send(&result);
|
|
fail_return:
|
|
result << uint8(4) << uint8(2);
|
|
Send(&result);
|
|
}
|
|
|
|
|
|
void CUser::SendCharacterSealProcess()
|
|
{
|
|
Packet result(WIZ_ITEM_UPGRADE,uint8(9));
|
|
|
|
if(!CheckExistItem(800111000) && !CheckExistItem(800112000))
|
|
return;
|
|
|
|
|
|
std::string strCharID1, strCharID2, strCharID3, strCharID4;
|
|
uint16 Class1 = 0, Class2 = 0, Class3 = 0, Class4 = 0;
|
|
uint8 Level1 = 0, Level2 = 0, Level3 = 0, Level4 = 0;
|
|
|
|
g_DBAgent.GetAllCharID(GetAccountName(), strCharID1, strCharID2, strCharID3, strCharID4);
|
|
|
|
for (int i = 1; i < 4; i++)
|
|
{
|
|
if(strCharID1.empty())
|
|
{
|
|
strCharID1 = strCharID2;
|
|
strCharID2.clear();
|
|
}
|
|
|
|
if(strCharID2.empty())
|
|
{
|
|
strCharID2 = strCharID3;
|
|
strCharID3.clear();
|
|
}
|
|
|
|
if(strCharID3.empty())
|
|
{
|
|
strCharID3 = strCharID4;
|
|
strCharID4.clear();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if(!strCharID1.empty())
|
|
Class1 = g_DBAgent.LoadAccountNTS(strCharID1);
|
|
if(!strCharID2.empty())
|
|
Class2 = g_DBAgent.LoadAccountNTS(strCharID2);
|
|
if(!strCharID3.empty())
|
|
Class3 = g_DBAgent.LoadAccountNTS(strCharID3);
|
|
if(!strCharID4.empty())
|
|
Class4 = g_DBAgent.LoadAccountNTS(strCharID4);
|
|
|
|
if(!strCharID1.empty())
|
|
Level1 = g_DBAgent.LoadCharLevel(strCharID1);
|
|
if(!strCharID2.empty())
|
|
Level2 = g_DBAgent.LoadCharLevel(strCharID2);
|
|
if(!strCharID3.empty())
|
|
Level3 = g_DBAgent.LoadCharLevel(strCharID3);
|
|
if(!strCharID4.empty())
|
|
Level4 = g_DBAgent.LoadCharLevel(strCharID4);
|
|
result.DByte();
|
|
result << uint8(1) << uint8(1);
|
|
|
|
for (int a = 0; a < 4; a++)
|
|
{
|
|
if(a == 0)
|
|
{
|
|
if (strCharID1.empty())
|
|
continue;
|
|
|
|
result << strCharID1 << uint8(1) << Class1 << Level1 << uint8(1) << uint32(1) << uint8(1);
|
|
result << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0);
|
|
|
|
}else if(a == 1)
|
|
{
|
|
if (strCharID2.empty())
|
|
continue;
|
|
|
|
result << strCharID2 << uint8(1) << Class2 << Level2 << uint8(1) << uint32(1) << uint8(1);
|
|
result << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0);
|
|
|
|
}else if(a == 2)
|
|
{
|
|
if (strCharID3.empty())
|
|
continue;
|
|
|
|
result << strCharID3 << uint8(1) << Class3 << Level3 << uint8(1) << uint32(1) << uint8(1);
|
|
result << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0);
|
|
|
|
}else if(a == 3)
|
|
{
|
|
if (strCharID4.empty())
|
|
continue;
|
|
|
|
result << strCharID4 << uint8(1) << Class4 << Level4 << uint8(1) << uint32(1) << uint8(1);
|
|
result << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0) << uint64(0);
|
|
}
|
|
|
|
}
|
|
|
|
Send(&result);
|
|
}
|
|
/**
|
|
* @brief Packet handler for the character sealing system.
|
|
*
|
|
* @param pkt The packet.
|
|
*/
|
|
void CUser::CharacterSealProcess(Packet & pkt)
|
|
{
|
|
uint8 command = pkt.read<uint8>();
|
|
|
|
if (isDead()
|
|
|| !isInGame()
|
|
|| isTrading()
|
|
|| isMerchanting()
|
|
|| isStoreOpen()
|
|
|| isSellingMerchant()
|
|
|| isBuyingMerchant()
|
|
|| isMining()
|
|
|| m_bMerchantStatex)
|
|
return;
|
|
|
|
switch (command)
|
|
{
|
|
case 1:
|
|
SendCharacterSealProcess();
|
|
break;
|
|
case 2:
|
|
CharacterGetSealed(pkt);
|
|
break;
|
|
case 3:
|
|
CharacterGetUnSealed(pkt);
|
|
break;
|
|
case 4:
|
|
SendCharacterSealInfo(pkt);
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
void CUser::CharacterGetSealed(Packet & pkt)
|
|
{
|
|
Packet result(WIZ_ITEM_UPGRADE, uint8(9));
|
|
uint16 UnkNow1;
|
|
uint32 ItemNum;
|
|
uint8 Slot, ErrorCode = 0;
|
|
std::string strUserID, strPasswd;
|
|
pkt.DByte();
|
|
pkt >> UnkNow1 >> Slot >> ItemNum;
|
|
|
|
pkt >> strUserID >> strPasswd;
|
|
|
|
if (strPasswd.empty() || strPasswd.length() > 8
|
|
|| strUserID.empty())
|
|
{
|
|
result << uint8(2) << uint8(2);// Character seal failed. 14032
|
|
Send(&result);
|
|
return;
|
|
}
|
|
|
|
_ITEM_DATA * pItem = GetItem(SLOT_MAX + Slot);
|
|
|
|
if(ItemNum != 800111000
|
|
|| pItem == nullptr
|
|
|| pItem->nNum != ItemNum)
|
|
{
|
|
result << uint8(2) << uint8(2);// Character seal failed. 14032
|
|
Send(&result);
|
|
return;
|
|
}
|
|
|
|
uint8 YanCharimi = g_DBAgent.LoadYanCharHAS(GetAccountName(),strUserID);
|
|
|
|
if (YanCharimi == 0)
|
|
{
|
|
result << uint8(2) << uint8(2);// Character seal failed. 14032
|
|
Send(&result);
|
|
return;
|
|
}
|
|
|
|
uint64 Serial = g_pMain->GenerateItemSerial();
|
|
|
|
_CYPHERRING_DATA * pData = new _CYPHERRING_DATA;
|
|
pData->m_Serial = Serial;
|
|
pData->UserName = strUserID;
|
|
pData->iExp = g_DBAgent.LoadCharExp(strUserID);
|
|
pData->bRace = g_DBAgent.LoadCharRace(strUserID);
|
|
pData->sClass = uint8(g_DBAgent.LoadAccountNTS(strUserID));
|
|
pData->bLevel = g_DBAgent.LoadCharLevel(strUserID);
|
|
g_pMain->LastPetID++;
|
|
pData->ID = g_pMain->LastPetID;
|
|
|
|
uint8 DBResult = g_DBAgent.InsertCypherRingData(Serial,pData, strPasswd);
|
|
|
|
if(DBResult != 1)
|
|
{
|
|
result << uint8(2) << uint8(0) << uint8(20 + DBResult);
|
|
Send(&result);
|
|
return;
|
|
}
|
|
|
|
g_pMain->m_CypherRingArray.insert(std::make_pair(pData->m_Serial, pData));
|
|
|
|
pItem->nSerialNum = Serial;
|
|
pItem->nNum = 800112000;
|
|
|
|
// Character seal successful. 14031
|
|
// When a bound item is equipped or stored on a character it cannot be sealed. 14044
|
|
// When a rental item is equipped or stored on a character it cannot be sealed. 14045
|
|
|
|
|
|
// 2 0 20 >> There is no password set for the VIP Vault. Visit the Inn Hostess to set the VIP Vault password.
|
|
// 2 0 22 >> Invalid Citizen Registry Number.
|
|
|
|
|
|
result.DByte();
|
|
result << uint8(2) << uint8(1) << Slot << uint32(pItem->nNum) << uint32(pData->ID) << pData->UserName << uint8(pData->sClass) << uint8(pData->bLevel) << uint16((pData->iExp * 10000) / g_pMain->GetExpByLevel(pData->bLevel)) << uint16(pData->bRace);
|
|
Send(&result);
|
|
|
|
// 2 2 hatalar
|
|
|
|
// uint16 -4 14044
|
|
// uint16 -5 14045
|
|
// nothing 14032
|
|
}
|
|
|
|
|
|
void CUser::CharacterGetUnSealed(Packet & pkt)
|
|
{
|
|
Packet result(WIZ_ITEM_UPGRADE,uint8(9));
|
|
uint16 unkNown;
|
|
uint32 ItemID;
|
|
uint8 Slot, CharacterSlot;
|
|
|
|
result << uint8(3);
|
|
|
|
if(!CheckExistItem(800112000))
|
|
goto fail_return;
|
|
|
|
pkt >> unkNown >> Slot >> ItemID >> CharacterSlot;
|
|
|
|
if(Slot >= HAVE_MAX || ItemID != 800112000)
|
|
goto fail_return;
|
|
|
|
_ITEM_DATA * pItem = GetItem(Slot + SLOT_MAX);
|
|
_ITEM_TABLE * pTable = g_pMain->GetItemPtr(ItemID);
|
|
|
|
if (pItem == nullptr || pTable == nullptr)
|
|
goto fail_return;
|
|
|
|
_CYPHERRING_DATA * pRingData = g_pMain->GetCypherRingPtr(pItem->nSerialNum);
|
|
|
|
if(pRingData == nullptr)
|
|
goto fail_return;
|
|
|
|
if(pRingData->m_Serial != pItem->nSerialNum)
|
|
goto fail_return;
|
|
|
|
uint8 YanChar = g_DBAgent.YanChar(GetAccountName());
|
|
uint8 Nation = g_DBAgent.LoadCharNation(pRingData->UserName);
|
|
|
|
if(YanChar > 3 || YanChar < 1 || Nation < 1 || Nation > 2 || Nation != GetNation())
|
|
goto fail_return;
|
|
|
|
uint8 DBResult = g_DBAgent.InsertCypherRingChar(GetAccountName(), pItem->nSerialNum);
|
|
|
|
if(DBResult != 1)
|
|
goto fail_return;
|
|
|
|
g_pMain->m_CypherRingArray.erase(pItem->nSerialNum);
|
|
RobItem(SLOT_MAX + Slot,pTable,1);
|
|
|
|
result << uint8(1);
|
|
|
|
fail_return:
|
|
result << uint8(2);
|
|
Send(&result);
|
|
} |