1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-03 17:27:47 +00:00

Merge pull request #928 from hkAlice/bs-stuff

[3.x] (WIP) EncounterFight and state refactor;
This commit is contained in:
Mordred 2025-01-04 16:51:41 +01:00 committed by GitHub
commit f19d3167ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
202 changed files with 12193 additions and 2736 deletions

View file

@ -18,28 +18,39 @@ if( UNIX )
"preferred path to MySQL (mysql_config)"
)
find_program(MYSQL_CONFIG mysql_config
# try mariadb first
find_program(MYSQL_CONFIG mariadb_config
${MYSQL_CONFIG_PREFER_PATH}
/usr/local/mysql/bin/
/usr/local/bin/
/usr/bin/
)
if( NOT MYSQL_CONFIG )
# fallback to mysql
find_program(MYSQL_CONFIG mysql_config
${MYSQL_CONFIG_PREFER_PATH}
/usr/local/mysql/bin/
/usr/local/bin/
/usr/bin/
)
endif()
if( MYSQL_CONFIG )
message(STATUS "Using mysql-config: ${MYSQL_CONFIG}")
# set INCLUDE_DIR
exec_program(${MYSQL_CONFIG}
ARGS --include
execute_process(COMMAND ${MYSQL_CONFIG} --include
OUTPUT_VARIABLE MY_TMP
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX REPLACE "-I([^ ]*)( .*)?" "\\1" MY_TMP "${MY_TMP}")
set(MYSQL_ADD_INCLUDE_PATH ${MY_TMP} CACHE FILEPATH INTERNAL)
#message("[DEBUG] MYSQL ADD_INCLUDE_PATH : ${MYSQL_ADD_INCLUDE_PATH}")
# set LIBRARY_DIR
exec_program(${MYSQL_CONFIG}
ARGS --libs_r
execute_process(COMMAND ${MYSQL_CONFIG} --libs_r
OUTPUT_VARIABLE MY_TMP
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(MYSQL_ADD_LIBRARIES "")
string(REGEX MATCHALL "-l[^ ]*" MYSQL_LIB_LIST "${MY_TMP}")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,104 @@
{
"872": {
"name": "Auto Attack",
"potency": 100,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
"rearPotency": 0,
"curePotency": 0,
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
}
},
"453": {
"name": "Incinerate",
"potency": 100,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
"rearPotency": 0,
"curePotency": 0,
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
}
},
"454": {
"name": "Vulcan Burst",
"potency": 100,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
"rearPotency": 0,
"curePotency": 0,
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
}
},
"458": {
"name": "Hellfire",
"potency": 400,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
"rearPotency": 0,
"curePotency": 0,
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [
{
"id": 394,
"duration": 8000,
"modifiers": [
{
"modifier": "DamageTakenPercent",
"value": -100
}
]
}
],
"target": []
}
},
"733": {
"name": "Eruption",
"potency": 200,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
"rearPotency": 0,
"curePotency": 0,
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
}
},
"734": {
"name": "Radiant Plume",
"potency": 200,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
"rearPotency": 0,
"curePotency": 0,
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
}
}
}

View file

@ -1,7 +1,7 @@
{
"7": {
"name": "Attack",
"potency": 0,
"potency": 110,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
@ -16,7 +16,7 @@
},
"8": {
"name": "Shot",
"potency": 0,
"potency": 100,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
@ -398,7 +398,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 83,
"duration": 20000,
"modifiers": [
{
"modifier": "DefensePercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -414,7 +425,18 @@
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
"target": [
{
"id": 244,
"duration": 30000,
"modifiers": [
{
"modifier": "TickDamage",
"value": 20
}
]
}
]
}
},
"34": {
@ -478,7 +500,18 @@
45
],
"statuses": {
"caster": [],
"caster": [
{
"id": 85,
"duration": 24000,
"modifiers": [
{
"modifier": "DamageDealtPercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -493,7 +526,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 86,
"duration": 20000,
"modifiers": [
{
"modifier": "AttackPowerPercent",
"value": 50
}
]
}
],
"target": []
}
},
@ -523,7 +567,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 87,
"duration": 20000,
"modifiers": [
{
"modifier": "HPPercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -574,7 +629,7 @@
},
"44": {
"name": "Vengeance",
"potency": 50,
"potency": 0,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
@ -583,7 +638,18 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 89,
"duration": 20000,
"modifiers": [
{
"modifier": "ReflectPhysical",
"value": 50
}
]
}
],
"target": []
}
},
@ -643,7 +709,34 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 91,
"duration": 0,
"modifiers": [
{
"modifier": "HPPercent",
"value": 25
},
{
"modifier": "DamageDealtPercent",
"value": -25
},
{
"modifier": "HealingMagicRecoveryPercent",
"value": 20
},
{
"modifier": "AccuracyPercent",
"value": 5
},
{
"modifier": "EnmityPercent",
"value": 20
}
]
}
],
"target": []
}
},
@ -703,7 +796,12 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 97,
"duration": 30000
}
],
"target": []
}
},
@ -1176,7 +1274,19 @@
"restorePercentage": 0,
"nextCombo": [],
"statuses": {
"caster": [],
"caster": [
{
"id": 116,
"duration": 10000,
"flag": 4096,
"modifiers": [
{
"modifier": "CriticalHitPercent",
"value": 100
}
]
}
],
"target": []
}
},
@ -2454,7 +2564,38 @@
"nextCombo": [],
"statuses": {
"caster": [],
"target": []
"target": [
{
"id": 180,
"duration": 24000,
"modifiers": [
{
"modifier": "TickDamage",
"value": 35
}
]
},
{
"id": 191,
"duration": 24000,
"modifiers": [
{
"modifier": "HealingRecoveryPercent",
"value": -20
}
]
},
{
"id": 240,
"duration": 24000,
"modifiers": [
{
"modifier": "HeavyPercent",
"value": 40
}
]
}
]
}
},
"169": {

View file

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

View file

@ -7,50 +7,6 @@
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
{
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() ) );
// Extract the header and skip to the record indices
auto exd_header = extract< ExdHeader >( iss );
auto exd_header = extract< ExdHeaderMinimal >( iss );
iss.seekg( 0x20 );
// Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex );
std::vector< ExdRecordIndex > 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 )
{
auto recordIndex = extract< ExdRecordIndex >( iss );
auto recordIndex = extract< ExdRecordIndexData >( iss );
_idCache[ recordIndex.id ] = ExdCacheEntry{ file_ptr, recordIndex.offset };
}
}
@ -290,16 +246,16 @@ namespace xiv::exd
std::istringstream iss( std::string( dataCpy.begin(), dataCpy.end() ) );
// Extract the header and skip to the record indices
auto exd_header = extract< ExdHeader >( iss );
auto exd_header = extract< ExdHeaderMinimal >( iss );
iss.seekg( 0x20 );
// Preallocate and extract the record_indices
const uint32_t record_count = exd_header.index_size / sizeof( ExdRecordIndex );
std::vector< ExdRecordIndex > 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( extract< ExdRecordIndex >( iss ) );
record_indices.emplace_back( extract< ExdRecordIndexData >( iss ) );
}
for( auto& record_index : record_indices )

191
deps/datReader/Exd.h vendored
View file

@ -2,6 +2,7 @@
#include <memory>
#include <map>
#include <unordered_map>
#include <variant>
@ -12,6 +13,49 @@
#include <fstream>
#include "Exh.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
{
@ -184,12 +228,153 @@ namespace xiv::exd
const std::vector< Field > get_row( uint32_t id, uint32_t subRow );
// Get all rows
const std::map< uint32_t, std::vector< Field>>& get_rows();
const std::map< 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:
// Data indexed by the ID of the row, the vector is field with the same order as exh.members
std::map< uint32_t, std::vector< Field>> _data;
std::vector< std::shared_ptr< dat::File>> _files;
std::map< uint32_t, std::vector< Field > > _data;
std::vector< std::shared_ptr< dat::File > > _files;
std::shared_ptr< Exh > _exh;
std::map< uint32_t, ExdCacheEntry > _idCache;
};

View file

@ -302,18 +302,18 @@ namespace Excel
uint16_t LimitBreakAction[3];
uint16_t PvpLimitBreakAction[3];
uint8_t Kind;
uint8_t UIPriority;
uint8_t Unknown6;
uint8_t JobIndex;
uint8_t MainClass;
uint8_t Role;
uint8_t Town;
int8_t MonsterNote;
int8_t UIPriority;
int8_t StartingLevel;
uint8_t PartyBuff;
int8_t WorkIndex;
int8_t BattleClassIndex;
int8_t CraftingClassIndex;
int8_t Unknown7;
int8_t MonsterNote;
};
/* 63507 */
@ -392,12 +392,13 @@ namespace Excel
uint8_t CostType;
uint8_t Cond;
uint8_t RecastGroup;
uint8_t Element;
uint8_t ProcStatus;
uint8_t UseClassJob;
uint8_t Unknown1; // todo: possibly cost type? tp etc?
uint8_t ClassJobCategory; // recastgroup
uint8_t Init;
uint8_t Omen;
int8_t Learn;
uint8_t Learn;
int8_t UseClassJob;
int8_t SelectRange;
int8_t SelectCorpse;
int8_t AttackType;
@ -429,7 +430,7 @@ namespace Excel
uint8_t HideCastBar : 1;
uint8_t IsTargetLine : 1;
int8_t padding0;
int8_t unknown;
};
/* 75653 */
@ -2053,7 +2054,8 @@ namespace Excel
uint8_t NotControl : 1;
uint8_t NotAction : 1;
uint8_t NotMove : 1;
uint8_t padding0 : 6;
uint8_t padding0 : 5;
uint8_t CanOff : 1;
uint8_t SemiTransparent : 1;
uint8_t FcAction : 1;
int8_t padding1[2];

View file

