1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-27 14:57:44 +00:00

Merge remote-tracking branch 'origin/develop'

This commit is contained in:
Mordred 2023-02-04 14:24:31 +01:00
commit 8afaa0a42d
82 changed files with 6056 additions and 2301 deletions

235
deps/datReader/Exd.cpp vendored
View file

@ -1,8 +1,6 @@
#include "Exd.h"
#include "bparse.h"
#include "stream.h"
#include <fstream>
#include "Exh.h"
using xiv::utils::bparse::extract;
@ -53,32 +51,24 @@ template<>
namespace xiv::exd
{
Exd::Exd( std::shared_ptr< Exh > i_exh, const std::vector< std::shared_ptr< dat::File>>& i_files )
Exd::Exd( std::shared_ptr< Exh > exh, const std::vector< std::shared_ptr< dat::File > >& files )
{
_exh = i_exh;
_files = i_files;
_exh = exh;
// Iterates over all the files
const uint32_t member_count = _exh->get_members().size();
for( auto& file_ptr : _files )
for( auto& file : files )
{
// Get a stream
std::vector< char > dataCpy = file_ptr->get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
std::vector< char > dataCpy = file->get_data_sections().front();
// Extract the header and skip to the record indices
auto exd_header = extract< ExdHeader >( iss );
iss.seekg( 0x20 );
// Extract the header
auto exdHeader = extract< ExdHeader >( dataCpy, 0 );
// Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex );
std::vector< ExdRecordIndex > record_indices;
record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i )
const uint32_t recordCount = exdHeader.index_size / sizeof( ExdRecordIndex );
for( uint32_t i = 0; i < recordCount; ++i )
{
auto recordIndex = extract< ExdRecordIndex >( iss );
_idCache[ recordIndex.id ] = ExdCacheEntry{ file_ptr, recordIndex.offset };
auto recordIndex = extract< ExdRecordIndex >( dataCpy, 32 + ( i * sizeof( ExdRecordIndex ) ) );
_idCache[ recordIndex.id ] = ExdCacheEntry{ file, recordIndex.offset + 6, extract< uint8_t >( dataCpy, recordIndex.offset + 5 ) };
}
}
}
@ -91,40 +81,24 @@ namespace xiv::exd
{
auto cacheEntryIt = _idCache.find( id );
if( cacheEntryIt == _idCache.end() )
throw std::runtime_error( "Id not found: " + std::to_string( id ) );
if( cacheEntryIt == _idCache.end() || subRow >= cacheEntryIt->second.subRows )
throw std::runtime_error( "Id + SubId combination not found: " + std::to_string( id ) + "." + std::to_string( subRow ) );
// Iterates over all the files
const uint32_t member_count = _exh->get_members().size();
auto& file_ptr = cacheEntryIt->second.file;
auto dataCpy = cacheEntryIt->second.file->get_data_sections().front();
std::vector< char > dataCpy = file_ptr->get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
std::vector< Field > fields;
fields.reserve( _exh->get_members().size() );
// Get the vector fields for the given record and preallocate it
auto fields = _data[ id ];
fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 );
uint32_t baseOffset = cacheEntryIt->second.offset + ( subRow * _exh->get_header().data_offset + 2 * ( subRow + 1 ) );
uint8_t subRows = *reinterpret_cast< uint8_t* >( &dataCpy[ cacheEntryIt->second.offset + 5 ] );
if( subRow >= subRows )
throw std::runtime_error( "Out of bounds sub-row!" );
int offset = cacheEntryIt->second.offset + 6 + ( subRow * _exh->get_header().data_offset + 2 * ( subRow + 1 ) );
for( auto& member_entry : _exh->get_exh_members() )
for( auto& memberEntry : _exh->get_exh_members() )
{
// Seek to the position of the member to extract.
// 6 is because we have uint32_t/uint16_t at the start of each record
iss.seekg( offset + member_entry.offset );
// Switch depending on the type to extract
switch( member_entry.type )
switch( memberEntry.type )
{
case DataType::string:
// Extract the offset to the actual string
// Seek to it then extract the actual string
// Then extract the actual string from that offset
{
throw std::runtime_error( "String not implemented for variant 2!" );
//auto string_offset = extract<uint32_t>( iss, "string_offset", false );
@ -134,50 +108,46 @@ namespace xiv::exd
break;
case DataType::boolean:
fields.emplace_back( extract< bool >( iss, "bool" ) );
fields.emplace_back( extract< bool >( dataCpy, baseOffset + memberEntry.offset ) );
break;
case DataType::int8:
fields.emplace_back( extract< int8_t >( iss, "int8_t" ) );
fields.emplace_back( extract< int8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break;
case DataType::uint8:
fields.emplace_back( extract< uint8_t >( iss, "uint8_t" ) );
fields.emplace_back( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break;
case DataType::int16:
fields.emplace_back( extract< int16_t >( iss, "int16_t", false ) );
fields.emplace_back( extract< int16_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::uint16:
fields.emplace_back( extract< uint16_t >( iss, "uint16_t", false ) );
fields.emplace_back( extract< uint16_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::int32:
fields.emplace_back( extract< int32_t >( iss, "int32_t", false ) );
fields.emplace_back( extract< int32_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::uint32:
fields.emplace_back( extract< uint32_t >( iss, "uint32_t", false ) );
fields.emplace_back( extract< uint32_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::float32:
fields.emplace_back( extract< float >( iss, "float", false ) );
fields.emplace_back( extract< float >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::uint64:
fields.emplace_back( extract< uint64_t >( iss, "uint64_t", false ) );
fields.emplace_back( extract< uint64_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
default:
auto type = static_cast< uint16_t >( member_entry.type );
auto type = static_cast< uint16_t >( memberEntry.type );
if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = extract< uint64_t >( iss, "bool" );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
fields.emplace_back( ( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) & ( 1 << ( type - 0x19 ) ) ) != 0 );
break;
}
}
@ -193,84 +163,68 @@ namespace xiv::exd
if( cacheEntryIt == _idCache.end() )
throw std::runtime_error( "Id not found: " + std::to_string( id ) );
// Iterates over all the files
const uint32_t member_count = _exh->get_members().size();
auto& file_ptr = cacheEntryIt->second.file;
auto dataCpy = cacheEntryIt->second.file->get_data_sections().front();
std::vector< char > dataCpy = file_ptr->get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
std::vector< Field > fields;
fields.reserve( _exh->get_members().size() );
// Get the vector fields for the given record and preallocate it
auto fields = _data[ id ];
fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 );
auto stringBaseOffset = cacheEntryIt->second.offset + _exh->get_header().data_offset;
uint8_t subRows = *reinterpret_cast< uint8_t* >( &dataCpy[ cacheEntryIt->second.offset + 5 ] );
for( auto& member_entry : _exh->get_exh_members() )
for( auto& memberEntry : _exh->get_exh_members() )
{
// Seek to the position of the member to extract.
// 6 is because we have uint32_t/uint16_t at the start of each record
iss.seekg( cacheEntryIt->second.offset + 6 + member_entry.offset );
// Switch depending on the type to extract
switch( member_entry.type )
switch( memberEntry.type )
{
case DataType::string:
// Extract the offset to the actual string
// Seek to it then extract the actual string
// Then extract the actual string from that offset
{
auto string_offset = extract< uint32_t >( iss, "string_offset", false );
iss.seekg( cacheEntryIt->second.offset + 6 + _exh->get_header().data_offset + string_offset );
fields.emplace_back( utils::bparse::extract_cstring( iss, "string" ) );
auto stringOffset = extract< uint32_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false );
fields.emplace_back( utils::bparse::extract_cstring( dataCpy, stringBaseOffset + stringOffset ) );
}
break;
case DataType::boolean:
fields.emplace_back( extract< bool >( iss, "bool" ) );
fields.emplace_back( extract< bool >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset ) );
break;
case DataType::int8:
fields.emplace_back( extract< int8_t >( iss, "int8_t" ) );
fields.emplace_back( extract< int8_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset ) );
break;
case DataType::uint8:
fields.emplace_back( extract< uint8_t >( iss, "uint8_t" ) );
fields.emplace_back( extract< uint8_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset ) );
break;
case DataType::int16:
fields.emplace_back( extract< int16_t >( iss, "int16_t", false ) );
fields.emplace_back( extract< int16_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break;
case DataType::uint16:
fields.emplace_back( extract< uint16_t >( iss, "uint16_t", false ) );
fields.emplace_back( extract< uint16_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break;
case DataType::int32:
fields.emplace_back( extract< int32_t >( iss, "int32_t", false ) );
fields.emplace_back( extract< int32_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break;
case DataType::uint32:
fields.emplace_back( extract< uint32_t >( iss, "uint32_t", false ) );
fields.emplace_back( extract< uint32_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break;
case DataType::float32:
fields.emplace_back( extract< float >( iss, "float", false ) );
fields.emplace_back( extract< float >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break;
case DataType::uint64:
fields.emplace_back( extract< uint64_t >( iss, "uint64_t", false ) );
fields.emplace_back( extract< uint64_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break;
default:
auto type = static_cast< uint16_t >( member_entry.type );
auto type = static_cast< uint16_t >( memberEntry.type );
if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = extract< uint64_t >( iss, "bool" );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
fields.emplace_back( ( extract< uint8_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset ) & ( 1 << ( type - 0x19 ) ) ) != 0 );
break;
}
}
@ -279,106 +233,99 @@ namespace xiv::exd
}
// Get all rows
const std::map< uint32_t, std::vector< Field>>& Exd::get_rows()
const std::map< ExdRow, std::vector< Field >, exdRowSort > Exd::get_rows()
{
// Iterates over all the files
const uint32_t member_count = _exh->get_members().size();
for( auto& file_ptr : _files )
std::map< ExdRow, std::vector< Field >, exdRowSort > data;
// Iterates over all the cached ids
const uint32_t memberCount = _exh->get_members().size();
for( auto& cacheEntry : _idCache )
{
// Get a stream
std::vector< char > dataCpy = file_ptr->get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
std::vector< char > dataCpy = cacheEntry.second.file->get_data_sections().front();
// Extract the header and skip to the record indices
auto exd_header = extract< ExdHeader >( iss );
iss.seekg( 0x20 );
auto baseOffset = cacheEntry.second.offset;
auto stringBaseOffset = baseOffset + _exh->get_header().data_offset;
// Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex );
std::vector< ExdRecordIndex > record_indices;
record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i )
{
record_indices.emplace_back( extract< ExdRecordIndex >( iss ) );
}
for( auto& record_index : record_indices )
for( int32_t i = 0; i < cacheEntry.second.subRows; i++ )
{
// Get the vector fields for the given record and preallocate it
auto& fields = _data[ record_index.id ];
fields.reserve( member_count );
ExdRow row = { cacheEntry.first, i };
auto& fields = data[ row ];
fields.reserve( memberCount );
for( auto& member_entry : _exh->get_exh_members() )
if( _exh->get_header().variant == 2 )
baseOffset = cacheEntry.second.offset + ( i * _exh->get_header().data_offset + 2 * ( i + 1 ) );
for( auto& memberEntry : _exh->get_exh_members() )
//for( auto& member_entry : _exh->get_members() )
{
// Seek to the position of the member to extract.
// 6 is because we have uint32_t/uint16_t at the start of each record
iss.seekg( record_index.offset + 6 + member_entry.offset );
// Switch depending on the type to extract
switch( member_entry.type )
switch( memberEntry.type )
{
case DataType::string:
// Extract the offset to the actual string
// Seek to it then extract the actual string
// Then extract the actual string from that offset
{
auto string_offset = extract< uint32_t >( iss, "string_offset", false );
iss.seekg( record_index.offset + 6 + _exh->get_header().data_offset + string_offset );
fields.emplace_back( utils::bparse::extract_cstring( iss, "string" ) );
if( _exh->get_header().variant == 1 )
{
auto stringOffset = extract< uint32_t >( dataCpy, baseOffset + memberEntry.offset, false );
fields.emplace_back( utils::bparse::extract_cstring( dataCpy, stringBaseOffset + stringOffset ) );
}
else if( _exh->get_header().variant == 2 )
{
throw std::runtime_error( "String not implemented for variant 2!" );
}
}
break;
case DataType::boolean:
fields.emplace_back( extract< bool >( iss, "bool" ) );
fields.emplace_back( extract< bool >( dataCpy, baseOffset + memberEntry.offset ) );
break;
case DataType::int8:
fields.emplace_back( extract< int8_t >( iss, "int8_t" ) );
fields.emplace_back( extract< int8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break;
case DataType::uint8:
fields.emplace_back( extract< uint8_t >( iss, "uint8_t" ) );
fields.emplace_back( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break;
case DataType::int16:
fields.emplace_back( extract< int16_t >( iss, "int16_t", false ) );
fields.emplace_back( extract< int16_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::uint16:
fields.emplace_back( extract< uint16_t >( iss, "uint16_t", false ) );
fields.emplace_back( extract< uint16_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::int32:
fields.emplace_back( extract< int32_t >( iss, "int32_t", false ) );
fields.emplace_back( extract< int32_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::uint32:
fields.emplace_back( extract< uint32_t >( iss, "uint32_t", false ) );
fields.emplace_back( extract< uint32_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::float32:
fields.emplace_back( extract< float >( iss, "float", false ) );
fields.emplace_back( extract< float >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
case DataType::uint64:
fields.emplace_back( extract< uint64_t >( iss, "uint64_t", false ) );
fields.emplace_back( extract< uint64_t >( dataCpy, baseOffset + memberEntry.offset, false ) );
break;
default:
auto type = static_cast< uint16_t >( member_entry.type );
auto type = static_cast< uint16_t >( memberEntry.type );
if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = extract< uint64_t >( iss, "bool" );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
fields.emplace_back( ( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) & ( 1 << ( type - 0x19 ) ) ) != 0 );
break;
}
}
}
}
return _data;
return data;
}
}

31
deps/datReader/Exd.h vendored
View file

@ -3,6 +3,8 @@
#include <memory>
#include <map>
#include <unordered_map>
#include <set>
#include <variant>
@ -30,19 +32,37 @@ namespace xiv::exd
{
std::shared_ptr< dat::File > file;
uint32_t offset;
uint8_t subRows;
};
struct ExdRow
{
uint32_t rowId;
uint8_t subRowId;
};
struct exdRowSort
{
constexpr bool operator()( const ExdRow& _Left, const ExdRow& _Right ) const
{
if( _Left.rowId == _Right.rowId )
return _Left.subRowId < _Right.subRowId;
return _Left.rowId < _Right.rowId;
}
};
// Data for a given language
class Exd
{
public:
// i_exh: the header
// i_files: the multiple exd files
// exh: the header
// files: the multiple exd files
Exd()
{
}
Exd( std::shared_ptr< Exh > i_exh, const std::vector< std::shared_ptr< dat::File>>& i_files );
Exd( std::shared_ptr< Exh > exh, const std::vector< std::shared_ptr< dat::File > >& files );
~Exd();
@ -53,12 +73,9 @@ namespace xiv::exd
const std::vector< Field > get_row( uint32_t id, uint32_t subRow );
// Get all rows
const std::map< uint32_t, std::vector< Field>>& get_rows();
const std::map< ExdRow, std::vector< Field >, exdRowSort > get_rows();
protected:
// Data indexed by the ID of the row, the vector is field with the same order as exh.members
std::map< uint32_t, std::vector< Field>> _data;
std::vector< std::shared_ptr< dat::File>> _files;
std::shared_ptr< Exh > _exh;
std::map< uint32_t, ExdCacheEntry > _idCache;
};

View file

@ -6,6 +6,7 @@
#include <mutex>
#include <filesystem>
#include <vector>
namespace xiv
{

View file

@ -6,6 +6,7 @@
#include <mutex>
#include <filesystem>
#include <vector>
namespace xiv::dat
{

View file

@ -5,4 +5,9 @@ std::string xiv::utils::bparse::extract_cstring( std::istream& i_stream, const s
std::string temp_str;
std::getline( i_stream, temp_str, '\0' );
return temp_str;
}
}
std::string xiv::utils::bparse::extract_cstring( std::vector< char >& data, uint32_t pos )
{
return &data[ pos ];
}

View file

@ -93,9 +93,27 @@ namespace xiv::utils::bparse
}
}
template< typename StructType >
StructType extract( std::vector< char >& data, uint32_t pos, bool isLe = true )
{
StructType tempStruct = *reinterpret_cast< StructType* >( &data[ pos ] );
if( std::is_class< StructType >::value )
{
reorder( tempStruct );
}
else if( !isLe )
{
tempStruct = byteswap( tempStruct );
}
return tempStruct;
}
// For cstrings
std::string extract_cstring( std::istream& i_stream, const std::string& i_name );
std::string extract_cstring( std::vector< char >& data, uint32_t pos );
}
#endif // XIV_UTILS_BPARSE_H

View file

@ -6,6 +6,7 @@
#include "CommonGen.h"
#include "Vector3.h"
#include "Network/PacketDef/Ipcs.h"
// +---------------------------------------------------------------------------
// The following enumerations are structures to require their type be included.
@ -25,7 +26,7 @@ namespace Sapphire::Common
const uint8_t CURRENT_EXPANSION_ID = 3;
const uint8_t CLASSJOB_TOTAL = 38;
const uint8_t CLASSJOB_SLOTS = 28;
const uint8_t CLASSJOB_SLOTS = 30;
const uint8_t TOWN_COUNT = 6;
@ -51,11 +52,11 @@ namespace Sapphire::Common
enum InventoryOperation : uint16_t
{
Discard = 0x013C,
Move = 0x013D,
Swap = 0x013E,
Split = 0x013F,
Merge = 0x0141,
Discard = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 7,
Move = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 8,
Swap = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 9,
Split = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 10,
Merge = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 12
};
enum ClientLanguage : uint8_t
@ -161,44 +162,27 @@ namespace Sapphire::Common
enum class EquipSlotCategory : uint8_t
{
// main slots
CharaMainHand = 0,
CharaOffHand = 1,
CharaHead = 2,
CharaBody = 3,
CharaHands = 4,
CharaWaist = 5,
CharaLegs = 6,
CharaFeet = 7,
CharaEars = 8,
CharaNeck = 9,
CharaWrist = 10,
CharaRing = 11,
CharaSoulCrystal = 12,
/* following slots not seem to exist any more.
when multi-slot gear is moved into equipment slot, normal slot listed above is used.
client will move any incompatible gears into armory but no InventoryModifiyHandler is sent.
server need to move those silently in order to sync with client.
*/
/*! Cannot equip gear to offhand slot */
//MainTwoHandedWeapon = 13,
/*! Can be equipped in either main or offhand slot */
//MainOrOffHand = 14, // unused
/*! Cannot equip gear to head */
//BodyDisallowHead = 15,
/*! Cannot equip gear to hands, legs and feet slots */
//BodyDisallowHandsLegsFeet = 16,
/*! Cannot equip gear to feet slot */
//LegsDisallowFeet = 18,
/*! Cannot equp gear to head, hands, legs, feet slots */
//BodyDisallowAll = 19,
/*! Cannot equip gear to hands slot */
//BodyDisallowHands = 20,
/*! Cannot equip gear to legs & feet slots */
//BodyDisallowLegsFeet = 21,
MainHand = 1,
OffHand = 2,
Head = 3,
Body = 4,
Hands = 5,
Waist = 6,
Legs = 7,
Feet = 8,
Ears = 9,
Neck = 10,
Wrist = 11,
Ring = 12,
MainTwoHandedWeapon = 13,
//MainOrOffHand = 14, // unused
BodyDisallowHead = 15,
BodyDisallowHandsLegsFeet = 16,
SoulCrystal = 17,
LegsDisallowFeet = 18,
BodyDisallowAll = 19,
BodyDisallowHands = 20,
BodyDisallowLegsFeet = 21,
};
enum InventoryType : uint16_t
@ -773,6 +757,7 @@ namespace Sapphire::Common
BetweenAreas = 24,
BoundByDuty = 28,
Performing = 40,
WatchingCutscene = 50, // this is actually just a dummy, this id is different
@ -1263,9 +1248,6 @@ namespace Sapphire::Common
GetGil = 9, // p1: gil
EmptyCoffer = 11, // seems like no param
};
using PlayerStateFlagList = std::vector< PlayerStateFlag >;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -257,6 +257,10 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements()
"UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ? WHERE HouseId = ?;",
CONNECTION_BOTH );
prepareStatement( HOUSING_HOUSE_DEL,
"DELETE FROM house WHERE HouseId = ?;",
CONNECTION_BOTH );
prepareStatement( LAND_INV_SEL_ALL,
"SELECT houseiteminventory.*, charaglobalitem.catalogId, charaglobalitem.stain, charaglobalitem.CharacterId, "
"landplaceditems.PosX, landplaceditems.PosY, landplaceditems.PosZ, landplaceditems.Rotation "

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -111,6 +111,8 @@ namespace Sapphire::Network::ActorControl
ScreenFadeOut = 0xAA,
CeremonyDecoration = 0xB9,
ZoneIn = 0xC8,
ZoneInDefaultPos = 0xC9,
@ -213,7 +215,19 @@ namespace Sapphire::Network::ActorControl
SetFavorite = 0x1FC,
LearnTeleport = 0x1FD,
OpenRecommendationGuide = 0x200,
/*!
* param1 = event type bitmask
* 1 = Quest
* 2 = GuildLeveAssignment
* 4 = GuildOrderGuide
* 8 = TripleTriad
* 16 = CustomTalk
* 32 = PreHandler
*/
BeginMapUpdate = 0x1FF,
FinishMapUpdate = 0x200,
//OpenRecommendationGuide = 0x200,
ArmoryErrorMsg = 0x201,
AchievementPopup = 0x203,
@ -364,6 +378,7 @@ namespace Sapphire::Network::ActorControl
TitleList = 0x12F,
UpdatedSeenHowTos = 0x133,
CutscenePlayed = 0x134, // param1 = cutscene id
AllotAttribute = 0x135,
ClearFieldMarkers = 0x13A,

View file

@ -1,8 +1,8 @@
#ifndef _CORE_NETWORK_PACKETS_IPCS_H
#define _CORE_NETWORK_PACKETS_IPCS_H
#include <stdint.h>
namespace Sapphire::Network::Packets
{
@ -43,67 +43,67 @@ namespace Sapphire::Network::Packets
*/
enum ServerZoneIpcType : uint16_t
{
Ping = 0x0219, // updated 5.35 hotfix
Init = 0x0185, // updated 5.35 hotfix
Ping = 0x02A8, // updated 5.58 hotfix
Init = 0x013C, // updated 5.58 hotfix
ActorFreeSpawn = 0x0239, // updated 5.35 hotfix
InitZone = 0x03CD, // updated 5.35 hotfix
ActorFreeSpawn = 0x00B5, // updated 5.58 hotfix
InitZone = 0x0320, // updated 5.58 hotfix
EffectResult = 0x01C2, // updated 5.35 hotfix
ActorControl = 0x02A4, // updated 5.35 hotfix
ActorControlSelf = 0x02C8, // updated 5.35 hotfix
ActorControlTarget = 0x0209, // updated 5.35 hotfix
EffectResult = 0x0387, // updated 5.58 hotfix
ActorControl = 0x00B0, // updated 5.58 hotfix
ActorControlSelf = 0x02B6, // updated 5.58 hotfix
ActorControlTarget = 0x03C5, // updated 5.58 hotfix
/*!
* @brief Used when resting
*/
UpdateHpMpTp = 0x0319, // updated 5.35 hotfix
UpdateHpMpTp = 0x01A7, // updated 5.58 hotfix
///////////////////////////////////////////////////
ChatBanned = 0xF06B,
Playtime = 0x03A4, // updated 5.35 hotfix
Logout = 0x02AD, // updated 5.35 hotfix
CFNotify = 0x02C4, // updated 5.35 hotfix
Playtime = 0x0179, // updated 5.58 hotfix
Logout = 0x0214, // updated 5.58 hotfix
CFNotify = 0x0327, // updated 5.58 hotfix
CFMemberStatus = 0x0079,
CFDutyInfo = 0x0193, // updated 5.35 hotfix
CFDutyInfo = 0x03AA, // updated 5.58 hotfix
CFPlayerInNeed = 0xF07F,
CFPreferredRole = 0x0196, // updated 5.35 hotfix
CFCancel = 0x00EC, // updated 5.35 hotfix
CFPreferredRole = 0x024B, // updated 5.58 hotfix
CFCancel = 0x01AC, // updated 5.58 hotfix
SocialRequestError = 0xF0AD,
CFRegistered = 0x010C, // updated 5.35 hotfix
SocialRequestResponse = 0x01C7, // updated 5.35 hotfix
SocialMessage = 0x0308, // updated 5.35 hotfix
SocialMessage2 = 0x037C, // updated 5.35 hotfix
CancelAllianceForming = 0x00C6, // updated 4.2
CFRegistered = 0x029F, // updated 5.58 hotfix
SocialRequestResponse = 0x0082, // updated 5.58 hotfix
SocialMessage = 0x03CB, // updated 5.58 hotfix
SocialMessage2 = 0x01D7, // updated 5.58 hotfix
CancelAllianceForming = 0xF0C6, // updated 4.2
LogMessage = 0x00D0,
LogMessage = 0x0118, // updated 5.58 hotfix
Chat = 0x0349, // updated 5.35 hotfix
Chat = 0x00FE, // updated 5.58 hotfix
PartyChat = 0x0065,
WorldVisitList = 0xF0FE, // added 4.5
SocialList = 0x0216, // updated 5.35 hotfix
SocialList = 0x015F, // updated 5.58 hotfix
ExamineSearchInfo = 0x03C3, // updated 5.35 hotfix
UpdateSearchInfo = 0x0121, // updated 5.35 hotfix
InitSearchInfo = 0x036F, // updated 5.35 hotfix
ExamineSearchComment = 0x0102, // updated 4.1
ExamineSearchInfo = 0x0133, // updated 5.58 hotfix
UpdateSearchInfo = 0x03E5, // updated 5.58 hotfix
InitSearchInfo = 0x0321, // updated 5.58 hotfix
ExamineSearchComment = 0x03AD, // updated 5.58 hotfix
ServerNoticeShort = 0x017A, // updated 5.35 hotfix
ServerNotice = 0x02F8, // updated 5.35 hotfix
SetOnlineStatus = 0x03D7, // updated 5.35 hotfix
ServerNoticeShort = 0x0333, // updated 5.58 hotfix
ServerNotice = 0x0171, // updated 5.58 hotfix
SetOnlineStatus = 0x037B, // updated 5.58 hotfix
CountdownInitiate = 0x0237, // updated 5.25
CountdownCancel = 0x00D9, // updated 5.18
CountdownInitiate = 0x0111, // updated 5.58 hotfix
CountdownCancel = 0x0231, // updated 5.58 hotfix
PlayerAddedToBlacklist = 0x033F, // updated 5.1
PlayerRemovedFromBlacklist = 0x0385, // updated 5.1
BlackList = 0x02DB, // updated 5.35 hotfix
PlayerAddedToBlacklist = 0x024E, // updated 5.58 hotfix
PlayerRemovedFromBlacklist = 0x011D, // updated 5.58 hotfix
BlackList = 0x03C0, // updated 5.58 hotfix
LinkshellList = 0x01F0, // updated 5.35 hotfix
LinkshellList = 0x02E2, // updated 5.58 hotfix
MailDeleteRequest = 0xF12B, // updated 5.0
@ -114,166 +114,181 @@ namespace Sapphire::Network::Packets
MarketTaxRates = 0x01F8, // updated 5.35 hotfix
MarketBoardSearchResult = 0x032C, // updated 5.35 hotfix
MarketBoardItemListingCount = 0x038F, // updated 5.35 hotfix
MarketBoardItemListingHistory = 0x0186, // updated 5.35 hotfix
MarketBoardItemListing = 0x025F, // updated 5.35 hotfix
MarketBoardSearchResult = 0x01F1, // updated 5.58 hotfix
MarketBoardItemListingCount = 0x0068, // updated 5.58 hotfix
MarketBoardItemListingHistory = 0x01BA, // updated 5.58 hotfix
MarketBoardItemListing = 0x0076, // updated 5.58 hotfix
CharaFreeCompanyTag = 0x013B, // updated 4.5
FreeCompanyBoardMsg = 0x013C, // updated 4.5
FreeCompanyInfo = 0xF13D, // updated 4.5
ExamineFreeCompanyInfo = 0xF13E, // updated 4.5
FreeCompanyBoardMsg = 0x03DB, // updated 5.58 hotfix
FreeCompanyInfo = 0x01F7, // updated 5.58 hotfix
ExamineFreeCompanyInfo = 0x0324, // updated 5.58 hotfix
FreeCompanyUpdateShortMessage = 0xF157, // added 5.0
StatusEffectList = 0x0382, // updated 5.35 hotfix
StatusEffectList = 0x0074, // updated 5.58 hotfix
EurekaStatusEffectList = 0x0167, // updated 5.18
BossStatusEffectList = 0x0312, // added 5.1
Effect = 0x0192, // updated 5.35 hotfix
AoeEffect8 = 0x012C, // updated 5.35 hotfix
AoeEffect16 = 0x01B9, // updated 5.35 hotfix
AoeEffect24 = 0x02B4, // updated 5.35 hotfix
AoeEffect32 = 0x00A4, // updated 5.35 hotfix
PersistantEffect = 0x0317, // updated 5.35 hotfix
Effect = 0x03CA, // updated 5.58 hotfix
AoeEffect8 = 0x03C4, // updated 5.58 hotfix
AoeEffect16 = 0x00FA, // updated 5.58 hotfix
AoeEffect24 = 0x0339, // updated 5.58 hotfix
AoeEffect32 = 0x023C, // updated 5.58 hotfix
PersistantEffect = 0x025D, // updated 5.58 hotfix
GCAffiliation = 0x0105, // updated 5.35 hotfix
GCAffiliation = 0x0094, // updated 5.58 hotfix
PlayerSpawn = 0x0179, // updated 5.35 hotfix
NpcSpawn = 0x03A8, // updated 5.35 hotfix
PlayerSpawn = 0x01D8, // updated 5.58 hotfix
NpcSpawn = 0x00D2, // updated 5.58 hotfix
NpcSpawn2 = 0x01CB, // ( Bigger statuseffectlist? ) updated 5.3
ActorMove = 0x01BF, // updated 5.35 hotfix
ActorMove = 0x00F8, // updated 5.58 hotfix
ActorSetPos = 0x03DF, // updated 5.35 hotfix
ActorSetPos = 0x0299, // updated 5.58 hotfix
ActorCast = 0x0302, // updated 5.35 hotfix
ActorCast = 0x015D, // updated 5.58 hotfix
SomeCustomiseChangePacketProbably = 0x00CD, // added 5.18
PartyList = 0x02B2, // updated 5.35 hotfix
PartyMessage = 0x00AE, // updated 5.35 hotfix
HateRank = 0x02CC, // updated 5.35 hotfix
HateList = 0x0198, // updated 5.35 hotfix
ObjectSpawn = 0x02B8, // updated 5.35 hotfix
ObjectDespawn = 0x00C0, // updated 5.35 hotfix
UpdateClassInfo = 0x0235, // updated 5.35 hotfix
SilentSetClassJob = 0x018E, // updated 5.0 - seems to be the case, not sure if it's actually used for anything
PlayerSetup = 0x0290, // updated 5.35 hotfix
PlayerStats = 0x023B, // updated 5.35 hotfix
ActorOwner = 0x00E8, // updated 5.35 hotfix
PlayerStateFlags = 0x00F8, // updated 5.35 hotfix
PlayerClassInfo = 0x02C3, // updated 5.35 hotfix
CharaVisualEffect = 0x02E2, // updated 5.35 hotfix
PartyList = 0x0349, // updated 5.58 hotfix
PartyMessage = 0x00A4, // updated 5.58 hotfix
HateRank = 0x0150, // updated 5.58 hotfix
HateList = 0x0243, // updated 5.58 hotfix
ObjectSpawn = 0x0125, // updated 5.58 hotfix
ObjectDespawn = 0x0148, // updated 5.58 hotfix
UpdateClassInfo = 0x0084, // updated 5.58 hotfix
SilentSetClassJob = 0xF18E, // updated 5.0 - seems to be the case, not sure if it's actually used for anything
PlayerSetup = 0x01D5, // updated 5.58 hotfix
PlayerStats = 0x0295, // updated 5.58 hotfix
ActorOwner = 0x0260, // updated 5.58 hotfix
PlayerStateFlags = 0x03BF, // updated 5.58 hotfix
PlayerClassInfo = 0x0131, // updated 5.58 hotfix
CharaVisualEffect = 0x0292, // updated 5.58 hotfix
ModelEquip = 0x0277, // updated 5.35 hotfix
Examine = 0x00BC, // updated 5.35 hotfix
CharaNameReq = 0x008E, // updated 5.35 hotfix
ModelEquip = 0x03A2, // updated 5.58 hotfix
Examine = 0x0365, // updated 5.58 hotfix
CharaNameReq = 0x01F0, // updated 5.58 hotfix
// nb: see #565 on github
UpdateRetainerItemSalePrice = 0xF19F, // updated 5.0
RetainerSaleHistory = 0x020E, // updated 5.21 hotfix
RetainerInformation = 0x01F9, // updated 5.35 hotfix
RetainerSaleHistory = 0x03CE, // updated 5.58 hotfix
RetainerInformation = 0x022F, // updated 5.58 hotfix
SetLevelSync = 0x1186, // not updated for 4.4, not sure what it is anymore
ItemInfo = 0x0214, // updated 5.35 hotfix
ContainerInfo = 0x00C5, // updated 5.35 hotfix
InventoryTransactionFinish = 0x02F0, // updated 5.35 hotfix
InventoryTransaction = 0x01FD, // updated 5.35 hotfix
CurrencyCrystalInfo = 0x0379, // updated 5.35 hotfix
ItemInfo = 0x01CC, // updated 5.58 hotfix
ContainerInfo = 0x025C, // updated 5.58 hotfix
InventoryTransactionFinish = 0x0176, // updated 5.58 hotfix
InventoryTransaction = 0x027F, // updated 5.58 hotfix
CurrencyCrystalInfo = 0x0345, // updated 5.58 hotfix
InventoryActionAck = 0x03E4, // updated 5.35 hotfix
UpdateInventorySlot = 0x036A, // updated 5.35 hotfix
InventoryActionAck = 0x03B8, // updated 5.58 hotfix
UpdateInventorySlot = 0x02F7, // updated 5.58 hotfix
HuntingLogEntry = 0x0146, // updated 5.35 hotfix
HuntingLogEntry = 0x01D9, // updated 5.58 hotfix
EventPlay = 0x00F3, // updated 5.35 hotfix
EventPlay4 = 0x00AC, // updated 5.35 hotfix
EventPlay8 = 0x023F, // updated 5.35 hotfix
EventPlay16 = 0x025B, // updated 5.35 hotfix
EventPlay32 = 0x029A, // updated 5.35 hotfix
EventPlay64 = 0x02C1, // updated 5.35 hotfix
EventPlay128 = 0x038A, // updated 5.35 hotfix
EventPlay255 = 0x034B, // updated 5.35 hotfix
EventPlay = 0x016B, // updated 5.58 hotfix
EventPlay4 = 0x010A, // updated 5.58 hotfix
EventPlay8 = 0x0337, // updated 5.58 hotfix
EventPlay16 = 0x0269, // updated 5.58 hotfix
EventPlay32 = 0x023E, // updated 5.58 hotfix
EventPlay64 = 0x00DE, // updated 5.58 hotfix
EventPlay128 = 0x02D0, // updated 5.58 hotfix
EventPlay255 = 0x0362, // updated 5.58 hotfix
EventStart = 0x009A, // updated 5.35 hotfix
EventFinish = 0x007E, // updated 5.35 hotfix
EventContinue = 0x00B6, // updated 5.58 hotfix
EventStart = 0x02DA, // updated 5.58 hotfix
EventFinish = 0x0235, // updated 5.58 hotfix
EventLinkshell = 0x1169,
QuestActiveList = 0x0117, // updated 5.35 hotfix
QuestUpdate = 0x0073, // updated 5.35 hotfix
QuestCompleteList = 0x0240, // updated 5.35 hotfix
QuestActiveList = 0x0097, // updated 5.58 hotfix
QuestUpdate = 0x01B2, // updated 5.58 hotfix
QuestCompleteList = 0x006D, // updated 5.58 hotfix
QuestFinish = 0x00E9, // updated 5.35 hotfix
MSQTrackerComplete = 0xF1D6, // updated 5.0
QuestFinish = 0x021B, // updated 5.58 hotfix
MSQTrackerComplete = 0x0348, // updated 5.58 hotfix
MSQTrackerProgress = 0xF1CD, // updated 4.5 ? this actually looks like the two opcodes have been combined, see #474
QuestMessage = 0x0381, // updated 5.35 hotfix
QuestMessage = 0x0220, // updated 5.58 hotfix
QuestTracker = 0x018B, // updated 5.35 hotfix
QuestTracker = 0x00D8, // updated 5.58 hotfix
Mount = 0x01B5, // updated 5.35 hotfix
Mount = 0x01E1, // updated 5.58 hotfix
DirectorVars = 0x011D, // updated 5.35 hotfix
DirectorVars = 0x0154, // updated 5.58 hotfix
SomeDirectorUnk1 = 0x0084, // updated 5.18
SomeDirectorUnk2 = 0xF0C1, // updated 5.18
SomeDirectorUnk4 = 0x0202, // updated 5.35 hotfix
SomeDirectorUnk4 = 0x03DD, // updated 5.58 hotfix
SomeDirectorUnk8 = 0x028A, // updated 5.18
SomeDirectorUnk16 = 0x028C, // updated 5.18
DirectorPopUp = 0xF162, // updated 5.18 - display dialogue pop-ups in duties and FATEs, for example, Teraflare's countdown
DirectorPopUp4 = 0x0214, // updated 5.18
DirectorPopUp8 = 0x00F8, // updated 5.18
DirectorPopUp = 0x03DF, // updated 5.58 hotfix
DirectorPopUp4 = 0x019B, // updated 5.58 hotfix
DirectorPopUp8 = 0x0271, // updated 5.58 hotfix
CFAvailableContents = 0xF1FD, // updated 4.2
WeatherChange = 0x027B, // updated 5.35 hotfix
PlayerTitleList = 0x0251, // updated 5.35 hotfix
Discovery = 0x031B, // updated 5.35 hotfix
WeatherChange = 0x0323, // updated 5.58 hotfix
PlayerTitleList = 0x014E, // updated 5.58 hotfix
Discovery = 0x01C2, // updated 5.58 hotfix
EorzeaTimeOffset = 0x01D4, // updated 5.35 hotfix
EorzeaTimeOffset = 0x0070, // updated 5.58 hotfix
EquipDisplayFlags = 0x00BE, // updated 5.35 hotfix
EquipDisplayFlags = 0x02C6, // updated 5.58 hotfix
MiniCactpotInit = 0x0286, // added 5.31
ShopMessage = 0x0197, // updated 5.35 hotfix
LootMessage = 0x01B7, // updated 5.35 hotfix
ShopMessage = 0x0287, // updated 5.58 hotfix
LootMessage = 0x0383, // updated 5.58 hotfix
ResultDialog = 0x0273, // updated 5.58 hotfix
DesynthResult = 0x0238, // updated 5.58 hotfix
/// Housing //////////////////////////////////////
LandSetInitialize = 0x0095, // updated 5.35 hotfix
LandUpdate = 0x00BF, // updated 5.35 hotfix
YardObjectSpawn = 0x01CA, // updated 5.35 hotfix
HousingIndoorInitialize = 0x01FF, // updated 5.35 hotfix
LandPriceUpdate = 0x0380, // updated 5.35 hotfix
LandInfoSign = 0x023D, // updated 5.35 hotfix
LandRename = 0x0140, // updated 5.35 hotfix
HousingEstateGreeting = 0x00C7, // updated 5.35 hotfix
HousingUpdateLandFlagsSlot = 0x027E, // updated 5.35 hotfix
HousingLandFlags = 0x022F, // updated 5.35 hotfix
HousingShowEstateGuestAccess = 0x03B5, // updated 5.35 hotfix
LandSetInitialize = 0x0159, // updated 5.58 hotfix
LandUpdate = 0x0228, // updated 5.58 hotfix
YardObjectSpawn = 0x023D, // updated 5.58 hotfix
HousingIndoorInitialize = 0x0210, // updated 5.58 hotfix
LandPriceUpdate = 0x0300, // updated 5.58 hotfix
LandInfoSign = 0x03E7, // updated 5.58 hotfix
LandRename = 0x01BF, // updated 5.58 hotfix
HousingEstateGreeting = 0x0126, // updated 5.58 hotfix
HousingUpdateLandFlagsSlot = 0x0157, // updated 5.58 hotfix
HousingLandFlags = 0x03B1, // updated 5.58 hotfix
HousingShowEstateGuestAccess = 0x00CC, // updated 5.58 hotfix
HousingObjectInitialize = 0x01AA, // updated 5.35 hotfix
HousingInternalObjectSpawn = 0x0234, // updated 5.35 hotfix
HousingObjectInitialize = 0x0112, // updated 5.58 hotfix
HousingInternalObjectSpawn = 0x02C8, // updated 5.58 hotfix
HousingWardInfo = 0x02FD, // updated 5.35 hotfix
HousingObjectMove = 0x022C, // updated 5.35 hotfix
HousingWardInfo = 0x012A, // updated 5.58 hotfix
HousingObjectMove = 0x0265, // updated 5.58 hotfix
SharedEstateSettingsResponse = 0x006A, // updated 5.35 hotfix
SharedEstateSettingsResponse = 0x030E, // updated 5.58 hotfix
LandUpdateHouseName = 0x00B1, // updated 5.35 hotfix
LandUpdateHouseName = 0x017C, // updated 5.58 hotfix
LandSetMap = 0x0149, // updated 5.35 hotfix
LandSetMap = 0x02E5, // updated 5.58 hotfix
CeremonySetActorAppearance = 0x02ED, // updated 5.58 hotfix
//////////////////////////////////////////////////
DuelChallenge = 0x0277, // 4.2; this is responsible for opening the ui
PerformNote = 0x0286, // updated 4.3
PerformNote = 0x0127, // updated 5.58 hotfix
PrepareZoning = 0x026C, // updated 5.35 hotfix
ActorGauge = 0x0112, // updated 5.35 hotfix
PrepareZoning = 0x02AB, // updated 5.58 hotfix
ActorGauge = 0x01C1, // updated 5.58 hotfix
DutyGauge = 0x02E5, // updated 5.58 hotfix
// daily quest info -> without them sent, login will take longer...
DailyQuests = 0x0139, // updated 5.35 hotfix
DailyQuestRepeatFlags = 0x024C, // updated 5.35 hotfix
DailyQuests = 0x02D6, // updated 5.58 hotfix
DailyQuestRepeatFlags = 0x01AB, // updated 5.58 hotfix
MapUpdate = 0x0394, // updated 5.58 hotfix
MapUpdate4 = 0x036F, // updated 5.58 hotfix
MapUpdate8 = 0x0311, // updated 5.58 hotfix
MapUpdate16 = 0x0108, // updated 5.58 hotfix
MapUpdate32 = 0x007A, // updated 5.58 hotfix
MapUpdate64 = 0x02A0, // updated 5.58 hotfix
MapUpdate128 = 0x0303, // updated 5.58 hotfix
/// Doman Mahjong //////////////////////////////////////
MahjongOpenGui = 0x02A4, // only available in mahjong instance
@ -282,10 +297,20 @@ namespace Sapphire::Network::Packets
MahjongEndRoundTsumo = 0x02BF, // called tsumo
MahjongEndRoundRon = 0x2C0, // called ron or double ron (waiting for action must be flagged from discard packet to call)
MahjongTileDiscard = 0x02C1, // giri (discarding a tile.) chi(1)/pon(2)/kan(4)/ron(8) flags etc..
MahjongPlayersInfo = 0x02C2, // actor id, name, rating and stuff..
MahjongPlayersInfo = 0xF2C2, // actor id, name, rating and stuff..
// 2C3 and 2C4 are currently unknown
MahjongEndRoundDraw = 0x02C5, // self explanatory
MahjongEndGame = 0x02C6, // finished oorasu(all-last) round; shows a result screen.
/// Airship & Submarine //////////////////////////////////////
AirshipExplorationResult = 0x0203, // updated 5.58 hotfix
AirshipStatus = 0x030C, // updated 5.58 hotfix
AirshipStatusList = 0x02FE, // updated 5.58 hotfix
AirshipTimers = 0x0166, // updated 5.58 hotfix
SubmarineExplorationResult = 0x00AA, // updated 5.58 hotfix
SubmarineProgressionStatus = 0x0357, // updated 5.58 hotfix
SubmarineStatusList = 0x01EF, // updated 5.58 hotfix
SubmarineTimers = 0x0247, // updated 5.58 hotfix
};
/**
@ -293,109 +318,111 @@ namespace Sapphire::Network::Packets
*/
enum ClientZoneIpcType : uint16_t
{
PingHandler = 0x0219, // updated 5.35 hotfix
InitHandler = 0x0185, // updated 5.35 hotfix
PingHandler = 0x0288, // updated 5.58 hotfix
InitHandler = 0x02EB, // updated 5.58 hotfix
FinishLoadingHandler = 0x01BE, // updated 5.35 hotfix
FinishLoadingHandler = 0x013C, // updated 5.58 hotfix
CFCommenceHandler = 0x0118, // updated 5.35 hotfix
CFCommenceHandler = 0x0381, // updated 5.58 hotfix
CFCancelHandler = 0x0332, // updated 5.35 hotfix
CFRegisterDuty = 0x0289, // updated 5.35 hotfix
CFRegisterRoulette = 0x0088, // updated 5.35 hotfix
PlayTimeHandler = 0x02A8, // updated 5.35 hotfix
LogoutHandler = 0x00EC, // updated 5.35 hotfix
CancelLogout = 0x03DB, // updated 5.35 hotfix
CFCancelHandler = 0x02B2, // updated 5.58 hotfix
CFRegisterDuty = 0x01BD, // updated 5.58 hotfix
CFRegisterRoulette = 0x037A, // updated 5.58 hotfix
PlayTimeHandler = 0x02B7, // updated 5.58 hotfix
LogoutHandler = 0x00A0, // updated 5.58 hotfix
CancelLogout = 0x01AC, // updated 5.58 hotfix
CFDutyInfoHandler = 0xF078, // updated 4.2
CFDutyInfoHandler = 0x0078, // updated 4.2
SocialReqSendHandler = 0x00D7, // updated 5.58 hotfix
SocialResponseHandler = 0x023B, // updated 5.58 hotfix
CreateCrossWorldLS = 0x035D, // updated 5.58 hotfix
SocialReqSendHandler = 0x0387, // updated 5.35 hotfix
SocialResponseHandler = 0x028D, // updated 5.35 hotfix
CreateCrossWorldLS = 0x00AF, // updated 4.3
ChatHandler = 0x0131, // updated 5.35 hotfix
ChatHandler = 0x03B0, // updated 5.58 hotfix
PartyChatHandler = 0x0065,
PartySetLeaderHandler = 0x0208, // updated 5.35 hotfix
LeavePartyHandler = 0x0337, // updated 5.35 hotfix
KickPartyMemberHandler = 0x014C, // updated 5.35 hotfix
DisbandPartyHandler = 0x0205, // updated 5.35 hotfix
PartySetLeaderHandler = 0x036C, // updated 5.58 hotfix
LeavePartyHandler = 0x019D, // updated 5.58 hotfix
KickPartyMemberHandler = 0x0262, // updated 5.58 hotfix
DisbandPartyHandler = 0x0276, // updated 5.58 hotfix
SocialListHandler = 0x0340, // updated 5.35 hotfix
SetSearchInfoHandler = 0x0314, // updated 5.35 hotfix
ReqSearchInfoHandler = 0x01E9, // updated 5.35 hotfix
SocialListHandler = 0x01CA, // updated 5.58 hotfix
SetSearchInfoHandler = 0x01D4, // updated 5.58 hotfix
ReqSearchInfoHandler = 0x014F, // updated 5.58 hotfix
ReqExamineSearchCommentHandler = 0x00E7, // updated 5.0
ReqRemovePlayerFromBlacklist = 0x00F1, // updated 5.0
BlackListHandler = 0x0079, // updated 5.35 hotfix
PlayerSearchHandler = 0x00F4, // updated 5.0
ReqRemovePlayerFromBlacklist = 0x00B4, // updated 5.58 hotfix
BlackListHandler = 0x00F2, // updated 5.58 hotfix
PlayerSearchHandler = 0x037D, // updated 5.58 hotfix
LinkshellListHandler = 0x024B, // updated 5.35 hotfix
LinkshellListHandler = 0x03B6, // updated 5.58 hotfix
MarketBoardRequestItemListingInfo = 0x0102, // updated 4.5
MarketBoardRequestItemListings = 0x0103, // updated 4.5
MarketBoardSearch = 0x0107, // updated 4.5
MarketBoardRequestItemListingInfo = 0x00F4, // updated 5.58 hotfix
MarketBoardRequestItemListings = 0x0122, // updated 5.58 hotfix
MarketBoardSearch = 0x0082, // updated 5.58 hotfix
ReqExamineFcInfo = 0x0113, // updated 4.1
ReqExamineFcInfo = 0x037B, // updated 5.58 hotfix
FcInfoReqHandler = 0x011A, // updated 4.2
FcInfoReqHandler = 0x03D4, // updated 5.58 hotfix
FreeCompanyUpdateShortMessageHandler = 0x0123, // added 5.0
ReqMarketWishList = 0x012C, // updated 4.3
ReqMarketWishList = 0x00C3, // updated 5.58 hotfix
ReqJoinNoviceNetwork = 0x0129, // updated 4.2
ReqCountdownInitiate = 0x025F, // updated 5.35 hotfix
ReqCountdownCancel = 0x0244, // updated 5.25
ReqCountdownInitiate = 0x02EC, // updated 5.58 hotfix
ReqCountdownCancel = 0x0068, // updated 5.58 hotfix
ZoneLineHandler = 0x0279, // updated 5.35 hotfix
ClientTrigger = 0x03D3, // updated 5.35 hotfix
DiscoveryHandler = 0x00E3, // updated 5.35 hotfix
ZoneLineHandler = 0x008D, // updated 5.58 hotfix
ClientTrigger = 0x03DB, // updated 5.58 hotfix
DiscoveryHandler = 0x038B, // updated 5.58 hotfix
PlaceFieldMarkerPreset = 0x023F, // updated 5.25
PlaceFieldMarker = 0x01BA, // updated 5.25
SkillHandler = 0x01CD, // updated 5.35 hotfix
GMCommand1 = 0x02AC, // updated 5.35 hotfix
GMCommand2 = 0x029F, // updated 5.35 hotfix
AoESkillHandler = 0x030C, // updated 5.35 hotfix
PlaceFieldMarkerPreset = 0x026D, // updated 5.58 hotfix
PlaceFieldMarker = 0x0371, // updated 5.58 hotfix
SkillHandler = 0x02DC, // updated 5.58 hotfix
GMCommand1 = 0x0272, // updated 5.58 hotfix
GMCommand2 = 0x00E9, // updated 5.58 hotfix
AoESkillHandler = 0x0152, // updated 5.58 hotfix
UpdatePositionHandler = 0x0236, // updated 5.35 hotfix
UpdatePositionHandler = 0x01AF, // updated 5.58 hotfix
InventoryModifyHandler = 0x0135, // updated 5.35 hotfix
InventoryEquipRecommendedItems = 0x0116, // updated 5.35 hotfix
InventoryModifyHandler = 0x029E, // updated 5.58 hotfix
ReqPlaceHousingItem = 0x02AE, // updated 5.35 hotfix
BuildPresetHandler = 0x01C2, // updated 5.35 hotfix
InventoryEquipRecommendedItems = 0x01C9, // updated 5.58 hotfix
TalkEventHandler = 0x02A4, // updated 5.35 hotfix
EmoteEventHandler = 0x02C8, // updated 5.35 hotfix
WithinRangeEventHandler = 0x0209, // updated 5.35 hotfix
OutOfRangeEventHandler = 0x0319, // updated 5.35 hotfix
EnterTeriEventHandler = 0x0192, // updated 5.35 hotfix
ShopEventHandler = 0x01F6, // updated 5.35 hotfix
ReqPlaceHousingItem = 0x02D4, // updated 5.58 hotfix
BuildPresetHandler = 0x0223, // updated 5.58 hotfix
ReturnEventHandler = 0x02B4, // updated 5.35 hotfix
TradeReturnEventHandler = 0x00A4, // updated 5.35 hotfix
TradeMultipleReturnEventHander = 0x035C, // updated 5.35 hotfix
TalkEventHandler = 0x0387, // updated 5.58 hotfix
EmoteEventHandler = 0x00B0, // updated 5.58 hotfix
WithinRangeEventHandler = 0x02B6, // updated 5.58 hotfix
OutOfRangeEventHandler = 0x03C5, // updated 5.58 hotfix
EnterTeriEventHandler = 0x01A7, // updated 5.58 hotfix
ShopEventHandler = 0x0384, // updated 5.58 hotfix
ReturnEventHandler = 0x00FA, // updated 5.58 hotfix
TradeReturnEventHandler = 0x0339, // updated 5.58 hotfix
TradeReturnEventHandler2 = 0x023C, // updated 5.58 hotfix
EventYield2Handler = 0x021D, // updated 5.58 hotfix
EventYield16Handler = 0x0207, // updated 5.58 hotfix
LinkshellEventHandler = 0x016B, // updated 4.5
LinkshellEventHandler1 = 0x016C, // updated 4.5
ReqEquipDisplayFlagsChange = 0x02F6, // updated 5.35 hotfix
ReqEquipDisplayFlagsChange = 0x02A5, // updated 5.58 hotfix
LandRenameHandler = 0x0155, // updated 5.35 hotfix
HousingUpdateHouseGreeting = 0x02EA, // updated 5.35 hotfix
HousingUpdateObjectPosition = 0x00D5, // updated 5.35 hotfix
LandRenameHandler = 0x028E, // updated 5.58 hotfix
HousingUpdateHouseGreeting = 0x0343, // updated 5.58 hotfix
HousingUpdateObjectPosition = 0x012C, // updated 5.58 hotfix
HousingEditExterior = 0x027B, // updated 5.58 hotfix
HousingEditInterior = 0x02E3, // updated 5.58 hotfix
SetSharedEstateSettings = 0x017B, // updated 5.0
SetSharedEstateSettings = 0x00D2, // updated 5.58 hotfix
UpdatePositionInstance = 0x0345, // updated 5.35 hotfix
UpdatePositionInstance = 0x00F8, // updated 5.58 hotfix
PerformNoteHandler = 0x029B, // updated 4.3
PerformNoteHandler = 0x0243, // updated 5.58 hotfix
WorldInteractionHandler = 0x00A9, // updated 5.35 hotfix
Dive = 0x02CC, // updated 5.35 hotfix
WorldInteractionHandler = 0x0274, // updated 5.58 hotfix
Dive = 0x0320, // updated 5.58 hotfix
};
////////////////////////////////////////////////////////////////////////////////
@ -423,5 +450,5 @@ namespace Sapphire::Network::Packets
}
#endif /*_CORE_NETWORK_PACKETS_IPCS_H*/

View file

@ -426,6 +426,51 @@ struct FFXIVIpcDive :
uint32_t padding;
};
struct FFXIVIpcHousingEditExterior :
FFXIVIpcBasePacket< HousingEditExterior >
{
uint16_t landId;
uint8_t unknown[6];
uint8_t removeFlag;
uint8_t unknown2;
uint16_t container[9];
uint16_t slot[9];
uint16_t padding;
};
struct FFXIVIpcHousingEditInterior :
FFXIVIpcBasePacket< HousingEditInterior >
{
uint64_t unknown;
uint16_t container[10];
uint16_t slot[10];
};
struct FFXIVIpcEventYieldHandler :
FFXIVIpcBasePacket< EventYield2Handler >
{
uint32_t eventId;
uint16_t scene;
uint16_t padding;
uint64_t unknown;
};
struct FFXIVIpcEventYield16Handler :
FFXIVIpcBasePacket< EventYield16Handler >
{
uint32_t eventId;
uint16_t scene;
uint16_t padding;
uint32_t params[16];
};
struct FFXIVIpcCFCommenceHandler :
FFXIVIpcBasePacket< CFCommenceHandler >
{
uint8_t param;
uint8_t dummy[7];
};
}
#endif //_CORE_NETWORK_PACKETS_ZONE_CLIENT_IPC_H

View file

@ -680,6 +680,7 @@ namespace Sapphire::Network::Packets::Server
uint16_t unk; // == 0
uint16_t modelChara;
uint16_t rotation;
uint16_t currentMount;
uint16_t activeMinion;
uint8_t spawnIndex;
uint8_t state;
@ -693,24 +694,20 @@ namespace Sapphire::Network::Packets::Server
uint8_t classJob;
uint8_t u26d;
uint16_t u27a;
uint8_t currentMount;
uint8_t mountHead;
uint8_t mountBody;
uint8_t mountFeet;
uint8_t mountColor;
uint8_t scale;
//uint32_t elementalLevel; one of these two field changed to 16bit
//uint32_t element;
uint8_t elementData[6];
uint8_t unknown5_5[3];
Common::StatusEffect effect[30];
Common::FFXIVARR_POSITION3 pos;
uint32_t models[10];
char name[32];
uint8_t look[26];
char fcTag[6];
uint32_t unk30;
uint32_t unk30[2];
};
/**
@ -753,9 +750,10 @@ namespace Sapphire::Network::Packets::Server
uint32_t displayFlags;
uint16_t fateID;
uint16_t mPCurr;
uint16_t unknown1; // 0
uint16_t unknown2; // 0 or pretty big numbers > 30000
uint16_t unknown1;
uint16_t unknown2;
uint16_t modelChara;
uint16_t currentMount;
uint16_t rotation;
uint16_t activeMinion;
uint8_t spawnIndex;
@ -770,14 +768,13 @@ namespace Sapphire::Network::Packets::Server
uint8_t classJob;
uint8_t u26d;
uint16_t u27a;
uint8_t currentMount;
uint8_t mountHead;
uint8_t mountBody;
uint8_t mountFeet;
uint8_t mountColor;
uint8_t scale;
uint16_t elementalLevel; // Eureka
uint16_t element; // Eureka
uint8_t elemental[6];
uint8_t unknown5_5[3];
Common::StatusEffect effect[30];
Common::FFXIVARR_POSITION3 pos;
uint32_t models[10];
@ -789,7 +786,7 @@ namespace Sapphire::Network::Packets::Server
uint8_t bNPCPartSlot;
uint8_t unk32;
uint16_t unk33;
uint32_t unk34;
uint32_t unk34[2];
};
/**
@ -930,13 +927,13 @@ namespace Sapphire::Network::Packets::Server
//Current instance can be confirmed at any time using the /instance text command." ( 7B F8 69 )
uint8_t unknown5;
uint32_t unknown7;
uint32_t unknown8;
uint16_t festivalId;
uint16_t additionalFestivalId;
uint32_t unknown9;
uint32_t unknown10;
uint32_t unknown11;
uint32_t unknown12[4];
uint32_t festivalId;
uint32_t unknown12[3];
uint32_t additionalFestivalId;
uint32_t unknown13[3];
Common::FFXIVARR_POSITION3 pos;
uint32_t unknown14[3];
@ -1041,13 +1038,12 @@ namespace Sapphire::Network::Packets::Server
unsigned char mountGuideMask[22];
unsigned char u19_2;
*/
unsigned char unknown5_3a[176];
unsigned char unknown5_55a[178];
unsigned char companionName[21];
unsigned char companionDefRank;
unsigned char companionAttRank;
unsigned char companionHealRank;
unsigned char mountGuideMask[23];
unsigned char maybeReservedMountSlots;
unsigned char mountGuideMask[29];
//==
char name[32];
unsigned char unknownOword[16];
@ -1056,10 +1052,10 @@ namespace Sapphire::Network::Packets::Server
unsigned char aetheryte[21];
unsigned char discovery[445];
unsigned char howto[34];
unsigned char minions[51];
unsigned char minions[55];
unsigned char chocoboTaxiMask[10];
unsigned char watchedCutscenes[131];
unsigned char companionBardingMask[10];
unsigned char watchedCutscenes[137];
unsigned char companionBardingMask[11];
unsigned char companionEquippedHead;
unsigned char companionEquippedBody;
unsigned char companionEquippedLegs;
@ -1074,7 +1070,7 @@ namespace Sapphire::Network::Packets::Server
unsigned char unknownPvp5AB[11];
unsigned char unknown5B9[5];
*/
unsigned char unknown5_3c[234];
unsigned char unknown5_45b[236];
//==
unsigned char pose;
/*
@ -1092,28 +1088,32 @@ namespace Sapphire::Network::Packets::Server
unsigned char aetherCurrentMask[22];
unsigned char u10[3];
*/
unsigned char unknown5_3d[292];
unsigned char unknown5_55b[295];
//==
unsigned char orchestrionMask[40];
unsigned char orchestrionMask[40]; // this field may already be extended, if it is, the beginning bytes are at the end of unknown5_55b
unsigned char hallOfNoviceCompletion[3];
unsigned char animaCompletion[11];
unsigned char unknown5_3e[33];
unsigned char unknown5_55c[35];
unsigned char unlockedRaids[28];
unsigned char unlockedDungeons[18];
unsigned char unlockedGuildhests[10];
unsigned char unlockedTrials[9]; // 5.35 trial:pvp either 9:5 or 8:6 not confirmed
unsigned char unlockedPvp[5];
/*
at least 8 bytes at most 10 bytes in unlockedTrials not confirmed, adjust unlockedPvp so they share a total of 15 bytes and sync with clearedTrials/clearedPvp.
*/
unsigned char unlockedTrials[9];
unsigned char unlockedPvp[6];
//==
unsigned char clearedRaids[28];
unsigned char clearedDungeons[18];
unsigned char clearedGuildhests[10];
unsigned char clearedTrials[9];
unsigned char clearedPvp[5];
unsigned char clearedPvp[6];
/*
unsigned short fishingRecordsFishWeight[26];
unsigned int exploratoryMissionNextTimestamp;
unsigned char pvpLevel;
*/
unsigned char padding2[8];
unsigned char unknown5_55d[9];
//==
};
@ -1439,6 +1439,20 @@ namespace Sapphire::Network::Packets::Server
uint8_t unknown[8];
};
struct FFXIVIpcEventPlay16 : FFXIVIpcBasePacket< EventPlay16 >
{
uint64_t actorId;
uint32_t eventId;
uint16_t scene;
uint16_t padding;
uint32_t flags;
uint32_t param3;
uint8_t paramSize;
uint8_t padding1[3];
uint32_t param[16];
uint32_t padding2;
};
template< int ArgCount >
struct FFXIVIpcEventPlayN
{
@ -1835,7 +1849,7 @@ namespace Sapphire::Network::Packets::Server
uint32_t bNPCName;
uint32_t textId;
uint32_t popupTimeMs;
uint32_t pad3[4];
uint32_t param[6];
};
@ -1847,7 +1861,7 @@ namespace Sapphire::Network::Packets::Server
struct FFXIVIpcPerformNote : FFXIVIpcBasePacket< PerformNote >
{
uint8_t data[32];
uint8_t data[16];
};
struct FFXIVIpcHousingUpdateLandFlagsSlot : FFXIVIpcBasePacket< HousingUpdateLandFlagsSlot >
@ -2238,6 +2252,97 @@ namespace Sapphire::Network::Packets::Server
char memberName[32];
uint8_t padding[3];
};
struct FFXIVIpcEventContinue : FFXIVIpcBasePacket< EventContinue >
{
uint32_t eventId;
uint16_t scene;
uint16_t unknown;
uint64_t unknown2;
};
struct FFXIVDirectorUnk4 : FFXIVIpcBasePacket< SomeDirectorUnk4 >
{
uint32_t param[4];
uint64_t unknown;
};
struct FFXIVCeremonySetActorAppearance : FFXIVIpcBasePacket< CeremonySetActorAppearance >
{
uint8_t u1;
uint8_t questBL;
uint16_t padding1;
uint32_t u3;
struct
{
uint64_t mainWeaponModel;
uint64_t secWeaponModel;
uint64_t craftToolModel;
uint32_t c_u6;
uint32_t c_u7;
uint32_t charId;
uint16_t u4;
uint16_t guardianDeity;
uint32_t u5;
uint32_t models[10];
uint8_t look[26];
uint16_t padding3;
} actors[2];
};
//For quests this is only used for pre-accepted ones. Accepted quests are getting handled by the client.
template< int ArgCount >
struct FFXIVIpcMapUpdateN
{
uint8_t entryCount;
uint8_t padding[ 3 ];
uint32_t iconIds[ ArgCount ];
uint32_t levelIds[ ArgCount ];
uint32_t eventIds[ ArgCount ]; // possible event ids for this: Quest, GuildLeveAssignment, GuildOrderGuide, TripleTriad, CustomTalk, PreHandler
uint8_t additionalData[ ArgCount ]; // use unknown
};
struct FFXIVIpcMapUpdate :
FFXIVIpcBasePacket< MapUpdate >,
FFXIVIpcMapUpdateN< 2 >
{
};
struct FFXIVIpcMapUpdate4 :
FFXIVIpcBasePacket< MapUpdate4 >,
FFXIVIpcMapUpdateN< 4 >
{
};
struct FFXIVIpcMapUpdate8 :
FFXIVIpcBasePacket< MapUpdate8 >,
FFXIVIpcMapUpdateN< 8 >
{
};
struct FFXIVIpcMapUpdate16 :
FFXIVIpcBasePacket< MapUpdate16 >,
FFXIVIpcMapUpdateN< 16 >
{
};
struct FFXIVIpcMapUpdate32 :
FFXIVIpcBasePacket< MapUpdate32 >,
FFXIVIpcMapUpdateN< 32 >
{
};
struct FFXIVIpcMapUpdate64 :
FFXIVIpcBasePacket< MapUpdate64 >,
FFXIVIpcMapUpdateN< 64 >
{
};
struct FFXIVIpcMapUpdate128 :
FFXIVIpcBasePacket< MapUpdate128 >,
FFXIVIpcMapUpdateN< 128 >
{
};
}
#endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/

View file

@ -129,7 +129,7 @@ uint32_t Util::getTimeSeconds()
uint64_t Util::getEorzeanTimeStamp()
{
return static_cast< uint64_t >( getTimeSeconds() * 20.571428571428573f );
return static_cast< uint64_t >( getTimeSeconds() * 20.571428571428573 );
}
void Util::valueToFlagByteIndexValue( uint32_t inVal, uint8_t& outVal, uint16_t& outIndex )

View file

@ -76,4 +76,9 @@ uint16_t Util::floatToUInt16Rot( float val )
uint8_t Util::floatToUInt8Rot( float val )
{
return static_cast< uint8_t >( 0x80 * ( ( val + PI ) ) / PI );
}
float Util::floatFromUInt16Rot( uint16_t rot )
{
return rot / 32768.0f * PI - PI;
}

View file

@ -25,6 +25,8 @@ namespace Sapphire::Common::Util
uint16_t floatToUInt16Rot( float val );
float floatFromUInt16Rot( uint16_t rot );
uint8_t floatToUInt8Rot( float val );
template < typename T >

View file

@ -452,8 +452,8 @@ void Lobby::GameConnection::generateEncryptionKey( uint32_t key, const std::stri
m_baseKey[ 2 ] = 0x34;
m_baseKey[ 3 ] = 0x12;
memcpy( m_baseKey + 0x04, &key, 4 );
m_baseKey[ 8 ] = 0x88;
m_baseKey[ 9 ] = 0x13;
m_baseKey[ 8 ] = 0x18;
m_baseKey[ 9 ] = 0x15;
memcpy( ( char* ) m_baseKey + 0x0C, keyPhrase.c_str(), keyPhrase.size() );
Common::Util::md5( m_baseKey, m_encKey, 0x2C );
}

View file

@ -32,7 +32,7 @@ public:
// todo: this is fucked
};
player.playScene( getId(), 1, 0xFB2EC8F8, 0, 1, returnScene, callback );
player.playScene( getId(), 1, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, 0, 1, returnScene, callback );
}
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override

