1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-22 04:37:47 +00:00

Merge pull request #948 from Skyliegirl33/actions-war

[3.x] Sync action branch to master
This commit is contained in:
Mordred 2023-08-01 15:56:39 +02:00 committed by GitHub
commit 6fd5720f74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 4039 additions and 688 deletions

View file

@ -227,6 +227,8 @@ struct LGB_GROUP
memcpy( (char*)&refs[0], buf + offset + header.LayerSetRef + layerSetReferencedList.LayerSets, layerSetReferencedList.LayerSetCount * sizeof( LayerSetReferenced ) ); memcpy( (char*)&refs[0], buf + offset + header.LayerSetRef + layerSetReferencedList.LayerSets, layerSetReferencedList.LayerSetCount * sizeof( LayerSetReferenced ) );
} }
entries.reserve( header.entryCount );
const auto entriesOffset = offset + header.entriesOffset; const auto entriesOffset = offset + header.entriesOffset;
for( auto i = 0; i < header.entryCount; ++i ) for( auto i = 0; i < header.entryCount; ++i )
{ {
@ -235,41 +237,53 @@ struct LGB_GROUP
try try
{ {
const auto type = *reinterpret_cast< LgbEntryType* >( buf + entryOffset ); const auto type = *reinterpret_cast< LgbEntryType* >( buf + entryOffset );
if( type == LgbEntryType::BgParts ) switch( type )
{ {
entries.push_back( std::make_shared< LGB_BGPARTS_ENTRY >( buf, entryOffset ) ); case LgbEntryType::BgParts:
} {
else if( type == LgbEntryType::Gimmick ) entries.emplace_back( std::make_shared< LGB_BGPARTS_ENTRY >( buf, entryOffset ) );
{ break;
entries.push_back( std::make_shared< LGB_GIMMICK_ENTRY >( buf, entryOffset ) ); }
} case LgbEntryType::Gimmick:
else if( type == LgbEntryType::EventNpc ) {
{ entries.emplace_back( std::make_shared< LGB_GIMMICK_ENTRY >( buf, entryOffset ) );
entries.push_back( std::make_shared< LGB_ENPC_ENTRY >( buf, entryOffset ) ); break;
} }
else if( type == LgbEntryType::EventObject ) case LgbEntryType::EventNpc:
{ {
entries.push_back( std::make_shared< LGB_EOBJ_ENTRY >( buf, entryOffset ) ); entries.emplace_back( std::make_shared< LGB_ENPC_ENTRY >( buf, entryOffset ) );
} break;
else if( type == LgbEntryType::ExitRange ) }
{ case LgbEntryType::EventObject:
entries.push_back( std::make_shared< LGB_EXIT_RANGE_ENTRY >( buf, entryOffset ) ); {
} entries.emplace_back( std::make_shared< LGB_EOBJ_ENTRY >( buf, entryOffset ) );
else if( type == LgbEntryType::EventRange ) break;
{ }
entries.push_back( std::make_shared< LGB_EVENT_RANGE_ENTRY >( buf, entryOffset ) ); case LgbEntryType::ExitRange:
} {
else if( type == LgbEntryType::PopRange ) entries.emplace_back( std::make_shared< LGB_EXIT_RANGE_ENTRY >( buf, entryOffset ) );
{ break;
entries.push_back( std::make_shared< LGB_POP_RANGE_ENTRY >( buf, entryOffset ) ); }
} case LgbEntryType::EventRange:
else if( type == LgbEntryType::MapRange ) {
{ entries.emplace_back( std::make_shared< LGB_EVENT_RANGE_ENTRY >( buf, entryOffset ) );
entries.push_back( std::make_shared< LGB_MAP_RANGE_ENTRY >( buf, entryOffset ) ); break;
} }
else case LgbEntryType::PopRange:
{ {
entries.push_back( std::make_shared< LgbEntry >( buf, entryOffset ) ); entries.emplace_back( std::make_shared< LGB_POP_RANGE_ENTRY >( buf, entryOffset ) );
break;
}
case LgbEntryType::MapRange:
{
entries.emplace_back( std::make_shared< LGB_MAP_RANGE_ENTRY >( buf, entryOffset ) );
break;
}
default:
{
entries.emplace_back( std::make_shared< LgbEntry >( buf, entryOffset ) );
break;
}
} }
} }
catch( std::exception& e ) catch( std::exception& e )

View file

@ -7,50 +7,6 @@
using xiv::utils::bparse::extract; using xiv::utils::bparse::extract;
namespace xiv::exd
{
struct ExdHeader
{
char magic[0x4];
uint16_t unknown;
uint16_t unknown2;
uint32_t index_size;
};
struct ExdRecordIndex
{
uint32_t id;
uint32_t offset;
};
}
namespace xiv::utils::bparse {
template<>
inline void reorder< xiv::exd::ExdHeader >( xiv::exd::ExdHeader& i_struct )
{
for( int32_t i = 0; i < 0x4; ++i )
{
xiv::utils::bparse::reorder( i_struct.magic[ i ] );
}
i_struct.unknown = xiv::utils::bparse::byteswap( i_struct.unknown );
xiv::utils::bparse::reorder( i_struct.unknown );
i_struct.unknown2 = xiv::utils::bparse::byteswap( i_struct.unknown2 );
xiv::utils::bparse::reorder( i_struct.unknown2 );
i_struct.index_size = xiv::utils::bparse::byteswap( i_struct.index_size );
xiv::utils::bparse::reorder( i_struct.index_size );
}
template<>
inline void reorder< xiv::exd::ExdRecordIndex >( xiv::exd::ExdRecordIndex& i_struct )
{
i_struct.id = xiv::utils::bparse::byteswap( i_struct.id );
xiv::utils::bparse::reorder( i_struct.id );
i_struct.offset = xiv::utils::bparse::byteswap( i_struct.offset );
xiv::utils::bparse::reorder( i_struct.offset );
}
};
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 > i_exh, const std::vector< std::shared_ptr< dat::File>>& i_files )
@ -68,16 +24,16 @@ namespace xiv::exd
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) ); std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
// Extract the header and skip to the record indices // Extract the header and skip to the record indices
auto exd_header = extract< ExdHeader >( iss ); auto exd_header = extract< ExdHeaderMinimal >( iss );
iss.seekg( 0x20 ); iss.seekg( 0x20 );
// Preallocate and extract the record_indices // Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex ); const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndexData );
std::vector< ExdRecordIndex > record_indices; std::vector< ExdRecordIndexData > record_indices;
record_indices.reserve( record_count ); record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i ) for( uint32_t i = 0; i < record_count; ++i )
{ {
auto recordIndex = extract< ExdRecordIndex >( iss ); auto recordIndex = extract< ExdRecordIndexData >( iss );
_idCache[ recordIndex.id ] = ExdCacheEntry{ file_ptr, recordIndex.offset }; _idCache[ recordIndex.id ] = ExdCacheEntry{ file_ptr, recordIndex.offset };
} }
} }
@ -290,16 +246,16 @@ namespace xiv::exd
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) ); std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
// Extract the header and skip to the record indices // Extract the header and skip to the record indices
auto exd_header = extract< ExdHeader >( iss ); auto exd_header = extract< ExdHeaderMinimal >( iss );
iss.seekg( 0x20 ); iss.seekg( 0x20 );
// Preallocate and extract the record_indices // Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex ); const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndexData );
std::vector< ExdRecordIndex > record_indices; std::vector< ExdRecordIndexData > record_indices;
record_indices.reserve( record_count ); record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i ) for( uint32_t i = 0; i < record_count; ++i )
{ {
record_indices.emplace_back( extract< ExdRecordIndex >( iss ) ); record_indices.emplace_back( extract< ExdRecordIndexData >( iss ) );
} }
for( auto& record_index : record_indices ) for( auto& record_index : record_indices )

191
deps/datReader/Exd.h vendored
View file

@ -2,6 +2,7 @@
#include <memory> #include <memory>
#include <map> #include <map>
#include <unordered_map>
#include <variant> #include <variant>
@ -12,6 +13,49 @@
#include <fstream> #include <fstream>
#include "Exh.h" #include "Exh.h"
#include "bparse.h" #include "bparse.h"
namespace xiv::exd
{
struct ExdHeaderMinimal {
char magic[ 0x4 ];
uint16_t unknown;
uint16_t unknown2;
uint32_t index_size;
};
struct ExdRecordIndexData {
uint32_t id;
uint32_t offset;
};
}
namespace xiv::utils::bparse
{
template<>
inline void reorder< xiv::exd::ExdHeaderMinimal >( xiv::exd::ExdHeaderMinimal& i_struct )
{
for( int32_t i = 0; i < 0x4; ++i )
{
xiv::utils::bparse::reorder( i_struct.magic[ i ] );
}
i_struct.unknown = xiv::utils::bparse::byteswap( i_struct.unknown );
xiv::utils::bparse::reorder( i_struct.unknown );
i_struct.unknown2 = xiv::utils::bparse::byteswap( i_struct.unknown2 );
xiv::utils::bparse::reorder( i_struct.unknown2 );
i_struct.index_size = xiv::utils::bparse::byteswap( i_struct.index_size );
xiv::utils::bparse::reorder( i_struct.index_size );
}
template<>
inline void reorder< xiv::exd::ExdRecordIndexData >( xiv::exd::ExdRecordIndexData& i_struct )
{
i_struct.id = xiv::utils::bparse::byteswap( i_struct.id );
xiv::utils::bparse::reorder( i_struct.id );
i_struct.offset = xiv::utils::bparse::byteswap( i_struct.offset );
xiv::utils::bparse::reorder( i_struct.offset );
}
};// namespace xiv::utils::bparse
namespace xiv::exd namespace xiv::exd
{ {
@ -184,12 +228,153 @@ 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< uint32_t, std::vector< Field > >& get_rows();
// Get all rows
template< typename T >
const std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< T > > > get_sheet_rows()
{
std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< T > > > sheets;
// Iterates over all the files
const uint32_t member_count = static_cast< uint32_t >( _exh->get_members().size() );
for( auto& file_ptr : _files )
{
// Get a stream
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 exd_header = xiv::utils::bparse::extract< ExdHeaderMinimal >( iss );
iss.seekg( 0x20 );
// Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndexData );
std::vector< ExdRecordIndexData > record_indices;
record_indices.reserve( record_count );
for( uint32_t i = 0; i < record_count; ++i )
{
record_indices.emplace_back( xiv::utils::bparse::extract< ExdRecordIndexData >( iss ) );
}
for( auto& record_index : record_indices )
{
auto cacheEntryIt = _idCache.find( record_index.id );
if( cacheEntryIt == _idCache.end() )
throw std::out_of_range( "Id not found: " + std::to_string( record_index.id ) );
auto pSheet = std::make_shared< Excel::ExcelStruct< T > >();
// Get the vector fields for the given record and preallocate it
auto fields = _data[ record_index.id ];
fields.reserve( member_count );
iss.seekg( cacheEntryIt->second.offset + 6 );
iss.read( reinterpret_cast<char*>( &pSheet.get()->_data ), sizeof( T ) );
int stringCount = 0;
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( member_entry.type )
{
case DataType::string:
// Extract the offset to the actual string
// Seek to it then extract the actual string
{
auto string_offset = xiv::utils::bparse::extract< uint32_t >( iss, "string_offset", false );
iss.seekg( cacheEntryIt->second.offset + 6 + _exh->get_header().data_offset + string_offset );
std::string value = xiv::utils::bparse::extract_cstring( iss, "string" );
auto it = pSheet->_strings.insert( pSheet->_strings.end(), value );
*reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) =
static_cast< uint32_t >( std::distance( pSheet->_strings.begin(), it ) );
}
break;
case DataType::boolean:
xiv::utils::bparse::extract< bool >( iss, "bool" );
break;
case DataType::int8:
xiv::utils::bparse::extract< int8_t >( iss, "int8_t" );
break;
case DataType::uint8:
xiv::utils::bparse::extract< uint8_t >( iss, "uint8_t" );
break;
case DataType::int16:
{
int16_t value = xiv::utils::bparse::extract< int16_t >( iss, "int16_t", false );
*reinterpret_cast< int16_t* >( pSheet->ptr() + member_entry.offset ) = value;
}
break;
case DataType::uint16:
{
uint16_t value = xiv::utils::bparse::extract< uint16_t >( iss, "uint16_t", false );
*reinterpret_cast< uint16_t* >( pSheet->ptr() + member_entry.offset ) = value;
}
break;
case DataType::int32:
{
int32_t value = xiv::utils::bparse::extract< int32_t >( iss, "int32_t", false );
*reinterpret_cast< int32_t* >( pSheet->ptr() + member_entry.offset ) = value;
}
break;
case DataType::uint32:
{
uint32_t value = xiv::utils::bparse::extract< uint32_t >( iss, "uint32_t", false );
*reinterpret_cast< uint32_t* >( pSheet->ptr() + member_entry.offset ) = value;
}
break;
case DataType::float32:
{
float value = xiv::utils::bparse::extract< float >( iss, "float", false );
*reinterpret_cast< float* >( pSheet->ptr() + member_entry.offset ) = value;
}
break;
case DataType::uint64:
{
uint64_t value = xiv::utils::bparse::extract< uint64_t >( iss, "uint64_t", false );
*reinterpret_cast< uint64_t* >( pSheet->ptr() + member_entry.offset ) = value;
}
break;
default:
auto type = static_cast< uint16_t >( member_entry.type );
if( type < 0x19 || type > 0x20 )
throw std::runtime_error( "Unknown DataType: " + std::to_string( type ) );
uint64_t val = xiv::utils::bparse::extract< uint64_t >( iss, "bool" );
int32_t shift = type - 0x19;
int32_t i = 1 << shift;
val &= i;
fields.emplace_back( ( val & i ) == i );
break;
}
}
sheets[ record_index.id ] = pSheet;
}
}
return sheets;
}
protected: protected:
// Data indexed by the ID of the row, the vector is field with the same order as exh.members // 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::map< uint32_t, std::vector< Field > > _data;
std::vector< std::shared_ptr< dat::File>> _files; 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