@ -3,6 +3,7 @@
#include <iomanip>
#include <sstream>
#include <vector>
#include <cstdint>
namespace xiv::utils::bparse
{

View file

@ -5,6 +5,7 @@
#include <iomanip>
#include <sstream>
#include <vector>
#include <cstdint>
namespace xivps3::utils::bparse
{

View file

@ -4,6 +4,7 @@
#include <list>
#include <map>
#include <iostream>
#include <cstdint>
namespace Mysql
{

View file

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include <sstream>
#include <cstdint>
#include <mysql.h>
//using MYSQL_FIELD = st_mysql_field;

View file

@ -32,14 +32,8 @@
#include <functional>
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem>
namespace ci { namespace fs = std::filesystem; }
#else
#include <experimental/filesystem>
namespace ci { namespace fs = std::experimental::filesystem; }
#endif
//! Exception for when Watchdog can't locate a file or parse the wildcard
class WatchedFileSystemExc : public std::exception {

View file

@ -0,0 +1 @@
ALTER TABLE `characlass` ADD `BorrowAction` binary(40) DEFAULT NULL NULL AFTER `Lvl`;

View file

@ -256,12 +256,14 @@ void PlayerMinimal::saveAsNew()
break;
}
// CharacterId, ClassIdx, Exp, Lvl
// CharacterId, ClassIdx, Exp, Lvl, BorrowAction
auto stmtClass = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_CLASS_INS );
stmtClass->setUInt64( 1, m_characterId );
stmtClass->setInt( 2, g_exdData.getRow< Excel::ClassJob >( m_class )->data().WorkIndex );
stmtClass->setInt( 3, 0 );
stmtClass->setInt( 4, 1 );
std::vector< uint8_t > borrowActionVec( Common::ARRSIZE_BORROWACTION * 4 );
stmtClass->setBinary( 5, borrowActionVec );
g_charaDb.directExecute( stmtClass );
auto stmtSearchInfo = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_SEARCHINFO_INS );

View file

@ -31,14 +31,8 @@
#include <Util/CrashHandler.h>
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
Sapphire::Common::Util::CrashHandler crashHandler;
@ -244,12 +238,14 @@ void createAccount( shared_ptr< HttpServer::Response > response, shared_ptr< Htt
std::string sId;
if( g_sapphireAPI.createAccount( user, pass, sId ) )
{
// todo: construct proper json object here
std::string json_string = "{\"sId\":\"" + sId +
"\", \"lobbyHost\":\"" +
m_config.global.network.lobbyHost +
"\", \"frontierHost\":\"" +
m_config.global.network.restHost + "\"}";
nlohmann::json response_json = {
{"sId", sId},
{"lobbyHost", m_config.global.network.lobbyHost},
{"frontierHost", m_config.global.network.restHost},
{"lobbyPort", m_config.global.network.lobbyPort}
};
std::string json_string = response_json.dump();
*response << buildHttpResponse( 200, json_string, JSON );
}
else
@ -277,12 +273,15 @@ void login( shared_ptr< HttpServer::Response > response, shared_ptr< HttpServer:
// reloadConfig();
if( g_sapphireAPI.login( user, pass, sId ) )
{
// todo: build proper json object and stringify it
std::string json_string = "{\"sId\":\"" + sId +
"\", \"lobbyHost\":\"" +
m_config.global.network.lobbyHost +
"\", \"frontierHost\":\"" +
m_config.global.network.restHost + "\"}";
nlohmann::json response_json = {
{"sId", sId},
{"lobbyHost", m_config.global.network.lobbyHost},
{"frontierHost", m_config.global.network.restHost},
{"lobbyPort", m_config.global.network.lobbyPort}
};
std::string json_string = response_json.dump();
*response << buildHttpResponse( 200, json_string, JSON );
}
else
@ -294,7 +293,6 @@ void login( shared_ptr< HttpServer::Response > response, shared_ptr< HttpServer:
*response << buildHttpResponse( 500 );
Logger::error( e.what() );
}
}
void deleteCharacter( shared_ptr< HttpServer::Response > response, shared_ptr< HttpServer::Request > request )

View file

@ -37,6 +37,8 @@ namespace Sapphire::Common
const uint16_t ARRSIZE_UNLOCKS = 64u;
const uint16_t ARRSIZE_ORCHESTRION = 40u;
const uint16_t ARRSIZE_MONSTERNOTE = 12u;
const uint16_t ARRSIZE_BORROWACTION = 10u;
const uint16_t ARRSIZE_CONDITION = 12u;
const uint8_t TOWN_COUNT = 6;
@ -887,22 +889,55 @@ namespace Sapphire::Common
Perception = 73,
// Unique modifiers
HPPercent = 1000,
MPPercent = 1001,
TPPercent = 1002,
GPPercent = 1003,
CPPercent = 1004,
PhysicalDamagePercent = 1005,
MagicDamagePercent = 1006,
AttackPowerPercent = 1007,
DefensePercent = 1008,
AccuracyPercent = 1009,
EvasionPercent = 1010,
MagicDefensePercent = 1011,
CriticalHitPowerPercent = 1012,
CriticalHitResiliencePercent = 1013,
CriticalHitPercent = 1014,
EnmityPercent = 1015
TickHeal = 1000,
TickDamage = 1001,
StrengthPercent = 1002,
DexterityPercent = 1003,
VitalityPercent = 1004,
IntelligencePercent = 1005,
MindPercent = 1006,
PietyPercent = 1007,
HPPercent = 1008,
MPPercent = 1009,
TPPercent = 1010,
GPPercent = 1011,
CPPercent = 1012,
PhysicalDamagePercent = 1013,
MagicDamagePercent = 1014,
AttackPowerPercent = 1015,
DefensePercent = 1016,
AccuracyPercent = 1017,
EvasionPercent = 1018,
MagicDefensePercent = 1019,
CriticalHitPowerPercent = 1020,
CriticalHitResiliencePercent = 1021,
CriticalHitPercent = 1022,
EnmityPercent = 1023,
DamageDealtPercent = 1024,
DamageTakenPercent = 1025,
HealingMagicRecoveryPercent = 1026,
SlashingResistancePercent = 1027,
PiercingResistancePercent = 1028,
BluntResistancePercent = 1029,
ProjectileResistancePercent = 1030,
ParryPercent = 1031
};
enum class StatusEffectFlag : uint32_t
{
BuffCategory = 1,
DebuffCategory = 2,
Permanent = 4,
IsGaze = 8,
Transfiguration = 16,
CanDispel = 32,
LockActions = 64,
LockControl = 128,
LockMovement = 256,
Invisibilty = 512,
CanStatusOff = 1024,
FcBuff = 2048,
RemoveOnSuccessfulHit = 4096
};
enum struct ActionAspect : uint8_t
@ -923,6 +958,7 @@ namespace Sapphire::Common
MagicPoints = 3,
TacticsPoints = 5,
TacticsPoints1 = 6,
StatusEffect = 10,
Sprint = 18,
// WARGauge = 22,
// DRKGauge = 25,
@ -948,77 +984,67 @@ namespace Sapphire::Common
LimitBreak = 8,
};
enum ActionEffectType : uint8_t
enum CalcResultType : uint8_t
{
CALC_RESULT_TYPE_NONE = 0x0,
CALC_RESULT_TYPE_MISS = 0x1,
CALC_RESULT_TYPE_RESIST = 0x2,
CALC_RESULT_TYPE_DAMAGE_HP = 0x3,
CALC_RESULT_TYPE_RECOVER_HP = 0x4,
CALC_RESULT_TYPE_CRITICAL_DAMAGE_HP = 0x5,
CALC_RESULT_TYPE_CRITICAL_RECOVER_HP = 0x6,
CALC_RESULT_TYPE_GUARD = 0x7,
CALC_RESULT_TYPE_PARRY = 0x8,
CALC_RESULT_TYPE_INVALID = 0x9,
CALC_RESULT_TYPE_UNEFFECTIVE = 0xA,
CALC_RESULT_TYPE_NEGLECT = 0xB,
CALC_RESULT_TYPE_DAMAGE_MP = 0xC,
CALC_RESULT_TYPE_RECOVER_MP = 0xD,
CALC_RESULT_TYPE_DAMAGE_TP = 0xE,
CALC_RESULT_TYPE_RECOVER_TP = 0xF,
CALC_RESULT_TYPE_RECOVER_GP = 0x10,
CALC_RESULT_TYPE_SET_STATUS = 0x11,
CALC_RESULT_TYPE_SET_STATUS_ME = 0x12,
CALC_RESULT_TYPE_RESET_STATUS = 0x13,
CALC_RESULT_TYPE_RESET_STATUS_ME = 0x14,
CALC_RESULT_TYPE_RESET_BAD_STATUS = 0x15,
CALC_RESULT_TYPE_UNEFFECTIVE_STATUS = 0x16,
CALC_RESULT_TYPE_HALF_GOOD_STATUS = 0x17,
CALC_RESULT_TYPE_HATE_DIRECT = 0x18,
CALC_RESULT_TYPE_HATE_INDIRECTION = 0x19,
CALC_RESULT_TYPE_HATE_TOP = 0x1A,
CALC_RESULT_TYPE_HATE_ADD = 0x1B,
CALC_RESULT_TYPE_HATE_MULT = 0x1C,
CALC_RESULT_TYPE_COMBO = 0x1D,
CALC_RESULT_TYPE_COMBO_HIT = 0x1E,
CALC_RESULT_TYPE_COUNTER = 0x1F,
CALC_RESULT_TYPE_DESTRUCT = 0x20,
CALC_RESULT_TYPE_PARALYSIS = 0x21,
CALC_RESULT_TYPE_KNOCK_BACK = 0x22,
CALC_RESULT_TYPE_DRAW_UP_CHAIRS = 0x23,
CALC_RESULT_TYPE_SUCKED = 0x24,
CALC_RESULT_TYPE_CT_DRAW_UP_CHAIRS = 0x25,
CALC_RESULT_TYPE_LIVE_CALLBACK = 0x26,
CALC_RESULT_TYPE_MOUNT = 0x27,
CALC_RESULT_ARCHER_DOT = 0x28,
CALC_RESULT_MASTER_DOT = 0x29,
CALC_RESULT_BLESSINGS_OF_GODDESS = 0x2A,
CALC_RESULT_BAD_BREATH = 0x2B,
CALC_RESULT_REVIVAL = 0x2C,
CALC_RESULT_PET = 0x2D,
CALC_RESULT_TYPE_BLOW = 0x2E,
CALC_RESULT_TYPE_STATUS_RESIST = 0x2F,
CALC_RESULT_TYPE_CLEAR_PHYSICAL = 0x30,
CALC_RESULT_BNPC_STATE = 0x31,
CALC_RESULT_TYPE_VFX = 0x32,
CALC_RESULT_TYPE_HARD_CODE = 0x33,
CALC_RESULT_CALC_ID = 0x34,
CALC_RESULT_TYPE_CLEAR_PVP_POINT = 0x35,
CALC_RESULT_TYPE_CHECK_BARRIER = 0x36,
CALC_RESULT_TYPE_REFLEC = 0x37,
TypeNone = 0x0,
TypeMiss = 0x1,
TypeResist = 0x2,
TypeDamageHp = 0x3,
TypeRecoverHp = 0x4,
TypeCriticalDamageHp = 0x5,
TypeCriticalRecoverHp = 0x6,
TypeGuard = 0x7,
TypeParry = 0x8,
TypeInvalid = 0x9,
TypeUneffective = 0xA,
TypeNeglect = 0xB,
TypeDamageMp = 0xC,
TypeRecoverMp = 0xD,
TypeDamageTp = 0xE,
TypeRecoverTp = 0xF,
TypeRecoverGp = 0x10,
TypeSetStatus = 0x11,
TypeSetStatusMe = 0x12,
TypeResetStatus = 0x13,
TypeResetStatusMe = 0x14,
TypeResetBadStatus = 0x15,
TypeUneffectiveStatus = 0x16,
TypeHalfGoodStatus = 0x17,
TypeHateDirect = 0x18,
TypeHateIndirection = 0x19,
TypeHateTop = 0x1A,
TypeHateAdd = 0x1B,
TypeHateMult = 0x1C,
TypeCombo = 0x1D,
TypeComboHit = 0x1E,
TypeCounter = 0x1F,
TypeDestruct = 0x20,
TypeParalysis = 0x21,
TypeKnockBack = 0x22,
TypeDrawUpChairs = 0x23,
TypeSucked = 0x24,
TypeCtDrawUpChairs = 0x25,
TypeLiveCallback = 0x26,
TypeMount = 0x27,
TypeArcherDot = 0x28,
TypeMasterDot = 0x29,
TypeBlessingOfGoddess = 0x2A,
TypeBadBreath = 0x2B,
TypeRevival = 0x2C,
TypePet = 0x2D,
TypeBlow = 0x2E,
TypeStatusResist = 0x2F,
TypeClearPhysical = 0x30,
TypeBNpcState = 0x31,
TypeVfx = 0x32,
TypeHardCode = 0x33,
TypeCalcId = 0x34,
TypeClearPvpPoint = 0x35,
TypeCheckBarrier = 0x36,
TypeReflect = 0x37,
};
enum class ActionHitSeverityType : uint8_t
{
NormalDamage = 0,
CritHeal = 0,
CritDamage = 1,
NormalHeal = 1,
DirectHitDamage = 2,
CritDirectHitDamage = 3
};
enum class ActionEffectResultFlag : uint8_t
enum class ActionResultFlag : uint8_t
{
None = 0,
Absorbed = 0x04,
@ -1033,6 +1059,7 @@ namespace Sapphire::Common
ItemActionCompanion = 853,
ItemActionVFX2 = 944,
ItemActionMount = 1322,
ItemActionSong = 5845,
};
enum ActionEffectDisplayType : uint8_t
@ -1383,9 +1410,10 @@ namespace Sapphire::Common
{
None1 = 0,
HideUILockChar = 1, // as the name suggests, hides the ui and logs the char...
InCombat = 2, // in Combat, locks gearchange/return/teleport
Casting = 3,
InNpcEvent = 6, // when talking to an npc, locks ui giving "occupied" message
InCombat = 18, // in Combat, locks gearchange/return/teleport
Casting = 19,
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...
@ -1798,6 +1826,7 @@ namespace Sapphire::Common
{
uint16_t targetAetheryte;
uint16_t cost;
bool useAetheryteTicket{ false };
};
enum EventSceneError : uint8_t
@ -1833,12 +1862,13 @@ namespace Sapphire::Common
THREAT
};
// todo: fill this out (Action.exd EffectType)
enum CastType : uint8_t
{
SingleTarget = 1,
CircularAOE = 2,
Type3 = 3, // another single target? no idea how to call it
RectangularAOE = 4,
RectangularAOE = 3,
ConeAOE = 4,
CircularAoEPlaced = 7
};
@ -1856,6 +1886,7 @@ namespace Sapphire::Common
using PlayerStateFlagList = std::vector< PlayerCondition >;
// todo: load BNpcBase and other exd data into this struct
struct BNPCInstanceObject
{
uint16_t territoryType;

View file

@ -2,13 +2,8 @@
#include <iostream>
#include <fstream>
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
using namespace Sapphire;
using namespace Sapphire::Common;
@ -94,4 +89,4 @@ bool ConfigMgr::copyDefaultConfig( const std::string& configName )
fs::copy_file( configPath.string() + m_configDefaultSuffix, configPath );
return true;
}
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <cstdint>
namespace Sapphire::Common::Util
{

View file

@ -161,11 +161,11 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements()
prepareStatement( CHARA_SEL_QUEST, "SELECT * FROM charaquest WHERE CharacterId = ?;", CONNECTION_SYNC );
/// CLASS INFO
prepareStatement( CHARA_CLASS_SEL, "SELECT ClassIdx, Exp, Lvl FROM characlass WHERE CharacterId = ?;",
prepareStatement( CHARA_CLASS_SEL, "SELECT ClassIdx, Exp, Lvl, BorrowAction FROM characlass WHERE CharacterId = ?;",
CONNECTION_SYNC );
prepareStatement( CHARA_CLASS_INS, "INSERT INTO characlass ( CharacterId, ClassIdx, Exp, Lvl ) VALUES( ?,?,?,? );",
prepareStatement( CHARA_CLASS_INS, "INSERT INTO characlass ( CharacterId, ClassIdx, Exp, Lvl, BorrowAction ) VALUES( ?,?,?,?,? );",
CONNECTION_BOTH );
prepareStatement( CHARA_CLASS_UP, "UPDATE characlass SET Exp = ?, Lvl = ? WHERE CharacterId = ? AND ClassIdx = ?;",
prepareStatement( CHARA_CLASS_UP, "UPDATE characlass SET Exp = ?, Lvl = ?, BorrowAction = ? WHERE CharacterId = ? AND ClassIdx = ?;",
CONNECTION_ASYNC );
prepareStatement( CHARA_CLASS_DEL, "DELETE FROM characlass WHERE CharacterId = ?;", CONNECTION_ASYNC );

View file

@ -91,6 +91,27 @@ namespace Sapphire::Data
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()
{
return m_data;

View file

@ -8,13 +8,8 @@
#include <spdlog/sinks/daily_file_sink.h>
// #include <iostream>
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
void Sapphire::Logger::init( const std::string& logPath )

View file

@ -39,8 +39,12 @@ float Util::distance2D( float x, float y, float x1, float y1 )
float Util::calcAngTo( float x, float y, float x1, float y1 )
{
float dx = x - x1;
float dy = y - y1;
float dx = x1 - x;
float dy = y1 - y;
if( dx == 0.0f && dy == 0.0f )
return 0.0f;
if( dy != 0.0f )
{
return atan2( dy, dx );
@ -55,6 +59,10 @@ float Util::calcAngFrom( float x, float y, float x1, float y1 )
{
float dx = x - x1;
float dy = y - y1;
if( dx == 0.0f && dy == 0.0f )
return 0.0f;
if( dy != 0.0f )
{
return atan2( dy, dx );
@ -80,6 +88,39 @@ uint8_t Util::floatToUInt8Rot( float val )
return static_cast< uint8_t >( 0x80 * ( ( val + PI ) ) / PI );
}
FFXIVARR_POSITION3 Util::getOffsettedPosition( const FFXIVARR_POSITION3& pos, float rot, float right, float up, float forward )
{
FFXIVARR_POSITION3 ret{ pos };
// height
ret.y += up;
// forward
float angle = rot + ( PI / 2 );
ret.x -= forward * cos( angle );
ret.z += forward * sin( angle );
// side
ret.x -= right * cos( rot );
ret.z += right * sin( rot );
return ret;
}
FFXIVARR_POSITION3 Util::getKnockbackPosition( const FFXIVARR_POSITION3& origin, const FFXIVARR_POSITION3& pos, float distance )
{
FFXIVARR_POSITION3 ret{ pos };
float from = Common::Util::calcAngFrom( origin.x, origin.z, pos.x, pos.z );
float angle = PI - from + ( PI / 2 );
angle = angle + ( PI / 2 );
ret.x -= distance * cos( angle );
ret.z += distance * sin( angle );
return ret;
}
FFXIVARR_POSITION3 Util::transform( const FFXIVARR_POSITION3& vector, const Matrix33& matrix )
{
FFXIVARR_POSITION3 dst{};
@ -143,3 +184,23 @@ float Util::trunc( float value, uint8_t digitsToRemain )
return std::floor( value * factor ) / factor;
}
float Util::length( const FFXIVARR_POSITION3& vec ) {
return std::sqrt( vec.x * vec.x + vec.y * vec.y + vec.z * vec.z );
}
FFXIVARR_POSITION3 Util::normalize( const FFXIVARR_POSITION3& vec ) {
float len = length( vec );
if( len == 0 ) return FFXIVARR_POSITION3();
return FFXIVARR_POSITION3{ vec.x / len, vec.y / len, vec.z / len };
}
float Util::dot( const FFXIVARR_POSITION3& vec1, const FFXIVARR_POSITION3& vec2 )
{
return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z;
}
FFXIVARR_POSITION3 Util::projectY( const FFXIVARR_POSITION3& vec )
{
return FFXIVARR_POSITION3{ vec.x, 0, vec.z };
}

View file

@ -29,6 +29,10 @@ namespace Sapphire::Common::Util
uint8_t floatToUInt8Rot( float val );
FFXIVARR_POSITION3 getOffsettedPosition( const FFXIVARR_POSITION3& pos, float rotation, float right, float up, float forward );
FFXIVARR_POSITION3 getKnockbackPosition( const FFXIVARR_POSITION3& origin, const FFXIVARR_POSITION3& pos, float distance );
template < typename T >
T clamp( T val, T minimum, T maximum )
{
@ -44,6 +48,14 @@ namespace Sapphire::Common::Util
FFXIVARR_POSITION3 transform( const FFXIVARR_POSITION3& vector, const Matrix33& matrix );
float eulerToDirection( const FFXIVARR_POSITION3& euler );
float length( const FFXIVARR_POSITION3& vec );
FFXIVARR_POSITION3 normalize( const FFXIVARR_POSITION3& vec );
float dot( const FFXIVARR_POSITION3& vec1, const FFXIVARR_POSITION3& vec2 );
FFXIVARR_POSITION3 projectY( const FFXIVARR_POSITION3& vec );
}
#endif

View file

@ -7,8 +7,17 @@ inline bool FFXIVARR_POSITION3::operator == ( const FFXIVARR_POSITION3& target )
return x == target.x && y == target.y && z == target.z;
}
FFXIVARR_POSITION3 FFXIVARR_POSITION3::operator - ( const FFXIVARR_POSITION3& target ) const
{
return FFXIVARR_POSITION3{ x - target.x, y - target.y, z - target.z };
}
inline bool Vector3::operator == ( const Vector3& target ) const
{
return x == target.x && y == target.y && z == target.z && reserve == target.reserve;
}
}
Vector3 Vector3::operator - ( const Vector3& target ) const
{
return Vector3{ x - target.x, y - target.y, z - target.z };
}

View file

@ -2,12 +2,14 @@
namespace Sapphire::Common
{
// todo: get rid of this struct and use an actual vector 3 class
struct FFXIVARR_POSITION3
{
float x;
float y;
float z;
inline bool operator == ( const FFXIVARR_POSITION3& target ) const;
FFXIVARR_POSITION3 operator - ( const FFXIVARR_POSITION3& target ) const;
};
struct Vector3
@ -17,6 +19,8 @@ namespace Sapphire::Common
float z;
float reserve;
inline bool operator == ( const Vector3& target ) const;
inline bool operator == ( const FFXIVARR_POSITION3& target ) const;
Vector3 operator - ( const Vector3& target ) const;
};
struct Matrix33

View file

@ -11,14 +11,8 @@
using namespace Sapphire;
using namespace Sapphire::Common;
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
DbManager::DbManager( const std::string& host, const std::string& database, const std::string& user, const std::string& pw, uint16_t port ) :

View file

@ -9,14 +9,8 @@
Sapphire::Common::Util::CrashHandler crashHandler;
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem>
namespace filesys = std::filesystem;
#else
#include <experimental/filesystem>
namespace filesys = std::experimental::filesystem;
#endif
#include <fstream>
#include <streambuf>

View file

@ -17,10 +17,7 @@ public:
return;
uint32_t duration = ( sourceChara->getAsPlayer()->getTp() / 50 ) * 1000;
action.getEffectbuilder()->applyStatusEffect( sourceChara, 50, 30 );
sourceChara->getAsPlayer()->addStatusEffectByIdIfNotExist( 50, duration, *sourceChara, 30 );
action.getActionResultBuilder()->applyStatusEffectSelf( 50, duration, 30, false );
sourceChara->getAsPlayer()->setTp( 0 );
}
};

View file

@ -22,6 +22,7 @@ public:
auto teleportQuery = pPlayer->getTeleportQuery();
if( pPlayer->getCurrency( Common::CurrencyType::Gil ) < teleportQuery.cost ||
teleportQuery.useAetheryteTicket && !pPlayer->removeItem( 7569 ) ||
teleportQuery.targetAetheryte == 0 )
{
action.interrupt();

View file

@ -0,0 +1,47 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
#include <Action/Action.h>
#include <StatusEffect/StatusEffect.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class ActionInnerBeast : public Sapphire::ScriptAPI::ActionScript
{
public:
ActionInnerBeast() : Sapphire::ScriptAPI::ActionScript( 49 )
{
}
static constexpr auto Potency = 300;
void onExecute( Sapphire::World::Action::Action& action ) override
{
auto pPlayer = action.getSourceChara()->getAsPlayer();
auto pSource = action.getSourceChara();
auto pTarget = action.getHitChara();
auto pActionBuilder = action.getActionResultBuilder();
if( !pPlayer || !pActionBuilder )
return;
if( auto status = pPlayer->getStatusEffectById( Defiance ); status )
status->setModifier( Common::ParamModifier::DamageDealtPercent, 0 );
auto dmg = action.calcDamage( Potency );
pActionBuilder->damage( pSource, pTarget, dmg.first, dmg.second );
pActionBuilder->heal( pTarget, pSource, dmg.first, Common::CalcResultType::TypeRecoverHp, Common::ActionResultFlag::EffectOnSource );
pActionBuilder->applyStatusEffectSelf( InnerBeast, 15000, 0, { StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } );
if( !pPlayer->hasStatusEffect( Unchained ) )
{
if( auto status = pPlayer->getStatusEffectById( Defiance ); status )
status->setModifier( Common::ParamModifier::DamageDealtPercent, -25 );
}
}
};
EXPOSE_SCRIPT( ActionInnerBeast );

View file

@ -0,0 +1,33 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
#include <Action/Action.h>
#include <StatusEffect/StatusEffect.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class ActionUnchained : public Sapphire::ScriptAPI::ActionScript
{
public:
ActionUnchained() : Sapphire::ScriptAPI::ActionScript( 50 )
{
}
void onExecute( Sapphire::World::Action::Action& action ) override
{
auto pPlayer = action.getSourceChara()->getAsPlayer();
auto pActionBuilder = action.getActionResultBuilder();
if( !pPlayer || !pActionBuilder )
return;
if( auto status = pPlayer->getStatusEffectById( Defiance ); status )
status->setModifier( Common::ParamModifier::DamageDealtPercent, 0 );
pActionBuilder->applyStatusEffectSelf( Unchained, 20000, 0 );
}
};
EXPOSE_SCRIPT( ActionUnchained );

View file

@ -62,10 +62,18 @@ public:
// eventParam4 (or params[1] if using EventPlay8, which is actually used on retail) anything bigger than 1 will show select instance menu item
eventMgr().playScene( player, eventId, 0, 1, { 1, 2 }, [ this ]( Entity::Player& player, const Event::SceneResult& result )
{
if( result.numOfResults == 1 ) // set homepoint
if( result.numOfResults == 1 )
{
player.setHomepoint( result.eventId & 0xFFFF );
eventMgr().sendEventNotice( player, result.eventId, 2, 0xEA, 0, 0 );
auto cmd = result.getResult( 0 );
if( cmd == 1 ) // set homepoint
{
player.setHomepoint( result.eventId & 0xFFFF );
eventMgr().sendEventNotice( player, result.eventId, 2, 0xEA, 0, 0 );
}
else if( cmd == 5 )
{
//TODO: Housing teleport selection
}
}
else if( result.numOfResults == 2 ) // aethernet access
{

View file

@ -26,7 +26,7 @@ public:
eventMgr().playScene( player, eventId, 1, 0, [this, eobj]( Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) != 1 )
player.exitInstance();
playerMgr().onExitInstance( player );
} );
}
};

View file

@ -1,5 +1,8 @@
#include <ScriptObject.h>
#include <Territory/QuestBattle.h>
#include <Actor/Player.h>
#include <Actor/GameObject.h>
#include <Actor/BNpc.h>
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);
}
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
{
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,
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

@ -823,7 +823,7 @@ private:
quest.setSeq( Seq6 );
eventMgr().sendEventNotice( player, getId(), 4, 0 );
playerMgr().sendUrgent( player, "QuestBattle Unimplemented, skipping..." );
player.exitInstance();
playerMgr().onExitInstance( player );
travelToPoprange( player, Poprange3, false );
}

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

@ -0,0 +1,105 @@
// 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: ClsCnj998_00133
// Quest Name: Way of the Conjurer
// Quest ID: 65669
// Start NPC: 1000323 (Madelle)
// End NPC: 1000692 (E-Sumi-Yan)
using namespace Sapphire;
class ClsCnj998 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// UI8AL
/// Countable Num: 1 Seq: 255 Event: 1 Listener: 1000692
// 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 = 1000323;// Madelle ( Pos: -234.028000 -4.000220 -11.062800 Teri: 133 )
static constexpr auto Actor1 = 1000692;// E-sumi-yan ( Pos: -258.808014 -5.773500 -27.237400 Teri: 133 )
static constexpr auto Classjob = 6;
static constexpr auto GearsetUnlock = 1905;
static constexpr auto LogmessageMonsterNotePageUnlock = 1009;
static constexpr auto UnlockImageClassCnj = 25;
public:
ClsCnj998() : Sapphire::ScriptAPI::QuestScript( 65669 ){};
~ClsCnj998() = 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;
}
}
}
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( &ClsCnj998::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )// accept quest
{
quest.setSeq( SeqFinish );
}
}
//////////////////////////////////////////////////////////////////////
void Scene00001( World::Quest& quest, Entity::Player& player )
{
eventMgr().playQuestScene( player, getId(), 1, FADE_OUT | HIDE_UI, bindSceneReturn( &ClsCnj998::Scene00001Return ) );
}
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId() );
player.setLevelForClass( 1, Sapphire::Common::ClassJob::Conjurer );
player.addGearSet();
}
}
};
EXPOSE_SCRIPT( ClsCnj998 );