View file

@ -78,7 +78,7 @@ public:
return;
}
player.setInstance( internalZone, pos );
player.setInstance( internalZone, pos, player.getRot() );
} );
}
};

View file

@ -132,7 +132,7 @@ private:
{
if( result.param2 == 1 )
{
if( player.giveQuestRewards( getId(), 0 ) )
if( player.giveQuestRewards( getId(), result.param3 ) )
{
player.setQuestUI8BH( getId(), 0 );
player.finishQuest( getId() );

View file

@ -162,12 +162,12 @@ std::string zoneNameToPath( const std::string& name )
auto teriPath = std::get< std::string >(
fields.at( static_cast< size_t >( TerritoryTypeExdIndexes::Path ) ) );
ZoneInfo info;
info.id = row.first;
info.id = row.first.rowId;
info.path = teriPath;
info.name = teriName;
info.mapId = std::get< uint16_t >(
fields.at( static_cast< size_t >( TerritoryTypeExdIndexes::Map ) ) );
zoneInfoMap[ row.first ] = info;
zoneInfoMap[ row.first.rowId ] = info;
if( !found && ( Common::Util::toLowerCopy( name ) == Common::Util::toLowerCopy( teriName ) ) )
{
@ -212,7 +212,7 @@ void loadEobjNames()
static auto exd = static_cast< xiv::exd::Exd >( cat.get_data_ln( xiv::exd::Language::en ) );
for( auto& row : exd.get_rows() )
{
auto id = row.first;
auto id = row.first.rowId;
auto& fields = row.second;
auto name = std::get< std::string >( fields.at( 0 ) );
eobjNameMap[ id ] = name;

View file

@ -42,7 +42,7 @@ std::string generateEnum( const std::string& exd, int8_t nameIndex, const std::s
for( auto row : rows )
{
auto& fields = row.second;
uint32_t id = row.first;
uint32_t id = row.first.rowId;
std::string value;
try

View file

@ -23,7 +23,7 @@ void Sapphire::Data::ExdDataGenerated::loadIdList( xiv::exd::Exd& data, std::set
for( auto row : pDataRows )
{
uint32_t id = row.first;
uint32_t id = row.first.rowId;
outIdList.insert( id );
}
}

View file

@ -89,7 +89,7 @@ std::string getEobjSgbPath( uint32_t eobjId )
for( auto& row : exportedSgExd.get_rows() )
{
auto id = row.first;
auto id = row.first.rowId;
auto& fields = row.second;
auto path = std::get< std::string >( fields.at( 0 ) );
@ -100,7 +100,7 @@ std::string getEobjSgbPath( uint32_t eobjId )
for( auto& row : eObjExd.get_rows() )
{
auto id = row.first;
auto id = row.first.rowId;
auto& fields = row.second;
eobjSgbPaths[id] = std::get< uint16_t >( fields.at( 11 ) );
@ -127,9 +127,9 @@ std::string zoneNameToPath( const std::string& name )
{
path = teriPath;
found = true;
zoneId = row.first;
zoneId = row.first.rowId;
}
zoneNameMap[ row.first ] = teriName;
zoneNameMap[ row.first.rowId ] = teriName;
}
if( found )

View file

@ -91,7 +91,7 @@ std::string getEobjSgbPath( uint32_t eobjId )
for( auto& row : exportedSgExd.get_rows() )
{
auto id = row.first;
auto id = row.first.rowId;
auto& fields = row.second;
auto path = std::get< std::string >( fields.at( 0 ) );
@ -102,7 +102,7 @@ std::string getEobjSgbPath( uint32_t eobjId )
for( auto& row : eObjExd.get_rows() )
{
auto id = row.first;
auto id = row.first.rowId;
auto& fields = row.second;
eobjSgbPaths[id] = std::get< uint16_t >( fields.at( 11 ) );
@ -129,9 +129,9 @@ std::string zoneNameToPath( const std::string& name )
{
path = teriPath;
found = true;
zoneId = row.first;
zoneId = row.first.rowId;
}
zoneNameMap[ row.first ] = teriName;
zoneNameMap[ row.first.rowId ] = teriName;
}
if( found )

View file

@ -351,7 +351,7 @@ Sapphire::InstanceContentPtr Sapphire::Entity::Actor::getCurrentInstance() const
return nullptr;
}
/*! \return QuestBattlePtr to the current instance, nullptr if not an instance or not set */
/*! \return QuestBattlePtr to the current instance, nullptr if not a quest battle or not set */
Sapphire::QuestBattlePtr Sapphire::Entity::Actor::getCurrentQuestBattle() const
{
if( m_pCurrentTerritory )
@ -360,6 +360,15 @@ Sapphire::QuestBattlePtr Sapphire::Entity::Actor::getCurrentQuestBattle() const
return nullptr;
}
/*! \return PublicContentPtr to the current instance, nullptr if not a public content or not set */
Sapphire::PublicContentPtr Sapphire::Entity::Actor::getCurrentPublicContent() const
{
if( m_pCurrentTerritory )
return m_pCurrentTerritory->getAsPublicContent();
return nullptr;
}
/*!
Get the current cell of a region the actor is in

View file

@ -130,6 +130,8 @@ namespace Sapphire::Entity
QuestBattlePtr getCurrentQuestBattle() const;
PublicContentPtr getCurrentPublicContent() const;
// get the current cell of a region the actor is in
Cell* getCellPtr();

View file

@ -13,7 +13,7 @@ namespace Sapphire::Entity
Common::FFXIVARR_POSITION3 pos, float rotation, const std::string& givenName = "none" );
using OnTalkEventHandler = std::function< void( Entity::Player&, Entity::EventObjectPtr,
TerritoryPtr, uint64_t ) >;
TerritoryPtr, uint32_t, uint64_t ) >;
uint32_t getGimmickId() const;

View file

@ -19,10 +19,13 @@
#include "Manager/HousingMgr.h"
#include "Manager/TerritoryMgr.h"
#include "Manager/RNGMgr.h"
#include "Manager/MapMgr.h"
#include "Territory/Territory.h"
#include "Territory/ZonePosition.h"
#include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Territory/InstanceObjectCache.h"
#include "Territory/Land.h"
@ -81,7 +84,8 @@ Sapphire::Entity::Player::Player() :
m_directorInitialized( false ),
m_onEnterEventDone( false ),
m_falling( false ),
m_pQueuedAction( nullptr )
m_pQueuedAction( nullptr ),
m_cfNotifiedContent( 0 )
{
m_id = 0;
m_currentStance = Stance::Passive;
@ -239,13 +243,16 @@ uint64_t Sapphire::Entity::Player::getOnlineStatusMask() const
return m_onlineStatus;
}
void Sapphire::Entity::Player::prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime, uint16_t animation )
void Sapphire::Entity::Player::prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime, uint16_t animation, uint8_t param4, uint8_t param7, uint8_t unknown )
{
auto preparePacket = makeZonePacket< FFXIVIpcPrepareZoning >( getId() );
preparePacket->data().targetZone = targetZone;
preparePacket->data().fadeOutTime = fadeOutTime;
preparePacket->data().animation = animation;
preparePacket->data().fadeOut = static_cast< uint8_t >( fadeOut ? 1 : 0 );
preparePacket->data().param4 = param4;
preparePacket->data().param7 = param7;
preparePacket->data().unknown = unknown;
queuePacket( preparePacket );
}
@ -468,7 +475,7 @@ bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance )
return teriMgr.movePlayer( instance, getAsPlayer() );
}
bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance, Common::FFXIVARR_POSITION3 pos )
bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance, Common::FFXIVARR_POSITION3 pos, float rot )
{
m_onEnterEventDone = false;
if( !instance )
@ -486,11 +493,17 @@ bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance, Common::FFXIV
m_prevTerritoryId = getTerritoryId();
}
m_pos = pos;
m_rot = rot;
if( teriMgr.movePlayer( instance, getAsPlayer() ) )
{
m_pos = pos;
return true;
}
else
{
m_pos = m_prevPos;
m_rot= m_prevRot;
}
return false;
}
@ -499,8 +512,18 @@ bool Sapphire::Entity::Player::exitInstance()
{
auto& teriMgr = Common::Service< TerritoryMgr >::ref();
auto pZone = getCurrentTerritory();
auto pInstance = pZone->getAsInstanceContent();
auto d = getCurrentTerritory()->getAsDirector();
if( d && d->getContentFinderConditionId() > 0 )
{
auto p = makeZonePacket< FFXIVDirectorUnk4 >( getId() );
p->data().param[0] = d->getDirectorId();
p->data().param[1] = 1534;
p->data().param[2] = 1;
p->data().param[3] = d->getContentFinderConditionId();
queuePacket( p );
prepareZoning( 0, 1, 1, 0, 0, 1, 9 );
}
resetHp();
resetMp();
@ -708,7 +731,7 @@ void Sapphire::Entity::Player::learnSong( uint8_t songId, uint32_t itemId )
queuePacket( makeActorControlSelf( getId(), ToggleOrchestrionUnlock, songId, 1, itemId ) );
}
bool Sapphire::Entity::Player::isActionLearned( uint8_t actionId ) const
bool Sapphire::Entity::Player::isActionLearned( uint32_t actionId ) const
{
uint16_t index;
uint8_t value;
@ -1264,6 +1287,17 @@ const uint8_t* Sapphire::Entity::Player::getMountGuideBitmask() const
return m_mountGuide;
}
const bool Sapphire::Entity::Player::hasMount( uint32_t mountId ) const
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto mount = exdData.get< Data::Mount >( mountId );
if( mount->order == -1 || mount->modelChara == 0 )
return false;
return m_mountGuide[ mount->order / 8 ] & ( 1 << ( mount->order % 8 ) );
}
uint64_t Sapphire::Entity::Player::getContentId() const
{
return m_contentId;
@ -1591,7 +1625,7 @@ uint16_t Sapphire::Entity::Player::getCurrentCompanion() const
return m_companionId;
}
uint8_t Sapphire::Entity::Player::getCurrentMount() const
uint16_t Sapphire::Entity::Player::getCurrentMount() const
{
return m_mount;
}
@ -1751,14 +1785,14 @@ void Sapphire::Entity::Player::sendZonePackets()
//setStateFlag( PlayerStateFlag::BetweenAreas );
//setStateFlag( PlayerStateFlag::BetweenAreas1 );
if( isActionLearned( static_cast< uint8_t >( Common::UnlockEntry::HuntingLog ) ) )
sendHuntingLog();
sendStats();
// only initialize the UI if the player in fact just logged in.
if( isLogin() )
{
if( isActionLearned( static_cast< uint8_t >( Common::UnlockEntry::HuntingLog ) ) )
sendHuntingLog();
auto contentFinderList = makeZonePacket< FFXIVIpcCFAvailableContents >( getId() );
for( auto i = 0; i < sizeof( contentFinderList->data().contents ); i++ )
@ -1808,6 +1842,12 @@ void Sapphire::Entity::Player::sendZonePackets()
initZonePacket->data().pos.x = getPos().x;
initZonePacket->data().pos.y = getPos().y;
initZonePacket->data().pos.z = getPos().z;
if( auto d = getCurrentTerritory()->getAsDirector() )
{
initZonePacket->data().contentfinderConditionId = d->getContentFinderConditionId();
initZonePacket->data().bitmask = 0xFF;
initZonePacket->data().bitmask1 = 0x2A;
}
queuePacket( initZonePacket );
getCurrentTerritory()->onPlayerZoneIn( *this );
@ -1862,6 +1902,8 @@ Sapphire::Entity::Player::sendZoneInPackets( uint32_t param1, uint32_t param2 =
setZoningType( Common::ZoneingType::None );
unsetStateFlag( PlayerStateFlag::BetweenAreas );
Common::Service< MapMgr >::ref().updateAll( *this );
}
void Sapphire::Entity::Player::finishZoning()

View file

@ -111,6 +111,8 @@ namespace Sapphire::Entity
void playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
Event::EventHandler::SceneChainCallback sceneChainCallback );
void playScene16( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t param3, std::vector< uint32_t > paramList, Event::EventHandler::SceneReturnCallback eventReturnCallback );
/*! setup the event and return a ptr to it */
Event::EventHandlerPtr bootstrapSceneEvent( uint32_t eventId, uint32_t flags );
@ -192,6 +194,8 @@ namespace Sapphire::Entity
/*! remove a given quest */
void removeQuest( uint16_t questId );
bool isQuestCompleted( uint16_t questId );
/*! add a quest to the completed quests mask */
void updateQuestsCompleted( uint32_t questId );
@ -490,7 +494,7 @@ namespace Sapphire::Entity
bool setInstance( TerritoryPtr instance );
/*! sets the players instance & initiates zoning process */
bool setInstance( Sapphire::TerritoryPtr instance, Sapphire::Common::FFXIVARR_POSITION3 pos );
bool setInstance( Sapphire::TerritoryPtr instance, Sapphire::Common::FFXIVARR_POSITION3 pos, float rot );
/*! returns the player to their position before zoning into an instance */
bool exitInstance();
@ -547,7 +551,7 @@ namespace Sapphire::Entity
void dyeItemFromDyeingInfo();
/*! prepares zoning / fades out the screen */
void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime = 0, uint16_t animation = 0 );
void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime = 0, uint16_t animation = 0, uint8_t param4 = 0, uint8_t param7 = 0, uint8_t unknown = 0 );
/*! get player's title list (available titles) */
uint8_t* getTitleList();
@ -584,7 +588,7 @@ namespace Sapphire::Entity
uint16_t getCurrentCompanion() const;
/*! get the current mount */
uint8_t getCurrentMount() const;
uint16_t getCurrentMount() const;
/*! set current persistent emote */
void setPersistentEmote( uint32_t emoteId );
@ -642,7 +646,7 @@ namespace Sapphire::Entity
void learnSong( uint8_t songId, uint32_t itemId );
/*! check if an action is already unlocked in the bitmask. */
bool isActionLearned( uint8_t actionId ) const;
bool isActionLearned( uint32_t actionId ) const;
/*! return a const pointer to the unlock bitmask array */
const uint8_t* getUnlockBitmask() const;
@ -653,6 +657,8 @@ namespace Sapphire::Entity
/*! return a const pointer to the mount guide bitmask array */
const uint8_t* getMountGuideBitmask() const;
const bool hasMount( uint32_t mountId ) const;
bool checkAction() override;
bool hasQueuedAction() const;
@ -942,12 +948,13 @@ namespace Sapphire::Entity
ItemPtr getItemAt( uint16_t containerId, uint8_t slotId );
bool updateContainer( uint16_t storageId, uint8_t slotId, ItemPtr pItem );
bool updateContainer( uint16_t storageId, uint8_t slotId, ItemPtr pItem, bool writeToDb = true );
/*! calculate and return player ilvl based off equipped gear */
uint16_t calculateEquippedGearItemLevel();
ItemPtr getEquippedWeapon();
ItemPtr getEquippedSecondaryWeapon();
/*! return the current amount of currency of type */
uint32_t getCurrency( Common::CurrencyType type );
@ -992,6 +999,8 @@ namespace Sapphire::Entity
//////////////////////////////////////////////////////////////////////////////////////////////////////
void setPosAndNotifyClient( float x, float y, float z, float rot );
Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index );
void sendHuntingLog();
@ -1005,6 +1014,7 @@ namespace Sapphire::Entity
uint64_t m_lastMoveTime;
uint8_t m_lastMoveflag;
bool m_falling;
uint16_t m_cfNotifiedContent;
std::vector< ShopBuyBackEntry >& getBuyBackListForShop( uint32_t shopId );
void addBuyBackItemForShop( uint32_t shopId, const ShopBuyBackEntry& entry );
@ -1067,8 +1077,8 @@ namespace Sapphire::Entity
uint16_t m_activeTitle;
uint8_t m_titleList[48];
uint8_t m_howTo[34];
uint8_t m_minions[40];
uint8_t m_mountGuide[22];
uint8_t m_minions[55];
uint8_t m_mountGuide[29];
uint8_t m_homePoint;
uint8_t m_startTown;
uint16_t m_townWarpFstFlags;
@ -1076,8 +1086,8 @@ namespace Sapphire::Entity
uint8_t m_discovery[445];
uint32_t m_playTime;
uint16_t m_classArray[28];
uint32_t m_expArray[28];
uint16_t m_classArray[ Common::CLASSJOB_SLOTS ];
uint32_t m_expArray[ Common::CLASSJOB_SLOTS ];
uint8_t m_aetheryte[21];
uint8_t m_unlocks[64];
uint8_t m_orchestrion[40];

View file

@ -323,6 +323,33 @@ void Sapphire::Entity::Player::eventFinish( uint32_t eventId, uint32_t freePlaye
unsetStateFlag( PlayerStateFlag::InNpcEvent );
}
void Sapphire::Entity::Player::playScene16( uint32_t eventId, uint32_t scene, uint32_t flags, uint32_t param3, std::vector< uint32_t > paramList, Event::EventHandler::SceneReturnCallback eventReturnCallback )
{
auto pEvent = bootstrapSceneEvent( eventId, flags );
if( !pEvent )
return;
pEvent->setPlayedScene( true );
pEvent->setEventReturnCallback( eventReturnCallback );
pEvent->setSceneChainCallback( nullptr );
auto eventPlay16 = makeZonePacket< FFXIVIpcEventPlay16 >( getId() );
eventPlay16->data().actorId = pEvent->getActorId();
eventPlay16->data().eventId = pEvent->getId();
eventPlay16->data().scene = scene;
eventPlay16->data().flags = flags;
eventPlay16->data().param3 = param3;
eventPlay16->data().paramSize = paramList.size();
int i = 0;
for( auto p : paramList )
{
assert( i < 16 );
eventPlay16->data().param[ i ] = paramList.at( i );
i++;
}
queuePacket( eventPlay16 );
}
void Sapphire::Entity::Player::eventActionStart( uint32_t eventId,
uint32_t action,
World::Action::ActionCallback finishCallback,

View file

@ -580,9 +580,9 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil
bool foundFreeSlot = false;
std::vector< uint16_t > bags = { Bag0, Bag1, Bag2, Bag3 };
sendDebug( "adding item: {}, equipSlotCategory: {}, stackSize: {}", itemToAdd->getId(), itemInfo->equipSlotCategory, itemInfo->stackSize );
// add the related armoury bag to the applicable bags and try and fill a free slot there before falling back to regular inventory
if( itemInfo->isEquippable && getEquipDisplayFlags() & StoreNewItemsInArmouryChest )
if( itemInfo->equipSlotCategory > 0 && getEquipDisplayFlags() & StoreNewItemsInArmouryChest )
{
auto bag = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( static_cast< Common::EquipSlotCategory >( itemInfo->equipSlotCategory ) );
@ -601,7 +601,7 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil
auto item = storage->getItem( slot );
// add any items that are stackable
if( canMerge && item && !itemInfo->isEquippable && item->getId() == itemToAdd->getId() )
if( canMerge && item && item->getMaxStackSize() > 1 && item->getId() == itemToAdd->getId() )
{
uint32_t count = item->getStackSize();
uint32_t maxStack = item->getMaxStackSize();
@ -739,7 +739,7 @@ Sapphire::Entity::Player::moveItem( uint16_t fromInventoryId, uint8_t fromSlotId
sendStatusEffectUpdate(); // send if any equip is changed
}
bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slotId, ItemPtr pItem )
bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slotId, ItemPtr pItem, bool writeToDb )
{
auto containerType = World::Manager::ItemMgr::getContainerType( storageId );
@ -752,7 +752,8 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot
case Bag:
case CurrencyCrystal:
{
writeInventory( static_cast< InventoryType >( storageId ) );
if( writeToDb )
writeInventory( static_cast< InventoryType >( storageId ) );
break;
}
@ -767,7 +768,8 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot
else
unequipItem( static_cast< GearSetSlot >( slotId ), pItem, true );
writeInventory( static_cast< InventoryType >( storageId ) );
if( writeToDb )
writeInventory( static_cast< InventoryType >( storageId ) );
break;
}
default:
@ -804,7 +806,7 @@ void Sapphire::Entity::Player::splitItem( uint16_t fromInventoryId, uint8_t from
fromItem->setStackSize( fromItem->getStackSize() - itemCount );
updateContainer( fromInventoryId, fromSlotId, fromItem );
updateContainer( fromInventoryId, fromSlotId, fromItem, fromInventoryId != toInventoryId );
updateContainer( toInventoryId, toSlot, newItem );
updateItemDb( fromItem );
@ -835,7 +837,7 @@ void Sapphire::Entity::Player::mergeItem( uint16_t fromInventoryId, uint8_t from
{
fromItem->setStackSize( stackOverflow );
updateItemDb( fromItem );
updateContainer( fromInventoryId, fromSlotId, fromItem );
updateContainer( fromInventoryId, fromSlotId, fromItem, fromInventoryId != toInventoryId );
}
@ -862,14 +864,16 @@ void Sapphire::Entity::Player::swapItem( uint16_t fromInventoryId, uint8_t fromS
&& !World::Manager::ItemMgr::isArmory( fromInventoryId ) )
{
updateContainer( fromInventoryId, fromSlotId, nullptr );
fromInventoryId = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( static_cast< Common::EquipSlotCategory >( toSlot ) );
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto itemInfo = exdData.get< Sapphire::Data::Item >( toItem->getId() );
fromInventoryId = World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId( static_cast< Common::EquipSlotCategory >( itemInfo->equipSlotCategory ) );
fromSlotId = static_cast < uint8_t >( m_storageMap[ fromInventoryId ]->getFreeSlot() );
}
auto containerTypeFrom = World::Manager::ItemMgr::getContainerType( fromInventoryId );
auto containerTypeTo = World::Manager::ItemMgr::getContainerType( toInventoryId );
updateContainer( toInventoryId, toSlot, fromItem );
updateContainer( toInventoryId, toSlot, fromItem, fromInventoryId != toInventoryId );
updateContainer( fromInventoryId, fromSlotId, toItem );
if( static_cast< InventoryType >( toInventoryId ) == GearSet0 ||
@ -939,6 +943,11 @@ Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedWeapon()
return m_storageMap[ GearSet0 ]->getItem( GearSetSlot::MainHand );
}
Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedSecondaryWeapon()
{
return m_storageMap[ InventoryType::GearSet0 ]->getItem( GearSetSlot::OffHand );
}
uint8_t Sapphire::Entity::Player::getFreeSlotsInBags()
{
uint8_t slots = 0;

View file

@ -7,6 +7,8 @@
#include "Network/GameConnection.h"
#include "Network/PacketWrappers/QuestMessagePacket.h"
#include "Manager/MapMgr.h"
#include "Session.h"
using namespace Sapphire::Common;
@ -17,8 +19,6 @@ void Sapphire::Entity::Player::finishQuest( uint16_t questId )
{
int8_t idx = getQuestIndex( questId );
removeQuest( questId );
auto questFinishPacket = makeZonePacket< FFXIVIpcQuestFinish >( getId() );
questFinishPacket->data().questId = questId;
questFinishPacket->data().flag1 = 1;
@ -26,6 +26,7 @@ void Sapphire::Entity::Player::finishQuest( uint16_t questId )
queuePacket( questFinishPacket );
updateQuestsCompleted( questId );
removeQuest( questId );
//sendQuestTracker(); already sent in removeQuest()
}
@ -33,7 +34,12 @@ void Sapphire::Entity::Player::finishQuest( uint16_t questId )
void Sapphire::Entity::Player::unfinishQuest( uint16_t questId )
{
removeQuestsCompleted( questId );
sendQuestInfo();
auto questFinishPacket = makeZonePacket< FFXIVIpcQuestFinish >( getId() );
questFinishPacket->data().questId = questId;
questFinishPacket->data().flag1 = 0;
questFinishPacket->data().flag2 = 1;
queuePacket( questFinishPacket );
}
void Sapphire::Entity::Player::removeQuest( uint16_t questId )
@ -42,6 +48,11 @@ void Sapphire::Entity::Player::removeQuest( uint16_t questId )
if( ( idx != -1 ) && ( m_activeQuests[ idx ] != nullptr ) )
{
std::shared_ptr< QuestActive > pQuest = m_activeQuests[ idx ];
m_activeQuests[ idx ].reset();
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() );
questUpdatePacket->data().slot = static_cast< uint8_t >( idx );
questUpdatePacket->data().questInfo.c.questId = 0;
@ -54,9 +65,6 @@ void Sapphire::Entity::Player::removeQuest( uint16_t questId )
m_questTracking[ ii ] = -1;
}
std::shared_ptr< QuestActive > pQuest = m_activeQuests[ idx ];
m_activeQuests[ idx ].reset();
m_questIdToQuestIdx.erase( questId );
m_questIdxToQuestId.erase( idx );
@ -916,6 +924,8 @@ void Sapphire::Entity::Player::updateQuest( uint16_t questId, uint8_t sequence )
m_questIdToQuestIdx[ questId ] = idx;
m_questIdxToQuestId[ idx ] = questId;
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() );
questUpdatePacket->data().slot = idx;
questUpdatePacket->data().questInfo = *pNewQuest;
@ -1013,6 +1023,11 @@ Sapphire::Entity::Player::sendQuestMessage( uint32_t questId, int8_t msgId, uint
}
bool Sapphire::Entity::Player::isQuestCompleted( uint16_t questId )
{
return ( m_questCompleteFlags[ questId / 8 ] & ( 0x80 >> ( questId % 8 ) ) );
}
void Sapphire::Entity::Player::updateQuestsCompleted( uint32_t questId )
{
uint16_t index = questId / 8;
@ -1030,7 +1045,10 @@ void Sapphire::Entity::Player::removeQuestsCompleted( uint32_t questId )
uint8_t value = 0x80 >> bitIndex;
m_questCompleteFlags[ index ] ^= value;
if( m_questCompleteFlags[ index ] & value )
m_questCompleteFlags[ index ] ^= value;
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
}

View file

@ -186,6 +186,9 @@ bool Sapphire::Entity::Player::load( uint32_t charId, World::SessionPtr pSession
auto titleList = res->getBlobVector( "TitleList" );
memcpy( reinterpret_cast< char* >( m_titleList ), titleList.data(), titleList.size() );
auto minions = res->getBlobVector( "Minions" );
memcpy( reinterpret_cast< char* >( m_minions ), minions.data(), minions.size() );
auto mountGuide = res->getBlobVector( "Mounts" );
memcpy( reinterpret_cast< char* >( m_mountGuide ), mountGuide.data(), mountGuide.size() );

View file

@ -16,9 +16,10 @@ using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::Server;
using namespace Sapphire::Network::ActorControl;
Sapphire::Event::Director::Director( Sapphire::Event::Director::DirectorType type, uint16_t contentId ) :
Sapphire::Event::Director::Director( Sapphire::Event::Director::DirectorType type, uint16_t contentId, uint16_t contentFinderConditionId ) :
m_contentId( contentId ),
m_type( type ),
m_contentFinderConditionId( contentFinderConditionId ),
m_directorId( ( static_cast< uint32_t >( type ) << 16 ) | contentId ),
m_sequence( 1 ),
m_branch( 0 ),
@ -37,6 +38,11 @@ uint16_t Sapphire::Event::Director::getContentId() const
return m_contentId;
}
uint16_t Sapphire::Event::Director::getContentFinderConditionId() const
{
return m_contentFinderConditionId;
}
uint8_t Sapphire::Event::Director::getSequence() const
{
return m_sequence;
@ -52,14 +58,18 @@ void Sapphire::Event::Director::sendDirectorVars( Sapphire::Entity::Player& play
auto varPacket = makeZonePacket< FFXIVIpcDirectorVars >( player.getId() );
varPacket->data().m_directorId = getDirectorId();
varPacket->data().m_sequence = getSequence();
varPacket->data().m_branch = 0;
varPacket->data().m_branch = getBranch();
memcpy( varPacket->data().m_unionData, m_unionData.arrData, sizeof( varPacket->data().m_unionData ) );
player.queuePacket( varPacket );
player.sendDebug( "DirectorVar#{}: {:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X} seq{}, b{}", getDirectorId(),
m_unionData.ui8.UI8A, m_unionData.ui8.UI8B, m_unionData.ui8.UI8C, m_unionData.ui8.UI8D, m_unionData.ui8.UI8E,
m_unionData.ui8.UI8F, m_unionData.ui8.UI8G, m_unionData.ui8.UI8H, m_unionData.ui8.UI8I, m_unionData.ui8.UI8J,
getSequence(), getBranch() );
}
void Sapphire::Event::Director::sendDirectorInit( Sapphire::Entity::Player& player ) const
{
Logger::debug( "DirectorID#{}, QuestBattleID#{}", m_directorId, m_contentId );
Logger::debug( "DirectorID#{}, ContentId#{}, ContentFinderConditionId#{}", m_directorId, m_contentId, m_contentFinderConditionId );
player.queuePacket( makeActorControlSelf( player.getId(), DirectorInit, m_directorId, m_contentId ) );
}
@ -183,12 +193,12 @@ void Sapphire::Event::Director::setDirectorSequence( uint8_t value )
m_sequence = value;
}
void Sapphire::Event::Director::setCustomVar( uint32_t varId, uint32_t value )
void Sapphire::Event::Director::setCustomVar( uint32_t varId, uint64_t value )
{
m_customVarMap[ varId ] = value;
}
uint32_t Sapphire::Event::Director::getCustomVar( uint32_t varId )
uint64_t Sapphire::Event::Director::getCustomVar( uint32_t varId )
{
auto it = m_customVarMap.find( varId );
if( it != m_customVarMap.end() )

View file

@ -42,12 +42,14 @@ namespace Sapphire::Event
DutyFailed
};
Director( DirectorType type, uint16_t contentId );
Director( DirectorType type, uint16_t contentId, uint16_t contentFinderConditionId = 0 );
uint32_t getDirectorId() const;
uint16_t getContentId() const;
uint16_t getContentFinderConditionId() const;
DirectorType getType() const;
uint8_t getSequence() const;
@ -104,8 +106,8 @@ namespace Sapphire::Event
void setDirectorBranch( uint8_t value );
void setCustomVar( uint32_t varId, uint32_t value );
uint32_t getCustomVar( uint32_t varId );
void setCustomVar( uint32_t varId, uint64_t value );
uint64_t getCustomVar( uint32_t varId );
private:
/*! Id of the content of the director */
@ -114,6 +116,8 @@ namespace Sapphire::Event
/*! DirectorType | ContentId */
uint32_t m_directorId;
uint16_t m_contentFinderConditionId;
/*! currect sequence */
uint8_t m_sequence;
@ -183,7 +187,7 @@ namespace Sapphire::Event
uint32_t m_elapsedTime;
std::unordered_map< uint32_t, uint32_t > m_customVarMap;
std::unordered_map< uint32_t, uint64_t > m_customVarMap;
};

View file

@ -78,10 +78,20 @@ namespace Sapphire::Event
FcTalk = 0x001F,
Adventure = 0x0021,
DailyQuestSupply = 0x0022,
TripleTriad = 0x0023,
PreHandler = 0x0036,
ICDirector = 0x8003,
PublicContentDirector = 0x8004,
QuestBattleDirector = 0x8006,
};
enum class QuestAvailability : uint8_t
{
Invisible,
Available,
Locked
};
using SceneReturnCallback = std::function< void( Entity::Player&, const SceneResult& ) >;
using SceneChainCallback = std::function< void( Entity::Player& ) >;
using EventFinishCallback = std::function< void( Entity::Player&, uint64_t ) >;

View file

@ -24,6 +24,7 @@ TYPE_FORWARD( HousingZone );
TYPE_FORWARD( House );
TYPE_FORWARD( InstanceContent );
TYPE_FORWARD( QuestBattle );
TYPE_FORWARD( PublicContent );
TYPE_FORWARD( Item );
TYPE_FORWARD( ItemContainer );
TYPE_FORWARD( ZonePosition );

View file

@ -85,6 +85,9 @@ Sapphire::ItemPtr Sapphire::ItemContainer::getItem( uint8_t slotId )
Logger::error( "Slot out of range {0}", slotId );
return nullptr;
}
if( m_itemMap.find( slotId ) == m_itemMap.end() )
return nullptr;
return m_itemMap[ slotId ];
}
@ -92,7 +95,10 @@ Sapphire::ItemPtr Sapphire::ItemContainer::getItem( uint8_t slotId )
void Sapphire::ItemContainer::setItem( uint8_t slotId, ItemPtr pItem )
{
if( slotId > m_size )
{
Logger::error( "Slot out of range {0}", slotId );
return;
}
m_itemMap[ slotId ] = pItem;
}

View file

@ -32,6 +32,7 @@
#include "Territory/HousingZone.h"
#include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Manager/TerritoryMgr.h"
#include "Event/EventDefs.h"
@ -60,6 +61,8 @@ Sapphire::World::Manager::DebugCommandMgr::DebugCommandMgr()
registerCommand( "script", &DebugCommandMgr::script, "Server script utilities.", 1 );
registerCommand( "instance", &DebugCommandMgr::instance, "Instance utilities", 1 );
registerCommand( "questbattle", &DebugCommandMgr::questBattle, "Quest battle utilities", 1 );
registerCommand( "pc", &DebugCommandMgr::pc, "Public content utilities", 1 );
registerCommand( "publiccontent", &DebugCommandMgr::pc, "Public content utilities", 1 );
registerCommand( "qb", &DebugCommandMgr::questBattle, "Quest battle utilities", 1 );
registerCommand( "housing", &DebugCommandMgr::housing, "Housing utilities", 1 );
}
@ -226,22 +229,14 @@ void Sapphire::World::Manager::DebugCommandMgr::set( char* data, Entity::Player&
if( player.getLevelForClass( static_cast< Common::ClassJob > ( id ) ) == 0 )
{
player.setLevelForClass( 1, static_cast< Common::ClassJob > ( id ) );
player.setClassJob( static_cast< Common::ClassJob > ( id ) );
player.sendModel();
player.sendItemLevel();
player.calculateStats();
player.sendStats();
player.sendStatusEffectUpdate();
player.sendStatusUpdate();
}
else
player.setClassJob( static_cast< Common::ClassJob > ( id ) );
player.sendModel();
player.sendItemLevel();
player.calculateStats();
player.sendStats();
player.sendStatusEffectUpdate();
player.sendStatusUpdate();
player.setClassJob( static_cast< Common::ClassJob > ( id ) );
player.sendModel();
player.sendItemLevel();
player.calculateStats();
player.sendStats();
player.sendStatusEffectUpdate();
player.sendStatusUpdate();
}
else if( subCommand == "cfpenalty" )
{
@ -591,9 +586,23 @@ void Sapphire::World::Manager::DebugCommandMgr::get( char* data, Entity::Player&
int16_t map_id = exdData.get< Sapphire::Data::TerritoryType >( player.getCurrentTerritory()->getTerritoryTypeId() )->map;
player.sendNotice( "Pos:\n {0}\n {1}\n {2}\n {3}\n MapId: {4}\n ZoneId:{5}",
player.sendNotice( "Pos: x: {0}, y: {1}, z: {2}, r: {3}\n MapId: {4}, ZoneId:{5}, Weather:{6}, Festival:{7}, {8}",
player.getPos().x, player.getPos().y, player.getPos().z,
player.getRot(), map_id, player.getCurrentTerritory()->getTerritoryTypeId() );
player.getRot(), map_id, player.getCurrentTerritory()->getTerritoryTypeId(),
static_cast< uint8_t >( player.getCurrentTerritory()->getCurrentWeather() ), player.getCurrentTerritory()->getCurrentFestival().first,
player.getCurrentTerritory()->getCurrentFestival().second );
if( auto instance = player.getCurrentInstance() )
{
player.sendNotice( "Instance info:\nContentId: {}, DirectorId: {}\nSequence: {}, Branch: {}, BGM: {}",
instance->getInstanceContentId(), instance->getDirectorId(), instance->getSequence(),
instance->getBranch(), instance->getCurrentBGM() );
}
else if( auto instance = player.getCurrentPublicContent() )
{
player.sendNotice( "Public content info:\nContentId: {}, DirectorId: {}\nSequence: {}, Branch: {}",
instance->getContentId(), instance->getDirectorId(), instance->getSequence(),
instance->getBranch() );
}
}
else
{
@ -1290,3 +1299,137 @@ void Sapphire::World::Manager::DebugCommandMgr::housing( char* data, Entity::Pla
player.sendDebug( "Unknown sub command." );
}
}
void Sapphire::World::Manager::DebugCommandMgr::pc( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command )
{
auto& terriMgr = Common::Service< TerritoryMgr >::ref();
std::string cmd( data ), params, subCommand;
auto cmdPos = cmd.find_first_of( ' ' );
if( cmdPos != std::string::npos )
{
params = cmd.substr( cmdPos + 1 );
auto p = params.find_first_of( ' ' );
if( p != std::string::npos )
{
subCommand = params.substr( 0, p );
params = params.substr( subCommand.length() + 1 );
}
else
subCommand = params;
}
if( subCommand == "create" || subCommand == "cr" )
{
uint32_t contentFinderConditionId;
sscanf( params.c_str(), "%d", &contentFinderConditionId );
auto instance = terriMgr.createPublicContent( contentFinderConditionId );
if( instance )
player.sendDebug( "Created instance with id#{0} -> {1}", instance->getGuId(), instance->getName() );
else
player.sendDebug( "Failed to create instance with id#{0}", contentFinderConditionId );
}
else if ( subCommand == "create2" || subCommand == "cr2" )
{
uint16_t contentId, terriId;
sscanf( params.c_str(), "%hu %hu", &contentId, &terriId );
auto instance = terriMgr.createPublicContent( contentId, terriId );
if( instance )
player.sendDebug( "Created instance with id#{0} -> {1}", instance->getGuId(), instance->getName() );
else
player.sendDebug( "Failed to create instance with id#{0}, territory#{1}. Server console output may contain additional info.", contentId, terriId );
}
else if( subCommand == "remove" || subCommand == "rm" )
{
uint32_t terriId;
sscanf( params.c_str(), "%d", &terriId );
if( terriMgr.removeTerritoryInstance( terriId ) )
player.sendDebug( "Removed instance with id#{0}", terriId );
else
player.sendDebug( "Failed to remove instance with id#{0}", terriId );
}
else if( subCommand == "return" || subCommand == "ret" )
{
player.exitInstance();
}
else if( subCommand == "set" )
{
uint32_t index;
uint32_t value;
sscanf( params.c_str(), "%d %d", &index, &value );
auto instance = std::dynamic_pointer_cast< PublicContent >( player.getCurrentTerritory() );
if( !instance )
return;
instance->setVar( static_cast< uint8_t >( index ), static_cast< uint8_t >( value ) );
}
else if( subCommand == "seq" )
{
uint8_t seq;
sscanf( params.c_str(), "%hhu", &seq );
auto instance = std::dynamic_pointer_cast< PublicContent >( player.getCurrentTerritory() );
if( !instance )
return;
instance->setSequence( seq );
}
else if( subCommand == "branch" )
{
uint8_t branch;
sscanf( params.c_str(), "%hhu", &branch );
auto instance = std::dynamic_pointer_cast< PublicContent >( player.getCurrentTerritory() );
if( !instance )
return;
instance->setBranch( branch );
}
else if( subCommand == "objstate" )
{
char objName[128];
uint8_t state;
sscanf( params.c_str(), "%s %hhu", objName, &state );
auto instance = std::dynamic_pointer_cast< PublicContent >( player.getCurrentTerritory() );
if( !instance )
return;
auto obj = instance->getEObjByName( objName );
if( !obj )
return;
obj->setState( state );
}
else if( subCommand == "objflag" )
{
char objName[256];
uint32_t state1;
uint32_t state2;
sscanf( params.c_str(), "%s %i %i", objName, &state1, &state2 );
auto instance = std::dynamic_pointer_cast< PublicContent >( player.getCurrentTerritory() );
if( !instance )
return;
auto obj = instance->getEObjByName( objName );
if( !obj )
{
player.sendDebug( "No eobj found." );
return;
}
obj->setAnimationFlag( state1, state2 );
}
}

View file

@ -53,6 +53,7 @@ namespace Sapphire::World::Manager
void instance( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command );
void questBattle( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command );
void pc( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command );
void housing( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command) ;

View file

@ -74,6 +74,13 @@ std::string Sapphire::World::Manager::EventMgr::getEventName( uint32_t eventId )
name[ 0 ] = toupper( name[ 0 ] );
return name;
}
case Event::EventHandler::EventHandlerType::PublicContentDirector:
{
auto pcInfo = exdData.get< Sapphire::Data::PublicContent >( eventId & 0x0000FFFF );
if( !pcInfo )
return "unknown";
return pcInfo->name;
}
case Event::EventHandler::EventHandlerType::Warp:

View file

@ -662,6 +662,14 @@ void Sapphire::World::Manager::HousingMgr::createHouse( Sapphire::HousePtr house
db.execute( stmt );
}
void Sapphire::World::Manager::HousingMgr::deleteHouse( Sapphire::HousePtr house ) const
{
auto& db = Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref();
auto stmt = db.getPreparedStatement( Db::HOUSING_HOUSE_DEL );
stmt->setUInt( 1, house->getId() );
db.execute( stmt );
}
void Sapphire::World::Manager::HousingMgr::buildPresetEstate( Entity::Player& player, uint8_t plotNum, uint32_t presetCatalogId )
{
auto hZone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentTerritory() );
@ -903,6 +911,8 @@ void Sapphire::World::Manager::HousingMgr::updateHouseModels( Sapphire::HousePtr
{
assert( house );
house->clearModelCache();
auto& containers = getEstateInventory( house->getLandIdent() );
auto extContainer = containers.find( static_cast< uint16_t >( InventoryType::HousingExteriorAppearance ) );
@ -1627,3 +1637,122 @@ Sapphire::Inventory::HousingItemPtr Sapphire::World::Manager::HousingMgr::getHou
return Inventory::make_HousingItem( tmpItem->getUId(), tmpItem->getId() );
}
void Sapphire::World::Manager::HousingMgr::editAppearance( bool isInterior, Sapphire::Entity::Player& player, const Common::LandIdent landIdent, std::vector< uint16_t > containerList, std::vector< uint8_t> slotList, uint8_t removeFlag )
{
auto landSetId = toLandSetId( static_cast< uint16_t >( landIdent.territoryTypeId ), static_cast< uint8_t >( landIdent.wardNum ) );
auto terri = getHousingZoneByLandSetId( landSetId );
auto land = terri->getLand( static_cast< uint8_t >( landIdent.landId ) );
if( !land )
return;
if( !hasPermission( player, *land, 0 ) )
return;
auto& housingContainer = getEstateInventory( landIdent )[ isInterior ? InventoryType::HousingInteriorAppearance : InventoryType::HousingExteriorAppearance ];
auto& invMgr = Service< InventoryMgr >::ref();
for( int i = 0; i < ( isInterior ? 10 : 9 ); i++ )
{
auto container = containerList.at( i );
auto slot = slotList.at( i );
if( container == 0x270F || slot == 0xFF )
{
if( i >= 5 )
{
auto removed = ( ( removeFlag >> ( i - 5 ) ) & 1 ) > 0;
if( removed )
{
auto oldItem = housingContainer->getItem( i );
if( oldItem )
{
housingContainer->removeItem( i );
invMgr.removeItemFromHousingContainer( landIdent, housingContainer->getId(), i );
player.addItem( oldItem, false, false, false );
}
}
}
continue;
}
auto item = getHousingItemFromPlayer( player, static_cast< Sapphire::Common::InventoryType >( container ), slot );
if( item )
{
auto oldItem = housingContainer->getItem( i );
housingContainer->setItem( i, item );
if( oldItem )
{
player.insertInventoryItem( static_cast< Sapphire::Common::InventoryType >( container ), slot, oldItem );
}
}
}
invMgr.sendInventoryContainer( player, housingContainer );
invMgr.saveHousingContainer( landIdent, housingContainer );
updateHouseModels( land->getHouse() );
if( !isInterior )
{
terri->sendLandUpdate( landIdent.landId );
}
}
void Sapphire::World::Manager::HousingMgr::removeHouse( Entity::Player& player, uint16_t plot )
{
auto terri = std::dynamic_pointer_cast< HousingZone >( player.getCurrentTerritory() );
if( !terri )
return;
auto land = terri->getLand( static_cast< uint8_t >( plot ) );
if( !land || !land->getHouse() )
return;
if( !hasPermission( player, *land, 0 ) )
return;
auto& interiorContainer = getEstateInventory( land->getLandIdent() )[ InventoryType::HousingInteriorAppearance ];
auto& invMgr = Service< InventoryMgr >::ref();
std::unordered_map< InventoryType, ItemContainerPtr > changedContainerSet = {};
for( int i = 0; i < interiorContainer->getMaxSize(); i++ )
{
auto item = interiorContainer->getItem( i );
if( !item )
continue;
Inventory::InventoryContainerPair freeSlotPair;
auto freeContainer = getFreeEstateInventorySlot( land->getLandIdent(), freeSlotPair, m_internalStoreroomContainers );
if ( !freeContainer )
{
// not sure what to do
interiorContainer->removeItem( i, true );
}
else
{
interiorContainer->removeItem( i, false );
freeContainer->setItem( freeSlotPair.second , item );
changedContainerSet[ freeSlotPair.first ] = freeContainer;
}
}
invMgr.sendInventoryContainer( player, interiorContainer );
invMgr.saveHousingContainer( land->getLandIdent(), interiorContainer );
for( auto entry : changedContainerSet )
{
invMgr.sendInventoryContainer( player, entry.second );
invMgr.saveHousingContainer( land->getLandIdent(), entry.second );
}
deleteHouse( land->getHouse() );
land->setHouse( nullptr );
land->setStatus( HouseStatus::Sold );
land->updateLandDb();
terri->sendLandUpdate( plot );
player.setLandFlags( LandFlagsSlot::Private, 0, land->getLandIdent() );
terri->removeEstateEntranceEObj( plot );
// missing reply for ClientTrigger RequestEstateHallRemoval
}

View file

@ -181,6 +181,10 @@ namespace Sapphire::World::Manager
bool hasPermission( Entity::Player& player, Land& land, uint32_t permission );
void editAppearance( bool isInterior, Sapphire::Entity::Player& player, const Common::LandIdent landIdent, std::vector< uint16_t > containerList, std::vector< uint8_t > slotList, uint8_t removeFlag );
void removeHouse( Entity::Player& player, uint16_t plot );
private:
Inventory::HousingItemPtr getHousingItemFromPlayer( Entity::Player& player, Common::InventoryType type, uint8_t slot );
@ -272,6 +276,8 @@ namespace Sapphire::World::Manager
*/
void createHouse( HousePtr house ) const;
void deleteHouse( HousePtr house ) const;
/*!
* @brief Gets the next available house id
* @return The next available house id

View file

@ -89,6 +89,9 @@ void Sapphire::World::Manager::InventoryMgr::saveHousingContainer( Common::LandI
for( auto& item : container->getItemMap() )
{
if( !item.second )
continue;
saveHousingContainerItem( u64ident, container->getId(), item.first, item.second->getUId() );
}
}
@ -143,15 +146,15 @@ void Sapphire::World::Manager::InventoryMgr::updateHousingItemPosition( Sapphire
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->setDouble( 2, static_cast< double >( pos.x ) );
stmt->setDouble( 3, static_cast< double >( pos.y ) );
stmt->setDouble( 4, static_cast< double >( pos.z ) );
stmt->setDouble( 5, static_cast< double >( rot ) );
stmt->setUInt( 6, pos.x );
stmt->setUInt( 7, pos.y );
stmt->setUInt( 8, pos.z );
stmt->setInt( 9, rot );
stmt->setDouble( 6, static_cast< double >( pos.x ) );
stmt->setDouble( 7, static_cast< double >( pos.y ) );
stmt->setDouble( 8, static_cast< double >( pos.z ) );
stmt->setDouble( 9, static_cast< double >( rot ) );
db.execute( stmt );
}

View file

@ -32,51 +32,51 @@ uint16_t Sapphire::World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId(
switch( slot )
{
case Common::EquipSlotCategory::CharaHead:
case Common::EquipSlotCategory::Head:
return Common::ArmoryHead;
case Common::EquipSlotCategory::CharaBody:
//case Common::EquipSlotCategory::BodyDisallowHead:
//case Common::EquipSlotCategory::BodyDisallowHandsLegsFeet:
//case Common::EquipSlotCategory::BodyDisallowAll:
//case Common::EquipSlotCategory::BodyDisallowHands:
//case Common::EquipSlotCategory::BodyDisallowLegsFeet:
case Common::EquipSlotCategory::Body:
case Common::EquipSlotCategory::BodyDisallowHead:
case Common::EquipSlotCategory::BodyDisallowHandsLegsFeet:
case Common::EquipSlotCategory::BodyDisallowAll:
case Common::EquipSlotCategory::BodyDisallowHands:
case Common::EquipSlotCategory::BodyDisallowLegsFeet:
return Common::ArmoryBody;
case Common::EquipSlotCategory::CharaEars:
case Common::EquipSlotCategory::Ears:
return Common::ArmoryEar;
case Common::EquipSlotCategory::CharaFeet:
case Common::EquipSlotCategory::Feet:
return Common::ArmoryFeet;
case Common::EquipSlotCategory::CharaHands:
case Common::EquipSlotCategory::Hands:
return Common::ArmoryHand;
case Common::EquipSlotCategory::CharaLegs:
//case Common::EquipSlotCategory::LegsDisallowFeet:
case Common::EquipSlotCategory::Legs:
case Common::EquipSlotCategory::LegsDisallowFeet:
return Common::ArmoryLegs;
case Common::EquipSlotCategory::CharaMainHand:
//case Common::EquipSlotCategory::MainTwoHandedWeapon:
case Common::EquipSlotCategory::MainHand:
case Common::EquipSlotCategory::MainTwoHandedWeapon:
//case Common::EquipSlotCategory::MainOrOffHand:
return Common::ArmoryMain;
case Common::EquipSlotCategory::CharaOffHand:
case Common::EquipSlotCategory::OffHand:
return Common::ArmoryOff;
case Common::EquipSlotCategory::CharaRing:
case Common::EquipSlotCategory::Ring:
return Common::ArmoryRing;
case Common::EquipSlotCategory::CharaWaist:
case Common::EquipSlotCategory::Waist:
return Common::ArmoryWaist;
case Common::EquipSlotCategory::CharaWrist:
case Common::EquipSlotCategory::Wrist:
return Common::ArmoryWrist;
case Common::EquipSlotCategory::CharaNeck:
case Common::EquipSlotCategory::Neck:
return Common::ArmoryNeck;
case Common::EquipSlotCategory::CharaSoulCrystal:
case Common::EquipSlotCategory::SoulCrystal:
return Common::ArmorySoulCrystal;
default:

View file

@ -0,0 +1,685 @@
#include <Common.h>
#include <Service.h>
#include <Exd/ExdDataGenerated.h>
#include <Event/EventHandler.h>
#include <Network/CommonActorControl.h>
#include <Network/PacketDef/Zone/ServerZoneDef.h>
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
#include <Util/Util.cpp>
#include <Actor/Player.h>
#include <Script/ScriptMgr.h>
#include <Script/NativeScriptMgr.h>
#include <Territory/InstanceObjectCache.h>
#include <datReader/DatCategories/bg/lgb.h>
#include "MapMgr.h"
#include "TerritoryMgr.h"
using namespace Sapphire::Event;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::Server;
Sapphire::World::Manager::MapMgr::MapMgr()
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
for( uint32_t questId = 65536; auto curQuest = exdData.get< Data::Quest >( questId ); questId++ )
m_quests.emplace( questId, curQuest );
}
void Sapphire::World::Manager::MapMgr::updateAll( Entity::Player& player )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto& objectCache = Common::Service< Sapphire::InstanceObjectCache >::ref();
EventSet mapData;
auto eventNpcs = objectCache.getAllEventNpc( player.getZoneId() );
if( eventNpcs )
{
for( const auto& eventNpc : *eventNpcs )
{
auto eNpc = exdData.get< Data::ENpcBase >( eventNpc.second->data.enpcId );
if( eNpc )
{
auto eNpcData = eNpc->eNpcData;
for( auto npcData : eNpcData )
{
if( npcData == 0 )
continue; // Some npcs have data gaps, so we have to iterate through the entire array
EventData eventData;
eventData.eventId = npcData;
eventData.levelId = eventNpc.first;
auto eventHandlerType = static_cast< EventHandler::EventHandlerType >( npcData >> 16 );
switch( eventHandlerType )
{
case EventHandler::EventHandlerType::Quest:
{
auto quest = m_quests[ npcData ];
if( quest->issuerLocation == eventNpc.first )
{
insertQuest( player, npcData, mapData );
}
break;
}
case EventHandler::EventHandlerType::GuildLeveAssignment:
{
if( player.isActionLearned( 5 ) )
{
auto guildLeve = exdData.get< Data::GuildleveAssignment >( npcData );
eventData.iconId = exdData.get< Data::EventIconType >( 5 )->mapIconAvailable + 1;
if( player.isQuestCompleted( guildLeve->quest[ 0 ] ) ||
( ( guildLeve->typeId == 2 || npcData == 393217 || npcData == 393223 || npcData == 393225 ) && // Leve npc locations: Bentbranch / Horizon / Swiftperch
( player.isQuestCompleted( 220 ) || player.isQuestCompleted( 687 ) || player.isQuestCompleted( 693 ) ) ) )
{
if( guildLeve->typeId == 2 )
{
if( player.getGc() != 0 )
{
for( int8_t i = 0; i < 3; i++ )
{
if( player.getGcRankArray()[ i ] >= guildLeve->grandCompanyRank )
{
mapData.insert( eventData );
break;
}
}
}
}
else
{
mapData.insert( eventData );
}
}
}
break;
}
case EventHandler::EventHandlerType::CustomTalk:
{
// Include only the beginner arena icon yet. There a few other ones, that aren't referenced in the game files (Some examples are: The Triple Triad Tournament npc which has multiple icons and the ocean fishing icon)
if( npcData == 721223 )
{
auto customTalk = exdData.get< Data::CustomTalk >( npcData );
eventData.iconId = customTalk->iconMap;
mapData.insert( eventData );
}
break;
}
case EventHandler::EventHandlerType::GuildOrderGuide:
{
if( player.isActionLearned( 7 ) )
{
eventData.iconId = exdData.get< Data::EventIconType >( 6 )->mapIconAvailable + 1;
mapData.insert( eventData );
}
break;
}
case EventHandler::EventHandlerType::TripleTriad:
{
if( npcData == 2293771 ) // Triple Triad Master npc for now only
{
eventData.iconId = exdData.get< Data::EventIconType >( 7 )->mapIconAvailable + 1;
mapData.insert( eventData );
}
break;
}
case EventHandler::EventHandlerType::PreHandler:
{
//I think this is used in Bozja and Zadnor, need evidence
break;
}
}
}
}
}
}
auto eventObjs = objectCache.getAllEventObj( player.getZoneId() );
if( eventObjs )
{
for( const auto& eventObj : *eventObjs )
{
auto eObj = exdData.get< Data::EObj >( eventObj.second->data.eobjId );
if( eObj )
{
auto eObjData = eObj->data;
if( eObjData )
{
EventData eventData;
eventData.eventId = eObjData;
eventData.levelId = eventObj.first;
auto eventHandlerType = static_cast< EventHandler::EventHandlerType >( eObjData >> 16 );
if( eventHandlerType == EventHandler::EventHandlerType::Quest )
{
auto quest = m_quests[ eObjData ];
if( quest->issuerLocation == eventObj.first )
{
insertQuest( player, eObjData, mapData );
}
}
}
}
}
}
sendPackets( player, mapData, All );
}
void Sapphire::World::Manager::MapMgr::updateQuests( Entity::Player& player )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto& objectCache = Common::Service< Sapphire::InstanceObjectCache >::ref();
EventSet mapData;
auto eventNpcs = objectCache.getAllEventNpc( player.getZoneId() );
if( eventNpcs )
{
for( const auto& eventNpc : *eventNpcs )
{
auto eNpcData = exdData.get< Data::ENpcBase >( eventNpc.second->data.enpcId )->eNpcData;
for( auto npcData : eNpcData )
{
if( npcData == 0 )
continue; // Some npcs have data gaps, so we have to iterate through the entire array
EventData eventData;
eventData.eventId = npcData;
eventData.levelId = eventNpc.first;
auto eventHandlerType = static_cast< EventHandler::EventHandlerType >( npcData >> 16 );
if( eventHandlerType == EventHandler::EventHandlerType::Quest )
{
auto quest = m_quests[ npcData ];
if( quest->issuerLocation == eventNpc.first )
{
insertQuest( player, npcData, mapData );
}
}
}
}
}
auto eventObjs = objectCache.getAllEventObj( player.getZoneId() );
if( eventObjs )
{
for( const auto& eventObj : *eventObjs )
{
auto eObj = exdData.get< Data::EObj >( eventObj.second->data.eobjId );
if( eObj )
{
auto eObjData = eObj->data;
if( eObjData )
{
EventData eventData;
eventData.eventId = eObjData;
eventData.levelId = eventObj.first;
auto eventHandlerType = static_cast< EventHandler::EventHandlerType >( eObjData >> 16 );
if( eventHandlerType == EventHandler::EventHandlerType::Quest )
{
auto quest = m_quests[ eObjData ];
if( quest->issuerLocation == eventObj.first )
{
insertQuest( player, eObjData, mapData );
}
}
}
}
}
}
sendPackets( player, mapData, Quest );
}
void Sapphire::World::Manager::MapMgr::insertQuest( Entity::Player& player, uint32_t questId, EventSet& mapData )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
auto quest = m_quests[ questId ];
if( isQuestVisible( player, questId, quest ) )
{
auto script = scriptMgr.getNativeScriptHandler().getScript< Sapphire::ScriptAPI::EventScript >( questId );
// Just don't show quests on map, that aren't implemented yet
if( script )
{
EventData eventData;
eventData.eventId = questId;
auto eventState = script->getQuestAvailability( player, questId );
if( eventState == Event::EventHandler::QuestAvailability::Available || eventState == Event::EventHandler::QuestAvailability::Locked )
{
if( eventState == Event::EventHandler::QuestAvailability::Available && isQuestAvailable( player, questId, quest ) )
{
eventData.iconId = exdData.get< Data::EventIconType >( quest->eventIconType )->mapIconAvailable + 1 + quest->isRepeatable;
eventData.levelId = quest->issuerLocation;
}
else
{
eventData.iconId = exdData.get< Data::EventIconType >( quest->eventIconType )->mapIconInvalid + 1 + quest->isRepeatable;
eventData.levelId = quest->issuerLocation;
}
mapData.insert( eventData );
}
}
}
}
bool Sapphire::World::Manager::MapMgr::isQuestAvailable( Entity::Player& player, uint32_t questId, Data::ExdDataGenerated::QuestPtr questPtr )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
if( questPtr->grandCompany || questPtr->grandCompanyRank )
{
if( questPtr->grandCompany != player.getGc() )
{
if( questPtr->grandCompanyRank > player.getGcRankArray()[ player.getGc() - 1 ] )
return false;
}
}
if( questPtr->instanceContentJoin == 1 )
{
for( int32_t i = 0; i < 3; i++ )
{
if( questPtr->instanceContent[ i ] == 0 )
continue;
return false;
}
return true;
}
else if( questPtr->instanceContentJoin == 2 )
{
for( int32_t i = 0; i < 3; i++ )
{
if( questPtr->instanceContent[ i ] == 0 )
continue;
return false;
}
}
if( questPtr->bellStart || questPtr->bellEnd )
{
uint64_t curEorzeaTime = Util::getEorzeanTimeStamp();
uint32_t convTime = 100 * ( curEorzeaTime / 3600 % 24 ) + curEorzeaTime / 60 % 60;
if( questPtr->bellStart <= questPtr->bellEnd )
{
if( convTime < questPtr->bellStart || convTime >= questPtr->bellEnd )
return false;
}
else
{
if( convTime < questPtr->bellStart && convTime >= questPtr->bellEnd )
return false;
}
}
auto classJobCategory = &exdData.get< Data::ClassJobCategory >( questPtr->classJobCategory0 )->aDV;
if( !classJobCategory[ static_cast< uint8_t >( player.getClass() ) ] )
return false;
if( questPtr->classJobCategory1 > 1 )
{
classJobCategory = &exdData.get< Data::ClassJobCategory >( questPtr->classJobCategory1 )->aDV;
if( !classJobCategory[ static_cast< uint8_t >( player.getClass() ) ] )
return false;
}
return true;
}
bool Sapphire::World::Manager::MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Data::ExdDataGenerated::QuestPtr questPtr )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
if( ( player.isQuestCompleted( questId ) && ( !questPtr->isRepeatable && questId != 67114 ) ) || player.hasQuest( questId ) )
return false;
if( questPtr->classJobUnlock )
{
if( questPtr->classJobUnlockFlag == 3 )
if( static_cast< uint8_t >( player.getClass() ) != questPtr->classJobUnlock )
return false;
else if( questPtr->classJobUnlockFlag == 4 )
if ( static_cast< uint8_t >( player.getClass() ) == questPtr->classJobUnlock )
return false;
else
return false;
}
// Was this really ever used?
if( questPtr->startTown )
{
if( questPtr->startTown != player.getStartTown() )
return false;
}
if( Common::CURRENT_EXPANSION_ID < questPtr->expansion )
return false;
if( questPtr->mountRequired )
{
if( !player.hasMount( questPtr->mountRequired ) )
return false;
}
if( questPtr->grandCompany )
{
if( questPtr->grandCompany != player.getGc() )
return false;
}
if( questPtr->header != 0 )
{
if ( !player.isActionLearned( questPtr->header ) )
return false;
}
if( questPtr->previousQuestJoin == 1 )
{
for( int32_t i = 0; i < 3; i++ )
{
if( questPtr->previousQuest[ i ] == 0 )
continue;
if( !player.isQuestCompleted( questPtr->previousQuest[ i ] ) )
{
if( i == 0 && questPtr->previousQuest0Sequence != 0 )
{
if( player.getQuestSeq( questPtr->previousQuest[ i ] ) < questPtr->previousQuest0Sequence )
return false;
}
else
{
return false;
}
}
}
}
else if( questPtr->previousQuestJoin == 2 )
{
for( int32_t i = 0; i <= 3; i++ )
{
if( i == 3 )
return false;
if( questPtr->previousQuest[ i ] == 0 )
continue;
if( player.isQuestCompleted( questPtr->previousQuest[ i ] ) )
break;
}
}
if( questPtr->questLockJoin == 1 )
{
for( int32_t i = 0; i <= 2; i++ )
{
if( i == 2 )
return false;
if( questPtr->questLock[ i ] == 0 )
continue;
if( !player.isQuestCompleted( questPtr->questLock[ i ] ) && !player.hasQuest( questPtr->questLock[ i ] ) )
break;
}
}
else if( questPtr->questLockJoin == 2 )
{
for( int32_t i = 0; i < 2; i++ )
{
if( questPtr->questLock[ i ] == 0 )
continue;
if( player.isQuestCompleted( questPtr->questLock[ i ] ) || player.hasQuest( questPtr->questLock[ i ] ) )
return false;
}
}
if( questPtr->festival )
{
auto& territoryMgr = Common::Service< Manager::TerritoryMgr >::ref();
auto& festival = territoryMgr.getCurrentFestival();
if( questPtr->festival != festival.first && questPtr->festival != festival.second )
return false;
// Don't show festivals with begin state other than 0 yet
if( questPtr->festivalBegin != 0 )
return false;
}
if( ( questPtr->type & 1 ) == 0 )
{
auto classJobCategory = &exdData.get< Data::ClassJobCategory >( questPtr->classJobCategory0 )->aDV;
for( int32_t i = 1; i <= Common::CLASSJOB_TOTAL; i++ )
{
if( classJobCategory[ i ] )
{
if( player.getLevelForClass( static_cast< Common::ClassJob >( i ) ) >= questPtr->classJobLevel0 )
break;
}
if( i == Common::CLASSJOB_TOTAL )
return false;
}
}
else
{
if( player.getLevel() < questPtr->classJobLevel0 )
return false;
}
for( int32_t i = 0; i <= Common::CLASSJOB_TOTAL; i++ )
{
auto classJob = exdData.get< Data::ClassJob >( i );
if( classJob->relicQuest == questId )
{
for( int32_t j = 0; i <= Common::CLASSJOB_TOTAL; i++ )
{
classJob = exdData.get< Data::ClassJob >( i );
if( player.hasQuest( classJob->relicQuest ) )
return false;
}
break;
}
}
if( questPtr->beastTribe )
return false;
if( questPtr->satisfactionNpc )
return false;
if( questPtr->isHouseRequired )
return false;
if( questPtr->deliveryQuest )
return false;
if( player.getQuestSeq( questId ) == 0 )
{
auto questAccept = exdData.get< Data::QuestAcceptAdditionCondition >( questId );
if( questAccept )
{
for( int32_t i = 0; i < 2; i++ )
{
if( ( &questAccept->requirement0 )[ i ] >= 65536 )
{
if( !player.isActionLearned( 245 ) && !player.isQuestCompleted( ( &questAccept->requirement0 )[ i ] ) )
return false;
}
else
{
if( !player.isActionLearned( ( &questAccept->requirement0 )[ i ] ) )
return false;
}
}
}
}
return true;
}
bool Sapphire::World::Manager::MapMgr::isTripleTriadAvailable( Entity::Player& player, uint32_t tripleTriadId )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto tripleTriad = exdData.get< Data::TripleTriad >( tripleTriadId );
if( tripleTriad->previousQuestJoin == 1 )
{
for( int32_t i = 0; i < 3; i++ )
{
if( tripleTriad->previousQuest[ i ] == 0 )
continue;
if( !player.isQuestCompleted( tripleTriad->previousQuest[ i ] ) )
return false;
}
}
else if( tripleTriad->previousQuestJoin == 2 )
{
for( int32_t i = 0; i < 3; i++ )
{
if( tripleTriad->previousQuest[ i ] == 0 )
continue;
if( player.isQuestCompleted( tripleTriad->previousQuest[ i ] ) )
break;
if( i == 2 )
return false;
}
}
return true;
}
void Sapphire::World::Manager::MapMgr::fillPacket( EventSet& mapData, uint32_t* iconIds, uint32_t* levelIds, uint32_t* eventIds )
{
int32_t i = 0;
for( auto& eventData : mapData )
{
iconIds[ i ] = eventData.iconId;
levelIds[ i ] = eventData.levelId;
eventIds[ i ] = eventData.eventId;
i++;
}
}
void Sapphire::World::Manager::MapMgr::sendPackets( Entity::Player& player, EventSet& mapData, UpdateMode updateMode )
{
player.queuePacket( makeActorControlSelf( player.getId(), Network::ActorControl::BeginMapUpdate, updateMode ) );
if( mapData.size() <= 2 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
else if( mapData.size() <= 4 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate4 >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
else if( mapData.size() <= 8 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate8 >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
else if( mapData.size() <= 16 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate16 >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
else if( mapData.size() <= 32 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate32 >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
else if( mapData.size() <= 64 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate64 >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
else if( mapData.size() <= 128 )
{
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate128 >( player.getId() );
mapUpdatePacket->data().entryCount = mapData.size();
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
player.queuePacket( mapUpdatePacket );
}
player.queuePacket( makeActorControlSelf( player.getId(), Network::ActorControl::FinishMapUpdate ) );
}

View file

@ -0,0 +1,77 @@
#ifndef SAPPHIRE_MAPMGR_H
#define SAPPHIRE_MAPMGR_H
#include "ForwardsZone.h"
#include <set>
#include <unordered_map>
namespace Sapphire::World::Manager
{
using QuestMap = std::unordered_map< uint32_t, Data::ExdDataGenerated::QuestPtr >;
class MapMgr
{
public:
enum UpdateMode : uint8_t
{
Quest = 1,
GuildLeveAssignment = 2,
GuildOrderGuide = 4,
TripleTriad = 8,
CustomTalk = 16,
PreHandler = 32,
All = 0x3F
};
MapMgr();
void updateAll( Entity::Player& player );
void updateQuests( Entity::Player& player );
private:
struct EventData
{
uint32_t iconId;
uint32_t levelId;
uint32_t eventId;
};
struct less
{
constexpr bool operator()( const EventData& _Left, const EventData& _Right ) const
{
const uint16_t left = _Left.eventId;
const uint16_t right = _Right.eventId;
if( left == right )
{
const uint16_t typeLeft = _Left.eventId >> 16;
const uint16_t typeRight = _Right.eventId >> 16;
return typeLeft < typeRight;
}
return left < right;
}
};
using EventSet = std::multiset< EventData, less >;
QuestMap m_quests;
void insertQuest( Entity::Player& player, uint32_t questId, EventSet& mapData );
bool isQuestVisible( Entity::Player& player, uint32_t questId, Data::ExdDataGenerated::QuestPtr questPtr );
bool isQuestAvailable( Entity::Player& player, uint32_t questId, Data::ExdDataGenerated::QuestPtr questPtr );
bool isTripleTriadAvailable( Entity::Player& player, uint32_t tripleTriadId );
void fillPacket( EventSet& mapData, uint32_t* iconIds, uint32_t* levelIds, uint32_t* eventIds );
void sendPackets( Entity::Player& player, EventSet& mapData, UpdateMode updateMode );
};
}
#endif // SAPPHIRE_MAPMGR_H

View file

@ -13,6 +13,7 @@
#include "Territory/ZonePosition.h"
#include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "TerritoryMgr.h"
#include "HousingMgr.h"
@ -301,7 +302,28 @@ Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createQuestBattle
if( !pQuestBattleInfo )
return nullptr;
auto pQuestInfo = exdData.get< Sapphire::Data::Quest >( pQuestBattleInfo->quest );
auto eventId = pQuestBattleInfo->quest;
auto eventType = static_cast< Event::EventHandler::EventHandlerType >( eventId >> 16 );
switch( eventType )
{
case Event::EventHandler::EventHandlerType::Array:
{
auto eventArray = exdData.get< Sapphire::Data::ArrayEventHandler >( eventId );
if( eventArray )
{
for( int i = 0; i < eventArray->data.size(); i++ )
{
auto nextId = eventArray->data[ i ];
if( nextId == 0 )
break;
eventId = nextId;
}
}
break;
}
}
auto pQuestInfo = exdData.get< Sapphire::Data::Quest >( eventId );
if( !pQuestInfo )
return nullptr;
@ -316,7 +338,7 @@ Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createQuestBattle
Logger::debug( "Starting instance for QuestBattle id: {0} ({1})", questBattleId, pQuestInfo->name );
auto pZone = make_QuestBattle( pQuestBattleInfo, pContentFinderCondition->territoryType, getNextInstanceId(),
pTeri->name, pQuestInfo->name, questBattleId );
pTeri->name, pQuestInfo->name, questBattleId, contentFinderConditionId );
pZone->init();
m_questBattleIdToInstanceMap[ questBattleId ][ pZone->getGuId() ] = pZone;
@ -350,7 +372,7 @@ Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createInstanceCon
Logger::debug( "Starting instance for InstanceContent id: {0} ({1})", instanceContentId, pContentFinderCondition->name );
auto pZone = make_InstanceContent( pInstanceContent, pContentFinderCondition->territoryType, getNextInstanceId(),
pTeri->name, pContentFinderCondition->name, instanceContentId );
pTeri->name, pContentFinderCondition->name, instanceContentId, contentFinderConditionId );
pZone->init();
m_instanceContentIdToInstanceMap[ instanceContentId ][ pZone->getGuId() ] = pZone;
@ -360,6 +382,68 @@ Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createInstanceCon
return pZone;
}
Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createPublicContent( uint32_t contentFinderConditionId )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto pContentFinderCondition = exdData.get< Sapphire::Data::ContentFinderCondition >( contentFinderConditionId );
if( !pContentFinderCondition )
return nullptr;
auto contentId = pContentFinderCondition->content;
auto pPublicContent = exdData.get< Sapphire::Data::PublicContent >( contentId );
if( !pPublicContent )
return nullptr;
auto pTeri = getTerritoryDetail( pContentFinderCondition->territoryType );
if( !pTeri || pContentFinderCondition->name.empty() )
return nullptr;
Logger::debug( "Starting instance for PublicContent id: {0} ({1})", contentId, pContentFinderCondition->name );
auto pZone = make_PublicContent( pPublicContent, pContentFinderCondition->territoryType, getNextInstanceId(),
pTeri->name, pContentFinderCondition->name, contentId, contentFinderConditionId );
pZone->init();
m_publicContentIdToInstanceMap[ contentId ][ pZone->getGuId() ] = pZone;
m_guIdToTerritoryPtrMap[ pZone->getGuId() ] = pZone;
m_instanceZoneSet.insert( pZone );
return pZone;
}
Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createPublicContent( uint16_t contentId, uint16_t territoryId )
{
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto pPublicContent = exdData.get< Sapphire::Data::PublicContent >( contentId );
if( !pPublicContent )
return nullptr;
if( pPublicContent->contentFinderCondition > 0 )
{
Logger::warn( "the public content {} has a ContentFinderCondition value of {}, create the instance using it instead.", contentId, pPublicContent->contentFinderCondition );
return nullptr;
}
auto pTeri = getTerritoryDetail( territoryId );
if( !pTeri )
return nullptr;
Logger::debug( "Starting instance for PublicContent id: {0} ({1})", contentId, pPublicContent->name );
auto pZone = make_PublicContent( pPublicContent, territoryId, getNextInstanceId(), pTeri->name, pPublicContent->name, contentId, 0 );
pZone->init();
m_publicContentIdToInstanceMap[ contentId ][ pZone->getGuId() ] = pZone;
m_guIdToTerritoryPtrMap[ pZone->getGuId() ] = pZone;
m_instanceZoneSet.insert( pZone );
return pZone;
}
Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::findOrCreateHousingInterior( const Common::LandIdent landIdent )
{
// check if zone already spawned first
@ -444,11 +528,21 @@ bool Sapphire::World::Manager::TerritoryMgr::removeTerritoryInstance( uint32_t g
m_instanceZoneSet.erase( pZone );
m_territorySet.erase( pZone );
if( isInstanceContentTerritory( pZone->getTerritoryTypeId() ) )
if( pZone->getAsInstanceContent() )
{
auto instance = std::dynamic_pointer_cast< InstanceContent >( pZone );
m_instanceContentIdToInstanceMap[ instance->getInstanceContentId() ].erase( pZone->getGuId() );
}
else if( pZone->getAsPublicContent() )
{
auto instance = std::dynamic_pointer_cast< PublicContent >( pZone );
m_publicContentIdToInstanceMap[ instance->getContentId() ].erase( pZone->getGuId() );
}
else if( pZone->getAsQuestBattle() )
{
auto instance = std::dynamic_pointer_cast< QuestBattle >( pZone );
m_questBattleIdToInstanceMap[ instance->getQuestBattleId() ].erase( pZone->getGuId() );
}
else
m_territoryTypeIdToInstanceGuidMap[ pZone->getTerritoryTypeId() ].erase( pZone->getGuId() );
@ -540,6 +634,7 @@ void Sapphire::World::Manager::TerritoryMgr::updateTerritoryInstances( uint64_t
// remove zone from maps
m_territorySet.erase( zone );
m_guIdToTerritoryPtrMap.erase( zone->getGuId() );
it = m_landIdentToTerritoryPtrMap.erase( it );
}
else
@ -615,7 +710,7 @@ bool Sapphire::World::Manager::TerritoryMgr::movePlayer( TerritoryPtr pZone, Sap
if( pHousing )
pPlayer->setTerritoryId( pHousing->getLandSetId() );
}
else if( isInstanceContentTerritory( pZone->getTerritoryTypeId() ) )
else if( pZone->getAsInstanceContent() || pZone->getAsQuestBattle() || pZone->getAsPublicContent() )
{
pPlayer->setTerritoryId( pZone->getGuId() );
}
@ -627,14 +722,27 @@ bool Sapphire::World::Manager::TerritoryMgr::movePlayer( TerritoryPtr pZone, Sap
// mark character as zoning in progress
pPlayer->setLoadingComplete( false );
bool zoneChanged = true;
if( pPlayer->getLastPing() != 0 && pPlayer->getCurrentTerritory() )
pPlayer->getCurrentTerritory()->removeActor( pPlayer );
{
zoneChanged = pPlayer->getCurrentTerritory()->getGuId() != pZone->getGuId();
if( zoneChanged )
pPlayer->getCurrentTerritory()->removeActor( pPlayer );
}
pPlayer->setCurrentZone( pZone );
pZone->pushActor( pPlayer );
if( zoneChanged )
{
pPlayer->setCurrentZone( pZone );
pZone->pushActor( pPlayer );
// map player to instanceId so it can be tracked.
m_playerIdToInstanceMap[ pPlayer->getId() ] = pZone->getGuId();
// map player to instanceId so it can be tracked.
m_playerIdToInstanceMap[ pPlayer->getId() ] = pZone->getGuId();
}
else
{
pPlayer->removeFromInRange();
pPlayer->clearInRangeSet();
}
pPlayer->sendZonePackets();

View file

@ -118,6 +118,9 @@ namespace Sapphire::World::Manager
TerritoryPtr createQuestBattle( uint32_t contentFinderConditionId );
TerritoryPtr createPublicContent( uint32_t contentFinderConditionId );
TerritoryPtr createPublicContent( uint16_t contentId, uint16_t territoryId );
void createAndJoinQuestBattle( Entity::Player& player, uint16_t contentFinderConditionId );
TerritoryPtr findOrCreateHousingInterior( const Common::LandIdent landIdent );
@ -179,6 +182,7 @@ namespace Sapphire::World::Manager
using InstanceContentIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >;
using QuestBattleIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >;
using QuestBattleIdToContentFinderCondMap = std::unordered_map< uint16_t, uint16_t >;
using PublicContentIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >;
using PlayerIdToInstanceIdMap = std::unordered_map< uint32_t, uint32_t >;
using PositionMap = std::unordered_map< int32_t, ZonePositionPtr >;
using InstanceIdList = std::vector< uint32_t >;
@ -196,9 +200,12 @@ namespace Sapphire::World::Manager
/*! map holding actual instances of InstanceContent */
InstanceContentIdToInstanceMap m_instanceContentIdToInstanceMap;
/*! map holding actual instances of InstanceContent */
/*! map holding actual instances of QuestBattle */
QuestBattleIdToInstanceMap m_questBattleIdToInstanceMap;
/*! map holding actual instances of PublicContent */
PublicContentIdToInstanceMap m_publicContentIdToInstanceMap;
/*! flat map for easier lookup of instances by guid */
InstanceIdToTerritoryPtrMap m_guIdToTerritoryPtrMap;

View file

@ -90,6 +90,8 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::ReqPlaceHousingItem, "ReqPlaceHousingItem", &GameConnection::reqPlaceHousingItem );
setZoneHandler( ClientZoneIpcType::HousingUpdateObjectPosition, "HousingUpdateObjectPosition",
&GameConnection::reqMoveHousingItem );
setZoneHandler( ClientZoneIpcType::HousingEditExterior, "HousingEditExterior", &GameConnection::housingEditExterior );
setZoneHandler( ClientZoneIpcType::HousingEditInterior, "HousingEditInterior", &GameConnection::housingEditInterior );
setZoneHandler( ClientZoneIpcType::TalkEventHandler, "EventHandlerTalk", &GameConnection::eventHandlerTalk );
setZoneHandler( ClientZoneIpcType::EmoteEventHandler, "EventHandlerEmote", &GameConnection::eventHandlerEmote );
@ -100,10 +102,12 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::EnterTeriEventHandler, "EventHandlerEnterTeri",
&GameConnection::eventHandlerEnterTerritory );
setZoneHandler( ClientZoneIpcType::ReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler, "EventHandlerReturn",
setZoneHandler( ClientZoneIpcType::ReturnEventHandler, "ReturnEventHandler", &GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler, "TradeReturnEventHandler",
&GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::TradeMultipleReturnEventHander, "EventHandlerReturn", &GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler2, "TradeReturnEventHandler2", &GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::EventYield2Handler, "EventYield2Handler", &GameConnection::eventYieldHandler );
setZoneHandler( ClientZoneIpcType::EventYield16Handler, "EventYield16Handler", &GameConnection::eventYieldHandler );
setZoneHandler( ClientZoneIpcType::ShopEventHandler, "ShopEventHandler",
&GameConnection::eventHandlerShop );
@ -118,7 +122,7 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::CFRegisterDuty, "CFRegisterDuty", &GameConnection::cfRegisterDuty );
setZoneHandler( ClientZoneIpcType::CFRegisterRoulette, "CFRegisterRoulette", &GameConnection::cfRegisterRoulette );
setZoneHandler( ClientZoneIpcType::CFCommenceHandler, "CFDutyAccepted", &GameConnection::cfDutyAccepted );
setZoneHandler( ClientZoneIpcType::CFCancelHandler, "CFCancel", &GameConnection::cfCancel );
//setZoneHandler( ClientZoneIpcType::CFCancelHandler, "CFCancel", &GameConnection::cfCancel );
setZoneHandler( ClientZoneIpcType::ReqEquipDisplayFlagsChange, "ReqEquipDisplayFlagsChange",
&GameConnection::reqEquipDisplayFlagsHandler );

View file

@ -181,6 +181,10 @@ namespace Sapphire::Network
DECLARE_HANDLER( reqMoveHousingItem );
DECLARE_HANDLER( housingEditExterior );
DECLARE_HANDLER( housingEditInterior );
DECLARE_HANDLER( marketBoardSearch );
DECLARE_HANDLER( marketBoardRequestItemInfo );
@ -190,6 +194,8 @@ namespace Sapphire::Network
DECLARE_HANDLER( worldInteractionhandler );
DECLARE_HANDLER( diveHandler );
DECLARE_HANDLER( eventYieldHandler );
};
}