@ -983,74 +983,64 @@ namespace Sapphire::Common
LimitBreak = 8, LimitBreak = 8,
}; };
enum ActionEffectType : uint8_t enum CalcResultType : uint8_t
{ {
CALC_RESULT_TYPE_NONE = 0x0, TypeNone = 0x0,
CALC_RESULT_TYPE_MISS = 0x1, TypeMiss = 0x1,
CALC_RESULT_TYPE_RESIST = 0x2, TypeResist = 0x2,
CALC_RESULT_TYPE_DAMAGE_HP = 0x3, TypeDamageHp = 0x3,
CALC_RESULT_TYPE_RECOVER_HP = 0x4, TypeRecoverHp = 0x4,
CALC_RESULT_TYPE_CRITICAL_DAMAGE_HP = 0x5, TypeCriticalDamageHp = 0x5,
CALC_RESULT_TYPE_CRITICAL_RECOVER_HP = 0x6, TypeCriticalRecoverHp = 0x6,
CALC_RESULT_TYPE_GUARD = 0x7, TypeGuard = 0x7,
CALC_RESULT_TYPE_PARRY = 0x8, TypeParry = 0x8,
CALC_RESULT_TYPE_INVALID = 0x9, TypeInvalid = 0x9,
CALC_RESULT_TYPE_UNEFFECTIVE = 0xA, TypeUneffective = 0xA,
CALC_RESULT_TYPE_NEGLECT = 0xB, TypeNeglect = 0xB,
CALC_RESULT_TYPE_DAMAGE_MP = 0xC, TypeDamageMp = 0xC,
CALC_RESULT_TYPE_RECOVER_MP = 0xD, TypeRecoverMp = 0xD,
CALC_RESULT_TYPE_DAMAGE_TP = 0xE, TypeDamageTp = 0xE,
CALC_RESULT_TYPE_RECOVER_TP = 0xF, TypeRecoverTp = 0xF,
CALC_RESULT_TYPE_RECOVER_GP = 0x10, TypeRecoverGp = 0x10,
CALC_RESULT_TYPE_SET_STATUS = 0x11, TypeSetStatus = 0x11,
CALC_RESULT_TYPE_SET_STATUS_ME = 0x12, TypeSetStatusMe = 0x12,
CALC_RESULT_TYPE_RESET_STATUS = 0x13, TypeResetStatus = 0x13,
CALC_RESULT_TYPE_RESET_STATUS_ME = 0x14, TypeResetStatusMe = 0x14,
CALC_RESULT_TYPE_RESET_BAD_STATUS = 0x15, TypeResetBadStatus = 0x15,
CALC_RESULT_TYPE_UNEFFECTIVE_STATUS = 0x16, TypeUneffectiveStatus = 0x16,
CALC_RESULT_TYPE_HALF_GOOD_STATUS = 0x17, TypeHalfGoodStatus = 0x17,
CALC_RESULT_TYPE_HATE_DIRECT = 0x18, TypeHateDirect = 0x18,
CALC_RESULT_TYPE_HATE_INDIRECTION = 0x19, TypeHateIndirection = 0x19,
CALC_RESULT_TYPE_HATE_TOP = 0x1A, TypeHateTop = 0x1A,
CALC_RESULT_TYPE_HATE_ADD = 0x1B, TypeHateAdd = 0x1B,
CALC_RESULT_TYPE_HATE_MULT = 0x1C, TypeHateMult = 0x1C,
CALC_RESULT_TYPE_COMBO = 0x1D, TypeCombo = 0x1D,
CALC_RESULT_TYPE_COMBO_HIT = 0x1E, TypeComboHit = 0x1E,
CALC_RESULT_TYPE_COUNTER = 0x1F, TypeCounter = 0x1F,
CALC_RESULT_TYPE_DESTRUCT = 0x20, TypeDestruct = 0x20,
CALC_RESULT_TYPE_PARALYSIS = 0x21, TypeParalysis = 0x21,
CALC_RESULT_TYPE_KNOCK_BACK = 0x22, TypeKnockBack = 0x22,
CALC_RESULT_TYPE_DRAW_UP_CHAIRS = 0x23, TypeDrawUpChairs = 0x23,
CALC_RESULT_TYPE_SUCKED = 0x24, TypeSucked = 0x24,
CALC_RESULT_TYPE_CT_DRAW_UP_CHAIRS = 0x25, TypeCtDrawUpChairs = 0x25,
CALC_RESULT_TYPE_LIVE_CALLBACK = 0x26, TypeLiveCallback = 0x26,
CALC_RESULT_TYPE_MOUNT = 0x27, TypeMount = 0x27,
CALC_RESULT_ARCHER_DOT = 0x28, TypeArcherDot = 0x28,
CALC_RESULT_MASTER_DOT = 0x29, TypeMasterDot = 0x29,
CALC_RESULT_BLESSINGS_OF_GODDESS = 0x2A, TypeBlessingOfGoddess = 0x2A,
CALC_RESULT_BAD_BREATH = 0x2B, TypeBadBreath = 0x2B,
CALC_RESULT_REVIVAL = 0x2C, TypeRevival = 0x2C,
CALC_RESULT_PET = 0x2D, TypePet = 0x2D,
CALC_RESULT_TYPE_BLOW = 0x2E, TypeBlow = 0x2E,
CALC_RESULT_TYPE_STATUS_RESIST = 0x2F, TypeStatusResist = 0x2F,
CALC_RESULT_TYPE_CLEAR_PHYSICAL = 0x30, TypeClearPhysical = 0x30,
CALC_RESULT_BNPC_STATE = 0x31, TypeBNpcState = 0x31,
CALC_RESULT_TYPE_VFX = 0x32, TypeVfx = 0x32,
CALC_RESULT_TYPE_HARD_CODE = 0x33, TypeHardCode = 0x33,
CALC_RESULT_CALC_ID = 0x34, TypeCalcId = 0x34,
CALC_RESULT_TYPE_CLEAR_PVP_POINT = 0x35, TypeClearPvpPoint = 0x35,
CALC_RESULT_TYPE_CHECK_BARRIER = 0x36, TypeCheckBarrier = 0x36,
CALC_RESULT_TYPE_REFLEC = 0x37, TypeReflect = 0x37,
};
enum class ActionHitSeverityType : uint8_t
{
NormalDamage = 0,
CritHeal = 0,
CritDamage = 1,
NormalHeal = 1,
DirectHitDamage = 2,
CritDirectHitDamage = 3
}; };
enum class ActionResultFlag : uint8_t enum class ActionResultFlag : uint8_t
@ -1418,9 +1408,10 @@ namespace Sapphire::Common
{ {
None1 = 0, None1 = 0,
HideUILockChar = 1, // as the name suggests, hides the ui and logs the char... HideUILockChar = 1, // as the name suggests, hides the ui and logs the char...
InCombat = 2, // in Combat, locks gearchange/return/teleport InCombat = 18, // in Combat, locks gearchange/return/teleport
Casting = 3, Casting = 19,
InNpcEvent = 6, // when talking to an npc, locks ui giving "occupied" message EventAction = 22,
InNpcEvent = 24, // when talking to an npc, locks ui giving "occupied" message
// InNpcEvent1 = 10, // Sent together with InNpcEvent, when waiting for input? just a guess... // InNpcEvent1 = 10, // Sent together with InNpcEvent, when waiting for input? just a guess...

View file

@ -91,6 +91,27 @@ namespace Sapphire::Data
return ids; return ids;
} }
template< typename T >
std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< T > > > getRows()
{
xiv::exd::Exd sheet;
auto needle = m_sheets.find( typeid( T ) );
if( needle == m_sheets.end() )
{
auto sheetName = getSheetName< T >();
// load sheet
auto& cat = m_exd_data->get_category( sheetName );
m_sheets[ typeid( T ) ] = sheet = static_cast< xiv::exd::Exd >( cat.get_data( xiv::exd::Language::en ) );
}
else
{
sheet = needle->second;
}
return sheet.get_sheet_rows< T >();
}
std::shared_ptr< xiv::dat::GameData > getGameData() std::shared_ptr< xiv::dat::GameData > getGameData()
{ {
return m_data; return m_data;

View file

@ -31,8 +31,7 @@ public:
auto dmg = action.calcDamage( Potency ); auto dmg = action.calcDamage( Potency );
action.getActionResultBuilder()->damage( pSource, pTarget, dmg.first, dmg.second ); action.getActionResultBuilder()->damage( pSource, pTarget, dmg.first, dmg.second );
action.getActionResultBuilder()->heal( pTarget, pSource, dmg.first, Common::ActionHitSeverityType::NormalHeal, action.getActionResultBuilder()->heal( pTarget, pSource, dmg.first, Common::CalcResultType::TypeRecoverHp, Common::ActionResultFlag::EffectOnSource );
Common::ActionResultFlag::EffectOnSource );
action.getActionResultBuilder()->applyStatusEffectSelf( InnerBeast, 15000, 0, { StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } ); action.getActionResultBuilder()->applyStatusEffectSelf( InnerBeast, 15000, 0, { StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } );

View file

@ -1,5 +1,8 @@
#include <ScriptObject.h> #include <ScriptObject.h>
#include <Territory/QuestBattle.h> #include <Territory/QuestBattle.h>
#include <Actor/Player.h>
#include <Actor/GameObject.h>
#include <Actor/BNpc.h>
using namespace Sapphire; using namespace Sapphire;
@ -100,15 +103,126 @@ public:
instance.addEObj( "Millioncornseedling", 2001255, 0, 3927161, 4, { -320.576813f, 25.833500f, -527.550171f }, 0.961304f, -0.384837f, 0); instance.addEObj( "Millioncornseedling", 2001255, 0, 3927161, 4, { -320.576813f, 25.833500f, -527.550171f }, 0.961304f, -0.384837f, 0);
} }
enum vars
{
SET_1_SPAWNED,
SET_2_SPAWNED,
SUCCESS_CALLED,
};
void onPlayerSetup( Sapphire::QuestBattle& instance, Entity::Player& player ) override
{
player.setRot( -71.03f );
player.setPos( { 198.303f, 14.244f, 538.248f } );
}
void onUpdate( QuestBattle& instance, uint64_t tickCount ) override void onUpdate( QuestBattle& instance, uint64_t tickCount ) override
{ {
auto pair1Spawnd = instance.getDirectorVar( SET_1_SPAWNED );
auto pair2Spawnd = instance.getDirectorVar( SET_2_SPAWNED );
auto successCalled = instance.getDirectorVar( SUCCESS_CALLED );
auto boss = instance.getActiveBNpcByLayoutId( INIT_POP_BOSS );
auto thancred = instance.getActiveBNpcByLayoutId( INIT_P_POP_01 );
auto pPlayer = instance.getPlayerPtr();
uint32_t bossHpPercent = 0;
if( boss )
bossHpPercent = boss->getHpPercent();
if( pPlayer && !pPlayer->isAlive() )
{
instance.fail();
return;
}
if (!thancred)
return;
if( pair1Spawnd == 0 && bossHpPercent <= 70 )
{
instance.setDirectorVar( SET_1_SPAWNED, 1 );
auto a2 = instance.createBNpcFromLayoutId( INIT_POP_01_01, 1440 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
auto a3 = instance.createBNpcFromLayoutId( INIT_POP_01_02, 1440 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
a2->setFlag( Entity::NoDeaggro );
a3->setFlag( Entity::NoDeaggro );
auto pPlayer = instance.getPlayerPtr();
a2->hateListAdd( pPlayer, 1 );
a3->hateListAdd( pPlayer, 1 );
thancred->hateListAdd( a2, 9999 );
thancred->hateListAdd( a3, 9999 );
}
if( pair2Spawnd == 0 && bossHpPercent <= 40 )
{
instance.setDirectorVar( SET_2_SPAWNED, 1 );
auto a2 = instance.createBNpcFromLayoutId( INIT_POP_02_01, 1440 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
auto a3 = instance.createBNpcFromLayoutId( INIT_POP_02_02, 1440 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
auto a4 = instance.createBNpcFromLayoutId( INIT_POP_02_03, 1440 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
auto a5 = instance.createBNpcFromLayoutId( INIT_POP_02_04, 1440 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
a2->setFlag( Entity::NoDeaggro );
a3->setFlag( Entity::NoDeaggro );
a4->setFlag( Entity::NoDeaggro );
a5->setFlag( Entity::NoDeaggro );
auto pPlayer = instance.getPlayerPtr();
a2->hateListAdd( pPlayer, 1 );
a3->hateListAdd( pPlayer, 1 );
a4->hateListAdd( pPlayer, 1 );
a5->hateListAdd( pPlayer, 1 );
thancred->hateListAdd( a2, 9999 );
thancred->hateListAdd( a3, 9999 );
thancred->hateListAdd( a4, 9999 );
thancred->hateListAdd( a5, 9999 );
}
if( instance.getCountEnemyBNpc() == 0 && successCalled == 0 )
{
instance.setDirectorVar( SUCCESS_CALLED, 1 );
instance.success();
return;
}
} }
void onEnterTerritory( QuestBattle& instance, Entity::Player& player, uint32_t eventId, uint16_t param1, void onEnterTerritory( QuestBattle& instance, Entity::Player& player, uint32_t eventId, uint16_t param1,
uint16_t param2 ) override uint16_t param2 ) override
{ {
eventMgr().playScene( player, instance.getDirectorId(), 1,
NO_DEFAULT_CAMERA | CONDITION_CUTSCENE | SILENT_ENTER_TERRI_ENV |
HIDE_HOTBAR | SILENT_ENTER_TERRI_BGM | SILENT_ENTER_TERRI_SE |
DISABLE_STEALTH | 0x00100000 | LOCK_HUD | LOCK_HOTBAR |
// todo: wtf is 0x00100000
DISABLE_CANCEL_EMOTE, [ & ]( Entity::Player& player, const Event::SceneResult& result ) {
player.setOnEnterEventDone( true );
} );
}
void onDutyComplete( QuestBattle& instance, Entity::Player& player ) override
{
auto idx = player.getQuestIndex( instance.getQuestId() );
if( idx == -1 )
return;
auto& quest = player.getQuestByIndex( idx );
quest.setSeq( 2 );
}
void onDutyCommence( QuestBattle& instance, Entity::Player& player ) override
{
// TODO: Change to correct HP values
auto boss = instance.createBNpcFromLayoutId( INIT_POP_BOSS, 10571 /*TODO: Find the right value*/, Common::BNpcType::Enemy );
auto thancred = instance.createBNpcFromLayoutId( INIT_P_POP_01, 27780 /*TODO: Find the right value*/, Common::BNpcType::Friendly );
boss->setFlag( Entity::NoDeaggro );
thancred->setFlag( Entity::NoDeaggro );
boss->hateListAdd( thancred, 10000 );
boss->hateListAdd( player.getAsPlayer(), 1 );
thancred->hateListAdd( boss, 10000 );
} }

View file

@ -0,0 +1,318 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include <Actor/Player.h>
#include "Manager/EventMgr.h"
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: ManWil005_00550
// Quest Name: Underneath the Sultantree
// Quest ID: 66086
// Start NPC: 1003995 (Papashan)
// End NPC: 1003995 (Papashan)
using namespace Sapphire;
class ManWil005 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// BitFlag8
// UI8AL
/// Countable Num: 1 Seq: 1 Event: 1 Listener: 1003996
/// Countable Num: 1 Seq: 2 Event: 1 Listener: 2001853
/// Countable Num: 0 Seq: 255 Event: 15 Listener: 5020000
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
Seq2 = 2,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1003995; // Papashan ( Pos: 75.338402 2.138110 316.362000 Teri: 141 )
static constexpr auto Actor1 = 1003996; // Hooded Lalafell ( Pos: 202.662994 14.104900 536.909973 Teri: 141 )
static constexpr auto Actor2 = 1003997; // ×次女a ( Pos: 76.674599 2.137120 317.433014 Teri: 141 )
static constexpr auto Actor3 = 1003998; // ×近衛a ( Pos: 77.176598 2.137340 315.631989 Teri: 141 )
static constexpr auto Actor4 = 1003999; // ×近衛b ( Pos: 77.310501 2.136910 316.973999 Teri: 141 )
static constexpr auto Actor5 = 1004000; // ×近衛c ( Pos: 78.402603 2.136520 316.269012 Teri: 141 )
static constexpr auto Actor6 = 1004001; // Lilira ( Pos: 76.643402 2.136930 318.191010 Teri: 141 )
static constexpr auto Actor20 = 1006171; // Lilira
static constexpr auto Actor30 = 1006167; // 侍女a
static constexpr auto Actor40 = 1006168; // 近衛a
static constexpr auto Actor50 = 1006169; // 近衛b
static constexpr auto Actor60 = 1006170; // 近衛c
static constexpr auto CutScene02 = 141;
static constexpr auto CutScene03 = 56;
static constexpr auto CutScene04 = 142;
static constexpr auto Eobject0 = 2001853; // ( Pos: 202.638000 14.137900 536.905029 Teri: 141 )
static constexpr auto EventActionSearch = 1; // Interaction
static constexpr auto Questbattle0 = 37;
static constexpr auto Seq0Actor0Lq = 50; // Goblin Thug
static constexpr auto Territorytype0 = 270;
static constexpr auto Territorytype1 = 141;
public:
ManWil005() : Sapphire::ScriptAPI::QuestScript( 66086 ){};
~ManWil005() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if (quest.getSeq() == Seq0)
{
Scene00000( quest, player );
}
else if (quest.getSeq() == SeqFinish)
{
Scene00006( quest, player );
}
break;
}
case Actor1:
{
if( quest.getSeq() == Seq1 )
Scene00002( quest, player );
break;
}
case Actor2:
{
break;
}
case Actor3:
{
break;
}
case Actor4:
{
break;
}
case Actor5:
{
break;
}
case Actor6:
{
break;
}
case Actor20:
{
break;
}
case Actor30:
{
break;
}
case Actor40:
{
break;
}
case Actor50:
{
break;
}
case Actor60:
{
break;
}
}
}
void onEnterTerritory( World::Quest& quest, Entity::Player& player, uint16_t param1, uint16_t param2 ) override
{
if( quest.getSeq() == Seq2 )
{
Scene00005( quest, player );
}
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &ManWil005::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
Scene00001( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, bindSceneReturn( &ManWil005::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setSeq( Seq1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, NONE, bindSceneReturn( &ManWil005::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
auto& pTeriMgr = Common::Service< Sapphire::World::Manager::TerritoryMgr >::ref();
eventMgr().eventFinish( player, result.eventId, 0 );
pTeriMgr.createAndJoinQuestBattle( player, Questbattle0 );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, NONE, bindSceneReturn( &ManWil005::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, NONE, bindSceneReturn( &ManWil005::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00005( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 5, NO_DEFAULT_CAMERA | CONDITION_CUTSCENE | SILENT_ENTER_TERRI_ENV | HIDE_HOTBAR | SILENT_ENTER_TERRI_BGM | SILENT_ENTER_TERRI_SE | DISABLE_STEALTH | 0x00100000 | LOCK_HUD | LOCK_HOTBAR |
// todo: wtf is 0x00100000
DISABLE_CANCEL_EMOTE,
bindSceneReturn( &ManWil005::Scene00005Return ) );
}
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setSeq( SeqFinish );
}
//////////////////////////////////////////////////////////////////////
void Scene00006( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 6, FADE_OUT | HIDE_HOTBAR | CONDITION_CUTSCENE | HIDE_UI, bindSceneReturn( &ManWil005::Scene00006Return ) );
}
void Scene00006Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00007( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 7, NONE, bindSceneReturn( &ManWil005::Scene00007Return ) );
}
void Scene00007Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00008( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 8, NONE, bindSceneReturn( &ManWil005::Scene00008Return ) );
}
void Scene00008Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00009( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 9, NONE, bindSceneReturn( &ManWil005::Scene00009Return ) );
}
void Scene00009Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00010( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 10, NONE, bindSceneReturn( &ManWil005::Scene00010Return ) );
}
void Scene00010Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00011( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 11, NONE, bindSceneReturn( &ManWil005::Scene00011Return ) );
}
void Scene00011Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
};
EXPOSE_SCRIPT( ManWil005 );

View file

@ -45,6 +45,7 @@ private:
static constexpr auto Actor4 = 1013231;//Hatchling static constexpr auto Actor4 = 1013231;//Hatchling
static constexpr auto BindActor1 = 5896086; static constexpr auto BindActor1 = 5896086;
static constexpr auto Item0 = 2001726; static constexpr auto Item0 = 2001726;
static constexpr auto Item0Icon = 26177;
static constexpr auto LocBgm1 = 313; static constexpr auto LocBgm1 = 313;
public: public:
@ -137,7 +138,7 @@ private:
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
eventMgr().sendEventNotice( player, getId(), 0, 0 );//TODO: Item Icon, probably eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setSeq( Seq2 ); quest.setSeq( Seq2 );
} }

View file

@ -64,6 +64,7 @@ private:
static constexpr auto Eobject2 = 2005950; static constexpr auto Eobject2 = 2005950;
static constexpr auto EventActionGatherMiddle = 7; static constexpr auto EventActionGatherMiddle = 7;
static constexpr auto Item0 = 2001728; static constexpr auto Item0 = 2001728;
static constexpr auto Item0Icon = 21223;
static constexpr auto LocActor1 = 1013860; static constexpr auto LocActor1 = 1013860;
static constexpr auto LocActor2 = 1013870; static constexpr auto LocActor2 = 1013870;
static constexpr auto LocActor3 = 1013871; static constexpr auto LocActor3 = 1013871;
@ -207,7 +208,7 @@ public:
private: private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player ) void checkQuestCompletion( World::Quest& quest, Entity::Player& player )
{ {
eventMgr().sendEventNotice( player, getId(), 1, 2, quest.getUI8AL(), 3 );//TODO: Item Icon, probably eventMgr().sendNotice( player, getId(), 1, { quest.getUI8AL(), 3, Item0Icon } );
if( quest.getUI8AL() >= 3 ) if( quest.getUI8AL() >= 3 )
{ {

View file

@ -56,6 +56,7 @@ private:
static constexpr auto Actor7 = 1013961;//Goblin Trader (Seq5) static constexpr auto Actor7 = 1013961;//Goblin Trader (Seq5)
static constexpr auto BindActor1 = 5896328; static constexpr auto BindActor1 = 5896328;
static constexpr auto Item0 = 2001729; static constexpr auto Item0 = 2001729;
static constexpr auto Item0Icon = 25919;
static constexpr auto LocActor1 = 1013954; static constexpr auto LocActor1 = 1013954;
static constexpr auto LocActor2 = 1013955; static constexpr auto LocActor2 = 1013955;
static constexpr auto LocBgm1 = 313; static constexpr auto LocBgm1 = 313;
@ -390,7 +391,7 @@ private:
void Scene00019Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00019Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 5, 0 );//TODO: Item Icon? eventMgr().sendNotice( player, getId(), 5, { Item0Icon } );
quest.setSeq( SeqFinish ); quest.setSeq( SeqFinish );
quest.setUI8BH( 0 ); quest.setUI8BH( 0 );
} }

View file

@ -101,6 +101,8 @@ private:
static constexpr auto Item4 = 2001029; static constexpr auto Item4 = 2001029;
static constexpr auto Item5 = 2001038; static constexpr auto Item5 = 2001038;
static constexpr auto Item6 = 2001047; static constexpr auto Item6 = 2001047;
static constexpr auto Item0Icon = 21003;
static constexpr auto Item3Icon = 26002;
static constexpr auto LocAction0 = 858; static constexpr auto LocAction0 = 858;
static constexpr auto LocAction1 = 995; static constexpr auto LocAction1 = 995;
static constexpr auto LocAction2 = 936; static constexpr auto LocAction2 = 936;
@ -109,6 +111,7 @@ private:
static constexpr auto Ritem0 = 2049;//Madman's Whispering Rod static constexpr auto Ritem0 = 2049;//Madman's Whispering Rod
static constexpr auto Ritem1 = 2046;//Unfinished Thyrus static constexpr auto Ritem1 = 2046;//Unfinished Thyrus
static constexpr auto Ritem2 = 6267;//Radz-at-Han Quenching Oil static constexpr auto Ritem2 = 6267;//Radz-at-Han Quenching Oil
static constexpr auto Ritem1Icon = 32627;
public: public:
JobWhm001() : Sapphire::ScriptAPI::QuestScript( 66660 ){}; JobWhm001() : Sapphire::ScriptAPI::QuestScript( 66660 ){};
@ -327,7 +330,7 @@ private:
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
eventMgr().sendEventNotice( player, getId(), 0, 0 );//TODO:Item Icon eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setSeq( Seq2 ); quest.setSeq( Seq2 );
} }
@ -469,7 +472,7 @@ private:
void Scene00013Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00013Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 7, 0 );//TODO: Item Icon eventMgr().sendNotice( player, getId(), 7, { Item3Icon } );
quest.setSeq( Seq9 ); quest.setSeq( Seq9 );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
} }
@ -497,7 +500,7 @@ private:
void Scene00015Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00015Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
quest.setUI8BH( 0 ); quest.setUI8BH( 0 );
eventMgr().sendEventNotice( player, getId(), 8, 0 );//TODO: Item Icon? eventMgr().sendNotice( player, getId(), 8, { Ritem1Icon } );
player.addItem( Ritem1 ); player.addItem( Ritem1 );
quest.setSeq( Seq10 ); quest.setSeq( Seq10 );
} }
@ -719,4 +722,4 @@ private:
} }
}; };
EXPOSE_SCRIPT( JobWhm001 ); EXPOSE_SCRIPT( JobWhm001 );

View file

@ -80,6 +80,10 @@ private:
static constexpr auto Ritem1 = 3894; static constexpr auto Ritem1 = 3894;
static constexpr auto Ritem2 = 3463; static constexpr auto Ritem2 = 3463;
static constexpr auto Ritem3 = 2902; static constexpr auto Ritem3 = 2902;
static constexpr auto Ritem0Icon = 48242;
static constexpr auto Ritem1Icon = 48219;
static constexpr auto Ritem2Icon = 45189;
static constexpr auto Ritem3Icon = 40616;
static constexpr auto VfxReaction = 177; static constexpr auto VfxReaction = 177;
public: public:
@ -214,7 +218,7 @@ private:
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 0, 0 );//TODO:Item Icon, Cleric's Gloves eventMgr().sendNotice( player, getId(), 0, { Ritem0Icon } );
playerMgr().sendLogMessage( player, Logmessage0 ); playerMgr().sendLogMessage( player, Logmessage0 );
quest.setUI8AL( 1 ); quest.setUI8AL( 1 );
quest.setUI8CH( 0 ); quest.setUI8CH( 0 );
@ -241,7 +245,7 @@ private:
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 1, 0 );//TODO:Item Icon, Cleric's Culottes eventMgr().sendNotice( player, getId(), 1, { Ritem2Icon } );
playerMgr().sendLogMessage( player, Logmessage0 ); playerMgr().sendLogMessage( player, Logmessage0 );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setUI8CL( 0 ); quest.setUI8CL( 0 );
@ -268,7 +272,7 @@ private:
void Scene00007Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00007Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 2, 0 );//TODO:Item Icon, Cleric's Boots eventMgr().sendNotice( player, getId(), 2, {Ritem1Icon} );
playerMgr().sendLogMessage( player, Logmessage0 ); playerMgr().sendLogMessage( player, Logmessage0 );
quest.setUI8BL( 1 ); quest.setUI8BL( 1 );
quest.setUI8DH( 0 ); quest.setUI8DH( 0 );
@ -436,7 +440,7 @@ private:
void Scene00021Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00021Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 6, 0 );//TODO:Item Icon, Cleric's Circlet eventMgr().sendNotice( player, getId(), 6, { Ritem3Icon } );
playerMgr().sendLogMessage( player, Logmessage0 ); playerMgr().sendLogMessage( player, Logmessage0 );
quest.setSeq( SeqFinish ); quest.setSeq( SeqFinish );
quest.setUI8BH( 0 ); quest.setUI8BH( 0 );

View file