View file

@ -0,0 +1,72 @@
// 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: ClsCnj999_00182
// Quest Name: So You Want to Be a Conjurer
// Quest ID: 65718
// Start NPC: 1000323 (Madelle)
// End NPC: 1000323 (Madelle)
using namespace Sapphire;
class ClsCnj999 : public Sapphire::ScriptAPI::QuestScript
{
private:
// Basic quest information
// Quest vars / flags used
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
enum Sequence : uint8_t
{
};
static constexpr auto Actor0 = 1000323;
// Entities found in the script data of the quest
public:
ClsCnj999() : Sapphire::ScriptAPI::QuestScript( 65718 ){};
~ClsCnj999() = default;
//////////////////////////////////////////////////////////////////////
// Event Handlers
void onTalk( World::Quest& quest, Entity::Player& player, uint64_t actorId ) override
{
switch( actorId )
{
case Actor0:
{
Scene00000( quest, player );
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, HIDE_HOTBAR, bindSceneReturn( &ClsCnj999::Scene00000Return ) );
}
void Scene00000Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
if( result.getResult( 0 ) == 1 )
{
player.finishQuest( getId(), 0 );
}
}
};
EXPOSE_SCRIPT( ClsCnj999 );

View file

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

View file

@ -64,6 +64,7 @@ private:
static constexpr auto Eobject2 = 2005950;
static constexpr auto EventActionGatherMiddle = 7;
static constexpr auto Item0 = 2001728;
static constexpr auto Item0Icon = 21223;
static constexpr auto LocActor1 = 1013860;
static constexpr auto LocActor2 = 1013870;
static constexpr auto LocActor3 = 1013871;
@ -207,7 +208,7 @@ public:
private:
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 )
{

View file

@ -56,6 +56,7 @@ private:
static constexpr auto Actor7 = 1013961;//Goblin Trader (Seq5)
static constexpr auto BindActor1 = 5896328;
static constexpr auto Item0 = 2001729;
static constexpr auto Item0Icon = 25919;
static constexpr auto LocActor1 = 1013954;
static constexpr auto LocActor2 = 1013955;
static constexpr auto LocBgm1 = 313;
@ -390,7 +391,7 @@ private:
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.setUI8BH( 0 );
}

