1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-26 22:37:45 +00:00

Merge pull request #318 from NotAdam/develop

add item splitting and stacking
This commit is contained in:
Mordred 2018-06-12 18:09:34 +02:00 committed by GitHub
commit c0fd27f73b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 16 deletions

View file

@ -69,11 +69,11 @@ namespace Packets {
SocialRequestError = 0x00AD, SocialRequestError = 0x00AD,
Playtime = 0x00DF, // updated 4.2
CFRegistered = 0x00B8, // updated 4.1 CFRegistered = 0x00B8, // updated 4.1
SocialRequestResponse = 0x00BB, // updated 4.1 SocialRequestResponse = 0x00BB, // updated 4.1
CancelAllianceForming = 0x00C6, // updated 4.2 CancelAllianceForming = 0x00C6, // updated 4.2
Playtime = 0x00F5, // updated 4.3
Chat = 0x00F7, // updated 4.3 Chat = 0x00F7, // updated 4.3
SocialList = 0x00FD, // updated 4.3 SocialList = 0x00FD, // updated 4.3

View file

@ -128,7 +128,7 @@ Core::ItemPtr Core::Inventory::getItemAt( uint16_t containerId, uint8_t slotId )
return m_inventoryMap[containerId]->getItem( slotId ); return m_inventoryMap[containerId]->getItem( slotId );
} }
Core::ItemPtr Core::Inventory::createItem( uint32_t catalogId, uint8_t quantity ) Core::ItemPtr Core::Inventory::createItem( uint32_t catalogId, uint16_t quantity )
{ {
auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto pExdData = g_fw.get< Data::ExdDataGenerated >();
auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >();
@ -144,7 +144,7 @@ Core::ItemPtr Core::Inventory::createItem( uint32_t catalogId, uint8_t quantity
uint8_t flags = 0; uint8_t flags = 0;
std::string itemName( itemInfo->name ); // std::string itemName( itemInfo->name );
ItemPtr pItem( new Item( catalogId ) ); ItemPtr pItem( new Item( catalogId ) );
@ -311,7 +311,7 @@ void Core::Inventory::updateBagDb( InventoryType type )
bool Core::Inventory::isArmory( uint16_t containerId ) bool Core::Inventory::isArmory( uint16_t containerId )
{ {
return return
containerId == ArmoryBody || containerId == ArmoryBody ||
containerId == ArmoryEar || containerId == ArmoryEar ||
containerId == ArmoryFeet || containerId == ArmoryFeet ||
@ -404,6 +404,12 @@ void Core::Inventory::updateItemDb( Core::ItemPtr pItem ) const
" WHERE itemId = " + std::to_string( pItem->getUId() ) ); " WHERE itemId = " + std::to_string( pItem->getUId() ) );
} }
void Core::Inventory::deleteItemDb( Core::ItemPtr item ) const
{
auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >();
pDb->execute( "UPDATE charaglobalitem SET IS_DELETE = 1 WHERE itemId = " + std::to_string( item->getUId() ) );
}
bool Core::Inventory::removeCurrency( CurrencyType type, uint32_t amount ) bool Core::Inventory::removeCurrency( CurrencyType type, uint32_t amount )
{ {
@ -468,12 +474,12 @@ bool Core::Inventory::isOneHandedWeapon( ItemUICategory weaponCategory )
bool Core::Inventory::isObtainable( uint32_t catalogId, uint8_t quantity ) bool Core::Inventory::isObtainable( uint32_t catalogId, uint8_t quantity )
{ {
return true; return true;
} }
int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint8_t quantity ) int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity, bool isHq, bool silent )
{ {
auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >();
auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto pExdData = g_fw.get< Data::ExdDataGenerated >();
@ -504,7 +510,9 @@ int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t
} }
auto item = createItem( catalogId, quantity ); auto item = createItem( catalogId, quantity );
item->setHq( isHq );
if( rSlotId != -1 ) if( rSlotId != -1 )
{ {
@ -523,7 +531,8 @@ int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t
invUpPacket.data().condition = 30000; invUpPacket.data().condition = 30000;
m_pOwner->queuePacket( invUpPacket ); m_pOwner->queuePacket( invUpPacket );
m_pOwner->queuePacket( ActorControlPacket143( m_pOwner->getId(), ItemObtainIcon, catalogId, item->getStackSize() ) ); if( !silent )
m_pOwner->queuePacket( ActorControlPacket143( m_pOwner->getId(), ItemObtainIcon, catalogId, item->getStackSize() ) );
} }
@ -598,6 +607,71 @@ bool Core::Inventory::updateContainer( uint16_t containerId, uint8_t slotId, Ite
return true; return true;
} }
void Core::Inventory::splitItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot, uint16_t itemCount )
{
auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId );
if( !fromItem )
return;
// check we have enough items in the origin slot
// nb: don't let the client 'split' a whole stack into another slot
if( fromItem->getStackSize() < itemCount )
// todo: correct the invalid item split? does retail do this or does it just ignore it?
return;
// make sure toInventoryId & toSlot are actually free so we don't orphan an item
if( m_inventoryMap[toInventoryId]->getItem( toSlot ) )
// todo: correct invalid move? again, not sure what retail does here
return;
auto newSlot = addItem( toInventoryId, toSlot, fromItem->getId(), itemCount, fromItem->isHq(), true );
if( newSlot == -1 )
return;
auto newItem = m_inventoryMap[toInventoryId]->getItem( static_cast< uint8_t >( newSlot ) );
fromItem->setStackSize( fromItem->getStackSize() - itemCount );
updateContainer( fromInventoryId, fromSlotId, fromItem );
updateContainer( toInventoryId, toSlot, newItem );
updateItemDb( fromItem );
}
void Core::Inventory::mergeItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot )
{
auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId );
auto toItem = m_inventoryMap[toInventoryId]->getItem( toSlot );
if( !fromItem || !toItem )
return;
if( fromItem->getId() != toItem->getId() )
return;
uint32_t stackSize = fromItem->getStackSize() + toItem->getStackSize();
uint32_t stackOverflow = stackSize - std::min< uint32_t >( m_maxSlotSize, stackSize );
// we can destroy the original stack if there's no overflow
if( stackOverflow == 0 )
{
m_inventoryMap[fromInventoryId]->removeItem( fromSlotId );
deleteItemDb( fromItem );
}
else
{
fromItem->setStackSize( stackOverflow );
updateItemDb( fromItem );
}
toItem->setStackSize( stackSize );
updateItemDb( toItem );
updateContainer( fromInventoryId, fromSlotId, fromItem );
updateContainer( toInventoryId, toSlot, toItem );
}
void Core::Inventory::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ) void Core::Inventory::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot )
{ {
auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId );
@ -609,7 +683,7 @@ void Core::Inventory::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, ui
// An item is being moved from bag0-3 to equippment, meaning // An item is being moved from bag0-3 to equippment, meaning
// the swapped out item will be placed in the matching armory. // the swapped out item will be placed in the matching armory.
if( isEquipment( toInventoryId ) if( isEquipment( toInventoryId )
&& !isEquipment( fromInventoryId ) && !isEquipment( fromInventoryId )
&& !isArmory( fromInventoryId ) ) && !isArmory( fromInventoryId ) )
{ {
@ -631,6 +705,8 @@ void Core::Inventory::discardItem( uint16_t fromInventoryId, uint8_t fromSlotId
uint32_t transactionId = 1; uint32_t transactionId = 1;
auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId );
deleteItemDb( fromItem );
m_inventoryMap[fromInventoryId]->removeItem( fromSlotId ); m_inventoryMap[fromInventoryId]->removeItem( fromSlotId );
updateContainer( fromInventoryId, fromSlotId, nullptr ); updateContainer( fromInventoryId, fromSlotId, nullptr );
@ -655,7 +731,7 @@ Core::ItemPtr Core::Inventory::loadItem( uint64_t uId )
{ {
auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto pExdData = g_fw.get< Data::ExdDataGenerated >();
auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >();
// load actual item // load actual item
auto itemRes = pDb->query( "SELECT catalogId, stack, flags FROM charaglobalitem WHERE itemId = " + std::to_string( uId ) + ";" ); auto itemRes = pDb->query( "SELECT catalogId, stack, flags FROM charaglobalitem WHERE itemId = " + std::to_string( uId ) + ";" );
if( !itemRes->next() ) if( !itemRes->next() )
return nullptr; return nullptr;
@ -664,10 +740,10 @@ Core::ItemPtr Core::Inventory::loadItem( uint64_t uId )
{ {
auto itemInfo = pExdData->get< Core::Data::Item >( itemRes->getUInt( 1 ) ); auto itemInfo = pExdData->get< Core::Data::Item >( itemRes->getUInt( 1 ) );
bool isHq = itemRes->getUInt( 3 ) == 1 ? true : false; bool isHq = itemRes->getUInt( 3 ) == 1 ? true : false;
ItemPtr pItem( new Item( uId, ItemPtr pItem( new Item( uId,
itemRes->getUInt( 1 ), itemRes->getUInt( 1 ),
itemInfo->modelMain, itemInfo->modelMain,
itemInfo->modelSub, itemInfo->modelSub,
static_cast< ItemUICategory >( itemInfo->itemUICategory ), static_cast< ItemUICategory >( itemInfo->itemUICategory ),
isHq ) ); isHq ) );
pItem->setStackSize( itemRes->getUInt( 2 ) ); pItem->setStackSize( itemRes->getUInt( 2 ) );

View file

@ -140,12 +140,14 @@ public:
InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId );
InvSlotPair getFreeBagSlot(); InvSlotPair getFreeBagSlot();
int16_t addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint8_t quantity = 1 ); int16_t addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity = 1, bool isHq = false, bool silent = false );
void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); 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 swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot );
void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ); void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId );
void splitItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot, uint16_t splitCount );
void mergeItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot );
ItemPtr createItem( uint32_t catalogId, uint8_t quantity = 1 ); ItemPtr createItem( uint32_t catalogId, uint16_t quantity = 1 );
ItemPtr loadItem( uint64_t uId ); ItemPtr loadItem( uint64_t uId );
@ -168,6 +170,7 @@ public:
void updateBagDb( InventoryType type ); void updateBagDb( InventoryType type );
void updateMannequinDb( InventoryType type ); void updateMannequinDb( InventoryType type );
void updateItemDb( ItemPtr pItem ) const; void updateItemDb( ItemPtr pItem ) const;
void deleteItemDb( ItemPtr pItem ) const;
bool isArmory( uint16_t containerId ); bool isArmory( uint16_t containerId );
bool isEquipment( uint16_t containerId ); bool isEquipment( uint16_t containerId );
@ -197,6 +200,7 @@ public:
private: private:
Entity::Player* m_pOwner; Entity::Player* m_pOwner;
InventoryMap m_inventoryMap; InventoryMap m_inventoryMap;
const uint32_t m_maxSlotSize = 999;
}; };
} }