@ -54,6 +54,7 @@ private:
static constexpr auto Eobject1 = 2000017;//Decaying Tree (South) static constexpr auto Eobject1 = 2000017;//Decaying Tree (South)
static constexpr auto Eobject2 = 2000018;//Decaying Tree (East) static constexpr auto Eobject2 = 2000018;//Decaying Tree (East)
static constexpr auto Item0 = 2000061; static constexpr auto Item0 = 2000061;
static constexpr auto Item0Icon = 20661;
static constexpr auto Seq0Actor0 = 0; static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq1Eobject0 = 1; static constexpr auto Seq1Eobject0 = 1;
static constexpr auto Seq1Eobject0Useitemno = 99; static constexpr auto Seq1Eobject0Useitemno = 99;
@ -184,7 +185,7 @@ public:
private: private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player ) void checkQuestCompletion( World::Quest& quest, Entity::Player& player )
{ {
eventMgr().sendEventNotice( player, getId(), 0, 2, quest.getUI8AH(), 3 );//TODO: Probably needs item icon eventMgr().sendNotice( player, getId(), 0, { quest.getUI8AH(), 3, Item0Icon } );
if( quest.getUI8AH() >= 3 ) if( quest.getUI8AH() >= 3 )
{ {

View file

@ -45,6 +45,8 @@ private:
static constexpr auto Enemy0 = 54;//Hornet Swarm (INCORRECT: 57) static constexpr auto Enemy0 = 54;//Hornet Swarm (INCORRECT: 57)
static constexpr auto Item0 = 2000099; static constexpr auto Item0 = 2000099;
static constexpr auto Item1 = 2000094; static constexpr auto Item1 = 2000094;
static constexpr auto Item0Icon = 22623;
static constexpr auto Item1Icon = 24403;
static constexpr auto Seq0Actor0 = 0; static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq2Actor0 = 1; static constexpr auto Seq2Actor0 = 1;
static constexpr auto Seq2Actor0Npctradeno = 99; static constexpr auto Seq2Actor0Npctradeno = 99;
@ -88,7 +90,7 @@ public:
{ {
quest.setUI8BH( quest.getUI8BH() + 1 ); quest.setUI8BH( quest.getUI8BH() + 1 );
quest.setUI8AL( quest.getUI8AL() + 1 ); quest.setUI8AL( quest.getUI8AL() + 1 );
eventMgr().sendEventNotice( player, getId(), 0, 2, quest.getUI8AL(), 4 );//TODO: Probably needs item icon eventMgr().sendNotice( player, getId(), 0, { quest.getUI8AL(), 4, Item0Icon } ); // item Icon 2 missing
if( quest.getUI8AL() >= 4 ) if( quest.getUI8AL() >= 4 )
{ {

View file

@ -53,6 +53,7 @@ private:
static constexpr auto Eventrange0 = 3841476; static constexpr auto Eventrange0 = 3841476;
static constexpr auto EventActionSearch = 1; static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000192; static constexpr auto Item0 = 2000192;
static constexpr auto Item0Icon = 22627;
public: public:
SubFst067() : Sapphire::ScriptAPI::QuestScript( 65919 ){}; SubFst067() : Sapphire::ScriptAPI::QuestScript( 65919 ){};
@ -182,7 +183,7 @@ public:
private: private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player ) void checkQuestCompletion( World::Quest& quest, Entity::Player& player )
{ {
eventMgr().sendEventNotice( player, getId(), 1, 2, quest.getUI8AL(), 3 );//TODO: Item Icon eventMgr().sendNotice( player, getId(), 1, { quest.getUI8AL(), 3, Item0Icon } );
if( quest.getUI8AL() >= 3 ) if( quest.getUI8AL() >= 3 )
{ {
quest.setUI8AL( 0 ); quest.setUI8AL( 0 );

View file

@ -39,6 +39,7 @@ private:
static constexpr auto Eobject0 = 2000685;//Well-worn Fishing Rod static constexpr auto Eobject0 = 2000685;//Well-worn Fishing Rod
static constexpr auto EventActionSearch = 1; static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000185; static constexpr auto Item0 = 2000185;
static constexpr auto Item0Icon = 38201;
static constexpr auto Seq0Actor0 = 0; static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq1Eobject0 = 1; static constexpr auto Seq1Eobject0 = 1;
static constexpr auto Seq1Eobject0Eventactionno = 99; static constexpr auto Seq1Eobject0Eventactionno = 99;
@ -169,7 +170,7 @@ private:
void Scene00100Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00100Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 0, 0 );//TODO: Probably Item Icon eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setSeq( SeqFinish ); quest.setSeq( SeqFinish );
} }

View file

@ -55,6 +55,7 @@ private:
static constexpr auto EventActionSearchMiddle = 3; static constexpr auto EventActionSearchMiddle = 3;
static constexpr auto Item0 = 2000616; static constexpr auto Item0 = 2000616;
static constexpr auto Item1 = 2000617; static constexpr auto Item1 = 2000617;
static constexpr auto Item1Icon = 20005;
public: public:
GaiUsa803() : Sapphire::ScriptAPI::QuestScript( 66323 ){}; GaiUsa803() : Sapphire::ScriptAPI::QuestScript( 66323 ){};
@ -140,7 +141,7 @@ public:
private: private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player ) void checkQuestCompletion( World::Quest& quest, Entity::Player& player )
{ {
eventMgr().sendEventNotice( player, getId(), 1, 2, quest.getUI8AH(), 5 );//TODO:Show Item Icon eventMgr().sendNotice( player, getId(), 1, { quest.getUI8AH(), 5, Item1Icon } );
if( quest.getUI8AH() >= 5 ) if( quest.getUI8AH() >= 5 )
{ {
quest.setUI8BH( quest.getUI8DH() ); quest.setUI8BH( quest.getUI8DH() );

View file

@ -55,6 +55,7 @@ private:
static constexpr auto Item0 = 2000720; static constexpr auto Item0 = 2000720;
static constexpr auto Item1 = 2000721; static constexpr auto Item1 = 2000721;
static constexpr auto Item2 = 2000722; static constexpr auto Item2 = 2000722;
static constexpr auto Item0Icon = 26002;
public: public:
GaiUsb808() : Sapphire::ScriptAPI::QuestScript( 66453 ){}; GaiUsb808() : Sapphire::ScriptAPI::QuestScript( 66453 ){};
@ -162,7 +163,7 @@ private:
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 1, 0 );//TODO:Show Item Icon (Needs func update) eventMgr().sendNotice( player, getId(), 1, { Item0Icon } );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setSeq( Seq3 ); quest.setSeq( Seq3 );
} }

View file

@ -61,6 +61,8 @@ private:
static constexpr auto EventActionSearch = 1; static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000963; static constexpr auto Item0 = 2000963;
static constexpr auto Item1 = 2000965; static constexpr auto Item1 = 2000965;
static constexpr auto Item0Icon = 22614;
static constexpr auto Item1Icon = 21452;
static constexpr auto Poprange0 = 3884000; static constexpr auto Poprange0 = 3884000;
static constexpr auto Territorytype0 = 204; static constexpr auto Territorytype0 = 204;
@ -263,7 +265,7 @@ private:
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 0, 0 ); eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setSeq( Seq2 ); quest.setSeq( Seq2 );
} }
@ -393,7 +395,7 @@ private:
{ {
quest.setSeq( Seq4 ); quest.setSeq( Seq4 );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
eventMgr().sendEventNotice( player, getId(), 2, 0 /*1, Item1*/ );//TODO:Item Icon Event Notice eventMgr().sendNotice( player, getId(), 2, { Item1Icon } );
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View file

@ -55,6 +55,8 @@ private:
static constexpr auto EventActionSearch = 1; static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000669; static constexpr auto Item0 = 2000669;
static constexpr auto Item1 = 2000929; static constexpr auto Item1 = 2000929;
static constexpr auto Item0Icon = 27241;
static constexpr auto Item1Icon = 22301;
public: public:
GaiUsb406() : Sapphire::ScriptAPI::QuestScript( 66398 ){}; GaiUsb406() : Sapphire::ScriptAPI::QuestScript( 66398 ){};
@ -143,7 +145,11 @@ private:
{ {
if( quest.getSeq() == Seq1 ) if( quest.getSeq() == Seq1 )
{ {
eventMgr().sendEventNotice( player, getId(), type, 2, ( type == 0 ) ? quest.getUI8AL() : quest.getUI8BH(), 3 ); //TODO: Item Icons if( type == 0 )
eventMgr().sendNotice( player, getId(), type, { quest.getUI8AL(), 3, Item1Icon } );
else
eventMgr().sendNotice( player, getId(), type, { quest.getUI8BH(), 3, Item0Icon } );
if( quest.getUI8BL() >= 3 && quest.getUI8CH() >= 3 ) if( quest.getUI8BL() >= 3 && quest.getUI8CH() >= 3 )
{ {
quest.setUI8BH( quest.getUI8BL() ); quest.setUI8BH( quest.getUI8BL() );

View file

@ -8,7 +8,7 @@
#include <Service.h> #include <Service.h>
// Quest Script: SubSea002_00112 // Quest Script: SubSea002_00112
// Quest Name: Suspiciously SoberF // Quest Name: Suspiciously Sober
// Quest ID: 65648 // Quest ID: 65648
// Start NPC: 1003604 (Ahldskyf) // Start NPC: 1003604 (Ahldskyf)
// End NPC: 1003275 (Frydwyb) // End NPC: 1003275 (Frydwyb)

View file

@ -145,6 +145,7 @@ private:
{ {
eventMgr().sendEventNotice( player, getId(), 0, 0 ); eventMgr().sendEventNotice( player, getId(), 0, 0 );
quest.setUI8AL( 1 ); quest.setUI8AL( 1 );
quest.setBitFlag8( 1, true );
checkQuestCompletion( quest, player, 1 ); checkQuestCompletion( quest, player, 1 );
} }
@ -159,6 +160,7 @@ private:
{ {
eventMgr().sendEventNotice( player, getId(), 1, 0 ); eventMgr().sendEventNotice( player, getId(), 1, 0 );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setBitFlag8( 2, true );
checkQuestCompletion( quest, player, 1 ); checkQuestCompletion( quest, player, 1 );
} }
@ -173,6 +175,7 @@ private:
{ {
eventMgr().sendEventNotice( player, getId(), 2, 0 ); eventMgr().sendEventNotice( player, getId(), 2, 0 );
quest.setUI8BL( 1 ); quest.setUI8BL( 1 );
quest.setBitFlag8( 3, true );
checkQuestCompletion( quest, player, 1 ); checkQuestCompletion( quest, player, 1 );
} }

View file

@ -41,6 +41,7 @@ class SubSea007 : public Sapphire::ScriptAPI::QuestScript
static constexpr auto Actor1 = 1000957; // R'sushmo ( Pos: -49.240898 43.991699 -146.380005 Teri: 128 ) static constexpr auto Actor1 = 1000957; // R'sushmo ( Pos: -49.240898 43.991699 -146.380005 Teri: 128 )
static constexpr auto Actor2 = 1000937; // Godebert ( Pos: -12.222500 44.998798 -251.850006 Teri: 128 ) static constexpr auto Actor2 = 1000937; // Godebert ( Pos: -12.222500 44.998798 -251.850006 Teri: 128 )
static constexpr auto Item0 = 2000455; static constexpr auto Item0 = 2000455;
static constexpr auto Item0Icon = 25906;
public: public:
SubSea007() : Sapphire::ScriptAPI::QuestScript( 65653 ){}; SubSea007() : Sapphire::ScriptAPI::QuestScript( 65653 ){};
@ -117,7 +118,7 @@ class SubSea007 : public Sapphire::ScriptAPI::QuestScript
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 0, 0 ); // TODO: Show item icon eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setSeq( Seq2 ); quest.setSeq( Seq2 );
} }

View file

@ -45,6 +45,7 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
static constexpr auto Actor2 = 1000938; // Ginnade ( Pos: -4.651690 45.018398 -241.815002 Teri: 128 ) static constexpr auto Actor2 = 1000938; // Ginnade ( Pos: -4.651690 45.018398 -241.815002 Teri: 128 )
static constexpr auto Actor3 = 1000947; // Lyngsath ( Pos: -54.642601 43.991699 -151.201996 Teri: 128 ) static constexpr auto Actor3 = 1000947; // Lyngsath ( Pos: -54.642601 43.991699 -151.201996 Teri: 128 )
static constexpr auto Item0 = 2000451; static constexpr auto Item0 = 2000451;
static constexpr auto Item0Icon = 25919;
public: public:
SubSea008() : Sapphire::ScriptAPI::QuestScript( 65654 ){}; SubSea008() : Sapphire::ScriptAPI::QuestScript( 65654 ){};
@ -58,7 +59,8 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
{ {
case Actor0: case Actor0:
{ {
Scene00000( quest, player ); if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break; break;
} }
case Actor1: case Actor1:
@ -71,12 +73,14 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
} }
case Actor2: case Actor2:
{ {
Scene00003( quest, player ); if( quest.getSeq() == Seq2 )
Scene00003( quest, player );
break; break;
} }
case Actor3: case Actor3:
{ {
Scene00005( quest, player ); if( quest.getSeq() == Seq2 )
Scene00005( quest, player );
break; break;
} }
} }
@ -137,7 +141,7 @@ private:
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
quest.setUI8BL( 1 ); quest.setUI8BL( 1 );
eventMgr().sendEventNotice( player, getId(), 0, 0 ); //TODO: add item icon eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setSeq( Seq2 ); quest.setSeq( Seq2 );
} }
@ -167,6 +171,7 @@ private:
{ {
eventMgr().sendEventNotice( player, getId(), 1, 0 ); eventMgr().sendEventNotice( player, getId(), 1, 0 );
quest.setUI8AL( 1 ); quest.setUI8AL( 1 );
quest.setBitFlag8( 1, true );
checkQuestCompletion( quest, player, 1 ); checkQuestCompletion( quest, player, 1 );
} }
@ -197,6 +202,7 @@ private:
{ {
eventMgr().sendEventNotice( player, getId(), 2, 0 ); eventMgr().sendEventNotice( player, getId(), 2, 0 );
quest.setUI8BH( 1 ); quest.setUI8BH( 1 );
quest.setBitFlag8( 2, true );
checkQuestCompletion( quest, player, 1 ); checkQuestCompletion( quest, player, 1 );
} }

View file

@ -64,7 +64,8 @@ class SubSea016 : public Sapphire::ScriptAPI::QuestScript
} }
case Actor1: case Actor1:
{ {
Scene00002( quest, player ); if( quest.getSeq() == Seq1 )
Scene00002( quest, player );
break; break;
} }
} }
@ -131,6 +132,7 @@ class SubSea016 : public Sapphire::ScriptAPI::QuestScript
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{ {
eventMgr().sendEventNotice( player, getId(), 0, 0 ); eventMgr().sendEventNotice( player, getId(), 0, 0 );
quest.setBitFlag8( 1, true );
quest.setSeq( SeqFinish ); quest.setSeq( SeqFinish );
} }

View file

@ -0,0 +1,324 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include "Manager/EventMgr.h"
#include <Actor/Player.h>
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil025_00671
// Quest Name: Nothing to See Here
// Quest ID: 66207
// Start NPC: 1003995 (Papashan)
// End NPC: 1003995 (Papashan)
using namespace Sapphire;
class SubWil025 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// BitFlag8
// UI8AH
// UI8AL
// UI8BH
// UI8BL
// UI8CH
// UI8CL
/// Countable Num: 0 Seq: 1 Event: 1 Listener: 1004599
/// Countable Num: 0 Seq: 255 Event: 1 Listener: 1004600
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1003995;// Papashan ( Pos: 75.338402 2.138110 316.362000 Teri: 141 )
static constexpr auto Actor1 = 1004599;// Stern Sultansworn ( Pos: 89.876198 4.633540 425.415009 Teri: 141 )
static constexpr auto Actor2 = 1004600;// Serious Sultansworn ( Pos: 126.024002 14.465300 278.462006 Teri: 141 )
static constexpr auto Actor3 = 1004601;// Servile Sultansworn ( Pos: -62.415001 4.641350 261.281006 Teri: 141 )
static constexpr auto Item0 = 2000463;
public:
SubWil025() : Sapphire::ScriptAPI::QuestScript( 66207 ){};
~SubWil025() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
else if( quest.getSeq() == SeqFinish )
Scene00010( quest, player );
break;
}
case Actor1:
{
if( quest.getSeq() == Seq1 )
{
if( quest.getUI8AL() == 0 )
Scene00001( quest, player );
else
Scene00003( quest, player );
}
else if( quest.getSeq() == SeqFinish )
{
Scene00011( quest, player );
}
break;
}
case Actor2:
{
if( quest.getSeq() == Seq1 )
{
if( quest.getUI8BH() == 0 )
Scene00004( quest, player );
else
Scene00006( quest, player );
}
else if( quest.getSeq() == SeqFinish )
{
Scene00012( quest, player );
}
break;
}
case Actor3:
{
if( quest.getSeq() == Seq1 )
{
if( quest.getUI8BL() == 0 )
Scene00007( quest, player );
else
Scene00009( quest, player );
}
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player, uint32_t varIdx )
{
if( varIdx == 1 )
{
quest.setUI8AH( quest.getUI8AH() + 1 );
quest.setUI8CH( quest.getUI8CH() - 1 );
auto actor1Talked = quest.getUI8AL();
auto actor2Talked = quest.getUI8BH();
auto actor3Talked = quest.getUI8BL();
if( actor1Talked && actor2Talked && actor3Talked )
{
quest.setSeq( SeqFinish );
}
eventMgr().sendEventNotice( player, getId(), 0, 2, quest.getUI8AH(), 3 );
}
}
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )// accept quest
{
quest.setSeq( Seq1 );
quest.setUI8CH( 3 );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
Scene00002( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8AL( 1 );
quest.setBitFlag8( 1, true );
checkQuestCompletion( quest, player, 1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
Scene00005( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00005( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 5, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00005Return ) );
}
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BH( 1 );
quest.setBitFlag8( 2, true );
checkQuestCompletion( quest, player, 1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00006( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 6, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00006Return ) );
}
void Scene00006Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00007( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 7, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00007Return ) );
}
void Scene00007Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
Scene00008( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00008( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 8, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00008Return ) );
}
void Scene00008Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BL( 1 );
quest.setBitFlag8( 3, true );
checkQuestCompletion( quest, player, 1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00009( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 9, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00009Return ) );
}
void Scene00009Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00010( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 10, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00010Return ) );
}
void Scene00010Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00011( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 11, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00011Return ) );
}
void Scene00011Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00012( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 12, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00012Return ) );
}
void Scene00012Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00013( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 13, HIDE_HOTBAR, bindSceneReturn( &SubWil025::Scene00013Return ) );
}
void Scene00013Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
};
EXPOSE_SCRIPT( SubWil025 );

View file

