1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-28 07:07: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

@ -6,3 +6,8 @@ std::string xiv::utils::bparse::extract_cstring( std::istream& i_stream, const s
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,
CharaSoulCrystal = 12,
/* following slots not seem to exist any more.
when multi-slot gear is moved into equipment slot, normal slot listed above is used.
client will move any incompatible gears into armory but no InventoryModifiyHandler is sent.
server need to move those silently in order to sync with client.
*/
/*! Cannot equip gear to offhand slot */
//MainTwoHandedWeapon = 13,
/*! Can be equipped in either main or offhand slot */
//MainOrOffHand = 14, // unused //MainOrOffHand = 14, // unused
/*! Cannot equip gear to head */ BodyDisallowHead = 15,
//BodyDisallowHead = 15, BodyDisallowHandsLegsFeet = 16,
/*! Cannot equip gear to hands, legs and feet slots */ SoulCrystal = 17,
//BodyDisallowHandsLegsFeet = 16, LegsDisallowFeet = 18,
/*! Cannot equip gear to feet slot */ BodyDisallowAll = 19,
//LegsDisallowFeet = 18, BodyDisallowHands = 20,
/*! Cannot equp gear to head, hands, legs, feet slots */ BodyDisallowLegsFeet = 21,
//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

View file

@ -25,9 +25,10 @@ enum class ActionCategory : uint8_t
System = 10, System = 10,
Artillery = 11, Artillery = 11,
Mount = 12, Mount = 12,
Glamour = 13, Special = 13,
ItemManipulation = 14, ItemManipulation = 14,
AdrenalineRush = 15, AdrenalineRush = 15,
//1 = 16,
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -85,7 +86,7 @@ enum class BaseParam : uint8_t
Morale = 48, Morale = 48,
Enmity = 49, Enmity = 49,
EnmityReduction = 50, EnmityReduction = 50,
CarefulDesynthesis = 51, DesynthesisSkillGain = 51,
EXPBonus = 52, EXPBonus = 52,
Regen = 53, Regen = 53,
Refresh = 54, Refresh = 54,
@ -129,6 +130,21 @@ enum class BeastReputationRank : uint8_t
//BeastTribe.exd //BeastTribe.exd
enum class BeastTribe : uint8_t enum class BeastTribe : uint8_t
{ {
/* = 0,
1 = 1,
2 = 2,
3 = 3,
4 = 4,
5 = 5,
6 = 6,
7 = 7,
8 = 8,
9 = 9,
10 = 10,
11 = 11,
12 = 12,
13 = 13,
14 = 14,*/
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -174,6 +190,8 @@ enum class ClassJob : uint8_t
Bluemage = 36, Bluemage = 36,
Gunbreaker = 37, Gunbreaker = 37,
Dancer = 38, Dancer = 38,
// = 39,
//1 = 40,
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -200,10 +218,16 @@ enum class ContentType : uint8_t
DisciplesoftheHand = 17, DisciplesoftheHand = 17,
RetainerVentures = 18, RetainerVentures = 18,
GoldSaucer = 19, GoldSaucer = 19,
//1 = 20,
DeepDungeons = 21, DeepDungeons = 21,
//2 = 22,
//3 = 23,
WondrousTails = 24, WondrousTails = 24,
CustomDeliveries = 25, CustomDeliveries = 25,
Eureka = 26, Eureka = 26,
//4 = 27,
UltimateRaids = 28,
//5 = 29,
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -214,6 +238,7 @@ enum class EmoteCategory : uint8_t
General = 1, General = 1,
Special = 2, Special = 2,
Expressions = 3, Expressions = 3,
//1 = 4,
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -460,7 +485,20 @@ enum class ItemSearchCategory : uint8_t
RedMagesArms = 84, RedMagesArms = 84,
ScholarsArms = 85, ScholarsArms = 85,
GunbreakersArms = 86, GunbreakersArms = 86,
ThrowingWeapons = 87, DancersArms1 = 87,
/*1 = 88,
2 = 89,
3 = 90,
4 = 91,
5 = 92,
6 = 93,
7 = 94,
8 = 95,
9 = 96,
10 = 97,
11 = 98,
12 = 99,
13 = 100,*/
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -563,8 +601,13 @@ enum class Town : uint8_t
Gridania = 2, Gridania = 2,
Uldah = 3, Uldah = 3,
Ishgard = 4, Ishgard = 4,
// = 5,
//1 = 6,
Kugane = 7, Kugane = 7,
TheCrystarium = 10, //2 = 8,
//3 = 9,
Crystarium = 10,
//4 = 11,
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -672,14 +715,14 @@ enum class Weather : uint8_t
Moonlight1 = 98, Moonlight1 = 98,
Moonlight2 = 99, Moonlight2 = 99,
Moonlight3 = 100, Moonlight3 = 100,
FairSkies10 = 101, RedMoon = 101,
Scarlet = 102, Scarlet = 102,
Scarlet1 = 103, Scarlet1 = 103,
Scarlet2 = 104, Scarlet2 = 104,
FairSkies11 = 105, FairSkies10 = 105,
FairSkies12 = 106, FairSkies11 = 106,
FairSkies13 = 107, FairSkies12 = 107,
FairSkies14 = 108, FairSkies13 = 108,
Flames = 109, Flames = 109,
Tsunamis = 110, Tsunamis = 110,
Cyclones = 111, Cyclones = 111,
@ -701,7 +744,30 @@ enum class Weather : uint8_t
Termination2 = 127, Termination2 = 127,
Termination3 = 128, Termination3 = 128,
EverlastingLight1 = 129, EverlastingLight1 = 129,
Eruptions1 = 130,
Termination4 = 131, Termination4 = 131,
FairSkies14 = 132,
UmbralFlare = 133,
UmbralDuststorm = 134,
UmbralLevin = 135,
UmbralTempest = 136,
Starshower = 137,
Delirium = 138,
Clouds2 = 139,
Clouds3 = 140,
Irradiance1 = 141,
Irradiance2 = 142,
StormClouds1 = 143,
Firestorm = 144,
SpectralCurrent = 145,
//1 = 146,
Climactic = 147,
//2 = 148,
//3 = 149,
//4 = 150,
//5 = 151,
//6 = 152,
//7 = 153,
}; };
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -721,6 +787,7 @@ enum class HousingAppeal : uint8_t
Sanctum = 10, Sanctum = 10,
Venue = 11, Venue = 11,
Florist = 12, Florist = 12,
// = 13,
Library = 14, Library = 14,
PhotoStudio = 15, PhotoStudio = 15,
HauntedHouse = 16, HauntedHouse = 16,
@ -728,8 +795,10 @@ enum class HousingAppeal : uint8_t
Bathhouse = 18, Bathhouse = 18,
Garden = 19, Garden = 19,
FarEastern = 20, FarEastern = 20,
VisitorsWelcome = 21,
Bakery = 22,
UnderRenovation = 23,
ConcertHall = 24,
}; };
} }
#endif #endif

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