View file

@ -12,6 +12,7 @@
#include "Network/GameConnection.h"
#include "Network/PacketWrappers/ServerNoticePacket.h"
#include "Network/PacketWrappers/PlayerStateFlagsPacket.h"
#include "Network/PacketDef/Zone/ClientZoneDef.h"
#include "Session.h"
@ -43,9 +44,6 @@ void Sapphire::Network::GameConnection::cfRegisterDuty( const Packets::FFXIVARR_
Entity::Player& player )
{
Packets::FFXIVARR_PACKET_RAW copy = inPacket;
auto& teriMgr = Common::Service< TerritoryMgr >::ref();
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
std::vector< uint16_t > selectedContent;
for( uint32_t offset = 0x1E; offset <= 0x26; offset += 0x2 )
@ -54,70 +52,75 @@ void Sapphire::Network::GameConnection::cfRegisterDuty( const Packets::FFXIVARR_
if( id == 0 )
break;
player.sendDebug( "got contentId#{0}", id );
player.sendDebug( "got contentFinderConditionId#{0}", id );
selectedContent.push_back( id );
}
// todo: rand bias problem, will do for now tho
auto index = static_cast< uint32_t >( std::rand() ) % selectedContent.size();
auto contentId = selectedContent.at( index );
auto contentFinderConditionId = selectedContent.at( index );
player.sendDebug( "Duty register request for contentid#{0}", contentId );
// let's cancel it because otherwise you can't register it again
/*
auto cfCancelPacket = makeZonePacket< FFXIVIpcCFNotify >( player.getId() );
cfCancelPacket->data().state1 = 3;
cfCancelPacket->data().state2 = 1; // Your registration is withdrawn.
queueOutPacket( cfCancelPacket );
*/
auto packet = makeZonePacket< FFXIVIpcCFCancel >( player.getId() );
packet->data().cancelReason = 890;
queueOutPacket( packet );
auto cfCondition = exdData.get< Sapphire::Data::ContentFinderCondition >( contentId );
if( !cfCondition )
return;
auto instance = teriMgr.createInstanceContent( cfCondition->content );
if( !instance )
return;
auto pInstance = instance->getAsInstanceContent();
pInstance->bindPlayer( player.getId() );
player.sendDebug( "Created instance with id#", instance->getGuId() );
player.setInstance( instance );
player.sendDebug( "Duty register request for contentFinderConditionId#{0}", contentFinderConditionId );
player.m_cfNotifiedContent = contentFinderConditionId;
auto notify = makeZonePacket< FFXIVIpcCFNotify >( player.getId() );
notify->data().state1 = 8195;
notify->data().param3 = 1;
notify->data().param4 = contentFinderConditionId;
player.queuePacket( notify );
}
void Sapphire::Network::GameConnection::cfRegisterRoulette( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player )
{
/*
auto cfCancelPacket = makeZonePacket< FFXIVIpcCFNotify >( player.getId() );
cfCancelPacket->data().state1 = 3;
cfCancelPacket->data().state2 = 1; // Your registration is withdrawn.
queueOutPacket( cfCancelPacket );
*/
auto packet = makeZonePacket< FFXIVIpcCFCancel >( player.getId() );
packet->data().cancelReason = 890;
queueOutPacket( packet );
player.sendDebug( "Roulette register" );
player.sendDebug( "Roulette register not implemented." );
}
void Sapphire::Network::GameConnection::cfDutyAccepted( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player )
{
player.sendDebug( "TODO: Duty accept" );
}
const auto packetIn = ZoneChannelPacket< Client::FFXIVIpcCFCommenceHandler >( inPacket );
if( packetIn.data().param == 0 )
{
// accept
if( player.m_cfNotifiedContent > 0 )
{
auto& teriMgr = Common::Service< TerritoryMgr >::ref();
auto instance = teriMgr.createInstanceContent( player.m_cfNotifiedContent );
if( !instance )
return;
player.m_cfNotifiedContent = 0;
auto pInstance = instance->getAsInstanceContent();
pInstance->bindPlayer( player.getId() );
player.sendDebug( "Created instance with id#{}", instance->getGuId() );
player.prepareZoning( pInstance->getTerritoryTypeId(), true, 1, 0, 0, 1, 9 );
player.setInstance( instance );
}
}
else
{
// cancel
player.m_cfNotifiedContent = 0;
auto packet = makeZonePacket< FFXIVIpcCFCancel >( player.getId() );
packet->data().cancelReason = 890;
queueOutPacket( packet );
}
}
/*
void Sapphire::Network::GameConnection::cfCancel( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player )
{
auto packet = makeZonePacket< FFXIVIpcCFCancel >( player.getId() );
packet->data().cancelReason = 890;
queueOutPacket( packet );
}
}
*/

