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

Merge pull request #948 from Skyliegirl33/actions-war

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

View file

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

@ -983,74 +983,64 @@ 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,
};
enum class ActionHitSeverityType : uint8_t
{
NormalDamage = 0,
CritHeal = 0,
CritDamage = 1,
NormalHeal = 1,
DirectHitDamage = 2,
CritDirectHitDamage = 3
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 ActionResultFlag : uint8_t
@ -1418,9 +1408,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...

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

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

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

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

View file

@ -45,6 +45,7 @@ private:
static constexpr auto Actor4 = 1013231;//Hatchling
static constexpr auto 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 );
}

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

@ -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

@ -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,6 +59,7 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
{
case Actor0:
{
if( quest.getSeq() == Seq0 )
Scene00000( quest, player );
break;
}
@ -71,11 +73,13 @@ class SubSea008 : public Sapphire::ScriptAPI::QuestScript
}
case Actor2:
{
if( quest.getSeq() == Seq2 )
Scene00003( quest, player );
break;
}
case Actor3:
{
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,6 +64,7 @@ class SubSea016 : public Sapphire::ScriptAPI::QuestScript
}
case Actor1:
{
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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

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

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

View file

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

View file

@ -457,7 +457,7 @@ void Action::Action::execute()
}
}
std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage( uint32_t potency )
std::pair< uint32_t, Common::CalcResultType > Action::Action::calcDamage( uint32_t potency )
{
// todo: what do for npcs?
auto wepDmg = 1.f;
@ -481,7 +481,7 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage(
return Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg );
}
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;
@ -520,7 +520,7 @@ void Action::Action::buildActionResults()
Network::Util::Packet::sendHudParam( *m_pSource );
if( !m_enableGenericHandler || !hasLutEntry || m_hitActors.empty() )
if( m_hitActors.empty() )
{
// send any effect packet added by script or an empty one just to play animation for other players
m_actionResultBuilder->sendActionResults( {} );
@ -539,6 +539,8 @@ void Action::Action::buildActionResults()
bool shouldRestoreMP = true;
bool shouldApplyComboSucceedEffect = true;
if( m_enableGenericHandler && hasLutEntry )
{
for( auto& actor : m_hitActors )
{
if( m_lutEntry.potency > 0 )
@ -557,7 +559,7 @@ void Action::Action::buildActionResults()
if( !isComboAction() || isCorrectCombo() )
{
if( m_lutEntry.curePotency > 0 ) // actions with self heal
if( m_lutEntry.curePotency > 0 )// actions with self heal
{
auto heal = calcHealing( m_lutEntry.curePotency );
m_actionResultBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionResultFlag::EffectOnSource );
@ -570,7 +572,7 @@ void Action::Action::buildActionResults()
}
if( !m_lutEntry.nextCombo.empty() ) // if we have a combo action followup
m_actionResultBuilder->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 )
@ -591,6 +593,10 @@ void Action::Action::buildActionResults()
}
}
if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 )
handleStatusEffects();
}
// If we hit an enemy
if( m_hitActors.size() > 0 && getHitChara()->getObjKind() != m_pSource->getObjKind() )
{
@ -599,9 +605,6 @@ void Action::Action::buildActionResults()
handleJobAction();
if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 )
handleStatusEffects();
m_actionResultBuilder->sendActionResults( m_hitActors );
// TODO: disabled, reset kills our queued actions

View file

