diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake index e629b8ae..d15f7abd 100644 --- a/cmake/FindMySQL.cmake +++ b/cmake/FindMySQL.cmake @@ -18,28 +18,39 @@ if( UNIX ) "preferred path to MySQL (mysql_config)" ) - find_program(MYSQL_CONFIG mysql_config + # try mariadb first + find_program(MYSQL_CONFIG mariadb_config ${MYSQL_CONFIG_PREFER_PATH} /usr/local/mysql/bin/ /usr/local/bin/ /usr/bin/ ) + if( NOT MYSQL_CONFIG ) + # fallback to mysql + find_program(MYSQL_CONFIG mysql_config + ${MYSQL_CONFIG_PREFER_PATH} + /usr/local/mysql/bin/ + /usr/local/bin/ + /usr/bin/ + ) + endif() + if( MYSQL_CONFIG ) message(STATUS "Using mysql-config: ${MYSQL_CONFIG}") # set INCLUDE_DIR - exec_program(${MYSQL_CONFIG} - ARGS --include + execute_process(COMMAND ${MYSQL_CONFIG} --include OUTPUT_VARIABLE MY_TMP + OUTPUT_STRIP_TRAILING_WHITESPACE ) string(REGEX REPLACE "-I([^ ]*)( .*)?" "\\1" MY_TMP "${MY_TMP}") set(MYSQL_ADD_INCLUDE_PATH ${MY_TMP} CACHE FILEPATH INTERNAL) #message("[DEBUG] MYSQL ADD_INCLUDE_PATH : ${MYSQL_ADD_INCLUDE_PATH}") # set LIBRARY_DIR - exec_program(${MYSQL_CONFIG} - ARGS --libs_r + execute_process(COMMAND ${MYSQL_CONFIG} --libs_r OUTPUT_VARIABLE MY_TMP + OUTPUT_STRIP_TRAILING_WHITESPACE ) set(MYSQL_ADD_LIBRARIES "") string(REGEX MATCHALL "-l[^ ]*" MYSQL_LIB_LIST "${MY_TMP}") diff --git a/deps/watchdog/Watchdog.h b/deps/watchdog/Watchdog.h index 08cc0678..7d5e9803 100644 --- a/deps/watchdog/Watchdog.h +++ b/deps/watchdog/Watchdog.h @@ -32,14 +32,8 @@ #include - // fucking filesystem -#if _MSC_VER >= 1925 #include namespace ci { namespace fs = std::filesystem; } -#else -#include -namespace ci { namespace fs = std::experimental::filesystem; } -#endif //! Exception for when Watchdog can't locate a file or parse the wildcard class WatchedFileSystemExc : public std::exception { diff --git a/src/api/main.cpp b/src/api/main.cpp index c21f0ddd..c1697b23 100644 --- a/src/api/main.cpp +++ b/src/api/main.cpp @@ -31,14 +31,8 @@ #include -// fucking filesystem -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif Sapphire::Common::Util::CrashHandler crashHandler; diff --git a/src/common/Common.h b/src/common/Common.h index 2c2a4f89..32d29b7a 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -38,6 +38,7 @@ namespace Sapphire::Common const uint16_t ARRSIZE_ORCHESTRION = 40u; const uint16_t ARRSIZE_MONSTERNOTE = 12u; const uint16_t ARRSIZE_BORROWACTION = 10u; + const uint16_t ARRSIZE_CONDITION = 12u; const uint8_t TOWN_COUNT = 6; diff --git a/src/common/Config/ConfigMgr.cpp b/src/common/Config/ConfigMgr.cpp index 5d95e17f..52808aac 100644 --- a/src/common/Config/ConfigMgr.cpp +++ b/src/common/Config/ConfigMgr.cpp @@ -2,13 +2,8 @@ #include #include -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif using namespace Sapphire; using namespace Sapphire::Common; @@ -94,4 +89,4 @@ bool ConfigMgr::copyDefaultConfig( const std::string& configName ) fs::copy_file( configPath.string() + m_configDefaultSuffix, configPath ); return true; -} \ No newline at end of file +} diff --git a/src/common/Logging/Logger.cpp b/src/common/Logging/Logger.cpp index 82867199..9fc6b37b 100644 --- a/src/common/Logging/Logger.cpp +++ b/src/common/Logging/Logger.cpp @@ -8,13 +8,8 @@ #include // #include -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif void Sapphire::Logger::init( const std::string& logPath ) diff --git a/src/common/Util/UtilMath.cpp b/src/common/Util/UtilMath.cpp index 44d9aa23..516c00c7 100644 --- a/src/common/Util/UtilMath.cpp +++ b/src/common/Util/UtilMath.cpp @@ -184,3 +184,23 @@ float Util::trunc( float value, uint8_t digitsToRemain ) return std::floor( value * factor ) / factor; } + +float Util::length( const FFXIVARR_POSITION3& vec ) { + return std::sqrt( vec.x * vec.x + vec.y * vec.y + vec.z * vec.z ); +} + +FFXIVARR_POSITION3 Util::normalize( const FFXIVARR_POSITION3& vec ) { + float len = length( vec ); + if( len == 0 ) return FFXIVARR_POSITION3(); + return FFXIVARR_POSITION3{ vec.x / len, vec.y / len, vec.z / len }; +} + +float Util::dot( const FFXIVARR_POSITION3& vec1, const FFXIVARR_POSITION3& vec2 ) +{ + return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z; +} + +FFXIVARR_POSITION3 Util::projectY( const FFXIVARR_POSITION3& vec ) +{ + return FFXIVARR_POSITION3{ vec.x, 0, vec.z }; +} \ No newline at end of file diff --git a/src/common/Util/UtilMath.h b/src/common/Util/UtilMath.h index 9f956494..9e5f4adc 100644 --- a/src/common/Util/UtilMath.h +++ b/src/common/Util/UtilMath.h @@ -48,6 +48,14 @@ namespace Sapphire::Common::Util FFXIVARR_POSITION3 transform( const FFXIVARR_POSITION3& vector, const Matrix33& matrix ); float eulerToDirection( const FFXIVARR_POSITION3& euler ); + + float length( const FFXIVARR_POSITION3& vec ); + + FFXIVARR_POSITION3 normalize( const FFXIVARR_POSITION3& vec ); + + float dot( const FFXIVARR_POSITION3& vec1, const FFXIVARR_POSITION3& vec2 ); + + FFXIVARR_POSITION3 projectY( const FFXIVARR_POSITION3& vec ); } #endif diff --git a/src/common/Vector3.cpp b/src/common/Vector3.cpp index 29aefdef..7f2bb394 100644 --- a/src/common/Vector3.cpp +++ b/src/common/Vector3.cpp @@ -7,12 +7,17 @@ inline bool FFXIVARR_POSITION3::operator == ( const FFXIVARR_POSITION3& target ) return x == target.x && y == target.y && z == target.z; } -inline bool Vector3::operator == ( const Vector3& target ) const +FFXIVARR_POSITION3 FFXIVARR_POSITION3::operator - ( const FFXIVARR_POSITION3& target ) const { - return x == target.x && y == target.y && z == target.z; + return FFXIVARR_POSITION3{ x - target.x, y - target.y, z - target.z }; } -inline bool Vector3::operator == ( const FFXIVARR_POSITION3& target ) const +inline bool Vector3::operator == ( const Vector3& target ) const { - return x == target.x && y == target.y && z == target.z; -} \ No newline at end of file + return x == target.x && y == target.y && z == target.z && reserve == target.reserve; +} + +Vector3 Vector3::operator - ( const Vector3& target ) const +{ + return Vector3{ x - target.x, y - target.y, z - target.z }; +} diff --git a/src/common/Vector3.h b/src/common/Vector3.h index e03b3dfc..539c4e03 100644 --- a/src/common/Vector3.h +++ b/src/common/Vector3.h @@ -9,6 +9,7 @@ namespace Sapphire::Common float y; float z; inline bool operator == ( const FFXIVARR_POSITION3& target ) const; + FFXIVARR_POSITION3 operator - ( const FFXIVARR_POSITION3& target ) const; }; struct Vector3 @@ -16,8 +17,10 @@ namespace Sapphire::Common float x; float y; float z; + float reserve; inline bool operator == ( const Vector3& target ) const; inline bool operator == ( const FFXIVARR_POSITION3& target ) const; + Vector3 operator - ( const Vector3& target ) const; }; struct Matrix33 diff --git a/src/dbm/DbManager.cpp b/src/dbm/DbManager.cpp index 0f706212..31ef313c 100644 --- a/src/dbm/DbManager.cpp +++ b/src/dbm/DbManager.cpp @@ -11,14 +11,8 @@ using namespace Sapphire; using namespace Sapphire::Common; -// fucking filesystem -#if _MSC_VER >= 1925 #include namespace fs = std::filesystem; -#else -#include -namespace fs = std::experimental::filesystem; -#endif DbManager::DbManager( const std::string& host, const std::string& database, const std::string& user, const std::string& pw, uint16_t port ) : diff --git a/src/dbm/main.cpp b/src/dbm/main.cpp index 7eaff903..dd28ef9f 100644 --- a/src/dbm/main.cpp +++ b/src/dbm/main.cpp @@ -9,14 +9,8 @@ Sapphire::Common::Util::CrashHandler crashHandler; -// fucking filesystem -#if _MSC_VER >= 1925 #include namespace filesys = std::filesystem; -#else -#include -namespace filesys = std::experimental::filesystem; -#endif #include #include diff --git a/src/scripts/common/eobj/InstanceExit.cpp b/src/scripts/common/eobj/InstanceExit.cpp index fae2b0de..d95d5cd2 100644 --- a/src/scripts/common/eobj/InstanceExit.cpp +++ b/src/scripts/common/eobj/InstanceExit.cpp @@ -26,7 +26,7 @@ public: eventMgr().playScene( player, eventId, 1, 0, [this, eobj]( Entity::Player& player, const Event::SceneResult& result ) { if( result.getResult( 0 ) != 1 ) - player.exitInstance(); + playerMgr().onExitInstance( player ); } ); } }; diff --git a/src/scripts/quest/ManFst407.cpp b/src/scripts/quest/ManFst407.cpp index 0eb4c376..59f77027 100644 --- a/src/scripts/quest/ManFst407.cpp +++ b/src/scripts/quest/ManFst407.cpp @@ -823,7 +823,7 @@ private: quest.setSeq( Seq6 ); eventMgr().sendEventNotice( player, getId(), 4, 0 ); playerMgr().sendUrgent( player, "QuestBattle Unimplemented, skipping..." ); - player.exitInstance(); + playerMgr().onExitInstance( player ); travelToPoprange( player, Poprange3, false ); } diff --git a/src/world/Action/ActionLutData.cpp b/src/world/Action/ActionLutData.cpp index d8a74a4d..108f0b77 100644 --- a/src/world/Action/ActionLutData.cpp +++ b/src/world/Action/ActionLutData.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using namespace Sapphire; using namespace Sapphire::World::Action; @@ -138,6 +139,10 @@ bool ActionLutData::cacheActions() { auto id = std::stoi( i.key() ); auto action = i.value().get< ActionEntry >(); + + if( ActionLut::m_actionLut.count( id ) > 0 ) + throw std::runtime_error( fmt::format( "Action with ID {} cannot be defined more than once (defined again in {})", i.key(), p.path().string() ) ); + ActionLut::m_actionLut.try_emplace( id, action ); } diff --git a/src/world/Action/ActionResultBuilder.cpp b/src/world/Action/ActionResultBuilder.cpp index cf073683..db0da0cb 100644 --- a/src/world/Action/ActionResultBuilder.cpp +++ b/src/world/Action/ActionResultBuilder.cpp @@ -171,6 +171,7 @@ std::shared_ptr< FFXIVPacketBase > ActionResultBuilder::createActionResultPacket break; } + m_actorResultsMap.clear(); return actionResult; } else // use Effect for single target diff --git a/src/world/Action/ItemAction.cpp b/src/world/Action/ItemAction.cpp index 6c20f290..462728cc 100644 --- a/src/world/Action/ItemAction.cpp +++ b/src/world/Action/ItemAction.cpp @@ -115,6 +115,6 @@ void ItemAction::handleSongItem() { auto player = getSourceChara()->getAsPlayer(); - player->learnSong( m_itemAction->data().Calcu0Arg[ 0 ], m_id ); + playerMgr().onSongLearned( *player, m_itemAction->data().Calcu0Arg[ 0 ], m_id ); player->dropInventoryItem( static_cast< Common::InventoryType >( m_itemSourceContainer ), static_cast< uint8_t >( m_itemSourceSlot ) ); } \ No newline at end of file diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index d09ab6cc..bd84aa5c 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -712,7 +712,7 @@ void BNpc::onDeath() if( pPlayer ) { playerMgr.onMobKill( *pPlayer, *this ); - pPlayer->gainExp( paramGrowthInfo->data().BaseExp ); + playerMgr.onGainExp( *pPlayer, paramGrowthInfo->data().BaseExp ); } } diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 302c4818..e4f65a02 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -834,6 +834,26 @@ float Chara::getModifier( Common::ParamModifier paramModifier ) const return result; } +// Compute forward direction based on rotation angle (assuming rotation around Z axis) +FFXIVARR_POSITION3 Chara::getForwardVector() const { + return Common::Util::normalize( FFXIVARR_POSITION3{ std::sin( getRot() ), 0, std::cos( getRot() ) } ); +} + +// Function to check if actor is facing target +bool Chara::isFacingTarget( const Chara& other, float threshold ) +{ + auto toActor = Common::Util::normalize( Common::Util::projectY( other.getPos() - getPos() ) ); + + auto forward = getForwardVector(); + + float dot = Common::Util::dot( forward, toActor ); + + // The threshold is used to determine how closely the actors need to be facing each other + // 1.0 means they need to be perfectly facing each other + // Lower values allow for some deviation + return dot >= threshold; +} + void Chara::onTick() { uint32_t thisTickDmg = 0; diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 23183b15..98683952 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -265,6 +265,10 @@ namespace Sapphire::Entity virtual void update( uint64_t tickCount ); + Common::FFXIVARR_POSITION3 getForwardVector() const; + + bool isFacingTarget( const Chara& other, float threshold = 0.95f ); + World::Action::ActionPtr getCurrentAction() const; void setCurrentAction( World::Action::ActionPtr pAction ); diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index cbb8fb6e..3ef1f5b5 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -397,18 +397,6 @@ bool Player::isAutoattackOn() const return m_bAutoattack; } -bool Player::exitInstance() -{ - auto& warpMgr = Common::Service< WarpMgr >::ref(); - - resetHp(); - resetMp(); - - warpMgr.requestMoveTerritory( *this, WarpType::WARP_TYPE_CONTENT_END_RETURN, getPrevTerritoryId(), getPrevPos(), getPrevRot() ); - - return true; -} - uint32_t Player::getPlayTime() const { return m_playTime; @@ -474,60 +462,6 @@ Player::Discovery& Player::getDiscoveryBitmask() return m_discovery; } -void Player::discover( int16_t mapId, int16_t subId ) -{ - auto& exdData = Common::Service< Data::ExdData >::ref(); - - int32_t offset; - - auto info = exdData.getRow< Excel::Map >( mapId ); - if( !info ) - { - PlayerMgr::sendDebug( *this, "discover(): Could not obtain map data for map_id == {0}", mapId ); - return; - } - - const auto& mapData = info->data(); - - if( mapData.IsUint16Discovery ) - offset = 2 * mapData.DiscoveryIndex; - else - offset = 320 + 4 * mapData.DiscoveryIndex; - - uint16_t index; - uint8_t value; - Util::valueToFlagByteIndexValue( subId, value, index ); - - m_discovery[ offset + index ] |= value; - - uint16_t level = getLevel(); - - uint32_t exp = ( exdData.getRow< Excel::ParamGrow >( level )->data().NextExp * 5 / 100 ); - gainExp( exp ); - - // gain 10x additional EXP if entire map is completed - uint32_t mask = mapData.DiscoveryFlag; - uint32_t discoveredAreas; - if( info->data().IsUint16Discovery ) - { - discoveredAreas = ( m_discovery[ offset + 1 ] << 8 ) | m_discovery[ offset ]; - } - else - { - discoveredAreas = ( m_discovery[ offset + 3 ] << 24 ) | - ( m_discovery[ offset + 2 ] << 16 ) | - ( m_discovery[ offset + 1 ] << 8 ) | - m_discovery[ offset ]; - } - - bool allDiscovered = ( ( discoveredAreas & mask ) == mask ); - - if( allDiscovered ) - { - gainExp( exp * 10 ); - } -} - bool Player::isNewAdventurer() const { return m_bNewAdventurer; @@ -563,6 +497,11 @@ void Player::setRewardFlag( Common::UnlockEntry unlockId ) Network::Util::Packet::sendActorControlSelf( *this, getId(), SetRewardFlag, unlock, 1 ); } +void Player::fillRewardFlags() +{ + memset( m_unlocks.data(), 0xFF, m_unlocks.size() ); +} + void Player::setBorrowAction( uint8_t slot, uint32_t action ) { if( slot > Common::ARRSIZE_BORROWACTION ) @@ -586,8 +525,6 @@ void Player::learnSong( uint8_t songId, uint32_t itemId ) Util::valueToFlagByteIndexValue( songId, value, index ); m_orchestrion[ index ] |= value; - - Network::Util::Packet::sendActorControlSelf( *this, getId(), ToggleOrchestrionUnlock, songId, 1, itemId ); } bool Player::hasReward( Common::UnlockEntry unlockId ) const @@ -614,52 +551,6 @@ bool Player::hasMount( uint32_t mountId ) const return m_mountGuide[ index ] & value; } -void Player::gainExp( uint32_t amount ) -{ - uint32_t currentExp = getExp(); - uint16_t level = getLevel(); - auto currentClass = static_cast< uint8_t >( getClass() ); - - if( level >= Common::MAX_PLAYER_LEVEL ) - { - setExp( 0 ); - if( currentExp != 0 ) - Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, 0 ); - - return; - } - - auto& exdData = Common::Service< Data::ExdData >::ref(); - - uint32_t neededExpToLevel = exdData.getRow< Excel::ParamGrow >( level )->data().NextExp; - uint32_t neededExpToLevelPlus1 = exdData.getRow< Excel::ParamGrow >( level + 1 )->data().NextExp; - - if( ( currentExp + amount ) >= neededExpToLevel ) - { - // levelup - amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelPlus1 ? neededExpToLevelPlus1 - 1 : ( currentExp + amount - neededExpToLevel ); - - if( level + 1 >= Common::MAX_PLAYER_LEVEL ) - amount = 0; - - setExp( amount ); - levelUp(); - } - else - setExp( currentExp + amount ); - - Network::Util::Packet::sendActorControlSelf( *this, getId(), GainExpMsg, currentClass, amount ); - Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, getExp() ); -} - -void Player::levelUp() -{ - m_hp = getMaxHp(); - m_mp = getMaxMp(); - - setLevel( getLevel() + 1 ); -} - uint8_t Player::getLevel() const { auto& exdData = Common::Service< Data::ExdData >::ref(); @@ -691,14 +582,14 @@ bool Player::isClassJobUnlocked( Common::ClassJob classJob ) const return getLevelForClass( classJob ) != 0; } -uint32_t Player::getExp() const +uint32_t Player::getCurrentExp() const { auto& exdData = Common::Service< Data::ExdData >::ref(); uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex; return m_expArray[ classJobIndex ]; } -void Player::setExp( uint32_t amount ) +void Player::setCurrentExp( uint32_t amount ) { auto& exdData = Common::Service< Data::ExdData >::ref(); uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex; @@ -719,20 +610,6 @@ void Player::setInCombat( bool mode ) void Player::setClassJob( Common::ClassJob classJob ) { m_class = classJob; - - if( getHp() > getMaxHp() ) - m_hp = getMaxHp(); - - if( getMp() > getMaxMp() ) - m_mp = getMaxMp(); - - m_tp = 0; - - Network::Util::Packet::sendChangeClass( *this ); - Network::Util::Packet::sendStatusUpdate( *this ); - Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), ClassJobChange, 4 ); - Network::Util::Packet::sendHudParam( *this ); - Service< World::Manager::MapMgr >::ref().updateQuests( *this ); } void Player::setLevel( uint8_t level ) @@ -740,16 +617,6 @@ void Player::setLevel( uint8_t level ) auto& exdData = Common::Service< Data::ExdData >::ref(); uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex; m_classArray[ classJobIndex ] = level; - - calculateStats(); - 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 ) @@ -1586,77 +1453,6 @@ Sapphire::Common::HuntingLogEntry& Player::getHuntingLogEntry( uint8_t index ) return m_huntingLogEntries[ index ]; } -void Player::updateHuntingLog( uint16_t id ) -{ - std::vector< uint32_t > rankRewards{ 2500, 10000, 20000, 30000, 40000 }; - const auto maxRank = 4; - auto& pExdData = Common::Service< Data::ExdData >::ref(); - - // make sure we get the matching base-class if a job is being used - auto classJobInfo = pExdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) ); - if( !classJobInfo ) - return; - - auto currentClassId = classJobInfo->data().MonsterNote; - if( currentClassId == -1 || currentClassId == 127 ) - return; - - auto& logEntry = m_huntingLogEntries[ currentClassId ]; - - bool logChanged = false; - - - bool allSectionsComplete = true; - for( int i = 1; i <= 10; ++i ) - { - bool sectionComplete = true; - bool sectionChanged = false; - auto monsterNoteId = static_cast< uint32_t >( classJobInfo->data().MainClass * 10000 + logEntry.rank * 10 + i ); - auto note = pExdData.getRow< Excel::MonsterNote >( monsterNoteId ); - - // for classes that don't have entries, if the first fails the rest will fail - if( !note ) - break; - - for( auto x = 0; x < 4; ++x ) - { - auto note1 = pExdData.getRow< Excel::MonsterNoteTarget >( note->data().Target[ x ] ); - if( note1->data().Monster == id && logEntry.entries[ i - 1 ][ x ] < note->data().NeededKills[ x ] ) - { - logEntry.entries[ i - 1 ][ x ]++; - Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogEntryUpdate, monsterNoteId, x, logEntry.entries[ i - 1 ][ x ] ); - logChanged = true; - sectionChanged = true; - } - if( logEntry.entries[ i - 1 ][ x ] != note->data().NeededKills[ x ] ) - sectionComplete = false; - } - if( logChanged && sectionComplete && sectionChanged ) - { - Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogSectionFinish, monsterNoteId, i, 0 ); - gainExp( note->data().RewardExp ); - } - if( !sectionComplete ) - { - allSectionsComplete = false; - } - } - if( logChanged && allSectionsComplete ) - { - Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogRankFinish, 4 ); - gainExp( rankRewards[ logEntry.rank ] ); - if( logEntry.rank < 4 ) - { - logEntry.rank++; - memset( logEntry.entries, 0, 40 ); - Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogRankUnlock, currentClassId, logEntry.rank + 1, 0 ); - } - } - - if( logChanged ) - Network::Util::Packet::sendHuntingLog( *this ); -} - void Player::setActiveLand( uint8_t land, uint8_t ward ) { m_activeLand.plot = land; @@ -1817,6 +1613,9 @@ void Player::setFalling( bool state, const Common::FFXIVARR_POSITION3& pos, bool takeDamage( damage ); } Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), SetFallDamage, damage ); + // todo: this used to work without refreshing the entire UI state + // does 3.x use some sort of fall integrity? + Network::Util::Packet::sendHudParam( *this ); } } } diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index a68ad613..87db7e18 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -34,7 +34,7 @@ namespace Sapphire::Entity using AetheryteList = std::array< uint8_t, Common::ARRSIZE_AETHERYTES >; using UnlockList = std::array< uint8_t, Common::ARRSIZE_UNLOCKS >; using OrchestrionList = std::array< uint8_t, Common::ARRSIZE_ORCHESTRION >; - using Condition = std::array< uint8_t, 12 >; + using Condition = std::array< uint8_t, Common::ARRSIZE_CONDITION >; using ClassList = std::array< uint16_t, Common::ARRSIZE_CLASSJOB >; using ExpList = std::array< uint32_t, Common::ARRSIZE_CLASSJOB >; @@ -215,16 +215,10 @@ namespace Sapphire::Entity bool isClassJobUnlocked( Common::ClassJob classJob ) const; /*! returns the exp of the currently active class / job */ - uint32_t getExp() const; + uint32_t getCurrentExp() const; /*! sets the exp of the currently active class / job */ - void setExp( uint32_t amount ); - - /*! adds exp to the currently active class / job */ - void gainExp( uint32_t amount ); - - /*! gain a level on the currently active class / job */ - void levelUp(); + void setCurrentExp( uint32_t amount ); /*! set level on the currently active class / job to given level */ void setLevel( uint8_t level ); @@ -294,9 +288,6 @@ namespace Sapphire::Entity /*! return current online status depending on current state / activity */ Common::OnlineStatus getOnlineStatus() const; - /*! returns the player to their position before zoning into an instance */ - bool exitInstance(); - /*! gets the players territoryTypeId */ uint32_t getPrevTerritoryTypeId() const; @@ -423,9 +414,6 @@ namespace Sapphire::Entity /*! get homepoint */ uint8_t getHomepoint() const; - /*! discover subarea subid fo map map_id, also send udpate packet */ - void discover( int16_t mapId, int16_t subId ); - /*! return a reference to the discovery bitmask array */ Discovery& getDiscoveryBitmask(); @@ -441,6 +429,9 @@ namespace Sapphire::Entity /*! learn an action / update the unlock bitmask. */ void setRewardFlag( Common::UnlockEntry unlockId ); + /*! helper/debug function to fill unlock bitmask */ + void fillRewardFlags(); + void setBorrowAction( uint8_t slot, uint32_t action ); BorrowAction& getBorrowAction(); @@ -778,8 +769,6 @@ namespace Sapphire::Entity Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index ); - void updateHuntingLog( uint16_t id ); - uint64_t getPartyId() const; void setPartyId( uint64_t partyId ); diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index 597b8e17..4d116629 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -118,7 +118,7 @@ void Player::equipWeapon( const Item& item ) if( ( isClassJobUnlocked( newClassJob ) ) && ( currentParentClass != newClassJob ) ) { - setClassJob( newClassJob ); + playerMgr().onClassJobChanged( *this, newClassJob ); } } @@ -131,7 +131,7 @@ void Player::equipSoulCrystal( const Item& item ) auto newClassJob = static_cast< ClassJob >( itemClassJob ); if( isClassJobUnlocked( newClassJob ) ) - setClassJob( newClassJob ); + playerMgr().onClassJobChanged( *this, newClassJob ); } void Player::updateModels( GearSetSlot equipSlotId, const Sapphire::Item& item ) @@ -263,7 +263,7 @@ void Player::unequipSoulCrystal() auto currentClassJob = exdData.getRow< Excel::ClassJob >( static_cast< uint32_t >( getClass() ) ); auto parentClass = static_cast< ClassJob >( currentClassJob->data().MainClass ); - setClassJob( parentClass ); + playerMgr().onClassJobChanged( *this, parentClass ); } uint32_t Player::currencyTypeToItem( Common::CurrencyType type ) const diff --git a/src/world/Actor/PlayerSql.cpp b/src/world/Actor/PlayerSql.cpp index a711d6c3..d51a8300 100644 --- a/src/world/Actor/PlayerSql.cpp +++ b/src/world/Actor/PlayerSql.cpp @@ -493,7 +493,7 @@ void Player::updateDbClass() const //Exp = ?, Lvl = ?, BorrowAction = ? WHERE CharacterId = ? AND ClassIdx = ? auto stmtS = db.getPreparedStatement( Db::CHARA_CLASS_UP ); - stmtS->setInt( 1, getExp() ); + stmtS->setInt( 1, getCurrentExp() ); stmtS->setInt( 2, getLevel() ); std::vector< uint8_t > borrowActionVec( borrowAction.size() * 4 ); diff --git a/src/world/Manager/DebugCommandMgr.cpp b/src/world/Manager/DebugCommandMgr.cpp index 20b21dee..028ec648 100644 --- a/src/world/Manager/DebugCommandMgr.cpp +++ b/src/world/Manager/DebugCommandMgr.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "DebugCommand/DebugCommand.h" @@ -84,6 +85,7 @@ DebugCommandMgr::DebugCommandMgr() registerCommand( "cf", &DebugCommandMgr::contentFinder, "Content-Finder", 1 ); registerCommand( "ew", &DebugCommandMgr::easyWarp, "Easy warping", 1 ); registerCommand( "reload", &DebugCommandMgr::hotReload, "Reloads a resource", 1 ); + registerCommand( "facing", &DebugCommandMgr::facing, "Checks if you are facing an actor", 1 ); registerCommand( "cbt", &DebugCommandMgr::cbt, "Create, bind and teleport to an instance", 1 ); } @@ -163,6 +165,7 @@ void DebugCommandMgr::help( char* data, Entity::Player& player, std::shared_ptr< void DebugCommandMgr::set( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command ) { auto& server = Sapphire::Common::Service< Sapphire::World::WorldServer >::ref(); + auto& playerMgr = Common::Service< PlayerMgr >::ref(); auto& terriMgr = Common::Service< TerritoryMgr >::ref(); auto pCurrentZone = terriMgr.getTerritoryByGuId( player.getTerritoryId() ); @@ -237,7 +240,8 @@ void DebugCommandMgr::set( char* data, Entity::Player& player, std::shared_ptr< else if( subCommand == "discovery_reset" ) { player.resetDiscovery(); - server.queueForPlayer( player.getCharacterId(), std::make_shared< PlayerSetupPacket >( player ) ); + player.setIsLogin( true ); + playerMgr.onMoveZone( player ); } else if( subCommand == "classjob" ) { @@ -248,10 +252,9 @@ void DebugCommandMgr::set( char* data, Entity::Player& player, std::shared_ptr< if( player.getLevelForClass( static_cast< Common::ClassJob > ( id ) ) == 0 ) { player.setLevelForClass( 1, static_cast< Common::ClassJob > ( id ) ); - player.setClassJob( static_cast< Common::ClassJob > ( id ) ); } - else - player.setClassJob( static_cast< Common::ClassJob > ( id ) ); + + playerMgr.onClassJobChanged( player, static_cast< Common::ClassJob > ( id ) ); } else if( subCommand == "cfpenalty" ) { @@ -389,6 +392,7 @@ void DebugCommandMgr::set( char* data, Entity::Player& player, std::shared_ptr< void DebugCommandMgr::add( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command ) { auto& terriMgr = Common::Service< TerritoryMgr >::ref(); + auto& playerMgr = Common::Service< PlayerMgr >::ref(); auto pCurrentZone = terriMgr.getTerritoryByGuId( player.getTerritoryId() ); std::string subCommand; @@ -529,6 +533,13 @@ void DebugCommandMgr::add( char* data, Entity::Player& player, std::shared_ptr< sscanf( params.c_str(), "%d", &id ); player.setRewardFlag( static_cast< Common::UnlockEntry >( id ) ); } + else if( subCommand == "unlockall" ) + { + player.fillRewardFlags(); + + player.setIsLogin( true ); + playerMgr.onMoveZone( player ); + } else if( subCommand == "effect" ) { uint16_t param1; @@ -969,7 +980,7 @@ void DebugCommandMgr::instance( char* data, Entity::Player& player, std::shared_ } else if( subCommand == "return" || subCommand == "ret" ) { - player.exitInstance(); + playerMgr().onExitInstance( player ); } else if( subCommand == "stringendomode" || subCommand == "sm" ) { @@ -1219,7 +1230,7 @@ void DebugCommandMgr::questBattle( char* data, Entity::Player& player, std::shar } else if( subCommand == "return" || subCommand == "ret" ) { - player.exitInstance(); + playerMgr().onExitInstance( player ); } else if( subCommand == "set" ) { @@ -1599,4 +1610,43 @@ void DebugCommandMgr::hotReload( char* data, Sapphire::Entity::Player& player, s { PlayerMgr::sendDebug( player, "Unknown sub command." ); } +} + +void DebugCommandMgr::facing( char* data, Sapphire::Entity::Player& player, std::shared_ptr< DebugCommand > command ) +{ + std::string subCommand; + std::string params = ""; + + // check if the command has parameters + std::string tmpCommand = std::string( data + command->getName().length() + 1 ); + + std::size_t pos = tmpCommand.find_first_of( ' ' ); + + if( pos != std::string::npos ) + // command has parameters, grab the first part + subCommand = tmpCommand.substr( 0, pos ); + else + // no subcommand given + subCommand = tmpCommand; + + if( command->getName().length() + 1 + pos + 1 < strlen( data ) ) + params = std::string( data + command->getName().length() + 1 + pos + 1 ); + + Logger::debug( "[{0}] subCommand: {1} params: {2}", player.getId(), subCommand, params ); + + float threshold = 0.95f; + sscanf( params.c_str(), "%f", &threshold ); + + if( player.getTargetId() != 0 ) + { + auto target = player.lookupTargetById( player.getTargetId() ); + + if( !target ) + return; + + if( auto bnpc = target->getAsBNpc() ) + { + PlayerMgr::sendDebug( player, "Player facing target {0}: {1}", bnpc->getLayoutId(), player.isFacingTarget( *bnpc->getAsChara(), threshold ) ); + } + } } \ No newline at end of file diff --git a/src/world/Manager/DebugCommandMgr.h b/src/world/Manager/DebugCommandMgr.h index b8e39c64..d25471b1 100644 --- a/src/world/Manager/DebugCommandMgr.h +++ b/src/world/Manager/DebugCommandMgr.h @@ -67,6 +67,8 @@ namespace Sapphire::World::Manager void hotReload( char* data, Sapphire::Entity::Player& player, std::shared_ptr< DebugCommand > command ); + void facing( char* data, Sapphire::Entity::Player& player, std::shared_ptr< DebugCommand > command ); + }; } \ No newline at end of file diff --git a/src/world/Manager/MapMgr.cpp b/src/world/Manager/MapMgr.cpp index f7a18a40..fb660315 100644 --- a/src/world/Manager/MapMgr.cpp +++ b/src/world/Manager/MapMgr.cpp @@ -35,11 +35,24 @@ using namespace Sapphire::World::Manager; bool MapMgr::loadQuests() { auto& exdData = Common::Service< Data::ExdData >::ref(); + auto questList = exdData.getRows< Excel::Quest >(); + auto eNpcList = exdData.getRows< Excel::ENpcBase >(); + auto eObjList = exdData.getRows< Excel::EObj >(); for( auto& [ id, questExdData ] : questList ) { - m_quests.emplace( id, std::move( questExdData ) ); + m_questCacheMap.emplace( id, std::move( questExdData ) ); + } + + for( auto& [ id, eNpcExdData ] : eNpcList ) + { + m_eNpcCacheMap.emplace( id, std::move( eNpcExdData ) ); + } + + for( auto& [ id, eObjExdData ] : eObjList ) + { + m_eObjCacheMap.emplace( id, std::move( eObjExdData ) ); } return true; @@ -58,12 +71,12 @@ void MapMgr::updateAll( Entity::Player& player ) for( const auto& eventNpc : *eventNpcs ) { - auto eNpc = exdData.getRow< Excel::ENpcBase >( eventNpc.second->data.enpcId ); - if( !eNpc ) + auto eNpcIt = m_eNpcCacheMap.find( eventNpc.second->data.enpcId ); + if( eNpcIt == std::end( m_eNpcCacheMap ) ) continue; - auto eNpcData = eNpc->data().EventHandler; - for( int npcEvent = 0; npcEvent < 32; npcEvent++ ) + auto eNpcData = eNpcIt->second->data().EventHandler; + for( auto npcEvent = 0; npcEvent < 32; ++npcEvent ) { auto npcData = eNpcData[ npcEvent ].EventHandler; @@ -80,7 +93,7 @@ void MapMgr::updateAll( Entity::Player& player ) { case EventHandler::EventHandlerType::Quest: { - auto& quest = m_quests[ npcData ]->data(); + auto& quest = m_questCacheMap[ npcData ]->data(); if( quest.Client == eventNpc.second->data.enpcId ) { @@ -102,7 +115,7 @@ void MapMgr::updateAll( Entity::Player& player ) { if( guildLeve.NeedGrandCompanyRank > 0 && player.getGc() != 0 ) { - for( int8_t i = 0; i < 3; i++ ) + for( int8_t i = 0; i < 3; ++i ) { if( player.getGcRankArray()[ i ] >= guildLeve.NeedGrandCompanyRank ) { @@ -162,11 +175,11 @@ void MapMgr::updateAll( Entity::Player& player ) for( const auto& eventObj : *eventObjs ) { - auto eObj = exdData.getRow< Excel::EObj >( eventObj.second->data.BaseId ); - if( !eObj ) - return; + auto eObjIt = m_eObjCacheMap.find( eventObj.second->data.BaseId ); + if( eObjIt == std::end( m_eObjCacheMap ) ) + continue; - auto eObjData = eObj->data(); + auto eObjData = eObjIt->second->data(); EventData eventData; eventData.handlerId = eObjData.EventHandler; eventData.layoutId = eventObj.first; @@ -175,7 +188,7 @@ void MapMgr::updateAll( Entity::Player& player ) if( eventHandlerType == EventHandler::EventHandlerType::Quest ) { - auto& quest = m_quests[ eObjData.EventHandler ]->data(); + auto& quest = m_questCacheMap[ eObjData.EventHandler ]->data(); if( quest.Client == eventObj.second->data.BaseId ) { @@ -200,13 +213,13 @@ void MapMgr::updateQuests( Entity::Player& player ) for( const auto& eventNpc : *eventNpcs ) { - auto eNpc = exdData.getRow< Excel::ENpcBase >( eventNpc.second->data.enpcId ); - if( !eNpc ) + auto eNpcIt = m_eNpcCacheMap.find( eventNpc.second->data.enpcId ); + if( eNpcIt == std::end( m_eNpcCacheMap ) ) continue; - auto eNpcData = eNpc->data().EventHandler; + auto eNpcData = eNpcIt->second->data().EventHandler; - for( int npcEvent = 0; npcEvent < 32; npcEvent++ ) + for( auto npcEvent = 0; npcEvent < 32; ++npcEvent ) { auto npcData = eNpcData[ npcEvent ].EventHandler; @@ -221,7 +234,7 @@ void MapMgr::updateQuests( Entity::Player& player ) if( eventHandlerType == EventHandler::EventHandlerType::Quest ) { - auto& quest = m_quests[ npcData ]->data(); + auto& quest = m_questCacheMap[ npcData ]->data(); if( quest.Client == eventNpc.second->data.enpcId ) { @@ -237,11 +250,11 @@ void MapMgr::updateQuests( Entity::Player& player ) for( const auto& eventObj : *eventObjs ) { - auto eObj = exdData.getRow< Excel::EObj >( eventObj.second->data.BaseId ); - if( !eObj ) - return; + auto eObjIt = m_eObjCacheMap.find( eventObj.second->data.BaseId ); + if( eObjIt == std::end( m_eObjCacheMap ) ) + continue; - auto eObjData = eObj->data(); + auto eObjData = eObjIt->second->data(); EventData eventData; eventData.handlerId = eObjData.EventHandler; eventData.layoutId = eventObj.first; @@ -250,7 +263,7 @@ void MapMgr::updateQuests( Entity::Player& player ) if( eventHandlerType == EventHandler::EventHandlerType::Quest ) { - auto& quest = m_quests[ eObjData.EventHandler ]->data(); + auto& quest = m_questCacheMap[ eObjData.EventHandler ]->data(); if( quest.Client == eventObj.second->data.BaseId ) { @@ -267,7 +280,7 @@ void MapMgr::insertQuest( Entity::Player& player, uint32_t questId, uint32_t lay auto& exdData = Common::Service< Data::ExdData >::ref(); auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); - auto& quest = m_quests[ questId ]->data(); + auto& quest = m_questCacheMap[ questId ]->data(); if( isQuestVisible( player, questId, quest ) ) { @@ -307,7 +320,7 @@ bool MapMgr::isQuestAvailable( Entity::Player& player, uint32_t questId, Excel:: if( quest.InstanceContentOperator == 1 ) { - for( int32_t i = 0; i < 3; i++ ) + for( int32_t i = 0; i < 3; ++i ) { if( quest.InstanceContent[ i ] == 0 ) continue; @@ -319,7 +332,7 @@ bool MapMgr::isQuestAvailable( Entity::Player& player, uint32_t questId, Excel:: } else if( quest.InstanceContentOperator == 2 ) { - for( int32_t i = 0; i < 3; i++ ) + for( int32_t i = 0; i < 3; ++i ) { if( quest.InstanceContent[ i ] == 0 ) continue; @@ -396,7 +409,7 @@ bool MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Excel::Qu if( quest.PrevQuestOperator == 1 ) { - for( int32_t i = 0; i < 3; i++ ) + for( auto i = 0; i < 3; ++i ) { if( quest.PrevQuest[ i ] == 0 ) continue; @@ -419,7 +432,7 @@ bool MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Excel::Qu } else if( quest.PrevQuestOperator == 2 ) { - for( int32_t i = 0; i < 3; i++ ) + for( auto i = 0; i < 3; ++i ) { if( quest.PrevQuest[ i ] == 0 ) continue; @@ -434,7 +447,7 @@ bool MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Excel::Qu if( quest.ExcludeQuestOperator == 1 ) { - for( int32_t i = 0; i < 2; i++ ) + for( auto i = 0; i < 2; ++i ) { if( quest.ExcludeQuest[ i ] == 0 ) continue; @@ -448,7 +461,7 @@ bool MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Excel::Qu } else if( quest.ExcludeQuestOperator == 2 ) { - for( int32_t i = 0; i < 2; i++ ) + for( auto i = 0; i < 2; ++i ) { if( quest.ExcludeQuest[ i ] == 0 ) continue; @@ -475,7 +488,7 @@ bool MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Excel::Qu { auto classJobCategory = exdData.getRow< Excel::ClassJobCategory >( quest.ClassJob )->data().ClassJob; - for( int32_t i = 1; i <= Common::CLASSJOB_TOTAL; i++ ) + for( auto i = 1; i <= Common::CLASSJOB_TOTAL; ++i ) { if( i == Common::CLASSJOB_TOTAL ) return false; @@ -494,13 +507,14 @@ bool MapMgr::isQuestVisible( Entity::Player& player, uint32_t questId, Excel::Qu } // TODO: I think this changed in 3.x to be for all relics, more research is needed. - for( int32_t i = 0; i < Common::CLASSJOB_TOTAL; i++ ) + // todo: fix this inner j loop + for( auto i = 0; i < Common::CLASSJOB_TOTAL; ++i ) { auto classJob = exdData.getRow< Excel::ClassJob >( i ); if( classJob->data().ARRRelicQuestId == questId ) { - for( int32_t j = 0; i < Common::CLASSJOB_TOTAL; i++ ) + for( auto j = 0; i < Common::CLASSJOB_TOTAL; ++i ) { classJob = exdData.getRow< Excel::ClassJob >( i ); diff --git a/src/world/Manager/MapMgr.h b/src/world/Manager/MapMgr.h index 6981ecf0..c23500e4 100644 --- a/src/world/Manager/MapMgr.h +++ b/src/world/Manager/MapMgr.h @@ -11,6 +11,8 @@ namespace Sapphire::World::Manager { using QuestMap = std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< Excel::Quest > > >; + using EObjDataCache = std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< Excel::EObj > > >; + using ENpcDataCache = std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< Excel::ENpcBase > > >; class MapMgr { @@ -64,8 +66,11 @@ namespace Sapphire::World::Manager }; using EventSet = std::multiset< EventData, less >; + - QuestMap m_quests; + QuestMap m_questCacheMap; + ENpcDataCache m_eNpcCacheMap; + EObjDataCache m_eObjCacheMap; void insertQuest( Entity::Player& player, uint32_t questId, uint32_t layoutId, EventSet& mapData ); diff --git a/src/world/Manager/PlayerMgr.cpp b/src/world/Manager/PlayerMgr.cpp index 675bf99d..19ce1296 100644 --- a/src/world/Manager/PlayerMgr.cpp +++ b/src/world/Manager/PlayerMgr.cpp @@ -3,12 +3,15 @@ #include #include - +#include #include +#include #include #include #include +#include +#include #include