View file

@ -475,6 +475,14 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
break;
}
case ClientTriggerType::RequestEstateHallRemoval:
{
auto& housingMgr = Common::Service< HousingMgr >::ref();
housingMgr.removeHouse( player, static_cast< uint16_t >( param11 ) );
break;
}
case ClientTriggerType::UpdateEstateGuestAccess:
{
auto canTeleport = ( param2 & 0xFF ) == 1;
@ -494,7 +502,27 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
player.sendDebug( "event battle p1: {0}, p11: {1}, p12: {2}, p2: {3}, p3: {4}, p4: {5}, p5: {6}", param1, param11, param12, param2, param3, param4, param5 );
break;
}
case ClientTriggerType::CutscenePlayed:
{
player.sendDebug( "cutscene: {}", param1 );
break;
}
case ClientTriggerType::OpenPerformInstrumentUI:
{
//param11 = instrument, 0 = end
player.sendDebug( "perform: {}", param11 );
if( param11 == 0 )
{
player.sendToInRangeSet( makeActorControl( player.getId(), ActorControl::SetStatus, 1, 0, 0, 0 ), true );
player.unsetStateFlag( PlayerStateFlag::Performing );
}
else
{
player.sendToInRangeSet( makeActorControl( player.getId(), ActorControl::SetStatus, 16, param11, 0, 0 ), true );
player.setStateFlag( PlayerStateFlag::Performing );
}
break;
}
default:
{
Logger::debug( "[{0}] Unhandled action: {1:04X}", m_pSession->getId(), commandId );

View file

@ -23,6 +23,7 @@
#include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Session.h"
@ -59,6 +60,14 @@ void Sapphire::Network::GameConnection::eventHandlerTalk( const Packets::FFXIVAR
{
instance->onTalk( player, eventId, actorId );
}
else if( auto instance = player.getCurrentQuestBattle() )
{
instance->onTalk( player, eventId, actorId );
}
else if( auto instance = player.getCurrentPublicContent() )
{
instance->onTalk( player, eventId, actorId );
}
else if( !scriptMgr.onTalk( player, actorId, eventId ) &&
eventType == Event::EventHandler::EventHandlerType::Quest )
{
@ -180,6 +189,11 @@ void Sapphire::Network::GameConnection::eventHandlerEnterTerritory( const Packet
player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 1, player.getZoneId() );
instance->onEnterTerritory( player, eventId, param1, param2 );
}
else if( auto instance = player.getCurrentPublicContent() )
{
player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 1, player.getZoneId() );
instance->onEnterTerritory( player, eventId, param1, param2 );
}
else
{
player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 0, player.getZoneId() );
@ -273,5 +287,39 @@ void Sapphire::Network::GameConnection::eventHandlerShop( const Packets::FFXIVAR
scriptMgr.onTalk( player, player.getId(), eventId );
}
void Sapphire::Network::GameConnection::eventYieldHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
auto& eventMgr = Common::Service< World::Manager::EventMgr >::ref();
auto opcode = *reinterpret_cast< const uint16_t* >( &inPacket.data[ 2 ] );
auto eventId = *reinterpret_cast< const uint32_t* >( &inPacket.data[ 0x10 + 0 ] );
auto scene = *reinterpret_cast< const uint16_t* >( &inPacket.data[ 0x10 + 4 ] );
auto pParam = reinterpret_cast< const uint32_t* >( &inPacket.data[ 0x10 + 8 ] );
std::vector< uint32_t > param;
switch( opcode )
{
case EventYield16Handler:
{
for( int i = 0; i < 16; i++ )
{
param.push_back( pParam[ i ] );
}
break;
}
}
std::string eventName = "onEventYield";
std::string objName = eventMgr.getEventName( eventId );
player.sendDebug( "Calling: {0}.{1} - {2} scene: {3}", objName, eventName, eventId, scene );
scriptMgr.onEventYield( player, eventId, scene, param );
auto response = makeZonePacket< FFXIVIpcEventContinue >( player.getId() );
response->data().eventId = eventId;
response->data().scene = scene;
player.queuePacket( response );
}