@ -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 InventoryEquipRecommendedItems = 0x01C9, // updated 5.58 hotfix
ReqPlaceHousingItem = 0x02AE, // updated 5.35 hotfix ReqPlaceHousingItem = 0x02D4, // updated 5.58 hotfix
BuildPresetHandler = 0x01C2, // updated 5.35 hotfix BuildPresetHandler = 0x0223, // updated 5.58 hotfix
TalkEventHandler = 0x02A4, // updated 5.35 hotfix TalkEventHandler = 0x0387, // updated 5.58 hotfix
EmoteEventHandler = 0x02C8, // updated 5.35 hotfix EmoteEventHandler = 0x00B0, // updated 5.58 hotfix
WithinRangeEventHandler = 0x0209, // updated 5.35 hotfix WithinRangeEventHandler = 0x02B6, // updated 5.58 hotfix
OutOfRangeEventHandler = 0x0319, // updated 5.35 hotfix OutOfRangeEventHandler = 0x03C5, // updated 5.58 hotfix
EnterTeriEventHandler = 0x0192, // updated 5.35 hotfix EnterTeriEventHandler = 0x01A7, // updated 5.58 hotfix
ShopEventHandler = 0x01F6, // updated 5.35 hotfix ShopEventHandler = 0x0384, // updated 5.58 hotfix
ReturnEventHandler = 0x00FA, // updated 5.58 hotfix
ReturnEventHandler = 0x02B4, // updated 5.35 hotfix TradeReturnEventHandler = 0x0339, // updated 5.58 hotfix
TradeReturnEventHandler = 0x00A4, // updated 5.35 hotfix TradeReturnEventHandler2 = 0x023C, // updated 5.58 hotfix
TradeMultipleReturnEventHander = 0x035C, // updated 5.35 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
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

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