@ -0,0 +1,117 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include <Actor/Player.h>
#include "Manager/EventMgr.h"
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil026_00623
// Quest Name: Takin' What They're Givin'
// Quest ID: 66159
// Start NPC: 1001353 (Momodi)
// End NPC: 1002065 (Dadanen)
using namespace Sapphire;
class SubWil026 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// UI8AL
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1002065
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001353; // Momodi ( Pos: 21.072599 7.450000 -78.782303 Teri: 130 )
static constexpr auto Actor1 = 1002065; // Dadanen ( Pos: 60.946701 45.145302 -204.985992 Teri: 140 )
public:
SubWil026() : Sapphire::ScriptAPI::QuestScript( 66159 ){};
~SubWil026() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break;
}
case Actor1:
{
if( quest.getSeq() == SeqFinish )
Scene00001( quest, player );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil026::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
quest.setUI8AL( 1 );
quest.setSeq( SeqFinish );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, FADE_OUT | CONDITION_CUTSCENE | HIDE_UI, bindSceneReturn( &SubWil026::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
Scene00002( quest, player );
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, HIDE_HOTBAR, bindSceneReturn( &SubWil026::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
};
EXPOSE_SCRIPT( SubWil026 );

View file

@ -0,0 +1,194 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include <Actor/Player.h>
#include "Manager/EventMgr.h"
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil060_00303
// Quest Name: Step Nine
// Quest ID: 65839
// Start NPC: 1001500 (Cicidoa)
// End NPC: 1001541 (Roger)
using namespace Sapphire;
class SubWil060 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// UI8AL
// UI8BH
// UI8BL
/// Countable Num: 1 Seq: 1 Event: 1 Listener: 1001455
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1001541
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001500; // Cicidoa ( Pos: 81.792099 1.050750 311.240997 Teri: 141 )
static constexpr auto Actor1 = 1001455; // Gagari ( Pos: 59.952599 0.999894 255.863998 Teri: 141 )
static constexpr auto Actor2 = 1001541; // Roger ( Pos: -99.395401 -11.380900 -41.723999 Teri: 141 )
static constexpr auto Item0 = 2000199;
static constexpr auto Item1 = 2000238;
static constexpr auto Item1Icon = 25210;
public:
SubWil060() : Sapphire::ScriptAPI::QuestScript( 65839 ){};
~SubWil060() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break;
}
case Actor1:
{
if( quest.getSeq() == Seq1 )
Scene00001( quest, player );
break;
}
case Actor2:
{
if( quest.getSeq() == SeqFinish )
Scene00004( quest, player );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil060::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
quest.setUI8BH( 1 );
quest.setSeq( Seq1 );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, HIDE_HOTBAR, bindSceneReturn( &SubWil060::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
Scene00002( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, HIDE_HOTBAR, bindSceneReturn( &SubWil060::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BH( 0 );
quest.setUI8BL( 1 );
eventMgr().sendNotice( player, getId(), 0, { Item1Icon } );
quest.setSeq( SeqFinish );
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, NONE, bindSceneReturn( &SubWil060::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, HIDE_HOTBAR, bindSceneReturn( &SubWil060::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
Scene00005( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00005( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 5, HIDE_HOTBAR, bindSceneReturn( &SubWil060::Scene00005Return ) );
}
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00006( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 6, NONE, bindSceneReturn( &SubWil060::Scene00006Return ) );
}
void Scene00006Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
};
EXPOSE_SCRIPT( SubWil060 );

View file

@ -0,0 +1,169 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include "Manager/EventMgr.h"
#include <Actor/Player.h>
#include <Actor/BNpc.h>
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil062_00305
// Quest Name: Until a Quieter Time
// Quest ID: 65841
// Start NPC: 1001541 (Roger)
// End NPC: 1001447 (Warin)
using namespace Sapphire;
class SubWil062 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// UI8AL
// UI8BH
/// Countable Num: 8 Seq: 1 Event: 9 Listener: 432
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1001447
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001541; // Roger ( Pos: -99.395401 -11.380900 -41.723999 Teri: 141 )
static constexpr auto Actor1 = 1001447; // Warin ( Pos: -32.639099 -1.033260 -148.485992 Teri: 141 )
static constexpr auto Enemy0 = 294; // Antling Worker
static constexpr auto Item0 = 2000168;
static constexpr auto Item0Icon = 22205;
public:
SubWil062() : Sapphire::ScriptAPI::QuestScript( 65841 ){};
~SubWil062() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break;
}
case Actor1:
{
if( quest.getSeq() == SeqFinish )
Scene00002( quest, player );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
void onBNpcKill( World::Quest& quest, Entity::BNpc& bnpc, Entity::Player& player ) override
{
if( bnpc.getBNpcNameId() != Enemy0 )
return;
unsigned currentKC = quest.getUI8AL() + 1;
quest.setUI8BH( currentKC );
quest.setUI8AL( currentKC );
if( currentKC >= 5 )
quest.setSeq( SeqFinish );
eventMgr().sendNotice( player, getId(), 0, { currentKC, 5, Item0Icon } );
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil062::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
quest.setUI8AL( 0 );
quest.setSeq( Seq1 );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, NONE, bindSceneReturn( &SubWil062::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, HIDE_HOTBAR, bindSceneReturn( &SubWil062::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
quest.setUI8BH( 0 );
Scene00003( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, HIDE_HOTBAR, bindSceneReturn( &SubWil062::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, NONE, bindSceneReturn( &SubWil062::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
};
EXPOSE_SCRIPT( SubWil062 );

View file

@ -0,0 +1,103 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include <Actor/Player.h>
#include "Manager/EventMgr.h"
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil063_00306
// Quest Name: Prudence at This Junction
// Quest ID: 65842
// Start NPC: 1001447 (Warin)
// End NPC: 1001447 (Warin)
using namespace Sapphire;
class SubWil063 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// UI8AL
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1001447
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001447; // Warin ( Pos: -32.639099 -1.033260 -148.485992 Teri: 141 )
static constexpr auto Seq0Actor0 = 0; //
static constexpr auto Seq1Actor0 = 1; //
public:
SubWil063() : Sapphire::ScriptAPI::QuestScript( 65842 ){};
~SubWil063() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
else if( quest.getSeq() == SeqFinish )
Scene00001( quest, player );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil063::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
quest.setSeq( SeqFinish );
eventMgr().sendNotice( player, getId(), 0, {} );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, HIDE_HOTBAR, bindSceneReturn( &SubWil063::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
};
EXPOSE_SCRIPT( SubWil063 );

View file

@ -0,0 +1,315 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include "Manager/EventMgr.h"
#include <Actor/Player.h>
#include <Actor/BNpc.h>
#include <ScriptObject.h>
#include <Service.h>
#include "Actor/BNpc.h"
#include "Manager/TerritoryMgr.h"
#include "Territory/Territory.h"
// Quest Script: SubWil064_00307
// Quest Name: Out of House and Home
// Quest ID: 65843
// Start NPC: 1001447 (Warin)
// End NPC: 1001447 (Warin)
using namespace Sapphire;
class SubWil064 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// BitFlag8
// UI8AL
// UI8BH
/// Countable Num: 0 Seq: 1 Event: 1 Listener: 2000268
/// Countable Num: 1 Seq: 255 Event: 8 Listener: 2000268
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001447; // Warin ( Pos: -32.639099 -1.033260 -148.485992 Teri: 141 )
static constexpr auto Enemy0 = 3785130; //
static constexpr auto Enemy1 = 3785131; //
static constexpr auto Enemy2 = 3785134; //
static constexpr auto Eobject0 = 2000268; // Narrow Fissure ( Pos: 25.690800 13.106300 47.828999 Teri: 141 )
static constexpr auto Item0 = 2000212;
public:
SubWil064() : Sapphire::ScriptAPI::QuestScript( 65843 ){};
~SubWil064() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
{
Scene00000( quest, player );
}
else if( quest.getSeq() == SeqFinish )
{
Scene00013( quest,player );
}
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
if (actorId == Eobject0)
{
Scene00002( quest, player );
}
}
void onBNpcKill( World::Quest& quest, Entity::BNpc& bnpc, Entity::Player& player ) override
{
switch( bnpc.getLayoutId() )
{
case Enemy0:
{
auto instance = teriMgr().getTerritoryByGuId( player.getTerritoryId() );
auto enemy1 = instance->createBNpcFromLayoutId( Enemy1, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
auto enemy2 = instance->createBNpcFromLayoutId( Enemy2, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
enemy1->setTriggerOwnerId( player.getId() );
enemy2->setTriggerOwnerId( player.getId() );
enemy1->hateListAddDelayed( player.getAsPlayer(), 1 );
enemy2->hateListAddDelayed( player.getAsPlayer(), 1 );
quest.setUI8AL( 1 );
break;
}
case Enemy1:
case Enemy2:
{
quest.setUI8AL( quest.getUI8AL() + 1 );
if( quest.getUI8AL() >= 4 )
{
quest.setUI8BH( 0 );
quest.setUI8AL( 0 );
quest.setSeq( SeqFinish );
eventMgr().sendNotice( player, getId(), 0, {} );
}
break;
}
}
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, NONE, bindSceneReturn( &SubWil064::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
Scene00001( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, NONE, bindSceneReturn( &SubWil064::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BH( 1 );
quest.setSeq( Seq1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, NONE, bindSceneReturn( &SubWil064::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setBitFlag8( 1, true );
auto instance = teriMgr().getTerritoryByGuId( player.getTerritoryId() );
auto enemy0 = instance->createBNpcFromLayoutId( Enemy0, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
enemy0->setTriggerOwnerId( player.getId() );
enemy0->hateListAddDelayed( player.getAsPlayer(), 1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, NONE, bindSceneReturn( &SubWil064::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, NONE, bindSceneReturn( &SubWil064::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00005( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 5, NONE, bindSceneReturn( &SubWil064::Scene00005Return ) );
}
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00006( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 6, NONE, bindSceneReturn( &SubWil064::Scene00006Return ) );
}
void Scene00006Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00007( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 7, NONE, bindSceneReturn( &SubWil064::Scene00007Return ) );
}
void Scene00007Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00008( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 8, NONE, bindSceneReturn( &SubWil064::Scene00008Return ) );
}
void Scene00008Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00009( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 9, NONE, bindSceneReturn( &SubWil064::Scene00009Return ) );
}
void Scene00009Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00010( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 10, NONE, bindSceneReturn( &SubWil064::Scene00010Return ) );
}
void Scene00010Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00011( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 11, NONE, bindSceneReturn( &SubWil064::Scene00011Return ) );
}
void Scene00011Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00012( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 12, NONE, bindSceneReturn( &SubWil064::Scene00012Return ) );
}
void Scene00012Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00013( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 13, NONE, bindSceneReturn( &SubWil064::Scene00013Return ) );
}
void Scene00013Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
};
EXPOSE_SCRIPT( SubWil064 );

View file

@ -0,0 +1,234 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include <Actor/Player.h>
#include "Manager/EventMgr.h"
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil070_00324
// Quest Name: Disorderly Conduct
// Quest ID: 65860
// Start NPC: 1001541 (Roger)
// End NPC: 1001541 (Roger)
using namespace Sapphire;
class SubWil070 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// BitFlag8
// UI8AL
// UI8BH
/// Countable Num: 4 Seq: 1 Event: 1 Listener: 1001462
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1001463
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001541; // Roger ( Pos: -99.395401 -11.380900 -41.723999 Teri: 141 )
static constexpr auto Actor1 = 1001462; // Roundelph ( Pos: -93.339500 -11.350300 -41.367199 Teri: 141 )
static constexpr auto Actor2 = 1001463; // Adalfuns ( Pos: -72.826401 -12.667800 -54.076199 Teri: 141 )
static constexpr auto Actor3 = 1001465; // Solid Trunk ( Pos: -90.043503 -11.398500 -53.666000 Teri: 141 )
static constexpr auto Actor4 = 1001466; // Ricard ( Pos: -89.735001 -11.350000 -51.539902 Teri: 141 )
static constexpr auto Item0 = 2000234;
static constexpr auto Item0Icon = 26153;
static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq1Actor1 = 1;
static constexpr auto Seq1Actor2 = 2;
static constexpr auto Seq1Actor3 = 3;
static constexpr auto Seq1Actor4 = 4;
static constexpr auto Seq2Actor0 = 5;
static constexpr auto Seq2Actor0Npctradeno = 99;
static constexpr auto Seq2Actor0Npctradeok = 100;
public:
SubWil070() : Sapphire::ScriptAPI::QuestScript( 65860 ){};
~SubWil070() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == 0 )
Scene00000( quest, player );
else if( quest.getSeq() == SeqFinish )
Scene00005( quest, player );
break;
}
case Actor1:
{
if( quest.getSeq() == 1 )
Scene00001( quest, player );
break;
}
case Actor2:
{
if( quest.getSeq() == 1 )
Scene00002( quest, player );
break;
}
case Actor3:
{
if( quest.getSeq() == 1 )
Scene00003( quest, player );
break;
}
case Actor4:
{
if( quest.getSeq() == 1 )
Scene00004( quest, player );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player, uint32_t varIdx )
{
if( varIdx == 1 )
{
quest.setUI8AL( quest.getUI8AL() + 1 );
if (quest.getUI8AL() == 4)
quest.setSeq( SeqFinish );
}
eventMgr().sendNotice( player, getId(), 0, { quest.getUI8AL(), 4, Item0Icon } );
}
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil070::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
quest.setUI8BH( 1 );
quest.setSeq( Seq1 );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, HIDE_HOTBAR, bindSceneReturn( &SubWil070::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
checkQuestCompletion( quest, player, 1 );
quest.setBitFlag8( 1, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, HIDE_HOTBAR, bindSceneReturn( &SubWil070::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
checkQuestCompletion( quest, player, 1 );
quest.setBitFlag8( 2, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, HIDE_HOTBAR, bindSceneReturn( &SubWil070::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
checkQuestCompletion( quest, player, 1 );
quest.setBitFlag8( 3, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, HIDE_HOTBAR, bindSceneReturn( &SubWil070::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
checkQuestCompletion( quest, player, 1 );
quest.setBitFlag8( 4, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00005( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 5, NONE, bindSceneReturn( &SubWil070::Scene00005Return ) );
}
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
Scene00100( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00099( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 99, NONE, bindSceneReturn( &SubWil070::Scene00099Return ) );
}
void Scene00099Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00100( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 100, HIDE_HOTBAR, bindSceneReturn( &SubWil070::Scene00100Return ) );
}
void Scene00100Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
};
EXPOSE_SCRIPT( SubWil070 );

View file

@ -0,0 +1,415 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include "Manager/EventMgr.h"
#include <Actor/Player.h>
#include <Actor/BNpc.h>
#include <ScriptObject.h>
#include <Service.h>
#include "Actor/BNpc.h"
#include "Manager/TerritoryMgr.h"
#include "Territory/Territory.h"
// Quest Script: SubWil073_00327
// Quest Name: Spriggan Cleaning
// Quest ID: 65863
// Start NPC: 1001447 (Warin)
// End NPC: 1001447 (Warin)
using namespace Sapphire;
class SubWil073 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// BitFlag8
// UI8AH
// UI8AL
// UI8BH
// UI8BL
// UI8CH
// UI8CL
// UI8DH
// UI8DL
// UI8EH
/// Countable Num: 4 Seq: 1 Event: 1 Listener: 2000377
/// Countable Num: 1 Seq: 255 Event: 5 Listener: 100
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
Seq1 = 1,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1001447; // Warin ( Pos: -32.639099 -1.033260 -148.485992 Teri: 141 )
static constexpr auto Enemy0 = 3742257; //
static constexpr auto Enemy1 = 3742258; //
static constexpr auto Enemy2 = 3742259; //
static constexpr auto Enemy3 = 3742261; //
static constexpr auto Eobject0 = 2000377; // Pockmarked Silver Ore ( Pos: -134.695999 6.168210 -116.594002 Teri: 141 )
static constexpr auto Eobject1 = 2000378; // Pockmarked Silver Ore ( Pos: -95.958298 -1.021940 -163.731003 Teri: 141 )
static constexpr auto Eobject2 = 2000379; // Pockmarked Silver Ore ( Pos: -103.938004 0.491295 -213.695007 Teri: 141 )
static constexpr auto Eobject3 = 2000380; // Pockmarked Silver Ore ( Pos: -1.174590 -1.322410 -111.265999 Teri: 141 )
static constexpr auto EventActionSearch = 1;
static constexpr auto Seq0Actor0 = 0; //
static constexpr auto Seq1Eobject0 = 1; //
static constexpr auto Seq1Eobject0Eventactionno = 99; // Hecatoncheir Piledriver
static constexpr auto Seq1Eobject0Eventactionok = 100; // Hecatoncheir Blastmaster ( Pos: -135.210007 5.708900 -117.417999 Teri: 141 )
static constexpr auto Seq1Eobject1 = 2; // Ruins Runner ( Pos: -5.462710 -1.142520 27.215000 Teri: 5 )
static constexpr auto Seq1Eobject1Eventactionno = 97; // Hecatoncheir Stonehauler
static constexpr auto Seq1Eobject1Eventactionok = 98; // Hecatoncheir Shockblocker
static constexpr auto Seq1Eobject2 = 3; // Antelope Doe
static constexpr auto Seq1Eobject2Eventactionno = 95; // Flux Flan
static constexpr auto Seq1Eobject2Eventactionok = 96; // Hecatoncheir Overseer
static constexpr auto Seq1Eobject3 = 4; // Antelope Stag
static constexpr auto Seq1Eobject3Eventactionno = 93; // Sargas
static constexpr auto Seq1Eobject3Eventactionok = 94; // Shaula
static constexpr auto Seq2Actor0 = 5; // Opo-opo
public:
SubWil073() : Sapphire::ScriptAPI::QuestScript( 65863 ){};
~SubWil073() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
else if( quest.getSeq() == SeqFinish )
Scene00005( quest, player );
break;
}
case Eobject0:
{
eventMgr().eventActionStart(
player, getId(), EventActionSearch,
[ & ]( Entity::Player& player, uint32_t eventId, uint64_t additional ) {
Scene00094( quest, player );
},
nullptr, 0 );
break;
}
case Eobject1:
{
eventMgr().eventActionStart(
player, getId(), EventActionSearch,
[ & ]( Entity::Player& player, uint32_t eventId, uint64_t additional ) {
Scene00095( quest, player );
},
nullptr, 0 );
break;
}
case Eobject2:
{
eventMgr().eventActionStart(
player, getId(), EventActionSearch,
[ & ]( Entity::Player& player, uint32_t eventId, uint64_t additional ) {
Scene00096( quest, player );
},
nullptr, 0 );
break;
}
case Eobject3:
{
eventMgr().eventActionStart(
player, getId(), EventActionSearch,
[ & ]( Entity::Player& player, uint32_t eventId, uint64_t additional ) {
Scene00097( quest, player );
},
nullptr, 0 );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
void onBNpcKill( World::Quest& quest, Entity::BNpc& bnpc, Entity::Player& player ) override
{
switch (bnpc.getLayoutId())
{
case Enemy0:
{
quest.setUI8AL( 1 );
quest.setUI8BH( 1 );
checkQuestCompletion( quest, player );
break;
}
case Enemy1:
{
quest.setUI8BL( 1 );
quest.setUI8CH( 1 );
checkQuestCompletion( quest, player );
break;
}
case Enemy2:
{
quest.setUI8CL( 1 );
quest.setUI8DH( 1 );
checkQuestCompletion( quest, player );
break;
}
case Enemy3:
{
quest.setUI8DL( 1 );
quest.setUI8EH( 1 );
checkQuestCompletion( quest, player );
break;
}
}
}
private:
void checkQuestCompletion( World::Quest& quest, Entity::Player& player )
{
quest.setUI8AH( quest.getUI8AH() + 1 );
eventMgr().sendEventNotice( player, getId(), 0, 2, quest.getUI8AH(), 4 );
if( quest.getUI8AH() >= 4 )
{
quest.setUI8AL( 0 );
quest.setUI8BH( 0 );
quest.setUI8BL( 0 );
quest.setUI8CH( 0 );
quest.setUI8CL( 0 );
quest.setUI8DH( 0 );
quest.setUI8DL( 0 );
quest.setUI8EH( 0 );
quest.setUI8AH( 0 );
quest.setBitFlag8( 1, false );
quest.setBitFlag8( 2, false );
quest.setBitFlag8( 3, false );
quest.setBitFlag8( 4, false );
quest.setSeq( SeqFinish );
}
}
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil073::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
Scene00001( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, HIDE_HOTBAR, bindSceneReturn( &SubWil073::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setSeq( Seq1 );
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, NONE, bindSceneReturn( &SubWil073::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, NONE, bindSceneReturn( &SubWil073::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00004( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 4, NONE, bindSceneReturn( &SubWil073::Scene00004Return ) );
}
void Scene00004Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00005( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 5, HIDE_HOTBAR, bindSceneReturn( &SubWil073::Scene00005Return ) );
}
void Scene00005Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00093( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 93, NONE, bindSceneReturn( &SubWil073::Scene00093Return ) );
}
void Scene00093Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00094( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 94, NONE, bindSceneReturn( &SubWil073::Scene00094Return ) );
}
void Scene00094Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
auto instance = teriMgr().getTerritoryByGuId( player.getTerritoryId() );
auto enemy = instance->createBNpcFromLayoutId( Enemy0, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
enemy->setTriggerOwnerId( player.getId() );
enemy->hateListAddDelayed( player.getAsPlayer(), 1 );
quest.setBitFlag8( 1, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00095( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 95, NONE, bindSceneReturn( &SubWil073::Scene00095Return ) );
}
void Scene00095Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
auto instance = teriMgr().getTerritoryByGuId( player.getTerritoryId() );
auto enemy = instance->createBNpcFromLayoutId( Enemy1, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
enemy->setTriggerOwnerId( player.getId() );
enemy->hateListAddDelayed( player.getAsPlayer(), 1 );
quest.setBitFlag8( 2, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00096( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 96, NONE, bindSceneReturn( &SubWil073::Scene00096Return ) );
}
void Scene00096Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
auto instance = teriMgr().getTerritoryByGuId( player.getTerritoryId() );
auto enemy = instance->createBNpcFromLayoutId( Enemy2, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
enemy->setTriggerOwnerId( player.getId() );
enemy->hateListAddDelayed( player.getAsPlayer(), 1 );
quest.setBitFlag8( 3, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00097( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 97, NONE, bindSceneReturn( &SubWil073::Scene00097Return ) );
}
void Scene00097Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
auto instance = teriMgr().getTerritoryByGuId( player.getTerritoryId() );
auto enemy = instance->createBNpcFromLayoutId( Enemy3, 1220 /*Find the right value*/, Common::BNpcType::Enemy );
enemy->setTriggerOwnerId( player.getId() );
enemy->hateListAddDelayed( player.getAsPlayer(), 1 );
quest.setBitFlag8( 4, true );
}
//////////////////////////////////////////////////////////////////////
void Scene00098( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 98, NONE, bindSceneReturn( &SubWil073::Scene00098Return ) );
}
void Scene00098Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00099( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 99, NONE, bindSceneReturn( &SubWil073::Scene00099Return ) );
}
void Scene00099Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
//////////////////////////////////////////////////////////////////////
void Scene00100( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 100, NONE, bindSceneReturn( &SubWil073::Scene00100Return ) );
}
void Scene00100Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
};
EXPOSE_SCRIPT( SubWil073 );

View file

@ -0,0 +1,136 @@
// This is an automatically generated C++ script template
// Content needs to be added by hand to make it function
// In order for this script to be loaded, move it to the correct folder in <root>/scripts/
#include <Actor/Player.h>
#include "Manager/EventMgr.h"
#include <ScriptObject.h>
#include <Service.h>
// Quest Script: SubWil080_00328
// Quest Name: Supply and Demands
// Quest ID: 65864
// Start NPC: 1002065 (Dadanen)
// End NPC: 1002061 (Drunken Stag)
using namespace Sapphire;
class SubWil080 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// UI8AL
// UI8BH
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1002061
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
Seq0 = 0,
SeqFinish = 255,
};
// Entities found in the script data of the quest
static constexpr auto Actor0 = 1002065; // Dadanen ( Pos: 60.946701 45.145302 -204.985992 Teri: 140 )
static constexpr auto Actor1 = 1002061; // Drunken Stag ( Pos: 240.998993 58.318298 -160.998001 Teri: 140 )
static constexpr auto Item0 = 2000368;
public:
SubWil080() : Sapphire::ScriptAPI::QuestScript( 65864 ){};
~SubWil080() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break;
}
case Actor1:
{
if( quest.getSeq() == SeqFinish )
Scene00001( quest, player );
break;
}
}
}
void onEventItem( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
}
private:
//////////////////////////////////////////////////////////////////////
// Available Scenes in this quest, not necessarly all are used
//////////////////////////////////////////////////////////////////////
void Scene00000( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 0, HIDE_HOTBAR, bindSceneReturn( &SubWil080::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 ) // accept quest
{
quest.setUI8BH( 1 );
quest.setSeq( SeqFinish );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, HIDE_HOTBAR, bindSceneReturn( &SubWil080::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if (result.getResult(0) == 1)
{
Scene00002( quest, player );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00002( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 2, NONE, bindSceneReturn( &SubWil080::Scene00002Return ) );
}
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), result.getResult( 1 ) );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00003( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 3, NONE, bindSceneReturn( &SubWil080::Scene00003Return ) );
}
void Scene00003Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
}
};
EXPOSE_SCRIPT( SubWil080 );

View file

@ -161,22 +161,20 @@ int main( int argc, char* argv[] )
Logger::fatal( "Error setting up EXD data " ); Logger::fatal( "Error setting up EXD data " );
return 0; return 0;
} }
auto idList = g_exdDataGen.getIdList< Excel::Action >(); auto actionList = g_exdDataGen.getRows< Excel::Action >();
std::map< uint32_t, ActionEntry > actions; std::map< uint32_t, ActionEntry > actions;
std::map< uint32_t, std::vector< uint32_t > > traversedCombos; std::map< uint32_t, std::vector< uint32_t > > traversedCombos;
auto total = idList.size(); auto total = actionList.size();
int cursor = 0; int cursor = 0;
for( auto id : idList ) for( const auto& [ id, action ] : actionList )
{ {
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f; auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 ) if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} actions of {} ({:.2f}%)", cursor, total, done ); Logger::info( "Processing {} actions of {} ({:.2f}%)", cursor, total, done );
auto action = g_exdDataGen.getRow< Excel::Action >( id );
//auto actionTransient = g_exdData.get< Sapphire::Data::ActionTransient >( id ); //auto actionTransient = g_exdData.get< Sapphire::Data::ActionTransient >( id );
if( action ) if( action )
{ {

View file

@ -89,11 +89,11 @@ int main( int argc, char* argv[] )
// CFC list // CFC list
{ {
auto idList = g_exdDataGen.getIdList< Excel::ContentFinderCondition >(); auto cfcList = g_exdDataGen.getRows< Excel::ContentFinderCondition >();
std::stringstream cfcOutputStream; std::stringstream cfcOutputStream;
auto total = idList.size(); auto total = cfcList.size();
int cursor = 0; int cursor = 0;
std::map< uint8_t, std::string > instanceContentTypeMap; std::map< uint8_t, std::string > instanceContentTypeMap;
@ -118,7 +118,7 @@ int main( int argc, char* argv[] )
cfcOutputStream << "| ID | Instance | Territory | Name | Type |" << std::endl cfcOutputStream << "| ID | Instance | Territory | Name | Type |" << std::endl
<< "| --- | --- | --- | --- | --- |" << std::endl; << "| --- | --- | --- | --- | --- |" << std::endl;
for( auto id : idList ) for( const auto& [ id, cfc ] : cfcList )
{ {
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f; auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 ) if( cursor % 50 == 0 && cursor > 0 )
@ -127,8 +127,6 @@ int main( int argc, char* argv[] )
if( id == 0 ) if( id == 0 )
continue; continue;
auto cfc = g_exdDataGen.getRow< Excel::ContentFinderCondition >( id );
if( cfc ) if( cfc )
{ {
auto& cfcData = cfc->data(); auto& cfcData = cfc->data();
@ -234,24 +232,22 @@ int main( int argc, char* argv[] )
teriTypeIntendedUseMap[ TheFeastArea ] = "TheFeastArea"; teriTypeIntendedUseMap[ TheFeastArea ] = "TheFeastArea";
teriTypeIntendedUseMap[ PrivateEventArea ] = "PrivateEventArea"; teriTypeIntendedUseMap[ PrivateEventArea ] = "PrivateEventArea";
auto idList = g_exdDataGen.getIdList< Excel::TerritoryType >(); auto teriList = g_exdDataGen.getRows< Excel::TerritoryType >();
std::stringstream teritypeOutputStream; std::stringstream teritypeOutputStream;
teritypeOutputStream << "| ID | Place Name | Name | Intended Use |" << std::endl teritypeOutputStream << "| ID | Place Name | Name | Intended Use |" << std::endl
<< "| --- | --- | --- | --- |" << std::endl; << "| --- | --- | --- | --- |" << std::endl;
auto total = idList.size(); auto total = teriList.size();
int cursor = 0; int cursor = 0;
for( auto id : idList ) for( const auto& [ id, teriType ] : teriList )
{ {
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f; auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 ) if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} teritypes of {} ({:.2f}%)", cursor, total, done ); Logger::info( "Processing {} teritypes of {} ({:.2f}%)", cursor, total, done );
auto teriType = g_exdDataGen.getRow< Excel::TerritoryType >( id );
if( teriType ) if( teriType )
{ {
auto& teriTypeData = teriType->data(); auto& teriTypeData = teriType->data();
@ -291,24 +287,22 @@ int main( int argc, char* argv[] )
// class/job list // class/job list
{ {
auto idList = g_exdDataGen.getIdList< Excel::ClassJob >(); auto classJobList = g_exdDataGen.getRows< Excel::ClassJob >();
std::stringstream classjobOutputStream; std::stringstream classjobOutputStream;
classjobOutputStream << "| ID | Name | Short | Main Class |" << std::endl classjobOutputStream << "| ID | Name | Short | Main Class |" << std::endl
<< "| --- | --- | --- | --- |" << std::endl; << "| --- | --- | --- | --- |" << std::endl;
auto total = idList.size(); auto total = classJobList.size();
int cursor = 0; int cursor = 0;
for( auto id : idList ) for( const auto& [ id, classJob ] : classJobList )
{ {
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f; auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 ) if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} classjobs of {} ({:.2f}%)", cursor, total, done ); Logger::info( "Processing {} classjobs of {} ({:.2f}%)", cursor, total, done );
auto classJob = g_exdDataGen.getRow< Excel::ClassJob >( id );
if( classJob ) if( classJob )
{ {
auto& classJobData = classJob->data(); auto& classJobData = classJob->data();
@ -338,7 +332,7 @@ int main( int argc, char* argv[] )
// achievement list // achievement list
{ {
auto idList = g_exdDataGen.getIdList< Excel::Achievement >(); auto achvList = g_exdDataGen.getRows< Excel::Achievement >();
enum class Type : uint8_t enum class Type : uint8_t
{ {
@ -394,17 +388,15 @@ int main( int argc, char* argv[] )
achvOutputStream << "| ID | Name | Type (Subtype) | Description |" << std::endl achvOutputStream << "| ID | Name | Type (Subtype) | Description |" << std::endl
<< "| --- | --- | --- | --- |" << std::endl; << "| --- | --- | --- | --- |" << std::endl;
auto total = idList.size(); auto total = achvList.size();
int cursor = 0; int cursor = 0;
for( auto id : idList ) for( const auto& [ id, pAchv ] : achvList )
{ {
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f; auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 ) if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} achievements of {} ({:.2f}%)", cursor, total, done ); Logger::info( "Processing {} achievements of {} ({:.2f}%)", cursor, total, done );
auto pAchv = g_exdDataGen.getRow< Excel::Achievement >( id );
if( pAchv ) if( pAchv )
{ {
auto& achvData = pAchv->data(); auto& achvData = pAchv->data();

View file

@ -0,0 +1,95 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include <Util/Util.h>
#include <Util/UtilMath.h>
#pragma once
namespace Sapphire::World::AI::Fsm
{
class Condition
{
public:
Condition() = default;
virtual ~Condition() = default;
virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) const = 0;
virtual bool update( Sapphire::Entity::BNpc& src, float time )
{
if( isConditionMet( src ) )
return true;
return false;
};
};
class RoamNextTimeReachedCondition : public Condition
{
public:
bool isConditionMet( Sapphire::Entity::BNpc& src ) const override
{
if( ( Common::Util::getTimeSeconds() - src.getLastRoamTargetReachedTime() ) > 20 )
return true;
return false;
}
};
class RoamTargetReachedCondition : public Condition
{
public:
bool isConditionMet( Sapphire::Entity::BNpc& src ) const override
{
if( src.isRoamTargetReached() )
return true;
return false;
}
};
class HateListEmptyCondition : public Condition
{
public:
bool isConditionMet( Sapphire::Entity::BNpc& src ) const override
{
if( src.hateListGetHighest() )
return false;
return true;
}
};
class HateListHasEntriesCondition : public Condition
{
public:
bool isConditionMet( Sapphire::Entity::BNpc& src ) const override
{
if( src.hateListGetHighest() )
return true;
return false;
}
};
class SpawnPointDistanceGtMaxDistanceCondition : public Condition
{
public:
bool isConditionMet( Sapphire::Entity::BNpc& src ) const override
{
auto distanceOrig = Common::Util::distance( src.getPos(), src.getSpawnPos() );
if( distanceOrig > 40 )
return true;
return false;
}
};
class IsDeadCondition : public Condition
{
public:
bool isConditionMet( Sapphire::Entity::BNpc& src ) const override
{
if( !src.isAlive() )
return true;
return false;
}
};
}

43
src/world/AI/Fsm/State.h Normal file
View file

@ -0,0 +1,43 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "Transition.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class State
{
public:
virtual ~State() = default;
virtual void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount ) = 0;
virtual void onEnter( Entity::BNpc& bnpc ) { }
virtual void onExit( Entity::BNpc& bnpc ) { }
void addTransition( TransitionPtr transition )
{
m_transitions.push_back( transition );
}
void addTransition( StatePtr targetState, ConditionPtr condition )
{
m_transitions.push_back( make_Transition( targetState, condition ) );
}
TransitionPtr getTriggeredTransition( Entity::BNpc& bnpc )
{
for( auto transition : m_transitions )
{
if( transition->hasTriggered( bnpc ) )
return transition;
}
return nullptr;
}
private:
std::vector< TransitionPtr > m_transitions;
};
}

View file

@ -0,0 +1,79 @@
#include "StateCombat.h"
#include "Actor/BNpc.h"
#include "Logging/Logger.h"
#include <Service.h>
#include <Manager/TerritoryMgr.h>
#include <Territory/Territory.h>
#include <Navi/NaviProvider.h>
using namespace Sapphire::World;
void AI::Fsm::StateCombat::onUpdate( Entity::BNpc& bnpc, uint64_t tickCount )
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto pZone = teriMgr.getTerritoryByGuId( bnpc.getTerritoryId() );
auto pNaviProvider = pZone->getNaviProvider();
auto pHatedActor = bnpc.hateListGetHighest();
if( !pHatedActor )
return;
pNaviProvider->updateAgentParameters( bnpc );
auto distanceOrig = Common::Util::distance( bnpc.getPos(), bnpc.getSpawnPos() );
if( !pHatedActor->isAlive() || bnpc.getTerritoryId() != pHatedActor->getTerritoryId() )
{
bnpc.hateListRemove( pHatedActor );
pHatedActor = bnpc.hateListGetHighest();
}
if( !pHatedActor )
return;
auto distance = Common::Util::distance( bnpc.getPos(), pHatedActor->getPos() );
if( !bnpc.hasFlag( Entity::NoDeaggro ) )
{
}
if( !bnpc.hasFlag( Entity::Immobile ) && distance > ( bnpc.getNaviTargetReachedDistance() + pHatedActor->getRadius() ) )
{
if( pNaviProvider )
pNaviProvider->setMoveTarget( bnpc, pHatedActor->getPos() );
bnpc.moveTo( *pHatedActor );
}
if( pNaviProvider->syncPosToChara( bnpc ) )
bnpc.sendPositionUpdate();
if( distance < ( bnpc.getNaviTargetReachedDistance() + pHatedActor->getRadius() ) )
{
if( !bnpc.hasFlag( Entity::TurningDisabled ) && bnpc.face( pHatedActor->getPos() ) )
bnpc.sendPositionUpdate();
if( !bnpc.checkAction() )
bnpc.processGambits( tickCount );
// in combat range. ATTACK!
bnpc.autoAttack( pHatedActor );
}
}
void AI::Fsm::StateCombat::onEnter( Entity::BNpc& bnpc )
{
}
void AI::Fsm::StateCombat::onExit( Entity::BNpc& bnpc )
{
bnpc.hateListClear();
bnpc.changeTarget( Common::INVALID_GAME_OBJECT_ID64 );
bnpc.setStance( Common::Stance::Passive );
bnpc.setOwner( nullptr );
}

View file

@ -0,0 +1,20 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "State.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class StateCombat : public State
{
public:
virtual ~StateCombat() = default;
void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount );
void onEnter( Entity::BNpc& bnpc );
void onExit( Entity::BNpc& bnpc );
};
}

View file

@ -0,0 +1,26 @@
#include "StateDead.h"
#include "Actor/BNpc.h"
#include "Logging/Logger.h"
#include <Service.h>
#include <Manager/TerritoryMgr.h>
#include <Territory/Territory.h>
#include <Navi/NaviProvider.h>
using namespace Sapphire::World;
void AI::Fsm::StateDead::onUpdate( Entity::BNpc& bnpc, uint64_t tickCount )
{
}
void AI::Fsm::StateDead::onEnter( Entity::BNpc& bnpc )
{
}
void AI::Fsm::StateDead::onExit( Entity::BNpc& bnpc )
{
}

View file

@ -0,0 +1,20 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "State.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class StateDead : public State
{
public:
virtual ~StateDead() = default;
void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount );
void onEnter( Entity::BNpc& bnpc );
void onExit( Entity::BNpc& bnpc );
};
}

View file

@ -0,0 +1,20 @@
#include "StateIdle.h"
#include "Actor/BNpc.h"
#include "Logging/Logger.h"
using namespace Sapphire::World;
void AI::Fsm::StateIdle::onUpdate( Entity::BNpc& bnpc, uint64_t tickCount )
{
}
void AI::Fsm::StateIdle::onEnter( Entity::BNpc& bnpc )
{
bnpc.setLastRoamTargetReachedTime( Common::Util::getTimeSeconds() );
}
void AI::Fsm::StateIdle::onExit( Entity::BNpc& bnpc )
{
}

View file

@ -0,0 +1,20 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "State.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class StateIdle : public State
{
public:
virtual ~StateIdle() = default;
void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount );
void onEnter( Entity::BNpc& bnpc );
void onExit( Entity::BNpc& bnpc );
};
}

View file

@ -0,0 +1,38 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "StateMachine.h"
#include "State.h"
#pragma once
using namespace Sapphire;
using namespace Sapphire::World;
AI::Fsm::StatePtr AI::Fsm::StateMachine::addState( Fsm::StatePtr state )
{
m_states.push_back( state );
return state;
}
void AI::Fsm::StateMachine::setCurrentState( Fsm::StatePtr state )
{
m_pCurrentState = state;
}
void AI::Fsm::StateMachine::update( Entity::BNpc& bnpc, uint64_t tickCount )
{
if( !m_pCurrentState )
return;
TransitionPtr transition = m_pCurrentState->getTriggeredTransition( bnpc );
if( transition )
{
m_pCurrentState->onExit( bnpc );
m_pCurrentState = transition->getTargetState();
m_pCurrentState->onEnter( bnpc );
}
m_pCurrentState->onUpdate( bnpc, tickCount );
}

View file

@ -0,0 +1,23 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class StateMachine
{
public:
StateMachine() = default;
~StateMachine() = default;
StatePtr addState( StatePtr state );
void setCurrentState( StatePtr state );
virtual void update( Entity::BNpc& bnpc, uint64_t tickCount );
protected:
std::vector< StatePtr > m_states;
StatePtr m_pCurrentState;
};
}

View file

@ -0,0 +1,41 @@
#include "StateRetreat.h"
#include "Actor/BNpc.h"
#include "Logging/Logger.h"
#include <Service.h>
#include <Manager/TerritoryMgr.h>
#include <Territory/Territory.h>
#include <Navi/NaviProvider.h>
using namespace Sapphire::World;
void AI::Fsm::StateRetreat::onUpdate( Entity::BNpc& bnpc, uint64_t tickCount )
{
if( bnpc.moveTo( bnpc.getSpawnPos() ) )
{
bnpc.setRoamTargetReached( true );
bnpc.setLastRoamTargetReachedTime( Common::Util::getTimeSeconds() );
}
}
void AI::Fsm::StateRetreat::onEnter( Entity::BNpc& bnpc )
{
bnpc.setRoamTargetReached( false );
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto pZone = teriMgr.getTerritoryByGuId( bnpc.getTerritoryId() );
auto pNaviProvider = pZone->getNaviProvider();
bnpc.setInvincibilityType( Common::InvincibilityType::InvincibilityIgnoreDamage );
if( pNaviProvider )
pNaviProvider->setMoveTarget( bnpc, bnpc.getSpawnPos() );
}
void AI::Fsm::StateRetreat::onExit( Entity::BNpc& bnpc )
{
bnpc.setOwner( nullptr );
bnpc.setRoamTargetReached( false );
bnpc.setInvincibilityType( Common::InvincibilityType::InvincibilityNone );
}

View file

@ -0,0 +1,20 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "State.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class StateRetreat : public State
{
public:
virtual ~StateRetreat() = default;
void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount );
void onEnter( Entity::BNpc& bnpc );
void onExit( Entity::BNpc& bnpc );
};
}

View file

@ -0,0 +1,49 @@
#include "StateRoam.h"
#include "Actor/BNpc.h"
#include "Logging/Logger.h"
#include <Service.h>
#include <Manager/TerritoryMgr.h>
#include <Territory/Territory.h>
#include <Navi/NaviProvider.h>
using namespace Sapphire::World;
void AI::Fsm::StateRoam::onUpdate( Entity::BNpc& bnpc, uint64_t tickCount )
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto pZone = teriMgr.getTerritoryByGuId( bnpc.getTerritoryId() );
auto pNaviProvider = pZone->getNaviProvider();
if( pNaviProvider )
pNaviProvider->setMoveTarget( bnpc, bnpc.getRoamTargetPos() );
if( bnpc.moveTo( bnpc.getRoamTargetPos() ) )
{
bnpc.setRoamTargetReached( true );
bnpc.setLastRoamTargetReachedTime( Common::Util::getTimeSeconds() );
}
}
void AI::Fsm::StateRoam::onEnter( Entity::BNpc& bnpc )
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto pZone = teriMgr.getTerritoryByGuId( bnpc.getTerritoryId() );
auto pNaviProvider = pZone->getNaviProvider();
if( !pNaviProvider )
{
bnpc.setRoamTargetReached( true );
return;
}
auto pos = pNaviProvider->findRandomPositionInCircle( bnpc.getSpawnPos(), bnpc.getInstanceObjectInfo()->WanderingRange );
bnpc.setRoamTargetPos( pos );
}
void AI::Fsm::StateRoam::onExit( Entity::BNpc& bnpc )
{
bnpc.setRoamTargetReached( false );
}

View file

@ -0,0 +1,20 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "State.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class StateRoam : public State
{
public:
virtual ~StateRoam() = default;
void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount );
void onEnter( Entity::BNpc& bnpc );
void onExit( Entity::BNpc& bnpc );
};
}

View file

@ -0,0 +1,22 @@
#include <cstdint>
#include "ForwardsZone.h"
#include "Actor/BNpc.h"
#include "AI/Fsm/Condition.h"
#pragma once
namespace Sapphire::World::AI::Fsm
{
class Transition
{
public:
Transition( StatePtr targetState, ConditionPtr condition ) : m_pTargetState( targetState ), m_pCondition( condition ) { }
virtual ~Transition() = default;
StatePtr getTargetState() { return m_pTargetState; }
bool hasTriggered( Entity::BNpc& bnpc ) { return m_pCondition->isConditionMet( bnpc ); }
private:
StatePtr m_pTargetState;
ConditionPtr m_pCondition;
};
}

135
src/world/AI/GambitPack.cpp Normal file
View file

@ -0,0 +1,135 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Service.h>
#include <Manager/ActionMgr.h>
#include <Action/Action.h>
#include "GambitTargetCondition.h"
#include "GambitRule.h"
#include "GambitPack.h"
using namespace Sapphire;
using namespace Sapphire::World;
AI::GambitTimeLinePack::GambitTimeLinePack( int8_t loopCount ) :
GambitPack( GambitPackType::TimeLine ),
m_loopCount( loopCount ),
m_currentIndex( 0 ),
m_currentLoop( 0 ),
m_startTimeMs( 0 )
{
}
void AI::GambitTimeLinePack::start()
{
m_startTimeMs = Common::Util::getTimeMs();
}
void AI::GambitTimeLinePack::addTimeLine( const GambitRulePtr& pRule, uint32_t offsetInSeconds )
{
auto timeLine = std::make_pair( pRule, offsetInSeconds );
m_gambits.push_back( timeLine );
}
void AI::GambitTimeLinePack::addTimeLine( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t offsetInSeconds )
{
auto pRule = make_GambitRule( targetCondition, action, 0 );
auto timeLine = std::make_pair( pRule, offsetInSeconds );
m_gambits.push_back( timeLine );
}
uint8_t AI::GambitTimeLinePack::getLoopCount() const
{
return m_loopCount;
}
uint8_t AI::GambitTimeLinePack::getCurrentIndex() const
{
return m_currentIndex;
}
void AI::GambitTimeLinePack::update( Entity::BNpc& bnpc, uint64_t tickCount )
{
if( m_startTimeMs == 0 || m_gambits.empty() )
return;
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref();
if( m_gambits.size() <= m_currentIndex )
{
if( m_currentLoop < m_loopCount || m_loopCount == -1 )
{
m_currentIndex = 0;
m_currentLoop++;
m_startTimeMs = Common::Util::getTimeMs();
}
else
{
m_startTimeMs = 0;
m_currentLoop = 0;
return;
}
}
auto currentTimeLine = m_gambits.at( m_currentIndex );
auto& pRule = currentTimeLine.first;
auto offset = currentTimeLine.second * 1000;
if( tickCount - m_startTimeMs >= offset )
{
if( pRule->getGambitTargetCondition()->isConditionMet( bnpc ) )
{
pRule->setLastExecutionMs( tickCount );
actionMgr.handleTargetedAction( bnpc, pRule->getActionPtr()->getId(), pRule->getGambitTargetCondition()->getTarget()->getId(), 0 );
}
m_currentIndex++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AI::GambitRuleSetPack::GambitRuleSetPack() : GambitPack( GambitPackType::RuleSetList )
{
}
void AI::GambitRuleSetPack::addRule( const GambitRulePtr& pRule )
{
m_gambits.push_back( pRule );
}
void AI::GambitRuleSetPack::addRule( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t coolDown )
{
auto pRule = make_GambitRule( targetCondition, action, coolDown );
m_gambits.push_back( pRule );
}
void AI::GambitRuleSetPack::update( Entity::BNpc& bnpc, uint64_t tickCount )
{
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref();
for( auto& gambitRule : m_gambits )
{
if( !gambitRule->isEnabled() )
continue;
if( ( tickCount - gambitRule->getLastExecutionMs() ) > gambitRule->getCoolDown() )
{
if( !gambitRule->getGambitTargetCondition()->isConditionMet( bnpc ) )
continue;
gambitRule->setLastExecutionMs( tickCount );
actionMgr.handleTargetedAction( bnpc, gambitRule->getActionPtr()->getId(), gambitRule->getGambitTargetCondition()->getTarget()->getId(), 0 );
break;
}
}
}
AI::GambitTimeLinePackPtr AI::GambitPack::getAsTimeLine()
{
return std::dynamic_pointer_cast< GambitTimeLinePack, GambitPack >( shared_from_this() );
}
AI::GambitRuleSetPackPtr AI::GambitPack::getAsRuleSet()
{
return std::dynamic_pointer_cast< GambitRuleSetPack, GambitPack >( shared_from_this() );
}

60
src/world/AI/GambitPack.h Normal file
View file

@ -0,0 +1,60 @@
#include <cstdint>
#include <ForwardsZone.h>
#include "GambitTargetCondition.h"
#include "GambitRule.h"
#pragma once
namespace Sapphire::World::AI
{
enum class GambitPackType : uint8_t
{
None,
RuleSetList,
TimeLine
};
class GambitPack : public std::enable_shared_from_this< GambitPack >
{
public:
GambitPack( GambitPackType type ) : m_type( type ) { };
virtual ~GambitPack() = default;
GambitPackType getType() const { return m_type; }
virtual void update( Entity::BNpc& bnpc, uint64_t tickCount ) = 0;
GambitTimeLinePackPtr getAsTimeLine();
GambitRuleSetPackPtr getAsRuleSet();
private:
GambitPackType m_type;
};
class GambitTimeLinePack : public GambitPack
{
public:
GambitTimeLinePack( int8_t loopCount );
void update( Entity::BNpc& bnpc, uint64_t tickCount );
void addTimeLine( const GambitRulePtr& pRule, uint32_t offsetInSeconds );
void addTimeLine( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t offsetInSeconds );
uint8_t getLoopCount() const;
uint8_t getCurrentIndex() const;
void start();
private:
std::vector< std::pair< GambitRulePtr, uint32_t > > m_gambits;
uint64_t m_startTimeMs;
uint8_t m_currentIndex;
int8_t m_loopCount;
uint8_t m_currentLoop;
};
class GambitRuleSetPack : public GambitPack
{
public:
GambitRuleSetPack();
void addRule( const GambitRulePtr& pRule );
void addRule( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t coolDown );
void update( Entity::BNpc& bnpc, uint64_t tickCount );
private:
std::vector< GambitRulePtr > m_gambits;
};
}

View file

@ -18,20 +18,19 @@ namespace Sapphire::World::AI
class GambitTargetCondition class GambitTargetCondition
{ {
public: public:
GambitTargetCondition( GambitTargetType targetType ) : m_targetType( targetType ) {}; GambitTargetCondition() = default;
virtual ~GambitTargetCondition() = default; virtual ~GambitTargetCondition() = default;
virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) { return false; }; virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) { return false; };
Sapphire::Entity::CharaPtr getTarget() const { return m_pTarget; }; Sapphire::Entity::CharaPtr getTarget() const { return m_pTarget; };
protected: protected:
GambitTargetType m_targetType;
Sapphire::Entity::CharaPtr m_pTarget; Sapphire::Entity::CharaPtr m_pTarget;
}; };
class TopHateTargetCondition : public GambitTargetCondition class TopHateTargetCondition : public GambitTargetCondition
{ {
public: public:
TopHateTargetCondition() : GambitTargetCondition( PlayerAndAlly ) {}; TopHateTargetCondition() = default;
bool isConditionMet( Sapphire::Entity::BNpc& src ) override bool isConditionMet( Sapphire::Entity::BNpc& src ) override
{ {
@ -45,12 +44,12 @@ namespace Sapphire::World::AI
}; };
}; };
class HPSelfPctLessThan : public GambitTargetCondition class HPSelfPctLessThanTargetCondition : public GambitTargetCondition
{ {
public: public:
HPSelfPctLessThan( uint8_t pct ) : GambitTargetCondition( Self ), m_HpPct( pct ) {}; HPSelfPctLessThanTargetCondition( uint8_t pct ) : m_HpPct( pct ) {};
virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) bool isConditionMet( Sapphire::Entity::BNpc& src ) override
{ {
if( src.getHpPercent() < m_HpPct ) if( src.getHpPercent() < m_HpPct )
{ {

View file

@ -457,7 +457,7 @@ void Action::Action::execute()
} }
} }
std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage( uint32_t potency ) std::pair< uint32_t, Common::CalcResultType > Action::Action::calcDamage( uint32_t potency )
{ {
// todo: what do for npcs? // todo: what do for npcs?
auto wepDmg = 1.f; auto wepDmg = 1.f;
@ -481,7 +481,7 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage(
return Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg ); return Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg );
} }
std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcHealing( uint32_t potency ) std::pair< uint32_t, Common::CalcResultType > Action::Action::calcHealing( uint32_t potency )
{ {
auto wepDmg = 1.f; auto wepDmg = 1.f;
@ -520,7 +520,7 @@ void Action::Action::buildActionResults()
Network::Util::Packet::sendHudParam( *m_pSource ); Network::Util::Packet::sendHudParam( *m_pSource );
if( !m_enableGenericHandler || !hasLutEntry || m_hitActors.empty() ) if( m_hitActors.empty() )
{ {
// send any effect packet added by script or an empty one just to play animation for other players // send any effect packet added by script or an empty one just to play animation for other players
m_actionResultBuilder->sendActionResults( {} ); m_actionResultBuilder->sendActionResults( {} );
@ -539,56 +539,62 @@ void Action::Action::buildActionResults()
bool shouldRestoreMP = true; bool shouldRestoreMP = true;
bool shouldApplyComboSucceedEffect = true; bool shouldApplyComboSucceedEffect = true;
for( auto& actor : m_hitActors ) if( m_enableGenericHandler && hasLutEntry )
{ {
if( m_lutEntry.potency > 0 ) for( auto& actor : m_hitActors )
{ {
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.comboPotency : m_lutEntry.potency ); if( m_lutEntry.potency > 0 )
m_actionResultBuilder->damage( m_pSource, actor, dmg.first, dmg.second );
if( dmg.first > 0 )
actor->onActionHostile( m_pSource );
if( isCorrectCombo() && shouldApplyComboSucceedEffect )
{ {
m_actionResultBuilder->comboSucceed( m_pSource ); auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.comboPotency : m_lutEntry.potency );
shouldApplyComboSucceedEffect = false; m_actionResultBuilder->damage( m_pSource, actor, dmg.first, dmg.second );
}
if( !isComboAction() || isCorrectCombo() ) if( dmg.first > 0 )
{ actor->onActionHostile( m_pSource );
if( m_lutEntry.curePotency > 0 ) // actions with self heal
if( isCorrectCombo() && shouldApplyComboSucceedEffect )
{ {
auto heal = calcHealing( m_lutEntry.curePotency ); m_actionResultBuilder->comboSucceed( m_pSource );
m_actionResultBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionResultFlag::EffectOnSource ); shouldApplyComboSucceedEffect = false;
} }
if( !isComboAction() || isCorrectCombo() )
{
if( m_lutEntry.curePotency > 0 )// actions with self heal
{
auto heal = calcHealing( m_lutEntry.curePotency );
m_actionResultBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionResultFlag::EffectOnSource );
}
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{
m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource );
shouldRestoreMP = false;
}
if( !m_lutEntry.nextCombo.empty() ) // if we have a combo action followup
m_actionResultBuilder->startCombo( m_pSource, getId() );// this is on all targets hit
}
}
else if( m_lutEntry.curePotency > 0 )
{
auto heal = calcHealing( m_lutEntry.curePotency );
m_actionResultBuilder->heal( actor, actor, heal.first, heal.second );
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP ) if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{ {
m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource ); m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource );
shouldRestoreMP = false; shouldRestoreMP = false;
} }
if( !m_lutEntry.nextCombo.empty() ) // if we have a combo action followup
m_actionResultBuilder->startCombo( m_pSource, getId() ); // this is on all targets hit
} }
} else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
else if( m_lutEntry.curePotency > 0 )
{
auto heal = calcHealing( m_lutEntry.curePotency );
m_actionResultBuilder->heal( actor, actor, heal.first, heal.second );
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{ {
m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource ); m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource );
shouldRestoreMP = false; shouldRestoreMP = false;
} }
} }
else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{ if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 )
m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource ); handleStatusEffects();
shouldRestoreMP = false;
}
} }
// If we hit an enemy // If we hit an enemy
@ -599,9 +605,6 @@ void Action::Action::buildActionResults()
handleJobAction(); handleJobAction();
if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 )
handleStatusEffects();
m_actionResultBuilder->sendActionResults( m_hitActors ); m_actionResultBuilder->sendActionResults( m_hitActors );
// TODO: disabled, reset kills our queued actions // TODO: disabled, reset kills our queued actions