View file

@ -101,6 +101,8 @@ private:
static constexpr auto Item4 = 2001029;
static constexpr auto Item5 = 2001038;
static constexpr auto Item6 = 2001047;
static constexpr auto Item0Icon = 21003;
static constexpr auto Item3Icon = 26002;
static constexpr auto LocAction0 = 858;
static constexpr auto LocAction1 = 995;
static constexpr auto LocAction2 = 936;
@ -109,6 +111,7 @@ private:
static constexpr auto Ritem0 = 2049;//Madman's Whispering Rod
static constexpr auto Ritem1 = 2046;//Unfinished Thyrus
static constexpr auto Ritem2 = 6267;//Radz-at-Han Quenching Oil
static constexpr auto Ritem1Icon = 32627;
public:
JobWhm001() : Sapphire::ScriptAPI::QuestScript( 66660 ){};
@ -327,7 +330,7 @@ private:
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BH( 1 );
eventMgr().sendEventNotice( player, getId(), 0, 0 );//TODO:Item Icon
eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setSeq( Seq2 );
}
@ -469,7 +472,7 @@ private:
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.setUI8BH( 1 );
}
@ -497,7 +500,7 @@ private:
void Scene00015Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BH( 0 );
eventMgr().sendEventNotice( player, getId(), 8, 0 );//TODO: Item Icon?
eventMgr().sendNotice( player, getId(), 8, { Ritem1Icon } );
player.addItem( Ritem1 );
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 Ritem2 = 3463;
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;
public:
@ -214,7 +218,7 @@ private:
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 );
quest.setUI8AL( 1 );
quest.setUI8CH( 0 );
@ -241,7 +245,7 @@ private:
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 );
quest.setUI8BH( 1 );
quest.setUI8CL( 0 );
@ -268,7 +272,7 @@ private:
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 );
quest.setUI8BL( 1 );
quest.setUI8DH( 0 );
@ -436,7 +440,7 @@ private:
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 );
quest.setSeq( SeqFinish );
quest.setUI8BH( 0 );

View file

@ -179,7 +179,8 @@ private:
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
player.addStatusEffectById( Status0, 0, player, Transformation0 );
// todo - fix status effect without action?
//player.addStatusEffectById( Status0, 0, player, Transformation0 );
eventMgr().sendEventNotice( player, getId(), 0, 0 );
quest.setSeq( Seq2 );
}
@ -294,7 +295,7 @@ private:
void Scene00010Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
player.addStatusEffectById( Status0, 0, player, Transformation0 );
// player.addStatusEffectById( Status0, 0, player, Transformation0 );
}
//////////////////////////////////////////////////////////////////////

View file

@ -54,6 +54,7 @@ private:
static constexpr auto Eobject1 = 2000017;//Decaying Tree (South)
static constexpr auto Eobject2 = 2000018;//Decaying Tree (East)
static constexpr auto Item0 = 2000061;
static constexpr auto Item0Icon = 20661;
static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq1Eobject0 = 1;
static constexpr auto Seq1Eobject0Useitemno = 99;
@ -184,7 +185,7 @@ public:
private:
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 )
{

View file

@ -45,6 +45,8 @@ private:
static constexpr auto Enemy0 = 54;//Hornet Swarm (INCORRECT: 57)
static constexpr auto Item0 = 2000099;
static constexpr auto Item1 = 2000094;
static constexpr auto Item0Icon = 22623;
static constexpr auto Item1Icon = 24403;
static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq2Actor0 = 1;
static constexpr auto Seq2Actor0Npctradeno = 99;
@ -88,7 +90,7 @@ public:
{
quest.setUI8BH( quest.getUI8BH() + 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 )
{

View file

@ -53,6 +53,7 @@ private:
static constexpr auto Eventrange0 = 3841476;
static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000192;
static constexpr auto Item0Icon = 22627;
public:
SubFst067() : Sapphire::ScriptAPI::QuestScript( 65919 ){};
@ -182,7 +183,7 @@ public:
private:
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 )
{
quest.setUI8AL( 0 );

View file

@ -39,6 +39,7 @@ private:
static constexpr auto Eobject0 = 2000685;//Well-worn Fishing Rod
static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000185;
static constexpr auto Item0Icon = 38201;
static constexpr auto Seq0Actor0 = 0;
static constexpr auto Seq1Eobject0 = 1;
static constexpr auto Seq1Eobject0Eventactionno = 99;
@ -169,7 +170,7 @@ private:
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.setSeq( SeqFinish );
}

View file

@ -55,6 +55,7 @@ private:
static constexpr auto EventActionSearchMiddle = 3;
static constexpr auto Item0 = 2000616;
static constexpr auto Item1 = 2000617;
static constexpr auto Item1Icon = 20005;
public:
GaiUsa803() : Sapphire::ScriptAPI::QuestScript( 66323 ){};
@ -140,7 +141,7 @@ public:
private:
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 )
{
quest.setUI8BH( quest.getUI8DH() );

View file

@ -55,6 +55,7 @@ private:
static constexpr auto Item0 = 2000720;
static constexpr auto Item1 = 2000721;
static constexpr auto Item2 = 2000722;
static constexpr auto Item0Icon = 26002;
public:
GaiUsb808() : Sapphire::ScriptAPI::QuestScript( 66453 ){};
@ -162,7 +163,7 @@ private:
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.setSeq( Seq3 );
}

View file

@ -61,6 +61,8 @@ private:
static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000963;
static constexpr auto Item1 = 2000965;
static constexpr auto Item0Icon = 22614;
static constexpr auto Item1Icon = 21452;
static constexpr auto Poprange0 = 3884000;
static constexpr auto Territorytype0 = 204;
@ -263,7 +265,7 @@ private:
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.setSeq( Seq2 );
}
@ -393,7 +395,7 @@ private:
{
quest.setSeq( Seq4 );
quest.setUI8BH( 1 );
eventMgr().sendEventNotice( player, getId(), 2, 0 /*1, Item1*/ );//TODO:Item Icon Event Notice
eventMgr().sendNotice( player, getId(), 2, { Item1Icon } );
}
//////////////////////////////////////////////////////////////////////

