1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-27 22:57:45 +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 "Exd.h"
#include "bparse.h" #include "bparse.h"
#include "stream.h"
#include <fstream>
#include "Exh.h" #include "Exh.h"
using xiv::utils::bparse::extract; using xiv::utils::bparse::extract;
@ -53,32 +51,24 @@ template<>
namespace xiv::exd 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; _exh = exh;
_files = i_files;
// Iterates over all the files // Iterates over all the files
const uint32_t member_count = _exh->get_members().size(); for( auto& file : files )
for( auto& file_ptr : _files )
{ {
// Get a stream std::vector< char > dataCpy = file->get_data_sections().front();
std::vector< char > dataCpy = file_ptr->get_data_sections().front();
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
// Extract the header and skip to the record indices // Extract the header
auto exd_header = extract< ExdHeader >( iss ); auto exdHeader = extract< ExdHeader >( dataCpy, 0 );
iss.seekg( 0x20 );
// Preallocate and extract the record_indices const uint32_t recordCount = exdHeader.index_size / sizeof( ExdRecordIndex );
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex ); for( uint32_t i = 0; i < recordCount; ++i )
std::vector< ExdRecordIndex > record_indices;
record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i )
{ {
auto recordIndex = extract< ExdRecordIndex >( iss ); auto recordIndex = extract< ExdRecordIndex >( dataCpy, 32 + ( i * sizeof( ExdRecordIndex ) ) );
_idCache[ recordIndex.id ] = ExdCacheEntry{ file_ptr, recordIndex.offset }; _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 ); auto cacheEntryIt = _idCache.find( id );
if( cacheEntryIt == _idCache.end() ) if( cacheEntryIt == _idCache.end() || subRow >= cacheEntryIt->second.subRows )
throw std::runtime_error( "Id not found: " + std::to_string( id ) ); throw std::runtime_error( "Id + SubId combination not found: " + std::to_string( id ) + "." + std::to_string( subRow ) );
// Iterates over all the files auto dataCpy = cacheEntryIt->second.file->get_data_sections().front();
const uint32_t member_count = _exh->get_members().size();
auto& file_ptr = cacheEntryIt->second.file;
std::vector< char > dataCpy = file_ptr->get_data_sections().front(); std::vector< Field > fields;
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) ); fields.reserve( _exh->get_members().size() );
// Get the vector fields for the given record and preallocate it uint32_t baseOffset = cacheEntryIt->second.offset + ( subRow * _exh->get_header().data_offset + 2 * ( subRow + 1 ) );
auto fields = _data[ id ];
fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 );
uint8_t subRows = *reinterpret_cast< uint8_t* >( &dataCpy[ cacheEntryIt->second.offset + 5 ] ); for( auto& memberEntry : _exh->get_exh_members() )
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() )
{ {
// 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 depending on the type to extract
switch( member_entry.type ) switch( memberEntry.type )
{ {
case DataType::string: case DataType::string:
// Extract the offset to the actual 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!" ); throw std::runtime_error( "String not implemented for variant 2!" );
//auto string_offset = extract<uint32_t>( iss, "string_offset", false ); //auto string_offset = extract<uint32_t>( iss, "string_offset", false );
@ -134,50 +108,46 @@ namespace xiv::exd
break; break;
case DataType::boolean: case DataType::boolean:
fields.emplace_back( extract< bool >( iss, "bool" ) ); fields.emplace_back( extract< bool >( dataCpy, baseOffset + memberEntry.offset ) );
break; break;
case DataType::int8: case DataType::int8:
fields.emplace_back( extract< int8_t >( iss, "int8_t" ) ); fields.emplace_back( extract< int8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break; break;
case DataType::uint8: case DataType::uint8:
fields.emplace_back( extract< uint8_t >( iss, "uint8_t" ) ); fields.emplace_back( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break; break;
case DataType::int16: 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; break;
case DataType::uint16: 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; break;
case DataType::int32: 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; break;
case DataType::uint32: 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; break;
case DataType::float32: case DataType::float32:
fields.emplace_back( extract< float >( iss, "float", false ) ); fields.emplace_back( extract< float >( dataCpy, baseOffset + memberEntry.offset, false ) );
break; break;
case DataType::uint64: 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; break;
default: default:
auto type = static_cast< uint16_t >( member_entry.type ); auto type = static_cast< uint16_t >( memberEntry.type );
if( type < 0x19 || type > 0x20 ) if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) ); throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = extract< uint64_t >( iss, "bool" ); fields.emplace_back( ( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) & ( 1 << ( type - 0x19 ) ) ) != 0 );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
break; break;
} }
} }
@ -193,84 +163,68 @@ namespace xiv::exd
if( cacheEntryIt == _idCache.end() ) if( cacheEntryIt == _idCache.end() )
throw std::runtime_error( "Id not found: " + std::to_string( id ) ); throw std::runtime_error( "Id not found: " + std::to_string( id ) );
// Iterates over all the files auto dataCpy = cacheEntryIt->second.file->get_data_sections().front();
const uint32_t member_count = _exh->get_members().size();
auto& file_ptr = cacheEntryIt->second.file;
std::vector< char > dataCpy = file_ptr->get_data_sections().front(); std::vector< Field > fields;
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) ); fields.reserve( _exh->get_members().size() );
// Get the vector fields for the given record and preallocate it auto stringBaseOffset = cacheEntryIt->second.offset + _exh->get_header().data_offset;
auto fields = _data[ id ];
fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 );
uint8_t subRows = *reinterpret_cast< uint8_t* >( &dataCpy[ cacheEntryIt->second.offset + 5 ] ); for( auto& memberEntry : _exh->get_exh_members() )
for( auto& member_entry : _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 depending on the type to extract
switch( member_entry.type ) switch( memberEntry.type )
{ {
case DataType::string: case DataType::string:
// Extract the offset to the actual 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 ); auto stringOffset = extract< uint32_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false );
iss.seekg( cacheEntryIt->second.offset + 6 + _exh->get_header().data_offset + string_offset ); fields.emplace_back( utils::bparse::extract_cstring( dataCpy, stringBaseOffset + stringOffset ) );
fields.emplace_back( utils::bparse::extract_cstring( iss, "string" ) );
} }
break; break;
case DataType::boolean: case DataType::boolean:
fields.emplace_back( extract< bool >( iss, "bool" ) ); fields.emplace_back( extract< bool >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset ) );
break; break;
case DataType::int8: 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; break;
case DataType::uint8: 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; break;
case DataType::int16: 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; break;
case DataType::uint16: 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; break;
case DataType::int32: 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; break;
case DataType::uint32: 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; break;
case DataType::float32: case DataType::float32:
fields.emplace_back( extract< float >( iss, "float", false ) ); fields.emplace_back( extract< float >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset, false ) );
break; break;
case DataType::uint64: 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; break;
default: default:
auto type = static_cast< uint16_t >( member_entry.type ); auto type = static_cast< uint16_t >( memberEntry.type );
if( type < 0x19 || type > 0x20 ) if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) ); throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = extract< uint64_t >( iss, "bool" ); fields.emplace_back( ( extract< uint8_t >( dataCpy, cacheEntryIt->second.offset + memberEntry.offset ) & ( 1 << ( type - 0x19 ) ) ) != 0 );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
break; break;
} }
} }
@ -279,106 +233,99 @@ namespace xiv::exd
} }
// Get all rows // 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 std::map< ExdRow, std::vector< Field >, exdRowSort > data;
const uint32_t member_count = _exh->get_members().size();
for( auto& file_ptr : _files ) // 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 = cacheEntry.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() ) );
// Extract the header and skip to the record indices auto baseOffset = cacheEntry.second.offset;
auto exd_header = extract< ExdHeader >( iss ); auto stringBaseOffset = baseOffset + _exh->get_header().data_offset;
iss.seekg( 0x20 );
// Preallocate and extract the record_indices for( int32_t i = 0; i < cacheEntry.second.subRows; i++ )
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 )
{ {
// Get the vector fields for the given record and preallocate it // Get the vector fields for the given record and preallocate it
auto& fields = _data[ record_index.id ]; ExdRow row = { cacheEntry.first, i };
fields.reserve( member_count ); 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() ) //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 depending on the type to extract
switch( member_entry.type ) switch( memberEntry.type )
{ {
case DataType::string: case DataType::string:
// Extract the offset to the actual 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 ); if( _exh->get_header().variant == 1 )
iss.seekg( record_index.offset + 6 + _exh->get_header().data_offset + string_offset ); {
fields.emplace_back( utils::bparse::extract_cstring( iss, "string" ) ); 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; break;
case DataType::boolean: case DataType::boolean:
fields.emplace_back( extract< bool >( iss, "bool" ) ); fields.emplace_back( extract< bool >( dataCpy, baseOffset + memberEntry.offset ) );
break; break;
case DataType::int8: case DataType::int8:
fields.emplace_back( extract< int8_t >( iss, "int8_t" ) ); fields.emplace_back( extract< int8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break; break;
case DataType::uint8: case DataType::uint8:
fields.emplace_back( extract< uint8_t >( iss, "uint8_t" ) ); fields.emplace_back( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) );
break; break;
case DataType::int16: 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; break;
case DataType::uint16: 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; break;
case DataType::int32: 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; break;
case DataType::uint32: 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; break;
case DataType::float32: case DataType::float32:
fields.emplace_back( extract< float >( iss, "float", false ) ); fields.emplace_back( extract< float >( dataCpy, baseOffset + memberEntry.offset, false ) );
break; break;
case DataType::uint64: 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; break;
default: default:
auto type = static_cast< uint16_t >( member_entry.type ); auto type = static_cast< uint16_t >( memberEntry.type );
if( type < 0x19 || type > 0x20 ) if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) ); throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = extract< uint64_t >( iss, "bool" ); fields.emplace_back( ( extract< uint8_t >( dataCpy, baseOffset + memberEntry.offset ) & ( 1 << ( type - 0x19 ) ) ) != 0 );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
break; break;
} }
} }
} }
} }
return _data; return data;
} }
} }

31
deps/datReader/Exd.h vendored
View file