View file

@ -21,6 +21,7 @@
#include "Territory/Territory.h"
#include "Territory/HousingZone.h"
#include "Territory/Housing/HousingInteriorTerritory.h"
#include "Territory/Land.h"
#include "Territory/ZonePosition.h"
#include "Territory/House.h"
@ -386,15 +387,18 @@ void Sapphire::Network::GameConnection::pingHandler( const Packets::FFXIVARR_PAC
void Sapphire::Network::GameConnection::finishLoadingHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player )
{
player.sendQuestInfo();
if( player.isLogin() )
{
player.sendQuestInfo();
// TODO: load and save this data instead of hardcoding
auto gcPacket = makeZonePacket< FFXIVGCAffiliation >( player.getId() );
gcPacket->data().gcId = player.getGc();
gcPacket->data().gcRank[ 0 ] = player.getGcRankArray()[ 0 ];
gcPacket->data().gcRank[ 1 ] = player.getGcRankArray()[ 1 ];
gcPacket->data().gcRank[ 2 ] = player.getGcRankArray()[ 2 ];
player.queuePacket( gcPacket );
// TODO: load and save this data instead of hardcoding
auto gcPacket = makeZonePacket< FFXIVGCAffiliation >( player.getId() );
gcPacket->data().gcId = player.getGc();
gcPacket->data().gcRank[ 0 ] = player.getGcRankArray()[ 0 ];
gcPacket->data().gcRank[ 1 ] = player.getGcRankArray()[ 1 ];
gcPacket->data().gcRank[ 2 ] = player.getGcRankArray()[ 2 ];
player.queuePacket( gcPacket );
}
player.getCurrentTerritory()->onFinishLoading( player );
@ -607,7 +611,7 @@ void Sapphire::Network::GameConnection::performNoteHandler( const Packets::FFXIV
Entity::Player& player )
{
auto performPacket = makeZonePacket< FFXIVIpcPerformNote >( player.getId() );
memcpy( &performPacket->data().data[ 0 ], &inPacket.data[ 0x10 ], 32 );
memcpy( &performPacket->data().data[ 0 ], &inPacket.data[ 0x10 ], 16 );
player.sendToInRangeSet( performPacket );
}
@ -691,6 +695,46 @@ void Sapphire::Network::GameConnection::reqMoveHousingItem( const Packets::FFXIV
housingMgr.reqMoveHousingItem( player, data.ident, data.slot, data.pos, data.rotation );
}
void Sapphire::Network::GameConnection::housingEditExterior( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
auto& housingMgr = Common::Service< HousingMgr >::ref();
const auto packet = ZoneChannelPacket< Client::FFXIVIpcHousingEditExterior >( inPacket );
auto terri = std::dynamic_pointer_cast< HousingZone >( player.getCurrentTerritory() );
if( !terri )
return;
std::vector< uint16_t > containerList;
std::vector< uint8_t > slotList;
for( int i = 0; i < 9; i++ )
{
auto container = packet.data().container[i];
containerList.push_back( container );
slotList.push_back( container != 0x270F ? static_cast< uint8_t >( packet.data().slot[i] ) : 0xFF );
}
housingMgr.editAppearance( false, player, terri->getLand( packet.data().landId )->getLandIdent(), containerList, slotList, packet.data().removeFlag );
}
void Sapphire::Network::GameConnection::housingEditInterior( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
auto& housingMgr = Common::Service< HousingMgr >::ref();
const auto packet = ZoneChannelPacket< Client::FFXIVIpcHousingEditInterior >( inPacket );
auto terri = std::dynamic_pointer_cast< World::Territory::Housing::HousingInteriorTerritory >( player.getCurrentTerritory() );
if( !terri )
return;
std::vector< uint16_t > containerList;
std::vector< uint8_t > slotList;
for( int i = 0; i < 10; i++ )
{
auto container = packet.data().container[i];
containerList.push_back( container );
slotList.push_back( container != 0x270F ? static_cast< uint8_t >( packet.data().slot[i] ) : 0xFF );
}
housingMgr.editAppearance( true, player, terri->getLandIdent(), containerList, slotList, 0 );
}
void Sapphire::Network::GameConnection::marketBoardSearch( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player )
{
@ -746,12 +790,12 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets::
break;
player.setPos( packet.data().position );
player.setRot( Util::floatFromUInt16Rot( param4 ) );
if( emote == 0x32 && player.hasInRangeActor() )
{
auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() );
setpos->data().r16 = param4;
setpos->data().waitForLoad = 18;
setpos->data().unknown1 = 1;
setpos->data().x = packet.data().position.x;
setpos->data().y = packet.data().position.y;
setpos->data().z = packet.data().position.z;
@ -781,7 +825,6 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets::
auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() );
setpos->data().r16 = param2;
setpos->data().waitForLoad = 18;
setpos->data().unknown1 = 2;
setpos->data().x = packet.data().position.x;
setpos->data().y = packet.data().position.y;
setpos->data().z = packet.data().position.z;

View file

@ -22,15 +22,15 @@ namespace Sapphire::Network::Packets::Server
uint32_t param3 = 0,
uint32_t param4 = 0,
uint32_t param5 = 0,
uint32_t padding1 = 0 ) :
uint32_t param6 = 0 ) :
ZoneChannelPacket< FFXIVIpcActorControlSelf >( actorId, actorId )
{
initialize( category, param1, param2, param3, param4, param5 );
initialize( category, param1, param2, param3, param4, param5, param6 );
};
private:
void initialize( uint16_t category, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4,
uint32_t param5 )
uint32_t param5, uint32_t param6 )
{
m_data.padding = 0;
m_data.category = category;
@ -39,6 +39,7 @@ namespace Sapphire::Network::Packets::Server
m_data.param3 = param3;
m_data.param4 = param4;
m_data.param5 = param5;
m_data.param6 = param6;
};
};

