diff --git a/src/common/Exd/ExdDataGenerated.cpp b/src/common/Exd/ExdDataGenerated.cpp index b058341e..9d364919 100644 --- a/src/common/Exd/ExdDataGenerated.cpp +++ b/src/common/Exd/ExdDataGenerated.cpp @@ -2314,6 +2314,7 @@ Core::Data::Item::Item( uint32_t row_id, Core::Data::ExdDataGenerated* exdData ) isUnique = exdData->getField< bool >( row, 20 ); isUntradable = exdData->getField< bool >( row, 21 ); isIndisposable = exdData->getField< bool >( row, 22 ); + isEquippable = exdData->getField< bool >( row, 23 ); priceMid = exdData->getField< uint32_t >( row, 24 ); priceLow = exdData->getField< uint32_t >( row, 25 ); canBeHq = exdData->getField< bool >( row, 26 ); diff --git a/src/common/Exd/ExdDataGenerated.h b/src/common/Exd/ExdDataGenerated.h index c0f9d8e4..62642872 100644 --- a/src/common/Exd/ExdDataGenerated.h +++ b/src/common/Exd/ExdDataGenerated.h @@ -2406,6 +2406,7 @@ struct Item bool isUnique; bool isUntradable; bool isIndisposable; + bool isEquippable; uint32_t priceMid; uint32_t priceLow; bool canBeHq; diff --git a/src/servers/Scripts/opening/OpeningGridania.cpp b/src/servers/Scripts/opening/OpeningGridania.cpp index 25ce7e31..77f23678 100644 --- a/src/servers/Scripts/opening/OpeningGridania.cpp +++ b/src/servers/Scripts/opening/OpeningGridania.cpp @@ -42,7 +42,7 @@ private: default: itemId = 4426; break; } - auto item = player.addItem( Common::InventoryType::ArmoryRing, -1, itemId, 1, false, true ); + auto item = player.addItem( itemId, 1, false, true ); if( item ) player.equipItem( Common::EquipSlot::Ring2, item, true ); diff --git a/src/servers/Scripts/opening/OpeningLimsa.cpp b/src/servers/Scripts/opening/OpeningLimsa.cpp index 71983122..b5e6a50b 100644 --- a/src/servers/Scripts/opening/OpeningLimsa.cpp +++ b/src/servers/Scripts/opening/OpeningLimsa.cpp @@ -57,7 +57,7 @@ private: default: itemId = 4426; break; } - auto item = player.addItem( Common::InventoryType::ArmoryRing, -1, itemId, 1, false, true ); + auto item = player.addItem( itemId, 1, false, true ); if( item ) player.equipItem( Common::EquipSlot::Ring2, item, true ); diff --git a/src/servers/Scripts/opening/OpeningUldah.cpp b/src/servers/Scripts/opening/OpeningUldah.cpp index 7c3529ea..3711eebd 100644 --- a/src/servers/Scripts/opening/OpeningUldah.cpp +++ b/src/servers/Scripts/opening/OpeningUldah.cpp @@ -43,7 +43,7 @@ private: default: itemId = 4426; break; } - auto item = player.addItem( Common::InventoryType::ArmoryRing, -1, itemId, 1, false, true ); + auto item = player.addItem( itemId, 1, false, true ); if( item ) player.equipItem( Common::EquipSlot::Ring2, item, true ); diff --git a/src/servers/sapphire_api/PlayerMinimal.h b/src/servers/sapphire_api/PlayerMinimal.h index e6e74df5..e2af647d 100644 --- a/src/servers/sapphire_api/PlayerMinimal.h +++ b/src/servers/sapphire_api/PlayerMinimal.h @@ -155,7 +155,7 @@ namespace Core { return m_gmInvis; } - bool setGmInvis( bool invis ) + void setGmInvis( bool invis ) { m_gmInvis = invis; } diff --git a/src/servers/sapphire_zone/Actor/Player.h b/src/servers/sapphire_zone/Actor/Player.h index 0d3393a3..48526f17 100644 --- a/src/servers/sapphire_zone/Actor/Player.h +++ b/src/servers/sapphire_zone/Actor/Player.h @@ -222,10 +222,6 @@ public: // Inventory / Item / Currency ////////////////////////////////////////////////////////////////////////////////////////////////////// - /*! add an item to the first free slot in one of the 4 main containers */ - bool tryAddItem( uint16_t catalogId, uint32_t quantity ); - /*! add an item to a given container */ -// bool addItem( uint16_t containerId, uint16_t catalogId, uint32_t quantity ); /*! equip an item to a specified slot */ void equipItem( Common::EquipSlot equipSlotId, ItemPtr pItem, bool sendModel ); /*! remove an item from an equipment slot */ @@ -612,7 +608,7 @@ public: bool loadInventory(); InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); InvSlotPair getFreeBagSlot(); - Core::ItemPtr addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity = 1, bool isHq = false, bool slient = false ); + Core::ItemPtr addItem( uint32_t catalogId, uint32_t quantity = 1, bool isHq = false, bool slient = false ); void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); void swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ); diff --git a/src/servers/sapphire_zone/Actor/PlayerInventory.cpp b/src/servers/sapphire_zone/Actor/PlayerInventory.cpp index 44ec57ab..8013f5f4 100644 --- a/src/servers/sapphire_zone/Actor/PlayerInventory.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerInventory.cpp @@ -320,17 +320,6 @@ void Core::Entity::Player::removeCrystal( Common::CrystalType type, uint32_t amo queuePacket( invUpdate ); } -bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint32_t quantity ) -{ - - for( uint16_t i = 0; i < 4; i++ ) - { - if( addItem( i, -1, catalogId, quantity ) ) - return true; - } - return false; -} - void Core::Entity::Player::sendInventory() { InventoryMap::iterator it; @@ -495,7 +484,7 @@ bool Core::Entity::Player::isObtainable( uint32_t catalogId, uint8_t quantity ) } -Core::ItemPtr Core::Entity::Player::addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity, bool isHq, bool silent ) +Core::ItemPtr Core::Entity::Player::addItem( uint32_t catalogId, uint32_t quantity, bool isHq, bool silent ) { auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); auto pExdData = g_fw.get< Data::ExdDataGenerated >(); @@ -507,57 +496,87 @@ Core::ItemPtr Core::Entity::Player::addItem( uint16_t inventoryId, int8_t slotId return nullptr; } - int8_t rSlotId = -1; + if( itemInfo->isEquippable ) + quantity = 1; - //if( itemInfo->stack_size > 1 ) - //{ - // auto itemList = this->getSlotsOfItemsInInventory( catalogId ); - // // TODO: this is a stacked item so we need to see if the item is already in inventory and - // // check how much free space we have on existing stacks before looking for empty slots. - //} - //else + // used for item obtain notification + uint32_t originalQuantity = quantity; + + // todo: for now we're just going to add any items to main inv + + std::pair< uint8_t, uint8_t > freeBagSlot; + bool foundFreeSlot = false; + + for( auto bag : { Bag0, Bag1, Bag2, Bag3 } ) { - auto freeSlot = getFreeBagSlot(); - inventoryId = freeSlot.first; - rSlotId = freeSlot.second; + auto storage = m_storageMap[bag]; - if( rSlotId == -1 ) - return nullptr; + for( uint8_t slot = 0; slot < storage->getMaxSize(); slot++ ) + { + auto item = storage->getItem( slot ); + + // add any items that are stackable + if( item && !itemInfo->isEquippable && item->getId() == catalogId ) + { + uint32_t count = item->getStackSize(); + uint32_t maxStack = item->getMaxStackSize(); + + // if slot is full, skip it + if( count >= maxStack ) + continue; + + // update stack + uint32_t newStackSize = count + quantity; + uint32_t overflow = 0; + + if( newStackSize > maxStack ) + { + overflow = newStackSize - item->getMaxStackSize(); + newStackSize = maxStack; + } + + item->setStackSize( newStackSize ); + + auto slotUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), slot, bag, *item ); + queuePacket( slotUpdate ); + + quantity = overflow; + + // return existing stack if we have no overflow - items fit into a preexisting stack + if( quantity == 0 ) + return item; + } + else if( !item && !foundFreeSlot ) + { + freeBagSlot = { bag, slot }; + foundFreeSlot = true; + + break; + } + } } - auto item = createItem( catalogId, quantity ); + // couldn't find a free slot and we still have some quantity of items left, shits fucked + if( !foundFreeSlot ) + return nullptr; + auto item = createItem( catalogId, quantity ); item->setHq( isHq ); - if( rSlotId != -1 ) + auto storage = m_storageMap[freeBagSlot.first]; + storage->setItem( freeBagSlot.second, item ); + + writeInventory( static_cast< InventoryType >( freeBagSlot.first ) ); + + if( !silent ) { + auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), freeBagSlot.second, freeBagSlot.first, *item ); + queuePacket( invUpdate ); - auto storage = m_storageMap[inventoryId]; - storage->setItem( rSlotId, item ); - - pDb->execute( "UPDATE " + storage->getTableName() + " SET container_" + - std::to_string( rSlotId ) + " = " + std::to_string( item->getUId() ) + - " WHERE storageId = " + std::to_string( inventoryId ) + - " AND CharacterId = " + std::to_string( getId() ) ); - - if( !silent ) - { - auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), - rSlotId, - inventoryId, - *item ); - - queuePacket( invUpdate ); - - queuePacket( boost::make_shared< ActorControlPacket143 >( getId(), ItemObtainIcon, - catalogId, item->getStackSize() ) ); - } - - + queuePacket( boost::make_shared< ActorControlPacket143 >( getId(), ItemObtainIcon, catalogId, originalQuantity ) ); } return item; - } void Core::Entity::Player::moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ) @@ -641,7 +660,7 @@ void Core::Entity::Player::splitItem( uint16_t fromInventoryId, uint8_t fromSlot // todo: correct invalid move? again, not sure what retail does here return; - auto newItem = addItem( toInventoryId, toSlot, fromItem->getId(), itemCount, fromItem->isHq(), true ); + auto newItem = addItem( fromItem->getId(), itemCount, fromItem->isHq(), true ); if( !newItem ) return; diff --git a/src/servers/sapphire_zone/Actor/PlayerQuest.cpp b/src/servers/sapphire_zone/Actor/PlayerQuest.cpp index b8c9d1f8..c7e1a77c 100644 --- a/src/servers/sapphire_zone/Actor/PlayerQuest.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerQuest.cpp @@ -1059,14 +1059,14 @@ bool Core::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optional { for( uint32_t i = 0; i < questInfo->itemReward0.size(); i++ ) { - addItem( -1, questInfo->itemReward0.at( i ), questInfo->itemCountReward0.at( i ) ); + addItem( questInfo->itemCountReward0.at( i ) ); } } if( optionalItemCount > 0 ) { auto itemId = questInfo->itemReward1.at( optionalChoice ); - addItem( -1, itemId, questInfo->itemCountReward1.at( optionalChoice ) ); + addItem( questInfo->itemCountReward1.at( optionalChoice ) ); } if( gilReward > 0 ) diff --git a/src/servers/sapphire_zone/Inventory/ItemUtil.cpp b/src/servers/sapphire_zone/Inventory/ItemUtil.cpp index 340dd7dc..b830f2c7 100644 --- a/src/servers/sapphire_zone/Inventory/ItemUtil.cpp +++ b/src/servers/sapphire_zone/Inventory/ItemUtil.cpp @@ -62,9 +62,10 @@ uint16_t Core::Items::Util::getArmoryToEquipSlot( uint8_t slotId ) case Common::Wrist: return Common::ArmoryWrist; - } - return 0; + default: + return 0; + } } diff --git a/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp index 0a81a927..bac41fb1 100644 --- a/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp @@ -323,7 +323,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACKET_R return; } - if( !targetPlayer->addItem( -1, param1, quantity ) ) + if( !targetPlayer->addItem( param1, quantity ) ) player.sendUrgent( "Item " + std::to_string( param1 ) + " could not be added to inventory." ); break; }