View file

@ -96,8 +96,6 @@ class SubFst009 : public Sapphire::ScriptAPI::QuestScript
{
if (result.getResult(0) == 1)
Scene00100(quest, player);
else
Scene00099(quest, player);
}
//////////////////////////////////////////////////////////////////////

View file

@ -67,8 +67,11 @@ public:
case Enemy0:
{
auto currentKC = quest.getUI8AL();
quest.setUI8AL( currentKC + 1 );
eventMgr().sendEventNotice( player, getId(), 0, 2, currentKC + 1, 6 );
if( currentKC < 6 )
{
quest.setUI8AL( currentKC + 1 );
eventMgr().sendEventNotice( player, getId(), 0, 2, currentKC + 1, 6 );
}
if( currentKC + 1 >= 6 )
quest.setSeq( SeqFinish );

View file

@ -55,6 +55,8 @@ private:
static constexpr auto EventActionSearch = 1;
static constexpr auto Item0 = 2000669;
static constexpr auto Item1 = 2000929;
static constexpr auto Item0Icon = 27241;
static constexpr auto Item1Icon = 22301;
public:
GaiUsb406() : Sapphire::ScriptAPI::QuestScript( 66398 ){};
@ -143,7 +145,11 @@ private:
{
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 )
{
quest.setUI8BH( quest.getUI8BL() );

View file

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

View file

@ -145,6 +145,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 0, 0 );
quest.setUI8AL( 1 );
quest.setBitFlag8( 1, true );
checkQuestCompletion( quest, player, 1 );
}
@ -159,6 +160,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 1, 0 );
quest.setUI8BH( 1 );
quest.setBitFlag8( 2, true );
checkQuestCompletion( quest, player, 1 );
}
@ -173,6 +175,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 2, 0 );
quest.setUI8BL( 1 );
quest.setBitFlag8( 3, true );
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 Actor2 = 1000937; // Godebert ( Pos: -12.222500 44.998798 -251.850006 Teri: 128 )
static constexpr auto Item0 = 2000455;
static constexpr auto Item0Icon = 25906;
public:
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 )
{
eventMgr().sendEventNotice( player, getId(), 0, 0 ); // TODO: Show item icon
eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setUI8BH( 1 );
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 Actor3 = 1000947; // Lyngsath ( Pos: -54.642601 43.991699 -151.201996 Teri: 128 )
static constexpr auto Item0 = 2000451;
static constexpr auto Item0Icon = 25919;
public:
SubSea008() : Sapphire::ScriptAPI::QuestScript( 65654 ){};
@ -58,7 +59,8 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
{
case Actor0:
{
Scene00000( quest, player );
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break;
}
case Actor1:
@ -71,12 +73,14 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
}
case Actor2:
{
Scene00003( quest, player );
if( quest.getSeq() == Seq2 )
Scene00003( quest, player );
break;
}
case Actor3:
{
Scene00005( quest, player );
if( quest.getSeq() == Seq2 )
Scene00005( quest, player );
break;
}
}
@ -137,7 +141,7 @@ private:
void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
quest.setUI8BL( 1 );
eventMgr().sendEventNotice( player, getId(), 0, 0 ); //TODO: add item icon
eventMgr().sendNotice( player, getId(), 0, { Item0Icon } );
quest.setSeq( Seq2 );
}
@ -167,6 +171,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 1, 0 );
quest.setUI8AL( 1 );
quest.setBitFlag8( 1, true );
checkQuestCompletion( quest, player, 1 );
}
@ -197,6 +202,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 2, 0 );
quest.setUI8BH( 1 );
quest.setBitFlag8( 2, true );
checkQuestCompletion( quest, player, 1 );
}

View file

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

@ -0,0 +1,22 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class StatusEffectDefiance : public Sapphire::ScriptAPI::StatusEffectScript
{
public:
StatusEffectDefiance() : Sapphire::ScriptAPI::StatusEffectScript( 91 )
{
}
void onExpire( Entity::Chara& actor ) override
{
actor.removeStatusEffectById( { Unchained, Wrath, WrathII, WrathIII, WrathIV, Infuriated } );
}
};
EXPOSE_SCRIPT( StatusEffectDefiance );

View file

@ -0,0 +1,24 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/CommonAction.h>
#include <StatusEffect/StatusEffect.h>
using namespace Sapphire;
using namespace Sapphire::World::Action;
class StatusEffectUnchained : public Sapphire::ScriptAPI::StatusEffectScript
{
public:
StatusEffectUnchained() : Sapphire::ScriptAPI::StatusEffectScript( 92 )
{
}
void onExpire( Entity::Chara& actor ) override
{
if( auto status = actor.getStatusEffectById( Defiance ); status )
status->setModifier( Common::ParamModifier::DamageDealtPercent, -25 );
}
};
EXPOSE_SCRIPT( StatusEffectUnchained );

View file