View file

@ -53,7 +53,7 @@ namespace Sapphire::Network::Packets::Server
memcpy( m_data.aetheryte, player.getAetheryteArray(), sizeof( m_data.aetheryte ) );
// Set the class levels and exp.
for( uint8_t i = 0; i < 25; i++ )
for( uint8_t i = 0; i < Common::CLASSJOB_SLOTS; i++ )
{
m_data.levels[ i ] = player.getClassArray()[ i ];
m_data.exp[ i ] = player.getExpArray()[ i ];

View file

@ -8,6 +8,8 @@
#include "Forwards.h"
#include "Inventory/Item.h"
#include "StatusEffect/StatusEffect.h"
#include "Territory/Territory.h"
#include "Event/Director.h"
namespace Sapphire::Network::Packets::Server
{
@ -143,6 +145,10 @@ namespace Sapphire::Network::Packets::Server
m_data.effect[ effect.first ].param = effect.second->getParam();
}
if( auto d = player.getCurrentTerritory()->getAsDirector() )
{
m_data.directorId = d->getDirectorId();
}
};
};

View file

@ -1,7 +1,6 @@
#include <string>
#include <typeinfo>
#include <typeindex>
#include <Event/EventHandler.h>
#include "NativeScriptApi.h"
#include <cassert>
@ -134,6 +133,15 @@ namespace Sapphire::ScriptAPI
{
}
void EventScript::onEventYield( Sapphire::Entity::Player& player, uint16_t scene, std::vector< uint32_t > param )
{
}
Event::EventHandler::QuestAvailability EventScript::getQuestAvailability( Sapphire::Entity::Player& player, uint32_t eventId )
{
return Event::EventHandler::QuestAvailability::Available;
}
///////////////////////////////////////////////////////////////////
EventObjectScript::EventObjectScript( uint32_t eobjId ) :
@ -216,5 +224,31 @@ namespace Sapphire::ScriptAPI
{
}
PublicContentScript::PublicContentScript( uint32_t contentId ) :
ScriptObject( uint32_t{ 0x8004 } << 16 | contentId, typeid( PublicContentScript ).hash_code() )
{
}
void PublicContentScript::onInit( PublicContent& instance )
{
}
void PublicContentScript::onUpdate( PublicContent& instance, uint64_t tickCount )
{
}
void PublicContentScript::onPlayerZoneIn( PublicContent& instance, Entity::Player& player )
{
}
void PublicContentScript::onLeaveTerritory( PublicContent& instance, Entity::Player& player )
{
}
void PublicContentScript::onEnterTerritory( PublicContent& instance, Entity::Player& player, uint32_t eventId,
uint16_t param1, uint16_t param2 )
{
}
}

