mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-27 22:57:45 +00:00
Merge pull request #459 from NotAdam/housing
[wip] housing object placement!!!!
This commit is contained in:
commit
f030e759af
41 changed files with 2102 additions and 292 deletions
|
@ -404,9 +404,6 @@ CREATE TABLE `house` (
|
|||
`Comment` binary(193) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`HouseName` binary(23) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`Endorsements` bigint(20) DEFAULT NULL,
|
||||
`HousePartModels` binary(32) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`HousePartColours` binary(8) DEFAULT '\0\0\0\0\0\0\0\0',
|
||||
`HouseInteriorModels` binary(40) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
|
||||
`UPDATE_DATE` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY(`HouseId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
@ -530,13 +527,15 @@ CREATE TABLE `landset` (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
CREATE TABLE `houseiteminventory` (
|
||||
`LandIdent` BIGINT(20) UNSIGNED NOT NULL,
|
||||
`ContainerId` INT(10) UNSIGNED NOT NULL,
|
||||
`ItemId` INT(20) NOT NULL,
|
||||
`SlotId` INT(10) UNSIGNED NOT NULL,
|
||||
INDEX `landIdent` (`landIdent`)
|
||||
`LandIdent` BIGINT(20) UNSIGNED NOT NULL,
|
||||
`ContainerId` INT(10) UNSIGNED NOT NULL,
|
||||
`SlotId` INT(10) UNSIGNED NOT NULL,
|
||||
`ItemId` INT(20) NOT NULL,
|
||||
PRIMARY KEY (`LandIdent`, `ContainerId`, `SlotId`),
|
||||
INDEX `landIdent` (`LandIdent`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
|
||||
CREATE TABLE `spawngroup` (
|
||||
`id` int(10) NOT NULL AUTO_INCREMENT,
|
||||
`territoryTypeId` int(5) NOT NULL,
|
||||
|
@ -564,3 +563,14 @@ CREATE TABLE `zonepositions` (
|
|||
`radius` int(11) NOT NULL DEFAULT '2',
|
||||
PRIMARY KEY(`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
|
||||
|
||||
CREATE TABLE `landplaceditems` (
|
||||
`ItemId` INT(20) UNSIGNED NOT NULL,
|
||||
`PosX` INT(10) NOT NULL,
|
||||
`PosY` INT(10) NOT NULL,
|
||||
`PosZ` INT(10) NOT NULL,
|
||||
`Rotation` INT(10) NOT NULL,
|
||||
PRIMARY KEY (`ItemId`)
|
||||
)
|
||||
COLLATE='latin1_swedish_ci' ENGINE=InnoDB;
|
||||
|
||||
|
|
|
@ -409,6 +409,7 @@ void PlayerMinimal::insertDbGlobalItem( uint32_t itemId, uint64_t uniqueId ) con
|
|||
stmtItemGlobal->setInt( 1, m_id );
|
||||
stmtItemGlobal->setInt64( 2, uniqueId );
|
||||
stmtItemGlobal->setInt( 3, itemId );
|
||||
stmtItemGlobal->setInt( 4, 1 ); // stack of 1
|
||||
g_charaDb.directExecute( stmtItemGlobal );
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,13 @@ namespace Sapphire::Common
|
|||
float z;
|
||||
};
|
||||
|
||||
struct FFXIVARR_POSITION3_U16
|
||||
{
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint16_t z;
|
||||
};
|
||||
|
||||
struct ActiveLand
|
||||
{
|
||||
uint8_t ward;
|
||||
|
@ -225,13 +232,15 @@ namespace Sapphire::Common
|
|||
// housing interior containers
|
||||
HousingInteriorAppearance = 25002,
|
||||
|
||||
// 50 in each container max, 300 slots max
|
||||
// 50 in each container max, 400 slots max
|
||||
HousingInteriorPlacedItems1 = 25003,
|
||||
HousingInteriorPlacedItems2 = 25004,
|
||||
HousingInteriorPlacedItems3 = 25005,
|
||||
HousingInteriorPlacedItems4 = 25006,
|
||||
HousingInteriorPlacedItems5 = 25007,
|
||||
HousingInteriorPlacedItems6 = 25008,
|
||||
HousingInteriorPlacedItems7 = 25009,
|
||||
HousingInteriorPlacedItems8 = 25010,
|
||||
|
||||
// 50 max per container, 400 slots max
|
||||
// slot limit increased 'temporarily' for relocation for all estates
|
||||
|
@ -247,9 +256,9 @@ namespace Sapphire::Common
|
|||
|
||||
|
||||
// housing exterior containers
|
||||
HousingOutdoorPlacedItems = 25001,
|
||||
HousingOutdoorAppearance = 25000,
|
||||
HousingOutdoorStoreroom = 27000,
|
||||
HousingExteriorAppearance = 25000,
|
||||
HousingExteriorPlacedItems = 25001,
|
||||
HousingExteriorStoreroom = 27000,
|
||||
|
||||
|
||||
};
|
||||
|
@ -769,8 +778,9 @@ namespace Sapphire::Common
|
|||
MountSkill = 0xD,
|
||||
};
|
||||
|
||||
enum HousePartSlot
|
||||
enum HouseExteriorSlot
|
||||
{
|
||||
HousePermit,
|
||||
ExteriorRoof,
|
||||
ExteriorWall,
|
||||
ExteriorWindow,
|
||||
|
@ -842,13 +852,11 @@ namespace Sapphire::Common
|
|||
uint32_t unkown1; //12
|
||||
};
|
||||
|
||||
struct YardObject
|
||||
struct HousingObject
|
||||
{
|
||||
uint32_t itemId;
|
||||
uint16_t itemRotation;
|
||||
uint16_t pos_x;
|
||||
uint16_t pos_y;
|
||||
uint16_t pos_z;
|
||||
Common::FFXIVARR_POSITION3_U16 pos;
|
||||
};
|
||||
|
||||
enum HouseSize : uint8_t
|
||||
|
|
|
@ -172,8 +172,8 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements()
|
|||
|
||||
/// ITEM GLOBAL
|
||||
prepareStatement( CHARA_ITEMGLOBAL_INS,
|
||||
"INSERT INTO charaglobalitem ( CharacterId, ItemId, catalogId, UPDATE_DATE ) VALUES ( ?, ?, ?, NOW() );",
|
||||
CONNECTION_BOTH );
|
||||
"INSERT INTO charaglobalitem ( CharacterId, ItemId, catalogId, stack, UPDATE_DATE ) VALUES ( ?, ?, ?, ?, NOW() );",
|
||||
CONNECTION_SYNC );
|
||||
|
||||
/// BNPC TEMPLATES
|
||||
prepareStatement( ZONE_SEL_BNPCTEMPLATES,
|
||||
|
@ -193,7 +193,7 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements()
|
|||
|
||||
/// HOUSING
|
||||
prepareStatement( HOUSING_HOUSE_INS,
|
||||
"INSERT INTO house ( LandSetId, HouseId ) VALUES ( ?, ? );",
|
||||
"INSERT INTO house ( LandSetId, HouseId, HouseName ) VALUES ( ?, ?, ? );",
|
||||
CONNECTION_BOTH );
|
||||
|
||||
prepareStatement( HOUSING_HOUSE_UP,
|
||||
|
@ -201,10 +201,13 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements()
|
|||
CONNECTION_BOTH );
|
||||
|
||||
prepareStatement( LAND_INV_SEL_ALL,
|
||||
"SELECT houseiteminventory.*, charaglobalitem.catalogId, charaglobalitem.stain, charaglobalitem.CharacterId "
|
||||
"SELECT houseiteminventory.*, charaglobalitem.catalogId, charaglobalitem.stain, charaglobalitem.CharacterId, "
|
||||
"landplaceditems.PosX, landplaceditems.PosY, landplaceditems.PosZ, landplaceditems.Rotation "
|
||||
"FROM houseiteminventory "
|
||||
"LEFT JOIN charaglobalitem "
|
||||
"ON houseiteminventory.ItemId = charaglobalitem.itemId;",
|
||||
"ON houseiteminventory.ItemId = charaglobalitem.itemId "
|
||||
"LEFT JOIN landplaceditems "
|
||||
"ON houseiteminventory.ItemId = landplaceditems.ItemId;",
|
||||
CONNECTION_BOTH );
|
||||
|
||||
prepareStatement( LAND_INV_SEL_HOUSE,
|
||||
|
@ -222,6 +225,28 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements()
|
|||
"ON land.HouseId = house.HouseId;",
|
||||
CONNECTION_SYNC );
|
||||
|
||||
prepareStatement( LAND_INV_UP,
|
||||
"INSERT INTO houseiteminventory ( LandIdent, ContainerId, SlotId, ItemId ) "
|
||||
"VALUES ( ?, ?, ?, ? ) "
|
||||
"ON DUPLICATE KEY UPDATE ItemId = ?;",
|
||||
CONNECTION_BOTH );
|
||||
|
||||
prepareStatement( LAND_INV_DEL,
|
||||
"DELETE FROM houseiteminventory "
|
||||
"WHERE LandIdent = ? AND ContainerId = ? AND SlotId = ?;",
|
||||
CONNECTION_BOTH );
|
||||
|
||||
prepareStatement( LAND_INV_UP_ITEMPOS,
|
||||
"INSERT INTO landplaceditems ( ItemId, PosX, PosY, PosZ, Rotation ) "
|
||||
"VALUES ( ?, ?, ?, ?, ? ) "
|
||||
"ON DUPLICATE KEY UPDATE PosX = ?, PosY = ?, PosZ = ?, Rotation = ?;",
|
||||
CONNECTION_BOTH );
|
||||
|
||||
prepareStatement( LAND_INV_DEL_ITEMPOS,
|
||||
"DELETE FROM landplaceditems "
|
||||
"WHERE ItemId = ?;",
|
||||
CONNECTION_BOTH );
|
||||
|
||||
/*prepareStatement( LAND_INS,
|
||||
"INSERT INTO land ( LandSetId ) VALUES ( ? );",
|
||||
CONNECTION_BOTH );
|
||||
|
|
|
@ -91,6 +91,8 @@ namespace Sapphire::Db
|
|||
LAND_INV_SEL_HOUSE,
|
||||
LAND_INV_DEL,
|
||||
LAND_INV_UP,
|
||||
LAND_INV_UP_ITEMPOS,
|
||||
LAND_INV_DEL_ITEMPOS,
|
||||
|
||||
|
||||
MAX_STATEMENTS
|
||||
|
|
|
@ -213,8 +213,34 @@ enum ActorControlType : uint16_t
|
|||
// Housing
|
||||
ShowHousingItemUI = 0x3F7,
|
||||
ShowBuildPresetUI = 0x3E9,
|
||||
/*!
|
||||
* param1 = plot id
|
||||
*/
|
||||
ShowEstateExternalAppearanceUI = 0x3EA,
|
||||
ShowEstateInternalAppearanceUI = 0x3EB,
|
||||
BuildPresetResponse = 0x3ED,
|
||||
|
||||
/*!
|
||||
* param1 = u16 landid
|
||||
* u16 slotid
|
||||
*/
|
||||
RemoveExteriorHousingItem = 0x3EF,
|
||||
|
||||
/*!
|
||||
* param1 = object array index
|
||||
*/
|
||||
RemoveInteriorHousingItem = 0x3F1,
|
||||
|
||||
/*!
|
||||
* param1 = identity shit
|
||||
* u16 1 - container id
|
||||
* u16 2 - plot id
|
||||
* param2 = item shit
|
||||
* u16 1 - slot
|
||||
*/
|
||||
HousingItemMoveConfirm = 0x3F9,
|
||||
OpenEstateSettingsUI = 0x3FF,
|
||||
|
||||
/*!
|
||||
* param1 = outdoor furnishings
|
||||
* u8 0 - relocation available, 1 = available
|
||||
|
@ -306,6 +332,8 @@ enum ActorControlType : uint16_t
|
|||
|
||||
SetEstateLightingLevel = 0x40B, // param1 = light level 0 - 5 maps to UI val 5-0
|
||||
RequestHousingBuildPreset = 0x44C,
|
||||
RequestEstateExteriorRemodel = 0x044D, // param11 = land id
|
||||
RequestEstateInteriorRemodel = 0x44E,
|
||||
RequestEstateHallRemoval = 0x44F,
|
||||
RequestBuildPreset = 0x450, // no idea what this is, it gets sent with BuildPresetHandler and has the plot id in param1
|
||||
RequestLandSignFree = 0x451,
|
||||
|
@ -313,6 +341,7 @@ enum ActorControlType : uint16_t
|
|||
RequestWardLandInfo = 0x453,
|
||||
RequestLandRelinquish = 0x454,
|
||||
RequestLandInventory = 0x0458,
|
||||
RequestHousingItemRemove = 0x0459,
|
||||
RequestEstateRename = 0x45A,
|
||||
RequestEstateEditGreeting = 0x45B,
|
||||
RequestEstateGreeting = 0x45C, // sends FFXIVIpcHousingEstateGreeting in return
|
||||
|
@ -322,6 +351,7 @@ enum ActorControlType : uint16_t
|
|||
RequestHousingItemUI = 0x463,
|
||||
RequestSharedEstateSettings = 0x46F,
|
||||
UpdateEstateLightingLevel = 0x471,
|
||||
HousingItemSelectedInUI = 0x47E,
|
||||
|
||||
CompanionAction = 0x6A4,
|
||||
CompanionSetBarding = 0x6A5,
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace Sapphire::Network::Packets
|
|||
|
||||
// Set the values of static fields.
|
||||
// The size must be the sum of the segment header and the content
|
||||
m_segHdr.size = sizeof( FFXIVARR_PACKET_SEGMENT_HEADER ) + getContentSize();
|
||||
m_segHdr.size = static_cast< uint32_t >( sizeof( FFXIVARR_PACKET_SEGMENT_HEADER ) + getContentSize() );
|
||||
m_segHdr.type = getSegmentType();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ void Sapphire::Network::Packets::PacketContainer::addPacket( Sapphire::Network::
|
|||
{
|
||||
m_entryList.push_back( entry );
|
||||
|
||||
m_ipcHdr.size += entry->getSize();
|
||||
m_ipcHdr.size += static_cast< uint32_t >( entry->getSize() );
|
||||
m_ipcHdr.count++;
|
||||
}
|
||||
|
||||
|
|
|
@ -201,9 +201,10 @@ namespace Sapphire::Network::Packets
|
|||
HousingShowEstateGuestAccess = 0x022A, // updated 4.4
|
||||
|
||||
HousingObjectInitialize = 0x022C, // updated 4.4
|
||||
HousingInternalObjectSpawn = 0x22D, // updated 4.4
|
||||
|
||||
HousingWardInfo = 0x022F, // updated 4.4
|
||||
YardObjectMove = 0x0230, // updated 4.4
|
||||
HousingObjectMove = 0x0230, // updated 4.4
|
||||
|
||||
SharedEstateSettingsResponse = 0x023C, // updated 4.4
|
||||
|
||||
|
@ -294,6 +295,8 @@ namespace Sapphire::Network::Packets
|
|||
|
||||
InventoryModifyHandler = 0x0142, // updated 4.4
|
||||
|
||||
ReqPlaceHousingItem = 0x145, // updated 4.4
|
||||
|
||||
BuildPresetHandler = 0x014A, // updated 4.4
|
||||
TalkEventHandler = 0x014B, // updated 4.4
|
||||
EmoteEventHandler = 0x014C, // updated 4.4
|
||||
|
@ -311,6 +314,7 @@ namespace Sapphire::Network::Packets
|
|||
|
||||
LandRenameHandler = 0x0171, // updated 4.4
|
||||
HousingUpdateHouseGreeting = 0x0172, // updated 4.4
|
||||
HousingUpdateObjectPosition = 0x0173, // updated 4.4
|
||||
|
||||
SetSharedEstateSettings = 0x0177, // updated 4.4
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@ struct FFXIVIpcClientTrigger :
|
|||
/* 0004 */ uint32_t param11;
|
||||
/* 0008 */ uint32_t param12;
|
||||
/* 000C */ uint32_t param2;
|
||||
/* 0010 */ char unk_10[8];
|
||||
/* 0010 */ uint32_t param4; // todo: really?
|
||||
/* 0014 */ uint32_t param5;
|
||||
/* 0018 */ uint64_t param3;
|
||||
};
|
||||
|
||||
|
@ -240,6 +241,35 @@ struct FFXIVIpcMarketBoardRequestItemListings :
|
|||
/* 0004 */ uint32_t padding;
|
||||
};
|
||||
|
||||
struct FFXIVIpcReqPlaceHousingItem :
|
||||
FFXIVIpcBasePacket< ReqPlaceHousingItem >
|
||||
{
|
||||
/* 0000 */ uint16_t landId; // 0 when plot 0 or inside an estate
|
||||
/* 0002 */ uint16_t unknown1;
|
||||
/* 0004 */ uint32_t unknown2;
|
||||
/* 0008 */ uint16_t sourceInvContainerId;
|
||||
/* 000A */ uint16_t sourceInvSlotId;
|
||||
|
||||
/* 000C */ Common::FFXIVARR_POSITION3 position;
|
||||
/* 0018 */ float rotation;
|
||||
|
||||
/* 001C */ uint32_t unknown3; // always 1?
|
||||
/* 0020 */ uint32_t unknown4[2]; // always 0 it looks like
|
||||
};
|
||||
|
||||
struct FFXIVIpcHousingUpdateObjectPosition :
|
||||
FFXIVIpcBasePacket< HousingUpdateObjectPosition >
|
||||
{
|
||||
/* 0000 */ Common::LandIdent ident;
|
||||
/* 0008 */ uint16_t slot;
|
||||
/* 000A */ uint16_t unk;
|
||||
|
||||
/* 000C */ Common::FFXIVARR_POSITION3 pos;
|
||||
/* 0018 */ float rotation;
|
||||
|
||||
/* 001C */ uint32_t padding;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1679,26 +1679,20 @@ struct FFXIVIpcLandSetInitialize : FFXIVIpcBasePacket< LandSetInitialize >
|
|||
LandStruct land[ 30 ];
|
||||
};
|
||||
|
||||
struct FFXIVIpcYardObjectSpawn : FFXIVIpcBasePacket<YardObjectSpawn>
|
||||
struct FFXIVIpcYardObjectSpawn : FFXIVIpcBasePacket< YardObjectSpawn >
|
||||
{
|
||||
uint8_t landSetId;
|
||||
uint8_t landId;
|
||||
uint8_t objectArray;
|
||||
uint16_t unknown1;
|
||||
uint32_t itemId;
|
||||
uint16_t itemRotation;
|
||||
uint16_t pos_x;
|
||||
uint16_t pos_y;
|
||||
uint16_t pos_z;
|
||||
Common::HousingObject object;
|
||||
};
|
||||
|
||||
struct FFXIVIpcYardObjectMove : FFXIVIpcBasePacket<YardObjectMove>
|
||||
struct FFXIVIpcHousingObjectMove : FFXIVIpcBasePacket< HousingObjectMove >
|
||||
{
|
||||
uint16_t itemRotation;
|
||||
uint8_t objectArray;
|
||||
uint8_t landSetId;
|
||||
uint16_t pos_x;
|
||||
uint16_t pos_y;
|
||||
uint16_t pos_z;
|
||||
uint8_t landId;
|
||||
Common::FFXIVARR_POSITION3_U16 pos;
|
||||
uint16_t unknown1;
|
||||
uint16_t unknown2;
|
||||
uint16_t unknown3;
|
||||
|
@ -1711,10 +1705,23 @@ struct FFXIVIpcHousingObjectInitialize : FFXIVIpcBasePacket< HousingObjectInitia
|
|||
uint8_t packetNum;
|
||||
uint8_t packetTotal;
|
||||
uint8_t u2; //Outdoor 0 / Indoor 100(?)
|
||||
Common::YardObject object[100];
|
||||
Common::HousingObject object[100];
|
||||
uint32_t unknown4; //unused
|
||||
};
|
||||
|
||||
struct FFXIVIpcHousingInternalObjectSpawn : FFXIVIpcBasePacket< HousingInternalObjectSpawn >
|
||||
{
|
||||
uint16_t containerId;
|
||||
uint8_t containerOffset;
|
||||
uint8_t pad1;
|
||||
|
||||
uint16_t itemId;
|
||||
uint8_t unk2;
|
||||
uint8_t pad2;
|
||||
uint16_t rotation;
|
||||
Common::FFXIVARR_POSITION3_U16 pos;
|
||||
};
|
||||
|
||||
struct FFXIVIpcHousingIndoorInitialize : FFXIVIpcBasePacket< HousingIndoorInitialize >
|
||||
{
|
||||
uint16_t u1;
|
||||
|
|
|
@ -365,6 +365,10 @@ namespace Sapphire::Entity
|
|||
|
||||
Common::GearModelSlot equipSlotToModelSlot( Common::GearSetSlot slot );
|
||||
|
||||
bool getFreeInventoryContainerSlot( Inventory::InventoryContainerPair& containerPair ) const;
|
||||
|
||||
void insertInventoryItem( Common::InventoryType type, uint16_t slot, const Sapphire::ItemPtr item );
|
||||
|
||||
/*!
|
||||
* Collect real item handins from container
|
||||
* @param itemIds a vector of each catalog id to collect
|
||||
|
@ -919,6 +923,8 @@ namespace Sapphire::Entity
|
|||
void setActiveLand( uint8_t land, uint8_t ward );
|
||||
Common::ActiveLand getActiveLand() const;
|
||||
|
||||
Sapphire::ItemPtr dropInventoryItem( Common::InventoryType type, uint16_t slotId );
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -754,7 +754,7 @@ void Sapphire::Entity::Player::swapItem( uint16_t fromInventoryId, uint8_t fromS
|
|||
void Sapphire::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;
|
||||
uint32_t transactionId = getNextInventorySequence();
|
||||
|
||||
auto fromItem = m_storageMap[ fromInventoryId ]->getItem( fromSlotId );
|
||||
|
||||
|
@ -869,3 +869,70 @@ uint32_t Sapphire::Entity::Player::getNextInventorySequence()
|
|||
{
|
||||
return m_inventorySequence++;
|
||||
}
|
||||
|
||||
Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common::InventoryType type, uint16_t slotId )
|
||||
{
|
||||
auto& container = m_storageMap[ type ];
|
||||
|
||||
auto item = container->getItem( slotId );
|
||||
if( !item )
|
||||
return nullptr;
|
||||
|
||||
// unlink item
|
||||
container->removeItem( slotId );
|
||||
updateContainer( type, slotId, nullptr );
|
||||
|
||||
auto seq = getNextInventorySequence();
|
||||
|
||||
// send inv update
|
||||
auto invTransPacket = makeZonePacket< FFXIVIpcInventoryTransaction >( getId() );
|
||||
invTransPacket->data().transactionId = seq;
|
||||
invTransPacket->data().ownerId = getId();
|
||||
invTransPacket->data().storageId = type;
|
||||
invTransPacket->data().catalogId = item->getId();
|
||||
invTransPacket->data().stackSize = item->getStackSize();
|
||||
invTransPacket->data().slotId = slotId;
|
||||
invTransPacket->data().type = 7;
|
||||
queuePacket( invTransPacket );
|
||||
|
||||
auto invTransFinPacket = makeZonePacket< FFXIVIpcInventoryTransactionFinish >( getId() );
|
||||
invTransFinPacket->data().transactionId = seq;
|
||||
invTransFinPacket->data().transactionId1 = seq;
|
||||
queuePacket( invTransFinPacket );
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::getFreeInventoryContainerSlot( Inventory::InventoryContainerPair& containerPair ) const
|
||||
{
|
||||
for( auto bagId : { Bag0, Bag1, Bag2, Bag3 } )
|
||||
{
|
||||
auto needle = m_storageMap.find( bagId );
|
||||
if( needle == m_storageMap.end() )
|
||||
continue;
|
||||
|
||||
auto& container = needle->second;
|
||||
|
||||
for( uint16_t idx = 0; idx < container->getMaxSize(); idx++ )
|
||||
{
|
||||
auto item = container->getItem( idx );
|
||||
if( !item )
|
||||
{
|
||||
containerPair = std::make_pair( bagId, idx );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::insertInventoryItem( Sapphire::Common::InventoryType type, uint16_t slot,
|
||||
const Sapphire::ItemPtr item )
|
||||
{
|
||||
updateContainer( type, slot, item );
|
||||
|
||||
auto slotUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), slot, type, *item );
|
||||
queuePacket( slotUpdate );
|
||||
|
||||
}
|
|
@ -84,6 +84,17 @@ bool Sapphire::Entity::Player::load( uint32_t charId, SessionPtr pSession )
|
|||
pCurrZone = pTeriMgr->getZoneByTerritoryTypeId( zoneId );
|
||||
}
|
||||
}
|
||||
else if( pTeriMgr->isInternalEstateTerritory( zoneId ) )
|
||||
{
|
||||
// todo: this needs to go to the area just outside of the plot door
|
||||
pCurrZone = pTeriMgr->getZoneByLandSetId( m_prevTerritoryId );
|
||||
|
||||
zoneId = m_prevTerritoryTypeId;
|
||||
m_pos.x = m_prevPos.x;
|
||||
m_pos.y = m_prevPos.y;
|
||||
m_pos.z = m_prevPos.z;
|
||||
setRot( m_prevRot );
|
||||
}
|
||||
else if( pTeriMgr->isHousingTerritory( zoneId ) )
|
||||
{
|
||||
pCurrZone = pTeriMgr->getZoneByLandSetId( m_territoryId );
|
||||
|
@ -580,10 +591,7 @@ Sapphire::ItemPtr Sapphire::Entity::Player::createItem( uint32_t catalogId, uint
|
|||
|
||||
uint8_t flags = 0;
|
||||
|
||||
ItemPtr pItem = make_Item( Items::Util::getNextUId(),
|
||||
catalogId,
|
||||
itemInfo->modelMain,
|
||||
itemInfo->modelSub );
|
||||
ItemPtr pItem = make_Item( Items::Util::getNextUId(), catalogId );
|
||||
|
||||
pItem->setStackSize( quantity );
|
||||
|
||||
|
|
|
@ -35,6 +35,13 @@ namespace World::Territory::Housing
|
|||
TYPE_FORWARD( HousingInteriorTerritory );
|
||||
}
|
||||
|
||||
namespace Inventory
|
||||
{
|
||||
using InventoryContainerPair = std::pair< Common::InventoryType, uint8_t >;
|
||||
using InventoryTypeList = std::vector< Common::InventoryType >;
|
||||
TYPE_FORWARD( HousingItem );
|
||||
}
|
||||
|
||||
namespace World::Manager
|
||||
{
|
||||
TYPE_FORWARD( HousingMgr );
|
||||
|
|
29
src/world/Inventory/HousingItem.cpp
Normal file
29
src/world/Inventory/HousingItem.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "HousingItem.h"
|
||||
|
||||
Sapphire::Inventory::HousingItem::HousingItem( uint64_t uId, uint32_t catalogId ) :
|
||||
Sapphire::Item( uId, catalogId, false )
|
||||
{
|
||||
m_stackSize = 1;
|
||||
m_spiritBond = 1;
|
||||
m_reservedFlag = 1092616192; // wat?
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Inventory::HousingItem::getRot() const
|
||||
{
|
||||
return m_rotation;
|
||||
}
|
||||
|
||||
void Sapphire::Inventory::HousingItem::setRot( uint16_t rot )
|
||||
{
|
||||
m_rotation = rot;
|
||||
}
|
||||
|
||||
Sapphire::Common::FFXIVARR_POSITION3_U16 Sapphire::Inventory::HousingItem::getPos() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
void Sapphire::Inventory::HousingItem::setPos( Sapphire::Common::FFXIVARR_POSITION3_U16 pos )
|
||||
{
|
||||
m_position = pos;
|
||||
}
|
26
src/world/Inventory/HousingItem.h
Normal file
26
src/world/Inventory/HousingItem.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef SAPPHIRE_HOUSINGITEM_H
|
||||
#define SAPPHIRE_HOUSINGITEM_H
|
||||
|
||||
#include "Item.h"
|
||||
|
||||
namespace Sapphire::Inventory
|
||||
{
|
||||
class HousingItem : public Item
|
||||
{
|
||||
public:
|
||||
HousingItem( uint64_t uId, uint32_t catalogId );
|
||||
virtual ~HousingItem() = default;
|
||||
|
||||
void setRot( uint16_t rot );
|
||||
uint16_t getRot() const;
|
||||
|
||||
void setPos( Common::FFXIVARR_POSITION3_U16 pos );
|
||||
Common::FFXIVARR_POSITION3_U16 getPos() const;
|
||||
|
||||
private:
|
||||
Common::FFXIVARR_POSITION3_U16 m_position;
|
||||
uint16_t m_rotation;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SAPPHIRE_HOUSINGITEM_H
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
extern Sapphire::Framework g_fw;
|
||||
|
||||
Sapphire::Item::Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t model2, bool isHq ) :
|
||||
Sapphire::Item::Item( uint64_t uId, uint32_t catalogId, bool isHq ) :
|
||||
m_id( catalogId ),
|
||||
m_uId( uId ),
|
||||
m_model1( model1 ),
|
||||
m_model2( model2 ),
|
||||
m_isHq( isHq ),
|
||||
m_stain( 0 ),
|
||||
m_durability( 30000 )
|
||||
m_durability( 30000 ),
|
||||
m_spiritBond( 0 ),
|
||||
m_reservedFlag( 0 )
|
||||
{
|
||||
auto pExdData = g_fw.get< Data::ExdDataGenerated >();
|
||||
auto itemInfo = pExdData->get< Sapphire::Data::Item >( catalogId );
|
||||
|
@ -22,11 +22,14 @@ Sapphire::Item::Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_
|
|||
m_delayMs = itemInfo->delayms;
|
||||
m_physicalDmg = itemInfo->damagePhys;
|
||||
m_magicalDmg = itemInfo->damageMag;
|
||||
m_model1 = itemInfo->modelMain;
|
||||
m_model2 = itemInfo->modelSub;
|
||||
m_weaponDmg = ( m_physicalDmg != 0 ) ? m_physicalDmg : m_magicalDmg;
|
||||
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;
|
||||
m_additionalData = itemInfo->additionalData;
|
||||
}
|
||||
|
||||
float Sapphire::Item::getAutoAttackDmg() const
|
||||
|
@ -154,3 +157,28 @@ void Sapphire::Item::setStain( uint16_t stain )
|
|||
{
|
||||
m_stain = stain;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::Item::getAdditionalData() const
|
||||
{
|
||||
return m_additionalData;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Item::getSpiritbond() const
|
||||
{
|
||||
return m_spiritBond;
|
||||
}
|
||||
|
||||
void Sapphire::Item::setSpiritbond( uint16_t spiritbond )
|
||||
{
|
||||
m_spiritBond = spiritbond;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::Item::getReservedFlag() const
|
||||
{
|
||||
return m_reservedFlag;
|
||||
}
|
||||
|
||||
void Sapphire::Item::setReservedFlag( uint32_t flag )
|
||||
{
|
||||
m_reservedFlag = flag;
|
||||
}
|
|
@ -10,9 +10,9 @@ namespace Sapphire
|
|||
{
|
||||
|
||||
public:
|
||||
Item( uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t model2, bool isHq = false );
|
||||
Item( uint64_t uId, uint32_t catalogId, bool isHq = false );
|
||||
|
||||
~Item() = default;
|
||||
virtual ~Item() = default;
|
||||
|
||||
uint32_t getId() const;
|
||||
|
||||
|
@ -62,6 +62,14 @@ namespace Sapphire
|
|||
uint16_t getStain() const;
|
||||
void setStain( uint16_t stain );
|
||||
|
||||
uint32_t getAdditionalData() const;
|
||||
|
||||
void setSpiritbond( uint16_t spiritbond );
|
||||
uint16_t getSpiritbond() const;
|
||||
|
||||
void setReservedFlag( uint32_t flag );
|
||||
uint32_t getReservedFlag() const;
|
||||
|
||||
|
||||
protected:
|
||||
uint32_t m_id;
|
||||
|
@ -87,6 +95,10 @@ namespace Sapphire
|
|||
uint16_t m_itemLevel;
|
||||
uint16_t m_durability;
|
||||
uint16_t m_stain;
|
||||
uint16_t m_spiritBond;
|
||||
uint32_t m_reservedFlag;
|
||||
|
||||
uint32_t m_additionalData;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
extern Sapphire::Framework g_fw;
|
||||
|
||||
Sapphire::ItemContainer::ItemContainer( uint16_t storageId, uint16_t maxSize, const std::string& tableName,
|
||||
bool isMultiStorage, bool isPersistentStorage ) :
|
||||
bool isMultiStorage, bool removeItemOnContainerRemoval ) :
|
||||
m_id( storageId ),
|
||||
m_size( maxSize ),
|
||||
m_tableName( tableName ),
|
||||
m_bMultiStorage( isMultiStorage ),
|
||||
m_isPersistentStorage( isPersistentStorage )
|
||||
m_removeItemOnContainerRemove( removeItemOnContainerRemoval )
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ void Sapphire::ItemContainer::removeItem( uint16_t slotId )
|
|||
|
||||
if( it != m_itemMap.end() )
|
||||
{
|
||||
if( m_isPersistentStorage )
|
||||
if( m_removeItemOnContainerRemove )
|
||||
pDb->execute( "DELETE FROM charaglobalitem WHERE itemId = " + std::to_string( it->second->getUId() ) );
|
||||
|
||||
m_itemMap.erase( it );
|
||||
|
@ -118,7 +118,7 @@ bool Sapphire::ItemContainer::isMultiStorage() const
|
|||
|
||||
bool Sapphire::ItemContainer::isPersistentStorage() const
|
||||
{
|
||||
return m_isPersistentStorage;
|
||||
return m_removeItemOnContainerRemove;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Sapphire
|
|||
|
||||
public:
|
||||
ItemContainer( uint16_t storageId, uint16_t maxSize, const std::string& tableName, bool isMultiStorage,
|
||||
bool isPersistentStorage = true );
|
||||
bool removeItemOnContainerRemoval = true );
|
||||
|
||||
~ItemContainer();
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace Sapphire
|
|||
uint16_t m_size;
|
||||
std::string m_tableName;
|
||||
bool m_bMultiStorage;
|
||||
bool m_isPersistentStorage;
|
||||
bool m_removeItemOnContainerRemove;
|
||||
ItemMap m_itemMap;
|
||||
Entity::PlayerPtr m_pOwner;
|
||||
};
|
||||
|
|
|
@ -132,11 +132,7 @@ Sapphire::ItemPtr Sapphire::Items::Util::loadItem( uint64_t uId )
|
|||
auto itemInfo = pExdData->get< Sapphire::Data::Item >( itemRes->getUInt( 1 ) );
|
||||
bool isHq = itemRes->getUInt( 3 ) == 1;
|
||||
|
||||
ItemPtr pItem = make_Item( uId,
|
||||
itemRes->getUInt( 1 ),
|
||||
itemInfo->modelMain,
|
||||
itemInfo->modelSub,
|
||||
isHq );
|
||||
ItemPtr pItem = make_Item( uId, itemRes->getUInt( 1 ), isHq );
|
||||
|
||||
pItem->setStackSize( itemRes->getUInt( 2 ) );
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,7 @@
|
|||
#include "Territory/HousingZone.h"
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <array>
|
||||
|
||||
namespace Sapphire::Data
|
||||
{
|
||||
|
@ -18,11 +19,15 @@ namespace Sapphire::World::Manager
|
|||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* @brief Structure that is generated and filled on world launch. Used to store housing data during init
|
||||
* so we don't need to query them individually.
|
||||
*/
|
||||
struct LandCacheEntry
|
||||
{
|
||||
// land table
|
||||
uint64_t m_landSetId;
|
||||
uint64_t m_landId;
|
||||
uint16_t m_landId;
|
||||
|
||||
Common::LandType m_type;
|
||||
uint8_t m_size;
|
||||
|
@ -42,6 +47,9 @@ namespace Sapphire::World::Manager
|
|||
|
||||
uint64_t m_buildTime;
|
||||
uint64_t m_endorsements;
|
||||
|
||||
uint16_t m_maxPlacedExternalItems;
|
||||
uint16_t m_maxPlacedInternalItems;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -106,6 +114,13 @@ namespace Sapphire::World::Manager
|
|||
*/
|
||||
void sendEstateInventory( Entity::Player& player, uint16_t inventoryType, uint8_t plotNum );
|
||||
|
||||
/*!
|
||||
* @brief Sends all the available internal inventories in one go. Used to initially populate the menu.
|
||||
* @param player The player to send the containers to
|
||||
* @param storeroom True if we should send the storeroom, false we send the placed items
|
||||
*/
|
||||
void sendInternalEstateInventoryBatch( Entity::Player& player, bool storeroom = false );
|
||||
|
||||
/*!
|
||||
* @brief Get the land & house data that was cached on world startup.
|
||||
* @return
|
||||
|
@ -131,13 +146,167 @@ namespace Sapphire::World::Manager
|
|||
* @return A map containing container ids to ItemContainerPtr
|
||||
*/
|
||||
ContainerIdToContainerMap& getEstateInventory( Common::LandIdent ident );
|
||||
|
||||
/**
|
||||
* @brief Sets up inventories and spawns the base items for the house appearance
|
||||
* @param land The house to update
|
||||
*/
|
||||
bool initHouseModels( Entity::Player& player, LandPtr land, uint32_t presetCatalogId );
|
||||
|
||||
void reqPlaceHousingItem( Entity::Player& player, uint16_t landId, uint16_t containerId, uint16_t slotId,
|
||||
Common::FFXIVARR_POSITION3 pos, float rotation );
|
||||
|
||||
/*!
|
||||
* @brief Returns the equivalent YardObject for a HousingItem
|
||||
* @param item The item to convert into a YardObject
|
||||
* @return The resultant YardObject
|
||||
*/
|
||||
Common::HousingObject getYardObjectForItem( Inventory::HousingItemPtr item ) const;
|
||||
|
||||
|
||||
void reqMoveHousingItem( Entity::Player& player, Common::LandIdent ident, uint16_t slot,
|
||||
Common::FFXIVARR_POSITION3 pos, float rot );
|
||||
|
||||
|
||||
void reqRemoveHousingItem( Sapphire::Entity::Player& player, uint16_t plot,
|
||||
uint16_t containerId, uint16_t slot,
|
||||
bool sendToStoreroom );
|
||||
|
||||
void reqEstateExteriorRemodel( Entity::Player& player, uint16_t plot );
|
||||
|
||||
void reqEstateInteriorRemodel( Entity::Player& player );
|
||||
|
||||
private:
|
||||
void loadLandCache();
|
||||
|
||||
ItemContainerPtr getFreeEstateInventorySlot( Common::LandIdent ident,
|
||||
Inventory::InventoryContainerPair& pair,
|
||||
Inventory::InventoryTypeList bagList );
|
||||
|
||||
/*!
|
||||
*
|
||||
* @param player
|
||||
* @param terri
|
||||
* @param containerId
|
||||
* @param slotId
|
||||
* @param sendToStoreroom
|
||||
* @return
|
||||
*/
|
||||
bool removeInternalItem( Entity::Player& player, Territory::Housing::HousingInteriorTerritory& terri,
|
||||
uint16_t containerId, uint16_t slotId, bool sendToStoreroom );
|
||||
|
||||
/*!
|
||||
*
|
||||
* @param player
|
||||
* @param terri
|
||||
* @param slotId
|
||||
* @param sendToStoreroom
|
||||
* @return
|
||||
*/
|
||||
bool removeExternalItem( Entity::Player& player, HousingZone& terri, Land& land,
|
||||
Common::InventoryType containerType, uint16_t slotId,
|
||||
bool sendToStoreroom );
|
||||
|
||||
/*!
|
||||
* @brief Processes the movement of an item placed in a HousingZone
|
||||
*
|
||||
* This function assumes that the player has permission to move the item.
|
||||
*
|
||||
* @param player The player who placed the item
|
||||
* @param ident The ident of the land that the item belongs to
|
||||
* @param containerIdx The index of the container
|
||||
* @param slot The slot of the item
|
||||
* @param pos The new position
|
||||
* @param rot The new rotation
|
||||
* @return true if moved successfully
|
||||
*/
|
||||
bool moveExternalItem( Entity::Player& player, Common::LandIdent ident, uint16_t slot,
|
||||
Sapphire::HousingZone& terri, Common::FFXIVARR_POSITION3 pos, float rot );
|
||||
|
||||
/*!
|
||||
* @brief Processes the movement of an item placed inside a HousingInteriorTerritory
|
||||
*
|
||||
* This function assumes that the player has permission to move the item.
|
||||
*
|
||||
* @param player The player who placed the item
|
||||
* @param ident The ident of the land that the item belongs to
|
||||
* @param slot The index of the container
|
||||
* @param slotIdx The slot of the item
|
||||
* @param pos The new position
|
||||
* @param rot The new rotation
|
||||
* @return true if moved successfully
|
||||
*/
|
||||
bool moveInternalItem( Entity::Player& player, Common::LandIdent ident,
|
||||
Territory::Housing::HousingInteriorTerritory& terri, uint16_t slot,
|
||||
Common::FFXIVARR_POSITION3 pos, float rot );
|
||||
|
||||
/*!
|
||||
* @brief Processes the spawning and linking of a newly placed housing item for external items
|
||||
* @param player The player who is placing the item
|
||||
* @param item The item that we're placing
|
||||
* @param ident The land that is going to own the item
|
||||
* @return true if the item was placed successfully, false if there's no free container slots to place it
|
||||
*/
|
||||
bool placeExternalItem( Entity::Player& player, Inventory::HousingItemPtr item, Common::LandIdent ident );
|
||||
|
||||
/*!
|
||||
* @brief Processing the spawning and linking of a newly placed item for interior items
|
||||
* @param player The player who is placing the item
|
||||
* @param item The item that we're placing
|
||||
* @return true if the item was placed successfully, false if there's no free spots to place it
|
||||
*/
|
||||
bool placeInteriorItem( Entity::Player& player, Inventory::HousingItemPtr item );
|
||||
|
||||
/*!
|
||||
* @brief Creates a house and saves the minimum amount required to persist a house through restarts.
|
||||
*
|
||||
* Any other changes will be covered by the usual saving logic and can be safely ignored here.
|
||||
*
|
||||
* @param house The house to create in the house table
|
||||
*/
|
||||
void createHouse( HousePtr house ) const;
|
||||
|
||||
/*!
|
||||
* @brief Gets the next available house id
|
||||
* @return The next available house id
|
||||
*/
|
||||
uint64_t getNextHouseId();
|
||||
|
||||
/*!
|
||||
* @brief Loads all the land entries from the database and caches them to speed up housing territory init
|
||||
*/
|
||||
void initLandCache();
|
||||
|
||||
/*!
|
||||
* @brief Loads all the inventories for every estate on the world and sets up their containers
|
||||
* @return True if it was successful
|
||||
*/
|
||||
bool loadEstateInventories();
|
||||
|
||||
/*!
|
||||
* @brief Gets the additionalData field from item.exd for an item
|
||||
* @param catalogId The item id to lookup in item.exd
|
||||
* @return The additionalData field from item.exd
|
||||
*/
|
||||
uint32_t getItemAdditionalData( uint32_t catalogId );
|
||||
|
||||
/*!
|
||||
* @brief Checks whether an estate inventory contains items that are placed and have a world position
|
||||
*
|
||||
* Eg, any item inside the 'placed' items container _should_ have a world position and can be spawned.
|
||||
*
|
||||
* @param type The inventory type that contains items
|
||||
* @return true if contains items that would have a world position
|
||||
*/
|
||||
bool isPlacedItemsInventory( Common::InventoryType type );
|
||||
|
||||
LandSetLandCacheMap m_landCache;
|
||||
LandIdentToInventoryContainerMap m_estateInventories;
|
||||
|
||||
Inventory::InventoryTypeList m_internalPlacedItemContainers;
|
||||
Inventory::InventoryTypeList m_internalStoreroomContainers;
|
||||
|
||||
std::array< std::pair< Common::InventoryType, Common::InventoryType >, 8 > m_containerMap;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -3,10 +3,18 @@
|
|||
#include <Common.h>
|
||||
#include "Actor/Player.h"
|
||||
#include "Inventory/ItemContainer.h"
|
||||
#include "Inventory/Item.h"
|
||||
#include "Inventory/HousingItem.h"
|
||||
#include "Inventory/ItemUtil.h"
|
||||
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
||||
#include <Network/GamePacketNew.h>
|
||||
|
||||
#include <Database/DatabaseDef.h>
|
||||
#include <Exd/ExdDataGenerated.h>
|
||||
|
||||
#include "Framework.h"
|
||||
|
||||
extern Sapphire::Framework g_fw;
|
||||
|
||||
using namespace Sapphire::Network::Packets;
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::sendInventoryContainer( Sapphire::Entity::Player& player,
|
||||
|
@ -41,8 +49,9 @@ void Sapphire::World::Manager::InventoryMgr::sendInventoryContainer( Sapphire::E
|
|||
itemInfoPacket->data().quantity = itM->second->getStackSize();
|
||||
itemInfoPacket->data().catalogId = itM->second->getId();
|
||||
itemInfoPacket->data().condition = itM->second->getDurability();
|
||||
itemInfoPacket->data().spiritBond = 0;
|
||||
itemInfoPacket->data().hqFlag = itM->second->isHq() ? 1 : 0;
|
||||
itemInfoPacket->data().spiritBond = itM->second->getSpiritbond();
|
||||
itemInfoPacket->data().reservedFlag = itM->second->getReservedFlag();
|
||||
itemInfoPacket->data().hqFlag = static_cast< uint8_t >( itM->second->isHq() ? 1 : 0 );
|
||||
itemInfoPacket->data().stain = itM->second->getStain();
|
||||
|
||||
player.queuePacket( itemInfoPacket );
|
||||
|
@ -55,4 +64,121 @@ void Sapphire::World::Manager::InventoryMgr::sendInventoryContainer( Sapphire::E
|
|||
containerInfoPacket->data().containerId = container->getId();
|
||||
|
||||
player.queuePacket( containerInfoPacket );
|
||||
}
|
||||
|
||||
Sapphire::ItemPtr Sapphire::World::Manager::InventoryMgr::createItem( Entity::Player& player,
|
||||
uint32_t catalogId, uint32_t quantity )
|
||||
{
|
||||
auto pExdData = g_fw.get< Data::ExdDataGenerated >();
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
auto itemInfo = pExdData->get< Sapphire::Data::Item >( catalogId );
|
||||
|
||||
if( !itemInfo )
|
||||
return nullptr;
|
||||
|
||||
auto item = make_Item( Items::Util::getNextUId(), catalogId );
|
||||
|
||||
item->setStackSize( std::max< uint32_t >( 1, quantity ) );
|
||||
|
||||
saveItem( player, item );
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::saveHousingContainer( Common::LandIdent ident,
|
||||
Sapphire::ItemContainerPtr container )
|
||||
{
|
||||
auto u64ident = *reinterpret_cast< uint64_t* >( &ident );
|
||||
|
||||
for( auto& item : container->getItemMap() )
|
||||
{
|
||||
saveHousingContainerItem( u64ident, container->getId(), item.first, item.second->getUId() );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::removeItemFromHousingContainer( Sapphire::Common::LandIdent ident,
|
||||
uint16_t containerId,
|
||||
uint16_t slotId )
|
||||
{
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
|
||||
auto stmt = pDb->getPreparedStatement( Db::LAND_INV_DEL );
|
||||
|
||||
auto u64ident = *reinterpret_cast< uint64_t* >( &ident );
|
||||
|
||||
stmt->setUInt64( 1, u64ident );
|
||||
stmt->setUInt( 2, containerId );
|
||||
stmt->setUInt( 3, slotId );
|
||||
|
||||
pDb->directExecute( stmt );
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::saveHousingContainerItem( uint64_t ident,
|
||||
uint16_t containerId, uint16_t slotId,
|
||||
uint64_t itemId )
|
||||
{
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
|
||||
auto stmt = pDb->getPreparedStatement( Db::LAND_INV_UP );
|
||||
// LandIdent, ContainerId, SlotId, ItemId, ItemId
|
||||
|
||||
stmt->setUInt64( 1, ident );
|
||||
stmt->setUInt( 2, containerId );
|
||||
stmt->setUInt( 3, slotId );
|
||||
stmt->setUInt64( 4, itemId );
|
||||
|
||||
// see query, we have to pass itemid in twice
|
||||
// the second time is for the ON DUPLICATE KEY UPDATE condition
|
||||
stmt->setUInt64( 5, itemId );
|
||||
|
||||
pDb->execute( stmt );
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::updateHousingItemPosition( Sapphire::Inventory::HousingItemPtr item )
|
||||
{
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
|
||||
auto stmt = pDb->getPreparedStatement( Db::LAND_INV_UP_ITEMPOS );
|
||||
// ItemId, PosX, PosY, PosZ, Rotation, PosX, PosY, PosZ, Rotation
|
||||
|
||||
auto pos = item->getPos();
|
||||
auto rot = item->getRot();
|
||||
|
||||
stmt->setUInt64( 1, item->getUId() );
|
||||
|
||||
stmt->setUInt( 2, pos.x );
|
||||
stmt->setUInt( 3, pos.y );
|
||||
stmt->setUInt( 4, pos.z );
|
||||
stmt->setInt( 5, rot );
|
||||
|
||||
stmt->setUInt( 6, pos.x );
|
||||
stmt->setUInt( 7, pos.y );
|
||||
stmt->setUInt( 8, pos.z );
|
||||
stmt->setInt( 9, rot );
|
||||
|
||||
pDb->execute( stmt );
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::removeHousingItemPosition( Sapphire::Inventory::HousingItem& item )
|
||||
{
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
|
||||
auto stmt = pDb->getPreparedStatement( Db::LAND_INV_DEL_ITEMPOS );
|
||||
|
||||
stmt->setUInt64( 1, item.getUId() );
|
||||
|
||||
pDb->directExecute( stmt );
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::InventoryMgr::saveItem( Sapphire::Entity::Player& player, Sapphire::ItemPtr item )
|
||||
{
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
auto stmt = pDb->getPreparedStatement( Db::CHARA_ITEMGLOBAL_INS );
|
||||
|
||||
stmt->setUInt( 1, player.getId() );
|
||||
stmt->setUInt64( 2, item->getUId() );
|
||||
stmt->setUInt( 3, item->getId() );
|
||||
stmt->setUInt( 4, item->getStackSize() );
|
||||
|
||||
pDb->directExecute( stmt );
|
||||
}
|
|
@ -9,7 +9,83 @@ namespace Sapphire::World::Manager
|
|||
class InventoryMgr
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Sends an item container to a player
|
||||
*
|
||||
* This does no checks on the container itself. You can send another players inventory to a different
|
||||
* player if you so wish - not that you should
|
||||
*
|
||||
* Automagically manages inventory packet sequencing.
|
||||
*
|
||||
* @param player The player to send the container to
|
||||
* @param container The container to send to the player
|
||||
*/
|
||||
void sendInventoryContainer( Sapphire::Entity::Player& player, Sapphire::ItemContainerPtr container );
|
||||
|
||||
/*!
|
||||
* @brief Creates an item, saves it to the global item table and returns a ptr to the created item
|
||||
* @param player The player that will 'own' the item
|
||||
* @param catalogId The ID for the item, see item.exd
|
||||
* @param quantity how much item to make
|
||||
* @return An ItemPtr to the newly created item
|
||||
*/
|
||||
Sapphire::ItemPtr createItem( Entity::Player& player, uint32_t catalogId, uint32_t quantity = 1 );
|
||||
|
||||
/*!
|
||||
* @brief Commits a housing containers contents to the db
|
||||
* @param ident The identity of the owner of the container
|
||||
* @param container The container to save to the db
|
||||
*/
|
||||
void saveHousingContainer( Common::LandIdent ident, Sapphire::ItemContainerPtr container );
|
||||
|
||||
/*!
|
||||
* @brief Unlinks an item from the housing container in the db
|
||||
* @param ident The identity of the estate that owns the item
|
||||
* @param containerId The container the item is in
|
||||
* @param slotId The slot the item is in
|
||||
*/
|
||||
void removeItemFromHousingContainer( Common::LandIdent ident, uint16_t containerId, uint16_t slotId );
|
||||
|
||||
/*!
|
||||
* @brief Update an item in the db
|
||||
* @param item The item to commit to the db
|
||||
*/
|
||||
void updateItem( Sapphire::ItemPtr item );
|
||||
|
||||
/*!
|
||||
* @brief Updates the position/rotation of a housing object
|
||||
* @param item The item to update
|
||||
*/
|
||||
void updateHousingItemPosition( Sapphire::Inventory::HousingItemPtr item );
|
||||
|
||||
/*!
|
||||
* @brief Removes the position/rotation from a housing object
|
||||
* @param item The item to remove the position from.
|
||||
*/
|
||||
void removeHousingItemPosition( Sapphire::Inventory::HousingItem& item );
|
||||
|
||||
/*!
|
||||
* @brief Saves an item to the global item table
|
||||
* @param player The player which owns the item
|
||||
* @param item The item to save
|
||||
*/
|
||||
void saveItem( Entity::Player& player, ItemPtr item );
|
||||
|
||||
private:
|
||||
/*!
|
||||
* @brief Saves an individual item to the db.
|
||||
*
|
||||
* This will create a new row in the event the slot doesn't exist in the db, otherwise this
|
||||
* will overwrite the itemid in the existing row.
|
||||
*
|
||||
* @param ident
|
||||
* @param containerId
|
||||
* @param slotId
|
||||
* @param itemId
|
||||
*/
|
||||
void saveHousingContainerItem( uint64_t ident,
|
||||
uint16_t containerId, uint16_t slotId,
|
||||
uint64_t itemId );
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -106,11 +106,20 @@ bool Sapphire::World::Manager::TerritoryMgr::isPrivateTerritory( uint32_t territ
|
|||
|
||||
return pTeri->territoryIntendedUse == TerritoryIntendedUse::OpeningArea ||
|
||||
pTeri->territoryIntendedUse == TerritoryIntendedUse::Inn ||
|
||||
pTeri->territoryIntendedUse == TerritoryIntendedUse::HousingPrivateArea ||
|
||||
pTeri->territoryIntendedUse == TerritoryIntendedUse::JailArea ||
|
||||
pTeri->territoryIntendedUse == TerritoryIntendedUse::MSQPrivateArea;
|
||||
}
|
||||
|
||||
bool Sapphire::World::Manager::TerritoryMgr::isInternalEstateTerritory( uint32_t territoryTypeId ) const
|
||||
{
|
||||
auto pTeri = getTerritoryDetail( territoryTypeId );
|
||||
|
||||
if( !pTeri )
|
||||
return false;
|
||||
|
||||
return pTeri->territoryIntendedUse == TerritoryIntendedUse::HousingPrivateArea;
|
||||
}
|
||||
|
||||
bool Sapphire::World::Manager::TerritoryMgr::isDefaultTerritory( uint32_t territoryTypeId ) const
|
||||
{
|
||||
auto pTeri = getTerritoryDetail( territoryTypeId );
|
||||
|
|
|
@ -87,6 +87,13 @@ namespace Sapphire::World::Manager
|
|||
/*! returns true if the territoryType in question is not a private zone */
|
||||
bool isPrivateTerritory( uint32_t territoryTypeId ) const;
|
||||
|
||||
/*!
|
||||
* @brief Checks if a territory type is an internal housing area
|
||||
* @param territoryTypeId The territory to test
|
||||
* @return true if it is a housing area, false if not
|
||||
*/
|
||||
bool isInternalEstateTerritory( uint32_t territoryTypeId ) const;
|
||||
|
||||
/*! returns true if the territoryType is a default non-instanced zone */
|
||||
bool isDefaultTerritory( uint32_t territoryTypeId ) const;
|
||||
|
||||
|
|
|
@ -88,6 +88,9 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
|
|||
setZoneHandler( ClientZoneIpcType::LandRenameHandler, "LandRenameHandler", &GameConnection::landRenameHandler );
|
||||
setZoneHandler( ClientZoneIpcType::HousingUpdateHouseGreeting, "HousingUpdateHouseGreeting",
|
||||
&GameConnection::housingUpdateGreetingHandler );
|
||||
setZoneHandler( ClientZoneIpcType::ReqPlaceHousingItem, "ReqPlaceHousingItem", &GameConnection::reqPlaceHousingItem );
|
||||
setZoneHandler( ClientZoneIpcType::HousingUpdateObjectPosition, "HousingUpdateObjectPosition",
|
||||
&GameConnection::reqMoveHousingItem );
|
||||
|
||||
setZoneHandler( ClientZoneIpcType::TalkEventHandler, "EventHandlerTalk", &GameConnection::eventHandlerTalk );
|
||||
setZoneHandler( ClientZoneIpcType::EmoteEventHandler, "EventHandlerEmote", &GameConnection::eventHandlerEmote );
|
||||
|
|
|
@ -171,6 +171,10 @@ namespace Sapphire::Network
|
|||
|
||||
DECLARE_HANDLER( tellHandler );
|
||||
|
||||
DECLARE_HANDLER( reqPlaceHousingItem );
|
||||
|
||||
DECLARE_HANDLER( reqMoveHousingItem );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -79,12 +79,16 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
|
|||
const auto param12 = packet.data().param12;
|
||||
const auto param2 = packet.data().param2;
|
||||
const auto param3 = packet.data().param3;
|
||||
const auto param4 = packet.data().param4;
|
||||
const auto param5 = packet.data().param5;
|
||||
|
||||
pLog->debug( "[" + std::to_string( m_pSession->getId() ) + "] Incoming action: " +
|
||||
Util::intToHexString( static_cast< uint32_t >( commandId & 0xFFFF ), 4 ) +
|
||||
"\nparam1: " + Util::intToHexString( static_cast< uint64_t >( param1 & 0xFFFFFFFFFFFFFFF ), 16 ) +
|
||||
"\nparam2: " + Util::intToHexString( static_cast< uint32_t >( param2 & 0xFFFFFFFF ), 8 ) +
|
||||
"\nparam3: " + Util::intToHexString( static_cast< uint64_t >( param3 & 0xFFFFFFFFFFFFFFF ), 16 )
|
||||
"\nparam3: " + Util::intToHexString( static_cast< uint64_t >( param3 & 0xFFFFFFFFFFFFFFF ), 16 ) +
|
||||
"\nparam4: " + Util::intToHexString( static_cast< uint32_t >( param4 & 0xFFFFFFFF ), 8 ) +
|
||||
"\nparam5: " + Util::intToHexString( static_cast< uint32_t >( param5 & 0xFFFFFFFF ), 8 )
|
||||
);
|
||||
|
||||
|
||||
|
@ -425,14 +429,13 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
|
|||
{
|
||||
uint8_t plot = ( param12 & 0xFF );
|
||||
|
||||
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
if( !housingMgr )
|
||||
break;
|
||||
|
||||
uint16_t inventoryType = Common::InventoryType::HousingOutdoorPlacedItems;
|
||||
uint16_t inventoryType = Common::InventoryType::HousingExteriorPlacedItems;
|
||||
if( param2 == 1 )
|
||||
inventoryType = Common::InventoryType::HousingOutdoorStoreroom;
|
||||
inventoryType = Common::InventoryType::HousingExteriorStoreroom;
|
||||
|
||||
housingMgr->sendEstateInventory( player, inventoryType, plot );
|
||||
|
||||
|
@ -440,15 +443,45 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
|
|||
}
|
||||
case ClientTriggerType::RequestEstateInventory:
|
||||
{
|
||||
// only sent if param1 is 1, because the client sends this with 0 when you open the ui for whatever reason
|
||||
if( param1 != 1 )
|
||||
return;
|
||||
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
if( !housingMgr )
|
||||
break;
|
||||
|
||||
// housingMgr->sendHousingInventory( player, Common::InventoryType::HousingInd, 255 );
|
||||
// param1 = 1 - storeroom
|
||||
// param1 = 0 - placed items
|
||||
|
||||
if( param1 == 1 )
|
||||
housingMgr->sendInternalEstateInventoryBatch( player, true );
|
||||
else
|
||||
housingMgr->sendInternalEstateInventoryBatch( player );
|
||||
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::RequestHousingItemRemove:
|
||||
{
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
|
||||
auto slot = param4 & 0xFF;
|
||||
auto sendToStoreroom = ( param4 >> 16 ) != 0;
|
||||
|
||||
//player, plot, containerId, slot, sendToStoreroom
|
||||
housingMgr->reqRemoveHousingItem( player, param12, param2, slot, sendToStoreroom );
|
||||
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::RequestEstateExteriorRemodel:
|
||||
{
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
|
||||
housingMgr->reqEstateExteriorRemodel( player, param11 );
|
||||
|
||||
break;
|
||||
}
|
||||
case ClientTriggerType::RequestEstateInteriorRemodel:
|
||||
{
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
|
||||
housingMgr->reqEstateInteriorRemodel( player );
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -705,3 +705,26 @@ void Sapphire::Network::GameConnection::housingUpdateGreetingHandler( const Pack
|
|||
|
||||
pHousingMgr->updateEstateGreeting( player, packet.data().ident, std::string( packet.data().greeting ) );
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::reqPlaceHousingItem( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
const auto packet = ZoneChannelPacket< Client::FFXIVIpcReqPlaceHousingItem >( inPacket );
|
||||
const auto& data = packet.data();
|
||||
|
||||
housingMgr->reqPlaceHousingItem( player, data.landId, data.sourceInvContainerId, data.sourceInvSlotId,
|
||||
data.position, data.rotation );
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::reqMoveHousingItem( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
auto housingMgr = g_fw.get< HousingMgr >();
|
||||
|
||||
const auto packet = ZoneChannelPacket< Client::FFXIVIpcHousingUpdateObjectPosition >( inPacket );
|
||||
const auto& data = packet.data();
|
||||
|
||||
housingMgr->reqMoveHousingItem( player, data.ident, data.slot, data.pos, data.rotation );
|
||||
|
||||
}
|
|
@ -29,11 +29,11 @@ namespace Sapphire::Network::Packets::Server
|
|||
m_data.slot = slot;
|
||||
m_data.quantity = item.getStackSize();
|
||||
m_data.catalogId = item.getId();
|
||||
m_data.reservedFlag = 0; // no idea
|
||||
m_data.reservedFlag = item.getReservedFlag(); // no idea
|
||||
m_data.signatureId = 0;
|
||||
m_data.hqFlag = item.isHq() ? 1 : 0;
|
||||
m_data.condition = 60000; // 200%
|
||||
m_data.spiritBond = 0;
|
||||
m_data.spiritBond = item.getSpiritbond();
|
||||
m_data.color = 0;
|
||||
m_data.glamourCatalogId = 0;
|
||||
m_data.materia1 = 0;
|
||||
|
|
|
@ -20,9 +20,7 @@ Sapphire::House::House( uint32_t houseId, uint32_t landSetId, Common::LandIdent
|
|||
m_landIdent( ident ),
|
||||
m_estateName( estateName ),
|
||||
m_estateComment( estateComment )
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
Sapphire::House::~House() = default;
|
||||
|
||||
|
@ -55,44 +53,14 @@ Sapphire::Common::LandIdent Sapphire::House::getLandIdent() const
|
|||
return m_landIdent;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::House::getHouseId() const
|
||||
uint32_t Sapphire::House::getId() const
|
||||
{
|
||||
return m_houseId;
|
||||
}
|
||||
|
||||
uint8_t Sapphire::House::getHousePartColor( Common::HousePartSlot slot ) const
|
||||
{
|
||||
return m_houseModelsCache[ slot ].second;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::House::getHouseInteriorPart( Common::HousingInteriorSlot slot ) const
|
||||
{
|
||||
return m_houseInteriorModels[ slot ];
|
||||
}
|
||||
|
||||
void Sapphire::House::setHousePart( Common::HousePartSlot slot, uint32_t id )
|
||||
{
|
||||
m_houseModelsCache[ slot ].first = id;
|
||||
}
|
||||
|
||||
void Sapphire::House::setHousePartColor( Common::HousePartSlot slot, uint32_t id )
|
||||
{
|
||||
m_houseModelsCache[ slot ].second = id;
|
||||
}
|
||||
|
||||
void Sapphire::House::setHouseInteriorPart( Common::HousingInteriorSlot slot, uint32_t id )
|
||||
{
|
||||
m_houseInteriorModels[ slot ] = id;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::House::getHousePart( Common::HousePartSlot slot ) const
|
||||
{
|
||||
return m_houseModelsCache[ slot ].first;
|
||||
}
|
||||
|
||||
Sapphire::House::HouseModelsArray const& Sapphire::House::getHouseModels() const
|
||||
{
|
||||
return m_houseModelsCache;
|
||||
return m_exteriorModelCache;
|
||||
}
|
||||
|
||||
const std::string& Sapphire::House::getHouseName() const
|
||||
|
@ -117,4 +85,24 @@ void Sapphire::House::setHouseName( const std::string& name )
|
|||
m_estateName = name;
|
||||
|
||||
updateHouseDb();
|
||||
}
|
||||
|
||||
void Sapphire::House::setExteriorModel( Sapphire::Common::HouseExteriorSlot slot, uint32_t modelId, uint16_t stain )
|
||||
{
|
||||
m_exteriorModelCache[ slot ] = std::make_pair( modelId, stain );
|
||||
}
|
||||
|
||||
Sapphire::House::HousePart Sapphire::House::getExteriorModel( Sapphire::Common::HouseExteriorSlot slot )
|
||||
{
|
||||
return m_exteriorModelCache[ slot ];
|
||||
}
|
||||
|
||||
void Sapphire::House::setInteriorModel( Sapphire::Common::HousingInteriorSlot slot, uint32_t modelId )
|
||||
{
|
||||
m_interiorModelCache[ slot ] = modelId;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::House::getInteriorModel( Sapphire::Common::HousingInteriorSlot slot )
|
||||
{
|
||||
return m_interiorModelCache[ slot ];
|
||||
}
|
|
@ -16,13 +16,13 @@ namespace Sapphire
|
|||
const std::string& estateComment );
|
||||
virtual ~House();
|
||||
|
||||
using HousePart = std::pair< uint32_t, uint8_t >;
|
||||
using HousePart = std::pair< uint32_t, uint16_t >;
|
||||
using HouseModelsArray = std::array< HousePart, 8 >;
|
||||
|
||||
//gerneral
|
||||
uint32_t getLandSetId() const;
|
||||
Common::LandIdent getLandIdent() const;
|
||||
uint32_t getHouseId() const;
|
||||
uint32_t getId() const;
|
||||
|
||||
const std::string& getHouseName() const;
|
||||
void setHouseName( const std::string& name );
|
||||
|
@ -31,12 +31,11 @@ namespace Sapphire
|
|||
void setHouseGreeting( const std::string& greeting );
|
||||
|
||||
//functions
|
||||
void setHousePart( Common::HousePartSlot slot, uint32_t id );
|
||||
void setHousePartColor( Common::HousePartSlot slot, uint32_t id );
|
||||
void setHouseInteriorPart( Common::HousingInteriorSlot slot, uint32_t id );
|
||||
uint32_t getHousePart( Common::HousePartSlot slot ) const;
|
||||
uint8_t getHousePartColor( Common::HousePartSlot slot ) const;
|
||||
uint32_t getHouseInteriorPart( Common::HousingInteriorSlot slot ) const;
|
||||
void setExteriorModel( Common::HouseExteriorSlot slot, uint32_t modelId, uint16_t stain );
|
||||
HousePart getExteriorModel( Common::HouseExteriorSlot slot );
|
||||
|
||||
void setInteriorModel( Common::HousingInteriorSlot slot, uint32_t modelId );
|
||||
uint32_t getInteriorModel( Common::HousingInteriorSlot slot );
|
||||
|
||||
HouseModelsArray const& getHouseModels() const;
|
||||
|
||||
|
@ -49,8 +48,8 @@ namespace Sapphire
|
|||
|
||||
uint64_t m_buildTime;
|
||||
|
||||
HouseModelsArray m_houseModelsCache;
|
||||
uint32_t m_houseInteriorModels[10];
|
||||
HouseModelsArray m_exteriorModelCache;
|
||||
uint32_t m_interiorModelCache[10];
|
||||
|
||||
std::string m_estateComment;
|
||||
std::string m_estateName;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <Exd/ExdDataGenerated.h>
|
||||
#include <Network/GamePacketNew.h>
|
||||
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
||||
#include <Network/PacketWrappers/ActorControlPacket143.h>
|
||||
#include <Network/CommonActorControl.h>
|
||||
|
||||
#include "Actor/Player.h"
|
||||
#include "Actor/Actor.h"
|
||||
|
@ -13,6 +15,8 @@
|
|||
#include "Manager/HousingMgr.h"
|
||||
#include "Territory/Land.h"
|
||||
#include "Territory/House.h"
|
||||
#include "Inventory/ItemContainer.h"
|
||||
#include "Inventory/HousingItem.h"
|
||||
|
||||
#include "Forwards.h"
|
||||
#include "HousingInteriorTerritory.h"
|
||||
|
@ -28,27 +32,27 @@ using namespace Sapphire::World::Manager;
|
|||
using namespace Sapphire;
|
||||
using namespace Sapphire::World::Territory;
|
||||
|
||||
Housing::HousingInteriorTerritory::HousingInteriorTerritory( Common::LandIdent ident, uint16_t territoryTypeId,
|
||||
uint32_t guId,
|
||||
const std::string& internalName,
|
||||
const std::string& contentName ) :
|
||||
Sapphire::World::Territory::Housing::HousingInteriorTerritory::HousingInteriorTerritory( Common::LandIdent ident,
|
||||
uint16_t territoryTypeId,
|
||||
uint32_t guId,
|
||||
const std::string& internalName,
|
||||
const std::string& contentName ) :
|
||||
Zone( territoryTypeId, guId, internalName, contentName ),
|
||||
m_landIdent( ident )
|
||||
{
|
||||
m_lastActivityTime = static_cast< uint32_t >( Util::getTimeSeconds() );
|
||||
}
|
||||
|
||||
Housing::HousingInteriorTerritory::~HousingInteriorTerritory()
|
||||
{
|
||||
Housing::HousingInteriorTerritory::~HousingInteriorTerritory() = default;
|
||||
|
||||
bool Sapphire::World::Territory::Housing::HousingInteriorTerritory::init()
|
||||
{
|
||||
updateHousingObjects();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Housing::HousingInteriorTerritory::init()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player )
|
||||
void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player )
|
||||
{
|
||||
auto pHousingMgr = g_fw.get< HousingMgr >();
|
||||
auto pLog = g_fw.get< Logger >();
|
||||
|
@ -56,7 +60,7 @@ void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player )
|
|||
"HousingInteriorTerritory::onPlayerZoneIn: Zone#" + std::to_string( getGuId() ) + "|" + std::to_string( getTerritoryTypeId() ) +
|
||||
", Entity#" + std::to_string( player.getId() ) );
|
||||
|
||||
auto indoorInitPacket = makeZonePacket< FFXIVIpcHousingIndoorInitialize >( player.getId() );
|
||||
auto indoorInitPacket = makeZonePacket< Server::FFXIVIpcHousingIndoorInitialize >( player.getId() );
|
||||
indoorInitPacket->data().u1 = 0;
|
||||
indoorInitPacket->data().u2 = 0;
|
||||
indoorInitPacket->data().u3 = 0;
|
||||
|
@ -68,43 +72,159 @@ void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player )
|
|||
|
||||
for( auto i = 0; i < 10; i++ )
|
||||
{
|
||||
indoorInitPacket->data().indoorItems[ i ] = pHouse->getHouseInteriorPart( static_cast< Common::HousingInteriorSlot >( i ) );
|
||||
indoorInitPacket->data().indoorItems[ i ] = pHouse->getInteriorModel(
|
||||
static_cast< Common::HousingInteriorSlot >( i ) );
|
||||
}
|
||||
|
||||
|
||||
uint32_t yardPacketNum;
|
||||
uint32_t yardPacketTotal = 2 + pLand->getSize();
|
||||
|
||||
player.queuePacket( indoorInitPacket );
|
||||
|
||||
for( yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ )
|
||||
auto yardPacketTotal = static_cast< uint8_t >( 2 + pLand->getSize() );
|
||||
for( uint8_t yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ )
|
||||
{
|
||||
auto objectInitPacket = makeZonePacket< FFXIVIpcHousingObjectInitialize >( player.getId() );
|
||||
auto objectInitPacket = makeZonePacket< Server::FFXIVIpcHousingObjectInitialize >( player.getId() );
|
||||
memcpy( &objectInitPacket->data().landIdent, &m_landIdent, sizeof( Common::LandIdent ) );
|
||||
objectInitPacket->data().u1 = 0;
|
||||
objectInitPacket->data().u2 = 100;
|
||||
objectInitPacket->data().packetNum = yardPacketNum;
|
||||
objectInitPacket->data().packetTotal = yardPacketTotal;
|
||||
|
||||
//TODO: Add Objects here
|
||||
auto yardObjectSize = sizeof( Common::HousingObject );
|
||||
memcpy( &objectInitPacket->data().object, m_housingObjects.data() + ( yardPacketNum * 100 ), yardObjectSize * 100 );
|
||||
|
||||
player.queuePacket( objectInitPacket );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Housing::HousingInteriorTerritory::onUpdate( uint32_t currTime )
|
||||
void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onUpdate( uint32_t currTime )
|
||||
{
|
||||
if( m_playerMap.size() > 0 )
|
||||
m_lastActivityTime = currTime;
|
||||
}
|
||||
|
||||
uint32_t Housing::HousingInteriorTerritory::getLastActivityTime() const
|
||||
uint32_t Sapphire::World::Territory::Housing::HousingInteriorTerritory::getLastActivityTime() const
|
||||
{
|
||||
return m_lastActivityTime;
|
||||
}
|
||||
|
||||
const Common::LandIdent Housing::HousingInteriorTerritory::getIdent() const
|
||||
const Common::LandIdent Sapphire::World::Territory::Housing::HousingInteriorTerritory::getLandIdent() const
|
||||
{
|
||||
return m_landIdent;
|
||||
}
|
||||
|
||||
void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateHousingObjects()
|
||||
{
|
||||
auto housingMgr = g_fw.get< Manager::HousingMgr >();
|
||||
|
||||
auto containerIds = {
|
||||
InventoryType::HousingInteriorPlacedItems1,
|
||||
InventoryType::HousingInteriorPlacedItems2,
|
||||
InventoryType::HousingInteriorPlacedItems3,
|
||||
InventoryType::HousingInteriorPlacedItems4,
|
||||
InventoryType::HousingInteriorPlacedItems5,
|
||||
InventoryType::HousingInteriorPlacedItems6,
|
||||
InventoryType::HousingInteriorPlacedItems7,
|
||||
InventoryType::HousingInteriorPlacedItems8,
|
||||
};
|
||||
|
||||
// zero out the array
|
||||
// there's some really weird behaviour where *some* values will cause the linkshell invite notification to pop up
|
||||
// for some reason
|
||||
Common::HousingObject obj {};
|
||||
memset( &obj, 0x0, sizeof( Common::HousingObject ) );
|
||||
m_housingObjects.fill( obj );
|
||||
|
||||
auto containers = housingMgr->getEstateInventory( getLandIdent() );
|
||||
|
||||
uint8_t containerIdx = 0;
|
||||
for( auto containerId : containerIds )
|
||||
{
|
||||
auto container = containers.find( containerId );
|
||||
if( container == containers.end() )
|
||||
// no more containers left
|
||||
break;
|
||||
|
||||
for( const auto& item : container->second->getItemMap() )
|
||||
{
|
||||
auto housingItem = std::dynamic_pointer_cast< Inventory::HousingItem >( item.second );
|
||||
assert( housingItem );
|
||||
|
||||
auto offset = item.first + ( containerIdx * 50 );
|
||||
|
||||
auto obj = housingMgr->getYardObjectForItem( housingItem );
|
||||
|
||||
m_housingObjects[ offset ] = obj;
|
||||
}
|
||||
|
||||
containerIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Territory::Housing::HousingInteriorTerritory::spawnHousingObject( uint8_t containerIdx,
|
||||
uint16_t slot,
|
||||
uint16_t containerType,
|
||||
Inventory::HousingItemPtr item )
|
||||
{
|
||||
auto housingMgr = g_fw.get< Manager::HousingMgr >();
|
||||
|
||||
auto offset = ( containerIdx * 50 ) + slot;
|
||||
auto obj = housingMgr->getYardObjectForItem( item );
|
||||
|
||||
m_housingObjects[ offset ] = obj;
|
||||
|
||||
for( const auto& player : m_playerMap )
|
||||
{
|
||||
auto objectSpawnPkt = makeZonePacket< Server::FFXIVIpcHousingInternalObjectSpawn >( player.second->getId() );
|
||||
|
||||
objectSpawnPkt->data().containerId = containerType;
|
||||
objectSpawnPkt->data().containerOffset = slot;
|
||||
|
||||
objectSpawnPkt->data().itemId = item->getAdditionalData() & 0xFFFF;
|
||||
objectSpawnPkt->data().rotation = item->getRot();
|
||||
objectSpawnPkt->data().pos = item->getPos();
|
||||
|
||||
player.second->queuePacket( objectSpawnPkt );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateHousingObjectPosition( Entity::Player& sourcePlayer,
|
||||
uint16_t slot,
|
||||
Common::FFXIVARR_POSITION3_U16 pos,
|
||||
uint16_t rot )
|
||||
{
|
||||
auto& obj = m_housingObjects[ slot ];
|
||||
|
||||
obj.pos = pos;
|
||||
obj.itemRotation = rot;
|
||||
|
||||
// todo: how does this update on other clients?
|
||||
|
||||
for( const auto& player : m_playerMap )
|
||||
{
|
||||
if( player.second->getId() == sourcePlayer.getId() )
|
||||
continue;
|
||||
|
||||
auto moveObjPkt = makeZonePacket< Server::FFXIVIpcHousingObjectMove >( player.second->getId() );
|
||||
|
||||
moveObjPkt->data().itemRotation = obj.itemRotation;
|
||||
moveObjPkt->data().pos = obj.pos;
|
||||
|
||||
// todo: how does this work when an item is in a slot >50 or u8 max? my guess is landid is the container index, but not sure...
|
||||
moveObjPkt->data().objectArray = static_cast< uint8_t >( slot % 50 );
|
||||
moveObjPkt->data().landId = static_cast< uint8_t >( slot / 50 );
|
||||
|
||||
player.second->queuePacket( moveObjPkt );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Territory::Housing::HousingInteriorTerritory::removeHousingObject( uint16_t slot )
|
||||
{
|
||||
memset( m_housingObjects.data() + slot, 0x0, sizeof( Common::HousingObject ) );
|
||||
|
||||
for( const auto& player : m_playerMap )
|
||||
{
|
||||
auto pkt = Server::makeActorControl143( player.second->getId(), Network::ActorControl::RemoveInteriorHousingItem, slot );
|
||||
|
||||
player.second->queuePacket( pkt );
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#include "ForwardsZone.h"
|
||||
#include "Territory/Zone.h"
|
||||
#include "Common.h"
|
||||
#include <array>
|
||||
|
||||
namespace Sapphire::World::Territory::Housing
|
||||
{
|
||||
|
@ -20,10 +22,19 @@ namespace Sapphire::World::Territory::Housing
|
|||
|
||||
uint32_t getLastActivityTime() const;
|
||||
|
||||
const Common::LandIdent getIdent() const;
|
||||
const Common::LandIdent getLandIdent() const;
|
||||
|
||||
void updateHousingObjects();
|
||||
void spawnHousingObject( uint8_t containerIdx, uint16_t slot, uint16_t containerType,
|
||||
Inventory::HousingItemPtr item );
|
||||
void updateHousingObjectPosition(
|
||||
Entity::Player& sourcePlayer, uint16_t slot, Sapphire::Common::FFXIVARR_POSITION3_U16 pos, uint16_t rot );
|
||||
void removeHousingObject( uint16_t slot );
|
||||
|
||||
private:
|
||||
Common::LandIdent m_landIdent;
|
||||
uint32_t m_lastActivityTime;
|
||||
|
||||
std::array< Sapphire::Common::HousingObject, 400 > m_housingObjects;
|
||||
};
|
||||
}
|
|
@ -6,12 +6,16 @@
|
|||
#include <Exd/ExdDataGenerated.h>
|
||||
#include <Network/GamePacketNew.h>
|
||||
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
||||
#include <Network/PacketWrappers/ActorControlPacket143.h>
|
||||
#include <Network/CommonActorControl.h>
|
||||
|
||||
#include "Actor/Player.h"
|
||||
#include "Actor/Actor.h"
|
||||
#include "Actor/EventObject.h"
|
||||
#include "Land.h"
|
||||
#include "House.h"
|
||||
#include "Inventory/HousingItem.h"
|
||||
#include "Inventory/ItemContainer.h"
|
||||
|
||||
#include "Forwards.h"
|
||||
#include "HousingZone.h"
|
||||
|
@ -51,7 +55,7 @@ bool Sapphire::HousingZone::init()
|
|||
}
|
||||
|
||||
|
||||
int housingIndex;
|
||||
uint32_t housingIndex = 0;
|
||||
if( m_territoryTypeId == 339 )
|
||||
housingIndex = 0;
|
||||
else if( m_territoryTypeId == 340 )
|
||||
|
@ -64,6 +68,52 @@ bool Sapphire::HousingZone::init()
|
|||
auto pExdData = g_fw.get< Data::ExdDataGenerated >();
|
||||
auto info = pExdData->get< Sapphire::Data::HousingLandSet >( housingIndex );
|
||||
|
||||
// build yard objects array indices
|
||||
int16_t cursor = -1;
|
||||
uint16_t index = 0;
|
||||
for( const auto size : info->plotSize )
|
||||
{
|
||||
uint16_t itemMax = 0;
|
||||
switch( size )
|
||||
{
|
||||
case 0:
|
||||
itemMax = 20;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
itemMax = 30;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
itemMax = 40;
|
||||
break;
|
||||
}
|
||||
|
||||
int16_t start = cursor + 1;
|
||||
int16_t end = cursor + itemMax;
|
||||
|
||||
m_yardObjectArrayBounds[ index++ ] = std::make_pair( start, end );
|
||||
|
||||
// reset cursor for subdivision
|
||||
if( index == 30 )
|
||||
{
|
||||
cursor = -1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
cursor += itemMax;
|
||||
}
|
||||
|
||||
// zero out the yard obj arrays so we don't leak memory like SE does :^)
|
||||
Common::HousingObject obj {};
|
||||
memset( &obj, 0x0, sizeof( Common::HousingObject ) );
|
||||
|
||||
for( auto& arr : m_yardObjects )
|
||||
{
|
||||
arr.fill( obj );
|
||||
}
|
||||
|
||||
auto housingMgr = g_fw.get< World::Manager::HousingMgr >();
|
||||
auto landCache = housingMgr->getLandCacheMap();
|
||||
|
||||
|
@ -83,7 +133,8 @@ bool Sapphire::HousingZone::init()
|
|||
// setup house
|
||||
if( entry.m_houseId )
|
||||
{
|
||||
auto house = make_House( entry.m_houseId, m_landSetId, land->getLandIdent(), entry.m_estateName, entry.m_estateComment );
|
||||
auto house = make_House( entry.m_houseId, m_landSetId, land->getLandIdent(), entry.m_estateName,
|
||||
entry.m_estateComment );
|
||||
|
||||
housingMgr->updateHouseModels( house );
|
||||
land->setHouse( house );
|
||||
|
@ -94,7 +145,9 @@ bool Sapphire::HousingZone::init()
|
|||
m_landPtrMap[ entry.m_landId ] = land;
|
||||
|
||||
if( entry.m_houseId > 0 )
|
||||
registerHouseEntranceEObj( entry.m_landId );
|
||||
registerEstateEntranceEObj( entry.m_landId );
|
||||
|
||||
updateYardObjects( land->getLandIdent() );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -109,6 +162,8 @@ void Sapphire::HousingZone::onPlayerZoneIn( Entity::Player& player )
|
|||
"HousingZone::onPlayerZoneIn: Zone#" + std::to_string( getGuId() ) + "|" + std::to_string( getTerritoryTypeId() ) +
|
||||
", Entity#" + std::to_string( player.getId() ) );
|
||||
|
||||
auto isInSubdivision = isPlayerSubInstance( player ) ? true : false;
|
||||
|
||||
uint32_t yardPacketNum;
|
||||
uint32_t yardPacketTotal = 8;
|
||||
|
||||
|
@ -122,14 +177,20 @@ void Sapphire::HousingZone::onPlayerZoneIn( Entity::Player& player )
|
|||
housingObjectInit->data().packetNum = yardPacketNum;
|
||||
housingObjectInit->data().packetTotal = yardPacketTotal;
|
||||
|
||||
//TODO: Add Objects here
|
||||
auto yardObjectSize = sizeof( Common::HousingObject );
|
||||
|
||||
auto& objects = m_yardObjects[ isInSubdivision ? 1 : 0 ];
|
||||
|
||||
memcpy( &housingObjectInit->data().object, objects.data() + ( yardPacketNum * 100 ), yardObjectSize * 100 );
|
||||
|
||||
player.queuePacket( housingObjectInit );
|
||||
}
|
||||
|
||||
auto landSetMap = makeZonePacket< FFXIVIpcLandSetMap >( player.getId() );
|
||||
landSetMap->data().subdivision = !isPlayerSubInstance( player ) ? 2 : 1;
|
||||
uint8_t startIndex = !isPlayerSubInstance( player ) ? 0 : 30;
|
||||
landSetMap->data().subdivision = isInSubdivision ? 1 : 2;
|
||||
|
||||
uint8_t startIndex = isInSubdivision ? 30 : 0;
|
||||
|
||||
for( uint8_t i = startIndex, count = 0; i < ( startIndex + 30 ); i++, count++ )
|
||||
{
|
||||
landSetMap->data().landInfo[ count ].status = 1;
|
||||
|
@ -252,16 +313,112 @@ Sapphire::LandPtr Sapphire::HousingZone::getLand( uint8_t id )
|
|||
return it->second;
|
||||
}
|
||||
|
||||
Sapphire::Entity::EventObjectPtr Sapphire::HousingZone::registerHouseEntranceEObj( uint8_t plotId )
|
||||
Sapphire::Entity::EventObjectPtr Sapphire::HousingZone::registerEstateEntranceEObj( uint8_t landId )
|
||||
{
|
||||
auto land = getLand( plotId );
|
||||
auto land = getLand( landId );
|
||||
assert( land );
|
||||
|
||||
auto eObj = Entity::make_EventObject( getNextEObjId(), 2002737, 0, 4, land->getMapMarkerPosition(), 0.f, "entrance" );
|
||||
eObj->setHousingLink( plotId << 8 );
|
||||
eObj->setHousingLink( landId << 8 );
|
||||
eObj->setScale( 1.f );
|
||||
|
||||
registerEObj( eObj );
|
||||
|
||||
return eObj;
|
||||
}
|
||||
|
||||
void Sapphire::HousingZone::updateYardObjects( Sapphire::Common::LandIdent ident )
|
||||
{
|
||||
auto housingMgr = g_fw.get< World::Manager::HousingMgr >();
|
||||
auto& landStorage = housingMgr->getEstateInventory( ident );
|
||||
|
||||
auto yardContainer = landStorage[ InventoryType::HousingExteriorPlacedItems ];
|
||||
|
||||
auto arrayBounds = m_yardObjectArrayBounds[ ident.landId ];
|
||||
auto yardMapIndex = ident.landId <= 29 ? 0 : 1;
|
||||
|
||||
for( const auto& item : yardContainer->getItemMap() )
|
||||
{
|
||||
auto housingItem = std::dynamic_pointer_cast< Inventory::HousingItem >( item.second );
|
||||
assert( housingItem );
|
||||
|
||||
auto idx = item.first + arrayBounds.first;
|
||||
m_yardObjects[ yardMapIndex ][ idx ] = housingMgr->getYardObjectForItem( housingItem );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::HousingZone::spawnYardObject( uint8_t landId, uint16_t slotId, Inventory::HousingItem& item )
|
||||
{
|
||||
auto bounds = m_yardObjectArrayBounds[ landId ];
|
||||
auto offset = bounds.first + slotId;
|
||||
|
||||
Common::HousingObject obj {};
|
||||
|
||||
obj.itemId = item.getAdditionalData();
|
||||
obj.itemRotation = item.getRot();
|
||||
|
||||
obj.pos = item.getPos();
|
||||
|
||||
// link obj
|
||||
auto yardMapIndex = landId <= 29 ? 0 : 1;
|
||||
m_yardObjects[ yardMapIndex ][ offset ] = obj;
|
||||
|
||||
// spawn obj in zone
|
||||
for( const auto& player : m_playerMap )
|
||||
{
|
||||
auto packet = makeZonePacket< Server::FFXIVIpcYardObjectSpawn >( player.second->getId() );
|
||||
|
||||
packet->data().landId = landId;
|
||||
packet->data().objectArray = static_cast< uint8_t >( slotId );
|
||||
packet->data().object = obj;
|
||||
|
||||
player.second->queuePacket( packet );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::HousingZone::updateYardObjectPos( Entity::Player& sourcePlayer, uint16_t slot, uint16_t landId,
|
||||
Inventory::HousingItem& item )
|
||||
{
|
||||
auto bounds = m_yardObjectArrayBounds[ landId ];
|
||||
auto offset = bounds.first + slot;
|
||||
auto yardMapIndex = landId <= 29 ? 0 : 1;
|
||||
|
||||
auto& obj = m_yardObjects[ yardMapIndex ][ offset ];
|
||||
|
||||
obj.itemRotation = item.getRot();
|
||||
obj.pos = item.getPos();
|
||||
|
||||
for( const auto& player : m_playerMap )
|
||||
{
|
||||
// don't send it from the origin player, it already has the position from the move request
|
||||
if( player.second->getId() == sourcePlayer.getId() )
|
||||
continue;
|
||||
|
||||
auto packet = makeZonePacket< Server::FFXIVIpcHousingObjectMove >( player.second->getId() );
|
||||
|
||||
packet->data().itemRotation = item.getRot();
|
||||
packet->data().pos = item.getPos();
|
||||
|
||||
packet->data().landId = landId;
|
||||
packet->data().objectArray = slot;
|
||||
|
||||
player.second->queuePacket( packet );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::HousingZone::despawnYardObject( uint16_t landId, uint16_t slot )
|
||||
{
|
||||
auto bounds = m_yardObjectArrayBounds[ landId ];
|
||||
auto offset = bounds.first + slot;
|
||||
auto yardMapIndex = landId <= 29 ? 0 : 1;
|
||||
|
||||
memset( &m_yardObjects[ yardMapIndex ][ offset ], 0x00, sizeof( Common::HousingObject ) );
|
||||
|
||||
for( const auto& player : m_playerMap )
|
||||
{
|
||||
auto param = ( landId << 16 ) | slot;
|
||||
auto pkt = Server::makeActorControl143( player.second->getId(), Network::ActorControl::RemoveExteriorHousingItem, param );
|
||||
|
||||
player.second->queuePacket( pkt );
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
#include "Zone.h"
|
||||
#include "Forwards.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Sapphire
|
||||
{
|
||||
enum class LandPurchaseResult
|
||||
|
@ -50,15 +52,40 @@ namespace Sapphire
|
|||
uint32_t getLandSetId() const;
|
||||
Sapphire::LandPtr getLand( uint8_t id );
|
||||
|
||||
Entity::EventObjectPtr registerHouseEntranceEObj( uint8_t plotId );
|
||||
Entity::EventObjectPtr registerEstateEntranceEObj( uint8_t landId );
|
||||
|
||||
void updateYardObjects( Common::LandIdent ident );
|
||||
void spawnYardObject( uint8_t landId, uint16_t slotId, Sapphire::Inventory::HousingItem& item );
|
||||
void updateYardObjectPos( Entity::Player& sourcePlayer, uint16_t slot, uint16_t landId,
|
||||
Inventory::HousingItem& item );
|
||||
void despawnYardObject( uint16_t landId, uint16_t slot );
|
||||
|
||||
private:
|
||||
using LandPtrMap = std::unordered_map< uint8_t, Sapphire::LandPtr >;
|
||||
using YardObjectArray = std::array< Common::HousingObject, 800 >;
|
||||
using YardObjectSubdivisionArray = std::array< YardObjectArray, 2 >;
|
||||
|
||||
/*!
|
||||
* @brief Maps the start and end index of the yard object array for a specific plot
|
||||
*
|
||||
* pair.first = start index
|
||||
* pair.second = end index
|
||||
*/
|
||||
using YardObjectArrayBoundsPair = std::pair< uint16_t, uint16_t >;
|
||||
|
||||
/*!
|
||||
* @brief Maps each plot to a YardObjectArrayBoundsPair to the start/end index of the yard object array.
|
||||
*/
|
||||
using YardObjectArrayBoundsArray = std::array< YardObjectArrayBoundsPair, 60 >;
|
||||
|
||||
const uint32_t m_landSetMax = 18;
|
||||
LandPtrMap m_landPtrMap;
|
||||
uint8_t m_wardNum;
|
||||
uint32_t m_landSetId;
|
||||
uint32_t m_territoryTypeId;
|
||||
|
||||
YardObjectSubdivisionArray m_yardObjects;
|
||||
YardObjectArrayBoundsArray m_yardObjectArrayBounds;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -70,43 +70,6 @@ void Sapphire::Land::init( Common::LandType type, uint8_t size, uint8_t state, u
|
|||
m_mapMarkerPosition.y = info->y;
|
||||
m_mapMarkerPosition.z = info->z;
|
||||
}
|
||||
|
||||
switch( m_size )
|
||||
{
|
||||
case HouseSize::Cottage:
|
||||
m_maxPlacedExternalItems = 20;
|
||||
m_maxPlacedInternalItems = 200;
|
||||
break;
|
||||
case HouseSize::House:
|
||||
m_maxPlacedExternalItems = 30;
|
||||
m_maxPlacedInternalItems = 300;
|
||||
break;
|
||||
case HouseSize::Mansion:
|
||||
m_maxPlacedExternalItems = 40;
|
||||
m_maxPlacedInternalItems = 400;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// init item containers
|
||||
// auto setupContainer = [ this ]( InventoryType type, uint16_t maxSize )
|
||||
// {
|
||||
// m_landInventoryMap[ type ] = make_ItemContainer( type, maxSize, "houseiteminventory", true, true );
|
||||
// };
|
||||
//
|
||||
// setupContainer( InventoryType::HousingOutdoorAppearance, 8 );
|
||||
// setupContainer( InventoryType::HousingOutdoorPlacedItems, m_maxPlacedExternalItems );
|
||||
// setupContainer( InventoryType::HousingOutdoorStoreroom, m_maxPlacedExternalItems );
|
||||
//
|
||||
// setupContainer( InventoryType::HousingInteriorAppearance, 9 );
|
||||
//
|
||||
// // nb: so we're going to store these internally in one container because SE is fucked in the head
|
||||
// // but when an inventory is requested, we will split them into groups of 50
|
||||
// setupContainer( InventoryType::HousingInteriorPlacedItems1, m_maxPlacedInternalItems );
|
||||
// setupContainer( InventoryType::HousingInteriorStoreroom1, m_maxPlacedInternalItems );
|
||||
//
|
||||
// loadItemContainerContents();
|
||||
}
|
||||
|
||||
uint32_t Sapphire::Land::convertItemIdToHousingItemId( uint32_t itemId )
|
||||
|
@ -251,7 +214,7 @@ void Sapphire::Land::updateLandDb()
|
|||
uint32_t houseId = 0;
|
||||
|
||||
if( getHouse() )
|
||||
houseId = getHouse()->getHouseId();
|
||||
houseId = getHouse()->getId();
|
||||
|
||||
// todo: change to prepared statement
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
|
@ -281,54 +244,7 @@ void Sapphire::Land::update( uint32_t currTime )
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t Sapphire::Land::getNextHouseId()
|
||||
Sapphire::Land::InvMaxItemsPair Sapphire::Land::getInventoryItemMax() const
|
||||
{
|
||||
auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >();
|
||||
auto pQR = pDb->query( "SELECT MAX( HouseId ) FROM house" );
|
||||
|
||||
if( !pQR->next() )
|
||||
return 0;
|
||||
|
||||
return pQR->getUInt( 1 ) + 1;
|
||||
}
|
||||
|
||||
bool Sapphire::Land::setPreset( uint32_t itemId )
|
||||
{
|
||||
auto housingItemId = convertItemIdToHousingItemId( itemId );
|
||||
|
||||
auto exdData = g_fw.get< Sapphire::Data::ExdDataGenerated >();
|
||||
if( !exdData )
|
||||
return false;
|
||||
|
||||
auto housingPreset = exdData->get< Sapphire::Data::HousingPreset >( housingItemId );
|
||||
if( !housingPreset )
|
||||
return false;
|
||||
|
||||
if( !getHouse() )
|
||||
{
|
||||
// todo: i guess we'd create a house here?
|
||||
auto newId = getNextHouseId();
|
||||
|
||||
m_pHouse = make_House( newId, getLandSetId(), getLandIdent(), "Estate #" + std::to_string( m_landIdent.landId + 1 ), "" );
|
||||
}
|
||||
|
||||
|
||||
getHouse()->setHousePart( Common::HousePartSlot::ExteriorRoof, convertItemIdToHousingItemId( housingPreset->exteriorRoof ) );
|
||||
getHouse()->setHousePart( Common::HousePartSlot::ExteriorWall, convertItemIdToHousingItemId( housingPreset->exteriorWall ) );
|
||||
getHouse()->setHousePart( Common::HousePartSlot::ExteriorWindow, convertItemIdToHousingItemId( housingPreset->exteriorWindow ) );
|
||||
getHouse()->setHousePart( Common::HousePartSlot::ExteriorDoor, convertItemIdToHousingItemId( housingPreset->exteriorDoor ) );
|
||||
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorWall, convertItemIdToHousingItemId( housingPreset->interiorWall ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorFloor, convertItemIdToHousingItemId( housingPreset->interiorFlooring ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight, convertItemIdToHousingItemId( housingPreset->interiorLighting ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorWall_Attic, convertItemIdToHousingItemId( housingPreset->otherFloorWall ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorFloor_Attic, convertItemIdToHousingItemId( housingPreset->otherFloorFlooring ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight_Attic, convertItemIdToHousingItemId( housingPreset->otherFloorLighting ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorWall_Basement, convertItemIdToHousingItemId( housingPreset->basementWall ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorFloor_Basement, convertItemIdToHousingItemId( housingPreset->basementFlooring ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight_Basement, convertItemIdToHousingItemId( housingPreset->basementLighting ) );
|
||||
getHouse()->setHouseInteriorPart( Common::HousingInteriorSlot::InteriorLight_Mansion, convertItemIdToHousingItemId( housingPreset->mansionLighting ) );
|
||||
|
||||
|
||||
return true;
|
||||
return std::make_pair( m_maxPlacedExternalItems, m_maxPlacedInternalItems );
|
||||
}
|
|
@ -20,6 +20,7 @@ namespace Sapphire
|
|||
void init( Common::LandType type, uint8_t size, uint8_t state, uint32_t currentPrice, uint64_t ownerId, uint64_t houseId );
|
||||
|
||||
using LandInventoryMap = std::unordered_map< uint16_t, ItemContainerPtr >;
|
||||
using InvMaxItemsPair = std::pair< uint16_t, uint16_t >;
|
||||
|
||||
//Primary state
|
||||
void setSize( uint8_t size );
|
||||
|
@ -61,6 +62,8 @@ namespace Sapphire
|
|||
void setLandTag( uint8_t slot, uint8_t tag );
|
||||
uint8_t getLandTag( uint8_t slot );
|
||||
|
||||
InvMaxItemsPair getInventoryItemMax() const;
|
||||
|
||||
private:
|
||||
uint32_t convertItemIdToHousingItemId( uint32_t itemId );
|
||||
uint32_t getNextHouseId();
|
||||
|
|
Loading…
Add table
Reference in a new issue