@ -1,7 +1,7 @@
{
"7": {
"name": "Attack",
"potency": 0,
"potency": 110,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
@ -16,7 +16,7 @@
},
"8": {
"name": "Shot",
"potency": 0,
"potency": 100,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,

View file

@ -40,6 +40,8 @@ struct StatusModifier
struct StatusEntry
{
uint16_t id;
uint32_t duration;
uint32_t flag;
std::vector< StatusModifier > modifiers;
};
@ -76,6 +78,8 @@ void to_json( nlohmann::ordered_json& j, const StatusEntry& statusEntry )
{
j = nlohmann::ordered_json{
{ "id", statusEntry.id },
{ "duration", statusEntry.duration },
{ "flag", statusEntry.flag },
{ "modifiers", statusEntry.modifiers }
};
}
@ -157,22 +161,20 @@ int main( int argc, char* argv[] )
Logger::fatal( "Error setting up EXD data " );
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, std::vector< uint32_t > > traversedCombos;
auto total = idList.size();
auto total = actionList.size();
int cursor = 0;
for( auto id : idList )
for( const auto& [ id, action ] : actionList )
{
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 )
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 );
if( action )
{

View file

@ -89,11 +89,11 @@ int main( int argc, char* argv[] )
// CFC list
{
auto idList = g_exdDataGen.getIdList< Excel::ContentFinderCondition >();
auto cfcList = g_exdDataGen.getRows< Excel::ContentFinderCondition >();
std::stringstream cfcOutputStream;
auto total = idList.size();
auto total = cfcList.size();
int cursor = 0;
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
<< "| --- | --- | --- | --- | --- |" << std::endl;
for( auto id : idList )
for( const auto& [ id, cfc ] : cfcList )
{
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 )
@ -127,8 +127,6 @@ int main( int argc, char* argv[] )
if( id == 0 )
continue;
auto cfc = g_exdDataGen.getRow< Excel::ContentFinderCondition >( id );
if( cfc )
{
auto& cfcData = cfc->data();
@ -234,24 +232,22 @@ int main( int argc, char* argv[] )
teriTypeIntendedUseMap[ TheFeastArea ] = "TheFeastArea";
teriTypeIntendedUseMap[ PrivateEventArea ] = "PrivateEventArea";
auto idList = g_exdDataGen.getIdList< Excel::TerritoryType >();
auto teriList = g_exdDataGen.getRows< Excel::TerritoryType >();
std::stringstream teritypeOutputStream;
teritypeOutputStream << "| ID | Place Name | Name | Intended Use |" << std::endl
<< "| --- | --- | --- | --- |" << std::endl;
auto total = idList.size();
auto total = teriList.size();
int cursor = 0;
for( auto id : idList )
for( const auto& [ id, teriType ] : teriList )
{
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} teritypes of {} ({:.2f}%)", cursor, total, done );
auto teriType = g_exdDataGen.getRow< Excel::TerritoryType >( id );
if( teriType )
{
auto& teriTypeData = teriType->data();
@ -291,24 +287,22 @@ int main( int argc, char* argv[] )
// class/job list
{
auto idList = g_exdDataGen.getIdList< Excel::ClassJob >();
auto classJobList = g_exdDataGen.getRows< Excel::ClassJob >();
std::stringstream classjobOutputStream;
classjobOutputStream << "| ID | Name | Short | Main Class |" << std::endl
<< "| --- | --- | --- | --- |" << std::endl;
auto total = idList.size();
auto total = classJobList.size();
int cursor = 0;
for( auto id : idList )
for( const auto& [ id, classJob ] : classJobList )
{
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} classjobs of {} ({:.2f}%)", cursor, total, done );
auto classJob = g_exdDataGen.getRow< Excel::ClassJob >( id );
if( classJob )
{
auto& classJobData = classJob->data();
@ -338,7 +332,7 @@ int main( int argc, char* argv[] )
// achievement list
{
auto idList = g_exdDataGen.getIdList< Excel::Achievement >();
auto achvList = g_exdDataGen.getRows< Excel::Achievement >();
enum class Type : uint8_t
{
@ -394,17 +388,15 @@ int main( int argc, char* argv[] )
achvOutputStream << "| ID | Name | Type (Subtype) | Description |" << std::endl
<< "| --- | --- | --- | --- |" << std::endl;
auto total = idList.size();
auto total = achvList.size();
int cursor = 0;
for( auto id : idList )
for( const auto& [ id, pAchv ] : achvList )
{
auto done = ( cursor++ / static_cast< float >( total ) ) * 100.f;
if( cursor % 50 == 0 && cursor > 0 )
Logger::info( "Processing {} achievements of {} ({:.2f}%)", cursor, total, done );
auto pAchv = g_exdDataGen.getRow< Excel::Achievement >( id );
if( pAchv )
{
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,80 @@
#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 );
}
pNaviProvider->syncPosToChara( bnpc );
if( distance < ( bnpc.getNaviTargetReachedDistance() + pHatedActor->getRadius() ) )
{
// todo: dont turn if facing
if( !bnpc.hasFlag( Entity::TurningDisabled ) )
bnpc.face( pHatedActor->getPos() );
if( !bnpc.checkAction() )
bnpc.processGambits( tickCount );
// in combat range. ATTACK!
if( !bnpc.hasFlag( Entity::BNpcFlag::AutoAttackDisabled ) )
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

@ -0,0 +1,52 @@
#include <cstdint>
#include <ForwardsZone.h>
#include "GambitTargetCondition.h"
#include "GambitRule.h"
using namespace Sapphire;
using namespace Sapphire::World;
AI::GambitRule::GambitRule( const GambitTargetConditionPtr targetCondition, Action::ActionPtr action, uint32_t coolDown ) :
m_targetCondition( targetCondition ),
m_pAction( std::move( action ) ),
m_lastExecutionMs( 0 ),
m_coolDownMs( coolDown ),
m_isEnabled( true )
{
}
void AI::GambitRule::toggleEnabled()
{
m_isEnabled = !m_isEnabled;
}
bool AI::GambitRule::isEnabled() const
{
return m_isEnabled;
}
uint64_t AI::GambitRule::getLastExecutionMs() const
{
return m_lastExecutionMs;
}
void AI::GambitRule::setLastExecutionMs( uint64_t lastExecution )
{
m_lastExecutionMs = lastExecution;
}
uint32_t AI::GambitRule::getCoolDown() const
{
return m_coolDownMs;
}
AI::GambitTargetConditionPtr AI::GambitRule::getGambitTargetCondition()
{
return m_targetCondition;
}
Action::ActionPtr AI::GambitRule::getActionPtr()
{
return m_pAction;
}

33
src/world/AI/GambitRule.h Normal file
View file

@ -0,0 +1,33 @@
#include <cstdint>
#include <ForwardsZone.h>
#include "GambitTargetCondition.h"
#pragma once
namespace Sapphire::World::AI
{
class GambitRule
{
public:
GambitRule( GambitTargetConditionPtr targetCondition, Action::ActionPtr action, uint32_t coolDown );
~GambitRule() = default;
bool isEnabled() const;
void toggleEnabled();
uint64_t getLastExecutionMs() const;
void setLastExecutionMs( uint64_t lastExecution );
uint32_t getCoolDown() const;
GambitTargetConditionPtr getGambitTargetCondition();
Action::ActionPtr getActionPtr();
private:
GambitTargetConditionPtr m_targetCondition;
Action::ActionPtr m_pAction;
uint32_t m_coolDownMs;
uint64_t m_lastExecutionMs;
bool m_isEnabled;
};
}

View file

@ -0,0 +1,65 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#pragma once
namespace Sapphire::World::AI
{
enum GambitTargetType : uint8_t
{
Self,
Player,
PlayerAndAlly,
Ally,
BNpc
};
class GambitTargetCondition
{
public:
GambitTargetCondition() = default;
virtual ~GambitTargetCondition() = default;
virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) { return false; };
Sapphire::Entity::CharaPtr getTarget() const { return m_pTarget; };
protected:
Sapphire::Entity::CharaPtr m_pTarget;
};
class TopHateTargetCondition : public GambitTargetCondition
{
public:
TopHateTargetCondition() = default;
bool isConditionMet( Sapphire::Entity::BNpc& src ) override
{
auto foundChara = src.hateListGetHighest();
if( foundChara )
{
m_pTarget = foundChara;
return true;
}
return false;
};
};
class HPSelfPctLessThanTargetCondition : public GambitTargetCondition
{
public:
HPSelfPctLessThanTargetCondition( uint8_t pct ) : m_HpPct( pct ) {};
bool isConditionMet( Sapphire::Entity::BNpc& src ) override
{
if( src.getHpPercent() < m_HpPct )
{
m_pTarget = src.getAsBNpc();
return true;
}
return false;
};
private:
uint8_t m_HpPct;
};
}

View file

@ -0,0 +1,271 @@
#include "TargetHelper.h"
#include <algorithm>
#include <Actor/BNpc.h>
#include <Actor/Chara.h>
#include <Actor/Player.h>
#include <Manager/PartyMgr.h>
#include <Manager/RNGMgr.h>
#include <Util/UtilMath.h>
#include <Service.h>
namespace Sapphire::World::AI
{
bool InsideRadiusFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) <= m_distance;
return m_negate ? !ret : ret;
}
bool OutsideRadiusFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) >= m_distance;
return m_negate ? !ret : ret;
}
bool PlayerFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = pTarget->isPlayer();
return m_negate ? !ret : ret;
}
bool AllyFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = false;
// todo: pets, companions, enpc
if( pSrc->isPlayer() )
{
auto pBNpcTarget = pTarget->getAsBNpc();
if( pBNpcTarget && pBNpcTarget->getEnemyType() == 0 )
ret = true;
else if( pTarget->isPlayer() )
ret = true;
}
else if( pSrc->isBattleNpc() )
{
auto pBNpcTarget = pTarget->getAsBNpc();
if( pBNpcTarget && pBNpcTarget->getEnemyType() == 0 )
ret = true;
else if( pTarget->isPlayer() )
ret = true;
}
return m_negate ? !ret : ret;
}
bool OwnBattalionFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
return false;
}
bool TankFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = pTarget->getRole() == Common::Role::Tank;
return m_negate ? !ret : ret;
}
bool HealerFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = pTarget->getRole() == Common::Role::Healer;
return m_negate ? !ret : ret;
}
bool DpsFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = true;
switch( pTarget->getRole() )
{
case Common::Role::Melee:
case Common::Role::RangedMagical:
case Common::Role::RangedPhysical:
ret = true;
break;
default:
ret = false;
break;
}
return m_negate ? !ret : ret;
}
bool HasStatusEffectFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
auto ret = pTarget->hasStatusEffect( m_statusId );
return m_negate ? !ret : ret;
}
bool TopAggroFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
auto pBNpc = pSrc->getAsBNpc();
bool ret = false;
if( pBNpc )
{
ret = pBNpc->hateListGetHighest() == pTarget;
}
return m_negate ? !ret : ret;
}
bool SecondAggroFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
auto pBNpc = pSrc->getAsBNpc();
bool ret = false;
if( pBNpc )
{
// todo: this is so dumb
auto hateList = pBNpc->getHateList();
std::vector sorted( hateList.begin(), hateList.end() );
std::sort( sorted.begin(), sorted.end(), []( Entity::HateListEntryPtr a, Entity::HateListEntryPtr b ) {
return a->m_hateAmount > b->m_hateAmount; } );
Entity::CharaPtr pChara = nullptr;
auto topIt = sorted.begin();
if( topIt != sorted.end() && ++topIt != sorted.end() )
pChara = topIt->get()->m_pChara;
ret = pChara == pTarget;
}
return m_negate ? !ret : ret;
}
bool PartyMemberFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
bool ret = false;
// todo: pets, companions, enpc
if( auto pPlayer = pSrc->getAsPlayer() )
{
if( auto pTargetPlayer = pTarget->getAsPlayer() )
{
ret = pPlayer->getPartyId() == pTargetPlayer->getPartyId();
}
else if( auto pBNpc = pTarget->getAsBNpc() )
{
ret = pBNpc->getBNpcType() == 0;
}
}
else if( auto pBNpc = pSrc->getAsBNpc() )
{
if( auto pTargetPlayer = pTarget->getAsPlayer() )
{
ret = pPlayer->getPartyId() == pTargetPlayer->getPartyId();
}
else if( auto pTargetBNpc = pTarget->getAsBNpc() )
{
ret = pBNpc->getBNpcType() == pTargetBNpc->getEnemyType();
}
}
return m_negate ? !ret : ret;
}
void Snapshot::createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
uint32_t count, bool fillWithRandom,
const std::vector< TargetSelectFilterPtr >& filters,
const std::vector< uint32_t >& exclude )
{
m_results.clear();
m_targetIds.clear();
auto& RNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
for( const auto& pActor : inRange )
{
auto pChara = pActor->getAsChara();
if( pChara == nullptr )
continue;
// exclude this character from the result set
auto excludeIt = std::find_if( exclude.begin(), exclude.end(),
[ pChara ]( uint32_t id ) { return pChara->getId() == id; }
);
if( excludeIt != exclude.end() )
continue;
bool matches = true;
for( const auto& filter : filters )
{
if( !filter->isApplicable( pSrc, pChara ) )
{
matches = false;
break;
}
}
if( matches )
{
CharaEntry entry{};
entry.m_entityId = pChara->getId();
entry.m_pos = pChara->getPos();
entry.m_rot = pChara->getRot();
m_results.push_back( entry );
if( m_results.size() == count ) break;
}
}
// fallback to fill with random entries if we dont have enough valid results
if( fillWithRandom && m_results.size() < count )
{
std::vector< Entity::CharaPtr > remaining;
for( const auto& pActor : inRange )
{
auto pChara = pActor->getAsChara();
if( pChara == nullptr )
continue;
auto excludeIt = std::find_if( exclude.begin(), exclude.end(),
[ pChara ]( uint32_t id ) { return pChara->getId() == id; }
);
if( excludeIt == exclude.end() && std::find_if( m_results.begin(), m_results.end(),
[ &pChara ]( CharaEntry entry ) { return entry.m_entityId == pChara->getId(); } ) == m_results.end() )
{
remaining.push_back( pChara );
}
}
while( m_results.size() < count && !remaining.empty() )
{
// idk
std::shuffle( remaining.begin(), remaining.end(), *RNGMgr.getRNGEngine() );
auto pChara = remaining.back();
CharaEntry entry{};
entry.m_entityId = pChara->getId();
entry.m_pos = pChara->getPos();
entry.m_rot = pChara->getRot();
m_results.emplace_back( entry );
remaining.pop_back();
}
}
// sort by distance at the end always
const auto& srcPos = pSrc->getPos();
std::sort( m_results.begin(), m_results.end(),
[ srcPos ]( CharaEntry l, CharaEntry r )
{
return Common::Util::distance( srcPos, l.m_pos ) < Common::Util::distance( srcPos, r.m_pos );
}
);
// we might want the target ids separately
m_targetIds.resize( m_results.size() );
for( auto i = 0; i < m_results.size(); ++i )
m_targetIds[ i ] = m_results[ i ].m_entityId;
}
const std::vector< Snapshot::CharaEntry >& Snapshot::getResults() const
{
return m_results;
}
const std::vector< uint32_t >& Snapshot::getTargetIds() const
{
return m_targetIds;
}
void Snapshot::clearResults()
{
m_results.clear();
m_targetIds.clear();
}
};// namespace Sapphire::World::AI

249
src/world/AI/TargetHelper.h Normal file
View file

