2020-08-21 02:10:14 +00:00
|
|
|
#include "CNShardServer.hpp"
|
|
|
|
#include "CNStructs.hpp"
|
|
|
|
#include "ItemManager.hpp"
|
|
|
|
#include "PlayerManager.hpp"
|
2020-09-25 05:35:27 +00:00
|
|
|
#include "NanoManager.hpp"
|
2020-08-21 02:10:14 +00:00
|
|
|
#include "Player.hpp"
|
|
|
|
|
2020-10-14 21:15:02 +00:00
|
|
|
#include <string.h> // for memset()
|
2020-08-28 16:25:03 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
2020-09-14 04:25:14 +00:00
|
|
|
std::map<std::pair<int32_t, int32_t>, Item> ItemManager::ItemData;
|
2020-09-13 22:54:47 +00:00
|
|
|
std::map<int32_t, std::vector<VendorListing>> ItemManager::VendorTables;
|
2020-09-16 00:30:01 +00:00
|
|
|
std::map<int32_t, CrocPotEntry> ItemManager::CrocPotTable;
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
std::map<int32_t, std::vector<int>> ItemManager::RarityRatios;
|
|
|
|
std::map<int32_t, Crate> ItemManager::Crates;
|
|
|
|
/// pair Itemset, Rarity -> vector of pointers (map iterators) to records in ItemData
|
|
|
|
std::map<std::pair<int32_t, int32_t>, std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator>> ItemManager::CrateItems;
|
|
|
|
|
|
|
|
// buffer for error messages
|
|
|
|
char buffer[255];
|
2020-09-13 22:54:47 +00:00
|
|
|
|
2020-08-21 02:10:14 +00:00
|
|
|
void ItemManager::init() {
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);
|
2020-08-21 20:09:52 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_ITEM, itemGMGiveHandler);
|
2020-09-25 05:35:27 +00:00
|
|
|
//this one is for gumballs
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_USE, itemUseHandler);
|
2020-09-14 14:03:30 +00:00
|
|
|
// Bank
|
2020-09-09 20:42:55 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BANK_OPEN, itemBankOpenHandler);
|
2020-09-14 14:03:30 +00:00
|
|
|
// Trade handlers
|
2020-08-26 17:40:10 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, itemTradeOfferHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, itemTradeOfferAcceptHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, itemTradeOfferRefusalHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, itemTradeConfirmHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, itemTradeConfirmCancelHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, itemTradeRegisterItemHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, itemTradeUnregisterItemHandler);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, itemTradeRegisterCashHandler);
|
2020-08-27 02:35:13 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, itemTradeChatHandler);
|
2020-08-28 16:25:03 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler);
|
2020-08-21 02:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_MOVE))
|
|
|
|
return; // ignore the malformed packet
|
2020-08-21 03:25:39 +00:00
|
|
|
|
2020-08-21 02:10:14 +00:00
|
|
|
sP_CL2FE_REQ_ITEM_MOVE* itemmove = (sP_CL2FE_REQ_ITEM_MOVE*)data->buf;
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_PC_ITEM_MOVE_SUCC, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-21 19:28:14 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// sanity check
|
2020-08-26 17:39:49 +00:00
|
|
|
if (plr.plr->Equip[itemmove->iFromSlotNum].iType != 0 && itemmove->eFrom == 0 && itemmove->eTo == 0) {
|
|
|
|
// this packet should never happen unless it is a weapon, tell the client to do nothing and do nothing ourself
|
2020-08-24 08:07:51 +00:00
|
|
|
resp.eTo = itemmove->eFrom;
|
|
|
|
resp.iToSlotNum = itemmove->iFromSlotNum;
|
2020-08-24 22:02:07 +00:00
|
|
|
resp.ToSlotItem = plr.plr->Equip[itemmove->iToSlotNum];
|
2020-08-24 08:07:51 +00:00
|
|
|
resp.eFrom = itemmove->eTo;
|
|
|
|
resp.iFromSlotNum = itemmove->iToSlotNum;
|
2020-08-24 22:02:07 +00:00
|
|
|
resp.FromSlotItem = plr.plr->Equip[itemmove->iFromSlotNum];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:39:49 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC));
|
2020-08-24 08:07:51 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-09 20:42:55 +00:00
|
|
|
// sanity check
|
2020-09-14 13:53:48 +00:00
|
|
|
if (itemmove->iToSlotNum > ABANK_COUNT || itemmove->iToSlotNum < 0)
|
|
|
|
return;
|
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// get the fromItem
|
2020-09-09 20:42:55 +00:00
|
|
|
sItemBase *fromItem;
|
2020-09-14 13:20:55 +00:00
|
|
|
switch ((SlotType)itemmove->eFrom) {
|
|
|
|
case SlotType::EQUIP:
|
2020-09-09 20:42:55 +00:00
|
|
|
fromItem = &plr.plr->Equip[itemmove->iFromSlotNum];
|
|
|
|
break;
|
2020-09-14 13:20:55 +00:00
|
|
|
case SlotType::INVENTORY:
|
2020-09-09 20:42:55 +00:00
|
|
|
fromItem = &plr.plr->Inven[itemmove->iFromSlotNum];
|
|
|
|
break;
|
2020-09-14 13:20:55 +00:00
|
|
|
case SlotType::BANK:
|
2020-09-09 20:42:55 +00:00
|
|
|
fromItem = &plr.plr->Bank[itemmove->iFromSlotNum];
|
|
|
|
break;
|
2020-09-21 19:43:53 +00:00
|
|
|
default:
|
|
|
|
std::cout << "[WARN] MoveItem submitted unknown Item Type?! " << itemmove->eFrom << std::endl;
|
|
|
|
return;
|
2020-08-21 02:10:14 +00:00
|
|
|
}
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// get the toItem
|
2020-09-09 20:42:55 +00:00
|
|
|
sItemBase* toItem;
|
2020-09-14 13:20:55 +00:00
|
|
|
switch ((SlotType)itemmove->eTo) {
|
|
|
|
case SlotType::EQUIP:
|
2020-09-09 20:42:55 +00:00
|
|
|
toItem = &plr.plr->Equip[itemmove->iToSlotNum];
|
|
|
|
break;
|
2020-09-14 13:20:55 +00:00
|
|
|
case SlotType::INVENTORY:
|
2020-09-09 20:42:55 +00:00
|
|
|
toItem = &plr.plr->Inven[itemmove->iToSlotNum];
|
|
|
|
break;
|
2020-09-14 13:20:55 +00:00
|
|
|
case SlotType::BANK:
|
2020-09-09 20:42:55 +00:00
|
|
|
toItem = &plr.plr->Bank[itemmove->iToSlotNum];
|
|
|
|
break;
|
2020-09-21 19:43:53 +00:00
|
|
|
default:
|
|
|
|
std::cout << "[WARN] MoveItem submitted unknown Item Type?! " << itemmove->eTo << std::endl;
|
|
|
|
return;
|
2020-08-21 02:10:14 +00:00
|
|
|
}
|
2020-08-21 03:25:39 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// save items to response
|
2020-09-09 20:42:55 +00:00
|
|
|
resp.ToSlotItem = *toItem;
|
|
|
|
resp.FromSlotItem = *fromItem;
|
|
|
|
|
2020-09-30 17:29:56 +00:00
|
|
|
// swap/stack items in session
|
|
|
|
Item* itemDat = getItemData(toItem->iID, toItem->iType);
|
2020-09-30 20:23:46 +00:00
|
|
|
if (itemDat->stackSize > 1 && itemDat == getItemData(fromItem->iID, fromItem->iType) && fromItem->iOpt < itemDat->stackSize && toItem->iOpt < itemDat->stackSize) {
|
|
|
|
// items are stackable, identical, and not maxed, so run stacking logic
|
2020-09-30 17:29:56 +00:00
|
|
|
|
|
|
|
toItem->iOpt += fromItem->iOpt; // sum counts
|
|
|
|
fromItem->iOpt = 0; // deplete from item
|
|
|
|
if (toItem->iOpt > itemDat->stackSize) {
|
|
|
|
// handle overflow
|
|
|
|
fromItem->iOpt += (toItem->iOpt - itemDat->stackSize); // add overflow to fromItem
|
|
|
|
toItem->iOpt = itemDat->stackSize; // set toItem count to max
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fromItem->iOpt == 0) { // from item count depleted
|
|
|
|
// delete item
|
|
|
|
fromItem->iID = 0;
|
|
|
|
fromItem->iType = 0;
|
|
|
|
fromItem->iTimeLimit = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
resp.iFromSlotNum = itemmove->iFromSlotNum;
|
|
|
|
resp.iToSlotNum = itemmove->iToSlotNum;
|
|
|
|
resp.FromSlotItem = *fromItem;
|
|
|
|
resp.ToSlotItem = *toItem;
|
|
|
|
} else {
|
|
|
|
// items not stackable; just swap them
|
|
|
|
sItemBase temp = *toItem;
|
|
|
|
*toItem = *fromItem;
|
|
|
|
*fromItem = temp;
|
|
|
|
resp.iFromSlotNum = itemmove->iToSlotNum;
|
|
|
|
resp.iToSlotNum = itemmove->iFromSlotNum;
|
|
|
|
}
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// send equip change to viewable players
|
2020-09-14 13:20:55 +00:00
|
|
|
if (itemmove->eFrom == (int)SlotType::EQUIP || itemmove->eTo == (int)SlotType::EQUIP) {
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, equipChange);
|
2020-08-22 23:31:09 +00:00
|
|
|
|
2020-08-24 22:02:07 +00:00
|
|
|
equipChange.iPC_ID = plr.plr->iID;
|
2020-09-14 13:20:55 +00:00
|
|
|
if (itemmove->eFrom == (int)SlotType::EQUIP) {
|
2020-08-22 23:31:09 +00:00
|
|
|
equipChange.iEquipSlotNum = itemmove->iFromSlotNum;
|
2020-09-09 20:42:55 +00:00
|
|
|
equipChange.EquipSlotItem = resp.ToSlotItem;
|
|
|
|
}
|
|
|
|
else {
|
2020-08-22 23:31:09 +00:00
|
|
|
equipChange.iEquipSlotNum = itemmove->iToSlotNum;
|
2020-09-09 20:42:55 +00:00
|
|
|
equipChange.EquipSlotItem = resp.FromSlotItem;
|
2020-08-22 23:31:09 +00:00
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
// unequip vehicle if equip slot 8 is 0
|
|
|
|
if (plr.plr->Equip[8].iID == 0)
|
|
|
|
plr.plr->iPCState = 0;
|
2020-08-22 23:31:09 +00:00
|
|
|
|
|
|
|
// send equip event to other players
|
2020-09-17 22:45:43 +00:00
|
|
|
PlayerManager::sendToViewable(sock, (void*)&equipChange, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE));
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-27 01:53:03 +00:00
|
|
|
// set equipment stats serverside
|
|
|
|
setItemStats(plr.plr);
|
2020-08-21 03:25:39 +00:00
|
|
|
}
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// send response
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.eTo = itemmove->eFrom;
|
|
|
|
resp.eFrom = itemmove->eTo;
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC));
|
2020-08-21 20:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemDeleteHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_DELETE))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_ITEM_DELETE* itemdel = (sP_CL2FE_REQ_PC_ITEM_DELETE*)data->buf;
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC, resp);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.eIL = itemdel->eIL;
|
|
|
|
resp.iSlotNum = itemdel->iSlotNum;
|
2020-08-21 20:09:52 +00:00
|
|
|
|
|
|
|
// so, im not sure what this eIL thing does since you always delete items in inventory and not equips
|
2020-08-24 22:02:07 +00:00
|
|
|
plr.plr->Inven[itemdel->iSlotNum].iID = 0;
|
|
|
|
plr.plr->Inven[itemdel->iSlotNum].iType = 0;
|
|
|
|
plr.plr->Inven[itemdel->iSlotNum].iOpt = 0;
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_ITEM_DELETE_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC));
|
2020-08-21 20:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemGMGiveHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_ITEM))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_GIVE_ITEM* itemreq = (sP_CL2FE_REQ_PC_GIVE_ITEM*)data->buf;
|
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
|
|
|
|
2020-09-22 02:26:12 +00:00
|
|
|
if (plr.plr->accountLevel > 50) {
|
2020-08-21 20:09:52 +00:00
|
|
|
// TODO: send fail packet
|
2020-09-22 02:26:12 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-08-21 20:09:52 +00:00
|
|
|
|
|
|
|
if (itemreq->eIL == 2) {
|
|
|
|
// Quest item, not a real item, handle this later, stubbed for now
|
|
|
|
// sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_GIVE_ITEM_FAIL, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_FAIL), sock->getFEKey()));
|
2020-08-25 17:43:46 +00:00
|
|
|
} else if (itemreq->eIL == 1 && itemreq->Item.iType >= 0 && itemreq->Item.iType <= 10) {
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-18 18:42:11 +00:00
|
|
|
if (ItemData.find(std::pair<int32_t, int32_t>(itemreq->Item.iID, itemreq->Item.iType)) == ItemData.end()) {
|
|
|
|
// invalid item
|
|
|
|
std::cout << "[WARN] Item id " << itemreq->Item.iID << " with type " << itemreq->Item.iType << " is invalid (give item)" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-23 00:26:18 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC, resp);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.eIL = itemreq->eIL;
|
|
|
|
resp.iSlotNum = itemreq->iSlotNum;
|
2020-10-12 18:06:28 +00:00
|
|
|
if (itemreq->Item.iType == 10) {
|
|
|
|
// item is vehicle, set expiration date
|
|
|
|
// set time limit: current time + 7days
|
|
|
|
itemreq->Item.iTimeLimit = getTimestamp() + 604800;
|
|
|
|
}
|
2020-08-22 23:31:09 +00:00
|
|
|
resp.Item = itemreq->Item;
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-24 22:02:07 +00:00
|
|
|
plr.plr->Inven[itemreq->iSlotNum] = itemreq->Item;
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-22 23:31:09 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC));
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:35:27 +00:00
|
|
|
void ItemManager::itemUseHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_USE))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
sP_CL2FE_REQ_ITEM_USE* request = (sP_CL2FE_REQ_ITEM_USE*)data->buf;
|
|
|
|
Player* player = PlayerManager::getPlayer(sock);
|
|
|
|
|
2020-09-28 18:11:13 +00:00
|
|
|
if (player == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-09-25 05:35:27 +00:00
|
|
|
//gumball can only be used from inventory, so we ignore eIL
|
|
|
|
sItemBase gumball = player->Inven[request->iSlotNum];
|
|
|
|
sNano nano = player->Nanos[player->equippedNanos[request->iNanoSlot]];
|
|
|
|
|
|
|
|
//sanity check, check if gumball exists
|
|
|
|
if (!(gumball.iOpt > 0 && gumball.iType == 7 && gumball.iID>=119 && gumball.iID<=121)) {
|
|
|
|
std::cout << "[WARN] Gumball not found" << std::endl;
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_USE_FAIL, response);
|
|
|
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_ITEM_USE_FAIL, sizeof(sP_FE2CL_REP_PC_ITEM_USE_FAIL));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//sanity check, check if gumball type matches nano style
|
|
|
|
int nanoStyle = NanoManager::nanoStyle(nano.iID);
|
|
|
|
if (!((gumball.iID == 119 && nanoStyle == 0) ||
|
|
|
|
( gumball.iID == 120 && nanoStyle == 1) ||
|
|
|
|
( gumball.iID == 121 && nanoStyle == 2))) {
|
|
|
|
std::cout << "[WARN] Gumball type doesn't match nano type" << std::endl;
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_USE_FAIL, response);
|
|
|
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_ITEM_USE_FAIL, sizeof(sP_FE2CL_REP_PC_ITEM_USE_FAIL));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gumball.iOpt -= 1;
|
|
|
|
if (gumball.iOpt == 0)
|
|
|
|
gumball = {};
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_USE_SUCC, response);
|
|
|
|
response.iPC_ID = player->iID;
|
|
|
|
response.eIL = 1;
|
|
|
|
response.iSlotNum = request->iSlotNum;
|
|
|
|
response.RemainItem = gumball;
|
|
|
|
// response.iTargetCnt = ?
|
|
|
|
// response.eST = ?
|
|
|
|
// response.iSkillID = ?
|
|
|
|
|
|
|
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_ITEM_USE_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_USE_SUCC));
|
|
|
|
//update inventory serverside
|
|
|
|
player->Inven[response.iSlotNum] = response.RemainItem;
|
|
|
|
|
|
|
|
//this is a temporary way of calling buff efect
|
|
|
|
//TODO: send buff data via response packet
|
|
|
|
int value1 = CSB_BIT_STIMPAKSLOT1 << request->iNanoSlot;
|
|
|
|
int value2 = ECSB_STIMPAKSLOT1 + request->iNanoSlot;
|
|
|
|
|
|
|
|
NanoManager::nanoBuff(sock, nano.iID, 144, EST_NANOSTIMPAK, value1, value2, 0);
|
|
|
|
}
|
|
|
|
|
2020-09-09 20:42:55 +00:00
|
|
|
void ItemManager::itemBankOpenHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_BANK_OPEN))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// just send bank inventory
|
2020-09-09 20:42:55 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_BANK_OPEN_SUCC, resp);
|
|
|
|
for (int i = 0; i < ABANK_COUNT; i++) {
|
|
|
|
resp.aBank[i] = PlayerManager::players[sock].plr->Bank[i];
|
|
|
|
}
|
|
|
|
resp.iExtraBank = 1;
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_BANK_OPEN_SUCC, sizeof(sP_FE2CL_REP_PC_BANK_OPEN_SUCC));
|
|
|
|
}
|
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
void ItemManager::itemTradeOfferHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_OFFER* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER*)data->buf;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
2020-09-14 13:20:55 +00:00
|
|
|
break;
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[otherSock];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
if (plr.plr->isTrading) {
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
resp.iID_Request = pacdat->iID_To;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
return; // prevent trading with a player already trading
|
2020-08-27 02:35:13 +00:00
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp);
|
|
|
|
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeOfferAcceptHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT*)data->buf;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
2020-09-14 13:20:55 +00:00
|
|
|
break;
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp);
|
|
|
|
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
// Clearing up trade slots
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
|
|
|
PlayerView& plr2 = PlayerManager::players[otherSock];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
if (plr2.plr->isTrading) {
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
resp.iID_Request = pacdat->iID_To;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
return; // prevent trading with a player already trading
|
2020-08-27 02:35:13 +00:00
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTrading = true;
|
|
|
|
plr2.plr->isTrading = true;
|
|
|
|
plr.plr->isTradeConfirm = false;
|
|
|
|
plr2.plr->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
memset(&plr.plr->Trade, 0, sizeof(plr.plr->Trade));
|
|
|
|
memset(&plr2.plr->Trade, 0, sizeof(plr2.plr->Trade));
|
2020-08-26 17:40:10 +00:00
|
|
|
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeOfferRefusalHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL*)data->buf;
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
|
|
|
|
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
2020-09-14 13:20:55 +00:00
|
|
|
break;
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeConfirmHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_CONFIRM* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM*)data->buf;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 13:20:55 +00:00
|
|
|
for (auto& pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
2020-09-14 13:20:55 +00:00
|
|
|
break;
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
|
|
|
PlayerView& plr2 = PlayerManager::players[otherSock];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
if (plr2.plr->isTradeConfirm) {
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTrading = false;
|
|
|
|
plr2.plr->isTrading = false;
|
|
|
|
plr.plr->isTradeConfirm = false;
|
|
|
|
plr2.plr->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
// Check if we have enough free slots
|
|
|
|
int freeSlots = 0;
|
|
|
|
int freeSlotsNeeded = 0;
|
|
|
|
int freeSlots2 = 0;
|
|
|
|
int freeSlotsNeeded2 = 0;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
|
|
|
if (plr.plr->Inven[i].iID == 0)
|
|
|
|
freeSlots++;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
if (plr.plr->Trade[i].iID != 0)
|
|
|
|
freeSlotsNeeded++;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
|
|
|
if (plr2.plr->Inven[i].iID == 0)
|
|
|
|
freeSlots2++;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
if (plr2.plr->Trade[i].iID != 0)
|
|
|
|
freeSlotsNeeded2++;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (freeSlotsNeeded2 - freeSlotsNeeded > freeSlots) {
|
2020-08-27 02:35:13 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
|
2020-08-26 17:40:10 +00:00
|
|
|
return; // Fail trade because of the lack of slots
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (freeSlotsNeeded - freeSlotsNeeded2 > freeSlots2) {
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL));
|
|
|
|
return; // Fail trade because of the lack of slots
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
|
|
|
|
// ^^ this is a must have or else the player won't accept a succ packet for some reason
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < freeSlotsNeeded; i++) {
|
|
|
|
plr.plr->Inven[plr.plr->Trade[i].iInvenNum].iID = 0;
|
|
|
|
plr.plr->Inven[plr.plr->Trade[i].iInvenNum].iType = 0;
|
|
|
|
plr.plr->Inven[plr.plr->Trade[i].iInvenNum].iOpt = 0;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < freeSlotsNeeded2; i++) {
|
|
|
|
plr2.plr->Inven[plr2.plr->Trade[i].iInvenNum].iID = 0;
|
|
|
|
plr2.plr->Inven[plr2.plr->Trade[i].iInvenNum].iType = 0;
|
|
|
|
plr2.plr->Inven[plr2.plr->Trade[i].iInvenNum].iOpt = 0;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
|
|
|
if (freeSlotsNeeded <= 0)
|
|
|
|
break;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (plr2.plr->Inven[i].iID == 0) {
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
plr2.plr->Inven[i].iID = plr.plr->Trade[freeSlotsNeeded - 1].iID;
|
|
|
|
plr2.plr->Inven[i].iType = plr.plr->Trade[freeSlotsNeeded - 1].iType;
|
|
|
|
plr2.plr->Inven[i].iOpt = plr.plr->Trade[freeSlotsNeeded - 1].iOpt;
|
|
|
|
plr.plr->Trade[freeSlotsNeeded - 1].iInvenNum = i;
|
|
|
|
freeSlotsNeeded--;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < AINVEN_COUNT; i++) {
|
2020-08-26 17:40:10 +00:00
|
|
|
if (freeSlotsNeeded2 <= 0)
|
|
|
|
break;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (plr.plr->Inven[i].iID == 0) {
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
plr.plr->Inven[i].iID = plr2.plr->Trade[freeSlotsNeeded2 - 1].iID;
|
|
|
|
plr.plr->Inven[i].iType = plr2.plr->Trade[freeSlotsNeeded2 - 1].iType;
|
|
|
|
plr.plr->Inven[i].iOpt = plr2.plr->Trade[freeSlotsNeeded2 - 1].iOpt;
|
|
|
|
plr2.plr->Trade[freeSlotsNeeded2 - 1].iInvenNum = i;
|
|
|
|
freeSlotsNeeded2--;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, resp2);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
resp2.iID_Request = pacdat->iID_Request;
|
|
|
|
resp2.iID_From = pacdat->iID_From;
|
|
|
|
resp2.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
plr.plr->money = plr.plr->money + plr2.plr->moneyInTrade - plr.plr->moneyInTrade;
|
|
|
|
resp2.iCandy = plr.plr->money;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
memcpy(resp2.Item, plr2.plr->Trade, sizeof(plr2.plr->Trade));
|
|
|
|
memcpy(resp2.ItemStay, plr.plr->Trade, sizeof(plr.plr->Trade));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
plr2.plr->money = plr2.plr->money + plr.plr->moneyInTrade - plr2.plr->moneyInTrade;
|
|
|
|
resp2.iCandy = plr2.plr->money;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
memcpy(resp2.Item, plr.plr->Trade, sizeof(plr.plr->Trade));
|
|
|
|
memcpy(resp2.ItemStay, plr2.plr->Trade, sizeof(plr2.plr->Trade));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC));
|
|
|
|
} else {
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTradeConfirm = true;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeConfirmCancelHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL*)data->buf;
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, resp);
|
|
|
|
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (auto pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
2020-08-27 02:35:13 +00:00
|
|
|
PlayerView& plr2 = PlayerManager::players[otherSock];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTrading = false;
|
|
|
|
plr.plr->isTradeConfirm = false;
|
|
|
|
plr2.plr->isTrading = false;
|
|
|
|
plr2.plr->isTradeConfirm = false;
|
2020-08-26 17:40:10 +00:00
|
|
|
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeRegisterItemHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER*)data->buf;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4)
|
|
|
|
return; // sanity check
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, resp);
|
|
|
|
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
|
|
|
resp.TradeItem = pacdat->Item;
|
|
|
|
resp.InvenItem = pacdat->Item;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (auto pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
|
|
|
plr.plr->Trade[pacdat->Item.iSlotNum] = pacdat->Item;
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER*)data->buf;
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, resp);
|
|
|
|
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
|
|
|
resp.TradeItem = pacdat->Item;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
|
|
|
resp.InvenItem = plr.plr->Trade[pacdat->Item.iSlotNum];
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (auto pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int temp_num = pacdat->Item.iSlotNum;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (temp_num >= 0 && temp_num <= 4) {
|
|
|
|
plr.plr->Trade[temp_num].iID = 0;
|
|
|
|
plr.plr->Trade[temp_num].iType = 0;
|
|
|
|
plr.plr->Trade[temp_num].iOpt = 0;
|
|
|
|
plr.plr->Trade[temp_num].iInvenNum = 0;
|
|
|
|
plr.plr->Trade[temp_num].iSlotNum = 0;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER))
|
|
|
|
return; // ignore the malformed packet
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER*)data->buf;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
PlayerView& plr = PlayerManager::players[sock];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
if (pacdat->iCandy < 0 || pacdat->iCandy > plr.plr->money)
|
2020-08-26 17:40:10 +00:00
|
|
|
return; // famous glitch, begone
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, resp);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
|
|
|
resp.iCandy = pacdat->iCandy;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
2020-08-21 20:09:52 +00:00
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
for (auto pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-26 17:40:10 +00:00
|
|
|
otherSock = pair.first;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
plr.plr->moneyInTrade = pacdat->iCandy;
|
2020-08-27 02:35:13 +00:00
|
|
|
plr.plr->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-26 17:40:10 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC));
|
2020-08-24 22:02:07 +00:00
|
|
|
}
|
2020-08-27 02:35:13 +00:00
|
|
|
|
|
|
|
void ItemManager::itemTradeChatHandler(CNSocket* sock, CNPacketData* data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT))
|
|
|
|
return; // malformed packet
|
|
|
|
sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT* pacdat = (sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT*)data->buf;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT, resp);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
memcpy(resp.szFreeChat, pacdat->szFreeChat, sizeof(pacdat->szFreeChat));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
resp.iEmoteCode = pacdat->iEmoteCode;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
int iID_Check;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
if (pacdat->iID_Request == pacdat->iID_From) {
|
|
|
|
iID_Check = pacdat->iID_To;
|
|
|
|
} else {
|
|
|
|
iID_Check = pacdat->iID_From;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
CNSocket* otherSock = sock;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
for (auto pair : PlayerManager::players) {
|
2020-09-14 13:53:48 +00:00
|
|
|
if (pair.second.plr->iID == iID_Check) {
|
2020-08-27 02:35:13 +00:00
|
|
|
otherSock = pair.first;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT));
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT));
|
2020-08-28 16:25:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
|
|
|
|
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN))
|
|
|
|
return; // ignore the malformed packet
|
|
|
|
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
sP_CL2FE_REQ_ITEM_CHEST_OPEN *chest = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf;
|
|
|
|
Player *player = PlayerManager::getPlayer(sock);
|
2020-08-28 16:25:03 +00:00
|
|
|
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
if (player == nullptr)
|
2020-09-28 18:11:13 +00:00
|
|
|
return;
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
|
2020-08-28 16:25:03 +00:00
|
|
|
// item giving packet
|
|
|
|
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
|
2020-09-08 01:01:47 +00:00
|
|
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
2020-08-28 16:25:03 +00:00
|
|
|
// we know it's only one trailing struct, so we can skip full validation
|
|
|
|
|
|
|
|
uint8_t respbuf[resplen]; // not a variable length array, don't worry
|
|
|
|
sP_FE2CL_REP_REWARD_ITEM *reward = (sP_FE2CL_REP_REWARD_ITEM *)respbuf;
|
|
|
|
sItemReward *item = (sItemReward *)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
|
|
|
|
|
|
|
|
// don't forget to zero the buffer!
|
|
|
|
memset(respbuf, 0, resplen);
|
|
|
|
|
2020-09-09 21:00:13 +00:00
|
|
|
// maintain stats
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
reward->m_iCandy = player->money;
|
|
|
|
reward->m_iFusionMatter = player->fusionmatter;
|
2020-08-28 16:25:03 +00:00
|
|
|
reward->iFatigue = 100; // prevents warning message
|
|
|
|
reward->iFatigue_Level = 1;
|
|
|
|
reward->iItemCnt = 1; // remember to update resplen if you change this
|
|
|
|
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
// item reward
|
|
|
|
if (chest->ChestItem.iType != 9)
|
|
|
|
std::cout << "[WARN] Player tried to open a crate with incorrect iType ?!" << std::endl;
|
|
|
|
else
|
|
|
|
item->sItem = openCrate(chest->ChestItem.iID, player->PCStyle.iGender);
|
|
|
|
|
|
|
|
item->iSlotNum = chest->iSlotNum;
|
|
|
|
item->eIL = chest->eIL;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
|
|
|
// update player
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
player->Inven[chest->iSlotNum] = item->sItem;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
|
|
|
// transmit item
|
|
|
|
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
|
|
|
|
|
|
|
|
// chest opening acknowledgement packet
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
|
|
|
|
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
resp.iSlotNum = chest->iSlotNum;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
|
|
|
std::cout << "opening chest..." << std::endl;
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC));
|
|
|
|
}
|
|
|
|
|
functional crates (no more plungers) (#133)
* FM, Taros and Boosts awards from killing mobs should be pretty
accurate now. A temporary formula for adjusting player/mob level gap is
implemented, but it will probably need to be adjusted in the future
* Mobs now drop correct crates
* Crates can be opened and give you correct items This includes
regular mob crates, world boss crates, mission crates, IZ race crates,
E.G.G.E.R.s, golden Eggs, and Event Crates. Keep in mind that neither
IZ races or golden Eggs are implemented, but if you spawn such a crate
it can be opened.
* All data is read from a json file, for which I'm going to release a
tool soon so it's easily adjustable
* There is a new setting for enabling events, which enables dropping
extra event crates These are Knishmas, Halloween and Easter
2020-10-10 17:18:47 +00:00
|
|
|
sItemBase ItemManager::openCrate(int crateId, int playerGender) {
|
|
|
|
sItemBase reward = {};
|
|
|
|
try {
|
|
|
|
Crate crate = getCrate(crateId);
|
|
|
|
int itemSetId = getItemSetId(crate, crateId);
|
|
|
|
int rarity = getRarity(crate, itemSetId);
|
|
|
|
reward = getCrateItem(itemSetId, rarity, playerGender);
|
|
|
|
}
|
|
|
|
catch (const std::exception& err) {
|
|
|
|
std::cerr << "[WARN] An error has occured while trying to open a crate. Error description: \n" << err.what() << std::endl;
|
|
|
|
// if we failed to open a crate, at least give the player a gumball (suggested by Jade)
|
|
|
|
reward.iType = 7;
|
|
|
|
reward.iID = 119 + (rand() % 3);
|
|
|
|
reward.iOpt = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return reward;
|
|
|
|
}
|
|
|
|
|
|
|
|
Crate ItemManager::getCrate(int crateId) {
|
|
|
|
if (Crates.find(crateId) == Crates.end())
|
|
|
|
throwError(sprintf(buffer, "Crate %d was not found!", crateId));
|
|
|
|
return Crates[crateId];
|
|
|
|
}
|
|
|
|
|
|
|
|
int ItemManager::getItemSetId(Crate crate, int crateId) {
|
|
|
|
int itemSetsCount = crate.itemSets.size();
|
|
|
|
if (itemSetsCount == 0)
|
|
|
|
throwError(sprintf(buffer, "Crate %d has no item sets assigned?!", crateId));
|
|
|
|
|
|
|
|
// if crate points to multiple itemSets, choose a random one
|
|
|
|
int itemSetIndex = rand() % itemSetsCount;
|
|
|
|
return crate.itemSets[itemSetIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
int ItemManager::getRarity(Crate crate , int itemSetId) {
|
|
|
|
// find rarity ratio
|
|
|
|
if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end())
|
|
|
|
throwError(sprintf(buffer, "Rarity Ratio %d not found!", crate.rarityRatioId));
|
|
|
|
|
|
|
|
std::vector<int> rarityRatio = RarityRatios[crate.rarityRatioId];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First we have to check if specified item set contains items with all specified rarities,
|
|
|
|
* and if not eliminate them from the draw
|
|
|
|
* it is simpler to do here than to fix individually in the file
|
|
|
|
*/
|
|
|
|
|
|
|
|
// remember that rarities start from 1 !
|
|
|
|
for (int i = 0; i < rarityRatio.size(); i++){
|
|
|
|
if (CrateItems.find(std::make_pair(itemSetId, i+1)) == CrateItems.end())
|
|
|
|
rarityRatio[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int total = 0;
|
|
|
|
for (int value : rarityRatio)
|
|
|
|
total += value;
|
|
|
|
|
|
|
|
// if we didn't find any items, throw exception
|
|
|
|
if (total == 0)
|
|
|
|
throwError(sprintf(buffer, "Item Set %d has no items assigned?!", itemSetId));
|
|
|
|
|
|
|
|
// now return a random rarity number
|
|
|
|
int randomNum = rand() % total;
|
|
|
|
int rarity = 0;
|
|
|
|
int sum = 0;
|
|
|
|
do {
|
|
|
|
sum += rarityRatio[rarity];
|
|
|
|
rarity++;
|
|
|
|
} while (sum <= randomNum);
|
|
|
|
|
|
|
|
return rarity;
|
|
|
|
}
|
|
|
|
|
|
|
|
sItemBase ItemManager::getCrateItem(int itemSetId, int rarity, int playerGender) {
|
|
|
|
std::pair key = std::make_pair(itemSetId, rarity);
|
|
|
|
|
|
|
|
if (CrateItems.find(key) == CrateItems.end())
|
|
|
|
throwError(sprintf(buffer, "Item Set ID %d Rarity %d items have not been found", itemSetId, rarity));
|
|
|
|
|
|
|
|
// only take into account items that have correct gender
|
|
|
|
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator> items;
|
|
|
|
for (auto crateitem : CrateItems[key])
|
|
|
|
{
|
|
|
|
int gender = crateitem->second.gender;
|
|
|
|
// if gender is incorrect, exclude item
|
|
|
|
if (gender != 0 && gender != playerGender)
|
|
|
|
continue;
|
|
|
|
items.push_back(crateitem);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (items.size() == 0)
|
|
|
|
throwError(sprintf(buffer, "Gender inequality! Set ID %d Rarity %d contains only %s items?!", itemSetId, rarity, playerGender==2 ? "boys" : "girls"));
|
|
|
|
auto item = items[rand() % items.size()];
|
|
|
|
sItemBase result = {};
|
|
|
|
result.iID = item->first.first;
|
|
|
|
result.iType = item->first.second;
|
|
|
|
result.iOpt = 1;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// argument is here only so we can call sprintf in brackets
|
|
|
|
void ItemManager::throwError(int ignore) {
|
|
|
|
throw buffer;
|
|
|
|
}
|
|
|
|
|
2020-08-28 16:25:03 +00:00
|
|
|
// TODO: use this in cleaned up ItemManager
|
|
|
|
int ItemManager::findFreeSlot(Player *plr) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < AINVEN_COUNT; i++)
|
2020-09-12 00:25:45 +00:00
|
|
|
if (plr->Inven[i].iType == 0 && plr->Inven[i].iID == 0 && plr->Inven[i].iOpt == 0)
|
2020-08-28 16:25:03 +00:00
|
|
|
return i;
|
|
|
|
|
|
|
|
// not found
|
|
|
|
return -1;
|
|
|
|
}
|
2020-09-14 04:25:14 +00:00
|
|
|
|
2020-09-14 14:27:52 +00:00
|
|
|
Item* ItemManager::getItemData(int32_t id, int32_t type) {
|
|
|
|
if(ItemData.find(std::pair<int32_t, int32_t>(id, type)) != ItemData.end())
|
|
|
|
return &ItemData[std::pair<int32_t, int32_t>(id, type)];
|
|
|
|
return nullptr;
|
2020-09-14 04:25:14 +00:00
|
|
|
}
|
2020-09-22 11:16:09 +00:00
|
|
|
|
|
|
|
void ItemManager::checkItemExpire(CNSocket* sock, Player* player) {
|
|
|
|
if (player->toRemoveVehicle.eIL == 0 && player->toRemoveVehicle.iSlotNum == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* prepare packet
|
|
|
|
* yes, this is a varadic packet, however analyzing client behavior and code
|
|
|
|
* it only checks takes the first item sent into account
|
|
|
|
* yes, this is very stupid
|
|
|
|
* therefore, we delete all but 1 expired vehicle while loading player
|
|
|
|
* to delete the last one here so player gets a notification
|
|
|
|
*/
|
|
|
|
|
|
|
|
const size_t resplen = sizeof(sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM) + sizeof(sTimeLimitItemDeleteInfo2CL);
|
|
|
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
|
|
|
// we know it's only one trailing struct, so we can skip full validation
|
2020-10-05 00:03:13 +00:00
|
|
|
uint8_t respbuf[resplen]; // not a variable length array, don't worry
|
2020-09-22 11:16:09 +00:00
|
|
|
sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM* packet = (sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM*)respbuf;
|
|
|
|
sTimeLimitItemDeleteInfo2CL* itemData = (sTimeLimitItemDeleteInfo2CL*)(respbuf + sizeof(sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM));
|
|
|
|
memset(respbuf, 0, resplen);
|
|
|
|
|
|
|
|
packet->iItemListCount = 1;
|
|
|
|
itemData->eIL = player->toRemoveVehicle.eIL;
|
|
|
|
itemData->iSlotNum = player->toRemoveVehicle.iSlotNum;
|
|
|
|
sock->sendPacket((void*)&respbuf, P_FE2CL_PC_DELETE_TIME_LIMIT_ITEM, resplen);
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-09-23 09:21:32 +00:00
|
|
|
// delete serverside
|
2020-09-22 11:16:09 +00:00
|
|
|
if (player->toRemoveVehicle.eIL == 0)
|
|
|
|
memset(&player->Equip[8], 0, sizeof(sItemBase));
|
|
|
|
else
|
|
|
|
memset(&player->Inven[player->toRemoveVehicle.iSlotNum], 0, sizeof(sItemBase));
|
2020-09-23 09:21:32 +00:00
|
|
|
|
|
|
|
player->toRemoveVehicle.eIL = 0;
|
|
|
|
player->toRemoveVehicle.iSlotNum = 0;
|
2020-09-22 11:16:09 +00:00
|
|
|
}
|
2020-09-27 01:53:03 +00:00
|
|
|
|
|
|
|
void ItemManager::setItemStats(Player* plr) {
|
|
|
|
|
|
|
|
plr->pointDamage = 8 + plr->level * 2;
|
|
|
|
plr->groupDamage = 8 + plr->level * 2;
|
|
|
|
plr->defense = 16 + plr->level * 4;
|
|
|
|
|
|
|
|
Item* itemStatsDat;
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
itemStatsDat = ItemManager::getItemData(plr->Equip[i].iID, plr->Equip[i].iType);
|
2020-10-14 21:15:02 +00:00
|
|
|
if (itemStatsDat == nullptr) {
|
|
|
|
std::cout << "[WARN] setItemStats(): getItemData() returned NULL" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-27 01:53:03 +00:00
|
|
|
plr->pointDamage += itemStatsDat->pointDamage;
|
|
|
|
plr->groupDamage += itemStatsDat->groupDamage;
|
|
|
|
plr->defense += itemStatsDat->defense;
|
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
}
|
2020-10-04 23:54:08 +00:00
|
|
|
|
|
|
|
// HACK: work around the invisible weapon bug
|
|
|
|
void ItemManager::updateEquips(CNSocket* sock, Player* plr) {
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, resp);
|
|
|
|
|
|
|
|
resp.iPC_ID = plr->iID;
|
|
|
|
resp.iEquipSlotNum = i;
|
|
|
|
resp.EquipSlotItem = plr->Equip[i];
|
2020-10-05 00:03:13 +00:00
|
|
|
|
2020-10-04 23:54:08 +00:00
|
|
|
PlayerManager::sendToViewable(sock, (void*)&resp, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE));
|
|
|
|
}
|
2020-10-05 00:03:13 +00:00
|
|
|
}
|