View file

@ -128,9 +128,9 @@ namespace Sapphire::World::Action
*/ */
void addDefaultActorFilters(); void addDefaultActorFilters();
std::pair< uint32_t, Common::ActionHitSeverityType > calcDamage( uint32_t potency ); std::pair< uint32_t, Common::CalcResultType > calcDamage( uint32_t potency );
std::pair< uint32_t, Common::ActionHitSeverityType > calcHealing( uint32_t potency ); std::pair< uint32_t, Common::CalcResultType > calcHealing( uint32_t potency );
std::vector< Entity::CharaPtr >& getHitCharas(); std::vector< Entity::CharaPtr >& getHitCharas();

View file

@ -10,19 +10,19 @@
#include "StatusEffect/StatusEffect.h" #include "StatusEffect/StatusEffect.h"
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::Common;
using namespace Sapphire::World::Action; using namespace Sapphire::World::Action;
ActionResult::ActionResult( Entity::CharaPtr target, uint64_t runAfter ) : ActionResult::ActionResult( Entity::CharaPtr target ) :
m_target( std::move( target ) ), m_target( std::move( target ) )
m_delayMs( runAfter )
{ {
m_result.Arg0 = 0; m_result.Arg0 = 0;
m_result.Arg1 = 0; m_result.Arg1 = 0;
m_result.Arg2 = 0; m_result.Arg2 = 0;
m_result.Value = 0; m_result.Value = 0;
m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::None ); m_result.Flag = static_cast< uint8_t >( ActionResultFlag::None );
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_NONE; m_result.Type = CalcResultType::TypeNone;
} }
Entity::CharaPtr ActionResult::getTarget() const Entity::CharaPtr ActionResult::getTarget() const
@ -30,52 +30,47 @@ Entity::CharaPtr ActionResult::getTarget() const
return m_target; return m_target;
} }
uint64_t ActionResult::getDelay() void ActionResult::damage( uint32_t amount, CalcResultType hitType, uint8_t hitEffect, ActionResultFlag flag )
{ {
return m_delayMs; m_result.Arg0 = hitEffect;
}
void ActionResult::damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag )
{
m_result.Arg0 = static_cast< uint8_t >( severity );
m_result.Value = static_cast< int16_t >( amount ); m_result.Value = static_cast< int16_t >( amount );
m_result.Flag = static_cast< uint8_t >( flag ); m_result.Flag = static_cast< uint8_t >( flag );
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP; m_result.Type = hitType;
} }
void ActionResult::heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) void ActionResult::heal( uint32_t amount, CalcResultType hitType, uint8_t hitEffect, ActionResultFlag flag )
{ {
m_result.Arg1 = static_cast< uint8_t >( severity ); m_result.Arg0 = hitEffect;
m_result.Value = static_cast< int16_t >( amount ); m_result.Value = static_cast< int16_t >( amount );
m_result.Flag = static_cast< uint8_t >( flag ); m_result.Flag = static_cast< uint8_t >( flag );
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP; m_result.Type = hitType;
} }
void ActionResult::restoreMP( uint32_t amount, Common::ActionResultFlag flag ) void ActionResult::restoreMP( uint32_t amount, ActionResultFlag flag )
{ {
m_result.Value = static_cast< int16_t >( amount ); m_result.Value = static_cast< int16_t >( amount );
m_result.Flag = static_cast< uint8_t >( flag ); m_result.Flag = static_cast< uint8_t >( flag );
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_MP; m_result.Type = CalcResultType::TypeRecoverMp;
} }
void ActionResult::startCombo( uint16_t actionId ) void ActionResult::startCombo( uint16_t actionId )
{ {
m_result.Value = static_cast< int16_t >( actionId ); m_result.Value = static_cast< int16_t >( actionId );
m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); m_result.Flag = static_cast< uint8_t >( ActionResultFlag::EffectOnSource );
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO; m_result.Type = CalcResultType::TypeCombo;
} }
void ActionResult::comboSucceed() void ActionResult::comboSucceed()
{ {
// no EffectOnSource flag on this // no EffectOnSource flag on this
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO_HIT; m_result.Type = CalcResultType::TypeComboHit;
} }
void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride ) void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride )
{ {
m_result.Value = static_cast< int16_t >( id ); m_result.Value = static_cast< int16_t >( id );
m_result.Arg2 = param; m_result.Arg2 = param;
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS; m_result.Type = CalcResultType::TypeSetStatus;
m_bOverrideStatus = shouldOverride; m_bOverrideStatus = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, 3000 );
@ -87,7 +82,7 @@ void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Cha
{ {
m_result.Value = static_cast< int16_t >( id ); m_result.Value = static_cast< int16_t >( id );
m_result.Arg2 = param; m_result.Arg2 = param;
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS; m_result.Type = CalcResultType::TypeSetStatus;
m_bOverrideStatus = shouldOverride; m_bOverrideStatus = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, modifiers, flag, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, modifiers, flag, 3000 );
@ -98,8 +93,8 @@ void ActionResult::applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t
{ {
m_result.Value = static_cast< int16_t >( id ); m_result.Value = static_cast< int16_t >( id );
m_result.Arg2 = param; m_result.Arg2 = param;
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME; m_result.Type = CalcResultType::TypeSetStatusMe;
m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); m_result.Flag = static_cast< uint8_t >( ActionResultFlag::EffectOnSource );
m_bOverrideStatus = shouldOverride; m_bOverrideStatus = shouldOverride;
m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, 3000 ); m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, 3000 );
@ -111,7 +106,7 @@ void ActionResult::applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t
{ {
m_result.Value = static_cast< int16_t >( id ); m_result.Value = static_cast< int16_t >( id );
m_result.Arg2 = param; m_result.Arg2 = param;
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME; m_result.Type = CalcResultType::TypeSetStatusMe;
m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource );
m_bOverrideStatus = shouldOverride; m_bOverrideStatus = shouldOverride;
@ -123,7 +118,7 @@ void ActionResult::mount( uint16_t mountId )
{ {
m_result.Value = static_cast< int16_t >( mountId ); m_result.Value = static_cast< int16_t >( mountId );
m_result.Arg0 = 1; m_result.Arg0 = 1;
m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT; m_result.Type = CalcResultType::TypeMount;
} }
const Common::CalcResultParam& ActionResult::getCalcResultParam() const const Common::CalcResultParam& ActionResult::getCalcResultParam() const
@ -143,26 +138,28 @@ void ActionResult::execute()
switch( m_result.Type ) switch( m_result.Type )
{ {
case Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP: case CalcResultType::TypeDamageHp:
case CalcResultType::TypeCriticalDamageHp:
{ {
m_target->takeDamage( m_result.Value ); m_target->takeDamage( m_result.Value );
break; break;
} }
case Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP: case CalcResultType::TypeRecoverHp:
case CalcResultType::TypeCriticalRecoverHp:
{ {
m_target->heal( m_result.Value ); m_target->heal( m_result.Value );
break; break;
} }
case Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_MP: case CalcResultType::TypeRecoverMp:
{ {
m_target->restoreMP( m_result.Value ); m_target->restoreMP( m_result.Value );
break; break;
} }
case Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS: case CalcResultType::TypeSetStatus:
case Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME: case CalcResultType::TypeSetStatusMe:
{ {
if( !m_bOverrideStatus ) if( !m_bOverrideStatus )
m_target->addStatusEffectByIdIfNotExist( m_pStatus ); m_target->addStatusEffectByIdIfNotExist( m_pStatus );
@ -171,7 +168,7 @@ void ActionResult::execute()
break; break;
} }
case Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT: case CalcResultType::TypeMount:
{ {
auto pPlayer = m_target->getAsPlayer(); auto pPlayer = m_target->getAsPlayer();
pPlayer->setMount( m_result.Value ); pPlayer->setMount( m_result.Value );

View file

@ -13,10 +13,10 @@ namespace Sapphire::World::Action
class ActionResult class ActionResult
{ {
public: public:
explicit ActionResult( Entity::CharaPtr target, uint64_t delayMs ); explicit ActionResult( Entity::CharaPtr target );
void damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); void damage( uint32_t amount, Common::CalcResultType hitType, uint8_t hitEffect, Common::ActionResultFlag flag = Common::ActionResultFlag::None );
void heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); void heal( uint32_t amount, Common::CalcResultType hitType, uint8_t hitEffect, Common::ActionResultFlag flag = Common::ActionResultFlag::None );
void restoreMP( uint32_t amount, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); void restoreMP( uint32_t amount, Common::ActionResultFlag flag = Common::ActionResultFlag::None );
void startCombo( uint16_t actionId ); void startCombo( uint16_t actionId );
void comboSucceed(); void comboSucceed();
@ -30,16 +30,12 @@ namespace Sapphire::World::Action
Entity::CharaPtr getTarget() const; Entity::CharaPtr getTarget() const;
uint64_t getDelay();
const Common::CalcResultParam& getCalcResultParam() const; const Common::CalcResultParam& getCalcResultParam() const;
const Sapphire::StatusEffect::StatusEffectPtr getStatusEffect() const; const Sapphire::StatusEffect::StatusEffectPtr getStatusEffect() const;
void execute(); void execute();
private: private:
uint64_t m_delayMs;
Entity::CharaPtr m_target; Entity::CharaPtr m_target;
Common::CalcResultParam m_result; Common::CalcResultParam m_result;

View file

@ -10,6 +10,7 @@
#include <Util/Util.h> #include <Util/Util.h>
#include <Util/UtilMath.h> #include <Util/UtilMath.h>
#include <Exd/ExdData.h>
#include <Logging/Logger.h> #include <Logging/Logger.h>
#include <Manager/TerritoryMgr.h> #include <Manager/TerritoryMgr.h>
@ -46,44 +47,48 @@ void ActionResultBuilder::addResultToActor( Entity::CharaPtr& chara, ActionResul
it->second.push_back( std::move( result ) ); it->second.push_back( std::move( result ) );
} }
void ActionResultBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) void ActionResultBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::CalcResultType hitType, Common::ActionResultFlag flag )
{ {
ActionResultPtr nextResult = make_ActionResult( healingTarget, 0 ); ActionResultPtr nextResult = make_ActionResult( healingTarget );
nextResult->heal( amount, severity, flag ); auto& exdData = Common::Service< Data::ExdData >::ref();
auto actionData = exdData.getRow< Excel::Action >( m_actionId );
nextResult->heal( amount, hitType, std::abs( actionData->data().AttackType ), flag );
addResultToActor( effectTarget, nextResult ); addResultToActor( effectTarget, nextResult );
} }
void ActionResultBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionResultFlag flag ) void ActionResultBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionResultFlag flag )
{ {
ActionResultPtr nextResult = make_ActionResult( restoringTarget, 0 ); // restore mp source actor ActionResultPtr nextResult = make_ActionResult( restoringTarget ); // restore mp source actor
nextResult->restoreMP( amount, flag ); nextResult->restoreMP( amount, flag );
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
void ActionResultBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) void ActionResultBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::CalcResultType hitType, Common::ActionResultFlag flag )
{ {
ActionResultPtr nextResult = make_ActionResult( damagingTarget, 0 ); ActionResultPtr nextResult = make_ActionResult( damagingTarget );
nextResult->damage( amount, severity, flag ); auto& exdData = Common::Service< Data::ExdData >::ref();
auto actionData = exdData.getRow< Excel::Action >( m_actionId );
nextResult->damage( amount, hitType, std::abs( actionData->data().AttackType ), flag );
addResultToActor( damagingTarget, nextResult ); addResultToActor( damagingTarget, nextResult );
} }
void ActionResultBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId ) void ActionResultBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId )
{ {
ActionResultPtr nextResult = make_ActionResult( target, 0 ); ActionResultPtr nextResult = make_ActionResult( target );
nextResult->startCombo( actionId ); nextResult->startCombo( actionId );
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
void ActionResultBuilder::comboSucceed( Entity::CharaPtr& target ) void ActionResultBuilder::comboSucceed( Entity::CharaPtr& target )
{ {
ActionResultPtr nextResult = make_ActionResult( target, 0 ); ActionResultPtr nextResult = make_ActionResult( target );
nextResult->comboSucceed(); nextResult->comboSucceed();
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride ) void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride )
{ {
ActionResultPtr nextResult = make_ActionResult( target, 0 ); ActionResultPtr nextResult = make_ActionResult( target );
nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, shouldOverride ); nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, shouldOverride );
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
@ -91,14 +96,14 @@ void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t
void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param,
std::vector< World::Action::StatusModifier > modifiers, uint32_t flag, bool shouldOverride ) std::vector< World::Action::StatusModifier > modifiers, uint32_t flag, bool shouldOverride )
{ {
ActionResultPtr nextResult = make_ActionResult( target, 0 ); ActionResultPtr nextResult = make_ActionResult( target );
nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, modifiers, flag, shouldOverride ); nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, modifiers, flag, shouldOverride );
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride ) void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride )
{ {
ActionResultPtr nextResult = make_ActionResult( m_sourceChara, 0 ); ActionResultPtr nextResult = make_ActionResult( m_sourceChara );
nextResult->applyStatusEffectSelf( statusId, duration, param, shouldOverride ); nextResult->applyStatusEffectSelf( statusId, duration, param, shouldOverride );
addResultToActor( m_sourceChara, nextResult ); addResultToActor( m_sourceChara, nextResult );
} }
@ -106,14 +111,14 @@ void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t dur
void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, std::vector< World::Action::StatusModifier > modifiers, void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, std::vector< World::Action::StatusModifier > modifiers,
uint32_t flag, bool shouldOverride ) uint32_t flag, bool shouldOverride )
{ {
ActionResultPtr nextResult = make_ActionResult( m_sourceChara, 0 ); ActionResultPtr nextResult = make_ActionResult( m_sourceChara );
nextResult->applyStatusEffectSelf( statusId, duration, param, modifiers, flag, shouldOverride ); nextResult->applyStatusEffectSelf( statusId, duration, param, modifiers, flag, shouldOverride );
addResultToActor( m_sourceChara, nextResult ); addResultToActor( m_sourceChara, nextResult );
} }
void ActionResultBuilder::mount( Entity::CharaPtr& target, uint16_t mountId ) void ActionResultBuilder::mount( Entity::CharaPtr& target, uint16_t mountId )
{ {
ActionResultPtr nextResult = make_ActionResult( target, 0 ); ActionResultPtr nextResult = make_ActionResult( target );
nextResult->mount( mountId ); nextResult->mount( mountId );
addResultToActor( target, nextResult ); addResultToActor( target, nextResult );
} }
@ -140,19 +145,17 @@ std::shared_ptr< FFXIVPacketBase > ActionResultBuilder::createActionResultPacket
if( targetCount > 1 ) // use AoeEffect packets if( targetCount > 1 ) // use AoeEffect packets
{ {
auto actionResult = std::make_shared< EffectPacket >( m_sourceChara->getId(), targetList[ 0 ]->getId(), m_actionId ); auto actionResult = makeEffectPacket( m_sourceChara->getId(), targetList[ 0 ]->getId(), m_actionId );
actionResult->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) ); actionResult->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) );
actionResult->setRequestId( m_requestId ); actionResult->setRequestId( m_requestId );
actionResult->setResultId( m_resultId ); actionResult->setResultId( m_resultId );
uint8_t targetIndex = 0; uint8_t targetIndex = 0;
for( auto it = m_actorResultsMap.begin(); it != m_actorResultsMap.end(); ++it ) for( auto& [ actor, actorResultList ] : m_actorResultsMap )
{ {
// get all effect results for an actor
auto actorResultList = it->second;
if( it->first ) if( actor )
taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, it->first, actorResultList, 300 ) ); taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, actor, actorResultList, 300 ) );
for( auto& result : actorResultList ) for( auto& result : actorResultList )
{ {
@ -173,23 +176,21 @@ std::shared_ptr< FFXIVPacketBase > ActionResultBuilder::createActionResultPacket
else // use Effect for single target else // use Effect for single target
{ {
uint32_t mainTargetId = targetList.empty() ? m_sourceChara->getId() : targetList[ 0 ]->getId(); uint32_t mainTargetId = targetList.empty() ? m_sourceChara->getId() : targetList[ 0 ]->getId();
auto actionResult = std::make_shared< EffectPacket1 >( m_sourceChara->getId(), mainTargetId, m_actionId ); auto actionResult = makeEffectPacket1( m_sourceChara->getId(), mainTargetId, m_actionId );
actionResult->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) ); actionResult->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) );
actionResult->setRequestId( m_requestId ); actionResult->setRequestId( m_requestId );
actionResult->setResultId( m_resultId ); actionResult->setResultId( m_resultId );
for( auto it = m_actorResultsMap.begin(); it != m_actorResultsMap.end(); ++it ) for( auto& [ actor, actorResultList ] : m_actorResultsMap )
{ {
// get all effect results for an actor if( actor )
auto actorResultList = it->second; taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, actor, actorResultList, 300 ) );
if( it->first )
taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, it->first, actorResultList, 300 ) );
for( auto& result : actorResultList ) for( auto& result : actorResultList )
{ {
auto effect = result->getCalcResultParam(); auto effect = result->getCalcResultParam();
if( result->getTarget() == m_sourceChara && result->getCalcResultParam().Type != Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME ) if( result->getTarget() == m_sourceChara &&
result->getCalcResultParam().Type != Common::CalcResultType::TypeSetStatusMe )
actionResult->addSourceEffect( effect ); actionResult->addSourceEffect( effect );
else else
actionResult->addTargetEffect( effect ); actionResult->addTargetEffect( effect );

View file

@ -12,14 +12,14 @@ namespace Sapphire::World::Action
ActionResultBuilder( Entity::CharaPtr source, uint32_t actionId, uint32_t resultId, uint16_t requestId ); ActionResultBuilder( Entity::CharaPtr source, uint32_t actionId, uint32_t resultId, uint16_t requestId );
void heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, void heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount,
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal, Common::CalcResultType hitType = Common::CalcResultType::TypeRecoverMp,
Common::ActionResultFlag flag = Common::ActionResultFlag::None ); Common::ActionResultFlag flag = Common::ActionResultFlag::None );
void restoreMP( Entity::CharaPtr& effectTarget, Entity::CharaPtr& restoringTarget, uint32_t amount, void restoreMP( Entity::CharaPtr& effectTarget, Entity::CharaPtr& restoringTarget, uint32_t amount,
Common::ActionResultFlag flag = Common::ActionResultFlag::None ); Common::ActionResultFlag flag = Common::ActionResultFlag::None );
void damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, void damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount,
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage, Common::CalcResultType hitType = Common::CalcResultType::TypeDamageHp,
Common::ActionResultFlag flag = Common::ActionResultFlag::None ); Common::ActionResultFlag flag = Common::ActionResultFlag::None );
void startCombo( Entity::CharaPtr& target, uint16_t actionId ); void startCombo( Entity::CharaPtr& target, uint16_t actionId );