@ -3,6 +3,8 @@
#include <memory> #include <memory>
#include <map> #include <map>
#include <unordered_map>
#include <set>
#include <variant> #include <variant>
@ -30,19 +32,37 @@ namespace xiv::exd
{ {
std::shared_ptr< dat::File > file; std::shared_ptr< dat::File > file;
uint32_t offset; 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 // Data for a given language
class Exd class Exd
{ {
public: public:
// i_exh: the header // exh: the header
// i_files: the multiple exd files // files: the multiple exd files
Exd() 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(); ~Exd();
@ -53,12 +73,9 @@ namespace xiv::exd
const std::vector< Field > get_row( uint32_t id, uint32_t subRow ); const std::vector< Field > get_row( uint32_t id, uint32_t subRow );
// Get all rows // Get all rows
const std::map< uint32_t, std::vector< Field>>& get_rows(); const std::map< ExdRow, std::vector< Field >, exdRowSort > get_rows();
protected: 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::shared_ptr< Exh > _exh;
std::map< uint32_t, ExdCacheEntry > _idCache; std::map< uint32_t, ExdCacheEntry > _idCache;
}; };

View file

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

View file

@ -6,6 +6,7 @@
#include <mutex> #include <mutex>
#include <filesystem> #include <filesystem>
#include <vector>
namespace xiv::dat 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::string temp_str;
std::getline( i_stream, temp_str, '\0' ); std::getline( i_stream, temp_str, '\0' );
return temp_str; 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 // For cstrings
std::string extract_cstring( std::istream& i_stream, const std::string& i_name ); 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 #endif // XIV_UTILS_BPARSE_H

View file

@ -6,6 +6,7 @@
#include "CommonGen.h" #include "CommonGen.h"
#include "Vector3.h" #include "Vector3.h"
#include "Network/PacketDef/Ipcs.h"
// +--------------------------------------------------------------------------- // +---------------------------------------------------------------------------
// The following enumerations are structures to require their type be included. // 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 CURRENT_EXPANSION_ID = 3;
const uint8_t CLASSJOB_TOTAL = 38; const uint8_t CLASSJOB_TOTAL = 38;
const uint8_t CLASSJOB_SLOTS = 28; const uint8_t CLASSJOB_SLOTS = 30;
const uint8_t TOWN_COUNT = 6; const uint8_t TOWN_COUNT = 6;
@ -51,11 +52,11 @@ namespace Sapphire::Common
enum InventoryOperation : uint16_t enum InventoryOperation : uint16_t
{ {
Discard = 0x013C, Discard = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 7,
Move = 0x013D, Move = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 8,
Swap = 0x013E, Swap = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 9,
Split = 0x013F, Split = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 10,
Merge = 0x0141, Merge = Network::Packets::ClientZoneIpcType::InventoryModifyHandler + 12
}; };
enum ClientLanguage : uint8_t enum ClientLanguage : uint8_t
@ -161,44 +162,27 @@ namespace Sapphire::Common
enum class EquipSlotCategory : uint8_t enum class EquipSlotCategory : uint8_t
{ {
// main slots MainHand = 1,
OffHand = 2,
CharaMainHand = 0, Head = 3,
CharaOffHand = 1, Body = 4,
CharaHead = 2, Hands = 5,
CharaBody = 3, Waist = 6,
CharaHands = 4, Legs = 7,
CharaWaist = 5, Feet = 8,
CharaLegs = 6, Ears = 9,
CharaFeet = 7, Neck = 10,
CharaEars = 8, Wrist = 11,
CharaNeck = 9, Ring = 12,
CharaWrist = 10, MainTwoHandedWeapon = 13,
CharaRing = 11, //MainOrOffHand = 14, // unused
CharaSoulCrystal = 12, BodyDisallowHead = 15,
BodyDisallowHandsLegsFeet = 16,
/* following slots not seem to exist any more. SoulCrystal = 17,
when multi-slot gear is moved into equipment slot, normal slot listed above is used. LegsDisallowFeet = 18,
client will move any incompatible gears into armory but no InventoryModifiyHandler is sent. BodyDisallowAll = 19,
server need to move those silently in order to sync with client. BodyDisallowHands = 20,
*/ BodyDisallowLegsFeet = 21,
/*! 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,
}; };
enum InventoryType : uint16_t enum InventoryType : uint16_t
@ -773,6 +757,7 @@ namespace Sapphire::Common
BetweenAreas = 24, BetweenAreas = 24,
BoundByDuty = 28, BoundByDuty = 28,
Performing = 40,
WatchingCutscene = 50, // this is actually just a dummy, this id is different WatchingCutscene = 50, // this is actually just a dummy, this id is different
@ -1263,9 +1248,6 @@ namespace Sapphire::Common
GetGil = 9, // p1: gil GetGil = 9, // p1: gil
EmptyCoffer = 11, // seems like no param EmptyCoffer = 11, // seems like no param
}; };
using PlayerStateFlagList = std::vector< PlayerStateFlag >;
} }
#endif #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 = ?;", "UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ? WHERE HouseId = ?;",
CONNECTION_BOTH ); CONNECTION_BOTH );
prepareStatement( HOUSING_HOUSE_DEL,
"DELETE FROM house WHERE HouseId = ?;",
CONNECTION_BOTH );
prepareStatement( LAND_INV_SEL_ALL, prepareStatement( LAND_INV_SEL_ALL,
"SELECT houseiteminventory.*, charaglobalitem.catalogId, charaglobalitem.stain, charaglobalitem.CharacterId, " "SELECT houseiteminventory.*, charaglobalitem.catalogId, charaglobalitem.stain, charaglobalitem.CharacterId, "
"landplaceditems.PosX, landplaceditems.PosY, landplaceditems.PosZ, landplaceditems.Rotation " "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, ScreenFadeOut = 0xAA,
CeremonyDecoration = 0xB9,
ZoneIn = 0xC8, ZoneIn = 0xC8,
ZoneInDefaultPos = 0xC9, ZoneInDefaultPos = 0xC9,
@ -213,7 +215,19 @@ namespace Sapphire::Network::ActorControl
SetFavorite = 0x1FC, SetFavorite = 0x1FC,
LearnTeleport = 0x1FD, 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, ArmoryErrorMsg = 0x201,
AchievementPopup = 0x203, AchievementPopup = 0x203,
@ -364,6 +378,7 @@ namespace Sapphire::Network::ActorControl
TitleList = 0x12F, TitleList = 0x12F,
UpdatedSeenHowTos = 0x133, UpdatedSeenHowTos = 0x133,
CutscenePlayed = 0x134, // param1 = cutscene id
AllotAttribute = 0x135, AllotAttribute = 0x135,
ClearFieldMarkers = 0x13A, ClearFieldMarkers = 0x13A,

View file

@ -1,8 +1,8 @@
#ifndef _CORE_NETWORK_PACKETS_IPCS_H #ifndef _CORE_NETWORK_PACKETS_IPCS_H
#define _CORE_NETWORK_PACKETS_IPCS_H #define _CORE_NETWORK_PACKETS_IPCS_H
#include <stdint.h> #include <stdint.h>
namespace Sapphire::Network::Packets namespace Sapphire::Network::Packets
{ {
@ -43,67 +43,67 @@ namespace Sapphire::Network::Packets
*/ */
enum ServerZoneIpcType : uint16_t enum ServerZoneIpcType : uint16_t
{ {
Ping = 0x0219, // updated 5.35 hotfix Ping = 0x02A8, // updated 5.58 hotfix
Init = 0x0185, // updated 5.35 hotfix Init = 0x013C, // updated 5.58 hotfix
ActorFreeSpawn = 0x0239, // updated 5.35 hotfix ActorFreeSpawn = 0x00B5, // updated 5.58 hotfix
InitZone = 0x03CD, // updated 5.35 hotfix InitZone = 0x0320, // updated 5.58 hotfix
EffectResult = 0x01C2, // updated 5.35 hotfix EffectResult = 0x0387, // updated 5.58 hotfix
ActorControl = 0x02A4, // updated 5.35 hotfix ActorControl = 0x00B0, // updated 5.58 hotfix
ActorControlSelf = 0x02C8, // updated 5.35 hotfix ActorControlSelf = 0x02B6, // updated 5.58 hotfix
ActorControlTarget = 0x0209, // updated 5.35 hotfix ActorControlTarget = 0x03C5, // updated 5.58 hotfix
/*! /*!
* @brief Used when resting * @brief Used when resting
*/ */
UpdateHpMpTp = 0x0319, // updated 5.35 hotfix UpdateHpMpTp = 0x01A7, // updated 5.58 hotfix
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
ChatBanned = 0xF06B, ChatBanned = 0xF06B,
Playtime = 0x03A4, // updated 5.35 hotfix Playtime = 0x0179, // updated 5.58 hotfix
Logout = 0x02AD, // updated 5.35 hotfix Logout = 0x0214, // updated 5.58 hotfix
CFNotify = 0x02C4, // updated 5.35 hotfix CFNotify = 0x0327, // updated 5.58 hotfix
CFMemberStatus = 0x0079, CFMemberStatus = 0x0079,
CFDutyInfo = 0x0193, // updated 5.35 hotfix CFDutyInfo = 0x03AA, // updated 5.58 hotfix
CFPlayerInNeed = 0xF07F, CFPlayerInNeed = 0xF07F,
CFPreferredRole = 0x0196, // updated 5.35 hotfix CFPreferredRole = 0x024B, // updated 5.58 hotfix
CFCancel = 0x00EC, // updated 5.35 hotfix CFCancel = 0x01AC, // updated 5.58 hotfix
SocialRequestError = 0xF0AD, SocialRequestError = 0xF0AD,
CFRegistered = 0x010C, // updated 5.35 hotfix CFRegistered = 0x029F, // updated 5.58 hotfix
SocialRequestResponse = 0x01C7, // updated 5.35 hotfix SocialRequestResponse = 0x0082, // updated 5.58 hotfix
SocialMessage = 0x0308, // updated 5.35 hotfix SocialMessage = 0x03CB, // updated 5.58 hotfix
SocialMessage2 = 0x037C, // updated 5.35 hotfix SocialMessage2 = 0x01D7, // updated 5.58 hotfix
CancelAllianceForming = 0x00C6, // updated 4.2 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, PartyChat = 0x0065,
WorldVisitList = 0xF0FE, // added 4.5 WorldVisitList = 0xF0FE, // added 4.5
SocialList = 0x0216, // updated 5.35 hotfix SocialList = 0x015F, // updated 5.58 hotfix
ExamineSearchInfo = 0x03C3, // updated 5.35 hotfix ExamineSearchInfo = 0x0133, // updated 5.58 hotfix
UpdateSearchInfo = 0x0121, // updated 5.35 hotfix UpdateSearchInfo = 0x03E5, // updated 5.58 hotfix
InitSearchInfo = 0x036F, // updated 5.35 hotfix InitSearchInfo = 0x0321, // updated 5.58 hotfix
ExamineSearchComment = 0x0102, // updated 4.1 ExamineSearchComment = 0x03AD, // updated 5.58 hotfix
ServerNoticeShort = 0x017A, // updated 5.35 hotfix ServerNoticeShort = 0x0333, // updated 5.58 hotfix
ServerNotice = 0x02F8, // updated 5.35 hotfix ServerNotice = 0x0171, // updated 5.58 hotfix
SetOnlineStatus = 0x03D7, // updated 5.35 hotfix SetOnlineStatus = 0x037B, // updated 5.58 hotfix
CountdownInitiate = 0x0237, // updated 5.25 CountdownInitiate = 0x0111, // updated 5.58 hotfix
CountdownCancel = 0x00D9, // updated 5.18 CountdownCancel = 0x0231, // updated 5.58 hotfix
PlayerAddedToBlacklist = 0x033F, // updated 5.1 PlayerAddedToBlacklist = 0x024E, // updated 5.58 hotfix
PlayerRemovedFromBlacklist = 0x0385, // updated 5.1 PlayerRemovedFromBlacklist = 0x011D, // updated 5.58 hotfix
BlackList = 0x02DB, // updated 5.35 hotfix BlackList = 0x03C0, // updated 5.58 hotfix
LinkshellList = 0x01F0, // updated 5.35 hotfix LinkshellList = 0x02E2, // updated 5.58 hotfix
MailDeleteRequest = 0xF12B, // updated 5.0 MailDeleteRequest = 0xF12B, // updated 5.0
@ -114,166 +114,181 @@ namespace Sapphire::Network::Packets
MarketTaxRates = 0x01F8, // updated 5.35 hotfix MarketTaxRates = 0x01F8, // updated 5.35 hotfix
MarketBoardSearchResult = 0x032C, // updated 5.35 hotfix MarketBoardSearchResult = 0x01F1, // updated 5.58 hotfix
MarketBoardItemListingCount = 0x038F, // updated 5.35 hotfix MarketBoardItemListingCount = 0x0068, // updated 5.58 hotfix
MarketBoardItemListingHistory = 0x0186, // updated 5.35 hotfix MarketBoardItemListingHistory = 0x01BA, // updated 5.58 hotfix
MarketBoardItemListing = 0x025F, // updated 5.35 hotfix MarketBoardItemListing = 0x0076, // updated 5.58 hotfix
CharaFreeCompanyTag = 0x013B, // updated 4.5 CharaFreeCompanyTag = 0x013B, // updated 4.5
FreeCompanyBoardMsg = 0x013C, // updated 4.5 FreeCompanyBoardMsg = 0x03DB, // updated 5.58 hotfix
FreeCompanyInfo = 0xF13D, // updated 4.5 FreeCompanyInfo = 0x01F7, // updated 5.58 hotfix
ExamineFreeCompanyInfo = 0xF13E, // updated 4.5 ExamineFreeCompanyInfo = 0x0324, // updated 5.58 hotfix
FreeCompanyUpdateShortMessage = 0xF157, // added 5.0 FreeCompanyUpdateShortMessage = 0xF157, // added 5.0
StatusEffectList = 0x0382, // updated 5.35 hotfix StatusEffectList = 0x0074, // updated 5.58 hotfix
EurekaStatusEffectList = 0x0167, // updated 5.18 EurekaStatusEffectList = 0x0167, // updated 5.18
BossStatusEffectList = 0x0312, // added 5.1 BossStatusEffectList = 0x0312, // added 5.1
Effect = 0x0192, // updated 5.35 hotfix Effect = 0x03CA, // updated 5.58 hotfix
AoeEffect8 = 0x012C, // updated 5.35 hotfix AoeEffect8 = 0x03C4, // updated 5.58 hotfix
AoeEffect16 = 0x01B9, // updated 5.35 hotfix AoeEffect16 = 0x00FA, // updated 5.58 hotfix
AoeEffect24 = 0x02B4, // updated 5.35 hotfix AoeEffect24 = 0x0339, // updated 5.58 hotfix
AoeEffect32 = 0x00A4, // updated 5.35 hotfix AoeEffect32 = 0x023C, // updated 5.58 hotfix
PersistantEffect = 0x0317, // updated 5.35 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 PlayerSpawn = 0x01D8, // updated 5.58 hotfix
NpcSpawn = 0x03A8, // updated 5.35 hotfix NpcSpawn = 0x00D2, // updated 5.58 hotfix
NpcSpawn2 = 0x01CB, // ( Bigger statuseffectlist? ) updated 5.3 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 SomeCustomiseChangePacketProbably = 0x00CD, // added 5.18
PartyList = 0x02B2, // updated 5.35 hotfix PartyList = 0x0349, // updated 5.58 hotfix
PartyMessage = 0x00AE, // updated 5.35 hotfix PartyMessage = 0x00A4, // updated 5.58 hotfix
HateRank = 0x02CC, // updated 5.35 hotfix HateRank = 0x0150, // updated 5.58 hotfix
HateList = 0x0198, // updated 5.35 hotfix HateList = 0x0243, // updated 5.58 hotfix
ObjectSpawn = 0x02B8, // updated 5.35 hotfix ObjectSpawn = 0x0125, // updated 5.58 hotfix
ObjectDespawn = 0x00C0, // updated 5.35 hotfix ObjectDespawn = 0x0148, // updated 5.58 hotfix
UpdateClassInfo = 0x0235, // updated 5.35 hotfix UpdateClassInfo = 0x0084, // updated 5.58 hotfix
SilentSetClassJob = 0x018E, // updated 5.0 - seems to be the case, not sure if it's actually used for anything SilentSetClassJob = 0xF18E, // updated 5.0 - seems to be the case, not sure if it's actually used for anything
PlayerSetup = 0x0290, // updated 5.35 hotfix PlayerSetup = 0x01D5, // updated 5.58 hotfix
PlayerStats = 0x023B, // updated 5.35 hotfix PlayerStats = 0x0295, // updated 5.58 hotfix
ActorOwner = 0x00E8, // updated 5.35 hotfix ActorOwner = 0x0260, // updated 5.58 hotfix
PlayerStateFlags = 0x00F8, // updated 5.35 hotfix PlayerStateFlags = 0x03BF, // updated 5.58 hotfix
PlayerClassInfo = 0x02C3, // updated 5.35 hotfix PlayerClassInfo = 0x0131, // updated 5.58 hotfix
CharaVisualEffect = 0x02E2, // updated 5.35 hotfix CharaVisualEffect = 0x0292, // updated 5.58 hotfix
ModelEquip = 0x0277, // updated 5.35 hotfix ModelEquip = 0x03A2, // updated 5.58 hotfix
Examine = 0x00BC, // updated 5.35 hotfix Examine = 0x0365, // updated 5.58 hotfix
CharaNameReq = 0x008E, // updated 5.35 hotfix CharaNameReq = 0x01F0, // updated 5.58 hotfix
// nb: see #565 on github // nb: see #565 on github
UpdateRetainerItemSalePrice = 0xF19F, // updated 5.0 UpdateRetainerItemSalePrice = 0xF19F, // updated 5.0
RetainerSaleHistory = 0x020E, // updated 5.21 hotfix RetainerSaleHistory = 0x03CE, // updated 5.58 hotfix
RetainerInformation = 0x01F9, // updated 5.35 hotfix RetainerInformation = 0x022F, // updated 5.58 hotfix
SetLevelSync = 0x1186, // not updated for 4.4, not sure what it is anymore SetLevelSync = 0x1186, // not updated for 4.4, not sure what it is anymore
ItemInfo = 0x0214, // updated 5.35 hotfix ItemInfo = 0x01CC, // updated 5.58 hotfix
ContainerInfo = 0x00C5, // updated 5.35 hotfix ContainerInfo = 0x025C, // updated 5.58 hotfix
InventoryTransactionFinish = 0x02F0, // updated 5.35 hotfix InventoryTransactionFinish = 0x0176, // updated 5.58 hotfix
InventoryTransaction = 0x01FD, // updated 5.35 hotfix InventoryTransaction = 0x027F, // updated 5.58 hotfix
CurrencyCrystalInfo = 0x0379, // updated 5.35 hotfix CurrencyCrystalInfo = 0x0345, // updated 5.58 hotfix
InventoryActionAck = 0x03E4, // updated 5.35 hotfix InventoryActionAck = 0x03B8, // updated 5.58 hotfix
UpdateInventorySlot = 0x036A, // updated 5.35 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 EventPlay = 0x016B, // updated 5.58 hotfix
EventPlay4 = 0x00AC, // updated 5.35 hotfix EventPlay4 = 0x010A, // updated 5.58 hotfix
EventPlay8 = 0x023F, // updated 5.35 hotfix EventPlay8 = 0x0337, // updated 5.58 hotfix
EventPlay16 = 0x025B, // updated 5.35 hotfix EventPlay16 = 0x0269, // updated 5.58 hotfix
EventPlay32 = 0x029A, // updated 5.35 hotfix EventPlay32 = 0x023E, // updated 5.58 hotfix
EventPlay64 = 0x02C1, // updated 5.35 hotfix EventPlay64 = 0x00DE, // updated 5.58 hotfix
EventPlay128 = 0x038A, // updated 5.35 hotfix EventPlay128 = 0x02D0, // updated 5.58 hotfix
EventPlay255 = 0x034B, // updated 5.35 hotfix EventPlay255 = 0x0362, // updated 5.58 hotfix
EventStart = 0x009A, // updated 5.35 hotfix EventContinue = 0x00B6, // updated 5.58 hotfix
EventFinish = 0x007E, // updated 5.35 hotfix
EventStart = 0x02DA, // updated 5.58 hotfix
EventFinish = 0x0235, // updated 5.58 hotfix
EventLinkshell = 0x1169, EventLinkshell = 0x1169,
QuestActiveList = 0x0117, // updated 5.35 hotfix QuestActiveList = 0x0097, // updated 5.58 hotfix
QuestUpdate = 0x0073, // updated 5.35 hotfix QuestUpdate = 0x01B2, // updated 5.58 hotfix
QuestCompleteList = 0x0240, // updated 5.35 hotfix QuestCompleteList = 0x006D, // updated 5.58 hotfix
QuestFinish = 0x00E9, // updated 5.35 hotfix QuestFinish = 0x021B, // updated 5.58 hotfix
MSQTrackerComplete = 0xF1D6, // updated 5.0 MSQTrackerComplete = 0x0348, // updated 5.58 hotfix
MSQTrackerProgress = 0xF1CD, // updated 4.5 ? this actually looks like the two opcodes have been combined, see #474 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 SomeDirectorUnk1 = 0x0084, // updated 5.18
SomeDirectorUnk2 = 0xF0C1, // 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 SomeDirectorUnk8 = 0x028A, // updated 5.18
SomeDirectorUnk16 = 0x028C, // 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 DirectorPopUp = 0x03DF, // updated 5.58 hotfix
DirectorPopUp4 = 0x0214, // updated 5.18 DirectorPopUp4 = 0x019B, // updated 5.58 hotfix
DirectorPopUp8 = 0x00F8, // updated 5.18 DirectorPopUp8 = 0x0271, // updated 5.58 hotfix
CFAvailableContents = 0xF1FD, // updated 4.2 CFAvailableContents = 0xF1FD, // updated 4.2
WeatherChange = 0x027B, // updated 5.35 hotfix WeatherChange = 0x0323, // updated 5.58 hotfix
PlayerTitleList = 0x0251, // updated 5.35 hotfix PlayerTitleList = 0x014E, // updated 5.58 hotfix
Discovery = 0x031B, // updated 5.35 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 MiniCactpotInit = 0x0286, // added 5.31
ShopMessage = 0x0197, // updated 5.35 hotfix ShopMessage = 0x0287, // updated 5.58 hotfix
LootMessage = 0x01B7, // updated 5.35 hotfix LootMessage = 0x0383, // updated 5.58 hotfix
ResultDialog = 0x0273, // updated 5.58 hotfix
DesynthResult = 0x0238, // updated 5.58 hotfix
/// Housing ////////////////////////////////////// /// Housing //////////////////////////////////////
LandSetInitialize = 0x0095, // updated 5.35 hotfix LandSetInitialize = 0x0159, // updated 5.58 hotfix
LandUpdate = 0x00BF, // updated 5.35 hotfix LandUpdate = 0x0228, // updated 5.58 hotfix
YardObjectSpawn = 0x01CA, // updated 5.35 hotfix YardObjectSpawn = 0x023D, // updated 5.58 hotfix
HousingIndoorInitialize = 0x01FF, // updated 5.35 hotfix HousingIndoorInitialize = 0x0210, // updated 5.58 hotfix
LandPriceUpdate = 0x0380, // updated 5.35 hotfix LandPriceUpdate = 0x0300, // updated 5.58 hotfix
LandInfoSign = 0x023D, // updated 5.35 hotfix LandInfoSign = 0x03E7, // updated 5.58 hotfix
LandRename = 0x0140, // updated 5.35 hotfix LandRename = 0x01BF, // updated 5.58 hotfix
HousingEstateGreeting = 0x00C7, // updated 5.35 hotfix HousingEstateGreeting = 0x0126, // updated 5.58 hotfix
HousingUpdateLandFlagsSlot = 0x027E, // updated 5.35 hotfix HousingUpdateLandFlagsSlot = 0x0157, // updated 5.58 hotfix
HousingLandFlags = 0x022F, // updated 5.35 hotfix HousingLandFlags = 0x03B1, // updated 5.58 hotfix
HousingShowEstateGuestAccess = 0x03B5, // updated 5.35 hotfix HousingShowEstateGuestAccess = 0x00CC, // updated 5.58 hotfix
HousingObjectInitialize = 0x01AA, // updated 5.35 hotfix HousingObjectInitialize = 0x0112, // updated 5.58 hotfix
HousingInternalObjectSpawn = 0x0234, // updated 5.35 hotfix HousingInternalObjectSpawn = 0x02C8, // updated 5.58 hotfix
HousingWardInfo = 0x02FD, // updated 5.35 hotfix HousingWardInfo = 0x012A, // updated 5.58 hotfix
HousingObjectMove = 0x022C, // updated 5.35 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 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 PrepareZoning = 0x02AB, // updated 5.58 hotfix
ActorGauge = 0x0112, // updated 5.35 hotfix ActorGauge = 0x01C1, // updated 5.58 hotfix
DutyGauge = 0x02E5, // updated 5.58 hotfix
// daily quest info -> without them sent, login will take longer... // daily quest info -> without them sent, login will take longer...
DailyQuests = 0x0139, // updated 5.35 hotfix DailyQuests = 0x02D6, // updated 5.58 hotfix
DailyQuestRepeatFlags = 0x024C, // updated 5.35 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 ////////////////////////////////////// /// Doman Mahjong //////////////////////////////////////
MahjongOpenGui = 0x02A4, // only available in mahjong instance MahjongOpenGui = 0x02A4, // only available in mahjong instance
@ -282,10 +297,20 @@ namespace Sapphire::Network::Packets
MahjongEndRoundTsumo = 0x02BF, // called tsumo MahjongEndRoundTsumo = 0x02BF, // called tsumo
MahjongEndRoundRon = 0x2C0, // called ron or double ron (waiting for action must be flagged from discard packet to call) 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.. 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 // 2C3 and 2C4 are currently unknown
MahjongEndRoundDraw = 0x02C5, // self explanatory MahjongEndRoundDraw = 0x02C5, // self explanatory
MahjongEndGame = 0x02C6, // finished oorasu(all-last) round; shows a result screen. 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 enum ClientZoneIpcType : uint16_t
{ {
PingHandler = 0x0219, // updated 5.35 hotfix PingHandler = 0x0288, // updated 5.58 hotfix
InitHandler = 0x0185, // updated 5.35 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 CFCancelHandler = 0x02B2, // updated 5.58 hotfix
CFRegisterDuty = 0x0289, // updated 5.35 hotfix CFRegisterDuty = 0x01BD, // updated 5.58 hotfix
CFRegisterRoulette = 0x0088, // updated 5.35 hotfix CFRegisterRoulette = 0x037A, // updated 5.58 hotfix
PlayTimeHandler = 0x02A8, // updated 5.35 hotfix PlayTimeHandler = 0x02B7, // updated 5.58 hotfix
LogoutHandler = 0x00EC, // updated 5.35 hotfix LogoutHandler = 0x00A0, // updated 5.58 hotfix
CancelLogout = 0x03DB, // updated 5.35 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 ChatHandler = 0x03B0, // updated 5.58 hotfix
SocialResponseHandler = 0x028D, // updated 5.35 hotfix
CreateCrossWorldLS = 0x00AF, // updated 4.3
ChatHandler = 0x0131, // updated 5.35 hotfix
PartyChatHandler = 0x0065, PartyChatHandler = 0x0065,
PartySetLeaderHandler = 0x0208, // updated 5.35 hotfix PartySetLeaderHandler = 0x036C, // updated 5.58 hotfix
LeavePartyHandler = 0x0337, // updated 5.35 hotfix LeavePartyHandler = 0x019D, // updated 5.58 hotfix
KickPartyMemberHandler = 0x014C, // updated 5.35 hotfix KickPartyMemberHandler = 0x0262, // updated 5.58 hotfix
DisbandPartyHandler = 0x0205, // updated 5.35 hotfix DisbandPartyHandler = 0x0276, // updated 5.58 hotfix
SocialListHandler = 0x0340, // updated 5.35 hotfix SocialListHandler = 0x01CA, // updated 5.58 hotfix
SetSearchInfoHandler = 0x0314, // updated 5.35 hotfix SetSearchInfoHandler = 0x01D4, // updated 5.58 hotfix
ReqSearchInfoHandler = 0x01E9, // updated 5.35 hotfix ReqSearchInfoHandler = 0x014F, // updated 5.58 hotfix
ReqExamineSearchCommentHandler = 0x00E7, // updated 5.0 ReqExamineSearchCommentHandler = 0x00E7, // updated 5.0
ReqRemovePlayerFromBlacklist = 0x00F1, // updated 5.0 ReqRemovePlayerFromBlacklist = 0x00B4, // updated 5.58 hotfix
BlackListHandler = 0x0079, // updated 5.35 hotfix BlackListHandler = 0x00F2, // updated 5.58 hotfix
PlayerSearchHandler = 0x00F4, // updated 5.0 PlayerSearchHandler = 0x037D, // updated 5.58 hotfix
LinkshellListHandler = 0x024B, // updated 5.35 hotfix LinkshellListHandler = 0x03B6, // updated 5.58 hotfix
MarketBoardRequestItemListingInfo = 0x0102, // updated 4.5 MarketBoardRequestItemListingInfo = 0x00F4, // updated 5.58 hotfix
MarketBoardRequestItemListings = 0x0103, // updated 4.5 MarketBoardRequestItemListings = 0x0122, // updated 5.58 hotfix
MarketBoardSearch = 0x0107, // updated 4.5 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 FreeCompanyUpdateShortMessageHandler = 0x0123, // added 5.0
ReqMarketWishList = 0x012C, // updated 4.3 ReqMarketWishList = 0x00C3, // updated 5.58 hotfix
ReqJoinNoviceNetwork = 0x0129, // updated 4.2 ReqJoinNoviceNetwork = 0x0129, // updated 4.2
ReqCountdownInitiate = 0x025F, // updated 5.35 hotfix ReqCountdownInitiate = 0x02EC, // updated 5.58 hotfix
ReqCountdownCancel = 0x0244, // updated 5.25 ReqCountdownCancel = 0x0068, // updated 5.58 hotfix
ZoneLineHandler = 0x0279, // updated 5.35 hotfix ZoneLineHandler = 0x008D, // updated 5.58 hotfix
ClientTrigger = 0x03D3, // updated 5.35 hotfix ClientTrigger = 0x03DB, // updated 5.58 hotfix
DiscoveryHandler = 0x00E3, // updated 5.35 hotfix DiscoveryHandler = 0x038B, // updated 5.58 hotfix
PlaceFieldMarkerPreset = 0x023F, // updated 5.25 PlaceFieldMarkerPreset = 0x026D, // updated 5.58 hotfix
PlaceFieldMarker = 0x01BA, // updated 5.25 PlaceFieldMarker = 0x0371, // updated 5.58 hotfix
SkillHandler = 0x01CD, // updated 5.35 hotfix SkillHandler = 0x02DC, // updated 5.58 hotfix
GMCommand1 = 0x02AC, // updated 5.35 hotfix GMCommand1 = 0x0272, // updated 5.58 hotfix
GMCommand2 = 0x029F, // updated 5.35 hotfix GMCommand2 = 0x00E9, // updated 5.58 hotfix
AoESkillHandler = 0x030C, // updated 5.35 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 InventoryModifyHandler = 0x029E, // updated 5.58 hotfix
InventoryEquipRecommendedItems = 0x0116, // updated 5.35 hotfix
ReqPlaceHousingItem = 0x02AE, // updated 5.35 hotfix InventoryEquipRecommendedItems = 0x01C9, // updated 5.58 hotfix
BuildPresetHandler = 0x01C2, // updated 5.35 hotfix
TalkEventHandler = 0x02A4, // updated 5.35 hotfix ReqPlaceHousingItem = 0x02D4, // updated 5.58 hotfix
EmoteEventHandler = 0x02C8, // updated 5.35 hotfix BuildPresetHandler = 0x0223, // updated 5.58 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
ReturnEventHandler = 0x02B4, // updated 5.35 hotfix TalkEventHandler = 0x0387, // updated 5.58 hotfix
TradeReturnEventHandler = 0x00A4, // updated 5.35 hotfix EmoteEventHandler = 0x00B0, // updated 5.58 hotfix
TradeMultipleReturnEventHander = 0x035C, // updated 5.35 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 LinkshellEventHandler = 0x016B, // updated 4.5
LinkshellEventHandler1 = 0x016C, // 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 LandRenameHandler = 0x028E, // updated 5.58 hotfix
HousingUpdateHouseGreeting = 0x02EA, // updated 5.35 hotfix HousingUpdateHouseGreeting = 0x0343, // updated 5.58 hotfix
HousingUpdateObjectPosition = 0x00D5, // updated 5.35 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 WorldInteractionHandler = 0x0274, // updated 5.58 hotfix
Dive = 0x02CC, // updated 5.35 hotfix Dive = 0x0320, // updated 5.58 hotfix
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -423,5 +450,5 @@ namespace Sapphire::Network::Packets
} }
#endif /*_CORE_NETWORK_PACKETS_IPCS_H*/ #endif /*_CORE_NETWORK_PACKETS_IPCS_H*/

View file

@ -426,6 +426,51 @@ struct FFXIVIpcDive :
uint32_t padding; 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 #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 unk; // == 0
uint16_t modelChara; uint16_t modelChara;
uint16_t rotation; uint16_t rotation;
uint16_t currentMount;
uint16_t activeMinion; uint16_t activeMinion;
uint8_t spawnIndex; uint8_t spawnIndex;
uint8_t state; uint8_t state;
@ -693,24 +694,20 @@ namespace Sapphire::Network::Packets::Server
uint8_t classJob; uint8_t classJob;
uint8_t u26d; uint8_t u26d;
uint16_t u27a; uint16_t u27a;
uint8_t currentMount;
uint8_t mountHead; uint8_t mountHead;
uint8_t mountBody; uint8_t mountBody;
uint8_t mountFeet; uint8_t mountFeet;
uint8_t mountColor; uint8_t mountColor;
uint8_t scale; uint8_t scale;
//uint32_t elementalLevel; one of these two field changed to 16bit
//uint32_t element;
uint8_t elementData[6]; uint8_t elementData[6];
uint8_t unknown5_5[3];
Common::StatusEffect effect[30]; Common::StatusEffect effect[30];
Common::FFXIVARR_POSITION3 pos; Common::FFXIVARR_POSITION3 pos;
uint32_t models[10]; uint32_t models[10];
char name[32]; char name[32];
uint8_t look[26]; uint8_t look[26];
char fcTag[6]; char fcTag[6];
uint32_t unk30; uint32_t unk30[2];
}; };
/** /**
@ -753,9 +750,10 @@ namespace Sapphire::Network::Packets::Server
uint32_t displayFlags; uint32_t displayFlags;
uint16_t fateID; uint16_t fateID;
uint16_t mPCurr; uint16_t mPCurr;
uint16_t unknown1; // 0 uint16_t unknown1;
uint16_t unknown2; // 0 or pretty big numbers > 30000 uint16_t unknown2;
uint16_t modelChara; uint16_t modelChara;
uint16_t currentMount;
uint16_t rotation; uint16_t rotation;
uint16_t activeMinion; uint16_t activeMinion;
uint8_t spawnIndex; uint8_t spawnIndex;
@ -770,14 +768,13 @@ namespace Sapphire::Network::Packets::Server
uint8_t classJob; uint8_t classJob;
uint8_t u26d; uint8_t u26d;
uint16_t u27a; uint16_t u27a;
uint8_t currentMount;
uint8_t mountHead; uint8_t mountHead;
uint8_t mountBody; uint8_t mountBody;
uint8_t mountFeet; uint8_t mountFeet;
uint8_t mountColor; uint8_t mountColor;
uint8_t scale; uint8_t scale;
uint16_t elementalLevel; // Eureka uint8_t elemental[6];
uint16_t element; // Eureka uint8_t unknown5_5[3];
Common::StatusEffect effect[30]; Common::StatusEffect effect[30];
Common::FFXIVARR_POSITION3 pos; Common::FFXIVARR_POSITION3 pos;
uint32_t models[10]; uint32_t models[10];
@ -789,7 +786,7 @@ namespace Sapphire::Network::Packets::Server
uint8_t bNPCPartSlot; uint8_t bNPCPartSlot;
uint8_t unk32; uint8_t unk32;
uint16_t unk33; 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 ) //Current instance can be confirmed at any time using the /instance text command." ( 7B F8 69 )
uint8_t unknown5; uint8_t unknown5;
uint32_t unknown7;
uint32_t unknown8; uint32_t unknown8;
uint16_t festivalId;
uint16_t additionalFestivalId;
uint32_t unknown9; uint32_t unknown9;
uint32_t unknown10; uint32_t unknown10;
uint32_t unknown11; uint32_t festivalId;
uint32_t unknown12[4]; uint32_t unknown12[3];
uint32_t additionalFestivalId;
uint32_t unknown13[3]; uint32_t unknown13[3];
Common::FFXIVARR_POSITION3 pos; Common::FFXIVARR_POSITION3 pos;
uint32_t unknown14[3]; uint32_t unknown14[3];
@ -1041,13 +1038,12 @@ namespace Sapphire::Network::Packets::Server
unsigned char mountGuideMask[22]; unsigned char mountGuideMask[22];
unsigned char u19_2; unsigned char u19_2;
*/ */
unsigned char unknown5_3a[176]; unsigned char unknown5_55a[178];
unsigned char companionName[21]; unsigned char companionName[21];
unsigned char companionDefRank; unsigned char companionDefRank;
unsigned char companionAttRank; unsigned char companionAttRank;
unsigned char companionHealRank; unsigned char companionHealRank;
unsigned char mountGuideMask[23]; unsigned char mountGuideMask[29];
unsigned char maybeReservedMountSlots;
//== //==
char name[32]; char name[32];
unsigned char unknownOword[16]; unsigned char unknownOword[16];
@ -1056,10 +1052,10 @@ namespace Sapphire::Network::Packets::Server
unsigned char aetheryte[21]; unsigned char aetheryte[21];
unsigned char discovery[445]; unsigned char discovery[445];
unsigned char howto[34]; unsigned char howto[34];
unsigned char minions[51]; unsigned char minions[55];
unsigned char chocoboTaxiMask[10]; unsigned char chocoboTaxiMask[10];
unsigned char watchedCutscenes[131]; unsigned char watchedCutscenes[137];
unsigned char companionBardingMask[10]; unsigned char companionBardingMask[11];
unsigned char companionEquippedHead; unsigned char companionEquippedHead;
unsigned char companionEquippedBody; unsigned char companionEquippedBody;
unsigned char companionEquippedLegs; unsigned char companionEquippedLegs;
@ -1074,7 +1070,7 @@ namespace Sapphire::Network::Packets::Server
unsigned char unknownPvp5AB[11]; unsigned char unknownPvp5AB[11];
unsigned char unknown5B9[5]; unsigned char unknown5B9[5];
*/ */
unsigned char unknown5_3c[234]; unsigned char unknown5_45b[236];
//== //==
unsigned char pose; unsigned char pose;
/* /*
@ -1092,28 +1088,32 @@ namespace Sapphire::Network::Packets::Server
unsigned char aetherCurrentMask[22]; unsigned char aetherCurrentMask[22];
unsigned char u10[3]; 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 hallOfNoviceCompletion[3];
unsigned char animaCompletion[11]; unsigned char animaCompletion[11];
unsigned char unknown5_3e[33]; unsigned char unknown5_55c[35];
unsigned char unlockedRaids[28]; unsigned char unlockedRaids[28];
unsigned char unlockedDungeons[18]; unsigned char unlockedDungeons[18];
unsigned char unlockedGuildhests[10]; 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 clearedRaids[28];
unsigned char clearedDungeons[18]; unsigned char clearedDungeons[18];
unsigned char clearedGuildhests[10]; unsigned char clearedGuildhests[10];
unsigned char clearedTrials[9]; unsigned char clearedTrials[9];
unsigned char clearedPvp[5]; unsigned char clearedPvp[6];
/* /*
unsigned short fishingRecordsFishWeight[26]; unsigned short fishingRecordsFishWeight[26];
unsigned int exploratoryMissionNextTimestamp; unsigned int exploratoryMissionNextTimestamp;
unsigned char pvpLevel; 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]; 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 > template< int ArgCount >
struct FFXIVIpcEventPlayN struct FFXIVIpcEventPlayN
{ {
@ -1835,7 +1849,7 @@ namespace Sapphire::Network::Packets::Server
uint32_t bNPCName; uint32_t bNPCName;
uint32_t textId; uint32_t textId;
uint32_t popupTimeMs; uint32_t popupTimeMs;
uint32_t pad3[4]; uint32_t param[6];
}; };
@ -1847,7 +1861,7 @@ namespace Sapphire::Network::Packets::Server
struct FFXIVIpcPerformNote : FFXIVIpcBasePacket< PerformNote > struct FFXIVIpcPerformNote : FFXIVIpcBasePacket< PerformNote >
{ {
uint8_t data[32]; uint8_t data[16];
}; };
struct FFXIVIpcHousingUpdateLandFlagsSlot : FFXIVIpcBasePacket< HousingUpdateLandFlagsSlot > struct FFXIVIpcHousingUpdateLandFlagsSlot : FFXIVIpcBasePacket< HousingUpdateLandFlagsSlot >
@ -2238,6 +2252,97 @@ namespace Sapphire::Network::Packets::Server
char memberName[32]; char memberName[32];
uint8_t padding[3]; 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*/ #endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/

View file

@ -129,7 +129,7 @@ uint32_t Util::getTimeSeconds()
uint64_t Util::getEorzeanTimeStamp() 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 ) 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 ) uint8_t Util::floatToUInt8Rot( float val )
{ {
return static_cast< uint8_t >( 0x80 * ( ( val + PI ) ) / PI ); 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 ); uint16_t floatToUInt16Rot( float val );
float floatFromUInt16Rot( uint16_t rot );
uint8_t floatToUInt8Rot( float val ); uint8_t floatToUInt8Rot( float val );
template < typename T > 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[ 2 ] = 0x34;
m_baseKey[ 3 ] = 0x12; m_baseKey[ 3 ] = 0x12;
memcpy( m_baseKey + 0x04, &key, 4 ); memcpy( m_baseKey + 0x04, &key, 4 );
m_baseKey[ 8 ] = 0x88; m_baseKey[ 8 ] = 0x18;
m_baseKey[ 9 ] = 0x13; m_baseKey[ 9 ] = 0x15;
memcpy( ( char* ) m_baseKey + 0x0C, keyPhrase.c_str(), keyPhrase.size() ); memcpy( ( char* ) m_baseKey + 0x0C, keyPhrase.c_str(), keyPhrase.size() );
Common::Util::md5( m_baseKey, m_encKey, 0x2C ); Common::Util::md5( m_baseKey, m_encKey, 0x2C );
} }

View file

@ -32,7 +32,7 @@ public:
// todo: this is fucked // 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 void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override

View file

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

View file

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

View file

@ -162,12 +162,12 @@ std::string zoneNameToPath( const std::string& name )
auto teriPath = std::get< std::string >( auto teriPath = std::get< std::string >(
fields.at( static_cast< size_t >( TerritoryTypeExdIndexes::Path ) ) ); fields.at( static_cast< size_t >( TerritoryTypeExdIndexes::Path ) ) );
ZoneInfo info; ZoneInfo info;
info.id = row.first; info.id = row.first.rowId;
info.path = teriPath; info.path = teriPath;
info.name = teriName; info.name = teriName;
info.mapId = std::get< uint16_t >( info.mapId = std::get< uint16_t >(
fields.at( static_cast< size_t >( TerritoryTypeExdIndexes::Map ) ) ); 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 ) ) ) 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 ) ); static auto exd = static_cast< xiv::exd::Exd >( cat.get_data_ln( xiv::exd::Language::en ) );
for( auto& row : exd.get_rows() ) for( auto& row : exd.get_rows() )
{ {
auto id = row.first; auto id = row.first.rowId;
auto& fields = row.second; auto& fields = row.second;
auto name = std::get< std::string >( fields.at( 0 ) ); auto name = std::get< std::string >( fields.at( 0 ) );
eobjNameMap[ id ] = name; 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 ) for( auto row : rows )
{ {
auto& fields = row.second; auto& fields = row.second;
uint32_t id = row.first; uint32_t id = row.first.rowId;
std::string value; std::string value;
try try

View file

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

View file

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

View file

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

View file

@ -351,7 +351,7 @@ Sapphire::InstanceContentPtr Sapphire::Entity::Actor::getCurrentInstance() const
return nullptr; 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 Sapphire::QuestBattlePtr Sapphire::Entity::Actor::getCurrentQuestBattle() const
{ {
if( m_pCurrentTerritory ) if( m_pCurrentTerritory )
@ -360,6 +360,15 @@ Sapphire::QuestBattlePtr Sapphire::Entity::Actor::getCurrentQuestBattle() const
return nullptr; 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 Get the current cell of a region the actor is in

View file

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

View file

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

View file

@ -19,10 +19,13 @@
#include "Manager/HousingMgr.h" #include "Manager/HousingMgr.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
#include "Manager/RNGMgr.h" #include "Manager/RNGMgr.h"
#include "Manager/MapMgr.h"
#include "Territory/Territory.h" #include "Territory/Territory.h"
#include "Territory/ZonePosition.h" #include "Territory/ZonePosition.h"
#include "Territory/InstanceContent.h" #include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Territory/InstanceObjectCache.h" #include "Territory/InstanceObjectCache.h"
#include "Territory/Land.h" #include "Territory/Land.h"
@ -81,7 +84,8 @@ Sapphire::Entity::Player::Player() :
m_directorInitialized( false ), m_directorInitialized( false ),
m_onEnterEventDone( false ), m_onEnterEventDone( false ),
m_falling( false ), m_falling( false ),
m_pQueuedAction( nullptr ) m_pQueuedAction( nullptr ),
m_cfNotifiedContent( 0 )
{ {
m_id = 0; m_id = 0;
m_currentStance = Stance::Passive; m_currentStance = Stance::Passive;
@ -239,13 +243,16 @@ uint64_t Sapphire::Entity::Player::getOnlineStatusMask() const
return m_onlineStatus; 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() ); auto preparePacket = makeZonePacket< FFXIVIpcPrepareZoning >( getId() );
preparePacket->data().targetZone = targetZone; preparePacket->data().targetZone = targetZone;
preparePacket->data().fadeOutTime = fadeOutTime; preparePacket->data().fadeOutTime = fadeOutTime;
preparePacket->data().animation = animation; preparePacket->data().animation = animation;
preparePacket->data().fadeOut = static_cast< uint8_t >( fadeOut ? 1 : 0 ); preparePacket->data().fadeOut = static_cast< uint8_t >( fadeOut ? 1 : 0 );
preparePacket->data().param4 = param4;
preparePacket->data().param7 = param7;
preparePacket->data().unknown = unknown;
queuePacket( preparePacket ); queuePacket( preparePacket );
} }
@ -468,7 +475,7 @@ bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance )
return teriMgr.movePlayer( instance, getAsPlayer() ); 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; m_onEnterEventDone = false;
if( !instance ) if( !instance )
@ -486,11 +493,17 @@ bool Sapphire::Entity::Player::setInstance( TerritoryPtr instance, Common::FFXIV
m_prevTerritoryId = getTerritoryId(); m_prevTerritoryId = getTerritoryId();
} }
m_pos = pos;
m_rot = rot;
if( teriMgr.movePlayer( instance, getAsPlayer() ) ) if( teriMgr.movePlayer( instance, getAsPlayer() ) )
{ {
m_pos = pos;
return true; return true;
} }
else
{
m_pos = m_prevPos;
m_rot= m_prevRot;
}
return false; return false;
} }
@ -499,8 +512,18 @@ bool Sapphire::Entity::Player::exitInstance()
{ {
auto& teriMgr = Common::Service< TerritoryMgr >::ref(); auto& teriMgr = Common::Service< TerritoryMgr >::ref();
auto pZone = getCurrentTerritory(); auto d = getCurrentTerritory()->getAsDirector();
auto pInstance = pZone->getAsInstanceContent(); 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(); resetHp();
resetMp(); resetMp();
@ -708,7 +731,7 @@ void Sapphire::Entity::Player::learnSong( uint8_t songId, uint32_t itemId )
queuePacket( makeActorControlSelf( getId(), ToggleOrchestrionUnlock, songId, 1, 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; uint16_t index;
uint8_t value; uint8_t value;
@ -1264,6 +1287,17 @@ const uint8_t* Sapphire::Entity::Player::getMountGuideBitmask() const
return m_mountGuide; 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 uint64_t Sapphire::Entity::Player::getContentId() const
{ {
return m_contentId; return m_contentId;
@ -1591,7 +1625,7 @@ uint16_t Sapphire::Entity::Player::getCurrentCompanion() const
return m_companionId; return m_companionId;
} }
uint8_t Sapphire::Entity::Player::getCurrentMount() const uint16_t Sapphire::Entity::Player::getCurrentMount() const
{ {
return m_mount; return m_mount;
} }
@ -1751,14 +1785,14 @@ void Sapphire::Entity::Player::sendZonePackets()
//setStateFlag( PlayerStateFlag::BetweenAreas ); //setStateFlag( PlayerStateFlag::BetweenAreas );
//setStateFlag( PlayerStateFlag::BetweenAreas1 ); //setStateFlag( PlayerStateFlag::BetweenAreas1 );
if( isActionLearned( static_cast< uint8_t >( Common::UnlockEntry::HuntingLog ) ) )
sendHuntingLog();
sendStats(); sendStats();
// only initialize the UI if the player in fact just logged in. // only initialize the UI if the player in fact just logged in.
if( isLogin() ) if( isLogin() )
{ {
if( isActionLearned( static_cast< uint8_t >( Common::UnlockEntry::HuntingLog ) ) )
sendHuntingLog();
auto contentFinderList = makeZonePacket< FFXIVIpcCFAvailableContents >( getId() ); auto contentFinderList = makeZonePacket< FFXIVIpcCFAvailableContents >( getId() );
for( auto i = 0; i < sizeof( contentFinderList->data().contents ); i++ ) 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.x = getPos().x;
initZonePacket->data().pos.y = getPos().y; initZonePacket->data().pos.y = getPos().y;
initZonePacket->data().pos.z = getPos().z; 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 ); queuePacket( initZonePacket );
getCurrentTerritory()->onPlayerZoneIn( *this ); getCurrentTerritory()->onPlayerZoneIn( *this );
@ -1862,6 +1902,8 @@ Sapphire::Entity::Player::sendZoneInPackets( uint32_t param1, uint32_t param2 =
setZoningType( Common::ZoneingType::None ); setZoningType( Common::ZoneingType::None );
unsetStateFlag( PlayerStateFlag::BetweenAreas ); unsetStateFlag( PlayerStateFlag::BetweenAreas );
Common::Service< MapMgr >::ref().updateAll( *this );
} }
void Sapphire::Entity::Player::finishZoning() 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, void playSceneChain( uint32_t eventId, uint32_t scene, uint32_t flags,
Event::EventHandler::SceneChainCallback sceneChainCallback ); 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 */ /*! setup the event and return a ptr to it */
Event::EventHandlerPtr bootstrapSceneEvent( uint32_t eventId, uint32_t flags ); Event::EventHandlerPtr bootstrapSceneEvent( uint32_t eventId, uint32_t flags );
@ -192,6 +194,8 @@ namespace Sapphire::Entity
/*! remove a given quest */ /*! remove a given quest */
void removeQuest( uint16_t questId ); void removeQuest( uint16_t questId );
bool isQuestCompleted( uint16_t questId );
/*! add a quest to the completed quests mask */ /*! add a quest to the completed quests mask */
void updateQuestsCompleted( uint32_t questId ); void updateQuestsCompleted( uint32_t questId );
@ -490,7 +494,7 @@ namespace Sapphire::Entity
bool setInstance( TerritoryPtr instance ); bool setInstance( TerritoryPtr instance );
/*! sets the players instance & initiates zoning process */ /*! 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 */ /*! returns the player to their position before zoning into an instance */
bool exitInstance(); bool exitInstance();
@ -547,7 +551,7 @@ namespace Sapphire::Entity
void dyeItemFromDyeingInfo(); void dyeItemFromDyeingInfo();
/*! prepares zoning / fades out the screen */ /*! 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) */ /*! get player's title list (available titles) */
uint8_t* getTitleList(); uint8_t* getTitleList();
@ -584,7 +588,7 @@ namespace Sapphire::Entity
uint16_t getCurrentCompanion() const; uint16_t getCurrentCompanion() const;
/*! get the current mount */ /*! get the current mount */
uint8_t getCurrentMount() const; uint16_t getCurrentMount() const;
/*! set current persistent emote */ /*! set current persistent emote */
void setPersistentEmote( uint32_t emoteId ); void setPersistentEmote( uint32_t emoteId );
@ -642,7 +646,7 @@ namespace Sapphire::Entity
void learnSong( uint8_t songId, uint32_t itemId ); void learnSong( uint8_t songId, uint32_t itemId );
/*! check if an action is already unlocked in the bitmask. */ /*! 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 */ /*! return a const pointer to the unlock bitmask array */
const uint8_t* getUnlockBitmask() const; const uint8_t* getUnlockBitmask() const;
@ -653,6 +657,8 @@ namespace Sapphire::Entity
/*! return a const pointer to the mount guide bitmask array */ /*! return a const pointer to the mount guide bitmask array */
const uint8_t* getMountGuideBitmask() const; const uint8_t* getMountGuideBitmask() const;
const bool hasMount( uint32_t mountId ) const;
bool checkAction() override; bool checkAction() override;
bool hasQueuedAction() const; bool hasQueuedAction() const;
@ -942,12 +948,13 @@ namespace Sapphire::Entity
ItemPtr getItemAt( uint16_t containerId, uint8_t slotId ); 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 */ /*! calculate and return player ilvl based off equipped gear */
uint16_t calculateEquippedGearItemLevel(); uint16_t calculateEquippedGearItemLevel();
ItemPtr getEquippedWeapon(); ItemPtr getEquippedWeapon();
ItemPtr getEquippedSecondaryWeapon();
/*! return the current amount of currency of type */ /*! return the current amount of currency of type */
uint32_t getCurrency( Common::CurrencyType 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 ); Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index );
void sendHuntingLog(); void sendHuntingLog();
@ -1005,6 +1014,7 @@ namespace Sapphire::Entity
uint64_t m_lastMoveTime; uint64_t m_lastMoveTime;
uint8_t m_lastMoveflag; uint8_t m_lastMoveflag;
bool m_falling; bool m_falling;
uint16_t m_cfNotifiedContent;
std::vector< ShopBuyBackEntry >& getBuyBackListForShop( uint32_t shopId ); std::vector< ShopBuyBackEntry >& getBuyBackListForShop( uint32_t shopId );
void addBuyBackItemForShop( uint32_t shopId, const ShopBuyBackEntry& entry ); void addBuyBackItemForShop( uint32_t shopId, const ShopBuyBackEntry& entry );
@ -1067,8 +1077,8 @@ namespace Sapphire::Entity
uint16_t m_activeTitle; uint16_t m_activeTitle;
uint8_t m_titleList[48]; uint8_t m_titleList[48];
uint8_t m_howTo[34]; uint8_t m_howTo[34];
uint8_t m_minions[40]; uint8_t m_minions[55];
uint8_t m_mountGuide[22]; uint8_t m_mountGuide[29];
uint8_t m_homePoint; uint8_t m_homePoint;
uint8_t m_startTown; uint8_t m_startTown;
uint16_t m_townWarpFstFlags; uint16_t m_townWarpFstFlags;
@ -1076,8 +1086,8 @@ namespace Sapphire::Entity
uint8_t m_discovery[445]; uint8_t m_discovery[445];
uint32_t m_playTime; uint32_t m_playTime;
uint16_t m_classArray[28]; uint16_t m_classArray[ Common::CLASSJOB_SLOTS ];
uint32_t m_expArray[28]; uint32_t m_expArray[ Common::CLASSJOB_SLOTS ];
uint8_t m_aetheryte[21]; uint8_t m_aetheryte[21];
uint8_t m_unlocks[64]; uint8_t m_unlocks[64];
uint8_t m_orchestrion[40]; 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 ); 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, void Sapphire::Entity::Player::eventActionStart( uint32_t eventId,
uint32_t action, uint32_t action,
World::Action::ActionCallback finishCallback, World::Action::ActionCallback finishCallback,

View file

@ -580,9 +580,9 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil
bool foundFreeSlot = false; bool foundFreeSlot = false;
std::vector< uint16_t > bags = { Bag0, Bag1, Bag2, Bag3 }; 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 // 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 ) ); 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 ); auto item = storage->getItem( slot );
// add any items that are stackable // 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 count = item->getStackSize();
uint32_t maxStack = item->getMaxStackSize(); 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 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 ); 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 Bag:
case CurrencyCrystal: case CurrencyCrystal:
{ {
writeInventory( static_cast< InventoryType >( storageId ) ); if( writeToDb )
writeInventory( static_cast< InventoryType >( storageId ) );
break; break;
} }
@ -767,7 +768,8 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot
else else
unequipItem( static_cast< GearSetSlot >( slotId ), pItem, true ); unequipItem( static_cast< GearSetSlot >( slotId ), pItem, true );
writeInventory( static_cast< InventoryType >( storageId ) ); if( writeToDb )
writeInventory( static_cast< InventoryType >( storageId ) );
break; break;
} }
default: default:
@ -804,7 +806,7 @@ void Sapphire::Entity::Player::splitItem( uint16_t fromInventoryId, uint8_t from
fromItem->setStackSize( fromItem->getStackSize() - itemCount ); fromItem->setStackSize( fromItem->getStackSize() - itemCount );
updateContainer( fromInventoryId, fromSlotId, fromItem ); updateContainer( fromInventoryId, fromSlotId, fromItem, fromInventoryId != toInventoryId );
updateContainer( toInventoryId, toSlot, newItem ); updateContainer( toInventoryId, toSlot, newItem );
updateItemDb( fromItem ); updateItemDb( fromItem );
@ -835,7 +837,7 @@ void Sapphire::Entity::Player::mergeItem( uint16_t fromInventoryId, uint8_t from
{ {
fromItem->setStackSize( stackOverflow ); fromItem->setStackSize( stackOverflow );
updateItemDb( fromItem ); 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 ) ) && !World::Manager::ItemMgr::isArmory( fromInventoryId ) )
{ {
updateContainer( fromInventoryId, fromSlotId, nullptr ); 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() ); fromSlotId = static_cast < uint8_t >( m_storageMap[ fromInventoryId ]->getFreeSlot() );
} }
auto containerTypeFrom = World::Manager::ItemMgr::getContainerType( fromInventoryId ); auto containerTypeFrom = World::Manager::ItemMgr::getContainerType( fromInventoryId );
auto containerTypeTo = World::Manager::ItemMgr::getContainerType( toInventoryId ); auto containerTypeTo = World::Manager::ItemMgr::getContainerType( toInventoryId );
updateContainer( toInventoryId, toSlot, fromItem ); updateContainer( toInventoryId, toSlot, fromItem, fromInventoryId != toInventoryId );
updateContainer( fromInventoryId, fromSlotId, toItem ); updateContainer( fromInventoryId, fromSlotId, toItem );
if( static_cast< InventoryType >( toInventoryId ) == GearSet0 || if( static_cast< InventoryType >( toInventoryId ) == GearSet0 ||
@ -939,6 +943,11 @@ Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedWeapon()
return m_storageMap[ GearSet0 ]->getItem( GearSetSlot::MainHand ); 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 Sapphire::Entity::Player::getFreeSlotsInBags()
{ {
uint8_t slots = 0; uint8_t slots = 0;

View file

@ -7,6 +7,8 @@
#include "Network/GameConnection.h" #include "Network/GameConnection.h"
#include "Network/PacketWrappers/QuestMessagePacket.h" #include "Network/PacketWrappers/QuestMessagePacket.h"
#include "Manager/MapMgr.h"
#include "Session.h" #include "Session.h"
using namespace Sapphire::Common; using namespace Sapphire::Common;
@ -17,8 +19,6 @@ void Sapphire::Entity::Player::finishQuest( uint16_t questId )
{ {
int8_t idx = getQuestIndex( questId ); int8_t idx = getQuestIndex( questId );
removeQuest( questId );
auto questFinishPacket = makeZonePacket< FFXIVIpcQuestFinish >( getId() ); auto questFinishPacket = makeZonePacket< FFXIVIpcQuestFinish >( getId() );
questFinishPacket->data().questId = questId; questFinishPacket->data().questId = questId;
questFinishPacket->data().flag1 = 1; questFinishPacket->data().flag1 = 1;
@ -26,6 +26,7 @@ void Sapphire::Entity::Player::finishQuest( uint16_t questId )
queuePacket( questFinishPacket ); queuePacket( questFinishPacket );
updateQuestsCompleted( questId ); updateQuestsCompleted( questId );
removeQuest( questId );
//sendQuestTracker(); already sent in removeQuest() //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 ) void Sapphire::Entity::Player::unfinishQuest( uint16_t questId )
{ {
removeQuestsCompleted( 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 ) 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 ) ) 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() ); auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() );
questUpdatePacket->data().slot = static_cast< uint8_t >( idx ); questUpdatePacket->data().slot = static_cast< uint8_t >( idx );
questUpdatePacket->data().questInfo.c.questId = 0; questUpdatePacket->data().questInfo.c.questId = 0;
@ -54,9 +65,6 @@ void Sapphire::Entity::Player::removeQuest( uint16_t questId )
m_questTracking[ ii ] = -1; m_questTracking[ ii ] = -1;
} }
std::shared_ptr< QuestActive > pQuest = m_activeQuests[ idx ];
m_activeQuests[ idx ].reset();
m_questIdToQuestIdx.erase( questId ); m_questIdToQuestIdx.erase( questId );
m_questIdxToQuestId.erase( idx ); m_questIdxToQuestId.erase( idx );
@ -916,6 +924,8 @@ void Sapphire::Entity::Player::updateQuest( uint16_t questId, uint8_t sequence )
m_questIdToQuestIdx[ questId ] = idx; m_questIdToQuestIdx[ questId ] = idx;
m_questIdxToQuestId[ idx ] = questId; m_questIdxToQuestId[ idx ] = questId;
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() ); auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() );
questUpdatePacket->data().slot = idx; questUpdatePacket->data().slot = idx;
questUpdatePacket->data().questInfo = *pNewQuest; 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 ) void Sapphire::Entity::Player::updateQuestsCompleted( uint32_t questId )
{ {
uint16_t index = questId / 8; uint16_t index = questId / 8;
@ -1030,7 +1045,10 @@ void Sapphire::Entity::Player::removeQuestsCompleted( uint32_t questId )
uint8_t value = 0x80 >> bitIndex; 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" ); auto titleList = res->getBlobVector( "TitleList" );
memcpy( reinterpret_cast< char* >( m_titleList ), titleList.data(), titleList.size() ); 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" ); auto mountGuide = res->getBlobVector( "Mounts" );
memcpy( reinterpret_cast< char* >( m_mountGuide ), mountGuide.data(), mountGuide.size() ); 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::Packets::Server;
using namespace Sapphire::Network::ActorControl; 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_contentId( contentId ),
m_type( type ), m_type( type ),
m_contentFinderConditionId( contentFinderConditionId ),
m_directorId( ( static_cast< uint32_t >( type ) << 16 ) | contentId ), m_directorId( ( static_cast< uint32_t >( type ) << 16 ) | contentId ),
m_sequence( 1 ), m_sequence( 1 ),
m_branch( 0 ), m_branch( 0 ),
@ -37,6 +38,11 @@ uint16_t Sapphire::Event::Director::getContentId() const
return m_contentId; return m_contentId;
} }
uint16_t Sapphire::Event::Director::getContentFinderConditionId() const
{
return m_contentFinderConditionId;
}
uint8_t Sapphire::Event::Director::getSequence() const uint8_t Sapphire::Event::Director::getSequence() const
{ {
return m_sequence; return m_sequence;
@ -52,14 +58,18 @@ void Sapphire::Event::Director::sendDirectorVars( Sapphire::Entity::Player& play
auto varPacket = makeZonePacket< FFXIVIpcDirectorVars >( player.getId() ); auto varPacket = makeZonePacket< FFXIVIpcDirectorVars >( player.getId() );
varPacket->data().m_directorId = getDirectorId(); varPacket->data().m_directorId = getDirectorId();
varPacket->data().m_sequence = getSequence(); 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 ) ); memcpy( varPacket->data().m_unionData, m_unionData.arrData, sizeof( varPacket->data().m_unionData ) );
player.queuePacket( varPacket ); 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 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 ) ); 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; 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; 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 ); auto it = m_customVarMap.find( varId );
if( it != m_customVarMap.end() ) if( it != m_customVarMap.end() )

