diff --git a/sql/charaitemcrystal.sql b/sql/charaitemcrystal.sql deleted file mode 100644 index 1f9c2d57..00000000 --- a/sql/charaitemcrystal.sql +++ /dev/null @@ -1,74 +0,0 @@ --- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) --- --- Host: localhost Database: sapphire --- ------------------------------------------------------ --- Server version 5.7.13-log - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `charaitemcrystal` --- - -DROP TABLE IF EXISTS `charaitemcrystal`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `charaitemcrystal` ( - `CharacterId` int(20) DEFAULT '0', - `storageId` int(10) DEFAULT '2001', - `type` int(5) DEFAULT '0', - `idx` int(5) NOT NULL AUTO_INCREMENT, - `container_0` int(20) DEFAULT '0', - `container_1` int(20) DEFAULT '0', - `container_2` int(20) DEFAULT '0', - `container_3` int(20) DEFAULT '0', - `container_4` int(20) DEFAULT '0', - `container_5` int(20) DEFAULT '0', - `container_6` int(20) DEFAULT '0', - `container_7` int(20) DEFAULT '0', - `container_8` int(20) DEFAULT '0', - `container_9` int(20) DEFAULT '0', - `container_10` int(20) DEFAULT '0', - `container_11` int(20) DEFAULT '0', - `container_12` int(20) DEFAULT '0', - `container_13` int(20) DEFAULT '0', - `container_14` int(20) DEFAULT '0', - `container_15` int(20) DEFAULT '0', - `container_16` int(20) DEFAULT '0', - `container_17` int(20) DEFAULT '0', - `IS_DELETE` int(3) DEFAULT '0', - `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', - `UPDATE_DATE` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`idx`), - KEY `CharacterId` (`CharacterId`) -) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `charaitemcrystal` --- - -LOCK TABLES `charaitemcrystal` WRITE; -/*!40000 ALTER TABLE `charaitemcrystal` DISABLE KEYS */; -/*!40000 ALTER TABLE `charaitemcrystal` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2016-12-09 17:37:10 diff --git a/sql/charaitemcurrency.sql b/sql/charaitemcurrency.sql deleted file mode 100644 index 681dc036..00000000 --- a/sql/charaitemcurrency.sql +++ /dev/null @@ -1,68 +0,0 @@ --- MySQL dump 10.13 Distrib 5.7.13, for Win64 (x86_64) --- --- Host: localhost Database: sapphire --- ------------------------------------------------------ --- Server version 5.7.13-log - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `charaitemcurrency` --- - -DROP TABLE IF EXISTS `charaitemcurrency`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `charaitemcurrency` ( - `CharacterId` int(20) NOT NULL, - `storageId` int(10) DEFAULT '2000', - `type` int(5) DEFAULT '0', - `idx` int(5) NOT NULL AUTO_INCREMENT, - `container_0` int(20) DEFAULT '0', - `container_1` int(20) DEFAULT '0', - `container_2` int(20) DEFAULT '0', - `container_3` int(20) DEFAULT '0', - `container_4` int(20) DEFAULT '0', - `container_5` int(20) DEFAULT '0', - `container_6` int(20) DEFAULT '0', - `container_7` int(20) DEFAULT '0', - `container_8` int(20) NOT NULL DEFAULT '0', - `container_9` int(20) NOT NULL DEFAULT '0', - `container_10` int(20) NOT NULL DEFAULT '0', - `container_11` int(20) NOT NULL DEFAULT '0', - `IS_DELETE` int(3) DEFAULT '0', - `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', - `UPDATE_DATE` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`idx`), - UNIQUE KEY `CharacterId` (`CharacterId`) -) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `charaitemcurrency` --- - -LOCK TABLES `charaitemcurrency` WRITE; -/*!40000 ALTER TABLE `charaitemcurrency` DISABLE KEYS */; -/*!40000 ALTER TABLE `charaitemcurrency` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2016-12-09 17:37:11 diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 662c2fba..ba25a4f9 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -135,7 +135,7 @@ namespace Packets { InventoryTransactionFinish = 0x0193, // updated 4.3 InventoryTransaction = 0x0194, // updated 4.3 InventoryActionAck = 0x0197, // updated 4.3 - CurrencyCrystalInfo = 0xFFFF, // updated 4.3 - wrong opcode + CurrencyCrystalInfo = 0x0197, // updated 4.3 - wrong opcode UpdateInventorySlot = 0x0198, // updated 4.3 diff --git a/src/servers/sapphire_api/Forwards.h b/src/servers/sapphire_api/Forwards.h index 73b6429d..bb416e83 100644 --- a/src/servers/sapphire_api/Forwards.h +++ b/src/servers/sapphire_api/Forwards.h @@ -12,7 +12,6 @@ namespace Core class ItemContainer; class Inventory; class Session; - class XMLConfig; class ZonePosition; typedef boost::shared_ptr ZonePtr; @@ -20,7 +19,6 @@ namespace Core typedef boost::shared_ptr ItemContainerPtr; typedef boost::shared_ptr InventoryPtr; typedef boost::shared_ptr SessionPtr; - typedef boost::shared_ptr XMLConfigPtr; typedef boost::shared_ptr ZonePositionPtr; namespace StatusEffect diff --git a/src/servers/sapphire_zone/Actor/Player.cpp b/src/servers/sapphire_zone/Actor/Player.cpp index 0d1dccb3..a36f6bd9 100644 --- a/src/servers/sapphire_zone/Actor/Player.cpp +++ b/src/servers/sapphire_zone/Actor/Player.cpp @@ -48,6 +48,10 @@ using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; using namespace Core::Network::ActorControl; +using InventoryMap = std::map< uint16_t, Core::ItemContainerPtr >; +using InvSlotPair = std::pair< uint16_t, int8_t >; +using InvSlotPairVec = std::vector< InvSlotPair >; + // player constructor Core::Entity::Player::Player() : Chara( ObjKind::Player ), @@ -1024,7 +1028,7 @@ void Core::Entity::Player::update( int64_t currTime ) { if( m_targetId && m_currentStance == Entity::Chara::Stance::Active && isAutoattackOn() ) { - auto mainWeap = m_pInventory->getItemAt( Common::GearSet0, Common::EquipSlot::MainHand ); + auto mainWeap = getItemAt( Common::GearSet0, Common::EquipSlot::MainHand ); // @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need... for( auto actor : m_inRangeActor ) @@ -1407,8 +1411,7 @@ uint32_t Core::Entity::Player::getPersistentEmote() const void Core::Entity::Player::autoAttack( CharaPtr pTarget ) { - auto mainWeap = m_pInventory->getItemAt( Common::GearSet0, - Common::EquipSlot::MainHand ); + auto mainWeap = getItemAt( Common::GearSet0, Common::EquipSlot::MainHand ); pTarget->onActionHostile( *this ); //uint64_t tick = Util::getTimeMs(); @@ -1562,7 +1565,7 @@ void Core::Entity::Player::sendZonePackets() classInfoPacket->data().level1 = getLevel(); queuePacket( classInfoPacket ); - m_itemLevel = getInventory()->calculateEquippedGearItemLevel(); + m_itemLevel = calculateEquippedGearItemLevel(); sendItemLevel(); } @@ -1738,4 +1741,3 @@ bool Core::Entity::Player::isOnEnterEventDone() const return m_onEnterEventDone; } - diff --git a/src/servers/sapphire_zone/Actor/Player.h b/src/servers/sapphire_zone/Actor/Player.h index 1ea9aec1..268ab9f9 100644 --- a/src/servers/sapphire_zone/Actor/Player.h +++ b/src/servers/sapphire_zone/Actor/Player.h @@ -7,7 +7,6 @@ #include #include "Chara.h" -#include "Inventory/Inventory.h" #include "Event/EventHandler.h" #include #include @@ -23,11 +22,13 @@ struct QueuedZoning float m_targetRotation; uint64_t m_queueTime; - QueuedZoning( uint16_t targetZone, const Common::FFXIVARR_POSITION3& targetPosition, uint64_t queuedTime, float targetRotation ) - : m_targetZone( targetZone ) - , m_targetPosition( targetPosition ) - , m_queueTime( queuedTime ) - , m_targetRotation( targetRotation ) {} + QueuedZoning( uint16_t targetZone, const Common::FFXIVARR_POSITION3& targetPosition, + uint64_t queuedTime, float targetRotation ) : + m_targetZone( targetZone ), + m_targetPosition( targetPosition ), + m_queueTime( queuedTime ), + m_targetRotation( targetRotation ) + {} }; /** Class representing the Player @@ -224,7 +225,7 @@ public: /*! 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 ); +// 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 */ @@ -235,8 +236,6 @@ public: uint16_t getItemLevel() const; /*! send player ilvl */ void sendItemLevel(); - /*! get a const pointer to the inventory object */ - InventoryPtr getInventory() const; /*! get the current main hand model */ uint64_t getModelMainWeapon() const; /*! get the current off hand model */ @@ -249,18 +248,12 @@ public: uint32_t getModelForSlot( Common::EquipSlot slot ); /*! set the equipment model in a specified equipment slot */ void setModelForSlot( Common::EquipSlot slot, uint32_t val ); - /*! return the current amount of currency of type */ - uint32_t getCurrency( uint8_t type ) const; /*! add amount to the currency of type */ - void addCurrency( uint8_t type, uint32_t amount ); + void addCurrency( Common::CurrencyType type, uint32_t amount ); /*! remove amount from the currency of type */ - void removeCurrency( uint8_t type, uint32_t amount ); + void removeCurrency( Common::CurrencyType type, uint32_t amount ); /*! return the current amount of crystals of type */ uint32_t getCrystal( uint8_t type ) const; - /*! add amount to the crystals of type */ - void addCrystal( uint8_t type, uint32_t amount ); - /*! remove amount from the crystals of type */ - void removeCrystal( uint8_t type, uint32_t amount ); // Class / Job / Exp ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -496,7 +489,7 @@ public: /*! send status update */ void sendStatusUpdate( bool toSelf = true ) override; /*! send the entire inventory sequence */ - void sendInventory() const; + void sendInventory(); /*! send active quest list */ void sendQuestInfo(); /*! send a quest specific message */ @@ -607,8 +600,53 @@ public: /*! checks if a spawn index is valid */ bool isObjSpawnIndexValid( uint8_t index ); - uint64_t m_lastMoveTime; + // Inventory Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + void initInventory(); + + using InvSlotPair = std::pair< uint16_t, int8_t >; + using InvSlotPairVec = std::vector< InvSlotPair >; + + ItemPtr createItem( uint32_t catalogId, uint32_t quantity = 1 ); + bool loadInventory(); + InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); + InvSlotPair getFreeBagSlot(); + 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 swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); + 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 getItemAt( uint16_t containerId, uint8_t slotId ); + + bool updateContainer( uint16_t storageId, uint8_t slotId, ItemPtr pItem ); + + /*! calculate and return player ilvl based off equipped gear */ + uint16_t calculateEquippedGearItemLevel(); + /*! return the current amount of currency of type */ + uint32_t getCurrency( Common::CurrencyType type ); + + void writeInventory( Common::InventoryType type ); + void writeItem( ItemPtr pItem ) const; + void deleteItemDb( ItemPtr pItem ) const; + + /*! return the crystal amount of currency of type */ + uint32_t getCrystal( Common::CrystalType type ); + /*! add amount to the crystal of type */ + void addCrystal( Common::CrystalType type, uint32_t amount ); + /*! remove amount from the crystals of type */ + void removeCrystal( Common::CrystalType type, uint32_t amount ); + bool isObtainable( uint32_t catalogId, uint8_t quantity ); + + void send(); + + uint8_t getFreeSlotsInBags(); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + uint64_t m_lastMoveTime; uint8_t m_lastMoveflag; private: @@ -628,8 +666,9 @@ private: bool m_onEnterEventDone; private: + using InventoryMap = std::map< uint16_t, Core::ItemContainerPtr >; - + InventoryMap m_storageMap; Common::FFXIVARR_POSITION3 m_prevPos; uint32_t m_prevZoneType; @@ -681,8 +720,6 @@ private: uint8_t m_openingSequence; uint16_t m_itemLevel; - InventoryPtr m_pInventory; - std::map< uint32_t, Event::EventHandlerPtr > m_eventHandlerMap; std::queue< uint8_t > m_freeHateSlotQueue; // queue with "hate slots" free to be assigned diff --git a/src/servers/sapphire_zone/Actor/PlayerInventory.cpp b/src/servers/sapphire_zone/Actor/PlayerInventory.cpp index 4e4a6eaa..6ae062ad 100644 --- a/src/servers/sapphire_zone/Actor/PlayerInventory.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerInventory.cpp @@ -6,23 +6,104 @@ #include "Network/PacketWrappers/ActorControlPacket142.h" #include "Network/PacketWrappers/ActorControlPacket143.h" +#include "Network/PacketWrappers/UpdateInventorySlotPacket.h" -#include "Inventory/Inventory.h" #include "Inventory/Item.h" +#include "Inventory/ItemContainer.h" +#include "Inventory/ItemUtil.h" #include "Player.h" #include "Framework.h" -extern Core::Framework g_framework; +#include +#include + +#include + +#include +#include +#include + +#include "Actor/Player.h" + +#include "Network/PacketWrappers/ServerNoticePacket.h" +#include "Network/PacketWrappers/ActorControlPacket143.h" + + +#include "Framework.h" +#include + +extern Core::Framework g_fw; using namespace Core::Common; using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; using namespace Core::Network::ActorControl; -Core::InventoryPtr Core::Entity::Player::getInventory() const + + + +void Core::Entity::Player::initInventory() { - return m_pInventory; + auto setupContainer = [this]( InventoryType type, uint8_t maxSize, const std::string& tableName, bool isMultiStorage ) + { m_storageMap[type] = make_ItemContainer( type, maxSize, tableName, isMultiStorage ); }; + + // main bags + setupContainer( Bag0, 34, "charaiteminventory", true ); + setupContainer( Bag1, 34, "charaiteminventory", true ); + setupContainer( Bag2, 34, "charaiteminventory", true ); + setupContainer( Bag3, 34, "charaiteminventory", true ); + + // gear set + setupContainer( GearSet0, 13, "charaitemgearset", true ); + + // gil contianer + setupContainer( Currency, 11, "charaiteminventory", true ); + + // crystals?? + setupContainer( Crystal, 11, "charaiteminventory", true ); + + // armory weapons - 0 + setupContainer( ArmoryMain, 34, "charaiteminventory", true ); + + // armory offhand - 1 + setupContainer( ArmoryOff, 34, "charaiteminventory", true ); + + //armory head - 2 + setupContainer( ArmoryHead, 34, "charaiteminventory", true ); + + //armory body - 3 + setupContainer( ArmoryBody, 34, "charaiteminventory", true ); + + //armory hand - 4 + setupContainer( ArmoryHand, 34, "charaiteminventory", true ); + + //armory waist - 5 + setupContainer( ArmoryWaist, 34, "charaiteminventory", true ); + + //armory legs - 6 + setupContainer( ArmoryLegs, 34, "charaiteminventory", true ); + + //armory feet - 7 + setupContainer( ArmoryFeet, 34, "charaiteminventory", true ); + + //neck + setupContainer( ArmotyNeck, 34, "charaiteminventory", true ); + + //earring + setupContainer( ArmoryEar, 34, "charaiteminventory", true ); + + //wrist + setupContainer( ArmoryWrist, 34, "charaiteminventory", true ); + + //armory rings - 11 + setupContainer( ArmoryRing, 34, "charaiteminventory", true ); + + //soul crystals - 13 + setupContainer( ArmorySoulCrystal, 34, "charaiteminventory", true ); + + loadInventory(); + } void Core::Entity::Player::sendItemLevel() @@ -121,7 +202,7 @@ void Core::Entity::Player::equipItem( Common::EquipSlot equipSlotId, ItemPtr pIt if( sendUpdate ) { this->sendModel(); - m_itemLevel = getInventory()->calculateEquippedGearItemLevel(); + m_itemLevel = calculateEquippedGearItemLevel(); sendItemLevel(); } } @@ -131,79 +212,108 @@ void Core::Entity::Player::unequipItem( Common::EquipSlot equipSlotId, ItemPtr p m_modelEquip[static_cast< uint8_t >( equipSlotId )] = 0; sendModel(); - m_itemLevel = getInventory()->calculateEquippedGearItemLevel(); + m_itemLevel = calculateEquippedGearItemLevel(); sendItemLevel(); } -uint32_t Core::Entity::Player::getCurrency( uint8_t type ) const -{ - return m_pInventory->getCurrency( static_cast< Common::CurrencyType >( type ) ); -} - // TODO: these next functions are so similar that they could likely be simplified -void Core::Entity::Player::addCurrency( uint8_t type, uint32_t amount ) +void Core::Entity::Player::addCurrency( CurrencyType type, uint32_t amount ) { - if( !m_pInventory->addCurrency( static_cast< Common::CurrencyType >( type ), amount ) ) - return; + auto slot = static_cast< uint8_t >( static_cast< uint8_t >( type ) - 1 ); + auto currItem = m_storageMap[Currency]->getItem( slot ); - auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() ); - invUpPacket->data().containerId = Common::InventoryType::Currency; - invUpPacket->data().catalogId = 1; - invUpPacket->data().quantity = m_pInventory->getCurrency( static_cast< Common::CurrencyType >( type ) ); - invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1; + if( !currItem ) + { + // TODO: map currency type to itemid + currItem = createItem( 1 ); + m_storageMap[Currency]->setItem( slot, currItem ); + } - queuePacket( invUpPacket ); + uint32_t currentAmount = currItem->getStackSize(); + currItem->setStackSize( currentAmount + amount ); + writeItem(currItem); + + updateContainer( Currency, slot, currItem ); + + auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), + static_cast< uint8_t >( type ) - 1, + Common::InventoryType::Currency, + *currItem ); + queuePacket( invUpdate ); } -void Core::Entity::Player::removeCurrency( uint8_t type, uint32_t amount ) +void Core::Entity::Player::removeCurrency( Common::CurrencyType type, uint32_t amount ) { - if( !m_pInventory->removeCurrency( static_cast< Common::CurrencyType >( type ), amount ) ) + + auto currItem = m_storageMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) return; - auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() ); - invUpPacket->data().containerId = Common::InventoryType::Currency; - invUpPacket->data().catalogId = 1; - invUpPacket->data().quantity = m_pInventory->getCurrency( static_cast< Common::CurrencyType >( type ) ); - invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1; + uint32_t currentAmount = currItem->getStackSize(); + if( amount > currentAmount ) + currItem->setStackSize( 0 ); + else + currItem->setStackSize( currentAmount - amount ); + writeItem(currItem); - queuePacket( invUpPacket ); + auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), + static_cast< uint8_t >( type ) - 1, + Common::InventoryType::Currency, + *currItem ); + queuePacket( invUpdate ); } -uint32_t Core::Entity::Player::getCrystal( uint8_t type ) const +void Core::Entity::Player::addCrystal( Common::CrystalType type, uint32_t amount ) { - return m_pInventory->getCrystal( static_cast< Common::CrystalType >( type ) ); -} + auto currItem = m_storageMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); -void Core::Entity::Player::addCrystal( uint8_t type, uint32_t amount ) -{ - if( !m_pInventory->addCrystal( static_cast< Common::CrystalType >( type ), amount ) ) - return; + if( !currItem ) + { + // TODO: map currency type to itemid + currItem = createItem( static_cast< uint8_t >( type ) + 1 ); + m_storageMap[Crystal]->setItem( static_cast< uint8_t >( type ) - 1, currItem ); + } - auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() ); - invUpPacket->data().containerId = Common::InventoryType::Crystal; - invUpPacket->data().catalogId = static_cast< uint8_t >( type ) + 1; - invUpPacket->data().quantity = m_pInventory->getCrystal( static_cast< Common::CrystalType >( type ) ); - invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1; + uint32_t currentAmount = currItem->getStackSize(); - queuePacket( invUpPacket ); + currItem->setStackSize( currentAmount + amount ); + writeItem(currItem); + + writeInventory( Crystal ); + + + auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), + static_cast< uint8_t >( type ) - 1, + Common::InventoryType::Crystal, + *currItem ); + queuePacket( invUpdate ); queuePacket( boost::make_shared< ActorControlPacket143 >( getId(), ItemObtainIcon, static_cast< uint8_t >( type ) + 1, amount ) ); } -void Core::Entity::Player::removeCrystal( uint8_t type, uint32_t amount ) +void Core::Entity::Player::removeCrystal( Common::CrystalType type, uint32_t amount ) { - if( !m_pInventory->removeCrystal( static_cast< Common::CrystalType >( type ), amount ) ) + auto currItem = m_storageMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) return; - auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() ); - invUpPacket->data().containerId = Common::InventoryType::Crystal; - invUpPacket->data().catalogId = static_cast< uint8_t >( type ) + 1; - invUpPacket->data().quantity = m_pInventory->getCrystal( static_cast< Common::CrystalType >( type ) ); - invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1; + uint32_t currentAmount = currItem->getStackSize(); + if( amount > currentAmount ) + currItem->setStackSize( 0 ); + else + currItem->setStackSize( currentAmount - amount ); - queuePacket( invUpPacket ); + writeItem(currItem); + + auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), + static_cast< uint8_t >( type ) - 1, + Common::InventoryType::Crystal, + *currItem ); + queuePacket( invUpdate ); } bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint32_t quantity ) @@ -211,22 +321,455 @@ bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint32_t quantity ) for( uint16_t i = 0; i < 4; i++ ) { - if( m_pInventory->addItem( i, -1, catalogId, quantity ) != -1 ) + if( addItem( i, -1, catalogId, quantity ) != -1 ) return true; } return false; } -bool Core::Entity::Player::addItem( uint16_t containerId, uint16_t catalogId, uint32_t quantity ) +void Core::Entity::Player::sendInventory() { - if( m_pInventory->addItem( containerId, -1, catalogId, quantity ) != -1 ) - return true; + InventoryMap::iterator it; + + int32_t count = 0; + for( it = m_storageMap.begin(); it != m_storageMap.end(); ++it, count++ ) + { + + auto pMap = it->second->getItemMap(); + auto itM = pMap.begin(); + + for( ; itM != pMap.end(); ++itM ) + { + if( !itM->second ) + return; + + if( it->second->getId() == InventoryType::Currency || it->second->getId() == InventoryType::Crystal ) + { + auto currencyInfoPacket = makeZonePacket< FFXIVIpcCurrencyCrystalInfo >( getId() ); + currencyInfoPacket->data().sequence = count; + currencyInfoPacket->data().catalogId = itM->second->getId(); + currencyInfoPacket->data().unknown = 1; + currencyInfoPacket->data().quantity = itM->second->getStackSize(); + currencyInfoPacket->data().containerId = it->second->getId(); + currencyInfoPacket->data().slot = 0; + queuePacket( currencyInfoPacket ); + } + else + { + auto itemInfoPacket = makeZonePacket< FFXIVIpcItemInfo >( getId() ); + itemInfoPacket->data().sequence = count; + itemInfoPacket->data().containerId = it->second->getId(); + itemInfoPacket->data().slot = itM->first; + itemInfoPacket->data().quantity = itM->second->getStackSize(); + itemInfoPacket->data().catalogId = itM->second->getId(); + itemInfoPacket->data().condition = 30000; + itemInfoPacket->data().spiritBond = 0; + itemInfoPacket->data().hqFlag = itM->second->isHq() ? 1 : 0; + queuePacket( itemInfoPacket ); + } + } + + auto containerInfoPacket = makeZonePacket< FFXIVIpcContainerInfo >( getId() ); + containerInfoPacket->data().sequence = count; + containerInfoPacket->data().numItems = it->second->getEntryCount(); + containerInfoPacket->data().containerId = it->second->getId(); + queuePacket( containerInfoPacket ); + + + } - return false; } -void Core::Entity::Player::sendInventory() const +Core::Entity::Player::InvSlotPairVec Core::Entity::Player::getSlotsOfItemsInInventory( uint32_t catalogId ) { - m_pInventory->send(); + InvSlotPairVec outVec; + for( auto i : { Bag0, Bag1, Bag2, Bag3 } ) + { + auto inv = m_storageMap[i]; + for( auto item : inv->getItemMap() ) + { + if( item.second && item.second->getId() == catalogId ) + outVec.push_back( std::make_pair( i, static_cast< int8_t >( item.first ) ) ); + } + } + return outVec; } +Core::Entity::Player::InvSlotPair Core::Entity::Player::getFreeBagSlot() +{ + for( auto i : { Bag0, Bag1, Bag2, Bag3 } ) + { + auto freeSlot = static_cast< int8_t >( m_storageMap[i]->getFreeSlot() ); + + if( freeSlot != -1 ) + return std::make_pair( i, freeSlot ); + } + // no room in inventory + return std::make_pair( 0, -1 ); +} + +Core::ItemPtr Core::Entity::Player::getItemAt( uint16_t containerId, uint8_t slotId ) +{ + return m_storageMap[containerId]->getItem( slotId ); +} + + +uint32_t Core::Entity::Player::getCurrency( CurrencyType type ) +{ + + auto currItem = m_storageMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + return 0; + + return currItem->getStackSize(); + +} + +uint32_t Core::Entity::Player::getCrystal( CrystalType type ) +{ + + auto currItem = m_storageMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); + + if( !currItem ) + return 0; + + return currItem->getStackSize(); + +} + +void Core::Entity::Player::writeInventory( InventoryType type ) +{ + auto pLog = g_fw.get< Logger >(); + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + + auto storage = m_storageMap[type]; + + std::string query = "UPDATE " + storage->getTableName() + " SET "; + + for( int32_t i = 0; i <= storage->getMaxSize(); i++ ) + { + auto currItem = storage->getItem( i ); + + if( i > 0 ) + query += ", "; + + query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem ? currItem->getUId() : 0 ); + } + + query += " WHERE CharacterId = " + std::to_string( getId() ); + + if( storage->isMultiStorage() ) + query += " AND storageId = " + std::to_string( static_cast< uint16_t >( type ) ); + + pLog->debug( query ); + pDb->execute( query ); +} + +void Core::Entity::Player::writeItem( Core::ItemPtr pItem ) const +{ + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + pDb->execute( "UPDATE charaglobalitem SET stack = " + std::to_string( pItem->getStackSize() ) + " " + + // TODO: add other attributes + " WHERE itemId = " + std::to_string( pItem->getUId() ) ); +} + +void Core::Entity::Player::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::Entity::Player::isObtainable( uint32_t catalogId, uint8_t quantity ) +{ + + return true; +} + + +int16_t Core::Entity::Player::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 pExdData = g_fw.get< Data::ExdDataGenerated >(); + auto itemInfo = pExdData->get< Core::Data::Item >( catalogId ); + + // if item data doesn't exist or it's a blank field + if( !itemInfo || itemInfo->levelItem == 0 ) + { + return -1; + } + + int8_t rSlotId = -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 + { + auto freeSlot = getFreeBagSlot(); + inventoryId = freeSlot.first; + rSlotId = freeSlot.second; + + if( rSlotId == -1 ) + return -1; + } + + auto item = createItem( catalogId, quantity ); + + item->setHq( isHq ); + + if( rSlotId != -1 ) + { + + 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() ) ); + + + auto invUpdate = boost::make_shared< UpdateInventorySlotPacket >( getId(), + rSlotId, + inventoryId, + *item ); + queuePacket( invUpdate ); + + if( !silent ) + queuePacket( boost::make_shared< ActorControlPacket143 >( getId(), ItemObtainIcon, + catalogId, item->getStackSize() ) ); + + } + + return rSlotId; + +} + +void Core::Entity::Player::moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ) +{ + + auto tmpItem = m_storageMap[fromInventoryId]->getItem( fromSlotId ); + auto& itemMap = m_storageMap[fromInventoryId]->getItemMap(); + + if( tmpItem == nullptr ) + return; + + itemMap[fromSlotId].reset(); + + m_storageMap[toInventoryId]->setItem( toSlot, tmpItem ); + + writeInventory( static_cast< InventoryType >( toInventoryId ) ); + + if( fromInventoryId != toInventoryId ) + writeInventory( static_cast< InventoryType >( fromInventoryId ) ); + + if( static_cast< InventoryType >( toInventoryId ) == GearSet0 ) + equipItem( static_cast< EquipSlot >( toSlot ), tmpItem, true ); + + if( static_cast< InventoryType >( fromInventoryId ) == GearSet0 ) + unequipItem( static_cast< EquipSlot >( fromSlotId ), tmpItem ); + + +} + +bool Core::Entity::Player::updateContainer( uint16_t storageId, uint8_t slotId, ItemPtr pItem ) +{ + auto containerType = Items::Util::getContainerType( storageId ); + + m_storageMap[storageId]->setItem( slotId, pItem ); + + switch( containerType ) + { + case Armory: + case Bag: + case CurrencyCrystal: + { + writeInventory( static_cast< InventoryType >( storageId ) ); + break; + } + + case GearSet: + { + if( pItem ) + equipItem( static_cast< EquipSlot >( slotId ), pItem, true ); + else + unequipItem( static_cast< EquipSlot >( slotId ), pItem ); + + writeInventory( static_cast< InventoryType >( storageId ) ); + break; + } + default: + break; + } + + return true; +} + +void Core::Entity::Player::splitItem( uint16_t fromInventoryId, uint8_t fromSlotId, + uint16_t toInventoryId, uint8_t toSlot, uint16_t itemCount ) +{ + auto fromItem = m_storageMap[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_storageMap[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_storageMap[toInventoryId]->getItem( static_cast< uint8_t >( newSlot ) ); + + fromItem->setStackSize( fromItem->getStackSize() - itemCount ); + + updateContainer( fromInventoryId, fromSlotId, fromItem ); + updateContainer( toInventoryId, toSlot, newItem ); + + writeItem(fromItem); +} + +void Core::Entity::Player::mergeItem( uint16_t fromInventoryId, uint8_t fromSlotId, + uint16_t toInventoryId, uint8_t toSlot ) +{ + auto fromItem = m_storageMap[fromInventoryId]->getItem( fromSlotId ); + auto toItem = m_storageMap[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 >( fromItem->getMaxStackSize(), stackSize ); + + // we can destroy the original stack if there's no overflow + if( stackOverflow == 0 ) + { + m_storageMap[fromInventoryId]->removeItem( fromSlotId ); + deleteItemDb( fromItem ); + } + else + { + fromItem->setStackSize( stackOverflow ); + writeItem(fromItem); + } + + + toItem->setStackSize( stackSize ); + writeItem(toItem); + + updateContainer( fromInventoryId, fromSlotId, fromItem ); + updateContainer( toInventoryId, toSlot, toItem ); +} + +void Core::Entity::Player::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, + uint16_t toInventoryId, uint8_t toSlot ) +{ + auto fromItem = m_storageMap[fromInventoryId]->getItem( fromSlotId ); + auto toItem = m_storageMap[toInventoryId]->getItem( toSlot ); + auto& itemMap = m_storageMap[fromInventoryId]->getItemMap(); + + if( fromItem == nullptr || toItem == nullptr ) + return; + + // An item is being moved from bag0-3 to equippment, meaning + // the swapped out item will be placed in the matching armory. + if( Items::Util::isEquipment( toInventoryId ) + && !Items::Util::isEquipment( fromInventoryId ) + && !Items::Util::isArmory( fromInventoryId ) ) + { + updateContainer( fromInventoryId, fromSlotId, nullptr ); + fromInventoryId = Items::Util::getArmoryToEquipSlot( toSlot ); + fromSlotId = static_cast < uint8_t >( m_storageMap[fromInventoryId]->getFreeSlot() ); + } + + auto containerTypeFrom = Items::Util::getContainerType( fromInventoryId ); + auto containerTypeTo = Items::Util::getContainerType( toInventoryId ); + + updateContainer( toInventoryId, toSlot, fromItem ); + updateContainer( fromInventoryId, fromSlotId, toItem ); +} + +void Core::Entity::Player::discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ) +{ + // i am not entirely sure how this should be generated or if it even is important for us... + uint32_t transactionId = 1; + + auto fromItem = m_storageMap[fromInventoryId]->getItem( fromSlotId ); + + deleteItemDb( fromItem ); + + m_storageMap[fromInventoryId]->removeItem( fromSlotId ); + updateContainer( fromInventoryId, fromSlotId, nullptr ); + + auto invTransPacket = makeZonePacket< FFXIVIpcInventoryTransaction >( getId() ); + invTransPacket->data().transactionId = transactionId; + invTransPacket->data().ownerId = getId(); + invTransPacket->data().storageId = fromInventoryId; + invTransPacket->data().catalogId = fromItem->getId(); + invTransPacket->data().stackSize = fromItem->getStackSize(); + invTransPacket->data().slotId = fromSlotId; + invTransPacket->data().type = 7; + queuePacket( invTransPacket ); + + auto invTransFinPacket = makeZonePacket< FFXIVIpcInventoryTransactionFinish >( getId() ); + invTransFinPacket->data().transactionId = transactionId; + invTransFinPacket->data().transactionId1 = transactionId; + queuePacket( invTransFinPacket ); +} + +uint16_t Core::Entity::Player::calculateEquippedGearItemLevel() +{ + uint32_t iLvlResult = 0; + + auto gearSetMap = m_storageMap[GearSet0]->getItemMap(); + + auto it = gearSetMap.begin(); + + while( it != gearSetMap.end() ) + { + auto currItem = it->second; + + if( currItem ) + { + iLvlResult += currItem->getItemLevel(); + + // If item is weapon and isn't one-handed + if( currItem->isWeapon() && !Items::Util::isOneHandedWeapon( currItem->getCategory() ) ) + { + iLvlResult += currItem->getItemLevel(); + } + } + + it++; + } + + return boost::algorithm::clamp( iLvlResult / 13, 0, 9999 ); +} + + +uint8_t Core::Entity::Player::getFreeSlotsInBags() +{ + uint8_t slots = 0; + for( uint8_t container : { Bag0, Bag1, Bag2, Bag3 } ) + { + const auto& storage = m_storageMap[container]; + slots += ( storage->getMaxSize() - storage->getEntryCount() ); + } + return slots; +} diff --git a/src/servers/sapphire_zone/Actor/PlayerQuest.cpp b/src/servers/sapphire_zone/Actor/PlayerQuest.cpp index 7c8a10f9..7cdfaed4 100644 --- a/src/servers/sapphire_zone/Actor/PlayerQuest.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerQuest.cpp @@ -1070,7 +1070,7 @@ bool Core::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optional } if( gilReward > 0 ) - addCurrency( 1, gilReward ); + addCurrency( CurrencyType::Gil, gilReward ); return true; } diff --git a/src/servers/sapphire_zone/Actor/PlayerSql.cpp b/src/servers/sapphire_zone/Actor/PlayerSql.cpp index 94224bf2..f87a2fc5 100644 --- a/src/servers/sapphire_zone/Actor/PlayerSql.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerSql.cpp @@ -14,6 +14,9 @@ #include "Zone/TerritoryMgr.h" #include "Zone/Zone.h" +#include "Inventory/Item.h" +#include "Inventory/ItemContainer.h" +#include "Inventory/ItemUtil.h" #include "ServerZone.h" #include "Framework.h" @@ -200,9 +203,6 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) m_modelSubWeapon = 0; m_lastTickTime = 0; - // TODO: remove Inventory and actually inline it in Player class - m_pInventory = make_Inventory( this ); - calculateStats(); // first login, run the script event @@ -228,7 +228,9 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) setStateFlag( PlayerStateFlag::BetweenAreas ); - m_pInventory->load(); + //m_pInventory->load(); + + initInventory(); initHateSlotQueue(); @@ -554,3 +556,102 @@ void Core::Entity::Player::insertQuest( uint16_t questId, uint8_t index, uint8_t stmt->setInt( 12, 0 ); pDb->execute( stmt ); } + +Core::ItemPtr Core::Entity::Player::createItem( uint32_t catalogId, uint32_t quantity ) +{ + auto pExdData = g_fw.get< Data::ExdDataGenerated >(); + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + auto itemInfo = pExdData->get< Core::Data::Item >( catalogId ); + + if( !itemInfo ) + return nullptr; + + if( !itemInfo ) + return nullptr; + + uint8_t flags = 0; + + ItemPtr pItem = make_Item( Items::Util::getNextUId(), + catalogId, + itemInfo->modelMain, + itemInfo->modelSub ); + + pItem->setStackSize( quantity ); + + pDb->execute( "INSERT INTO charaglobalitem ( CharacterId, itemId, catalogId, stack, flags ) VALUES ( " + + std::to_string( getId() ) + ", " + + std::to_string( pItem->getUId() ) + ", " + + std::to_string( pItem->getId() ) + ", " + + std::to_string( quantity ) + ", " + + std::to_string( flags ) + ");" ); + + return pItem; +} + +bool Core::Entity::Player::loadInventory() +{ + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // load active gearset + auto res = pDb->query( "SELECT storageId, container_0, container_1, container_2, container_3, " + "container_4, container_5, container_6, container_7, " + "container_8, container_9, container_10, container_11, " + "container_12, container_13 " + "FROM charaitemgearset " \ + "WHERE CharacterId = " + std::to_string( getId() ) + " " \ + "ORDER BY storageId ASC;" ); + + while( res->next() ) + { + uint16_t storageId = res->getUInt16( 1 ); + + for( uint32_t i = 1; i <= 14; i++ ) + { + uint64_t uItemId = res->getUInt64( i + 1 ); + if( uItemId == 0 ) + continue; + + ItemPtr pItem = Items::Util::loadItem( uItemId ); + + if( pItem == nullptr ) + continue; + + m_storageMap[storageId]->getItemMap()[i - 1] = pItem; + equipItem( static_cast< EquipSlot >( i - 1 ), pItem, false ); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // Load everything + auto bagRes = pDb->query( "SELECT storageId, " + "container_0, container_1, container_2, container_3, container_4, " + "container_5, container_6, container_7, container_8, container_9, " + "container_10, container_11, container_12, container_13, container_14, " + "container_15, container_16, container_17, container_18, container_19, " + "container_20, container_21, container_22, container_23, container_24, " + "container_25, container_26, container_27, container_28, container_29, " + "container_30, container_31, container_32, container_33, container_34 " + "FROM charaiteminventory " \ + "WHERE CharacterId = " + std::to_string( getId() ) + " " \ + "ORDER BY storageId ASC;" ); + + while( bagRes->next() ) + { + uint16_t storageId = bagRes->getUInt16( 1 ); + for( uint32_t i = 1; i <= m_storageMap[storageId]->getMaxSize(); i++ ) + { + uint64_t uItemId = bagRes->getUInt64( i + 1 ); + if( uItemId == 0 ) + continue; + + ItemPtr pItem = Items::Util::loadItem( uItemId ); + + if( pItem == nullptr ) + continue; + + m_storageMap[storageId]->getItemMap()[i - 1] = pItem; + } + } + + return true; +} diff --git a/src/servers/sapphire_zone/Forwards.h b/src/servers/sapphire_zone/Forwards.h index b81d6dee..f685b569 100644 --- a/src/servers/sapphire_zone/Forwards.h +++ b/src/servers/sapphire_zone/Forwards.h @@ -24,9 +24,7 @@ namespace Core TYPE_FORWARD( InstanceContent ); TYPE_FORWARD( Item ); TYPE_FORWARD( ItemContainer ); - TYPE_FORWARD( Inventory ); TYPE_FORWARD( Session ); - TYPE_FORWARD( XMLConfig ); TYPE_FORWARD( ZonePosition ) namespace StatusEffect diff --git a/src/servers/sapphire_zone/Inventory/Inventory.cpp b/src/servers/sapphire_zone/Inventory/Inventory.cpp deleted file mode 100644 index 61a8a82e..00000000 --- a/src/servers/sapphire_zone/Inventory/Inventory.cpp +++ /dev/null @@ -1,1024 +0,0 @@ -#include -#include - -#include - -#include -#include -#include - -#include "Actor/Player.h" - -#include "Network/PacketWrappers/ServerNoticePacket.h" -#include "Network/PacketWrappers/ActorControlPacket143.h" - -#include "ItemContainer.h" -#include "Item.h" -#include "Framework.h" -#include - -extern Core::Framework g_fw; - -using namespace Core::Common; -using namespace Core::Network; -using namespace Core::Network::Packets; -using namespace Core::Network::Packets::Server; -using namespace Core::Network::ActorControl; - -Core::Inventory::Inventory( Core::Entity::Player* pOwner ) -{ - - m_pOwner = pOwner; - - // shortcut for setting up inventory - // TODO: use a loop to set theese up? - auto setupContainer = []( InventoryMap& map, InventoryType type ) - { map[type] = make_ItemContainer( type ); }; - - // main bags - setupContainer( m_inventoryMap, Bag0 ); - setupContainer( m_inventoryMap, Bag1 ); - setupContainer( m_inventoryMap, Bag2 ); - setupContainer( m_inventoryMap, Bag3 ); - - // gear set - setupContainer( m_inventoryMap, GearSet0 ); - - // gil contianer - setupContainer( m_inventoryMap, Currency ); - - // crystals?? - setupContainer( m_inventoryMap, Crystal ); - //m_inventoryMap[0x07D3] = ItemContainerPtr( new ItemContainer( UNKNOWN_0 ) ); - //m_inventoryMap[0x07D8] = ItemContainerPtr( new ItemContainer( UNKNOWN_1 ) ); - - // armory weapons - 0 - setupContainer( m_inventoryMap, ArmoryMain ); - - // armory offhand - 1 - setupContainer( m_inventoryMap, ArmoryOff ); - - //armory head - 2 - setupContainer( m_inventoryMap, ArmoryHead ); - - //armory body - 3 - setupContainer( m_inventoryMap, ArmoryBody ); - - //armory hand - 4 - setupContainer( m_inventoryMap, ArmoryHand ); - - //armory waist - 5 - setupContainer( m_inventoryMap, ArmoryWaist ); - - //armory legs - 6 - setupContainer( m_inventoryMap, ArmoryLegs ); - - //armory feet - 7 - setupContainer( m_inventoryMap, ArmoryFeet ); - - //neck - setupContainer( m_inventoryMap, ArmotyNeck ); - - //earring - setupContainer( m_inventoryMap, ArmoryEar ); - - //wrist - setupContainer( m_inventoryMap, ArmoryWrist ); - - //armory rings - 11 - setupContainer( m_inventoryMap, ArmoryRing ); - - //soul crystals - 13 - setupContainer( m_inventoryMap, ArmorySoulCrystal ); -} - - -Core::Inventory::~Inventory() -{ -} - -Core::Inventory::InvSlotPairVec Core::Inventory::getSlotsOfItemsInInventory( uint32_t catalogId ) -{ - InvSlotPairVec outVec; - for( auto i : { Bag0, Bag1, Bag2, Bag3 } ) - { - auto inv = m_inventoryMap[i]; - for( auto item : inv->getItemMap() ) - { - if( item.second && item.second->getId() == catalogId ) - outVec.push_back( std::make_pair( i, static_cast< int8_t >( item.first ) ) ); - } - } - return outVec; -} - -Core::Inventory::InvSlotPair Core::Inventory::getFreeBagSlot() -{ - for( auto i : { Bag0, Bag1, Bag2, Bag3 } ) - { - auto freeSlot = static_cast< int8_t >( m_inventoryMap[i]->getFreeSlot() ); - - if( freeSlot != -1 ) - return std::make_pair( i, freeSlot ); - } - // no room in inventory - return std::make_pair( 0, -1 ); -} - -Core::ItemPtr Core::Inventory::getItemAt( uint16_t containerId, uint8_t slotId ) -{ - return m_inventoryMap[containerId]->getItem( slotId ); -} - -Core::ItemPtr Core::Inventory::createItem( uint32_t catalogId, uint16_t quantity ) -{ - auto pExdData = g_fw.get< Data::ExdDataGenerated >(); - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - auto itemInfo = pExdData->get< Core::Data::Item >( catalogId ); - - if( !itemInfo ) - return nullptr; - - uint16_t itemAmount = quantity; - - if( itemInfo->stackSize == 1 ) - itemAmount = 1; - - if( !itemInfo ) - return nullptr; - - uint8_t flags = 0; - - ItemPtr pItem = make_Item( getNextUId(), - catalogId, - itemInfo->modelMain, - itemInfo->modelSub ); - - pItem->setStackSize( itemAmount ); - - pDb->execute( "INSERT INTO charaglobalitem ( CharacterId, itemId, catalogId, stack, flags ) VALUES ( " + - std::to_string( m_pOwner->getId() ) + ", " + - std::to_string( pItem->getUId() ) + ", " + - std::to_string( pItem->getId() ) + ", " + - std::to_string( itemAmount ) + ", " + - std::to_string( flags ) + ");" ); - - return pItem; -} - - -uint32_t Core::Inventory::getCurrency( CurrencyType type ) -{ - - auto currItem = m_inventoryMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); - - if( !currItem ) - return 0; - - return currItem->getStackSize(); - -} - -uint32_t Core::Inventory::getCrystal( CrystalType type ) -{ - - auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); - - if( !currItem ) - return 0; - - return currItem->getStackSize(); - -} - -bool Core::Inventory::addCrystal( CrystalType type, uint32_t amount ) -{ - auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); - - if( !currItem ) - { - // TODO: map currency type to itemid - currItem = createItem( static_cast< uint8_t >( type ) + 1 ); - m_inventoryMap[Crystal]->setItem( static_cast< uint8_t >( type ) - 1, currItem ); - updateCrystalDb(); - } - - uint32_t currentAmount = currItem->getStackSize(); - - currItem->setStackSize( currentAmount + amount ); - - updateItemDb( currItem ); - - return true; - -} - -bool Core::Inventory::addCurrency( CurrencyType type, uint32_t amount ) -{ - auto currItem = m_inventoryMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); - - if( !currItem ) - { - // TODO: map currency type to itemid - currItem = createItem( 1 ); - m_inventoryMap[Currency]->setItem( static_cast< uint8_t >( type ) - 1, currItem ); - updateCurrencyDb(); - } - - uint32_t currentAmount = currItem->getStackSize(); - - currItem->setStackSize( currentAmount + amount ); - - updateItemDb( currItem ); - - return true; - -} - -void Core::Inventory::updateCurrencyDb() -{ - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - int32_t firstItemPos = -1; - std::string query = "UPDATE charaitemcurrency SET "; - - for( int32_t i = 0; i <= 11; i++ ) - { - auto currItem = m_inventoryMap[Currency]->getItem( i ); - - if( currItem ) - { - if( firstItemPos == -1 ) - firstItemPos = i; - - if( i > firstItemPos ) - query += ", "; - - query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem->getUId() ); - } - } - - query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ); - - pDb->execute( query ); -} - - -void Core::Inventory::updateCrystalDb() -{ - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - int32_t firstItemPos = -1; - std::string query = "UPDATE charaitemcrystal SET "; - - for( int32_t i = 0; i <= 11; i++ ) - { - auto currItem = m_inventoryMap[Crystal]->getItem( i ); - - if( currItem ) - { - if( firstItemPos == -1 ) - firstItemPos = i; - - if( i > firstItemPos ) - query += ", "; - - query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem->getUId() ); - } - } - - query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ); - - pDb->execute( query ); -} - -void Core::Inventory::updateBagDb( InventoryType type ) -{ - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - std::string query = "UPDATE charaiteminventory SET "; - - for( int32_t i = 0; i <= 34; i++ ) - { - auto currItem = m_inventoryMap[type]->getItem( i ); - - if( i > 0 ) - query += ", "; - - query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem ? currItem->getUId() : 0 ); - } - - query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + - " AND storageId = " + std::to_string( static_cast< uint16_t >( type ) ); - - pDb->execute( query ); -} - -bool Core::Inventory::isArmory( uint16_t containerId ) -{ - return - containerId == ArmoryBody || - containerId == ArmoryEar || - containerId == ArmoryFeet || - containerId == ArmoryHand || - containerId == ArmoryHead || - containerId == ArmoryLegs || - containerId == ArmoryMain || - containerId == ArmoryOff || - containerId == ArmoryRing || - containerId == ArmoryWaist || - containerId == ArmoryWrist; -} - -uint16_t Core::Inventory::getArmoryToEquipSlot( uint8_t slotId ) -{ - switch( slotId ) - { - case Body: - return ArmoryBody; - - case Ear: - return ArmoryEar; - - case Feet: - return ArmoryFeet; - - case Hands: - return ArmoryHand; - - case Legs: - return ArmoryLegs; - - case MainHand: - return ArmoryMain; - - case OffHand: - return ArmoryOff; - - case Ring2: - case Ring1: - return ArmoryRing; - - case Waist: - return ArmoryWaist; - - case Wrist: - return ArmoryWrist; - } - - return 0; -} - - - -bool Core::Inventory::isEquipment( uint16_t containerId ) -{ - return containerId == GearSet0; -} - - -void Core::Inventory::updateMannequinDb( InventoryType type ) -{ - auto pLog = g_fw.get< Logger >(); - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - std::string query = "UPDATE charaitemgearset SET "; - - for( int32_t i = 0; i <= 13; i++ ) - { - auto currItem = m_inventoryMap[type]->getItem( i ); - - if( i > 0 ) - query += ", "; - - query += "container_" + std::to_string( i ) + " = " + std::to_string( currItem ? currItem->getUId() : 0 ); - } - - query += " WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + - " AND storageId = " + std::to_string( static_cast< uint16_t >( type ) ); - - pLog->debug( query ); - pDb->execute( query ); -} - - -void Core::Inventory::updateItemDb( Core::ItemPtr pItem ) const -{ - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - pDb->execute( "UPDATE charaglobalitem SET stack = " + std::to_string( pItem->getStackSize() ) + " " + - // TODO: add other attributes - " 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 ) -{ - - auto currItem = m_inventoryMap[Currency]->getItem( static_cast< uint8_t >( type ) - 1 ); - - if( !currItem ) - return false; - - uint32_t currentAmount = currItem->getStackSize(); - if( amount > currentAmount ) - currItem->setStackSize( 0 ); - else - currItem->setStackSize( currentAmount - amount ); - - updateItemDb( currItem ); - - return true; -} - -bool Core::Inventory::removeCrystal( CrystalType type, uint32_t amount ) -{ - - auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 ); - - if( !currItem ) - return false; - - uint32_t currentAmount = currItem->getStackSize(); - if( amount > currentAmount ) - currItem->setStackSize( 0 ); - else - currItem->setStackSize( currentAmount - amount ); - - updateItemDb( currItem ); - - return true; -} - -bool Core::Inventory::isOneHandedWeapon( ItemUICategory weaponCategory ) -{ - switch ( weaponCategory ) - { - case ItemUICategory::AlchemistsPrimaryTool: - case ItemUICategory::ArmorersPrimaryTool: - case ItemUICategory::BotanistsPrimaryTool: - case ItemUICategory::CulinariansPrimaryTool: - case ItemUICategory::OnehandedConjurersArm: - case ItemUICategory::CarpentersPrimaryTool: - case ItemUICategory::FishersPrimaryTool: - case ItemUICategory::GladiatorsArm: - case ItemUICategory::GoldsmithsPrimaryTool: - case ItemUICategory::LeatherworkersPrimaryTool: - case ItemUICategory::MinersPrimaryTool: - case ItemUICategory::OnehandedThaumaturgesArm: - case ItemUICategory::WeaversPrimaryTool: - case ItemUICategory::BlacksmithsPrimaryTool: - return true; - default: - return false; - } -} - -bool Core::Inventory::isObtainable( uint32_t catalogId, uint8_t quantity ) -{ - - return true; -} - - -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 pExdData = g_fw.get< Data::ExdDataGenerated >(); - auto itemInfo = pExdData->get< Core::Data::Item >( catalogId ); - - // if item data doesn't exist or it's a blank field - if( !itemInfo || itemInfo->levelItem == 0 ) - { - return -1; - } - - int8_t rSlotId = -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 - { - auto freeSlot = this->getFreeBagSlot(); - inventoryId = freeSlot.first; - rSlotId = freeSlot.second; - - if( rSlotId == -1 ) - return -1; - } - - auto item = createItem( catalogId, quantity ); - - item->setHq( isHq ); - - if( rSlotId != -1 ) - { - - m_inventoryMap[inventoryId]->setItem( rSlotId, item ); - - pDb->execute( "UPDATE charaiteminventory SET container_" + std::to_string( rSlotId ) + " = " + std::to_string( item->getUId() ) + - " WHERE storageId = " + std::to_string( inventoryId ) + - " AND CharacterId = " + std::to_string( m_pOwner->getId() ) ); - - - auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( m_pOwner->getId() ); - invUpPacket->data().containerId = inventoryId; - invUpPacket->data().catalogId = catalogId; - invUpPacket->data().quantity = item->getStackSize(); - invUpPacket->data().hqFlag = item->isHq() ? 1 : 0; - invUpPacket->data().slot = rSlotId; - invUpPacket->data().condition = 30000; - m_pOwner->queuePacket( invUpPacket ); - - if( !silent ) - m_pOwner->queuePacket( boost::make_shared< ActorControlPacket143 >( m_pOwner->getId(), ItemObtainIcon, - catalogId, item->getStackSize() ) ); - - } - - return rSlotId; - -} - -void Core::Inventory::moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ) -{ - - auto tmpItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); - auto& itemMap = m_inventoryMap[fromInventoryId]->getItemMap(); - - if( tmpItem == nullptr ) - return; - - itemMap[fromSlotId].reset(); - - m_inventoryMap[toInventoryId]->setItem( toSlot, tmpItem ); - - if( toInventoryId != GearSet0 ) - updateBagDb( static_cast< InventoryType >( toInventoryId ) ); - - if( fromInventoryId != GearSet0 && fromInventoryId != toInventoryId ) - updateBagDb( static_cast< InventoryType >( fromInventoryId ) ); - - if( static_cast< InventoryType >( toInventoryId ) == GearSet0 ) - { - m_pOwner->equipItem( static_cast< EquipSlot >( toSlot ), tmpItem, true ); - updateMannequinDb( static_cast< InventoryType >( toInventoryId ) ); - } - - if( static_cast< InventoryType >( fromInventoryId ) == GearSet0 ) - { - m_pOwner->unequipItem( static_cast< EquipSlot >( fromSlotId ), tmpItem ); - updateMannequinDb( static_cast< InventoryType >( fromInventoryId ) ); - } - - -} - -bool Core::Inventory::updateContainer( uint16_t containerId, uint8_t slotId, ItemPtr pItem ) -{ - auto containerType = getContainerType( containerId ); - - m_inventoryMap[containerId]->setItem( slotId, pItem ); - - switch( containerType ) - { - case Armory: - case CurrencyCrystal: - case Bag: - { - updateBagDb( static_cast< InventoryType >( containerId ) ); - break; - } - - case GearSet: - { - if( pItem ) - m_pOwner->equipItem( static_cast< EquipSlot >( slotId ), pItem, true ); - else - m_pOwner->unequipItem( static_cast< EquipSlot >( slotId ), pItem ); - - updateMannequinDb( static_cast< InventoryType >( containerId ) ); - break; - } - default: - break; - } - - 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 ) -{ - auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); - auto toItem = m_inventoryMap[toInventoryId]->getItem( toSlot ); - auto& itemMap = m_inventoryMap[fromInventoryId]->getItemMap(); - - if( fromItem == nullptr || toItem == nullptr ) - return; - - // An item is being moved from bag0-3 to equippment, meaning - // the swapped out item will be placed in the matching armory. - if( isEquipment( toInventoryId ) - && !isEquipment( fromInventoryId ) - && !isArmory( fromInventoryId ) ) - { - updateContainer( fromInventoryId, fromSlotId, nullptr ); - fromInventoryId = getArmoryToEquipSlot( toSlot ); - fromSlotId = static_cast < uint8_t >( m_inventoryMap[fromInventoryId]->getFreeSlot() ); - } - - auto containerTypeFrom = getContainerType( fromInventoryId ); - auto containerTypeTo = getContainerType( toInventoryId ); - - updateContainer( toInventoryId, toSlot, fromItem ); - updateContainer( fromInventoryId, fromSlotId, toItem ); -} - -void Core::Inventory::discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ) -{ - // i am not entirely sure how this should be generated or if it even is important for us... - uint32_t transactionId = 1; - - auto fromItem = m_inventoryMap[fromInventoryId]->getItem( fromSlotId ); - - deleteItemDb( fromItem ); - - m_inventoryMap[fromInventoryId]->removeItem( fromSlotId ); - updateContainer( fromInventoryId, fromSlotId, nullptr ); - - auto invTransPacket = makeZonePacket< FFXIVIpcInventoryTransaction >( m_pOwner->getId() ); - invTransPacket->data().transactionId = transactionId; - invTransPacket->data().ownerId = m_pOwner->getId(); - invTransPacket->data().storageId = fromInventoryId; - invTransPacket->data().catalogId = fromItem->getId(); - invTransPacket->data().stackSize = fromItem->getStackSize(); - invTransPacket->data().slotId = fromSlotId; - invTransPacket->data().type = 7; - m_pOwner->queuePacket( invTransPacket ); - - auto invTransFinPacket = makeZonePacket< FFXIVIpcInventoryTransactionFinish >( m_pOwner->getId() ); - invTransFinPacket->data().transactionId = transactionId; - invTransFinPacket->data().transactionId1 = transactionId; - m_pOwner->queuePacket( invTransFinPacket ); -} - -Core::ItemPtr Core::Inventory::loadItem( uint64_t uId ) -{ - auto pExdData = g_fw.get< Data::ExdDataGenerated >(); - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - // load actual item - auto itemRes = pDb->query( "SELECT catalogId, stack, flags FROM charaglobalitem WHERE itemId = " + std::to_string( uId ) + ";" ); - if( !itemRes->next() ) - return nullptr; - - try - { - auto itemInfo = pExdData->get< Core::Data::Item >( itemRes->getUInt( 1 ) ); - bool isHq = itemRes->getUInt( 3 ) == 1; - - ItemPtr pItem = make_Item( uId, - itemRes->getUInt( 1 ), - itemInfo->modelMain, - itemInfo->modelSub, - isHq ); - - pItem->setStackSize( itemRes->getUInt( 2 ) ); - - return pItem; - } - catch( ... ) - { - return nullptr; - } -} - -bool Core::Inventory::load() -{ - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - ////////////////////////////////////////////////////////////////////////////////////////////////////// - // load active gearset - auto res = pDb->query( "SELECT storageId, container_0, container_1, container_2, container_3, " - "container_4, container_5, container_6, container_7, " - "container_8, container_9, container_10, container_11, " - "container_12, container_13 " - "FROM charaitemgearset " \ - "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ - "ORDER BY storageId ASC;" ); - - while( res->next() ) - { - uint16_t storageId = res->getUInt16( 1 ); - - for( uint32_t i = 1; i <= 14; i++ ) - { - uint64_t uItemId = res->getUInt64( i + 1 ); - if( uItemId == 0 ) - continue; - - ItemPtr pItem = loadItem( uItemId ); - - if( pItem == nullptr ) - continue; - - m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; - m_pOwner->equipItem( static_cast< EquipSlot >( i - 1 ), pItem, false ); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////// - // Load Bags - auto bagRes = pDb->query( "SELECT storageId, " - "container_0, container_1, container_2, container_3, container_4, " - "container_5, container_6, container_7, container_8, container_9, " - "container_10, container_11, container_12, container_13, container_14, " - "container_15, container_16, container_17, container_18, container_19, " - "container_20, container_21, container_22, container_23, container_24, " - "container_25, container_26, container_27, container_28, container_29, " - "container_30, container_31, container_32, container_33, container_34 " - "FROM charaiteminventory " \ - "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ - "ORDER BY storageId ASC;" ); - - while( bagRes->next() ) - { - uint16_t storageId = bagRes->getUInt16( 1 ); - for( uint32_t i = 1; i <= 35; i++ ) - { - uint64_t uItemId = bagRes->getUInt64( i + 1 ); - if( uItemId == 0 ) - continue; - - ItemPtr pItem = loadItem( uItemId ); - - if( pItem == nullptr ) - continue; - - m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////////// - // Load Currency - auto curRes = pDb->query( "SELECT storageId, " - "container_0, container_1, container_2, container_3, container_4, " - "container_5, container_6, container_7, container_8, container_9, " - "container_10, container_11 " - "FROM charaitemcurrency " \ - "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ - "ORDER BY storageId ASC;" ); - - while( curRes->next() ) - { - uint16_t storageId = curRes->getUInt16( 1 ); - for( uint32_t i = 1; i <= 12; i++ ) - { - uint64_t uItemId = curRes->getUInt64( i + 1 ); - if( uItemId == 0 ) - continue; - - ItemPtr pItem = loadItem( uItemId ); - - if( pItem == nullptr ) - continue; - - m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////////// - // Load Crystals - auto crystalRes = pDb->query( "SELECT storageId, " - "container_0, container_1, container_2, container_3, container_4, " - "container_5, container_6, container_7, container_8, container_9, " - "container_10, container_11, container_12, container_13, container_14, " - "container_15, container_16, container_17 " - "FROM charaitemcrystal " \ - "WHERE CharacterId = " + std::to_string( m_pOwner->getId() ) + " " \ - "ORDER BY storageId ASC;" ); - - while( crystalRes->next() ) - { - uint16_t storageId = crystalRes->getUInt16( 1 ); - for( int32_t i = 1; i <= 17; i++ ) - { - uint64_t uItemId = crystalRes->getUInt64( i + 1 ); - if( uItemId == 0 ) - continue; - - ItemPtr pItem = loadItem( uItemId ); - - if( pItem == nullptr ) - continue; - - m_inventoryMap[storageId]->getItemMap()[i - 1] = pItem; - } - } - - return true; -} - - -void Core::Inventory::send() -{ - InventoryMap::iterator it; - - int32_t count = 0; - for( it = m_inventoryMap.begin(); it != m_inventoryMap.end(); ++it, count++ ) - { - - auto pMap = it->second->getItemMap(); - auto itM = pMap.begin(); - - for( ; itM != pMap.end(); ++itM ) - { - if( !itM->second ) - return; - - if( it->second->getId() == InventoryType::Currency || it->second->getId() == InventoryType::Crystal ) - { - auto currencyInfoPacket = makeZonePacket< FFXIVIpcCurrencyCrystalInfo >( m_pOwner->getId() ); - currencyInfoPacket->data().sequence = count; - currencyInfoPacket->data().catalogId = itM->second->getId(); - currencyInfoPacket->data().unknown = 1; - currencyInfoPacket->data().quantity = itM->second->getStackSize(); - currencyInfoPacket->data().containerId = it->second->getId(); - currencyInfoPacket->data().slot = 0; - m_pOwner->queuePacket( currencyInfoPacket ); - } - else - { - auto itemInfoPacket = makeZonePacket< FFXIVIpcItemInfo >( m_pOwner->getId() ); - itemInfoPacket->data().sequence = count; - itemInfoPacket->data().containerId = it->second->getId(); - itemInfoPacket->data().slot = itM->first; - itemInfoPacket->data().quantity = itM->second->getStackSize(); - itemInfoPacket->data().catalogId = itM->second->getId(); - itemInfoPacket->data().condition = 30000; - itemInfoPacket->data().spiritBond = 0; - itemInfoPacket->data().hqFlag = itM->second->isHq() ? 1 : 0; - m_pOwner->queuePacket( itemInfoPacket ); - } - } - - auto containerInfoPacket = makeZonePacket< FFXIVIpcContainerInfo >( m_pOwner->getId() ); - containerInfoPacket->data().sequence = count; - containerInfoPacket->data().numItems = it->second->getEntryCount(); - containerInfoPacket->data().containerId = it->second->getId(); - m_pOwner->queuePacket( containerInfoPacket ); - - - } - -} - -uint16_t Core::Inventory::calculateEquippedGearItemLevel() -{ - uint32_t iLvlResult = 0; - - auto gearSetMap = m_inventoryMap[GearSet0]->getItemMap(); - - auto it = gearSetMap.begin(); - - while( it != gearSetMap.end() ) - { - auto currItem = it->second; - - if( currItem ) - { - iLvlResult += currItem->getItemLevel(); - - // If item is weapon and isn't one-handed - if( currItem->isWeapon() && !isOneHandedWeapon( currItem->getCategory() ) ) - { - iLvlResult += currItem->getItemLevel(); - } - } - - it++; - } - - return boost::algorithm::clamp( iLvlResult / 13, 0, 9999 ); -} - - -uint8_t Core::Inventory::getFreeSlotsInBags() -{ - uint8_t slots = 0; - for( uint8_t container : { 0, 1, 2, 3 } ) - { - slots += 34 - m_inventoryMap[container]->getEntryCount(); - } - return slots; -} - - -Core::Common::ContainerType Core::Inventory::getContainerType( uint32_t containerId ) -{ - if( containerId < 5 ) - { - return Bag; - } - else if( containerId < 2000 ) - { - return GearSet; - } - else if( containerId < 3200 ) - { - return CurrencyCrystal; - } - else if( containerId < 3600 ) - { - return Armory; - } - else - { - return Common::Unknown; - } -} - -uint32_t Core::Inventory::getNextUId() -{ - uint32_t charId = 0; - auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); - auto pQR = pDb->query( "SELECT MAX(ItemId) FROM charaglobalitem" ); - - if( !pQR->next() ) - return 0x00500001; - - charId = pQR->getUInt( 1 ) + 1; - if( charId < 0x00500001 ) - return 0x00500001; - - return charId; -} diff --git a/src/servers/sapphire_zone/Inventory/Inventory.h b/src/servers/sapphire_zone/Inventory/Inventory.h deleted file mode 100644 index daaa188c..00000000 --- a/src/servers/sapphire_zone/Inventory/Inventory.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef INVENTORY_H_ -#define INVENTORY_H_ -#include -#include -#include "Forwards.h" - -namespace Core -{ - -class ItemContainer; - -using InventoryMap = std::map< uint16_t, ItemContainerPtr >; -class Inventory -{ -public: - Inventory( Entity::Player* pOwner ); - ~Inventory(); - - using InvSlotPair = std::pair< uint16_t, int8_t >; - typedef std::vector< InvSlotPair > InvSlotPairVec; - - InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); - InvSlotPair getFreeBagSlot(); - 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 swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); - 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, uint16_t quantity = 1 ); - - ItemPtr loadItem( uint64_t uId ); - - ItemPtr getItemAt( uint16_t containerId, uint8_t slotId ); - - bool updateContainer( uint16_t containerId, uint8_t slotId, ItemPtr pItem ); - - /*! heck if weapon category qualifies the weapon as onehanded */ - bool isOneHandedWeapon( Common::ItemUICategory weaponCategory ); - /*! calculate and return player ilvl based off equipped gear */ - uint16_t calculateEquippedGearItemLevel(); - /*! return the current amount of currency of type */ - uint32_t getCurrency( Common::CurrencyType type ); - /*! add amount to the current of type */ - bool addCurrency( Common::CurrencyType type, uint32_t amount ); - /*! remove amount from the currency of type */ - bool removeCurrency( Common::CurrencyType type, uint32_t amount ); - - void updateCurrencyDb(); - void updateBagDb( Common::InventoryType type ); - void updateMannequinDb( Common::InventoryType type ); - void updateItemDb( ItemPtr pItem ) const; - void deleteItemDb( ItemPtr pItem ) const; - - bool isArmory( uint16_t containerId ); - bool isEquipment( uint16_t containerId ); - uint16_t getArmoryToEquipSlot( uint8_t slotId ); - - /*! return the crystal amount of currency of type */ - uint32_t getCrystal( Common::CrystalType type ); - /*! add amount to the crystal of type */ - bool addCrystal( Common::CrystalType type, uint32_t amount ); - /*! remove amount from the crystals of type */ - bool removeCrystal( Common::CrystalType type, uint32_t amount ); - bool isObtainable( uint32_t catalogId, uint8_t quantity ); - - void updateCrystalDb(); - - bool load(); - - void send(); - - uint8_t getFreeSlotsInBags(); - - Common::ContainerType getContainerType( uint32_t containerId ); - - uint32_t getNextUId(); - - -private: - Entity::Player* m_pOwner; - InventoryMap m_inventoryMap; - const uint32_t m_maxSlotSize = 999; -}; - -} -#endif diff --git a/src/servers/sapphire_zone/Inventory/Item.cpp b/src/servers/sapphire_zone/Inventory/Item.cpp index 73727509..cf959296 100644 --- a/src/servers/sapphire_zone/Inventory/Item.cpp +++ b/src/servers/sapphire_zone/Inventory/Item.cpp @@ -24,6 +24,7 @@ Core::Item::Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t mo m_autoAttackDmg = static_cast< float >( m_weaponDmg * m_delayMs ) / 3000; m_category = static_cast< Common::ItemUICategory >( itemInfo->itemUICategory ); m_itemLevel = itemInfo->levelItem; + m_maxStackSize = itemInfo->stackSize; } float Core::Item::getAutoAttackDmg() const @@ -83,7 +84,7 @@ void Core::Item::setUId( uint64_t id ) void Core::Item::setStackSize( uint32_t size ) { - m_stackSize = size; + m_stackSize = std::min< uint32_t >( size, m_maxStackSize ); } uint32_t Core::Item::getStackSize() const @@ -126,3 +127,8 @@ void Core::Item::setHq( bool isHq ) { m_isHq = isHq; } + +uint32_t Core::Item::getMaxStackSize() const +{ + return m_maxStackSize; +} diff --git a/src/servers/sapphire_zone/Inventory/Item.h b/src/servers/sapphire_zone/Inventory/Item.h index 2801adf4..f694fccb 100644 --- a/src/servers/sapphire_zone/Inventory/Item.h +++ b/src/servers/sapphire_zone/Inventory/Item.h @@ -52,6 +52,8 @@ public: uint16_t getItemLevel() const; + uint32_t getMaxStackSize() const; + protected: uint32_t m_id; @@ -61,6 +63,7 @@ protected: Common::ItemUICategory m_category; uint32_t m_stackSize; + uint32_t m_maxStackSize; std::vector< uint8_t > m_classJobList; uint64_t m_model1; diff --git a/src/servers/sapphire_zone/Inventory/ItemContainer.cpp b/src/servers/sapphire_zone/Inventory/ItemContainer.cpp index 54a65309..e9a4d28d 100644 --- a/src/servers/sapphire_zone/Inventory/ItemContainer.cpp +++ b/src/servers/sapphire_zone/Inventory/ItemContainer.cpp @@ -11,9 +11,11 @@ extern Core::Framework g_fw; -Core::ItemContainer::ItemContainer( uint16_t locationId ) : - m_id( locationId ), - m_size( 35 ) +Core::ItemContainer::ItemContainer( uint16_t storageId, uint8_t maxSize, const std::string& tableName, bool isMultiStorage ) : + m_id( storageId ), + m_size( maxSize ), + m_tableName( tableName ), + m_bMultiStorage( isMultiStorage ) { } @@ -53,17 +55,17 @@ void Core::ItemContainer::removeItem( uint8_t slotId ) } } -Core::ItemMap & Core::ItemContainer::getItemMap() +Core::ItemMap& Core::ItemContainer::getItemMap() { return m_itemMap; } -const Core::ItemMap & Core::ItemContainer::getItemMap() const +const Core::ItemMap& Core::ItemContainer::getItemMap() const { return m_itemMap; } -int16_t Core::ItemContainer::getFreeSlot() +int8_t Core::ItemContainer::getFreeSlot() { for( uint8_t slotId = 0; slotId < m_size; slotId++ ) { @@ -95,3 +97,20 @@ void Core::ItemContainer::setItem( uint8_t slotId, ItemPtr pItem ) m_itemMap[slotId] = pItem; } + +uint8_t Core::ItemContainer::getMaxSize() const +{ + return m_size; +} + +std::string Core::ItemContainer::getTableName() const +{ + return m_tableName; +} + +bool Core::ItemContainer::isMultiStorage() const +{ + return m_bMultiStorage; +} + + diff --git a/src/servers/sapphire_zone/Inventory/ItemContainer.h b/src/servers/sapphire_zone/Inventory/ItemContainer.h index 223dd7ae..0d0334bf 100644 --- a/src/servers/sapphire_zone/Inventory/ItemContainer.h +++ b/src/servers/sapphire_zone/Inventory/ItemContainer.h @@ -10,13 +10,13 @@ namespace Core { - typedef std::map< uint8_t, ItemPtr > ItemMap; + using ItemMap = std::map< uint8_t, ItemPtr >; class ItemContainer { public: - ItemContainer( uint16_t locationId ); + ItemContainer( uint16_t storageId, uint8_t maxSize, const std::string& tableName, bool isMultiStorage ); ~ItemContainer(); uint16_t getId() const; @@ -33,11 +33,19 @@ namespace Core void setItem( uint8_t slotId, ItemPtr item ); - int16_t getFreeSlot(); + int8_t getFreeSlot(); + uint8_t getMaxSize() const; + + std::string getTableName() const; + + bool isMultiStorage() const; + private: uint16_t m_id; uint8_t m_size; + std::string m_tableName; + bool m_bMultiStorage; ItemMap m_itemMap; Entity::PlayerPtr m_pOwner; }; diff --git a/src/servers/sapphire_zone/Inventory/ItemUtil.cpp b/src/servers/sapphire_zone/Inventory/ItemUtil.cpp new file mode 100644 index 00000000..340dd7dc --- /dev/null +++ b/src/servers/sapphire_zone/Inventory/ItemUtil.cpp @@ -0,0 +1,171 @@ +#include "ItemUtil.h" + +#include "ItemContainer.h" +#include "Item.h" +#include "Framework.h" +#include + +#include +#include +#include + + +extern Core::Framework g_fw; + +bool Core::Items::Util::isArmory( uint16_t containerId ) +{ + return + containerId == Common::ArmoryBody || + containerId == Common::ArmoryEar || + containerId == Common::ArmoryFeet || + containerId == Common::ArmoryHand || + containerId == Common::ArmoryHead || + containerId == Common::ArmoryLegs || + containerId == Common::ArmoryMain || + containerId == Common::ArmoryOff || + containerId == Common::ArmoryRing || + containerId == Common::ArmoryWaist || + containerId == Common::ArmoryWrist; +} + +uint16_t Core::Items::Util::getArmoryToEquipSlot( uint8_t slotId ) +{ + switch( slotId ) + { + case Common::Body: + return Common::ArmoryBody; + + case Common::Ear: + return Common::ArmoryEar; + + case Common::Feet: + return Common::ArmoryFeet; + + case Common::Hands: + return Common::ArmoryHand; + + case Common::Legs: + return Common::ArmoryLegs; + + case Common::MainHand: + return Common::ArmoryMain; + + case Common::OffHand: + return Common::ArmoryOff; + + case Common::Ring2: + case Common::Ring1: + return Common::ArmoryRing; + + case Common::Waist: + return Common::ArmoryWaist; + + case Common::Wrist: + return Common::ArmoryWrist; + } + + return 0; +} + + + +bool Core::Items::Util::isEquipment( uint16_t containerId ) +{ + return containerId == Common::GearSet0; +} + + +bool Core::Items::Util::isOneHandedWeapon( Common::ItemUICategory weaponCategory ) +{ + switch ( weaponCategory ) + { + case Common::ItemUICategory::AlchemistsPrimaryTool: + case Common::ItemUICategory::ArmorersPrimaryTool: + case Common::ItemUICategory::BotanistsPrimaryTool: + case Common::ItemUICategory::CulinariansPrimaryTool: + case Common::ItemUICategory::OnehandedConjurersArm: + case Common::ItemUICategory::CarpentersPrimaryTool: + case Common::ItemUICategory::FishersPrimaryTool: + case Common::ItemUICategory::GladiatorsArm: + case Common::ItemUICategory::GoldsmithsPrimaryTool: + case Common::ItemUICategory::LeatherworkersPrimaryTool: + case Common::ItemUICategory::MinersPrimaryTool: + case Common::ItemUICategory::OnehandedThaumaturgesArm: + case Common::ItemUICategory::WeaversPrimaryTool: + case Common::ItemUICategory::BlacksmithsPrimaryTool: + return true; + default: + return false; + } +} + +Core::ItemPtr Core::Items::Util::loadItem( uint64_t uId ) +{ + auto pExdData = g_fw.get< Data::ExdDataGenerated >(); + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + // load actual item + auto itemRes = pDb->query( "SELECT catalogId, stack, flags FROM charaglobalitem WHERE itemId = " + std::to_string( uId ) + ";" ); + if( !itemRes->next() ) + return nullptr; + + try + { + auto itemInfo = pExdData->get< Core::Data::Item >( itemRes->getUInt( 1 ) ); + bool isHq = itemRes->getUInt( 3 ) == 1; + + ItemPtr pItem = make_Item( uId, + itemRes->getUInt( 1 ), + itemInfo->modelMain, + itemInfo->modelSub, + isHq ); + + pItem->setStackSize( itemRes->getUInt( 2 ) ); + + return pItem; + } + catch( ... ) + { + return nullptr; + } +} + +Core::Common::ContainerType Core::Items::Util::getContainerType( uint32_t containerId ) +{ + if( containerId < 5 ) + { + return Common::Bag; + } + else if( containerId < 2000 ) + { + return Common::GearSet; + } + else if( containerId < 3200 ) + { + return Common::CurrencyCrystal; + } + else if( containerId < 3600 ) + { + return Common::Armory; + } + else + { + return Common::Unknown; + } +} + + +uint32_t Core::Items::Util::getNextUId() +{ + uint32_t charId = 0; + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + auto pQR = pDb->query( "SELECT MAX(ItemId) FROM charaglobalitem" ); + + if( !pQR->next() ) + return 0x00500001; + + charId = pQR->getUInt( 1 ) + 1; + if( charId < 0x00500001 ) + return 0x00500001; + + return charId; +} diff --git a/src/servers/sapphire_zone/Inventory/ItemUtil.h b/src/servers/sapphire_zone/Inventory/ItemUtil.h new file mode 100644 index 00000000..9d0e401c --- /dev/null +++ b/src/servers/sapphire_zone/Inventory/ItemUtil.h @@ -0,0 +1,30 @@ +#ifndef SAPPHIRE_ITEMUTIL_H +#define SAPPHIRE_ITEMUTIL_H + +#include +#include "Forwards.h" + +namespace Core { +namespace Items { +namespace Util { + + ItemPtr loadItem( uint64_t uId ); + + /*! check if weapon category qualifies the weapon as onehanded */ + bool isOneHandedWeapon( Common::ItemUICategory weaponCategory ); + + bool isArmory( uint16_t containerId ); + bool isEquipment( uint16_t containerId ); + uint16_t getArmoryToEquipSlot( uint8_t slotId ); + + Common::ContainerType getContainerType( uint32_t containerId ); + + uint32_t getNextUId(); + +} +} +} + + + +#endif //SAPPHIRE_ITEMMGR_H diff --git a/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp index 4a583bc6..f009e69d 100644 --- a/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/GMCommandHandlers.cpp @@ -196,7 +196,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACKET_R case GmCommand::Inspect: { player.sendNotice( "Name: " + targetPlayer->getName() + - "\nGil: " + std::to_string( targetPlayer->getCurrency( 1 ) ) + + "\nGil: " + std::to_string( targetPlayer->getCurrency( CurrencyType::Gil ) ) + "\nZone: " + targetPlayer->getCurrentZone()->getName() + "(" + std::to_string( targetPlayer->getZoneId() ) + ")" + "\nClass: " + std::to_string( static_cast< uint8_t >( targetPlayer->getClass() ) ) + @@ -329,13 +329,13 @@ void Core::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACKET_R } case GmCommand::Gil: { - targetPlayer->addCurrency( 1, param1 ); - player.sendNotice( "Added " + std::to_string( param1 ) + " Gil for " + targetPlayer->getName() ); + targetPlayer->addCurrency( CurrencyType::Gil, param1 ); + player.sendNotice( "Added " + std::to_string( param1 ) + " Gil for " + targetPlayer->getName() ); break; } case GmCommand::Collect: { - uint32_t gil = targetPlayer->getCurrency( 1 ); + uint32_t gil = targetPlayer->getCurrency( CurrencyType::Gil ); if( gil < param1 ) { @@ -343,7 +343,7 @@ void Core::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACKET_R } else { - targetPlayer->removeCurrency( 1, param1 ); + targetPlayer->removeCurrency( CurrencyType::Gil, param1 ); player.sendNotice( "Removed " + std::to_string( param1 ) + " Gil from " + targetPlayer->getName() + "(" + std::to_string( gil ) + " before)" ); diff --git a/src/servers/sapphire_zone/Network/Handlers/InventoryHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/InventoryHandler.cpp index 71ae48cc..91e962f2 100644 --- a/src/servers/sapphire_zone/Network/Handlers/InventoryHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/InventoryHandler.cpp @@ -58,31 +58,31 @@ void Core::Network::GameConnection::inventoryModifyHandler( const Packets::FFXIV case InventoryOperation::Discard: // discard item action { - player.getInventory()->discardItem( fromContainer, fromSlot ); + player.discardItem( fromContainer, fromSlot ); } break; case InventoryOperation::Move: // move item action { - player.getInventory()->moveItem( fromContainer, fromSlot, toContainer, toSlot ); + player.moveItem( fromContainer, fromSlot, toContainer, toSlot ); } break; case InventoryOperation::Swap: // swap item action { - player.getInventory()->swapItem( fromContainer, fromSlot, toContainer, toSlot ); + player.swapItem( fromContainer, fromSlot, toContainer, toSlot ); } break; case InventoryOperation::Merge: // merge stack action { - player.getInventory()->mergeItem( fromContainer, fromSlot, toContainer, toSlot ); + player.mergeItem( fromContainer, fromSlot, toContainer, toSlot ); } break; case InventoryOperation::Split: // split stack action { - player.getInventory()->splitItem( fromContainer, fromSlot, toContainer, toSlot, splitCount ); + player.splitItem( fromContainer, fromSlot, toContainer, toSlot, splitCount ); } break; diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h index 760a1c2b..b41282de 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -6,7 +6,6 @@ #include #include "Actor/Player.h" #include "Forwards.h" -#include "Inventory/Inventory.h" #include "Inventory/Item.h" #include "StatusEffect/StatusEffect.h" @@ -50,7 +49,7 @@ namespace Server { memcpy( m_data.look, player.getLookArray(), 26 ); - auto item = player.getInventory()->getItemAt( Common::GearSet0, Common::EquipSlot::MainHand ); + auto item = player.getItemAt( Common::GearSet0, Common::EquipSlot::MainHand ); if( item ) m_data.mainWeaponModel = item->getModelId1(); m_data.secWeaponModel = player.getModelSubWeapon(); diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/UpdateInventorySlotPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/UpdateInventorySlotPacket.h new file mode 100644 index 00000000..00a9e31a --- /dev/null +++ b/src/servers/sapphire_zone/Network/PacketWrappers/UpdateInventorySlotPacket.h @@ -0,0 +1,60 @@ +#ifndef _INVENTORY_SLOT_UPDATE_PACKET_H +#define _INVENTORY_SLOT_UPDATE_PACKET_H + +#include +#include "Actor/Player.h" +#include "Inventory/Item.h" +#include "Forwards.h" + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The update inventory-slot packet. +*/ +class UpdateInventorySlotPacket : + public ZoneChannelPacket< FFXIVIpcUpdateInventorySlot > +{ +public: + UpdateInventorySlotPacket( uint32_t playerId, uint8_t slot, uint16_t storageId, const Item& item ) : + ZoneChannelPacket< FFXIVIpcUpdateInventorySlot >( playerId, playerId ) + { + initialize( slot, storageId, item ); + }; + +private: + void initialize( uint8_t slot, uint16_t storageId, const Item& item ) + { + m_data.sequence = 0; + m_data.containerId = storageId; + m_data.slot = slot; + m_data.quantity = item.getStackSize(); + m_data.catalogId = item.getId(); + m_data.reservedFlag = 0; // no idea + m_data.signatureId = 0; + m_data.hqFlag = item.isHq() ? 1 : 0; + m_data.condition = 60000; // 200% + m_data.spiritBond = 0; + m_data.color = 0; + m_data.glamourCatalogId = 0; + m_data.materia1 = 0; + m_data.materia2 = 0; + m_data.materia3 = 0; + m_data.materia4 = 0; + m_data.materia5 = 0; + //m_data.buffer1; + //uint8_t buffer2; + //uint8_t buffer3; + //uint8_t buffer4; + //uint8_t buffer5; + }; +}; + +} +} +} +} + +#endif /*_MODELEQUIPPACKET_H*/