View file

@ -78,7 +78,7 @@ void ItemAction::interrupt()
void ItemAction::handleVFXItem() void ItemAction::handleVFXItem()
{ {
Common::CalcResultParam effect{}; Common::CalcResultParam effect{};
effect.Type = Common::ActionEffectType::CALC_RESULT_TYPE_CHECK_BARRIER; effect.Type = Common::CalcResultType::TypeCheckBarrier;
effect.Value = m_itemAction->data().Calcu0Arg[ 0 ]; effect.Value = m_itemAction->data().Calcu0Arg[ 0 ];
auto effectPacket = std::make_shared< EffectPacket >( getSourceChara()->getId(), getSourceChara()->getId(), getId() ); auto effectPacket = std::make_shared< EffectPacket >( getSourceChara()->getId(), getSourceChara()->getId(), getId() );

View file

@ -46,7 +46,15 @@
#include <Action/Action.h> #include <Action/Action.h>
#include <AI/GambitRule.h> #include <AI/GambitRule.h>
#include <AI/GambitPack.h>
#include <AI/GambitTargetCondition.h> #include <AI/GambitTargetCondition.h>
#include <AI/Fsm/StateMachine.h>
#include <AI/Fsm/Condition.h>
#include <AI/Fsm/StateIdle.h>
#include <AI/Fsm/StateRoam.h>
#include <AI/Fsm/StateCombat.h>
#include <AI/Fsm/StateRetreat.h>
#include <AI/Fsm/StateDead.h>
using namespace Sapphire; using namespace Sapphire;
using namespace Sapphire::World; using namespace Sapphire::World;
@ -88,8 +96,7 @@ BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstanceObject > pInfo, co
m_flags = 0; m_flags = 0;
m_rank = pInfo->BNPCRankId; m_rank = pInfo->BNPCRankId;
if( pInfo->WanderingRange == 0 || pInfo->BoundInstanceID != 0 )
setFlag( Immobile );
// Striking Dummy // Striking Dummy
if( pInfo->NameId == 541 ) if( pInfo->NameId == 541 )
@ -107,6 +114,9 @@ BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstanceObject > pInfo, co
m_modelChara = bNpcBaseData->data().Model; m_modelChara = bNpcBaseData->data().Model;
m_enemyType = bNpcBaseData->data().Battalion; m_enemyType = bNpcBaseData->data().Battalion;
if( pInfo->WanderingRange == 0 || pInfo->BoundInstanceID != 0 || m_enemyType == 0 )
setFlag( Immobile );
m_class = ClassJob::Gladiator; m_class = ClassJob::Gladiator;
m_territoryTypeId = zone.getTerritoryTypeId(); m_territoryTypeId = zone.getTerritoryTypeId();
@ -324,7 +334,7 @@ uint32_t BNpc::getBNpcNameId() const
void BNpc::spawn( PlayerPtr pTarget ) void BNpc::spawn( PlayerPtr pTarget )
{ {
m_lastRoamTargetReached = Common::Util::getTimeSeconds(); m_lastRoamTargetReachedTime = Common::Util::getTimeSeconds();
auto& server = Common::Service< World::WorldServer >::ref(); auto& server = Common::Service< World::WorldServer >::ref();
server.queueForPlayer( pTarget->getCharacterId(), std::make_shared< NpcSpawnPacket >( *this, *pTarget ) ); server.queueForPlayer( pTarget->getCharacterId(), std::make_shared< NpcSpawnPacket >( *this, *pTarget ) );
@ -589,6 +599,11 @@ void BNpc::aggro( const Sapphire::Entity::CharaPtr& pChara )
auto& pRNGMgr = Common::Service< World::Manager::RNGMgr >::ref(); auto& pRNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
auto variation = static_cast< uint32_t >( pRNGMgr.getRandGenerator< float >( 500, 1000 ).next() ); auto variation = static_cast< uint32_t >( pRNGMgr.getRandGenerator< float >( 500, 1000 ).next() );
if( m_pGambitPack && m_pGambitPack->getAsTimeLine() )
{
m_pGambitPack->getAsTimeLine()->start();
}
m_lastAttack = Common::Util::getTimeMs() + variation; m_lastAttack = Common::Util::getTimeMs() + variation;
setStance( Stance::Active ); setStance( Stance::Active );
@ -638,165 +653,8 @@ void BNpc::onTick()
void BNpc::update( uint64_t tickCount ) void BNpc::update( uint64_t tickCount )
{ {
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto pZone = teriMgr.getTerritoryByGuId( getTerritoryId() );
const uint8_t maxDistanceToOrigin = 40;
const uint32_t roamTick = 20;
auto pNaviProvider = pZone->getNaviProvider();
if( !pNaviProvider )
return;
if( !checkAction() )
processGambits( tickCount );
switch( m_state )
{
case BNpcState::Dead:
case BNpcState::JustDied:
return;
case BNpcState::Retreat:
{
setInvincibilityType( InvincibilityType::InvincibilityIgnoreDamage );
if( pNaviProvider )
pNaviProvider->setMoveTarget( *this, m_spawnPos );
if( moveTo( m_spawnPos ) )
{
setInvincibilityType( InvincibilityType::InvincibilityNone );
// retail doesn't seem to roam straight after retreating
// todo: perhaps requires more investigation?
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
// resetHp
setHp( getMaxHp() );
m_state = BNpcState::Idle;
setOwner( nullptr );
}
}
break;
case BNpcState::Roaming:
{
if( pNaviProvider )
pNaviProvider->setMoveTarget( *this, m_roamPos );
if( moveTo( m_roamPos ) )
{
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
m_state = BNpcState::Idle;
}
checkAggro();
}
break;
case BNpcState::Idle:
{
auto pHatedActor = hateListGetHighest();
if( pHatedActor )
aggro( pHatedActor );
if( pNaviProvider->syncPosToChara( *this ) )
sendPositionUpdate();
if( !hasFlag( Immobile ) && ( Common::Util::getTimeSeconds() - m_lastRoamTargetReached > roamTick ) )
{
if( !pNaviProvider )
{
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
break;
}
if( m_pInfo->WanderingRange != 0 && getEnemyType() != 0 )
{
m_roamPos = pNaviProvider->findRandomPositionInCircle( m_spawnPos, m_pInfo->WanderingRange );
}
else
{
m_roamPos = m_spawnPos;
}
m_state = BNpcState::Roaming;
}
checkAggro();
break;
}
case BNpcState::Combat:
{
auto pHatedActor = hateListGetHighest();
if( !pHatedActor )
return;
pNaviProvider->updateAgentParameters( *this );
auto distanceOrig = Common::Util::distance( getPos(), m_spawnPos );
if( pHatedActor && !pHatedActor->isAlive() )
{
hateListRemove( pHatedActor );
pHatedActor = hateListGetHighest();
}
if( pHatedActor )
{
auto distance = Common::Util::distance( getPos(), pHatedActor->getPos() );
if( !hasFlag( NoDeaggro ) && ( ( distanceOrig > maxDistanceToOrigin ) || distance > 30.0f ) )
{
hateListClear();
changeTarget( INVALID_GAME_OBJECT_ID64 );
setStance( Stance::Passive );
setOwner( nullptr );
m_state = BNpcState::Retreat;
break;
}
if( distance > ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) )
{
if( hasFlag( Immobile ) )
break;
if( pNaviProvider )
pNaviProvider->setMoveTarget( *this, pHatedActor->getPos() );
moveTo( *pHatedActor );
}
if( pNaviProvider->syncPosToChara( *this ) )
sendPositionUpdate();
if( distance < ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) )
{
if( !hasFlag( TurningDisabled ) && face( pHatedActor->getPos() ) )
sendPositionUpdate();
// in combat range. ATTACK!
autoAttack( pHatedActor );
}
}
else
{
changeTarget( INVALID_GAME_OBJECT_ID64 );
setStance( Stance::Passive );
//setOwner( nullptr );
m_state = BNpcState::Retreat;
pNaviProvider->updateAgentParameters( *this );
}
}
break;
}
Chara::update( tickCount ); Chara::update( tickCount );
m_fsm->update( *this, tickCount );
} }
void BNpc::restHp() void BNpc::restHp()
@ -1054,31 +912,98 @@ void BNpc::init()
m_maxHp = Math::CalcStats::calculateMaxHp( *getAsChara() ); m_maxHp = Math::CalcStats::calculateMaxHp( *getAsChara() );
m_hp = m_maxHp; m_hp = m_maxHp;
m_lastRoamTargetReachedTime = Common::Util::getTimeSeconds();
//setup a test gambit //setup a test gambit
auto testGambitRule = AI::make_GambitRule( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 5000 ); auto testGambitRule = AI::make_GambitRule( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 5000 );
auto testGambitRule1 = AI::make_GambitRule( AI::make_HPSelfPctLessThan( 50 ), Action::make_Action( getAsChara(), 120, 0 ), 5000 ); auto testGambitRule1 = AI::make_GambitRule( AI::make_HPSelfPctLessThanTargetCondition( 50 ), Action::make_Action( getAsChara(), 120, 0 ), 5000 );
/*
auto gambitPack = AI::make_GambitRuleSetPack();
gambitPack->addRule( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 5000 );
gambitPack->addRule( AI::make_HPSelfPctLessThanTargetCondition( 50 ), Action::make_Action( getAsChara(), 120, 0 ), 10000 );
m_pGambitPack = gambitPack;
*/
m_gambits.push_back( testGambitRule ); auto gambitPack = AI::make_GambitTimeLinePack( -1 );
m_gambits.push_back( testGambitRule1 ); gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 2 );
gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 89, 0 ), 4 );
gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 90, 0 ), 6 );
gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 91, 0 ), 8 );
gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 92, 0 ), 10 );
gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 81, 0 ), 12 );
gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 82, 0 ), 14 );
m_pGambitPack = gambitPack;
using namespace AI::Fsm;
m_fsm = make_StateMachine();
auto stateIdle = make_StateIdle();
auto stateCombat = make_StateCombat();
auto stateDead = make_StateDead();
if( !hasFlag( Immobile ) )
{
auto stateRoam = make_StateRoam();
stateIdle->addTransition( stateRoam, make_RoamNextTimeReachedCondition() );
stateRoam->addTransition( stateIdle, make_RoamTargetReachedCondition() );
stateRoam->addTransition( stateCombat, make_HateListHasEntriesCondition() );
stateRoam->addTransition( stateDead, make_IsDeadCondition() );
m_fsm->addState( stateRoam );
}
stateIdle->addTransition( stateCombat, make_HateListHasEntriesCondition() );
stateCombat->addTransition( stateIdle, make_HateListEmptyCondition() );
stateIdle->addTransition( stateDead, make_IsDeadCondition() );
stateCombat->addTransition( stateDead, make_IsDeadCondition() );
m_fsm->addState( stateIdle );
if( !hasFlag( NoDeaggro ) )
{
auto stateRetreat = make_StateRetreat();
stateCombat->addTransition( stateRetreat, make_SpawnPointDistanceGtMaxDistanceCondition() );
stateRetreat->addTransition( stateIdle, make_RoamTargetReachedCondition() );
}
m_fsm->setCurrentState( stateIdle );
} }
void BNpc::processGambits( uint64_t tickCount ) void BNpc::processGambits( uint64_t tickCount )
{ {
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref(); m_tp = 1000;
for( auto& gambitRule : m_gambits ) m_pGambitPack->update( *this, tickCount );
{
if( !gambitRule->isEnabled() )
continue;
if( ( tickCount - gambitRule->getLastExecutionMs() ) > gambitRule->getCoolDown() )
{
if( !gambitRule->getGambitTargetCondition()->isConditionMet( *this ) )
continue;
gambitRule->setLastExecutionMs( tickCount );
actionMgr.handleTargetedAction( *this, gambitRule->getActionPtr()->getId(), gambitRule->getGambitTargetCondition()->getTarget()->getId(), 0 );
break;
}
}
} }
uint32_t BNpc::getLastRoamTargetReachedTime() const
{
return m_lastRoamTargetReachedTime;
}
void BNpc::setLastRoamTargetReachedTime( uint32_t time )
{
m_lastRoamTargetReachedTime = time;
}
std::shared_ptr< Common::BNPCInstanceObject > BNpc::getInstanceObjectInfo() const
{
return m_pInfo;
}
void BNpc::setRoamTargetReached( bool reached )
{
m_roamTargetReached = reached;
}
bool BNpc::isRoamTargetReached() const
{
return m_roamTargetReached;
}
void BNpc::setRoamTargetPos( const FFXIVARR_POSITION3& targetPos )
{
m_roamPos = targetPos;
}
const Common::FFXIVARR_POSITION3& BNpc::getRoamTargetPos() const
{
return m_roamPos;
}
const Common::FFXIVARR_POSITION3& BNpc::getSpawnPos() const
{
return m_spawnPos;
}