View file

@ -42,12 +42,14 @@ namespace Sapphire::Event
DutyFailed DutyFailed
}; };
Director( DirectorType type, uint16_t contentId ); Director( DirectorType type, uint16_t contentId, uint16_t contentFinderConditionId = 0 );
uint32_t getDirectorId() const; uint32_t getDirectorId() const;
uint16_t getContentId() const; uint16_t getContentId() const;
uint16_t getContentFinderConditionId() const;
DirectorType getType() const; DirectorType getType() const;
uint8_t getSequence() const; uint8_t getSequence() const;
@ -104,8 +106,8 @@ namespace Sapphire::Event
void setDirectorBranch( uint8_t value ); void setDirectorBranch( uint8_t value );
void setCustomVar( uint32_t varId, uint32_t value ); void setCustomVar( uint32_t varId, uint64_t value );
uint32_t getCustomVar( uint32_t varId ); uint64_t getCustomVar( uint32_t varId );
private: private:
/*! Id of the content of the director */ /*! Id of the content of the director */
@ -114,6 +116,8 @@ namespace Sapphire::Event
/*! DirectorType | ContentId */ /*! DirectorType | ContentId */
uint32_t m_directorId; uint32_t m_directorId;
uint16_t m_contentFinderConditionId;
/*! currect sequence */ /*! currect sequence */
uint8_t m_sequence; uint8_t m_sequence;
@ -183,7 +187,7 @@ namespace Sapphire::Event
uint32_t m_elapsedTime; 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, FcTalk = 0x001F,
Adventure = 0x0021, Adventure = 0x0021,
DailyQuestSupply = 0x0022, DailyQuestSupply = 0x0022,
TripleTriad = 0x0023,
PreHandler = 0x0036,
ICDirector = 0x8003, ICDirector = 0x8003,
PublicContentDirector = 0x8004,
QuestBattleDirector = 0x8006, QuestBattleDirector = 0x8006,
}; };
enum class QuestAvailability : uint8_t
{
Invisible,
Available,
Locked
};
using SceneReturnCallback = std::function< void( Entity::Player&, const SceneResult& ) >; using SceneReturnCallback = std::function< void( Entity::Player&, const SceneResult& ) >;
using SceneChainCallback = std::function< void( Entity::Player& ) >; using SceneChainCallback = std::function< void( Entity::Player& ) >;
using EventFinishCallback = std::function< void( Entity::Player&, uint64_t ) >; using EventFinishCallback = std::function< void( Entity::Player&, uint64_t ) >;

