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-11-13 23:06:18 +00:00
|
|
|
#include "NPCManager.hpp"
|
2020-08-21 02:10:14 +00:00
|
|
|
#include "Player.hpp"
|
2020-12-31 01:13:43 +00:00
|
|
|
#include "ChatManager.hpp"
|
2020-08-21 02:10:14 +00:00
|
|
|
|
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-12-04 18:57:08 +00:00
|
|
|
std::map<std::pair<int32_t, int32_t>, ItemManager::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;
|
2020-10-19 17:26:14 +00:00
|
|
|
// pair Itemset, Rarity -> vector of pointers (map iterators) to records in ItemData
|
2020-12-04 18:57:08 +00:00
|
|
|
std::map<std::pair<int32_t, int32_t>, std::vector<std::map<std::pair<int32_t, int32_t>, ItemManager::Item>::iterator>> ItemManager::CrateItems;
|
2020-12-21 22:01:21 +00:00
|
|
|
std::map<std::string, std::vector<std::pair<int32_t, int32_t>>> ItemManager::CodeItems;
|
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-12-16 15:27:18 +00:00
|
|
|
#ifdef ACADEMY
|
|
|
|
std::map<int32_t, int32_t> ItemManager::NanoCapsules; // crate id -> nano id
|
|
|
|
#endif
|
|
|
|
|
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-10-17 23:19:05 +00:00
|
|
|
// this one is for gumballs
|
2020-09-25 05:35:27 +00:00
|
|
|
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
|
2021-01-01 07:16:44 +00:00
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, tradeOffer);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, tradeOfferAccept);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, tradeOfferRefusal);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, tradeConfirm);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, tradeConfirmCancel);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, tradeRegisterItem);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, tradeUnregisterItem);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, tradeRegisterCash);
|
|
|
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, tradeChat);
|
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-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// sanity check
|
2021-01-05 12:17:59 +00:00
|
|
|
if (itemmove->iToSlotNum < 0 || itemmove->iFromSlotNum < 0)
|
2020-09-14 13:53:48 +00:00
|
|
|
return;
|
2021-01-05 12:17:59 +00:00
|
|
|
// NOTE: sending a no-op, "move in-place" packet is not necessary
|
2020-09-14 13:53:48 +00:00
|
|
|
|
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) {
|
2021-01-05 12:17:59 +00:00
|
|
|
case SlotType::EQUIP:
|
|
|
|
if (itemmove->iFromSlotNum >= AEQUIP_COUNT)
|
2020-09-21 19:43:53 +00:00
|
|
|
return;
|
2021-01-05 12:17:59 +00:00
|
|
|
|
|
|
|
fromItem = &plr->Equip[itemmove->iFromSlotNum];
|
|
|
|
break;
|
|
|
|
case SlotType::INVENTORY:
|
|
|
|
if (itemmove->iFromSlotNum >= AINVEN_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fromItem = &plr->Inven[itemmove->iFromSlotNum];
|
|
|
|
break;
|
|
|
|
case SlotType::BANK:
|
|
|
|
if (itemmove->iFromSlotNum >= ABANK_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fromItem = &plr->Bank[itemmove->iFromSlotNum];
|
|
|
|
break;
|
|
|
|
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:
|
2021-01-05 12:17:59 +00:00
|
|
|
if (itemmove->iToSlotNum >= AEQUIP_COUNT)
|
|
|
|
return;
|
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
toItem = &plr->Equip[itemmove->iToSlotNum];
|
2020-09-09 20:42:55 +00:00
|
|
|
break;
|
2020-09-14 13:20:55 +00:00
|
|
|
case SlotType::INVENTORY:
|
2021-01-05 12:17:59 +00:00
|
|
|
if (itemmove->iToSlotNum >= AINVEN_COUNT)
|
|
|
|
return;
|
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
toItem = &plr->Inven[itemmove->iToSlotNum];
|
2020-09-09 20:42:55 +00:00
|
|
|
break;
|
2020-12-03 23:58:10 +00:00
|
|
|
case SlotType::BANK:
|
2021-01-05 12:17:59 +00:00
|
|
|
if (itemmove->iToSlotNum >= ABANK_COUNT)
|
|
|
|
return;
|
|
|
|
|
2020-12-03 23:58:10 +00:00
|
|
|
toItem = &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
|
|
|
|
2021-01-05 12:17:59 +00:00
|
|
|
// if equipping an item, validate that it's of the correct type for the slot
|
|
|
|
if ((SlotType)itemmove->eTo == SlotType::EQUIP) {
|
|
|
|
if (fromItem->iType == 10 && itemmove->iToSlotNum != 8)
|
|
|
|
return; // vehicle in wrong slot
|
|
|
|
else if (fromItem->iType != 10
|
|
|
|
&& !(fromItem->iType == 0 && itemmove->iToSlotNum == 7)
|
|
|
|
&& fromItem->iType != itemmove->iToSlotNum)
|
|
|
|
return; // something other than a vehicle or a weapon in a non-matching slot
|
|
|
|
else if (itemmove->iToSlotNum >= AEQUIP_COUNT) // TODO: reject slots >= 9?
|
|
|
|
return; // invalid slot
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:03:30 +00:00
|
|
|
// save items to response
|
2021-01-05 12:17:59 +00:00
|
|
|
resp.eTo = itemmove->eFrom;
|
|
|
|
resp.eFrom = itemmove->eTo;
|
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-11-17 23:16:16 +00:00
|
|
|
equipChange.iPC_ID = plr->iID;
|
2020-10-21 05:24:51 +00:00
|
|
|
if (itemmove->eTo == (int)SlotType::EQUIP) {
|
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-10-21 05:24:51 +00:00
|
|
|
} else {
|
|
|
|
equipChange.iEquipSlotNum = itemmove->iFromSlotNum;
|
|
|
|
equipChange.EquipSlotItem = resp.ToSlotItem;
|
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
|
2020-11-17 23:16:16 +00:00
|
|
|
if (plr->Equip[8].iID == 0)
|
|
|
|
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
|
2020-11-17 23:16:16 +00:00
|
|
|
setItemStats(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
|
|
|
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
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
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-11-17 23:16:16 +00:00
|
|
|
plr->Inven[itemdel->iSlotNum].iID = 0;
|
|
|
|
plr->Inven[itemdel->iSlotNum].iType = 0;
|
|
|
|
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;
|
2020-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
if (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-11-17 23:16:16 +00:00
|
|
|
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-12-01 19:18:01 +00:00
|
|
|
if (request->iSlotNum < 0 || request->iSlotNum >= AINVEN_COUNT)
|
2020-10-25 22:33:02 +00:00
|
|
|
return; // sanity check
|
2020-09-28 18:11:13 +00:00
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
// gumball can only be used from inventory, so we ignore eIL
|
2020-09-25 05:35:27 +00:00
|
|
|
sItemBase gumball = player->Inven[request->iSlotNum];
|
|
|
|
sNano nano = player->Nanos[player->equippedNanos[request->iNanoSlot]];
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
// sanity check, check if gumball exists
|
2020-09-25 05:35:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-10-19 17:26:14 +00:00
|
|
|
// sanity check, check if gumball type matches nano style
|
2020-09-25 05:35:27 +00:00
|
|
|
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 = {};
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
size_t resplen = sizeof(sP_FE2CL_REP_PC_ITEM_USE_SUCC) + sizeof(sSkillResult_Buff);
|
|
|
|
|
2020-11-23 23:42:34 +00:00
|
|
|
// validate response packet
|
2020-11-08 00:26:44 +00:00
|
|
|
if (!validOutVarPacket(sizeof(sP_FE2CL_REP_PC_ITEM_USE_SUCC), 1, sizeof(sSkillResult_Buff))) {
|
|
|
|
std::cout << "[WARN] bad sP_FE2CL_REP_PC_ITEM_USE_SUCC packet size" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-13 23:06:18 +00:00
|
|
|
if (gumball.iOpt == 0)
|
|
|
|
gumball = {};
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
|
|
|
memset(respbuf, 0, resplen);
|
|
|
|
|
|
|
|
sP_FE2CL_REP_PC_ITEM_USE_SUCC *resp = (sP_FE2CL_REP_PC_ITEM_USE_SUCC*)respbuf;
|
|
|
|
sSkillResult_Buff *respdata = (sSkillResult_Buff*)(respbuf+sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC));
|
|
|
|
resp->iPC_ID = player->iID;
|
|
|
|
resp->eIL = 1;
|
|
|
|
resp->iSlotNum = request->iSlotNum;
|
|
|
|
resp->RemainItem = gumball;
|
|
|
|
resp->iTargetCnt = 1;
|
|
|
|
resp->eST = EST_NANOSTIMPAK;
|
2020-11-17 03:30:01 +00:00
|
|
|
resp->iSkillID = 144;
|
2020-09-25 05:35:27 +00:00
|
|
|
|
|
|
|
int value1 = CSB_BIT_STIMPAKSLOT1 << request->iNanoSlot;
|
|
|
|
int value2 = ECSB_STIMPAKSLOT1 + request->iNanoSlot;
|
|
|
|
|
2020-11-08 00:26:44 +00:00
|
|
|
respdata->eCT = 1;
|
|
|
|
respdata->iID = player->iID;
|
|
|
|
respdata->iConditionBitFlag = value1;
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt);
|
|
|
|
pkt.eCSTB = value2; // eCharStatusTimeBuffID
|
|
|
|
pkt.eTBU = 1; // eTimeBuffUpdate
|
|
|
|
pkt.eTBT = 1; // eTimeBuffType 1 means nano
|
|
|
|
pkt.iConditionBitFlag = player->iConditionBitFlag |= value1;
|
|
|
|
sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
|
|
|
|
|
|
|
sock->sendPacket((void*)&respbuf, P_FE2CL_REP_PC_ITEM_USE_SUCC, resplen);
|
|
|
|
// update inventory serverside
|
|
|
|
player->Inven[resp->iSlotNum] = resp->RemainItem;
|
2020-11-13 23:06:18 +00:00
|
|
|
|
2020-11-17 03:30:01 +00:00
|
|
|
std::pair<CNSocket*, int32_t> key = std::make_pair(sock, value1);
|
2020-12-01 18:47:54 +00:00
|
|
|
time_t until = getTime() + (time_t)NanoManager::SkillTable[144].durationTime[0] * 100;
|
2020-11-13 23:06:18 +00:00
|
|
|
NPCManager::EggBuffs[key] = until;
|
2020-09-25 05:35:27 +00:00
|
|
|
}
|
|
|
|
|
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-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
|
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++) {
|
2020-12-03 23:58:10 +00:00
|
|
|
resp.aBank[i] = plr->Bank[i];
|
2020-09-09 20:42:55 +00:00
|
|
|
}
|
|
|
|
resp.iExtraBank = 1;
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_BANK_OPEN_SUCC, sizeof(sP_FE2CL_REP_PC_BANK_OPEN_SUCC));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeOffer(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(otherSock);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
if (plr->isTrading) {
|
2020-08-27 02:35:13 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
|
|
|
|
resp.iID_Request = pacdat->iID_To;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
|
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;
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeOfferAccept(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
Player* plr2 = PlayerManager::getPlayer(otherSock);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
if (plr2->isTrading) {
|
2020-08-27 02:35:13 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
|
2021-01-01 07:16:44 +00:00
|
|
|
resp.iID_Request = pacdat->iID_From;
|
2020-08-27 02:35:13 +00:00
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
|
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
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// clearing up trade slots
|
|
|
|
plr->moneyInTrade = 0;
|
|
|
|
plr2->moneyInTrade = 0;
|
|
|
|
memset(&plr->Trade, 0, sizeof(plr->Trade));
|
|
|
|
memset(&plr2->Trade, 0, sizeof(plr2->Trade));
|
|
|
|
|
|
|
|
// marking players as traders
|
2020-11-17 23:16:16 +00:00
|
|
|
plr->isTrading = true;
|
|
|
|
plr2->isTrading = true;
|
2021-01-01 07:16:44 +00:00
|
|
|
|
|
|
|
// marking players as unconfirmed
|
2020-11-17 23:16:16 +00:00
|
|
|
plr->isTradeConfirm = false;
|
|
|
|
plr2->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// inform the other player that offer is accepted
|
|
|
|
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-08-26 17:40:10 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC));
|
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));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeOfferRefusal(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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;
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
|
|
|
|
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
|
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;
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeConfirm(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock; // weird flip flop because we need to know who the other player is
|
|
|
|
if (pacdat->iID_Request == pacdat->iID_From)
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
|
|
|
else
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
Player* plr2 = PlayerManager::getPlayer(otherSock);
|
2021-01-01 07:16:44 +00:00
|
|
|
|
|
|
|
if (!(plr->isTrading && plr2->isTrading)) { // both players must be trading
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp);
|
|
|
|
resp.iID_Request = plr2->iID;
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2021-01-01 07:16:44 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
|
|
|
|
resp.iID_Request = plr->iID;
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
|
|
|
|
return;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// send the confirm packet
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp);
|
|
|
|
resp.iID_Request = pacdat->iID_Request;
|
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (!(plr2->isTradeConfirm)) {
|
|
|
|
plr->isTradeConfirm = true;
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
|
|
|
|
return;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// both players are no longer trading
|
|
|
|
plr->isTrading = false;
|
|
|
|
plr2->isTrading = false;
|
|
|
|
plr->isTradeConfirm = false;
|
|
|
|
plr2->isTradeConfirm = false;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (doTrade(plr, plr2)) { // returns false if not enough slots
|
2020-08-26 17:40:10 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, resp2);
|
|
|
|
resp2.iID_Request = pacdat->iID_Request;
|
|
|
|
resp2.iID_From = pacdat->iID_From;
|
|
|
|
resp2.iID_To = pacdat->iID_To;
|
2020-11-17 23:16:16 +00:00
|
|
|
plr->money = plr->money + plr2->moneyInTrade - plr->moneyInTrade;
|
|
|
|
resp2.iCandy = plr->money;
|
|
|
|
memcpy(resp2.Item, plr2->Trade, sizeof(plr2->Trade));
|
|
|
|
memcpy(resp2.ItemStay, plr->Trade, sizeof(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-11-17 23:16:16 +00:00
|
|
|
plr2->money = plr2->money + plr->moneyInTrade - plr2->moneyInTrade;
|
|
|
|
resp2.iCandy = plr2->money;
|
|
|
|
memcpy(resp2.Item, plr->Trade, sizeof(plr->Trade));
|
|
|
|
memcpy(resp2.ItemStay, plr2->Trade, sizeof(plr2->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 {
|
2021-01-01 07:16:44 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp);
|
|
|
|
resp.iID_Request = plr2->iID;
|
2020-08-26 17:40:10 +00:00
|
|
|
resp.iID_From = pacdat->iID_From;
|
|
|
|
resp.iID_To = pacdat->iID_To;
|
2021-01-01 07:16:44 +00:00
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
|
|
|
|
resp.iID_Request = plr->iID;
|
|
|
|
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
|
|
|
|
return;
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
bool ItemManager::doTrade(Player* plr, Player* plr2) {
|
|
|
|
// init dummy inventories
|
|
|
|
sItemBase plrInven[AINVEN_COUNT];
|
|
|
|
sItemBase plr2Inven[AINVEN_COUNT];
|
|
|
|
memcpy(plrInven, plr->Inven, AINVEN_COUNT * sizeof(sItemBase));
|
|
|
|
memcpy(plr2Inven, plr2->Inven, AINVEN_COUNT * sizeof(sItemBase));
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
// remove items offered by us
|
|
|
|
if (plr->Trade[i].iID != 0) {
|
|
|
|
if (plrInven[plr->Trade[i].iInvenNum].iID == 0) // pulling a fast one on us
|
|
|
|
return false;
|
|
|
|
|
2021-01-17 21:50:49 +00:00
|
|
|
if (plr->Trade[i].iOpt < 1) {
|
|
|
|
std::cout << "[WARN] Player tried trading an iOpt < 1 amount" << std::endl;
|
|
|
|
plr->Trade[i].iOpt = 1;
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// for stacked items
|
|
|
|
plrInven[plr->Trade[i].iInvenNum].iOpt -= plr->Trade[i].iOpt;
|
|
|
|
|
|
|
|
if (plrInven[plr->Trade[i].iInvenNum].iOpt == 0) {
|
|
|
|
plrInven[plr->Trade[i].iInvenNum].iID = 0;
|
|
|
|
plrInven[plr->Trade[i].iInvenNum].iType = 0;
|
|
|
|
plrInven[plr->Trade[i].iInvenNum].iOpt = 0;
|
|
|
|
} else if (plrInven[plr->Trade[i].iInvenNum].iOpt < 0) { // another dupe attempt
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-08-26 17:40:10 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (plr2->Trade[i].iID != 0) {
|
|
|
|
if (plr2Inven[plr2->Trade[i].iInvenNum].iID == 0) // pulling a fast one on us
|
|
|
|
return false;
|
2020-08-26 17:40:10 +00:00
|
|
|
|
2021-01-17 21:50:49 +00:00
|
|
|
if (plr2->Trade[i].iOpt < 1) {
|
|
|
|
std::cout << "[WARN] Player tried trading an iOpt < 1 amount" << std::endl;
|
|
|
|
plr2->Trade[i].iOpt = 1;
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// for stacked items
|
|
|
|
plr2Inven[plr2->Trade[i].iInvenNum].iOpt -= plr2->Trade[i].iOpt;
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (plr2Inven[plr2->Trade[i].iInvenNum].iOpt == 0) {
|
|
|
|
plr2Inven[plr2->Trade[i].iInvenNum].iID = 0;
|
|
|
|
plr2Inven[plr2->Trade[i].iInvenNum].iType = 0;
|
|
|
|
plr2Inven[plr2->Trade[i].iInvenNum].iOpt = 0;
|
|
|
|
} else if (plr2Inven[plr2->Trade[i].iInvenNum].iOpt < 0) { // another dupe attempt
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// add items offered to us
|
|
|
|
if (plr2->Trade[i].iID != 0) {
|
|
|
|
for (int n = 0; n < AINVEN_COUNT; n++) {
|
|
|
|
if (plrInven[n].iID == 0) {
|
|
|
|
plrInven[n].iID = plr2->Trade[i].iID;
|
|
|
|
plrInven[n].iType = plr2->Trade[i].iType;
|
|
|
|
plrInven[n].iOpt = plr2->Trade[i].iOpt;
|
|
|
|
plr2->Trade[i].iInvenNum = n;
|
|
|
|
break;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (n >= AINVEN_COUNT - 1)
|
|
|
|
return false; // not enough space
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plr->Trade[i].iID != 0) {
|
|
|
|
for (int n = 0; n < AINVEN_COUNT; n++) {
|
|
|
|
if (plr2Inven[n].iID == 0) {
|
|
|
|
plr2Inven[n].iID = plr->Trade[i].iID;
|
|
|
|
plr2Inven[n].iType = plr->Trade[i].iType;
|
|
|
|
plr2Inven[n].iOpt = plr->Trade[i].iOpt;
|
|
|
|
plr->Trade[i].iInvenNum = n;
|
|
|
|
break;
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (n >= AINVEN_COUNT - 1)
|
|
|
|
return false; // not enough space
|
|
|
|
}
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// if everything went well, back into player inventory it goes
|
|
|
|
memcpy(plr->Inven, plrInven, AINVEN_COUNT * sizeof(sItemBase));
|
|
|
|
memcpy(plr2->Inven, plr2Inven, AINVEN_COUNT * sizeof(sItemBase));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ItemManager::tradeConfirmCancel(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;
|
|
|
|
|
|
|
|
CNSocket* otherSock; // weird flip flop because we need to know who the other player is
|
|
|
|
if (pacdat->iID_Request == pacdat->iID_From)
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
|
|
|
else
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
|
|
|
|
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
Player* plr2 = PlayerManager::getPlayer(otherSock);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// both players are not trading nor are in a confirmed state
|
2020-11-17 23:16:16 +00:00
|
|
|
plr->isTrading = false;
|
|
|
|
plr->isTradeConfirm = false;
|
|
|
|
plr2->isTrading = false;
|
|
|
|
plr2->isTradeConfirm = false;
|
2020-08-26 17:40:10 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
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-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));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeRegisterItem(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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)
|
2021-01-01 07:16:44 +00:00
|
|
|
return; // sanity check, there are only 5 trade slots
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock; // weird flip flop because we need to know who the other player is
|
|
|
|
if (pacdat->iID_Request == pacdat->iID_From)
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
|
|
|
else
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
2020-08-26 17:40:10 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
plr->Trade[pacdat->Item.iSlotNum] = pacdat->Item;
|
|
|
|
plr->isTradeConfirm = false;
|
|
|
|
|
|
|
|
// since you can spread items like gumballs over multiple slots, we need to count them all
|
|
|
|
// to make sure the inventory shows the right value during trade.
|
|
|
|
int count = 0;
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
if (plr->Trade[i].iInvenNum == pacdat->Item.iInvenNum)
|
|
|
|
count += plr->Trade[i].iOpt;
|
|
|
|
}
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, resp);
|
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.TradeItem = pacdat->Item;
|
|
|
|
resp.InvenItem = pacdat->Item;
|
2021-01-01 07:16:44 +00:00
|
|
|
resp.InvenItem.iOpt = plr->Inven[pacdat->Item.iInvenNum].iOpt - count; // subtract this count
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (resp.InvenItem.iOpt < 0) // negative count items, doTrade() will block this later on
|
|
|
|
std::cout << "[WARN] tradeRegisterItem: an item went negative count client side." << std::endl;
|
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));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeUnregisterItem(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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;
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4)
|
|
|
|
return; // sanity check, there are only 5 trade slots
|
|
|
|
|
|
|
|
CNSocket* otherSock; // weird flip flop because we need to know who the other player is
|
|
|
|
if (pacdat->iID_Request == pacdat->iID_From)
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
|
|
|
else
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
|
|
|
|
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
plr->isTradeConfirm = false;
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, resp);
|
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.TradeItem = pacdat->Item;
|
2020-11-17 23:16:16 +00:00
|
|
|
resp.InvenItem = plr->Trade[pacdat->Item.iSlotNum];
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
memset(&plr->Trade[pacdat->Item.iSlotNum], 0, sizeof(plr->Trade[pacdat->Item.iSlotNum])); // clean up item slot
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
// since you can spread items like gumballs over multiple slots, we need to count them all
|
|
|
|
// to make sure the inventory shows the right value during trade.
|
|
|
|
int count = 0;
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
if (plr->Trade[i].iInvenNum == resp.InvenItem.iInvenNum)
|
|
|
|
count += plr->Trade[i].iOpt;
|
2020-08-26 17:40:10 +00:00
|
|
|
}
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
resp.InvenItem.iOpt = plr->Inven[resp.InvenItem.iInvenNum].iOpt - count; // subtract this count
|
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));
|
|
|
|
}
|
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeRegisterCash(CNSocket* sock, CNPacketData* data) {
|
2020-08-26 17:40:10 +00:00
|
|
|
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-11-17 23:16:16 +00:00
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
if (pacdat->iCandy < 0 || pacdat->iCandy > plr->money)
|
2020-08-26 17:40:10 +00:00
|
|
|
return; // famous glitch, begone
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock; // weird flip flop because we need to know who the other player is
|
|
|
|
if (pacdat->iID_Request == pacdat->iID_From)
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
|
|
|
else
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
2020-08-21 20:09:52 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, resp);
|
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;
|
2021-01-01 07:16:44 +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-09-14 13:53:48 +00:00
|
|
|
|
2020-11-17 23:16:16 +00:00
|
|
|
plr->moneyInTrade = pacdat->iCandy;
|
|
|
|
plr->isTradeConfirm = false;
|
2020-08-24 22:02:07 +00:00
|
|
|
}
|
2020-08-27 02:35:13 +00:00
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
void ItemManager::tradeChat(CNSocket* sock, CNPacketData* data) {
|
2020-08-27 02:35:13 +00:00
|
|
|
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
|
|
|
|
2021-01-01 07:16:44 +00:00
|
|
|
CNSocket* otherSock; // weird flip flop because we need to know who the other player is
|
|
|
|
if (pacdat->iID_Request == pacdat->iID_From)
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_To);
|
|
|
|
else
|
|
|
|
otherSock = PlayerManager::getSockFromID(pacdat->iID_From);
|
|
|
|
|
|
|
|
if (otherSock == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Player *otherPlr = PlayerManager::getPlayer(otherSock);
|
|
|
|
|
2020-08-27 02:35:13 +00:00
|
|
|
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT, resp);
|
2020-12-31 01:13:43 +00:00
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
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-12-31 01:13:43 +00:00
|
|
|
std::string fullChat = ChatManager::sanitizeText(U16toU8(pacdat->szFreeChat));
|
|
|
|
U8toU16(fullChat, resp.szFreeChat, sizeof(resp.szFreeChat));
|
2020-09-14 13:53:48 +00:00
|
|
|
|
2020-12-31 01:13:43 +00:00
|
|
|
std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat;
|
|
|
|
|
|
|
|
std::cout << logLine << std::endl;
|
|
|
|
ChatManager::dump.push_back(logLine);
|
|
|
|
|
|
|
|
resp.iEmoteCode = pacdat->iEmoteCode;
|
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;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
2020-12-16 15:27:18 +00:00
|
|
|
// sanity check
|
|
|
|
if (chest->ChestItem.iType != 9) {
|
|
|
|
std::cout << "[WARN] Player tried to open a crate with incorrect iType ?!" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ACADEMY
|
|
|
|
// check if chest isn't a nano capsule
|
|
|
|
if (NanoCapsules.find(chest->ChestItem.iID) != NanoCapsules.end())
|
|
|
|
return nanoCapsuleHandler(sock, chest);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Player *plr = PlayerManager::getPlayer(sock);
|
2020-10-17 23:19:05 +00:00
|
|
|
// chest opening acknowledgement packet
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
|
|
|
|
resp.iSlotNum = chest->iSlotNum;
|
|
|
|
|
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
|
2020-10-17 23:19:05 +00:00
|
|
|
reward->m_iCandy = plr->money;
|
|
|
|
reward->m_iFusionMatter = plr->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
|
2020-11-17 18:48:20 +00:00
|
|
|
reward->m_iBatteryN = plr->batteryN;
|
|
|
|
reward->m_iBatteryW = plr->batteryW;
|
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
|
|
|
item->iSlotNum = chest->iSlotNum;
|
|
|
|
item->eIL = chest->eIL;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
int itemSetId = -1, rarity = -1, ret = -1;
|
|
|
|
bool failing = false;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
// find the crate
|
|
|
|
if (Crates.find(chest->ChestItem.iID) == Crates.end()) {
|
|
|
|
std::cout << "[WARN] Crate " << chest->ChestItem.iID << " not found!" << std::endl;
|
|
|
|
failing = true;
|
|
|
|
}
|
|
|
|
Crate& crate = Crates[chest->ChestItem.iID];
|
2020-08-28 16:25:03 +00:00
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
if (!failing)
|
|
|
|
itemSetId = getItemSetId(crate, chest->ChestItem.iID);
|
|
|
|
if (itemSetId == -1)
|
|
|
|
failing = true;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
if (!failing)
|
|
|
|
rarity = getRarity(crate, itemSetId);
|
|
|
|
if (rarity == -1)
|
|
|
|
failing = true;
|
2020-08-28 16:25:03 +00:00
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
if (!failing)
|
|
|
|
ret = getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender);
|
|
|
|
if (ret == -1)
|
|
|
|
failing = true;
|
|
|
|
|
|
|
|
// if we failed to open a crate, at least give the player a gumball (suggested by Jade)
|
|
|
|
if (failing) {
|
|
|
|
item->sItem.iType = 7;
|
|
|
|
item->sItem.iID = 119 + (rand() % 3);
|
|
|
|
item->sItem.iOpt = 1;
|
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-10-17 23:19:05 +00:00
|
|
|
// update player
|
|
|
|
plr->Inven[chest->iSlotNum] = item->sItem;
|
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-10-17 23:19:05 +00:00
|
|
|
// transmit item
|
|
|
|
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
|
|
|
|
|
|
|
|
// transmit chest opening acknowledgement packet
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
int ItemManager::getItemSetId(Crate& crate, int crateId) {
|
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
|
|
|
int itemSetsCount = crate.itemSets.size();
|
2020-10-17 23:19:05 +00:00
|
|
|
if (itemSetsCount == 0) {
|
|
|
|
std::cout << "[WARN] Crate " << crateId << " has no item sets assigned?!" << std::endl;
|
|
|
|
return -1;
|
|
|
|
}
|
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 crate points to multiple itemSets, choose a random one
|
|
|
|
int itemSetIndex = rand() % itemSetsCount;
|
|
|
|
return crate.itemSets[itemSetIndex];
|
|
|
|
}
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
int ItemManager::getRarity(Crate& crate, int itemSetId) {
|
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
|
|
|
// find rarity ratio
|
2020-10-17 23:19:05 +00:00
|
|
|
if (RarityRatios.find(crate.rarityRatioId) == RarityRatios.end()) {
|
|
|
|
std::cout << "[WARN] Rarity Ratio " << crate.rarityRatioId << " not found!" << std::endl;
|
|
|
|
return -1;
|
|
|
|
}
|
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::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
|
|
|
|
*/
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
// remember that rarities start from 1!
|
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
|
|
|
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;
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
if (total == 0) {
|
|
|
|
std::cout << "Item Set " << itemSetId << " has no items assigned?!" << std::endl;
|
|
|
|
return -1;
|
|
|
|
}
|
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
|
|
|
|
|
|
|
// now return a random rarity number
|
|
|
|
int randomNum = rand() % total;
|
|
|
|
int rarity = 0;
|
|
|
|
int sum = 0;
|
|
|
|
do {
|
|
|
|
sum += rarityRatio[rarity];
|
2020-10-17 23:19:05 +00:00
|
|
|
rarity++;
|
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
|
|
|
} while (sum <= randomNum);
|
2020-10-17 23:19:05 +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
|
|
|
return rarity;
|
|
|
|
}
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
int ItemManager::getCrateItem(sItemBase& result, int itemSetId, int rarity, int playerGender) {
|
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::pair key = std::make_pair(itemSetId, rarity);
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
if (CrateItems.find(key) == CrateItems.end()) {
|
2020-12-22 04:55:43 +00:00
|
|
|
std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " does not exist" << std::endl;
|
2020-10-17 23:19:05 +00:00
|
|
|
return -1;
|
|
|
|
}
|
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
|
|
|
|
|
|
|
// only take into account items that have correct gender
|
|
|
|
std::vector<std::map<std::pair<int32_t, int32_t>, Item>::iterator> items;
|
2020-10-19 17:26:14 +00:00
|
|
|
for (auto crateitem : CrateItems[key]) {
|
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
|
|
|
int gender = crateitem->second.gender;
|
|
|
|
// if gender is incorrect, exclude item
|
|
|
|
if (gender != 0 && gender != playerGender)
|
|
|
|
continue;
|
|
|
|
items.push_back(crateitem);
|
|
|
|
}
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
if (items.size() == 0) {
|
2020-12-22 04:55:43 +00:00
|
|
|
std::cout << "[WARN] Set ID " << itemSetId << " Rarity " << rarity << " contains no valid items" << std::endl;
|
2020-10-17 23:19:05 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
auto item = items[rand() % items.size()];
|
2020-10-17 23:19:05 +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
|
|
|
result.iID = item->first.first;
|
|
|
|
result.iType = item->first.second;
|
|
|
|
result.iOpt = 1;
|
|
|
|
|
2020-10-17 23:19:05 +00:00
|
|
|
return 0;
|
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
|
|
|
// 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-12-04 18:57:08 +00:00
|
|
|
ItemManager::Item* ItemManager::getItemData(int32_t id, int32_t type) {
|
|
|
|
if(ItemData.find(std::make_pair(id, type)) != ItemData.end())
|
|
|
|
return &ItemData[std::make_pair(id, type)];
|
2020-09-14 14:27:52 +00:00
|
|
|
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;
|
2020-12-31 05:54:57 +00:00
|
|
|
plr->fireRate = 0;
|
2020-09-27 01:53:03 +00:00
|
|
|
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;
|
2020-12-31 05:54:57 +00:00
|
|
|
plr->fireRate += itemStatsDat->fireRate;
|
2020-09-27 01:53:03 +00:00
|
|
|
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
|
|
|
}
|
2020-12-16 15:27:18 +00:00
|
|
|
|
|
|
|
#ifdef ACADEMY
|
|
|
|
void ItemManager::nanoCapsuleHandler(CNSocket* sock, sP_CL2FE_REQ_ITEM_CHEST_OPEN* chest) {
|
|
|
|
Player* plr = PlayerManager::getPlayer(sock);
|
|
|
|
int32_t nanoId = NanoCapsules[chest->ChestItem.iID];
|
|
|
|
|
|
|
|
// chest opening acknowledgement packet
|
|
|
|
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
|
|
|
|
resp.iSlotNum = chest->iSlotNum;
|
|
|
|
|
|
|
|
// in order to remove capsule form inventory, we have to send item reward packet with empty item
|
|
|
|
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
|
|
|
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// maintain stats
|
|
|
|
reward->m_iCandy = plr->money;
|
|
|
|
reward->m_iFusionMatter = plr->fusionmatter;
|
|
|
|
reward->iFatigue = 100; // prevents warning message
|
|
|
|
reward->iFatigue_Level = 1;
|
|
|
|
reward->iItemCnt = 1; // remember to update resplen if you change this
|
|
|
|
reward->m_iBatteryN = plr->batteryN;
|
|
|
|
reward->m_iBatteryW = plr->batteryW;
|
|
|
|
|
|
|
|
item->iSlotNum = chest->iSlotNum;
|
|
|
|
item->eIL = chest->eIL;
|
|
|
|
|
|
|
|
// update player serverside
|
|
|
|
plr->Inven[chest->iSlotNum] = item->sItem;
|
|
|
|
|
|
|
|
// transmit item
|
|
|
|
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
|
|
|
|
|
|
|
|
// transmit chest opening acknowledgement packet
|
|
|
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC));
|
|
|
|
|
|
|
|
// check if player doesn't already have this nano
|
|
|
|
if (plr->Nanos[nanoId].iID != 0) {
|
|
|
|
INITSTRUCT(sP_FE2CL_GM_REP_PC_ANNOUNCE, msg);
|
|
|
|
msg.iDuringTime = 4;
|
|
|
|
std::string text = "You have already aquired this nano!";
|
|
|
|
U8toU16(text, msg.szAnnounceMsg, sizeof(text));
|
|
|
|
sock->sendPacket((void*)&msg, P_FE2CL_GM_REP_PC_ANNOUNCE, sizeof(sP_FE2CL_GM_REP_PC_ANNOUNCE));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
NanoManager::addNano(sock, nanoId, -1, false);
|
|
|
|
}
|
|
|
|
#endif
|