View file

@ -151,6 +151,19 @@ namespace Sapphire::Entity
void processGambits( uint64_t tickCount ); void processGambits( uint64_t tickCount );
uint32_t getLastRoamTargetReachedTime() const;
void setLastRoamTargetReachedTime( uint32_t time );
std::shared_ptr< Common::BNPCInstanceObject > getInstanceObjectInfo() const;
void setRoamTargetReached( bool reached );
bool isRoamTargetReached() const;
void setRoamTargetPos( const Common::FFXIVARR_POSITION3& targetPos );
const Common::FFXIVARR_POSITION3& getRoamTargetPos() const;
const Common::FFXIVARR_POSITION3& getSpawnPos() const;
private: private:
uint32_t m_bNpcBaseId; uint32_t m_bNpcBaseId;
uint32_t m_bNpcNameId; uint32_t m_bNpcNameId;
@ -178,7 +191,8 @@ namespace Sapphire::Entity
std::shared_ptr< Common::BNPCInstanceObject > m_pInfo; std::shared_ptr< Common::BNPCInstanceObject > m_pInfo;
uint32_t m_timeOfDeath; uint32_t m_timeOfDeath;
uint32_t m_lastRoamTargetReached; uint32_t m_lastRoamTargetReachedTime;
bool m_roamTargetReached{ false };
Common::FFXIVARR_POSITION3 m_spawnPos; Common::FFXIVARR_POSITION3 m_spawnPos;
Common::FFXIVARR_POSITION3 m_roamPos; Common::FFXIVARR_POSITION3 m_roamPos;
@ -192,7 +206,9 @@ namespace Sapphire::Entity
Common::FFXIVARR_POSITION3 m_naviTarget; Common::FFXIVARR_POSITION3 m_naviTarget;
CharaPtr m_pOwner; CharaPtr m_pOwner;
std::vector< World::AI::GambitRulePtr > m_gambits; World::AI::GambitPackPtr m_pGambitPack;
std::shared_ptr< World::AI::Fsm::StateMachine > m_fsm;
}; };

View file

@ -475,8 +475,8 @@ void Chara::autoAttack( CharaPtr pTarget )
Common::CalcResultParam effectEntry{}; Common::CalcResultParam effectEntry{};
effectEntry.Value = static_cast< int16_t >( damage ); effectEntry.Value = static_cast< int16_t >( damage );
effectEntry.Type = ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP; effectEntry.Type = CalcResultType::TypeDamageHp;
effectEntry.Arg0 = static_cast< uint8_t >( ActionHitSeverityType::NormalDamage ); effectEntry.Arg0 = 1;
effectEntry.Arg2 = 0x71; effectEntry.Arg2 = 0x71;
effectPacket->addTargetEffect( effectEntry ); effectPacket->addTargetEffect( effectEntry );
@ -825,7 +825,7 @@ void Chara::onTick()
{ {
takeDamage( thisTickDmg ); takeDamage( thisTickDmg );
Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), HPFloatingText, 0, Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), HPFloatingText, 0,
ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP, thisTickDmg ); CalcResultType::TypeDamageHp, thisTickDmg );
Network::Util::Packet::sendHudParam( *this ); Network::Util::Packet::sendHudParam( *this );
} }
@ -834,7 +834,7 @@ void Chara::onTick()
{ {
heal( thisTickHeal ); heal( thisTickHeal );
Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), HPFloatingText, 0, Network::Util::Packet::sendActorControl( getInRangePlayerIds( isPlayer() ), getId(), HPFloatingText, 0,
ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP, thisTickHeal ); CalcResultType::TypeRecoverMp, thisTickHeal );
Network::Util::Packet::sendHudParam( *this ); Network::Util::Packet::sendHudParam( *this );
} }

View file

@ -658,11 +658,6 @@ void Player::levelUp()
m_mp = getMaxMp(); m_mp = getMaxMp();
setLevel( getLevel() + 1 ); setLevel( getLevel() + 1 );
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), LevelUpEffect, static_cast< uint8_t >( getClass() ), getLevel(), getLevel() - 1 );
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( *this, static_cast< uint32_t >( getClass() ) );
Service< World::Manager::MapMgr >::ref().updateQuests( *this );
} }
uint8_t Player::getLevel() const uint8_t Player::getLevel() const
@ -750,6 +745,11 @@ void Player::setLevel( uint8_t level )
Network::Util::Packet::sendBaseParams( *this ); Network::Util::Packet::sendBaseParams( *this );
Network::Util::Packet::sendHudParam( *this ); Network::Util::Packet::sendHudParam( *this );
Network::Util::Packet::sendStatusUpdate( *this ); Network::Util::Packet::sendStatusUpdate( *this );
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), LevelUpEffect, static_cast< uint8_t >( getClass() ), getLevel(), getLevel() - 1 );
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( *this, static_cast< uint32_t >( getClass() ) );
Service< World::Manager::MapMgr >::ref().updateQuests( *this );
} }
void Player::setLevelForClass( uint8_t level, Common::ClassJob classjob ) void Player::setLevelForClass( uint8_t level, Common::ClassJob classjob )
@ -1151,6 +1151,7 @@ const std::map< uint32_t, uint8_t >& Player::getActorIdToHateSlotMap()
void Player::onMobAggro( const BNpc& bnpc ) void Player::onMobAggro( const BNpc& bnpc )
{ {
hateListAdd( bnpc ); hateListAdd( bnpc );
setCondition( PlayerCondition::InCombat );
Network::Util::Packet::sendActorControl( *this, getId(), SetBattle, 1 ); Network::Util::Packet::sendActorControl( *this, getId(), SetBattle, 1 );
} }
@ -1158,7 +1159,10 @@ void Player::onMobDeaggro( const BNpc& bnpc )
{ {
hateListRemove( bnpc ); hateListRemove( bnpc );
if( m_actorIdTohateSlotMap.empty() ) if( m_actorIdTohateSlotMap.empty() )
{
removeCondition( PlayerCondition::InCombat );
Network::Util::Packet::sendActorControl( *this, getId(), SetBattle, 0 ); Network::Util::Packet::sendActorControl( *this, getId(), SetBattle, 0 );
}
} }
bool Player::isLogin() const bool Player::isLogin() const

View file

@ -9,6 +9,7 @@ file( GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
Action/*.cpp Action/*.cpp
Action/Job/*.cpp Action/Job/*.cpp
AI/*.cpp AI/*.cpp
AI/Fsm/*.cpp
ContentFinder/*.cpp ContentFinder/*.cpp
DebugCommand/*.cpp DebugCommand/*.cpp
Event/*.cpp Event/*.cpp

View file

@ -131,12 +131,11 @@ void World::ContentFinder::registerContentRequest( Entity::Player &player, uint3
void World::ContentFinder::registerRandomContentRequest( Entity::Player &player, uint32_t randomContentTypeId ) void World::ContentFinder::registerRandomContentRequest( Entity::Player &player, uint32_t randomContentTypeId )
{ {
auto& exdData = Service< Data::ExdData >::ref(); auto& exdData = Service< Data::ExdData >::ref();
auto contentListIds = exdData.getIdList< Excel::ContentFinderCondition >(); auto contentFinderList = exdData.getRows< Excel::ContentFinderCondition >();
std::vector< uint32_t > idList; std::vector< uint32_t > idList;
for( auto id : contentListIds ) for( const auto& [ id, instanceContent ] : contentFinderList )
{ {
auto instanceContent = exdData.getRow< Excel::ContentFinderCondition >( id );
if( instanceContent->data().RandomContentType == randomContentTypeId ) if( instanceContent->data().RandomContentType == randomContentTypeId )
{ {
if( instanceContent->data().LevelMin <= player.getLevel() ) if( instanceContent->data().LevelMin <= player.getLevel() )

View file

@ -51,9 +51,37 @@ namespace World::AI
{ {
TYPE_FORWARD( GambitTargetCondition ); TYPE_FORWARD( GambitTargetCondition );
TYPE_FORWARD( TopHateTargetCondition ); TYPE_FORWARD( TopHateTargetCondition );
TYPE_FORWARD( HPSelfPctLessThan ); TYPE_FORWARD( HPSelfPctLessThanTargetCondition );
TYPE_FORWARD( GambitRule ); TYPE_FORWARD( GambitRule );
TYPE_FORWARD( GambitPack );
TYPE_FORWARD( GambitTimeLinePack );
TYPE_FORWARD( GambitRuleSetPack );
}
namespace World::AI::Fsm
{
TYPE_FORWARD( Condition );
TYPE_FORWARD( State );
TYPE_FORWARD( Transition );
TYPE_FORWARD( StateMachine );
TYPE_FORWARD( StateIdle );
TYPE_FORWARD( StateRoam );
TYPE_FORWARD( StateCombat );
TYPE_FORWARD( StateRetreat );
TYPE_FORWARD( StateDead );
TYPE_FORWARD( RoamNextTimeReachedCondition );
TYPE_FORWARD( RoamTargetReachedCondition );
TYPE_FORWARD( HateListEmptyCondition );
TYPE_FORWARD( HateListHasEntriesCondition );
TYPE_FORWARD( SpawnPointDistanceGtMaxDistanceCondition );
TYPE_FORWARD( IsDeadCondition );
} }
namespace Inventory namespace Inventory

View file

@ -14,12 +14,10 @@ using namespace Sapphire::World::Manager;
bool AchievementMgr::cacheAchievements() bool AchievementMgr::cacheAchievements()
{ {
auto& exdData = Common::Service< Data::ExdData >::ref(); auto& exdData = Common::Service< Data::ExdData >::ref();
auto idList = exdData.getIdList< Excel::Achievement >(); auto achvDat = exdData.getRows< Excel::Achievement >();
for( auto id : idList ) for( const auto& [ id, achvExdData ] : achvDat )
{ {
auto achvExdData = exdData.getRow< Excel::Achievement >( id );
uint32_t key = achvExdData->data().ConditionType; uint32_t key = achvExdData->data().ConditionType;
auto achvType = static_cast< Common::Achievement::Type >( key ); auto achvType = static_cast< Common::Achievement::Type >( key );

View file

@ -534,8 +534,8 @@ void DebugCommandMgr::add( char* data, Entity::Player& player, std::shared_ptr<
Common::CalcResultParam entry{}; Common::CalcResultParam entry{};
entry.Value = static_cast< int16_t >( param1 ); entry.Value = static_cast< int16_t >( param1 );
entry.Type = Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP; entry.Type = Common::CalcResultType::TypeDamageHp;
entry.Arg0 = static_cast< uint8_t >( Common::ActionHitSeverityType::NormalDamage ); entry.Arg0 = 1;
effectPacket->addTargetEffect( entry, static_cast< uint64_t >( player.getId() ) ); effectPacket->addTargetEffect( entry, static_cast< uint64_t >( player.getId() ) );
effectPacket->setResultId( pCurrentZone->getNextActionResultId() ); effectPacket->setResultId( pCurrentZone->getNextActionResultId() );

View file

@ -35,12 +35,10 @@ using namespace Sapphire::World::Manager;
bool MapMgr::loadQuests() bool MapMgr::loadQuests()
{ {
auto& exdData = Common::Service< Data::ExdData >::ref(); auto& exdData = Common::Service< Data::ExdData >::ref();
auto idList = exdData.getIdList< Excel::Quest >(); auto questList = exdData.getRows< Excel::Quest >();
for( auto id : idList ) for( auto& [ id, questExdData ] : questList )
{ {
auto questExdData = exdData.getRow< Excel::Quest >( id );
m_quests.emplace( id, std::move( questExdData ) ); m_quests.emplace( id, std::move( questExdData ) );
} }

View file

@ -313,7 +313,7 @@ void PlayerMgr::onUpdate( Entity::Player& player, uint64_t tickCount )
void PlayerMgr::checkAutoAttack( Entity::Player& player, uint64_t tickCount ) const void PlayerMgr::checkAutoAttack( Entity::Player& player, uint64_t tickCount ) const
{ {
auto mainWeap = player.getItemAt( Common::GearSet0, Common::MainHand ); auto mainWeap = player.getItemAt( Common::GearSet0, Common::MainHand );
if( !mainWeap || !player.isAutoattackOn() || player.checkAction() || !player.getTargetId() || player.getStance() != Common::Active ) if( !mainWeap || player.checkAction() || !player.isAutoattackOn() || !player.getTargetId() || player.getStance() != Common::Active )
return; return;
for( const auto& actor : player.getInRangeActors() ) for( const auto& actor : player.getInRangeActors() )

View file

@ -12,13 +12,12 @@ using namespace Sapphire::World::Manager;
void ShopMgr::cacheShop( uint32_t shopId ) void ShopMgr::cacheShop( uint32_t shopId )
{ {
auto& exdData = Common::Service< Data::ExdData >::ref(); auto& exdData = Common::Service< Data::ExdData >::ref();
auto itemShopList = exdData.getIdList< Excel::Shop >(); auto itemShopList = exdData.getRows< Excel::Shop >();
uint8_t count = 0; uint8_t count = 0;
for( auto itemShop : itemShopList ) for( const auto& [ itemShop, shop ] : itemShopList )
{ {
if( shopId == itemShop ) if( shopId == itemShop )
{ {
auto shop = exdData.getRow< Excel::Shop >( itemShop );
for( auto shopItemId : shop->data().Item ) for( auto shopItemId : shop->data().Item )
{ {
auto shopItem = exdData.getRow< Excel::ShopItem >( shopItemId ); auto shopItem = exdData.getRow< Excel::ShopItem >( shopItemId );

View file

@ -35,14 +35,12 @@ TerritoryMgr::TerritoryMgr() :
void TerritoryMgr::loadTerritoryTypeDetailCache() void TerritoryMgr::loadTerritoryTypeDetailCache()
{ {
auto& exdData = Common::Service< Data::ExdData >::ref(); auto& exdData = Common::Service< Data::ExdData >::ref();
auto idList = exdData.getIdList< Excel::TerritoryType >(); auto teriList = exdData.getRows< Excel::TerritoryType >();
for( auto id : idList ) for( const auto& [ id, teri ] : teriList )
{ {
auto teri1 = exdData.getRow< Excel::TerritoryType >( id ); if( !teri->getString( teri->data().Name ).empty() && id > 90 )
m_territoryTypeDetailCacheMap[ id ] = teri;
if( !teri1->getString( teri1->data().Name ).empty() && id > 90 )
m_territoryTypeDetailCacheMap[ id ] = teri1;
} }
} }
@ -77,12 +75,11 @@ uint32_t TerritoryMgr::getNextInstanceId()
Excel::ExcelStructPtr< Excel::TerritoryType > TerritoryMgr::getTerritoryDetail( uint32_t territoryTypeId ) const Excel::ExcelStructPtr< Excel::TerritoryType > TerritoryMgr::getTerritoryDetail( uint32_t territoryTypeId ) const
{ {
auto& exdData = Common::Service< Data::ExdData >::ref(); auto it = m_territoryTypeDetailCacheMap.find( territoryTypeId );
auto teri1 = exdData.getRow< Excel::TerritoryType >( territoryTypeId ); if( it == m_territoryTypeDetailCacheMap.end() )
if( !teri1 )
return nullptr; return nullptr;
return teri1; return it->second;
} }
bool TerritoryMgr::isInstanceContentTerritory( uint32_t territoryTypeId ) const bool TerritoryMgr::isInstanceContentTerritory( uint32_t territoryTypeId ) const
@ -159,11 +156,10 @@ bool TerritoryMgr::isHousingTerritory( uint32_t territoryTypeId ) const
uint32_t TerritoryMgr::getInstanceContentId( uint32_t territoryTypeId ) const uint32_t TerritoryMgr::getInstanceContentId( uint32_t territoryTypeId ) const
{ {
auto& exdData = Common::Service< Data::ExdData >::ref(); auto& exdData = Common::Service< Data::ExdData >::ref();
auto contentListIds = exdData.getIdList< Excel::InstanceContent >(); auto contentFinderList = exdData.getRows< Excel::InstanceContent >();
for( auto id : contentListIds ) for( const auto& [ id, instanceContent ] : contentFinderList )
{ {
auto instanceContent = exdData.getRow< Excel::InstanceContent >( id );
if( instanceContent->data().TerritoryType == territoryTypeId ) if( instanceContent->data().TerritoryType == territoryTypeId )
{ {
return id; return id;
@ -198,8 +194,6 @@ bool TerritoryMgr::createDefaultTerritories()
pPlaceName->getString( pPlaceName->data().Text.SGL ) ); pPlaceName->getString( pPlaceName->data().Text.SGL ) );
pZone->init(); pZone->init();
std::string bgPath = territoryInfo->getString( territoryData.LVB );
bool hasNaviMesh = pZone->getNaviProvider() != nullptr; bool hasNaviMesh = pZone->getNaviProvider() != nullptr;
Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}",
@ -309,10 +303,10 @@ TerritoryPtr TerritoryMgr::createQuestBattle( uint32_t questBattleId )
if( !pQuestInfo || pQuestInfo->getString( pQuestInfo->data().Text.Name ).empty() ) if( !pQuestInfo || pQuestInfo->getString( pQuestInfo->data().Text.Name ).empty() )
return nullptr; return nullptr;
for( auto& teriId : exdData.getIdList< Excel::TerritoryType >() ) auto teriList = exdData.getRows< Excel::TerritoryType >();
{
auto pTeri = exdData.getRow< Excel::TerritoryType >( teriId ); for( const auto& [ teriId, pTeri ] : teriList )
{
if( !pTeri || pTeri->data().QuestBattle != questBattleId ) if( !pTeri || pTeri->data().QuestBattle != questBattleId )
continue; continue;
@ -332,8 +326,6 @@ TerritoryPtr TerritoryMgr::createQuestBattle( uint32_t questBattleId )
m_instanceZoneSet.insert( pZone ); m_instanceZoneSet.insert( pZone );
return pZone; return pZone;
} }
return nullptr; return nullptr;

View file

@ -85,16 +85,14 @@ const int levelTable[61][6] =
{ 218, 354, 858, 2600, 282, 215 }, { 218, 354, 858, 2600, 282, 215 },
}; };
std::random_device CalcStats::dev; std::unique_ptr< RandGenerator< float > > CalcStats::rnd = nullptr;
std::mt19937 CalcStats::rng( dev() );
std::uniform_int_distribution< std::mt19937::result_type > CalcStats::range100( 0, 99 );
/* /*
Class used for battle-related formulas and calculations. Class used for battle-related formulas and calculations.
Big thanks to the Theoryjerks group! Big thanks to the Theoryjerks group!
NOTE: NOTE:
Formulas here shouldn't be considered final. It's possible that the formula it was based on is correct but Formulas here shouldn't be considered final. It's possible that the formula it was based on is correct but
wasn't implemented correctly here, or approximated things due to limited knowledge of how things work in retail. wasn't implemented correctly here, or approximated things due to limited knowledge of how things work in retail.
It's also possible that we're using formulas that were correct for previous patches, but not the current version. It's also possible that we're using formulas that were correct for previous patches, but not the current version.
@ -144,12 +142,15 @@ uint32_t CalcStats::calculateMaxHp( Player& player )
uint16_t hpMod = paramGrowthInfo->data().ParamBase; uint16_t hpMod = paramGrowthInfo->data().ParamBase;
uint16_t jobModHp = classInfo->data().Hp; uint16_t jobModHp = classInfo->data().Hp;
float approxBaseHp = 0.0f; // Read above float approxBaseHp = 0.0f; // Read above
float hpModPercent = player.getModifier( Common::ParamModifier::HPPercent );
approxBaseHp = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::HP ] ); approxBaseHp = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::HP ] );
auto result = static_cast< uint32_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) + auto result = static_cast< uint32_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) +
floor( hpMod / 100.0f * ( vitStat - baseStat ) ) ); floor( hpMod / 100.0f * ( vitStat - baseStat ) ) );
result *= hpModPercent;
return result; return result;
} }
@ -287,19 +288,6 @@ float CalcStats::blockProbability( const Chara& chara )
return std::floor( ( 30 * blockRate ) / levelVal + 10 ); return std::floor( ( 30 * blockRate ) / levelVal + 10 );
} }
float CalcStats::directHitProbability( const Chara& chara )
{
const auto& baseStats = chara.getStats();
auto level = chara.getLevel();
auto dhRate = chara.getStatValueFloat( Common::BaseParam::Accuracy );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
return std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f;
}
float CalcStats::criticalHitProbability( const Chara& chara ) float CalcStats::criticalHitProbability( const Chara& chara )
{ {
const auto& baseStats = chara.getStats(); const auto& baseStats = chara.getStats();
@ -564,7 +552,7 @@ float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara )
return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::HealingMagicPotency ) - 292.f ) / 264.f + 100.f ) / 100.f; return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::HealingMagicPotency ) - 292.f ) / 264.f + 100.f ) / 100.f;
} }
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ) std::pair< float, Sapphire::Common::CalcResultType > CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara )
{ {
// D = ⌊ f(ptc) × f(aa) × f(ap) × f(det) × f(tnc) × traits ⌋ × f(ss) ⌋ × // D = ⌊ f(ptc) × f(aa) × f(ap) × f(det) × f(tnc) × traits ⌋ × f(ss) ⌋ ×
// f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ × buff_1 ⌋ × buff... ⌋ // f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ × buff_1 ⌋ × buff... ⌋
@ -577,27 +565,19 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA
// todo: everything after tenacity // todo: everything after tenacity
auto factor = Common::Util::trunc( pot * aa * ap * det, 0 ); auto factor = Common::Util::trunc( pot * aa * ap * det, 0 );
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage; Sapphire::Common::CalcResultType hitType = Sapphire::Common::CalcResultType::TypeDamageHp;
// todo: traits // todo: traits
factor = std::floor( factor * speed( chara ) ); factor = std::floor( factor * speed( chara ) );
if( criticalHitProbability( chara ) > range100( rng ) ) if( criticalHitProbability( chara ) > getRandomNumber0To100() )
{ {
factor *= criticalHitBonus( chara ); factor *= criticalHitBonus( chara );
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage; hitType = Sapphire::Common::CalcResultType::TypeCriticalDamageHp;
} }
if( directHitProbability( chara ) > range100( rng ) ) factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
{
factor *= 1.25f;
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage :
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
}
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
// todo: buffs // todo: buffs
@ -615,7 +595,7 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA
return std::pair( factor, hitType ); return std::pair( factor, hitType );
} }
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ) std::pair< float, Sapphire::Common::CalcResultType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
{ {
// D = ⌊ f(pot) × f(wd) × f(ap) × f(det) × f(tnc) × traits ⌋ // D = ⌊ f(pot) × f(wd) × f(ap) × f(det) × f(tnc) × traits ⌋
// × f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ buff_1 ⌋ × buff_1 ⌋ × buff... ⌋ // × f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ buff_1 ⌋ × buff_1 ⌋ × buff... ⌋
@ -624,33 +604,29 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
auto wd = weaponDamage( chara, wepDmg ); auto wd = weaponDamage( chara, wepDmg );
auto ap = getPrimaryAttackPower( chara ); auto ap = getPrimaryAttackPower( chara );
auto det = determination( chara ); auto det = determination( chara );
auto damageDealtMod = chara.getModifier( Common::ParamModifier::DamageDealtPercent );
auto factor = Common::Util::trunc( pot * wd * ap * det, 0 ); auto factor = Common::Util::trunc( pot * wd * ap * det, 0 );
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage; Sapphire::Common::CalcResultType hitType = Sapphire::Common::CalcResultType::TypeDamageHp;
if( criticalHitProbability( chara ) > range100( rng ) ) if( criticalHitProbability( chara ) > getRandomNumber0To100() )
{ {
factor *= criticalHitBonus( chara ); factor *= criticalHitBonus( chara );
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage; hitType = Sapphire::Common::CalcResultType::TypeCriticalDamageHp;
} }
if( directHitProbability( chara ) > range100( rng ) ) factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
{
factor *= 1.25f;
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage :
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
}
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
// todo: buffs // todo: buffs
factor *= damageDealtMod;
constexpr auto format = "dmg: pot: {} ({}) wd: {} ({}) ap: {} det: {} = {}"; constexpr auto format = "dmg: pot: {} ({}) wd: {} ({}) ap: {} det: {} = {}";
if( auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer() ) if( auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer() )
{ {
PlayerMgr::sendDebug( *player, format, pot, ptc, wd, wepDmg, ap, det, factor ); PlayerMgr::sendDebug( *player, format, pot, ptc, wd, wepDmg, ap, det, factor );
PlayerMgr::sendDebug( *player, "DamageDealtPercent: {}", damageDealtMod );
} }
else else
{ {
@ -660,22 +636,23 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
return std::pair( factor, hitType ); return std::pair( factor, hitType );
} }
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ) std::pair< float, Sapphire::Common::CalcResultType > CalcStats::calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
{ {
// lol just for testing // lol just for testing
float det = chara.getStatValue( Common::BaseParam::Determination ); float det = chara.getStatValue( Common::BaseParam::Determination );
float mnd = chara.getStatValue( Common::BaseParam::Mind ); float mnd = chara.getStatValue( Common::BaseParam::Mind );
auto factor = std::floor( ( wepDmg * ( mnd / 200 ) + ( det / 10 ) ) * ( ptc / 100 ) * 1.3f ); auto factor = std::floor( ( wepDmg * ( mnd / 200 ) + ( det / 10 ) ) * ( ptc / 100 ) * 1.3f );
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalHeal;
if( criticalHitProbability( chara ) > range100( rng ) ) Sapphire::Common::CalcResultType hitType = Sapphire::Common::CalcResultType::TypeRecoverHp;
if( criticalHitProbability( chara ) > getRandomNumber0To100() )
{ {
factor *= criticalHitBonus( chara ); factor *= criticalHitBonus( chara );
hitType = Sapphire::Common::ActionHitSeverityType::CritHeal; hitType = Sapphire::Common::CalcResultType::TypeCriticalRecoverHp;
} }
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f ); factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
return std::pair( factor, hitType ); return std::pair( factor, hitType );
} }
@ -683,4 +660,13 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara ) uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara )
{ {
return chara.getStatValue( chara.getPrimaryStat() ); return chara.getStatValue( chara.getPrimaryStat() );
} }
float CalcStats::getRandomNumber0To100()
{
if( !rnd )
{
rnd = std::make_unique< RandGenerator< float > >( Common::Service< RNGMgr >::ref().getRandGenerator< float >( 0, 100 ) );
}
return rnd->next();
}

View file

@ -1,11 +1,12 @@
#pragma once #pragma once
#include <random>
#include <Common.h> #include <Common.h>
#include "Forwards.h" #include "Forwards.h"
#include "Manager/RNGMgr.h"
namespace Sapphire::Math namespace Sapphire::Math
{ {
using namespace Sapphire::World::Manager;
class CalcStats class CalcStats
{ {
@ -34,11 +35,6 @@ namespace Sapphire::Math
*/ */
static float blockProbability( const Sapphire::Entity::Chara& chara ); static float blockProbability( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates the probability of a direct hit happening
*/
static float directHitProbability( const Sapphire::Entity::Chara& chara );
/*! /*!
* @brief Calculates the probability of a critical hit happening * @brief Calculates the probability of a critical hit happening
*/ */
@ -140,11 +136,11 @@ namespace Sapphire::Math
//////////////////////////////////////////// ////////////////////////////////////////////
static std::pair< float, Common::ActionHitSeverityType > calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ); static std::pair< float, Common::CalcResultType > calcAutoAttackDamage( const Sapphire::Entity::Chara& chara );
static std::pair< float, Common::ActionHitSeverityType > calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ); static std::pair< float, Common::CalcResultType > calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
static std::pair< float, Common::ActionHitSeverityType > calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ); static std::pair< float, Common::CalcResultType > calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg );
static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara ); static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara );
private: private:
@ -156,9 +152,8 @@ namespace Sapphire::Math
*/ */
static float calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t attackPower ); static float calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t attackPower );
static std::random_device dev; static float getRandomNumber0To100();
static std::mt19937 rng; static std::unique_ptr< RandGenerator< float > > rnd;
static std::uniform_int_distribution< std::mt19937::result_type > range100;
}; };
} }