View file

@ -24,6 +24,7 @@ TYPE_FORWARD( HousingZone );
TYPE_FORWARD( House ); TYPE_FORWARD( House );
TYPE_FORWARD( InstanceContent ); TYPE_FORWARD( InstanceContent );
TYPE_FORWARD( QuestBattle ); TYPE_FORWARD( QuestBattle );
TYPE_FORWARD( PublicContent );
TYPE_FORWARD( Item ); TYPE_FORWARD( Item );
TYPE_FORWARD( ItemContainer ); TYPE_FORWARD( ItemContainer );
TYPE_FORWARD( ZonePosition ); 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 ); Logger::error( "Slot out of range {0}", slotId );
return nullptr; return nullptr;
} }
if( m_itemMap.find( slotId ) == m_itemMap.end() )
return nullptr;
return m_itemMap[ slotId ]; 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 ) void Sapphire::ItemContainer::setItem( uint8_t slotId, ItemPtr pItem )
{ {
if( slotId > m_size ) if( slotId > m_size )
{
Logger::error( "Slot out of range {0}", slotId );
return; return;
}
m_itemMap[ slotId ] = pItem; m_itemMap[ slotId ] = pItem;
} }

View file

@ -32,6 +32,7 @@
#include "Territory/HousingZone.h" #include "Territory/HousingZone.h"
#include "Territory/InstanceContent.h" #include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h" #include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
#include "Event/EventDefs.h" #include "Event/EventDefs.h"
@ -60,6 +61,8 @@ Sapphire::World::Manager::DebugCommandMgr::DebugCommandMgr()
registerCommand( "script", &DebugCommandMgr::script, "Server script utilities.", 1 ); registerCommand( "script", &DebugCommandMgr::script, "Server script utilities.", 1 );
registerCommand( "instance", &DebugCommandMgr::instance, "Instance utilities", 1 ); registerCommand( "instance", &DebugCommandMgr::instance, "Instance utilities", 1 );
registerCommand( "questbattle", &DebugCommandMgr::questBattle, "Quest battle 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( "qb", &DebugCommandMgr::questBattle, "Quest battle utilities", 1 );
registerCommand( "housing", &DebugCommandMgr::housing, "Housing 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 ) if( player.getLevelForClass( static_cast< Common::ClassJob > ( id ) ) == 0 )
{ {
player.setLevelForClass( 1, static_cast< Common::ClassJob > ( id ) ); 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.setClassJob( static_cast< Common::ClassJob > ( id ) ); player.sendModel();
player.sendModel(); player.sendItemLevel();
player.sendItemLevel(); player.calculateStats();
player.calculateStats(); player.sendStats();
player.sendStats(); player.sendStatusEffectUpdate();
player.sendStatusEffectUpdate(); player.sendStatusUpdate();
player.sendStatusUpdate();
} }
else if( subCommand == "cfpenalty" ) 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; 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.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 else
{ {
@ -1290,3 +1299,137 @@ void Sapphire::World::Manager::DebugCommandMgr::housing( char* data, Entity::Pla
player.sendDebug( "Unknown sub command." ); 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 instance( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command );
void questBattle( 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) ; 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 ] ); name[ 0 ] = toupper( name[ 0 ] );
return name; 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: case Event::EventHandler::EventHandlerType::Warp:

View file

@ -662,6 +662,14 @@ void Sapphire::World::Manager::HousingMgr::createHouse( Sapphire::HousePtr house
db.execute( stmt ); 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 ) void Sapphire::World::Manager::HousingMgr::buildPresetEstate( Entity::Player& player, uint8_t plotNum, uint32_t presetCatalogId )
{ {
auto hZone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentTerritory() ); auto hZone = std::dynamic_pointer_cast< HousingZone >( player.getCurrentTerritory() );
@ -903,6 +911,8 @@ void Sapphire::World::Manager::HousingMgr::updateHouseModels( Sapphire::HousePtr
{ {
assert( house ); assert( house );
house->clearModelCache();
auto& containers = getEstateInventory( house->getLandIdent() ); auto& containers = getEstateInventory( house->getLandIdent() );
auto extContainer = containers.find( static_cast< uint16_t >( InventoryType::HousingExteriorAppearance ) ); 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() ); 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 ); 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: private:
Inventory::HousingItemPtr getHousingItemFromPlayer( Entity::Player& player, Common::InventoryType type, uint8_t slot ); 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 createHouse( HousePtr house ) const;
void deleteHouse( HousePtr house ) const;
/*! /*!
* @brief Gets the next available house id * @brief Gets the next available house id
* @return 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() ) for( auto& item : container->getItemMap() )
{ {
if( !item.second )
continue;
saveHousingContainerItem( u64ident, container->getId(), item.first, item.second->getUId() ); 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->setUInt64( 1, item->getUId() );
stmt->setUInt( 2, pos.x ); stmt->setDouble( 2, static_cast< double >( pos.x ) );
stmt->setUInt( 3, pos.y ); stmt->setDouble( 3, static_cast< double >( pos.y ) );
stmt->setUInt( 4, pos.z ); stmt->setDouble( 4, static_cast< double >( pos.z ) );
stmt->setInt( 5, rot ); stmt->setDouble( 5, static_cast< double >( rot ) );
stmt->setUInt( 6, pos.x ); stmt->setDouble( 6, static_cast< double >( pos.x ) );
stmt->setUInt( 7, pos.y ); stmt->setDouble( 7, static_cast< double >( pos.y ) );
stmt->setUInt( 8, pos.z ); stmt->setDouble( 8, static_cast< double >( pos.z ) );
stmt->setInt( 9, rot ); stmt->setDouble( 9, static_cast< double >( rot ) );
db.execute( stmt ); db.execute( stmt );
} }

View file

@ -32,51 +32,51 @@ uint16_t Sapphire::World::Manager::ItemMgr::getCharaEquipSlotCategoryToArmoryId(
switch( slot ) switch( slot )
{ {
case Common::EquipSlotCategory::CharaHead: case Common::EquipSlotCategory::Head:
return Common::ArmoryHead; return Common::ArmoryHead;
case Common::EquipSlotCategory::CharaBody: case Common::EquipSlotCategory::Body:
//case Common::EquipSlotCategory::BodyDisallowHead: case Common::EquipSlotCategory::BodyDisallowHead:
//case Common::EquipSlotCategory::BodyDisallowHandsLegsFeet: case Common::EquipSlotCategory::BodyDisallowHandsLegsFeet:
//case Common::EquipSlotCategory::BodyDisallowAll: case Common::EquipSlotCategory::BodyDisallowAll:
//case Common::EquipSlotCategory::BodyDisallowHands: case Common::EquipSlotCategory::BodyDisallowHands:
//case Common::EquipSlotCategory::BodyDisallowLegsFeet: case Common::EquipSlotCategory::BodyDisallowLegsFeet:
return Common::ArmoryBody; return Common::ArmoryBody;
case Common::EquipSlotCategory::CharaEars: case Common::EquipSlotCategory::Ears:
return Common::ArmoryEar; return Common::ArmoryEar;
case Common::EquipSlotCategory::CharaFeet: case Common::EquipSlotCategory::Feet:
return Common::ArmoryFeet; return Common::ArmoryFeet;
case Common::EquipSlotCategory::CharaHands: case Common::EquipSlotCategory::Hands:
return Common::ArmoryHand; return Common::ArmoryHand;
case Common::EquipSlotCategory::CharaLegs: case Common::EquipSlotCategory::Legs:
//case Common::EquipSlotCategory::LegsDisallowFeet: case Common::EquipSlotCategory::LegsDisallowFeet:
return Common::ArmoryLegs; return Common::ArmoryLegs;
case Common::EquipSlotCategory::CharaMainHand: case Common::EquipSlotCategory::MainHand:
//case Common::EquipSlotCategory::MainTwoHandedWeapon: case Common::EquipSlotCategory::MainTwoHandedWeapon:
//case Common::EquipSlotCategory::MainOrOffHand: //case Common::EquipSlotCategory::MainOrOffHand:
return Common::ArmoryMain; return Common::ArmoryMain;
case Common::EquipSlotCategory::CharaOffHand: case Common::EquipSlotCategory::OffHand:
return Common::ArmoryOff; return Common::ArmoryOff;
case Common::EquipSlotCategory::CharaRing: case Common::EquipSlotCategory::Ring:
return Common::ArmoryRing; return Common::ArmoryRing;
case Common::EquipSlotCategory::CharaWaist: case Common::EquipSlotCategory::Waist:
return Common::ArmoryWaist; return Common::ArmoryWaist;
case Common::EquipSlotCategory::CharaWrist: case Common::EquipSlotCategory::Wrist:
return Common::ArmoryWrist; return Common::ArmoryWrist;
case Common::EquipSlotCategory::CharaNeck: case Common::EquipSlotCategory::Neck:
return Common::ArmoryNeck; return Common::ArmoryNeck;
case Common::EquipSlotCategory::CharaSoulCrystal: case Common::EquipSlotCategory::SoulCrystal:
return Common::ArmorySoulCrystal; return Common::ArmorySoulCrystal;
default: 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/ZonePosition.h"
#include "Territory/InstanceContent.h" #include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h" #include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "TerritoryMgr.h" #include "TerritoryMgr.h"
#include "HousingMgr.h" #include "HousingMgr.h"
@ -301,7 +302,28 @@ Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createQuestBattle
if( !pQuestBattleInfo ) if( !pQuestBattleInfo )
return nullptr; 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 ) if( !pQuestInfo )
return nullptr; 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 ); Logger::debug( "Starting instance for QuestBattle id: {0} ({1})", questBattleId, pQuestInfo->name );
auto pZone = make_QuestBattle( pQuestBattleInfo, pContentFinderCondition->territoryType, getNextInstanceId(), auto pZone = make_QuestBattle( pQuestBattleInfo, pContentFinderCondition->territoryType, getNextInstanceId(),
pTeri->name, pQuestInfo->name, questBattleId ); pTeri->name, pQuestInfo->name, questBattleId, contentFinderConditionId );
pZone->init(); pZone->init();
m_questBattleIdToInstanceMap[ questBattleId ][ pZone->getGuId() ] = pZone; 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 ); Logger::debug( "Starting instance for InstanceContent id: {0} ({1})", instanceContentId, pContentFinderCondition->name );
auto pZone = make_InstanceContent( pInstanceContent, pContentFinderCondition->territoryType, getNextInstanceId(), auto pZone = make_InstanceContent( pInstanceContent, pContentFinderCondition->territoryType, getNextInstanceId(),
pTeri->name, pContentFinderCondition->name, instanceContentId ); pTeri->name, pContentFinderCondition->name, instanceContentId, contentFinderConditionId );
pZone->init(); pZone->init();
m_instanceContentIdToInstanceMap[ instanceContentId ][ pZone->getGuId() ] = pZone; m_instanceContentIdToInstanceMap[ instanceContentId ][ pZone->getGuId() ] = pZone;
@ -360,6 +382,68 @@ Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::createInstanceCon
return pZone; 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 ) Sapphire::TerritoryPtr Sapphire::World::Manager::TerritoryMgr::findOrCreateHousingInterior( const Common::LandIdent landIdent )
{ {
// check if zone already spawned first // check if zone already spawned first
@ -444,11 +528,21 @@ bool Sapphire::World::Manager::TerritoryMgr::removeTerritoryInstance( uint32_t g
m_instanceZoneSet.erase( pZone ); m_instanceZoneSet.erase( pZone );
m_territorySet.erase( pZone ); m_territorySet.erase( pZone );
if( isInstanceContentTerritory( pZone->getTerritoryTypeId() ) ) if( pZone->getAsInstanceContent() )
{ {
auto instance = std::dynamic_pointer_cast< InstanceContent >( pZone ); auto instance = std::dynamic_pointer_cast< InstanceContent >( pZone );
m_instanceContentIdToInstanceMap[ instance->getInstanceContentId() ].erase( pZone->getGuId() ); 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 else
m_territoryTypeIdToInstanceGuidMap[ pZone->getTerritoryTypeId() ].erase( pZone->getGuId() ); m_territoryTypeIdToInstanceGuidMap[ pZone->getTerritoryTypeId() ].erase( pZone->getGuId() );
@ -540,6 +634,7 @@ void Sapphire::World::Manager::TerritoryMgr::updateTerritoryInstances( uint64_t
// remove zone from maps // remove zone from maps
m_territorySet.erase( zone ); m_territorySet.erase( zone );
m_guIdToTerritoryPtrMap.erase( zone->getGuId() );
it = m_landIdentToTerritoryPtrMap.erase( it ); it = m_landIdentToTerritoryPtrMap.erase( it );
} }
else else
@ -615,7 +710,7 @@ bool Sapphire::World::Manager::TerritoryMgr::movePlayer( TerritoryPtr pZone, Sap
if( pHousing ) if( pHousing )
pPlayer->setTerritoryId( pHousing->getLandSetId() ); pPlayer->setTerritoryId( pHousing->getLandSetId() );
} }
else if( isInstanceContentTerritory( pZone->getTerritoryTypeId() ) ) else if( pZone->getAsInstanceContent() || pZone->getAsQuestBattle() || pZone->getAsPublicContent() )
{ {
pPlayer->setTerritoryId( pZone->getGuId() ); pPlayer->setTerritoryId( pZone->getGuId() );
} }
@ -627,14 +722,27 @@ bool Sapphire::World::Manager::TerritoryMgr::movePlayer( TerritoryPtr pZone, Sap
// mark character as zoning in progress // mark character as zoning in progress
pPlayer->setLoadingComplete( false ); pPlayer->setLoadingComplete( false );
bool zoneChanged = true;
if( pPlayer->getLastPing() != 0 && pPlayer->getCurrentTerritory() ) 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 ); if( zoneChanged )
pZone->pushActor( pPlayer ); {
pPlayer->setCurrentZone( pZone );
pZone->pushActor( pPlayer );
// map player to instanceId so it can be tracked. // map player to instanceId so it can be tracked.
m_playerIdToInstanceMap[ pPlayer->getId() ] = pZone->getGuId(); m_playerIdToInstanceMap[ pPlayer->getId() ] = pZone->getGuId();
}
else
{
pPlayer->removeFromInRange();
pPlayer->clearInRangeSet();
}
pPlayer->sendZonePackets(); pPlayer->sendZonePackets();

View file

@ -118,6 +118,9 @@ namespace Sapphire::World::Manager
TerritoryPtr createQuestBattle( uint32_t contentFinderConditionId ); 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 ); void createAndJoinQuestBattle( Entity::Player& player, uint16_t contentFinderConditionId );
TerritoryPtr findOrCreateHousingInterior( const Common::LandIdent landIdent ); TerritoryPtr findOrCreateHousingInterior( const Common::LandIdent landIdent );
@ -179,6 +182,7 @@ namespace Sapphire::World::Manager
using InstanceContentIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >; using InstanceContentIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >;
using QuestBattleIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >; using QuestBattleIdToInstanceMap = std::unordered_map< uint16_t, InstanceIdToTerritoryPtrMap >;
using QuestBattleIdToContentFinderCondMap = std::unordered_map< uint16_t, uint16_t >; 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 PlayerIdToInstanceIdMap = std::unordered_map< uint32_t, uint32_t >;
using PositionMap = std::unordered_map< int32_t, ZonePositionPtr >; using PositionMap = std::unordered_map< int32_t, ZonePositionPtr >;
using InstanceIdList = std::vector< uint32_t >; using InstanceIdList = std::vector< uint32_t >;
@ -196,9 +200,12 @@ namespace Sapphire::World::Manager
/*! map holding actual instances of InstanceContent */ /*! map holding actual instances of InstanceContent */
InstanceContentIdToInstanceMap m_instanceContentIdToInstanceMap; InstanceContentIdToInstanceMap m_instanceContentIdToInstanceMap;
/*! map holding actual instances of InstanceContent */ /*! map holding actual instances of QuestBattle */
QuestBattleIdToInstanceMap m_questBattleIdToInstanceMap; QuestBattleIdToInstanceMap m_questBattleIdToInstanceMap;
/*! map holding actual instances of PublicContent */
PublicContentIdToInstanceMap m_publicContentIdToInstanceMap;
/*! flat map for easier lookup of instances by guid */ /*! flat map for easier lookup of instances by guid */
InstanceIdToTerritoryPtrMap m_guIdToTerritoryPtrMap; 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::ReqPlaceHousingItem, "ReqPlaceHousingItem", &GameConnection::reqPlaceHousingItem );
setZoneHandler( ClientZoneIpcType::HousingUpdateObjectPosition, "HousingUpdateObjectPosition", setZoneHandler( ClientZoneIpcType::HousingUpdateObjectPosition, "HousingUpdateObjectPosition",
&GameConnection::reqMoveHousingItem ); &GameConnection::reqMoveHousingItem );
setZoneHandler( ClientZoneIpcType::HousingEditExterior, "HousingEditExterior", &GameConnection::housingEditExterior );
setZoneHandler( ClientZoneIpcType::HousingEditInterior, "HousingEditInterior", &GameConnection::housingEditInterior );
setZoneHandler( ClientZoneIpcType::TalkEventHandler, "EventHandlerTalk", &GameConnection::eventHandlerTalk ); setZoneHandler( ClientZoneIpcType::TalkEventHandler, "EventHandlerTalk", &GameConnection::eventHandlerTalk );
setZoneHandler( ClientZoneIpcType::EmoteEventHandler, "EventHandlerEmote", &GameConnection::eventHandlerEmote ); setZoneHandler( ClientZoneIpcType::EmoteEventHandler, "EventHandlerEmote", &GameConnection::eventHandlerEmote );
@ -100,10 +102,12 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::EnterTeriEventHandler, "EventHandlerEnterTeri", setZoneHandler( ClientZoneIpcType::EnterTeriEventHandler, "EventHandlerEnterTeri",
&GameConnection::eventHandlerEnterTerritory ); &GameConnection::eventHandlerEnterTerritory );
setZoneHandler( ClientZoneIpcType::ReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandlerReturn ); setZoneHandler( ClientZoneIpcType::ReturnEventHandler, "ReturnEventHandler", &GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler, "EventHandlerReturn", setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler, "TradeReturnEventHandler",
&GameConnection::eventHandlerReturn ); &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", setZoneHandler( ClientZoneIpcType::ShopEventHandler, "ShopEventHandler",
&GameConnection::eventHandlerShop ); &GameConnection::eventHandlerShop );
@ -118,7 +122,7 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::CFRegisterDuty, "CFRegisterDuty", &GameConnection::cfRegisterDuty ); setZoneHandler( ClientZoneIpcType::CFRegisterDuty, "CFRegisterDuty", &GameConnection::cfRegisterDuty );
setZoneHandler( ClientZoneIpcType::CFRegisterRoulette, "CFRegisterRoulette", &GameConnection::cfRegisterRoulette ); setZoneHandler( ClientZoneIpcType::CFRegisterRoulette, "CFRegisterRoulette", &GameConnection::cfRegisterRoulette );
setZoneHandler( ClientZoneIpcType::CFCommenceHandler, "CFDutyAccepted", &GameConnection::cfDutyAccepted ); setZoneHandler( ClientZoneIpcType::CFCommenceHandler, "CFDutyAccepted", &GameConnection::cfDutyAccepted );
setZoneHandler( ClientZoneIpcType::CFCancelHandler, "CFCancel", &GameConnection::cfCancel ); //setZoneHandler( ClientZoneIpcType::CFCancelHandler, "CFCancel", &GameConnection::cfCancel );
setZoneHandler( ClientZoneIpcType::ReqEquipDisplayFlagsChange, "ReqEquipDisplayFlagsChange", setZoneHandler( ClientZoneIpcType::ReqEquipDisplayFlagsChange, "ReqEquipDisplayFlagsChange",
&GameConnection::reqEquipDisplayFlagsHandler ); &GameConnection::reqEquipDisplayFlagsHandler );