View file

@ -47,6 +47,8 @@ void Core::Network::GameConnection::inventoryModifyHandler( const Packets::GameP
uint8_t toSlot = inPacket.getValAt< uint8_t >( 0x44 ); uint8_t toSlot = inPacket.getValAt< uint8_t >( 0x44 );
uint16_t fromContainer = inPacket.getValAt< uint16_t >( 0x2C ); uint16_t fromContainer = inPacket.getValAt< uint16_t >( 0x2C );
uint16_t toContainer = inPacket.getValAt< uint16_t >( 0x40 ); uint16_t toContainer = inPacket.getValAt< uint16_t >( 0x40 );
// todo: check packet handler in game and see if this is sent as a u16 or u32
uint16_t splitCount = inPacket.getValAt< uint16_t >( 0x48 );
ZoneChannelPacket< FFXIVIpcInventoryActionAck > ackPacket( player.getId() ); ZoneChannelPacket< FFXIVIpcInventoryActionAck > ackPacket( player.getId() );
ackPacket.data().sequence = seq; ackPacket.data().sequence = seq;
@ -83,13 +85,13 @@ void Core::Network::GameConnection::inventoryModifyHandler( const Packets::GameP
case InventoryOperation::Merge: // merge stack action case InventoryOperation::Merge: // merge stack action
{ {
player.getInventory()->mergeItem( fromContainer, fromSlot, toContainer, toSlot );
} }
break; break;
case InventoryOperation::Split: // split stack action case InventoryOperation::Split: // split stack action
{ {
player.getInventory()->splitItem( fromContainer, fromSlot, toContainer, toSlot, splitCount );
} }
break; break;