View file

@ -74,12 +74,12 @@ void Sapphire::Network::GameConnection::find5Contents( const Packets::FFXIVARR_P
if( territoryType != 0 ) if( territoryType != 0 )
selectedContent.insert( territoryType ); selectedContent.insert( territoryType );
auto contentListIds = exdData.getIdList< Excel::InstanceContent >();
std::vector< uint32_t > idList; std::vector< uint32_t > idList;
for( auto id : contentListIds )
auto contentFinderList = exdData.getRows< Excel::InstanceContent >();
for( const auto& [ id, instanceContent ] : contentFinderList )
{ {
auto instanceContent = exdData.getRow< Excel::InstanceContent >( id );
if( selectedContent.count( instanceContent->data().TerritoryType ) ) if( selectedContent.count( instanceContent->data().TerritoryType ) )
{ {
idList.push_back( id ); idList.push_back( id );

View file

@ -506,19 +506,14 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
bool doTeleport = false; bool doTeleport = false;
uint16_t teleport; uint16_t teleport;
auto idList = exdData.getIdList< Excel::Aetheryte >(); auto aetheryteList = exdData.getRows< Excel::Aetheryte >();
for( auto i : idList ) for( const auto& [ id, data ] : aetheryteList )
{ {
auto data = exdData.getRow< Excel::Aetheryte >( i );
if( !data )
continue;
if( data->data().TerritoryType == param1 && data->data().Telepo ) if( data->data().TerritoryType == param1 && data->data().Telepo )
{ {
doTeleport = true; doTeleport = true;
teleport = static_cast< uint16_t >( i ); teleport = static_cast< uint16_t >( id );
break; break;
} }

View file

@ -92,4 +92,10 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
uint8_t m_sourceEffectCount{ 0 }; uint8_t m_sourceEffectCount{ 0 };
}; };
template< typename... Args >
std::shared_ptr< EffectPacket > makeEffectPacket( Args... args )
{
return std::make_shared< EffectPacket >( args... );
}
} }

View file

@ -83,5 +83,10 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
uint8_t m_sourceEffectCount{ 0 }; uint8_t m_sourceEffectCount{ 0 };
}; };
template< typename... Args >
std::shared_ptr< EffectPacket1 > makeEffectPacket1( Args... args )
{
return std::make_shared< EffectPacket1 >( args... );
}
} }

View file

@ -77,6 +77,16 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
m_data.NpcId = bnpc.getBNpcBaseId(); m_data.NpcId = bnpc.getBNpcBaseId();
m_data.NameId = bnpc.getBNpcNameId(); m_data.NameId = bnpc.getBNpcNameId();
if( bnpc.getInstanceObjectInfo() )
{
m_data.LinkCountLimit = bnpc.getInstanceObjectInfo()->LinkCountLimit;
m_data.LinkFamily = bnpc.getInstanceObjectInfo()->LinkFamily;
m_data.LinkGroup = bnpc.getInstanceObjectInfo()->LinkGroup;
m_data.LinkParent = bnpc.getInstanceObjectInfo()->LinkParent;
m_data.LinkRange = bnpc.getInstanceObjectInfo()->LinkRange;
m_data.LinkReply = bnpc.getInstanceObjectInfo()->LinkReply;
}
assert( target.getId() != bnpc.getId() ); assert( target.getId() != bnpc.getId() );
m_data.Index = target.getSpawnIdForActorId( bnpc.getId() ); m_data.Index = target.getSpawnIdForActorId( bnpc.getId() );

View file

@ -52,15 +52,13 @@ void Util::Packet::sendBaseParams( Entity::Player& player )
std::fill( std::begin( statParams ), std::end( statParams ), 0 ); std::fill( std::begin( statParams ), std::end( statParams ), 0 );
auto& exd = Common::Service< Data::ExdData >::ref(); auto& exd = Common::Service< Data::ExdData >::ref();
auto idList = exd.getIdList< Excel::BaseParam >(); auto baseParamList = exd.getRows< Excel::BaseParam >();
for( const auto id : idList ) for( const auto& [ id, row ] : baseParamList )
{ {
auto row = exd.getRow< Excel::BaseParam >( id ); if( !row || row->data().PacketIndex < 0 )
if( !row )
continue;
if( row->data().PacketIndex < 0 )
continue; continue;
statParams[ row->data().PacketIndex ] = player.getStatValue( static_cast< Common::BaseParam >( id ) ); statParams[ row->data().PacketIndex ] = player.getStatValue( static_cast< Common::BaseParam >( id ) );
} }

View file

@ -52,8 +52,8 @@ void ActionIntegrityTask::execute()
if( actionResult && actionResult->getTarget() ) if( actionResult && actionResult->getTarget() )
actionResult->execute(); actionResult->execute();
if( ( actionResult->getCalcResultParam().Type == Common::CALC_RESULT_TYPE_SET_STATUS ) || if( ( actionResult->getCalcResultParam().Type == Common::TypeSetStatus ) ||
( actionResult->getCalcResultParam().Type == Common::CALC_RESULT_TYPE_SET_STATUS_ME ) ) ( actionResult->getCalcResultParam().Type == Common::TypeSetStatusMe ) )
{ {
auto& status = data.Status[ statusIdx++ ]; auto& status = data.Status[ statusIdx++ ];
auto pEffect = actionResult->getStatusEffect(); auto pEffect = actionResult->getStatusEffect();

View file

@ -41,6 +41,10 @@ void MoveTerritoryTask::execute()
if( !pPlayer ) if( !pPlayer )
return; return;
pPlayer->setTargetId( 0 );
pPlayer->setStance( Common::Stance::Passive );
pPlayer->setAutoattack( false );
auto inRangePlayerIds = pPlayer->getInRangePlayerIds( true ); auto inRangePlayerIds = pPlayer->getInRangePlayerIds( true );
auto warpStart = makeActorControlSelf( pPlayer->getId(), WarpStart, m_warpInfo.m_warpType, 1, 0, m_warpInfo.m_targetTerritoryId, 1 ); auto warpStart = makeActorControlSelf( pPlayer->getId(), WarpStart, m_warpInfo.m_warpType, 1, 0, m_warpInfo.m_targetTerritoryId, 1 );

View file

@ -102,3 +102,12 @@ void Sapphire::Cell::unload()
removeActors(); removeActors();
} }
uint32_t Sapphire::Cell::getLastActiveTime() const
{
return m_lastActiveTime;
}
void Sapphire::Cell::setLastActiveTime( uint32_t lastActiveTime )
{
m_lastActiveTime = lastActiveTime;
}

View file

@ -20,6 +20,7 @@ private:
bool m_bActive; bool m_bActive;
uint16_t m_playerCount; uint16_t m_playerCount;
uint32_t m_lastActiveTime;
public: public:
Cell(); Cell();
@ -82,6 +83,9 @@ public:
{ {
return m_posY; return m_posY;
} }
uint32_t getLastActiveTime() const;
void setLastActiveTime( uint32_t lastActiveTime );
}; };
} }

View file

@ -11,27 +11,24 @@
#include <ExdCat.h> #include <ExdCat.h>
#include <Exd.h> #include <Exd.h>
#include <algorithm>
#include <execution>
#include <Logging/Logger.h> #include <Logging/Logger.h>
#include <Service.h> #include <Service.h>
#include <Util/UtilMath.h> #include <Util/UtilMath.h>
Sapphire::InstanceObjectCache::InstanceObjectCache() Sapphire::InstanceObjectCache::InstanceObjectCache()
{ {
auto& exdData = Common::Service< Sapphire::Data::ExdData >::ref(); auto& exdData = Common::Service< Sapphire::Data::ExdData >::ref();
auto idList = exdData.getIdList< Excel::TerritoryType >(); auto teriList = exdData.getRows< Excel::TerritoryType >();
size_t count = 0; size_t count = 0;
for( const auto& id : idList ) for( const auto& [ id, territoryType ] : teriList ) {
{
// show some loading indication... // show some loading indication...
if( count++ % 10 == 0 ) if( count++ % 10 == 0 )
std::cout << "."; std::cout << ".";
auto territoryType = exdData.getRow< Excel::TerritoryType >( id );
if( !territoryType )
continue;
auto path = territoryType->getString( territoryType->data().LVB ); auto path = territoryType->getString( territoryType->data().LVB );
if( path.empty() ) if( path.empty() )
@ -56,7 +53,11 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
try try
{ {
bgFile = exdData.getGameData()->getFile( bgLgbPath ); if( exdData.getGameData()->doesFileExist( bgLgbPath ) )
bgFile = exdData.getGameData()->getFile( bgLgbPath );
else
continue;
planmap_file = exdData.getGameData()->getFile( planmapLgbPath ); planmap_file = exdData.getGameData()->getFile( planmapLgbPath );
planevent_file = exdData.getGameData()->getFile( planeventLgbPath ); planevent_file = exdData.getGameData()->getFile( planeventLgbPath );
} }
@ -101,49 +102,60 @@ Sapphire::InstanceObjectCache::InstanceObjectCache()
{ {
for( const auto& pEntry : group.entries ) for( const auto& pEntry : group.entries )
{ {
switch( pEntry->getType() )
{
case LgbEntryType::MapRange:
{
auto pMapRange = std::reinterpret_pointer_cast< LGB_MAP_RANGE_ENTRY >( pEntry );
m_mapRangeCache.insert( id, pMapRange );
if( pEntry->getType() == LgbEntryType::MapRange ) break;
{ }
auto pMapRange = std::reinterpret_pointer_cast< LGB_MAP_RANGE_ENTRY >( pEntry ); case LgbEntryType::ExitRange:
m_mapRangeCache.insert( id, pMapRange ); {
} auto pExitRange = std::reinterpret_pointer_cast< LGB_EXIT_RANGE_ENTRY >( pEntry );
else if( pEntry->getType() == LgbEntryType::ExitRange ) m_exitRangeCache.insert( id, pExitRange );
{
auto pExitRange = std::reinterpret_pointer_cast< LGB_EXIT_RANGE_ENTRY >( pEntry );
m_exitRangeCache.insert( id, pExitRange );
}
else if( pEntry->getType() == LgbEntryType::PopRange )
{
auto pPopRange = std::reinterpret_pointer_cast< LGB_POP_RANGE_ENTRY >( pEntry ); break;
m_popRangeCache.insert( id, pPopRange ); }
} case LgbEntryType::PopRange:
else if( pEntry->getType() == LgbEntryType::CollisionBox ) {
{ auto pPopRange = std::reinterpret_pointer_cast< LGB_POP_RANGE_ENTRY >( pEntry );
//auto pEObj = std::reinterpret_pointer_cast< LGB_ENPC_ENTRY >( pEntry ); m_popRangeCache.insert( id, pPopRange );
break;
}
case LgbEntryType::CollisionBox:
{
//auto pEObj = std::reinterpret_pointer_cast< LGB_ENPC_ENTRY >( pEntry );
//Logger::debug( "CollisionBox {}", pEntry->header.nameOffset ); //Logger::debug( "CollisionBox {}", pEntry->header.nameOffset );
} break;
else if( pEntry->getType() == LgbEntryType::EventObject ) }
{ case LgbEntryType::EventObject:
auto pEObj = std::reinterpret_pointer_cast< LGB_EOBJ_ENTRY >( pEntry ); {
m_eobjCache.insert( id, pEObj ); auto pEObj = std::reinterpret_pointer_cast< LGB_EOBJ_ENTRY >( pEntry );
} m_eobjCache.insert( id, pEObj );
else if( pEntry->getType() == LgbEntryType::EventNpc ) break;
{ }
auto pENpc = std::reinterpret_pointer_cast< LGB_ENPC_ENTRY >( pEntry ); case LgbEntryType::EventNpc:
m_enpcCache.insert( id, pENpc ); {
} auto pENpc = std::reinterpret_pointer_cast< LGB_ENPC_ENTRY >( pEntry );
else if( pEntry->getType() == LgbEntryType::EventRange ) m_enpcCache.insert( id, pENpc );
{ break;
auto pEventRange = std::reinterpret_pointer_cast< LGB_EVENT_RANGE_ENTRY >( pEntry ); }
m_eventRangeCache.insert( 0, pEventRange ); case LgbEntryType::EventRange:
{
auto pEventRange = std::reinterpret_pointer_cast< LGB_EVENT_RANGE_ENTRY >( pEntry );
m_eventRangeCache.insert( 0, pEventRange );
break;
}
} }
} }
} }
} }
} }
std::cout << "\n";
std::cout << std::endl;
Logger::debug( Logger::debug(
"InstanceObjectCache Cached: MapRange: {} ExitRange: {} PopRange: {} EventObj: {} EventNpc: {} EventRange: {}", "InstanceObjectCache Cached: MapRange: {} ExitRange: {} PopRange: {} EventObj: {} EventNpc: {} EventRange: {}",

View file

@ -528,6 +528,7 @@ bool Territory::isCellActive( uint32_t x, uint32_t y )
uint32_t posY; uint32_t posY;
CellPtr pCell; CellPtr pCell;
uint32_t time = Common::Util::getTimeSeconds();
for( posX = startX; posX <= endX; posX++ ) for( posX = startX; posX <= endX; posX++ )
{ {
@ -535,7 +536,7 @@ bool Territory::isCellActive( uint32_t x, uint32_t y )
{ {
pCell = getCellPtr( posX, posY ); pCell = getCellPtr( posX, posY );
if( pCell && ( pCell->hasPlayers() || pCell->isForcedActive() ) ) if( pCell && ( pCell->hasPlayers() || pCell->isForcedActive() || ( time - pCell->getLastActiveTime() ) < 20 ) )
return true; return true;
} }
} }
@ -566,13 +567,13 @@ void Territory::updateCellActivity( uint32_t x, uint32_t y, int32_t radius )
{ {
pCell = create( posX, posY ); pCell = create( posX, posY );
pCell->init( posX, posY ); pCell->init( posX, posY );
pCell->setActivity( true ); pCell->setActivity( true );
pCell->setLastActiveTime( Common::Util::getTimeSeconds() );
} }
} }
else else
{ {
pCell->setLastActiveTime( Common::Util::getTimeSeconds() );
//Cell is now active //Cell is now active
if( isCellActive( posX, posY ) && !pCell->isActive() ) if( isCellActive( posX, posY ) && !pCell->isActive() )
{ {
@ -581,6 +582,7 @@ void Territory::updateCellActivity( uint32_t x, uint32_t y, int32_t radius )
else if( !isCellActive( posX, posY ) && pCell->isActive() ) else if( !isCellActive( posX, posY ) && pCell->isActive() )
pCell->setActivity( false ); pCell->setActivity( false );
} }
} }
} }
} }
@ -834,6 +836,7 @@ Entity::BNpcPtr Territory::createBNpcFromLayoutId( uint32_t layoutId, uint32_t h
return nullptr; return nullptr;
auto pBNpc = std::make_shared< Entity::BNpc >( getNextActorId(), infoPtr->second, *this, hp, bnpcType ); auto pBNpc = std::make_shared< Entity::BNpc >( getNextActorId(), infoPtr->second, *this, hp, bnpcType );
pBNpc->init();
pBNpc->setTriggerOwnerId( triggerOwnerId ); pBNpc->setTriggerOwnerId( triggerOwnerId );
pushActor( pBNpc ); pushActor( pBNpc );
return pBNpc; return pBNpc;

View file

@ -144,6 +144,11 @@ std::string readFileToString( const std::string& filename )
// Close the file // Close the file
file.close(); file.close();
// Remove all newlines from the file contents
fileContents.erase( std::remove( fileContents.begin(), fileContents.end(), '\n' ), fileContents.end() );
fileContents.erase( std::remove( fileContents.begin(), fileContents.end(), '\r' ), fileContents.end() );
// Return the file contents as a string // Return the file contents as a string
return fileContents; return fileContents;
} }