View file

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

View file

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

View file

@ -475,6 +475,14 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
break; break;
} }
case ClientTriggerType::RequestEstateHallRemoval:
{
auto& housingMgr = Common::Service< HousingMgr >::ref();
housingMgr.removeHouse( player, static_cast< uint16_t >( param11 ) );
break;
}
case ClientTriggerType::UpdateEstateGuestAccess: case ClientTriggerType::UpdateEstateGuestAccess:
{ {
auto canTeleport = ( param2 & 0xFF ) == 1; 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 ); 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; 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: default:
{ {
Logger::debug( "[{0}] Unhandled action: {1:04X}", m_pSession->getId(), commandId ); Logger::debug( "[{0}] Unhandled action: {1:04X}", m_pSession->getId(), commandId );

View file

@ -23,6 +23,7 @@
#include "Territory/InstanceContent.h" #include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h" #include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Session.h" #include "Session.h"
@ -59,6 +60,14 @@ void Sapphire::Network::GameConnection::eventHandlerTalk( const Packets::FFXIVAR
{ {
instance->onTalk( player, eventId, actorId ); 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 ) && else if( !scriptMgr.onTalk( player, actorId, eventId ) &&
eventType == Event::EventHandler::EventHandlerType::Quest ) 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() ); player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 1, player.getZoneId() );
instance->onEnterTerritory( player, eventId, param1, param2 ); 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 else
{ {
player.eventStart( player.getId(), eventId, Event::EventHandler::EnterTerritory, 0, player.getZoneId() ); 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 ); 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/Territory.h"
#include "Territory/HousingZone.h" #include "Territory/HousingZone.h"
#include "Territory/Housing/HousingInteriorTerritory.h"
#include "Territory/Land.h" #include "Territory/Land.h"
#include "Territory/ZonePosition.h" #include "Territory/ZonePosition.h"
#include "Territory/House.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, void Sapphire::Network::GameConnection::finishLoadingHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player ) Entity::Player& player )
{ {
player.sendQuestInfo(); if( player.isLogin() )
{
player.sendQuestInfo();
// TODO: load and save this data instead of hardcoding // TODO: load and save this data instead of hardcoding
auto gcPacket = makeZonePacket< FFXIVGCAffiliation >( player.getId() ); auto gcPacket = makeZonePacket< FFXIVGCAffiliation >( player.getId() );
gcPacket->data().gcId = player.getGc(); gcPacket->data().gcId = player.getGc();
gcPacket->data().gcRank[ 0 ] = player.getGcRankArray()[ 0 ]; gcPacket->data().gcRank[ 0 ] = player.getGcRankArray()[ 0 ];
gcPacket->data().gcRank[ 1 ] = player.getGcRankArray()[ 1 ]; gcPacket->data().gcRank[ 1 ] = player.getGcRankArray()[ 1 ];
gcPacket->data().gcRank[ 2 ] = player.getGcRankArray()[ 2 ]; gcPacket->data().gcRank[ 2 ] = player.getGcRankArray()[ 2 ];
player.queuePacket( gcPacket ); player.queuePacket( gcPacket );
}
player.getCurrentTerritory()->onFinishLoading( player ); player.getCurrentTerritory()->onFinishLoading( player );
@ -607,7 +611,7 @@ void Sapphire::Network::GameConnection::performNoteHandler( const Packets::FFXIV
Entity::Player& player ) Entity::Player& player )
{ {
auto performPacket = makeZonePacket< FFXIVIpcPerformNote >( player.getId() ); 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 ); 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 ); 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, void Sapphire::Network::GameConnection::marketBoardSearch( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player ) Entity::Player& player )
{ {
@ -746,12 +790,12 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets::
break; break;
player.setPos( packet.data().position ); player.setPos( packet.data().position );
player.setRot( Util::floatFromUInt16Rot( param4 ) );
if( emote == 0x32 && player.hasInRangeActor() ) if( emote == 0x32 && player.hasInRangeActor() )
{ {
auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() );
setpos->data().r16 = param4; setpos->data().r16 = param4;
setpos->data().waitForLoad = 18; setpos->data().waitForLoad = 18;
setpos->data().unknown1 = 1;
setpos->data().x = packet.data().position.x; setpos->data().x = packet.data().position.x;
setpos->data().y = packet.data().position.y; setpos->data().y = packet.data().position.y;
setpos->data().z = packet.data().position.z; setpos->data().z = packet.data().position.z;
@ -781,7 +825,6 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets::
auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() );
setpos->data().r16 = param2; setpos->data().r16 = param2;
setpos->data().waitForLoad = 18; setpos->data().waitForLoad = 18;
setpos->data().unknown1 = 2;
setpos->data().x = packet.data().position.x; setpos->data().x = packet.data().position.x;
setpos->data().y = packet.data().position.y; setpos->data().y = packet.data().position.y;
setpos->data().z = packet.data().position.z; setpos->data().z = packet.data().position.z;

View file

@ -22,15 +22,15 @@ namespace Sapphire::Network::Packets::Server
uint32_t param3 = 0, uint32_t param3 = 0,
uint32_t param4 = 0, uint32_t param4 = 0,
uint32_t param5 = 0, uint32_t param5 = 0,
uint32_t padding1 = 0 ) : uint32_t param6 = 0 ) :
ZoneChannelPacket< FFXIVIpcActorControlSelf >( actorId, actorId ) ZoneChannelPacket< FFXIVIpcActorControlSelf >( actorId, actorId )
{ {
initialize( category, param1, param2, param3, param4, param5 ); initialize( category, param1, param2, param3, param4, param5, param6 );
}; };
private: private:
void initialize( uint16_t category, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4, 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.padding = 0;
m_data.category = category; m_data.category = category;
@ -39,6 +39,7 @@ namespace Sapphire::Network::Packets::Server
m_data.param3 = param3; m_data.param3 = param3;
m_data.param4 = param4; m_data.param4 = param4;
m_data.param5 = param5; 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 ) ); memcpy( m_data.aetheryte, player.getAetheryteArray(), sizeof( m_data.aetheryte ) );
// Set the class levels and exp. // 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.levels[ i ] = player.getClassArray()[ i ];
m_data.exp[ i ] = player.getExpArray()[ i ]; m_data.exp[ i ] = player.getExpArray()[ i ];

View file

@ -8,6 +8,8 @@
#include "Forwards.h" #include "Forwards.h"
#include "Inventory/Item.h" #include "Inventory/Item.h"
#include "StatusEffect/StatusEffect.h" #include "StatusEffect/StatusEffect.h"
#include "Territory/Territory.h"
#include "Event/Director.h"
namespace Sapphire::Network::Packets::Server namespace Sapphire::Network::Packets::Server
{ {
@ -143,6 +145,10 @@ namespace Sapphire::Network::Packets::Server
m_data.effect[ effect.first ].param = effect.second->getParam(); 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 <string>
#include <typeinfo> #include <typeinfo>
#include <typeindex> #include <typeindex>
#include <Event/EventHandler.h>
#include "NativeScriptApi.h" #include "NativeScriptApi.h"
#include <cassert> #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 ) : 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 #define NATIVE_SCRIPT_API
#include <string> #include <string>
#include <Event/EventHandler.h>
#include "ForwardsZone.h" #include "ForwardsZone.h"
#ifdef _MSC_VER #ifdef _MSC_VER
@ -166,6 +167,10 @@ namespace Sapphire::ScriptAPI
uint32_t catalogId ); uint32_t catalogId );
virtual void onEObjHit( Sapphire::Entity::Player& player, uint64_t actorId, uint32_t actionId ); 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 ); 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 #endif

View file

@ -7,6 +7,7 @@
#include "Territory/Territory.h" #include "Territory/Territory.h"
#include "Territory/InstanceContent.h" #include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h" #include "Territory/QuestBattle.h"
#include "Territory/PublicContent.h"
#include "Actor/Player.h" #include "Actor/Player.h"
#include "Actor/EventObject.h" #include "Actor/EventObject.h"
#include "ServerMgr.h" #include "ServerMgr.h"
@ -530,8 +531,7 @@ Sapphire::Scripting::NativeScriptMgr& Sapphire::Scripting::ScriptMgr::getNativeS
return *m_nativeScriptMgr; return *m_nativeScriptMgr;
} }
bool bool Sapphire::Scripting::ScriptMgr::onDutyComplete( Sapphire::QuestBattlePtr instance, Sapphire::Entity::Player& player )
Sapphire::Scripting::ScriptMgr::onDutyComplete( Sapphire::QuestBattlePtr instance, Sapphire::Entity::Player& player )
{ {
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::QuestBattleScript >( instance->getDirectorId() ); auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::QuestBattleScript >( instance->getDirectorId() );
if( script ) if( script )
@ -542,3 +542,77 @@ Sapphire::Scripting::ScriptMgr::onDutyComplete( Sapphire::QuestBattlePtr instanc
return false; 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 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 ); bool loadDir( const std::string& dirname, std::set< std::string >& files, const std::string& ext );
NativeScriptMgr& getNativeScriptHandler(); NativeScriptMgr& getNativeScriptHandler();

View file

@ -41,6 +41,7 @@
#include "Manager/RNGMgr.h" #include "Manager/RNGMgr.h"
#include "Manager/NaviMgr.h" #include "Manager/NaviMgr.h"
#include "Manager/ActionMgr.h" #include "Manager/ActionMgr.h"
#include "Manager/MapMgr.h"
#include "Territory/InstanceObjectCache.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 >(); auto pInstanceObjCache = std::make_shared< Sapphire::InstanceObjectCache >();
Common::Service< Sapphire::InstanceObjectCache >::set( pInstanceObjCache ); 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 >(); auto pActionMgr = std::make_shared< Manager::ActionMgr >();
Common::Service< Manager::ActionMgr >::set( pActionMgr ); Common::Service< Manager::ActionMgr >::set( pActionMgr );

View file

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

View file

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

View file

@ -321,6 +321,23 @@ Sapphire::Entity::EventObjectPtr Sapphire::HousingZone::registerEstateEntranceEO
return eObj; 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 ) void Sapphire::HousingZone::updateYardObjects( Sapphire::Common::LandIdent ident )
{ {
auto& housingMgr = Common::Service< World::Manager::HousingMgr >::ref(); auto& housingMgr = Common::Service< World::Manager::HousingMgr >::ref();

View file

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

View file

@ -28,7 +28,7 @@ namespace Sapphire
uint32_t guId, uint32_t guId,
const std::string& internalName, const std::string& internalName,
const std::string& contentName, const std::string& contentName,
uint32_t instanceContentId ); uint32_t instanceContentId, uint16_t contentFinderConditionId = 0 );
virtual ~InstanceContent(); 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... // 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 bgLgbPath( path + "/level/bg.lgb" );
std::string planmapLgbPath( path + "/level/planmap.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 > bgSection;
std::vector< char > planmapSection; 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 > bgFile;
std::unique_ptr< xiv::dat::File > planmap_file; 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 try
{ {
bgFile = exdData.getGameData()->getFile( bgLgbPath ); bgFile = exdData.getGameData()->getFile( bgLgbPath );
planmap_file = exdData.getGameData()->getFile( planmapLgbPath ); planmap_file = exdData.getGameData()->getFile( planmapLgbPath );
planevent_file = exdData.getGameData()->getFile( planeventLgbPath );
} }
catch( std::runtime_error& ) catch( std::runtime_error& )
{ {
@ -60,6 +67,7 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
bgSection = bgFile->access_data_sections().at( 0 ); bgSection = bgFile->access_data_sections().at( 0 );
planmapSection = planmap_file->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; std::vector< std::string > stringList;
@ -67,8 +75,23 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
LGB_FILE bgLgb( &bgSection[ 0 ], "bg" ); LGB_FILE bgLgb( &bgSection[ 0 ], "bg" );
LGB_FILE planmapLgb( &planmapSection[ 0 ], "planmap" ); 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; uint32_t max_index = 0;
for( const auto& lgb : lgbList ) for( const auto& lgb : lgbList )
@ -92,6 +115,16 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
auto pPopRange = std::reinterpret_pointer_cast< LGB_POP_RANGE_ENTRY >( pEntry ); auto pPopRange = std::reinterpret_pointer_cast< LGB_POP_RANGE_ENTRY >( pEntry );
m_popRangeCache.insert( id, pPopRange ); 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"; std::cout << "\n";
Logger::debug( Logger::debug(
"InstanceObjectCache Cached: MapRange: {} ExitRange: {} PopRange: {}", "InstanceObjectCache Cached: MapRange: {} ExitRange: {} PopRange: {} ENpc: {} Eobj: {}",
m_mapRangeCache.size(), m_exitRangeCache.size(), m_popRangeCache.size() 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 ) Sapphire::InstanceObjectCache::getPopRange( uint16_t zoneId, uint32_t popRangeId )
{ {
return m_popRangeCache.get( zoneId, 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_MAP_RANGE_ENTRY;
struct LGB_EXIT_RANGE_ENTRY; struct LGB_EXIT_RANGE_ENTRY;
struct LGB_POP_RANGE_ENTRY; struct LGB_POP_RANGE_ENTRY;
struct LGB_ENPC_ENTRY;
struct LGB_EOBJ_ENTRY;
namespace Sapphire namespace Sapphire
@ -37,6 +39,16 @@ namespace Sapphire
} }
return nullptr; 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 ) 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 MapRangePtr = std::shared_ptr< LGB_MAP_RANGE_ENTRY >;
using ExitRangePtr = std::shared_ptr< LGB_EXIT_RANGE_ENTRY >; using ExitRangePtr = std::shared_ptr< LGB_EXIT_RANGE_ENTRY >;
using PopRangePtr = std::shared_ptr< LGB_POP_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();
~InstanceObjectCache() = default; ~InstanceObjectCache() = default;
@ -72,11 +89,18 @@ namespace Sapphire
MapRangePtr getMapRange( uint16_t zoneId, uint32_t mapRangeId ); MapRangePtr getMapRange( uint16_t zoneId, uint32_t mapRangeId );
ExitRangePtr getExitRange( uint16_t zoneId, uint32_t exitRangeId ); ExitRangePtr getExitRange( uint16_t zoneId, uint32_t exitRangeId );
PopRangePtr getPopRange( uint16_t zoneId, uint32_t popRangeId ); 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: private:
ObjectCache< LGB_MAP_RANGE_ENTRY > m_mapRangeCache; ObjectCache< LGB_MAP_RANGE_ENTRY > m_mapRangeCache;
ObjectCache< LGB_EXIT_RANGE_ENTRY > m_exitRangeCache; ObjectCache< LGB_EXIT_RANGE_ENTRY > m_exitRangeCache;
ObjectCache< LGB_POP_RANGE_ENTRY > m_popRangeCache; 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; 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 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; Sapphire::HousePtr m_pHouse;
//item storage
uint16_t m_maxPlacedExternalItems;
uint16_t m_maxPlacedInternalItems;
//price //price
uint32_t m_initPrice; uint32_t m_initPrice;
uint32_t m_nextDrop; 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, uint32_t guId,
const std::string& internalName, const std::string& internalName,
const std::string& contentName, const std::string& contentName,
uint32_t questBattleId ) : uint32_t questBattleId, uint16_t contentFinderConditionId ) :
Territory( static_cast< uint16_t >( territoryType ), guId, internalName, contentName ), Territory( static_cast< uint16_t >( territoryType ), guId, internalName, contentName ),
Director( Event::Director::QuestBattle, questBattleId ), Director( Event::Director::QuestBattle, questBattleId, contentFinderConditionId ),
m_pBattleDetails( pBattleDetails ), m_pBattleDetails( pBattleDetails ),
m_questBattleId( questBattleId ), m_questBattleId( questBattleId ),
m_state( Created ), m_state( Created ),
@ -330,7 +330,7 @@ void Sapphire::QuestBattle::onTalk( Sapphire::Entity::Player& player, uint32_t e
return; return;
if( auto onTalkHandler = it->second->getOnTalkHandler() ) if( auto onTalkHandler = it->second->getOnTalkHandler() )
onTalkHandler( player, it->second, getAsQuestBattle(), actorId ); onTalkHandler( player, it->second, getAsQuestBattle(), eventId, actorId );
else else
player.sendDebug( "No onTalk handler found for interactable eobj with EObjID#{0}, eventId#{1} ", player.sendDebug( "No onTalk handler found for interactable eobj with EObjID#{0}, eventId#{1} ",
it->second->getObjectId(), eventId ); it->second->getObjectId(), eventId );

View file

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

View file

@ -19,6 +19,7 @@
#include "Territory.h" #include "Territory.h"
#include "InstanceContent.h" #include "InstanceContent.h"
#include "QuestBattle.h" #include "QuestBattle.h"
#include "PublicContent.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
#include "Navi/NaviProvider.h" #include "Navi/NaviProvider.h"
@ -268,12 +269,7 @@ void Sapphire::Territory::pushActor( Entity::ActorPtr pActor )
void Sapphire::Territory::removeActor( Entity::ActorPtr pActor ) void Sapphire::Territory::removeActor( Entity::ActorPtr pActor )
{ {
float mx = pActor->getPos().x; Cell* pCell = pActor->getCellPtr();
float my = pActor->getPos().z;
uint32_t cx = getPosX( mx );
uint32_t cy = getPosY( my );
Cell* pCell = getCellPtr( cx, cy );
if( pCell && pCell->hasActor( pActor ) ) if( pCell && pCell->hasActor( pActor ) )
pCell->removeActorFromCell( pActor ); pCell->removeActorFromCell( pActor );
@ -780,6 +776,11 @@ Sapphire::Entity::EventObjectPtr Sapphire::Territory::getEObj( uint32_t objId )
return obj->second; 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() Sapphire::InstanceContentPtr Sapphire::Territory::getAsInstanceContent()
{ {
return std::dynamic_pointer_cast< InstanceContent, Territory >( shared_from_this() ); 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() ); 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() uint32_t Sapphire::Territory::getNextEObjId()
{ {
return ++m_nextEObjId; return ++m_nextEObjId;
@ -1043,4 +1049,19 @@ void Sapphire::Territory::processEffectResults( uint64_t tickCount )
it = m_effectResults.erase( it ); 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 ); Entity::EventObjectPtr getEObj( uint32_t objId );
Event::DirectorPtr getAsDirector();
InstanceContentPtr getAsInstanceContent(); InstanceContentPtr getAsInstanceContent();
QuestBattlePtr getAsQuestBattle(); QuestBattlePtr getAsQuestBattle();
PublicContentPtr getAsPublicContent();
void updateSpawnPoints(); void updateSpawnPoints();
uint32_t getNextEffectSequence(); uint32_t getNextEffectSequence();
@ -179,6 +183,10 @@ namespace Sapphire
void addEffectResult( World::Action::EffectResultPtr result ); void addEffectResult( World::Action::EffectResultPtr result );
void processEffectResults( uint64_t tickCount ); void processEffectResults( uint64_t tickCount );
Entity::PlayerPtr getPlayer( uint32_t charId );
void foreachPlayer( std::function< void( Entity::PlayerPtr player ) > callback );
}; };
} }