View file

@ -2,6 +2,7 @@
#define NATIVE_SCRIPT_API
#include <string>
#include <Event/EventHandler.h>
#include "ForwardsZone.h"
#ifdef _MSC_VER
@ -166,6 +167,10 @@ namespace Sapphire::ScriptAPI
uint32_t catalogId );
virtual void onEObjHit( Sapphire::Entity::Player& player, uint64_t actorId, uint32_t actionId );
virtual void onEventYield( Sapphire::Entity::Player& player, uint16_t scene, std::vector< uint32_t > param );
virtual Event::EventHandler::QuestAvailability getQuestAvailability( Sapphire::Entity::Player& player, uint32_t eventId );
};
/*!
@ -237,6 +242,23 @@ namespace Sapphire::ScriptAPI
uint16_t param1, uint16_t param2 );
};
class PublicContentScript : public ScriptObject
{
public:
explicit PublicContentScript( uint32_t contentId );
virtual void onInit( Sapphire::PublicContent& instance );
virtual void onUpdate( Sapphire::PublicContent& instance, uint64_t tickCount );
virtual void onPlayerZoneIn( Sapphire::PublicContent& instance, Sapphire::Entity::Player& player );
virtual void onLeaveTerritory( Sapphire::PublicContent& instance, Sapphire::Entity::Player& player );
virtual void onEnterTerritory( Sapphire::PublicContent& instance, Sapphire::Entity::Player& player, uint32_t eventId,
uint16_t param1, uint16_t param2 );
};
}
#endif

View file

@ -7,6 +7,7 @@
#include "Territory/Territory.h"
#include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Actor/Player.h"
#include "Actor/EventObject.h"
#include "ServerMgr.h"
@ -530,8 +531,7 @@ Sapphire::Scripting::NativeScriptMgr& Sapphire::Scripting::ScriptMgr::getNativeS
return *m_nativeScriptMgr;
}
bool
Sapphire::Scripting::ScriptMgr::onDutyComplete( Sapphire::QuestBattlePtr instance, Sapphire::Entity::Player& player )
bool Sapphire::Scripting::ScriptMgr::onDutyComplete( Sapphire::QuestBattlePtr instance, Sapphire::Entity::Player& player )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::QuestBattleScript >( instance->getDirectorId() );
if( script )
@ -542,3 +542,77 @@ Sapphire::Scripting::ScriptMgr::onDutyComplete( Sapphire::QuestBattlePtr instanc
return false;
}
bool Sapphire::Scripting::ScriptMgr::onEventYield( Sapphire::Entity::Player& player, uint32_t eventId, uint16_t scene, std::vector< uint32_t > param )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::EventScript >( eventId );
if( script )
{
script->onEventYield( player, scene, param );
return true;
}
return false;
}
bool Sapphire::Scripting::ScriptMgr::onPublicContentInit( PublicContentPtr instance )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::PublicContentScript >( instance->getDirectorId() );
if( script )
{
script->onInit( *instance );
return true;
}
return false;
}
bool Sapphire::Scripting::ScriptMgr::onPublicContentUpdate( PublicContentPtr instance, uint64_t tickCount )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::PublicContentScript >( instance->getDirectorId() );
if( script )
{
script->onUpdate( *instance, tickCount );
return true;
}
return false;
}
bool Sapphire::Scripting::ScriptMgr::onPublicContentPlayerZoneIn( PublicContentPtr instance, Entity::Player& player )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::PublicContentScript >( instance->getDirectorId() );
if( script )
{
script->onPlayerZoneIn( *instance, player );
return true;
}
return false;
}
bool Sapphire::Scripting::ScriptMgr::onPublicContentLeaveTerritory( PublicContentPtr instance, Entity::Player& player )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::PublicContentScript >( instance->getDirectorId() );
if( script )
{
script->onLeaveTerritory( *instance, player );
return true;
}
return false;
}
bool Sapphire::Scripting::ScriptMgr::onPublicContentEnterTerritory( PublicContentPtr instance, Entity::Player& player,
uint32_t eventId, uint16_t param1, uint16_t param2 )
{
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::PublicContentScript >( instance->getDirectorId() );
if( script )
{
script->onEnterTerritory( *instance, player, eventId, param1, param2 );
return true;
}
return false;
}

View file

@ -112,6 +112,18 @@ namespace Sapphire::Scripting
bool onDutyComplete( QuestBattlePtr instance, Entity::Player& player );
bool onEventYield( Entity::Player& player, uint32_t eventId, uint16_t scene, std::vector< uint32_t > param );
bool onPublicContentInit( PublicContentPtr instance );
bool onPublicContentUpdate( PublicContentPtr instance, uint64_t tickCount );
bool onPublicContentPlayerZoneIn( PublicContentPtr instance, Entity::Player& player );
bool onPublicContentLeaveTerritory( PublicContentPtr instance, Entity::Player& player );
bool onPublicContentEnterTerritory( PublicContentPtr instance, Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 );
bool loadDir( const std::string& dirname, std::set< std::string >& files, const std::string& ext );
NativeScriptMgr& getNativeScriptHandler();

View file

@ -41,6 +41,7 @@
#include "Manager/RNGMgr.h"
#include "Manager/NaviMgr.h"
#include "Manager/ActionMgr.h"
#include "Manager/MapMgr.h"
#include "Territory/InstanceObjectCache.h"
@ -173,6 +174,10 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] )
auto pInstanceObjCache = std::make_shared< Sapphire::InstanceObjectCache >();
Common::Service< Sapphire::InstanceObjectCache >::set( pInstanceObjCache );
Logger::info( "MapMgr: Caching quests" );
auto pMapMgr = std::make_shared< Manager::MapMgr >();
Common::Service< Manager::MapMgr >::set( pMapMgr );
auto pActionMgr = std::make_shared< Manager::ActionMgr >();
Common::Service< Manager::ActionMgr >::set( pActionMgr );

View file

@ -64,6 +64,11 @@ Sapphire::House::ExteriorModelsArray const& Sapphire::House::getHouseModels() co
return m_exteriorModelCache;
}
void Sapphire::House::clearModelCache()
{
m_exteriorModelCache.fill( std::make_pair( 0, 0 ) );
}
const std::string& Sapphire::House::getHouseName() const
{
return m_estateName;

View file

@ -41,6 +41,8 @@ namespace Sapphire
ExteriorModelsArray const& getHouseModels() const;
void clearModelCache();
void updateHouseDb();
void setHasAetheryte( bool hasAetheryte );

View file

@ -321,6 +321,23 @@ Sapphire::Entity::EventObjectPtr Sapphire::HousingZone::registerEstateEntranceEO
return eObj;
}
void Sapphire::HousingZone::removeEstateEntranceEObj( uint8_t landId )
{
auto land = getLand( landId );
assert( land );
for( auto entry : m_eventObjects )
{
auto eObj = entry.second;
if( eObj->getHousingLink() == landId << 8 )
{
removeActor( eObj );
m_eventObjects.erase( entry.first );
break;
}
}
}
void Sapphire::HousingZone::updateYardObjects( Sapphire::Common::LandIdent ident )
{
auto& housingMgr = Common::Service< World::Manager::HousingMgr >::ref();

View file

@ -53,6 +53,7 @@ namespace Sapphire
Sapphire::LandPtr getLand( uint8_t id );
Entity::EventObjectPtr registerEstateEntranceEObj( uint8_t landId );
void removeEstateEntranceEObj( uint8_t landId );
void updateYardObjects( Common::LandIdent ident );
void spawnYardObject( uint8_t landId, uint16_t slotId, Sapphire::Inventory::HousingItem& item );

View file

@ -32,9 +32,9 @@ Sapphire::InstanceContent::InstanceContent( std::shared_ptr< Sapphire::Data::Ins
uint32_t guId,
const std::string& internalName,
const std::string& contentName,
uint32_t instanceContentId ) :
uint32_t instanceContentId, uint16_t contentFinderConditionId ) :
Territory( static_cast< uint16_t >( territoryType ), guId, internalName, contentName ),
Director( Event::Director::InstanceContent, instanceContentId ),
Director( Event::Director::InstanceContent, instanceContentId, contentFinderConditionId ),
m_instanceConfiguration( pInstanceConfiguration ),
m_instanceContentId( instanceContentId ),
m_state( Created ),
@ -80,11 +80,7 @@ void Sapphire::InstanceContent::onPlayerZoneIn( Entity::Player& player )
// mark player as "bound by duty"
player.setStateFlag( PlayerStateFlag::BoundByDuty );
// if the instance was not started yet, director init is sent on enter event.
// else it will be sent on finish loading.
if( m_state == Created )
sendDirectorInit( player );
sendDirectorInit( player );
}
void Sapphire::InstanceContent::onLeaveTerritory( Entity::Player& player )
@ -164,7 +160,6 @@ void Sapphire::InstanceContent::onUpdate( uint64_t tickCount )
void Sapphire::InstanceContent::onFinishLoading( Entity::Player& player )
{
sendDirectorInit( player );
}
void Sapphire::InstanceContent::onInitDirector( Entity::Player& player )
@ -310,6 +305,8 @@ void Sapphire::InstanceContent::onRegisterEObj( Entity::EventObjectPtr object )
m_eventObjectMap[ object->getName() ] = object;
if( object->getObjectId() == 2000182 ) // start
m_pEntranceEObj = object;
if( m_pEntranceEObj == nullptr && object->getName() == "Entrance" )
m_pEntranceEObj = object;
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto objData = exdData.get< Sapphire::Data::EObj >( object->getObjectId() );
@ -375,7 +372,7 @@ void Sapphire::InstanceContent::onTalk( Sapphire::Entity::Player& player, uint32
return;
if( auto onTalk = it->second->getOnTalkHandler() )
onTalk( player, it->second, getAsInstanceContent(), actorId );
onTalk( player, it->second, getAsInstanceContent(), eventId, actorId );
else
player.sendDebug( "No onTalk handler found for interactable eobj with EObjID#{0}, eventId#{1} ",
it->second->getObjectId(), eventId );

View file

@ -28,7 +28,7 @@ namespace Sapphire
uint32_t guId,
const std::string& internalName,
const std::string& contentName,
uint32_t instanceContentId );
uint32_t instanceContentId, uint16_t contentFinderConditionId = 0 );
virtual ~InstanceContent();

View file

@ -41,16 +41,23 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
// TODO: it does feel like this needs to be streamlined into the datReader instead of being done here...
std::string bgLgbPath( path + "/level/bg.lgb" );
std::string planmapLgbPath( path + "/level/planmap.lgb" );
std::string planeventLgbPath( path + "/level/planevent.lgb" );
std::string plannerLgbPath( path + "/level/planner.lgb" );
std::vector< char > bgSection;
std::vector< char > planmapSection;
std::vector< char > planeventSection;
std::vector< char > plannerSection;
std::unique_ptr< xiv::dat::File > bgFile;
std::unique_ptr< xiv::dat::File > planmap_file;
std::unique_ptr< xiv::dat::File > planevent_file;
std::unique_ptr< xiv::dat::File > planner_file;
try
{
bgFile = exdData.getGameData()->getFile( bgLgbPath );
planmap_file = exdData.getGameData()->getFile( planmapLgbPath );
planevent_file = exdData.getGameData()->getFile( planeventLgbPath );
}
catch( std::runtime_error& )
{
@ -60,6 +67,7 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
bgSection = bgFile->access_data_sections().at( 0 );
planmapSection = planmap_file->access_data_sections().at( 0 );
planeventSection = planevent_file->access_data_sections().at( 0 );
std::vector< std::string > stringList;
@ -67,8 +75,23 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
LGB_FILE bgLgb( &bgSection[ 0 ], "bg" );
LGB_FILE planmapLgb( &planmapSection[ 0 ], "planmap" );
LGB_FILE planeventLgb( &planeventSection[ 0 ], "planevent" );
std::vector< LGB_FILE > lgbList;
try
{
planner_file = exdData.getGameData()->getFile( plannerLgbPath );
plannerSection = planner_file->access_data_sections().at( 0 );
LGB_FILE plannerLgb( &plannerSection[ 0 ], "planner" );
lgbList = { bgLgb, planmapLgb, planeventLgb, plannerLgb };
}
catch( std::runtime_error& )
{
lgbList = { bgLgb, planmapLgb, planeventLgb };
}
std::vector< LGB_FILE > lgbList{ bgLgb, planmapLgb };
uint32_t max_index = 0;
for( const auto& lgb : lgbList )
@ -92,6 +115,16 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
auto pPopRange = std::reinterpret_pointer_cast< LGB_POP_RANGE_ENTRY >( pEntry );
m_popRangeCache.insert( id, pPopRange );
}
else if( pEntry->getType() == LgbEntryType::EventNpc )
{
auto pEventNpc = std::reinterpret_pointer_cast< LGB_ENPC_ENTRY >( pEntry );
m_eventNpcCache.insert( id, pEventNpc );
}
else if( pEntry->getType() == LgbEntryType::EventObject )
{
auto pEventObj = std::reinterpret_pointer_cast< LGB_EOBJ_ENTRY >( pEntry );
m_eventObjCache.insert( id, pEventObj );
}
}
}
}
@ -99,8 +132,8 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
std::cout << "\n";
Logger::debug(
"InstanceObjectCache Cached: MapRange: {} ExitRange: {} PopRange: {}",
m_mapRangeCache.size(), m_exitRangeCache.size(), m_popRangeCache.size()
"InstanceObjectCache Cached: MapRange: {} ExitRange: {} PopRange: {} ENpc: {} Eobj: {}",
m_mapRangeCache.size(), m_exitRangeCache.size(), m_popRangeCache.size(), m_eventNpcCache.size(), m_eventObjCache.size()
);
}
@ -121,4 +154,28 @@ Sapphire::InstanceObjectCache::PopRangePtr
Sapphire::InstanceObjectCache::getPopRange( uint16_t zoneId, uint32_t popRangeId )
{
return m_popRangeCache.get( zoneId, popRangeId );
}
Sapphire::InstanceObjectCache::EventNpcPtr
Sapphire::InstanceObjectCache::getEventNpc( uint16_t zoneId, uint32_t eventNpcId )
{
return m_eventNpcCache.get( zoneId, eventNpcId );
}
Sapphire::InstanceObjectCache::EventObjPtr
Sapphire::InstanceObjectCache::getEventObj( uint16_t zoneId, uint32_t eventObjId )
{
return m_eventObjCache.get( zoneId, eventObjId );
}
Sapphire::InstanceObjectCache::EventNpcMapPtr
Sapphire::InstanceObjectCache::getAllEventNpc( uint16_t zoneId )
{
return m_eventNpcCache.getAll( zoneId );
}
Sapphire::InstanceObjectCache::EventObjMapPtr
Sapphire::InstanceObjectCache::getAllEventObj( uint16_t zoneId )
{
return m_eventObjCache.getAll( zoneId );
}

View file

@ -7,6 +7,8 @@
struct LGB_MAP_RANGE_ENTRY;
struct LGB_EXIT_RANGE_ENTRY;
struct LGB_POP_RANGE_ENTRY;
struct LGB_ENPC_ENTRY;
struct LGB_EOBJ_ENTRY;
namespace Sapphire
@ -37,6 +39,16 @@ namespace Sapphire
}
return nullptr;
}
ObjectMap* getAll( uint16_t zoneId )
{
auto it = m_objectCache.find( zoneId );
if( it != m_objectCache.end() )
{
return &it->second;
}
return nullptr;
}
void insert( uint16_t zoneId, std::shared_ptr< T > entry )
{
@ -65,6 +77,11 @@ namespace Sapphire
using MapRangePtr = std::shared_ptr< LGB_MAP_RANGE_ENTRY >;
using ExitRangePtr = std::shared_ptr< LGB_EXIT_RANGE_ENTRY >;
using PopRangePtr = std::shared_ptr< LGB_POP_RANGE_ENTRY >;
using EventNpcPtr = std::shared_ptr< LGB_ENPC_ENTRY >;
using EventObjPtr = std::shared_ptr< LGB_EOBJ_ENTRY >;
using EventNpcMapPtr = std::unordered_map< uint32_t, EventNpcPtr >*;
using EventObjMapPtr = std::unordered_map< uint32_t, EventObjPtr >*;
InstanceObjectCache();
~InstanceObjectCache() = default;
@ -72,11 +89,18 @@ namespace Sapphire
MapRangePtr getMapRange( uint16_t zoneId, uint32_t mapRangeId );
ExitRangePtr getExitRange( uint16_t zoneId, uint32_t exitRangeId );
PopRangePtr getPopRange( uint16_t zoneId, uint32_t popRangeId );
EventNpcPtr getEventNpc( uint16_t zoneId, uint32_t eventNpcId );
EventObjPtr getEventObj( uint16_t zoneId, uint32_t eventObjId );
EventNpcMapPtr getAllEventNpc( uint16_t zoneId );
EventObjMapPtr getAllEventObj( uint16_t zoneId );
private:
ObjectCache< LGB_MAP_RANGE_ENTRY > m_mapRangeCache;
ObjectCache< LGB_EXIT_RANGE_ENTRY > m_exitRangeCache;
ObjectCache< LGB_POP_RANGE_ENTRY > m_popRangeCache;
ObjectCache< LGB_ENPC_ENTRY > m_eventNpcCache;
ObjectCache< LGB_EOBJ_ENTRY > m_eventObjCache;
std::shared_ptr< Framework > m_pFramework;
};

View file

@ -240,5 +240,20 @@ void Sapphire::Land::update( uint64_t tickCount )
Sapphire::Land::InvMaxItemsPair Sapphire::Land::getInventoryItemMax() const
{
return std::make_pair( m_maxPlacedExternalItems, m_maxPlacedInternalItems );
switch( m_size )
{
case HouseSize::Cottage:
{
return std::make_pair( 20, 200 );
}
case HouseSize::House:
{
return std::make_pair( 30, 300 );
}
case HouseSize::Mansion:
{
return std::make_pair( 40, 400 );
}
}
assert( false );
}

View file

@ -86,10 +86,6 @@ namespace Sapphire
Sapphire::HousePtr m_pHouse;
//item storage
uint16_t m_maxPlacedExternalItems;
uint16_t m_maxPlacedInternalItems;
//price
uint32_t m_initPrice;
uint32_t m_nextDrop;

View file

@ -0,0 +1,270 @@
#include <Common.h>
#include <Logging/Logger.h>
#include <Util/Util.h>
#include <Util/UtilMath.h>
#include <Exd/ExdDataGenerated.h>
#include <Network/CommonActorControl.h>
#include <Service.h>
#include "Actor/Player.h"
#include "Actor/EventObject.h"
#include "Event/Director.h"
#include "Event/EventDefs.h"
#include "Event/EventHandler.h"
#include "Script/ScriptMgr.h"
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Network/PacketWrappers/ActorControlSelfPacket.h"
#include "PublicContent.h"
using namespace Sapphire::Common;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::Server;
using namespace Sapphire::Network::ActorControl;
Sapphire::PublicContent::PublicContent( std::shared_ptr< Sapphire::Data::PublicContent > pConfiguration,
uint16_t territoryType,
uint32_t guId,
const std::string& internalName,
const std::string& contentName,
uint32_t contentId, uint16_t contentFinderConditionId ) :
Territory( static_cast< uint16_t >( territoryType ), guId, internalName, contentName ),
Director( Event::Director::PublicContent, contentId, contentFinderConditionId ),
m_Configuration( pConfiguration ),
m_ContentId( contentId )
{
}
bool Sapphire::PublicContent::init()
{
if( !Territory::init() )
return false;
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPublicContentInit( getAsPublicContent() );
return true;
}
Sapphire::PublicContent::~PublicContent()
{
}
uint32_t Sapphire::PublicContent::getContentId() const
{
return m_ContentId;
}
Sapphire::Data::ExdDataGenerated::PublicContentPtr Sapphire::PublicContent::getConfiguration() const
{
return m_Configuration;
}
void Sapphire::PublicContent::onPlayerZoneIn( Entity::Player& player )
{
Logger::debug( "PublicContent::onPlayerZoneIn: Territory#{0}|{1}, Entity#{2}", getGuId(), getTerritoryTypeId(), player.getId() );
sendDirectorInit( player );
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPublicContentPlayerZoneIn( getAsPublicContent(), player );
}
void Sapphire::PublicContent::onLeaveTerritory( Entity::Player& player )
{
Logger::debug( "PublicContent::onLeaveTerritory: Territory#{0}|{1}, Entity#{2}", getGuId(), getTerritoryTypeId(), player.getId() );
clearDirector( player );
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPublicContentLeaveTerritory( getAsPublicContent(), player );
}
void Sapphire::PublicContent::onUpdate( uint64_t tickCount )
{
updateBNpcs( tickCount );
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPublicContentUpdate( getAsPublicContent(), tickCount );
}
void Sapphire::PublicContent::onFinishLoading( Entity::Player& player )
{
}
void Sapphire::PublicContent::onInitDirector( Entity::Player& player )
{
sendDirectorVars( player );
player.setDirectorInitialized( true );
}
void Sapphire::PublicContent::onDirectorSync( Entity::Player& player )
{
player.queuePacket( makeActorControlSelf( player.getId(), DirectorUpdate, 0x00110001, 0x80000000, 1 ) );
}
void Sapphire::PublicContent::onBeforePlayerZoneIn( Sapphire::Entity::Player& player )
{
/*if( m_pEntranceEObj != nullptr )
{
player.setRot( PI );
player.setPos( m_pEntranceEObj->getPos() );
}
else
{
player.setRot( PI );
player.setPos( { 0.f, 0.f, 0.f } );
}*/
player.resetObjSpawnIndex();
}
void Sapphire::PublicContent::onEnterTerritory( Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 )
{
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPublicContentEnterTerritory( getAsPublicContent(), player, eventId, param1, param2 );
}
void Sapphire::PublicContent::onRegisterEObj( Entity::EventObjectPtr object )
{
if( object->getName() != "none" )
m_eventObjectMap[ object->getName() ] = object;
if( object->getObjectId() == 2000182 ) // start
m_pEntranceEObj = object;
if( m_pEntranceEObj == nullptr && object->getName() == "Entrance" )
m_pEntranceEObj = object;
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
auto objData = exdData.get< Sapphire::Data::EObj >( object->getObjectId() );
if( objData )
m_eventIdToObjectMap[ objData->data ] = object;
else
Logger::error( "PublicContent::onRegisterEObj Territory " +
m_internalName + ": No EObj data found for EObj with ID: " +
std::to_string( object->getObjectId() ) );
}
Sapphire::Entity::EventObjectPtr Sapphire::PublicContent::getEObjByName( const std::string& name )
{
auto it = m_eventObjectMap.find( name );
if( it == m_eventObjectMap.end() )
return nullptr;
return it->second;
}
void Sapphire::PublicContent::clearDirector( Entity::Player& player )
{
sendDirectorClear( player );
player.setDirectorInitialized( false );
}
void Sapphire::PublicContent::onTalk( Sapphire::Entity::Player& player, uint32_t eventId, uint64_t actorId )
{
auto it = m_eventIdToObjectMap.find( eventId );
if( it == m_eventIdToObjectMap.end() )
return;
if( auto onTalk = it->second->getOnTalkHandler() )
onTalk( player, it->second, getAsPublicContent(), eventId, actorId );
else
player.sendDebug( "No onTalk handler found for interactable eobj with EObjID#{0}, eventId#{1} ",
it->second->getObjectId(), eventId );
}
void Sapphire::PublicContent::setVar( uint8_t index, uint8_t value )
{
if( index > 19 )
return;
switch( index )
{
case 0:
setDirectorUI8AL( value );
break;
case 1:
setDirectorUI8AH( value );
break;
case 2:
setDirectorUI8BL( value );
break;
case 3:
setDirectorUI8BH( value );
break;
case 4:
setDirectorUI8CL( value );
break;
case 5:
setDirectorUI8CH( value );
break;
case 6:
setDirectorUI8DL( value );
break;
case 7:
setDirectorUI8DH( value );
break;
case 8:
setDirectorUI8EL( value );
break;
case 9:
setDirectorUI8EH( value );
break;
case 10:
setDirectorUI8FL( value );
break;
case 11:
setDirectorUI8FH( value );
break;
case 12:
setDirectorUI8GL( value );
break;
case 13:
setDirectorUI8GH( value );
break;
case 14:
setDirectorUI8HL( value );
break;
case 15:
setDirectorUI8HH( value );
break;
case 16:
setDirectorUI8IL( value );
break;
case 17:
setDirectorUI8IH( value );
break;
case 18:
setDirectorUI8JL( value );
break;
case 19:
setDirectorUI8JH( value );
break;
}
// todo: genericise this?
for( const auto& playerIt : m_playerMap )
{
sendDirectorVars( *playerIt.second );
}
}
void Sapphire::PublicContent::setSequence( uint8_t value )
{
setDirectorSequence( value );
for( const auto& playerIt : m_playerMap )
{
sendDirectorVars( *playerIt.second );
}
}
void Sapphire::PublicContent::setBranch( uint8_t value )
{
setDirectorBranch( value );
for( const auto& playerIt : m_playerMap )
{
sendDirectorVars( *playerIt.second );
}
}

