2018-03-06 22:22:19 +01:00
|
|
|
#include <Common.h>
|
|
|
|
#include <Logging/Logger.h>
|
2018-06-23 21:38:04 +02:00
|
|
|
#include <Network/CommonActorControl.h>
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
#include "Zone/Zone.h"
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
#include "Network/PacketWrappers/ActorControlPacket142.h"
|
|
|
|
#include "Network/PacketWrappers/ActorControlPacket143.h"
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
#include "Inventory/Item.h"
|
2018-07-24 23:58:08 +02:00
|
|
|
#include "Inventory/ItemContainer.h"
|
|
|
|
#include "Inventory/ItemUtil.h"
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2018-03-02 07:22:25 -03:00
|
|
|
#include "Player.h"
|
|
|
|
#include "Framework.h"
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
#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;
|
2017-11-16 00:03:36 -02:00
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
using namespace Core::Common;
|
|
|
|
using namespace Core::Network::Packets;
|
|
|
|
using namespace Core::Network::Packets::Server;
|
2018-06-23 21:38:04 +02:00
|
|
|
using namespace Core::Network::ActorControl;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Core::Entity::Player::initInventory()
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
auto setupContainer = [this]( InventoryType type )
|
|
|
|
{ m_inventoryMap[type] = make_ItemContainer( type ); };
|
|
|
|
|
|
|
|
// main bags
|
|
|
|
setupContainer( Bag0 );
|
|
|
|
setupContainer( Bag1 );
|
|
|
|
setupContainer( Bag2 );
|
|
|
|
setupContainer( Bag3 );
|
|
|
|
|
|
|
|
// gear set
|
|
|
|
setupContainer( GearSet0 );
|
|
|
|
|
|
|
|
// gil contianer
|
|
|
|
setupContainer( Currency );
|
|
|
|
|
|
|
|
// crystals??
|
|
|
|
setupContainer( Crystal );
|
|
|
|
//m_inventoryMap[0x07D3] = ItemContainerPtr( new ItemContainer( UNKNOWN_0 ) );
|
|
|
|
//m_inventoryMap[0x07D8] = ItemContainerPtr( new ItemContainer( UNKNOWN_1 ) );
|
|
|
|
|
|
|
|
// armory weapons - 0
|
|
|
|
setupContainer( ArmoryMain );
|
|
|
|
|
|
|
|
// armory offhand - 1
|
|
|
|
setupContainer( ArmoryOff );
|
|
|
|
|
|
|
|
//armory head - 2
|
|
|
|
setupContainer( ArmoryHead );
|
|
|
|
|
|
|
|
//armory body - 3
|
|
|
|
setupContainer( ArmoryBody );
|
|
|
|
|
|
|
|
//armory hand - 4
|
|
|
|
setupContainer( ArmoryHand );
|
|
|
|
|
|
|
|
//armory waist - 5
|
|
|
|
setupContainer( ArmoryWaist );
|
|
|
|
|
|
|
|
//armory legs - 6
|
|
|
|
setupContainer( ArmoryLegs );
|
|
|
|
|
|
|
|
//armory feet - 7
|
|
|
|
setupContainer( ArmoryFeet );
|
|
|
|
|
|
|
|
//neck
|
|
|
|
setupContainer( ArmotyNeck );
|
|
|
|
|
|
|
|
//earring
|
|
|
|
setupContainer( ArmoryEar );
|
|
|
|
|
|
|
|
//wrist
|
|
|
|
setupContainer( ArmoryWrist );
|
|
|
|
|
|
|
|
//armory rings - 11
|
|
|
|
setupContainer( ArmoryRing );
|
|
|
|
|
|
|
|
//soul crystals - 13
|
|
|
|
setupContainer( ArmorySoulCrystal );
|
|
|
|
|
|
|
|
loadInventory();
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-11-16 00:03:36 -02:00
|
|
|
void Core::Entity::Player::sendItemLevel()
|
|
|
|
{
|
2018-06-28 00:07:07 +02:00
|
|
|
queuePacket( boost::make_shared< ActorControlPacket142 >( getId(), SetItemLevel, getItemLevel(), 0 ) );
|
2017-11-16 00:03:36 -02:00
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
// TODO: This has to be redone and simplified
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::equipWeapon( ItemPtr pItem )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
ClassJob currentClass = static_cast< ClassJob >( getClass() );
|
|
|
|
|
|
|
|
switch( pItem->getCategory() )
|
|
|
|
{
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::PugilistsArm:
|
|
|
|
if( currentClass != ClassJob::Pugilist &&
|
|
|
|
currentClass != ClassJob::Monk )
|
|
|
|
setClassJob( ClassJob::Pugilist );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::GladiatorsArm:
|
|
|
|
if( currentClass != ClassJob::Gladiator &&
|
|
|
|
currentClass != ClassJob::Paladin )
|
|
|
|
setClassJob( ClassJob::Gladiator );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::MaraudersArm:
|
|
|
|
if( currentClass != ClassJob::Marauder &&
|
|
|
|
currentClass != ClassJob::Warrior )
|
|
|
|
setClassJob( ClassJob::Marauder );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::ArchersArm:
|
|
|
|
if( currentClass != ClassJob::Archer &&
|
|
|
|
currentClass != ClassJob::Bard )
|
|
|
|
setClassJob( ClassJob::Archer );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::LancersArm:
|
|
|
|
if( currentClass != ClassJob::Lancer &&
|
|
|
|
currentClass != ClassJob::Dragoon )
|
|
|
|
setClassJob( ClassJob::Lancer );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::OnehandedThaumaturgesArm:
|
|
|
|
case ItemUICategory::TwohandedThaumaturgesArm:
|
|
|
|
if( currentClass != ClassJob::Thaumaturge &&
|
|
|
|
currentClass != ClassJob::Blackmage )
|
|
|
|
setClassJob( ClassJob::Thaumaturge );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::OnehandedConjurersArm:
|
|
|
|
case ItemUICategory::TwohandedConjurersArm:
|
|
|
|
if( currentClass != ClassJob::Conjurer &&
|
|
|
|
currentClass != ClassJob::Whitemage )
|
|
|
|
setClassJob( ClassJob::Conjurer );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
2017-11-18 01:25:55 +01:00
|
|
|
case ItemUICategory::ArcanistsGrimoire:
|
|
|
|
if( currentClass != ClassJob::Arcanist &&
|
|
|
|
currentClass != ClassJob::Summoner &&
|
|
|
|
currentClass != ClassJob::Scholar )
|
|
|
|
setClassJob( ClassJob::Arcanist );
|
2017-08-08 13:53:47 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// equip an item
|
2018-07-20 22:39:10 +02:00
|
|
|
void Core::Entity::Player::equipItem( Common::EquipSlot equipSlotId, ItemPtr pItem, bool sendUpdate )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
|
2018-03-02 07:22:25 -03:00
|
|
|
//g_framework.getLogger().debug( "Equipping into slot " + std::to_string( equipSlotId ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
uint64_t model = pItem->getModelId1();
|
|
|
|
uint64_t model2 = pItem->getModelId2();
|
|
|
|
|
|
|
|
switch( equipSlotId )
|
|
|
|
{
|
2018-07-20 22:39:10 +02:00
|
|
|
case Common::EquipSlot::MainHand:
|
2017-08-08 13:53:47 +02:00
|
|
|
m_modelMainWeapon = model;
|
|
|
|
m_modelSubWeapon = model2;
|
|
|
|
// TODO: add job change upon changing weapon if needed
|
|
|
|
// equipWeapon( pItem );
|
|
|
|
break;
|
|
|
|
|
2018-07-20 22:39:10 +02:00
|
|
|
case Common::EquipSlot::OffHand:
|
2017-08-08 13:53:47 +02:00
|
|
|
m_modelSubWeapon = model;
|
|
|
|
break;
|
|
|
|
|
2018-07-20 22:39:10 +02:00
|
|
|
case Common::EquipSlot::SoulCrystal:
|
2017-08-08 13:53:47 +02:00
|
|
|
// TODO: add Job change on equipping crystal
|
|
|
|
// change job
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // any other slot
|
|
|
|
m_modelEquip[static_cast< uint8_t >( equipSlotId )] = static_cast< uint32_t >( model );
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-11-16 20:44:37 -02:00
|
|
|
if( sendUpdate )
|
|
|
|
{
|
2017-08-08 13:53:47 +02:00
|
|
|
this->sendModel();
|
2018-07-24 23:58:08 +02:00
|
|
|
m_itemLevel = calculateEquippedGearItemLevel();
|
2017-11-16 20:44:37 -02:00
|
|
|
sendItemLevel();
|
|
|
|
}
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-20 22:39:10 +02:00
|
|
|
void Core::Entity::Player::unequipItem( Common::EquipSlot equipSlotId, ItemPtr pItem )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
m_modelEquip[static_cast< uint8_t >( equipSlotId )] = 0;
|
|
|
|
sendModel();
|
2017-11-16 00:03:36 -02:00
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
m_itemLevel = calculateEquippedGearItemLevel();
|
2017-11-16 00:03:36 -02:00
|
|
|
sendItemLevel();
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: these next functions are so similar that they could likely be simplified
|
2018-07-24 23:58:08 +02:00
|
|
|
void Core::Entity::Player::addCurrency( CurrencyType type, uint32_t amount )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
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 );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2018-06-28 00:07:07 +02:00
|
|
|
auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() );
|
2018-07-20 22:39:10 +02:00
|
|
|
invUpPacket->data().containerId = Common::InventoryType::Currency;
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().catalogId = 1;
|
2018-07-24 23:58:08 +02:00
|
|
|
invUpPacket->data().quantity = getCurrency( type );
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
queuePacket( invUpPacket );
|
|
|
|
}
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
void Core::Entity::Player::removeCurrency( Common::CurrencyType type, uint32_t amount )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
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 );
|
2018-06-28 00:07:07 +02:00
|
|
|
auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() );
|
2018-07-20 22:39:10 +02:00
|
|
|
invUpPacket->data().containerId = Common::InventoryType::Currency;
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().catalogId = 1;
|
2018-07-24 23:58:08 +02:00
|
|
|
invUpPacket->data().quantity = getCurrency( type );
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
queuePacket( invUpPacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
void Core::Entity::Player::addCrystal( Common::CrystalType type, uint32_t amount )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
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 );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2018-06-28 00:07:07 +02:00
|
|
|
auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() );
|
2018-07-20 22:39:10 +02:00
|
|
|
invUpPacket->data().containerId = Common::InventoryType::Crystal;
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().catalogId = static_cast< uint8_t >( type ) + 1;
|
2018-07-24 23:58:08 +02:00
|
|
|
invUpPacket->data().quantity = getCrystal( type );
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
queuePacket( invUpPacket );
|
|
|
|
|
2018-06-28 00:07:07 +02:00
|
|
|
queuePacket( boost::make_shared< ActorControlPacket143 >( getId(), ItemObtainIcon,
|
|
|
|
static_cast< uint8_t >( type ) + 1, amount ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
void Core::Entity::Player::removeCrystal( Common::CrystalType type, uint32_t amount )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 );
|
|
|
|
|
|
|
|
if( !currItem )
|
2017-08-08 13:53:47 +02:00
|
|
|
return;
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
uint32_t currentAmount = currItem->getStackSize();
|
|
|
|
if( amount > currentAmount )
|
|
|
|
currItem->setStackSize( 0 );
|
|
|
|
else
|
|
|
|
currItem->setStackSize( currentAmount - amount );
|
|
|
|
|
|
|
|
updateItemDb( currItem );
|
|
|
|
|
2018-06-28 00:07:07 +02:00
|
|
|
auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( getId() );
|
2018-07-20 22:39:10 +02:00
|
|
|
invUpPacket->data().containerId = Common::InventoryType::Crystal;
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().catalogId = static_cast< uint8_t >( type ) + 1;
|
2018-07-24 23:58:08 +02:00
|
|
|
invUpPacket->data().quantity = getCrystal( static_cast< Common::CrystalType >( type ) );
|
2018-06-28 00:07:07 +02:00
|
|
|
invUpPacket->data().slot = static_cast< uint8_t >( type ) - 1;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
queuePacket( invUpPacket );
|
|
|
|
}
|
|
|
|
|
2017-10-22 18:53:58 -07:00
|
|
|
bool Core::Entity::Player::tryAddItem( uint16_t catalogId, uint32_t quantity )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
|
2017-10-22 18:53:58 -07:00
|
|
|
for( uint16_t i = 0; i < 4; i++ )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
if( addItem( i, -1, catalogId, quantity ) != -1 )
|
2017-08-08 13:53:47 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
/*bool Core::Entity::Player::addItem( uint16_t containerId, uint16_t catalogId, uint32_t quantity )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
if( addItem( containerId, -1, catalogId, quantity ) != -1 )
|
2017-08-08 13:53:47 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
2018-07-24 23:58:08 +02:00
|
|
|
}*/
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendInventory()
|
|
|
|
{
|
|
|
|
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 >( 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 );
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::Player::InvSlotPairVec Core::Entity::Player::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::Entity::Player::InvSlotPair Core::Entity::Player::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::Entity::Player::getItemAt( uint16_t containerId, uint8_t slotId )
|
|
|
|
{
|
|
|
|
return m_inventoryMap[containerId]->getItem( slotId );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t Core::Entity::Player::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::Entity::Player::getCrystal( CrystalType type )
|
|
|
|
{
|
|
|
|
|
|
|
|
auto currItem = m_inventoryMap[Crystal]->getItem( static_cast< uint8_t >( type ) - 1 );
|
|
|
|
|
|
|
|
if( !currItem )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return currItem->getStackSize();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::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( getId() );
|
|
|
|
|
|
|
|
pDb->execute( query );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Core::Entity::Player::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( getId() );
|
|
|
|
|
|
|
|
pDb->execute( query );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::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( getId() ) +
|
|
|
|
" AND storageId = " + std::to_string( static_cast< uint16_t >( type ) );
|
|
|
|
|
|
|
|
pDb->execute( query );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Core::Entity::Player::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( getId() ) +
|
|
|
|
" AND storageId = " + std::to_string( static_cast< uint16_t >( type ) );
|
|
|
|
|
|
|
|
pLog->debug( query );
|
|
|
|
pDb->execute( query );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
|
|
|
|
void Core::Entity::Player::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::Entity::Player::deleteItemDb( Core::ItemPtr item ) const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2018-07-24 23:58:08 +02:00
|
|
|
auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >();
|
|
|
|
pDb->execute( "UPDATE charaglobalitem SET IS_DELETE = 1 WHERE itemId = " + std::to_string( item->getUId() ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 23:58:08 +02:00
|
|
|
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
|
|
|
|
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( getId() ) );
|
|
|
|
|
|
|
|
|
|
|
|
auto invUpPacket = makeZonePacket< FFXIVIpcUpdateInventorySlot >( 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;
|
|
|
|
queuePacket( invUpPacket );
|
|
|
|
|
|
|
|
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_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 )
|
|
|
|
{
|
|
|
|
equipItem( static_cast< EquipSlot >( toSlot ), tmpItem, true );
|
|
|
|
updateMannequinDb( static_cast< InventoryType >( toInventoryId ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( static_cast< InventoryType >( fromInventoryId ) == GearSet0 )
|
|
|
|
{
|
|
|
|
unequipItem( static_cast< EquipSlot >( fromSlotId ), tmpItem );
|
|
|
|
updateMannequinDb( static_cast< InventoryType >( fromInventoryId ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::updateContainer( uint16_t containerId, uint8_t slotId, ItemPtr pItem )
|
|
|
|
{
|
|
|
|
auto containerType = Items::Util::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 )
|
|
|
|
equipItem( static_cast< EquipSlot >( slotId ), pItem, true );
|
|
|
|
else
|
|
|
|
unequipItem( static_cast< EquipSlot >( slotId ), pItem );
|
|
|
|
|
|
|
|
updateMannequinDb( static_cast< InventoryType >( containerId ) );
|
|
|
|
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_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::Entity::Player::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 >( fromItem->getMaxStackSize(), 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::Entity::Player::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( 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_inventoryMap[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_inventoryMap[fromInventoryId]->getItem( fromSlotId );
|
|
|
|
|
|
|
|
deleteItemDb( fromItem );
|
|
|
|
|
|
|
|
m_inventoryMap[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_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() && !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 : { 0, 1, 2, 3 } )
|
|
|
|
{
|
|
|
|
slots += 34 - m_inventoryMap[container]->getEntryCount();
|
|
|
|
}
|
|
|
|
return slots;
|
|
|
|
}
|