@ -0,0 +1,249 @@
#ifndef _TARGETHELPER_H
#define _TARGETHELPER_H
#include <memory>
#include <unordered_map>
#include <set>
#include <vector>
#include <ForwardsZone.h>
namespace Sapphire::World::AI
{
//
// Filters
//
class TargetSelectFilter :
public std::enable_shared_from_this< TargetSelectFilter >
{
public:
enum class Type
{
InsideRadius,
OutsideRadius,
Player,
Ally,
OwnBattalion,
Tank,
Healer,
Dps,
HasStatusEffect,
TopAggro,
SecondAggro,
PartyMember,
AllianceA,
AllianceB,
AllianceC
};
protected:
Type m_type;
bool m_negate{ false };
public:
TargetSelectFilter( Type type, bool negate ) :
m_type( type ),
m_negate( negate )
{
}
virtual ~TargetSelectFilter()
{
}
virtual bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
{
return false;
};
bool isNegate() const
{
return m_negate;
}
};
using TargetSelectFilterPtr = std::shared_ptr< TargetSelectFilter >;
class InsideRadiusFilter : public TargetSelectFilter
{
private:
float m_distance{ 0 };
public:
InsideRadiusFilter( float distance, bool negate ) :
TargetSelectFilter( Type::InsideRadius, negate ),
m_distance( distance )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class OutsideRadiusFilter : public TargetSelectFilter
{
private:
float m_distance{ 0 };
public:
OutsideRadiusFilter( float distance, bool negate ) :
TargetSelectFilter( Type::OutsideRadius, negate ),
m_distance( distance )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class PlayerFilter : public TargetSelectFilter
{
public:
PlayerFilter( bool negate ) :
TargetSelectFilter( Type::Player, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class AllyFilter : public TargetSelectFilter
{
public:
AllyFilter( bool negate ) :
TargetSelectFilter( Type::Ally, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class OwnBattalionFilter : public TargetSelectFilter
{
public:
OwnBattalionFilter( bool negate ) :
TargetSelectFilter( Type::OwnBattalion, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class TankFilter : public TargetSelectFilter
{
public:
TankFilter( bool negate ) :
TargetSelectFilter( Type::Tank, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class HealerFilter : public TargetSelectFilter
{
public:
HealerFilter( bool negate ) :
TargetSelectFilter( Type::Healer, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class DpsFilter : public TargetSelectFilter
{
public:
DpsFilter( bool negate ) :
TargetSelectFilter( Type::Dps, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class HasStatusEffectFilter : public TargetSelectFilter
{
private:
uint32_t m_statusId{ 0 };
public:
HasStatusEffectFilter( uint32_t statusId, bool negate ) :
TargetSelectFilter( Type::HasStatusEffect, negate ),
m_statusId( statusId )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class TopAggroFilter : public TargetSelectFilter
{
public:
TopAggroFilter( bool negate ) :
TargetSelectFilter( Type::TopAggro, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class SecondAggroFilter : public TargetSelectFilter
{
public:
SecondAggroFilter( bool negate ) :
TargetSelectFilter( Type::SecondAggro, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
class PartyMemberFilter : public TargetSelectFilter
{
public:
PartyMemberFilter( bool negate ) :
TargetSelectFilter( Type::PartyMember, negate )
{
}
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
};
//
// Helpers
//
class Snapshot :
public std::enable_shared_from_this< Snapshot >
{
public:
struct CharaEntry
{
uint32_t m_entityId;
Common::FFXIVARR_POSITION3 m_pos;
float m_rot;
// todo: status effects?
};
using Results = std::vector< CharaEntry >;
using TargetIds = std::vector< uint32_t >;
private:
std::vector< CharaEntry > m_results;
std::vector< uint32_t > m_targetIds;
public:
Snapshot() {}
void createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
uint32_t count, bool fillWithRandom,
const std::vector< TargetSelectFilterPtr >& filters,
const std::vector< uint32_t >& exclude = {} );
// returns actors sorted by distance
const std::vector< CharaEntry >& getResults() const;
const std::vector< uint32_t >& getTargetIds() const;
void clearResults();
};
using SnapshotPtr = std::shared_ptr< Snapshot >;
}// namespace Sapphire::World::AI
#endif

View file

@ -16,6 +16,7 @@
#include "Manager/PlayerMgr.h"
#include "Manager/MgrUtil.h"
#include "Manager/TerritoryMgr.h"
#include "Session.h"
#include "Network/GameConnection.h"
@ -23,7 +24,7 @@
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Network/PacketWrappers/ActorControlSelfPacket.h"
#include "Network/PacketWrappers/ActorControlTargetPacket.h"
#include "Network/Util/PlayerUtil.h"
#include "Network/Util/PacketUtil.h"
#include <Logging/Logger.h>
@ -31,6 +32,8 @@
#include <Service.h>
#include "WorldServer.h"
#include "Job/Warrior.h"
using namespace Sapphire;
using namespace Sapphire::Common;
using namespace Sapphire::Network;
@ -65,6 +68,16 @@ uint32_t Action::Action::getId() const
return m_id;
}
uint32_t Action::Action::getResultId() const
{
return m_resultId;
}
std::shared_ptr< Excel::ExcelStruct< Excel::Action > > Action::Action::getActionData() const
{
return m_actionData;
}
bool Action::Action::init()
{
if( !m_actionData )
@ -78,14 +91,18 @@ bool Action::Action::init()
m_actionData = actionData;
}
auto teriMgr = Common::Service< Manager::TerritoryMgr >::ref();
auto zone = teriMgr.getTerritoryByGuId( m_pSource->getTerritoryId() );
m_resultId = zone->getNextActionResultId();
m_effectBuilder = make_EffectBuilder( m_pSource, getId(), m_requestId );
m_actionResultBuilder = make_ActionResultBuilder( m_pSource, getId(), m_resultId, m_requestId );
m_castTimeMs = static_cast< uint32_t >( m_actionData->data().CastTime * 100 );
m_recastTimeMs = static_cast< uint32_t >( m_actionData->data().RecastTime * 100 );
m_cooldownGroup = m_actionData->data().RecastGroup;
m_range = m_actionData->data().SelectRange;
m_effectRange = m_actionData->data().EffectRange;
m_effectWidth = m_actionData->data().EffectWidth;
m_category = static_cast< Common::ActionCategory >( m_actionData->data().Category );
m_castType = static_cast< Common::CastType >( m_actionData->data().EffectType );
m_aspect = static_cast< Common::ActionAspect >( m_actionData->data().AttackType );
@ -105,9 +122,9 @@ bool Action::Action::init()
{
case Common::ClassJob::Bard:
case Common::ClassJob::Archer:
case Common::ClassJob::Machinist:
m_range = 25;
break;
// anything that isnt ranged
default:
m_range = 3;
break;
@ -117,7 +134,7 @@ bool Action::Action::init()
m_primaryCostType = static_cast< Common::ActionPrimaryCostType >( m_actionData->data().CostType );
m_primaryCost = m_actionData->data().CostValue;
/*if( !m_actionData->targetArea )
if( !m_actionData->data().SelectGround )
{
// override pos to target position
// todo: this is kinda dirty
@ -129,7 +146,7 @@ bool Action::Action::init()
break;
}
}
}*/
}
// todo: add missing rows for secondaryCostType/secondaryCostType and rename the current rows to primaryCostX
@ -164,6 +181,16 @@ const Common::FFXIVARR_POSITION3& Action::Action::getPos() const
return m_pos;
}
void Action::Action::setRot( float rot )
{
m_rot = rot;
}
float Action::Action::getRot() const
{
return m_rot;
}
void Action::Action::setTargetId( uint64_t targetId )
{
m_targetId = targetId;
@ -241,34 +268,34 @@ bool Action::Action::update()
// todo: check if the target is still in range
}
uint64_t tickCount = Common::Util::getTimeMs();
uint32_t castTime = m_castTimeMs;
auto tickCount = static_cast< time_t >( Common::Util::getTimeMs() );
auto startTime = static_cast< time_t >( m_startTime );
uint64_t castTime = m_castTimeMs;
if( auto player = m_pSource->getAsPlayer() )
{
uint64_t lastActionTick = player->getLastActionTick();
auto lastActionTick = static_cast< time_t >( player->getLastActionTick() );
uint32_t lastTickMs = 0;
if( lastActionTick > 0 )
{
lastTickMs = static_cast< uint32_t >( std::difftime( static_cast< time_t >( tickCount ), static_cast< time_t >( lastActionTick ) ) );
lastTickMs = static_cast< uint32_t >( std::difftime( tickCount, lastActionTick ) );
if( lastTickMs > 100 ) //max 100ms
lastTickMs = 100;
}
player->setLastActionTick( tickCount );
uint32_t delayMs = 100 - lastTickMs;
uint64_t delayMs = 100 - lastTickMs;
castTime = ( m_castTimeMs + delayMs );
m_castTimeRestMs = static_cast< uint64_t >( m_castTimeMs ) -
static_cast< uint64_t >( std::difftime( static_cast< time_t >( tickCount ), static_cast< time_t >( m_startTime ) ) );
m_castTimeRestMs = static_cast< uint64_t >( m_castTimeMs ) - static_cast< uint64_t >( std::difftime( tickCount, startTime ) );
}
if( !hasCastTime() || std::difftime( static_cast< time_t >( tickCount ), static_cast< time_t >( m_startTime ) ) > castTime )
if( !hasCastTime() || std::difftime( tickCount, startTime ) > castTime )
{
execute();
return true;
}
if( m_pTarget == nullptr && m_targetId != 0 )
if( !m_pTarget && m_targetId != 0 )
{
// try to search for the target actor
for( const auto& actor : m_pSource->getInRangeActors( true ) )
@ -281,7 +308,7 @@ bool Action::Action::update()
}
}
if( m_pTarget != nullptr && !m_pTarget->isAlive() )
if( m_pTarget && !m_pTarget->isAlive() )
{
// interrupt the cast if target died
setInterrupted( Common::ActionInterruptType::RegularInterrupt );
@ -310,25 +337,25 @@ void Action::Action::start()
data.CastTime = static_cast< float >( m_castTimeMs ) / 1000.f;
data.Target = static_cast< uint32_t >( m_targetId );
data.TargetPos[ 0 ] = Common::Util::floatToUInt16( m_pSource->getPos().x );
data.TargetPos[ 1 ] = Common::Util::floatToUInt16( m_pSource->getPos().y );
data.TargetPos[ 2 ] = Common::Util::floatToUInt16( m_pSource->getPos().z );
data.Dir = m_pSource->getRot();
data.TargetPos[ 0 ] = Common::Util::floatToUInt16( m_pos.x );
data.TargetPos[ 1 ] = Common::Util::floatToUInt16( m_pos.y );
data.TargetPos[ 2 ] = Common::Util::floatToUInt16( m_pos.z );
data.Dir = m_rot;
server().queueForPlayers( m_pSource->getInRangePlayerIds( true ), castPacket );
server().queueForPlayers( m_pSource->getInRangePlayerIds( m_pSource->isPlayer() ), castPacket );
if( player )
{
player->setCondition( PlayerCondition::Casting );
}
}
// todo: m_recastTimeMs needs to be adjusted for player sks/sps
auto actionStartPkt = makeActorControlSelf( m_pSource->getId(), ActorControlType::ActionStart, m_cooldownGroup, getId(), m_recastTimeMs / 10 );
player->setRecastGroup( m_cooldownGroup, static_cast< float >( m_castTimeMs ) / 1000.f );
server().queueForPlayer( player->getCharacterId(), actionStartPkt );
if( player )
{
player->setRecastGroup( m_cooldownGroup, static_cast< float >( m_castTimeMs ) / 1000.f );
server().queueForPlayer( player->getCharacterId(), actionStartPkt );
}
onStart();
@ -362,7 +389,6 @@ void Action::Action::onStart()
void Action::Action::interrupt()
{
assert( m_pSource );
// things that aren't players don't care about cooldowns and state flags
if( m_pSource->isPlayer() )
{
@ -402,25 +428,20 @@ void Action::Action::onInterrupt()
void Action::Action::execute()
{
assert( m_pSource );
// subtract costs first, if somehow the caster stops meeting those requirements cancel the cast
if( !consumeResources() )
{
interrupt();
return;
}
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
if( hasCastTime() )
if( hasCastTime() && m_pSource->isPlayer() )
{
if( auto pPlayer = m_pSource->getAsPlayer(); pPlayer )
{
pPlayer->setLastActionTick( 0 );
pPlayer->removeCondition( PlayerCondition::Casting );
}
auto pPlayer = m_pSource->getAsPlayer();
pPlayer->setLastActionTick( 0 );
pPlayer->removeCondition( PlayerCondition::Casting );
}
if( isCorrectCombo() )
@ -430,13 +451,9 @@ void Action::Action::execute()
}
if( !hasClientsideTarget() )
{
buildEffects();
}
buildActionResults();
else if( auto player = m_pSource->getAsPlayer() )
{
scriptMgr.onEObjHit( *player, m_targetId, getId() );
}
// set currently casted action as the combo action if it interrupts a combo
// ignore it otherwise (ogcds, etc.)
@ -444,17 +461,13 @@ void Action::Action::execute()
{
// potential combo starter or correct combo from last action, must hit something to progress combo
if( !m_hitActors.empty() && ( !isComboAction() || isCorrectCombo() ) )
{
m_pSource->setLastComboActionId( getId() );
}
else // clear last combo action if the combo breaks
{
m_pSource->setLastComboActionId( 0 );
}
}
}
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?
auto wepDmg = 1.f;
@ -466,19 +479,19 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage(
auto role = player->getRole();
if( role == Common::Role::RangedMagical || role == Common::Role::Healer )
{
wepDmg = item->getMagicalDmg();
}
else
{
wepDmg = item->getPhysicalDmg();
}
// is auto attack
if( getId() == 7 || getId() == 8 )
return Math::CalcStats::calcAutoAttackDamage( *m_pSource->getAsPlayer() );
}
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;
@ -489,41 +502,38 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcHealing
auto role = player->getRole();
if( role == Common::Role::RangedMagical || role == Common::Role::Healer )
{
wepDmg = item->getMagicalDmg();
}
else
{
wepDmg = item->getPhysicalDmg();
}
}
return Math::CalcStats::calcActionHealing( *m_pSource, potency, wepDmg );
}
void Action::Action::buildEffects()
void Action::Action::buildActionResults()
{
snapshotAffectedActors( m_hitActors );
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
auto hasLutEntry = hasValidLutEntry();
auto hasScript = scriptMgr.onExecute( *this );
if( !scriptMgr.onExecute( *this ) && !hasLutEntry )
if( !hasScript && !hasLutEntry )
{
if( auto player = m_pSource->getAsPlayer() )
{
Manager::PlayerMgr::sendUrgent( *player, "missing lut entry for action#{}", getId() );
}
return;
}
Network::Util::Player::sendHudParam( *m_pSource->getAsPlayer() );
if( !hasScript )
m_enableGenericHandler = true;
if( !hasLutEntry || m_hitActors.empty() )
Network::Util::Packet::sendHudParam( *m_pSource );
if( !m_enableGenericHandler || !hasLutEntry || m_hitActors.empty() )
{
// send any effect packet added by script or an empty one just to play animation for other players
m_effectBuilder->buildAndSendPackets( m_hitActors );
m_actionResultBuilder->sendActionResults( {} );
return;
}
@ -544,14 +554,14 @@ void Action::Action::buildEffects()
if( m_lutEntry.potency > 0 )
{
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.comboPotency : m_lutEntry.potency );
m_effectBuilder->damage( m_pSource, actor, dmg.first, dmg.second );
m_actionResultBuilder->damage( m_pSource, actor, dmg.first, dmg.second );
if( dmg.first > 0 )
actor->onActionHostile( m_pSource );
if( isCorrectCombo() && shouldApplyComboSucceedEffect )
{
m_effectBuilder->comboSucceed( m_pSource );
m_actionResultBuilder->comboSucceed( m_pSource );
shouldApplyComboSucceedEffect = false;
}
@ -560,46 +570,100 @@ void Action::Action::buildEffects()
if( m_lutEntry.curePotency > 0 ) // actions with self heal
{
auto heal = calcHealing( m_lutEntry.curePotency );
m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource );
m_actionResultBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionResultFlag::EffectOnSource );
}
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
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_effectBuilder->startCombo( m_pSource, getId() ); // this is on all targets hit
}
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_effectBuilder->heal( actor, actor, heal.first, heal.second );
m_actionResultBuilder->heal( actor, actor, heal.first, heal.second );
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource );
shouldRestoreMP = false;
}
}
else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
{
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource );
shouldRestoreMP = false;
}
}
m_effectBuilder->buildAndSendPackets( m_hitActors );
// If we hit an enemy
if( !m_hitActors.empty() && getHitChara()->getObjKind() != m_pSource->getObjKind() )
{
m_pSource->removeStatusEffectByFlag( Common::StatusEffectFlag::RemoveOnSuccessfulHit );
}
handleJobAction();
handleStatusEffects();
m_actionResultBuilder->sendActionResults( m_hitActors );
// TODO: disabled, reset kills our queued actions
// at this point we're done with it and no longer need it
// m_effectBuilder.reset();
}
void Action::Action::handleStatusEffects()
{
auto pActionBuilder = getActionResultBuilder();
if( !pActionBuilder )
return;
if( isComboAction() && !isCorrectCombo() )
return;
// handle caster statuses
if( !m_lutEntry.statuses.caster.empty() )
{
for( auto& status : m_lutEntry.statuses.caster )
{
pActionBuilder->applyStatusEffectSelf( status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true );
}
}
// handle hit actor statuses
if( !m_lutEntry.statuses.target.empty() && !m_hitActors.empty() )
{
for( auto& actor : m_hitActors )
{
for( auto& status : m_lutEntry.statuses.target )
{
pActionBuilder->applyStatusEffect( actor, status.id, status.duration, 0, std::move( status.modifiers ), status.flag, true );
}
if( !actor->getStatusEffectMap().empty() )
actor->onActionHostile( m_pSource );
}
}
}
void Action::Action::handleJobAction()
{
switch( m_pSource->getClass() )
{
case ClassJob::Warrior:
{
Warrior::onAction( *m_pSource->getAsPlayer(), *this );
break;
}
}
}
bool Action::Action::preCheck()
{
if( auto player = m_pSource->getAsPlayer() )
@ -678,9 +742,7 @@ bool Action::Action::isCorrectCombo() const
auto lastActionId = m_pSource->getLastComboActionId();
if( lastActionId == 0 )
{
return false;
}
return m_actionData->data().ComboParent == lastActionId;
}
@ -724,6 +786,17 @@ bool Action::Action::primaryCostCheck( bool subtractCosts )
return true;
}
case Common::ActionPrimaryCostType::StatusEffect:
{
if( !m_pSource->hasStatusEffect( m_primaryCost ) )
return false;
if( subtractCosts )
m_pSource->removeSingleStatusEffectById( m_primaryCost );
return true;
}
// free casts, likely just pure ogcds
case Common::ActionPrimaryCostType::None:
{
@ -791,8 +864,10 @@ void Action::Action::addDefaultActorFilters()
{
switch( m_castType )
{
// todo: figure these out and remove 5/RectangularAOE to own handler
case( Common::CastType ) 5:
case Common::CastType::RectangularAOE:
case Common::CastType::SingleTarget:
case Common::CastType::Type3:
{
auto filter = std::make_shared< World::Util::ActorFilterSingleTarget >( static_cast< uint32_t >( m_targetId ) );
addActorFilter( filter );
@ -854,22 +929,20 @@ std::vector< Entity::CharaPtr >& Action::Action::getHitCharas()
Entity::CharaPtr Action::Action::getHitChara()
{
if( !m_hitActors.empty() )
{
return m_hitActors.at( 0 );
}
return nullptr;
}
bool Action::Action::hasValidLutEntry() const
{
return m_lutEntry.potency != 0 || m_lutEntry.comboPotency != 0 || m_lutEntry.flankPotency != 0 || m_lutEntry.frontPotency != 0 ||
m_lutEntry.rearPotency != 0 || m_lutEntry.curePotency != 0 || m_lutEntry.restoreMPPercentage != 0;
m_lutEntry.rearPotency != 0 || m_lutEntry.curePotency != 0 || m_lutEntry.restoreMPPercentage != 0 ||
m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0;
}
Action::EffectBuilderPtr Action::Action::getEffectbuilder()
Action::ActionResultBuilderPtr Action::Action::getActionResultBuilder()
{
return m_effectBuilder;
return m_actionResultBuilder;
}
uint8_t Action::Action::getActionKind() const
@ -891,3 +964,8 @@ uint64_t Action::Action::getCastTimeRest() const
{
return m_castTimeRestMs;
}
void Action::Action::enableGenericHandler()
{
m_enableGenericHandler = true;
}

View file

@ -4,7 +4,7 @@
#include "ActionLut.h"
#include "Util/ActorFilter.h"
#include "ForwardsZone.h"
#include "EffectBuilder.h"
#include "ActionResultBuilder.h"
#include "Exd/Structs.h"
namespace Sapphire::World::Action
@ -23,11 +23,16 @@ namespace Sapphire::World::Action
uint32_t getId() const;
uint32_t getResultId() const;
bool init();
void setPos( const Common::FFXIVARR_POSITION3& pos );
const Common::FFXIVARR_POSITION3& getPos() const;
void setRot( float rot );
float getRot() const;
void setTargetId( uint64_t targetId );
uint64_t getTargetId() const;
Entity::CharaPtr getSourceChara() const;
@ -53,6 +58,10 @@ namespace Sapphire::World::Action
uint64_t getCastTimeRest() const;
void enableGenericHandler();
std::shared_ptr< Excel::ExcelStruct< Excel::Action > > getActionData() const;
/*!
* @brief Checks if a chara has enough resources available to cast the action (tp/mp/etc)
* @return true if they have the required resources
@ -103,9 +112,13 @@ namespace Sapphire::World::Action
*/
bool snapshotAffectedActors( std::vector< Entity::CharaPtr >& actors );
EffectBuilderPtr getEffectbuilder();
ActionResultBuilderPtr getActionResultBuilder();
void buildEffects();
void buildActionResults();
void handleStatusEffects();
void handleJobAction();
/*!
* @brief Adds an actor filter to this action.
@ -118,9 +131,9 @@ namespace Sapphire::World::Action
*/
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();
@ -172,6 +185,7 @@ namespace Sapphire::World::Action
uint8_t m_actionKind{};
uint16_t m_requestId{};
uint32_t m_resultId{};
Common::ActionPrimaryCostType m_primaryCostType;
uint16_t m_primaryCost{};
@ -185,6 +199,7 @@ namespace Sapphire::World::Action
uint8_t m_cooldownGroup{};
int8_t m_range{};
uint8_t m_effectRange{};
uint8_t m_effectWidth{};
uint8_t m_xAxisModifier{};
Common::ActionAspect m_aspect;
Common::CastType m_castType;
@ -201,14 +216,16 @@ namespace Sapphire::World::Action
bool m_canTargetFriendly{};
bool m_canTargetHostile{};
bool m_canTargetDead{};
bool m_enableGenericHandler{};
Common::ActionInterruptType m_interruptType;
std::shared_ptr< Excel::ExcelStruct< Excel::Action > > m_actionData;
Common::FFXIVARR_POSITION3 m_pos{};
float m_rot{};
EffectBuilderPtr m_effectBuilder;
ActionResultBuilderPtr m_actionResultBuilder;
std::vector< World::Util::ActorFilterPtr > m_actorFilters;
std::vector< Entity::CharaPtr > m_hitActors;

View file

@ -15,7 +15,8 @@ bool ActionLut::validEntryExists( uint16_t actionId )
// if all of the fields are 0, it's not 'valid' due to parse error or no useful data in the tooltip
return entry.potency != 0 || entry.comboPotency != 0 || entry.flankPotency != 0 || entry.frontPotency != 0 ||
entry.rearPotency != 0 || entry.curePotency != 0;
entry.rearPotency != 0 || entry.curePotency != 0 ||
entry.statuses.caster.size() > 0 || entry.statuses.target.size() > 0;
}
const ActionEntry& ActionLut::getEntry( uint16_t actionId )

View file

@ -17,6 +17,8 @@ namespace Sapphire::World::Action
struct StatusEntry
{
uint16_t id;
uint32_t duration;
uint32_t flag;
std::vector< StatusModifier > modifiers;
};

Some files were not shown because too many files have changed in this diff Show more