View file

@ -0,0 +1,72 @@
#ifndef SAPPHIRE_PUBLICCONTENT_H
#define SAPPHIRE_PUBLICCONTENT_H
#include "Territory.h"
#include "Event/Director.h"
#include "Forwards.h"
namespace Sapphire::Data
{
struct PublicContent;
}
namespace Sapphire
{
class PublicContent : public Event::Director, public Territory
{
public:
PublicContent( std::shared_ptr< Sapphire::Data::PublicContent > pConfiguration,
uint16_t territoryType,
uint32_t guId,
const std::string& internalName,
const std::string& contentName,
uint32_t contentId, uint16_t contentFinderConditionId = 0 );
virtual ~PublicContent();
bool init() override;
void onBeforePlayerZoneIn( Entity::Player& player ) override;
void onPlayerZoneIn( Entity::Player& player ) override;
void onLeaveTerritory( Entity::Player& player ) override;
void onUpdate( uint64_t tickCount ) override;
void onFinishLoading( Entity::Player& player ) override;
void onInitDirector( Entity::Player& player ) override;
void onDirectorSync( Entity::Player& player ) override;
void onEnterTerritory( Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ) override;
void onRegisterEObj( Entity::EventObjectPtr object ) override;
std::shared_ptr< Sapphire::Data::PublicContent > getConfiguration() const;
uint32_t getContentId() const;
Entity::EventObjectPtr getEObjByName( const std::string& name );
void clearDirector( Entity::Player& player );
void onTalk( Entity::Player& player, uint32_t eventId, uint64_t actorId );
void setSequence( uint8_t value );
void setBranch( uint8_t value );
void setVar( uint8_t index, uint8_t value );
private:
std::shared_ptr< Sapphire::Data::PublicContent > m_Configuration;
uint32_t m_ContentId;
Entity::EventObjectPtr m_pEntranceEObj;
std::map< std::string, Entity::EventObjectPtr > m_eventObjectMap;
std::unordered_map< uint32_t, Entity::EventObjectPtr > m_eventIdToObjectMap;
};
}
#endif //SAPPHIRE_PUBLICCONTENT_H

View file

@ -34,9 +34,9 @@ Sapphire::QuestBattle::QuestBattle( std::shared_ptr< Sapphire::Data::QuestBattle
uint32_t guId,
const std::string& internalName,
const std::string& contentName,
uint32_t questBattleId ) :
uint32_t questBattleId, uint16_t contentFinderConditionId ) :
Territory( static_cast< uint16_t >( territoryType ), guId, internalName, contentName ),
Director( Event::Director::QuestBattle, questBattleId ),
Director( Event::Director::QuestBattle, questBattleId, contentFinderConditionId ),
m_pBattleDetails( pBattleDetails ),
m_questBattleId( questBattleId ),
m_state( Created ),
@ -330,7 +330,7 @@ void Sapphire::QuestBattle::onTalk( Sapphire::Entity::Player& player, uint32_t e
return;
if( auto onTalkHandler = it->second->getOnTalkHandler() )
onTalkHandler( player, it->second, getAsQuestBattle(), actorId );
onTalkHandler( player, it->second, getAsQuestBattle(), eventId, actorId );
else
player.sendDebug( "No onTalk handler found for interactable eobj with EObjID#{0}, eventId#{1} ",
it->second->getObjectId(), eventId );

View file

@ -20,7 +20,7 @@ namespace Sapphire
uint32_t guId,
const std::string& internalName,
const std::string& contentName,
uint32_t questBattleId );
uint32_t questBattleId, uint16_t contentFinderConditionId = 0 );
virtual ~QuestBattle() = default;

View file

@ -19,6 +19,7 @@
#include "Territory.h"
#include "InstanceContent.h"
#include "QuestBattle.h"
#include "PublicContent.h"
#include "Manager/TerritoryMgr.h"
#include "Navi/NaviProvider.h"
@ -268,12 +269,7 @@ void Sapphire::Territory::pushActor( Entity::ActorPtr pActor )
void Sapphire::Territory::removeActor( Entity::ActorPtr pActor )
{
float mx = pActor->getPos().x;
float my = pActor->getPos().z;
uint32_t cx = getPosX( mx );
uint32_t cy = getPosY( my );
Cell* pCell = getCellPtr( cx, cy );
Cell* pCell = pActor->getCellPtr();
if( pCell && pCell->hasActor( pActor ) )
pCell->removeActorFromCell( pActor );
@ -780,6 +776,11 @@ Sapphire::Entity::EventObjectPtr Sapphire::Territory::getEObj( uint32_t objId )
return obj->second;
}
Sapphire::Event::DirectorPtr Sapphire::Territory::getAsDirector()
{
return std::dynamic_pointer_cast< Event::Director, Territory >( shared_from_this() );
}
Sapphire::InstanceContentPtr Sapphire::Territory::getAsInstanceContent()
{
return std::dynamic_pointer_cast< InstanceContent, Territory >( shared_from_this() );
@ -790,6 +791,11 @@ Sapphire::QuestBattlePtr Sapphire::Territory::getAsQuestBattle()
return std::dynamic_pointer_cast< QuestBattle, Territory >( shared_from_this() );
}
Sapphire::PublicContentPtr Sapphire::Territory::getAsPublicContent()
{
return std::dynamic_pointer_cast< PublicContent, Territory >( shared_from_this() );
}
uint32_t Sapphire::Territory::getNextEObjId()
{
return ++m_nextEObjId;
@ -1043,4 +1049,19 @@ void Sapphire::Territory::processEffectResults( uint64_t tickCount )
it = m_effectResults.erase( it );
}
}
Sapphire::Entity::PlayerPtr Sapphire::Territory::getPlayer( uint32_t charId )
{
return m_playerMap[ charId ];
}
void Sapphire::Territory::foreachPlayer( std::function< void( Sapphire::Entity::PlayerPtr player ) > callback )
{
if( !callback )
return;
for( auto entry : m_playerMap )
{
callback( entry.second );
}
}

View file

@ -166,10 +166,14 @@ namespace Sapphire
Entity::EventObjectPtr getEObj( uint32_t objId );
Event::DirectorPtr getAsDirector();
InstanceContentPtr getAsInstanceContent();
QuestBattlePtr getAsQuestBattle();
PublicContentPtr getAsPublicContent();
void updateSpawnPoints();
uint32_t getNextEffectSequence();
@ -179,6 +183,10 @@ namespace Sapphire
void addEffectResult( World::Action::EffectResultPtr result );
void processEffectResults( uint64_t tickCount );
Entity::PlayerPtr getPlayer( uint32_t charId );
void foreachPlayer( std::function< void( Entity::PlayerPtr player ) > callback );
};
}