1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-25 14:07:46 +00:00

Merge remote-tracking branch 'origin/inventory_rework' into develop

This commit is contained in:
Mordred 2018-07-26 23:43:27 +02:00
commit f5aba946be
22 changed files with 1090 additions and 1369 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -12,7 +12,6 @@ namespace Core
class ItemContainer;
class Inventory;
class Session;
class XMLConfig;
class ZonePosition;
typedef boost::shared_ptr<Zone> ZonePtr;
@ -20,7 +19,6 @@ namespace Core
typedef boost::shared_ptr<ItemContainer> ItemContainerPtr;
typedef boost::shared_ptr<Inventory> InventoryPtr;
typedef boost::shared_ptr<Session> SessionPtr;
typedef boost::shared_ptr<XMLConfig> XMLConfigPtr;
typedef boost::shared_ptr<ZonePosition> ZonePositionPtr;
namespace StatusEffect

View file

@ -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;
}

View file

@ -7,7 +7,6 @@
#include <Util/SpawnIndexAllocator.h>
#include "Chara.h"
#include "Inventory/Inventory.h"
#include "Event/EventHandler.h"
#include <map>
#include <queue>
@ -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

View file

@ -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 <boost/lexical_cast.hpp>
#include <boost/algorithm/clamp.hpp>
#include <Network/PacketDef/Zone/ServerZoneDef.h>
#include <Exd/ExdDataGenerated.h>
#include <Logging/Logger.h>
#include <Database/DatabaseDef.h>
#include "Actor/Player.h"
#include "Network/PacketWrappers/ServerNoticePacket.h"
#include "Network/PacketWrappers/ActorControlPacket143.h"
#include "Framework.h"
#include <Network/CommonActorControl.h>
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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -1,88 +0,0 @@
#ifndef INVENTORY_H_
#define INVENTORY_H_
#include <map>
#include <Common.h>
#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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
};

View file

@ -0,0 +1,171 @@
#include "ItemUtil.h"
#include "ItemContainer.h"
#include "Item.h"
#include "Framework.h"
#include <Network/CommonActorControl.h>
#include <Exd/ExdDataGenerated.h>
#include <Logging/Logger.h>
#include <Database/DatabaseDef.h>
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;
}

View file

@ -0,0 +1,30 @@
#ifndef SAPPHIRE_ITEMUTIL_H
#define SAPPHIRE_ITEMUTIL_H
#include <Common.h>
#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

View file

@ -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)" );

View file

@ -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;

View file

@ -6,7 +6,6 @@
#include <Util/Util.h>
#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();

View file

@ -0,0 +1,60 @@
#ifndef _INVENTORY_SLOT_UPDATE_PACKET_H
#define _INVENTORY_SLOT_UPDATE_PACKET_H
#include <Network/GamePacketNew.h>
#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*/