@ -77,3 +77,8 @@ 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,6 +752,7 @@ bool Sapphire::Entity::Player::updateContainer( uint16_t storageId, uint8_t slot
case Bag: case Bag:
case CurrencyCrystal: case CurrencyCrystal:
{ {
if( writeToDb )
writeInventory( static_cast< InventoryType >( storageId ) ); writeInventory( static_cast< InventoryType >( storageId ) );
break; break;
} }
@ -767,6 +768,7 @@ 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 );
if( writeToDb )
writeInventory( static_cast< InventoryType >( storageId ) ); writeInventory( static_cast< InventoryType >( storageId ) );
break; break;
} }
@ -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,8 +1045,11 @@ void Sapphire::Entity::Player::removeQuestsCompleted( uint32_t questId )
uint8_t value = 0x80 >> bitIndex; uint8_t value = 0x80 >> bitIndex;
if( m_questCompleteFlags[ index ] & value )
m_questCompleteFlags[ index ] ^= value; m_questCompleteFlags[ index ] ^= value;
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
} }
bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optionalChoice ) bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optionalChoice )

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

@ -86,13 +86,19 @@ Sapphire::ItemPtr Sapphire::ItemContainer::getItem( uint8_t slotId )
return nullptr; return nullptr;
} }
if( m_itemMap.find( slotId ) == m_itemMap.end() )
return nullptr;
return m_itemMap[ slotId ]; return m_itemMap[ 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,15 +229,7 @@ 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();
@ -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() )
{
zoneChanged = pPlayer->getCurrentTerritory()->getGuId() != pZone->getGuId();
if( zoneChanged )
pPlayer->getCurrentTerritory()->removeActor( pPlayer ); pPlayer->getCurrentTerritory()->removeActor( pPlayer );
}
if( zoneChanged )
{
pPlayer->setCurrentZone( pZone ); pPlayer->setCurrentZone( pZone );
pZone->pushActor( pPlayer ); 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,66 +52,70 @@ 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 )
{ {
@ -121,3 +123,4 @@ void Sapphire::Network::GameConnection::cfCancel( const Packets::FFXIVARR_PACKET
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"
@ -385,6 +386,8 @@ 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 )
{
if( player.isLogin() )
{ {
player.sendQuestInfo(); player.sendQuestInfo();
@ -395,6 +398,7 @@ void Sapphire::Network::GameConnection::finishLoadingHandler( const Packets::FFX
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.
// else it will be sent on finish loading.
if( m_state == Created )
sendDirectorInit( player ); 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()
); );
} }
@ -122,3 +155,27 @@ Sapphire::InstanceObjectCache::PopRangePtr
{ {
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
@ -38,6 +40,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 )
{ {
if( m_objectCache.find( zoneId ) == m_objectCache.end() ) if( m_objectCache.find( zoneId ) == m_objectCache.end() )
@ -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;
@ -1044,3 +1050,18 @@ 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 );
}; };
} }