@ -128,9 +128,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();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -85,9 +85,7 @@ const int levelTable[61][6] =
{ 218, 354, 858, 2600, 282, 215 },
};
std::random_device CalcStats::dev;
std::mt19937 CalcStats::rng( dev() );
std::uniform_int_distribution< std::mt19937::result_type > CalcStats::range100( 0, 99 );
std::unique_ptr< RandGenerator< float > > CalcStats::rnd = nullptr;
/*
Class used for battle-related formulas and calculations.
@ -144,12 +142,15 @@ uint32_t CalcStats::calculateMaxHp( Player& player )
uint16_t hpMod = paramGrowthInfo->data().ParamBase;
uint16_t jobModHp = classInfo->data().Hp;
float approxBaseHp = 0.0f; // Read above
float hpModPercent = player.getModifier( Common::ParamModifier::HPPercent );
approxBaseHp = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::HP ] );
auto result = static_cast< uint32_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) +
floor( hpMod / 100.0f * ( vitStat - baseStat ) ) );
result *= hpModPercent;
return result;
}
@ -287,19 +288,6 @@ float CalcStats::blockProbability( const Chara& chara )
return std::floor( ( 30 * blockRate ) / levelVal + 10 );
}
float CalcStats::directHitProbability( const Chara& chara )
{
const auto& baseStats = chara.getStats();
auto level = chara.getLevel();
auto dhRate = chara.getStatValueFloat( Common::BaseParam::Accuracy );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
return std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f;
}
float CalcStats::criticalHitProbability( const Chara& chara )
{
const auto& baseStats = chara.getStats();
@ -564,7 +552,7 @@ float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara )
return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::HealingMagicPotency ) - 292.f ) / 264.f + 100.f ) / 100.f;
}
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara )
std::pair< float, Sapphire::Common::CalcResultType > CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara )
{
// D = ⌊ f(ptc) × f(aa) × f(ap) × f(det) × f(tnc) × traits ⌋ × f(ss) ⌋ ×
// f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ × buff_1 ⌋ × buff... ⌋
@ -577,27 +565,19 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA
// todo: everything after tenacity
auto factor = Common::Util::trunc( pot * aa * ap * det, 0 );
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage;
Sapphire::Common::CalcResultType hitType = Sapphire::Common::CalcResultType::TypeDamageHp;
// todo: traits
factor = std::floor( factor * speed( chara ) );
if( criticalHitProbability( chara ) > range100( rng ) )
if( criticalHitProbability( chara ) > getRandomNumber0To100() )
{
factor *= criticalHitBonus( chara );
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage;
hitType = Sapphire::Common::CalcResultType::TypeCriticalDamageHp;
}
if( directHitProbability( chara ) > range100( rng ) )
{
factor *= 1.25f;
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage :
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
}
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
// todo: buffs
@ -615,7 +595,7 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA
return std::pair( factor, hitType );
}
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
std::pair< float, Sapphire::Common::CalcResultType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
{
// D = ⌊ f(pot) × f(wd) × f(ap) × f(det) × f(tnc) × traits ⌋
// × f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ buff_1 ⌋ × buff_1 ⌋ × buff... ⌋
@ -624,33 +604,29 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
auto wd = weaponDamage( chara, wepDmg );
auto ap = getPrimaryAttackPower( chara );
auto det = determination( chara );
auto damageDealtMod = chara.getModifier( Common::ParamModifier::DamageDealtPercent );
auto factor = Common::Util::trunc( pot * wd * ap * det, 0 );
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage;
Sapphire::Common::CalcResultType hitType = Sapphire::Common::CalcResultType::TypeDamageHp;
if( criticalHitProbability( chara ) > range100( rng ) )
if( criticalHitProbability( chara ) > getRandomNumber0To100() )
{
factor *= criticalHitBonus( chara );
hitType = Sapphire::Common::ActionHitSeverityType::CritDamage;
hitType = Sapphire::Common::CalcResultType::TypeCriticalDamageHp;
}
if( directHitProbability( chara ) > range100( rng ) )
{
factor *= 1.25f;
hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ?
Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage :
Sapphire::Common::ActionHitSeverityType::DirectHitDamage;
}
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
// todo: buffs
factor *= damageDealtMod;
constexpr auto format = "dmg: pot: {} ({}) wd: {} ({}) ap: {} det: {} = {}";
if( auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer() )
{
PlayerMgr::sendDebug( *player, format, pot, ptc, wd, wepDmg, ap, det, factor );
PlayerMgr::sendDebug( *player, "DamageDealtPercent: {}", damageDealtMod );
}
else
{
@ -660,22 +636,23 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActio
return std::pair( factor, hitType );
}
std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
std::pair< float, Sapphire::Common::CalcResultType > CalcStats::calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg )
{
// lol just for testing
float det = chara.getStatValue( Common::BaseParam::Determination );
float mnd = chara.getStatValue( Common::BaseParam::Mind );
auto factor = std::floor( ( wepDmg * ( mnd / 200 ) + ( det / 10 ) ) * ( ptc / 100 ) * 1.3f );
Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalHeal;
if( criticalHitProbability( chara ) > range100( rng ) )
Sapphire::Common::CalcResultType hitType = Sapphire::Common::CalcResultType::TypeRecoverHp;
if( criticalHitProbability( chara ) > getRandomNumber0To100() )
{
factor *= criticalHitBonus( chara );
hitType = Sapphire::Common::ActionHitSeverityType::CritHeal;
hitType = Sapphire::Common::CalcResultType::TypeCriticalRecoverHp;
}
factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f );
factor *= 1.0f + ( ( getRandomNumber0To100() - 50.0f ) / 1000.0f );
return std::pair( factor, hitType );
}
@ -684,3 +661,12 @@ uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara )
{
return chara.getStatValue( chara.getPrimaryStat() );
}
float CalcStats::getRandomNumber0To100()
{
if( !rnd )
{
rnd = std::make_unique< RandGenerator< float > >( Common::Service< RNGMgr >::ref().getRandGenerator< float >( 0, 100 ) );
}
return rnd->next();
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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