1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-02 16:57:47 +00:00

myerge squashy;

This commit is contained in:
Alice Ogeda 2025-01-03 02:46:22 -03:00
parent ee72f2e447
commit f2805e951c
37 changed files with 498 additions and 343 deletions

View file

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

View file

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

View file

@ -31,14 +31,8 @@
#include <Util/CrashHandler.h>
// fucking filesystem
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
Sapphire::Common::Util::CrashHandler crashHandler;

View file

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

View file

@ -2,13 +2,8 @@
#include <iostream>
#include <fstream>
#if _MSC_VER >= 1925
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
using namespace Sapphire;
using namespace Sapphire::Common;

View file

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

View file

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

View file

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

View file

@ -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;
return x == target.x && y == target.y && z == target.z && reserve == target.reserve;
}
Vector3 Vector3::operator - ( const Vector3& target ) const
{
return Vector3{ x - target.x, y - target.y, z - target.z };
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,7 @@
#include <fstream>
#include <filesystem>
#include <iostream>
#include <Logging/Logger.h>
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 );
}

View file

@ -171,6 +171,7 @@ std::shared_ptr< FFXIVPacketBase > ActionResultBuilder::createActionResultPacket
break;
}
m_actorResultsMap.clear();
return actionResult;
}
else // use Effect for single target

View file

@ -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 ) );
}

View file

@ -712,7 +712,7 @@ void BNpc::onDeath()
if( pPlayer )
{
playerMgr.onMobKill( *pPlayer, *this );
pPlayer->gainExp( paramGrowthInfo->data().BaseExp );
playerMgr.onGainExp( *pPlayer, paramGrowthInfo->data().BaseExp );
}
}

View file

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

View file

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

View file

@ -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 );
}
}
}

View file

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

View file

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

View file

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

View file

@ -11,6 +11,7 @@
#include <Database/DatabaseDef.h>
#include <cmath>
#include <Network/PacketWrappers/EffectPacket.h>
#include <Network/Util/PacketUtil.h>
#include <Service.h>
#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" )
{
@ -1600,3 +1611,42 @@ 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 ) );
}
}
}

View file

@ -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 );
};
}

View file

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

View file

@ -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
{
@ -65,7 +67,10 @@ 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 );

View file

@ -3,12 +3,15 @@
#include <Service.h>
#include <Exd/ExdData.h>
#include <Util/Util.h>
#include <Territory/Land.h>
#include <Manager/AchievementMgr.h>
#include <Manager/TerritoryMgr.h>
#include <Manager/HousingMgr.h>
#include <Manager/QuestMgr.h>
#include <Manager/WarpMgr.h>
#include <Manager/MapMgr.h>
#include <Script/ScriptMgr.h>
#include <Common.h>
@ -204,7 +207,7 @@ void PlayerMgr::onMobKill( Entity::Player& player, Entity::BNpc& bnpc )
scriptMgr.onBNpcKill( player, bnpc );
if( player.hasReward( Common::UnlockEntry::HuntingLog ) )
player.updateHuntingLog( bnpc.getBNpcNameId() );
onUpdateHuntingLog( player, bnpc.getBNpcNameId() );
}
void PlayerMgr::sendLoginMessage( Entity::Player& player )
@ -339,6 +342,101 @@ void PlayerMgr::checkAutoAttack( Entity::Player& player, uint64_t tickCount ) co
}
void PlayerMgr::onGainExp( Entity::Player& player, uint32_t exp )
{
uint32_t currentExp = player.getCurrentExp();
uint16_t level = player.getLevel();
auto currentClass = static_cast< uint8_t >( player.getClass() );
if( level >= Common::MAX_PLAYER_LEVEL )
{
player.setCurrentExp( 0 );
if( currentExp != 0 )
Network::Util::Packet::sendActorControlSelf( player, player.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 + exp ) >= neededExpToLevel )
{
// levelup
exp = ( currentExp + exp - neededExpToLevel ) > neededExpToLevelPlus1 ? neededExpToLevelPlus1 - 1 : ( currentExp + exp - neededExpToLevel );
if( level + 1 >= Common::MAX_PLAYER_LEVEL )
exp = 0;
else
onLevelChanged( player, level + 1 );
player.setCurrentExp( exp );
}
else
player.setCurrentExp( currentExp + exp );
Network::Util::Packet::sendActorControlSelf( player, player.getId(), GainExpMsg, currentClass, exp );
Network::Util::Packet::sendActorControlSelf( player, player.getId(), UpdateUiExp, currentClass, player.getCurrentExp() );
}
void PlayerMgr::onDiscoverArea( Entity::Player& player, 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 )
{
sendDebug( player, "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;
Common::Util::valueToFlagByteIndexValue( subId, value, index );
auto& discovery = player.getDiscoveryBitmask();
discovery[ offset + index ] |= value;
uint16_t level = player.getLevel();
uint32_t exp = ( exdData.getRow< Excel::ParamGrow >( level )->data().NextExp * 5 / 100 );
onGainExp( player, exp );
// gain 10x additional EXP if entire map is completed
uint32_t mask = mapData.DiscoveryFlag;
uint32_t discoveredAreas;
if( info->data().IsUint16Discovery )
{
discoveredAreas = ( discovery[ offset + 1 ] << 8 ) | discovery[ offset ];
}
else
{
discoveredAreas = ( discovery[ offset + 3 ] << 24 ) |
( discovery[ offset + 2 ] << 16 ) |
( discovery[ offset + 1 ] << 8 ) |
discovery[ offset ];
}
bool allDiscovered = ( ( discoveredAreas & mask ) == mask );
if( allDiscovered )
{
onGainExp( player, exp * 10 );
}
}
////////// Helper ///////////
@ -371,3 +469,129 @@ void PlayerMgr::sendBattleTalk( Sapphire::Entity::Player& player, uint32_t battl
Network::Util::Packet::sendBattleTalk( player, battleTalkId, handlerId, kind, nameId, talkerId,
param1, param2, param3, param4, param5, param6, param7, param8 );
}
void PlayerMgr::onUpdateHuntingLog( Entity::Player& player, uint8_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 >( player.getClass() ) );
if( !classJobInfo )
return;
auto currentClassId = classJobInfo->data().MonsterNote;
if( currentClassId == -1 || currentClassId == 127 )
return;
auto& logEntry = player.getHuntingLogEntry( 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( player, player.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( player, player.getId(), HuntingLogSectionFinish, monsterNoteId, i, 0 );
onGainExp( player, note->data().RewardExp );
}
if( !sectionComplete )
{
allSectionsComplete = false;
}
}
if( logChanged && allSectionsComplete )
{
Network::Util::Packet::sendActorControlSelf( player, player.getId(), HuntingLogRankFinish, 4 );
onGainExp( player, rankRewards[ logEntry.rank ] );
if( logEntry.rank < 4 )
{
logEntry.rank++;
memset( logEntry.entries, 0, 40 );
Network::Util::Packet::sendActorControlSelf( player, player.getId(), HuntingLogRankUnlock, currentClassId, logEntry.rank + 1, 0 );
}
}
if( logChanged )
Network::Util::Packet::sendHuntingLog( player );
}
void PlayerMgr::onExitInstance( Entity::Player& player )
{
auto& warpMgr = Common::Service< WarpMgr >::ref();
player.resetHp();
player.resetMp();
warpMgr.requestMoveTerritory( player, Common::WarpType::WARP_TYPE_CONTENT_END_RETURN,
player.getPrevTerritoryId(), player.getPrevPos(), player.getPrevRot() );
}
void PlayerMgr::onClassJobChanged( Entity::Player& player, Common::ClassJob classJob )
{
player.setClassJob( classJob );
if( player.getHp() > player.getMaxHp() )
player.setHp( player.getMaxHp() );
if( player.getMp() > player.getMaxMp() )
player.setMp( player.getMaxMp() );
player.setTp( 0 );
Network::Util::Packet::sendChangeClass( player );
Network::Util::Packet::sendStatusUpdate( player );
Network::Util::Packet::sendActorControl( player.getInRangePlayerIds( true ), player.getId(), ClassJobChange, 4 );
Network::Util::Packet::sendHudParam( player );
Common::Service< World::Manager::MapMgr >::ref().updateQuests( player );
}
void PlayerMgr::onLevelChanged( Entity::Player& player, uint8_t level )
{
player.setLevel( level );
player.calculateStats();
player.setHp( player.getMaxHp() );
player.setMp( player.getMaxMp() );
Network::Util::Packet::sendBaseParams( player );
Network::Util::Packet::sendHudParam( player );
Network::Util::Packet::sendStatusUpdate( player );
Network::Util::Packet::sendActorControl( player.getInRangePlayerIds( true ), player.getId(), LevelUpEffect, static_cast< uint8_t >( player.getClass() ), player.getLevel(), player.getLevel() - 1 );
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( player, static_cast< uint32_t >( player.getClass() ) );
Common::Service< World::Manager::MapMgr >::ref().updateQuests( player );
}
void PlayerMgr::onSongLearned( Entity::Player& player, uint8_t songId, uint32_t itemId )
{
player.learnSong( songId, itemId );
Network::Util::Packet::sendActorControlSelf( player, player.getId(), ToggleOrchestrionUnlock, songId, 1, itemId );
}

View file

@ -34,6 +34,20 @@ namespace Sapphire::World::Manager
void onUpdate( Sapphire::Entity::Player& player, uint64_t tickCount );
void onGainExp( Sapphire::Entity::Player& player, uint32_t exp );
void onDiscoverArea( Sapphire::Entity::Player& player, int16_t mapId, int16_t subId );
void onUpdateHuntingLog( Sapphire::Entity::Player& player, uint8_t id );
void onExitInstance( Sapphire::Entity::Player& player );
void onClassJobChanged( Sapphire::Entity::Player& player, Common::ClassJob classJob );
void onLevelChanged( Sapphire::Entity::Player& player, uint8_t level );
void onSongLearned( Sapphire::Entity::Player& player, uint8_t songId, uint32_t itemId );
//////////// Helpers
static void sendServerNotice( Sapphire::Entity::Player& player, const std::string& message );

View file

@ -9,6 +9,8 @@
#include "Network/GameConnection.h"
#include <Manager/PlayerMgr.h>
#include "QuestMgr.h"
#include "AchievementMgr.h"
@ -60,6 +62,7 @@ void QuestMgr::onRemoveQuest( Entity::Player &player, uint8_t questIndex )
bool QuestMgr::giveQuestRewards( Entity::Player& player, uint16_t questId, uint32_t optionalChoice )
{
auto& playerMgr = Common::Service< World::Manager::PlayerMgr >::ref();
auto& exdData = Common::Service< Data::ExdData >::ref();
auto questInfo = exdData.getRow< Excel::Quest >( static_cast< uint32_t >( Event::EventHandler::EventHandlerType::Quest ) << 16 | questId );
@ -71,7 +74,9 @@ bool QuestMgr::giveQuestRewards( Entity::Player& player, uint16_t questId, uint3
// TODO: check if there is room in inventory, else return false;
if( exp > 0 )
player.gainExp( exp );
{
playerMgr.onGainExp( player, exp );
}
for( uint32_t i = 0; i < 6; i++ )
{

View file

@ -225,7 +225,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
targetPlayer->getTerritoryTypeId(),
static_cast< uint8_t >( targetPlayer->getClass() ),
targetPlayer->getLevel(),
targetPlayer->getExp(),
targetPlayer->getCurrentExp(),
targetPlayer->getSearchMessage(),
targetPlayer->getPlayTime() );
break;
@ -253,7 +253,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
}
case GmCommand::Kill:
{
targetActor->getAsChara()->takeDamage( 9999999 );
targetActor->getAsChara()->takeDamage( 0xFFFFFFFF );
PlayerMgr::sendServerNotice( player, "Killed {0}", targetActor->getId());
break;
}
@ -307,7 +307,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
}
case GmCommand::Exp:
{
targetPlayer->gainExp( param1 );
playerMgr().onGainExp( *targetPlayer, param1 );
PlayerMgr::sendServerNotice( player, "{0} Exp was added to {1}", param1, targetPlayer->getName());
break;
}
@ -328,7 +328,9 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
if( param2 == 0 )
{
for( uint8_t i = 0; i < 255; i++ )
targetPlayer->learnSong( i, 0 );
{
playerMgr().onSongLearned( *targetPlayer, i, 0 );
}
PlayerMgr::sendServerNotice( player, "All Songs for {0} were turned on.", targetPlayer->getName() );
}

View file

@ -319,7 +319,7 @@ void Sapphire::Network::GameConnection::newDiscoveryHandler( const Packets::FFXI
discoveryPacket->data().mapId = tInfo->data().Map;
discoveryPacket->data().mapPartId = pRefInfo->data.discoveryIndex;
server().queueForPlayer( player.getCharacterId(), discoveryPacket );
player.discover( tInfo->data().Map, pRefInfo->data.discoveryIndex );
playerMgr().onDiscoverArea( player, tInfo->data().Map, pRefInfo->data.discoveryIndex );
}

View file

@ -84,7 +84,7 @@ void Util::Packet::sendStatusUpdate( Entity::Player& player )
playerStatusUpdate->data().Lv = player.getLevel();
playerStatusUpdate->data().Lv1 = player.getLevel();
playerStatusUpdate->data().LvSync = 0; //player.getLevelSync();
playerStatusUpdate->data().Exp = player.getExp();
playerStatusUpdate->data().Exp = player.getCurrentExp();
server().queueForPlayer( player.getCharacterId(), playerStatusUpdate );
}

View file

@ -57,13 +57,20 @@ Sapphire::StatusEffect::StatusEffect::StatusEffect( uint32_t id, Entity::CharaPt
Util::eraseAll( m_name, ')' );
m_flag |= entry->data().Category;
m_flag |= static_cast< uint32_t >( entry->data().Forever ) << static_cast< uint32_t >( Common::StatusEffectFlag::Permanent );
m_flag |= static_cast< uint32_t >( entry->data().CanOff ) << static_cast< uint32_t >( Common::StatusEffectFlag::CanStatusOff );
m_flag |= static_cast< uint32_t >( entry->data().NotAction ) << static_cast< uint32_t >( Common::StatusEffectFlag::LockActions );
m_flag |= static_cast< uint32_t >( entry->data().NotControl ) << static_cast< uint32_t >( Common::StatusEffectFlag::LockControl );
m_flag |= static_cast< uint32_t >( entry->data().NotMove ) << static_cast< uint32_t >( Common::StatusEffectFlag::LockMovement );
m_flag |= static_cast< uint32_t >( entry->data().NotLookAt ) << static_cast< uint32_t >( Common::StatusEffectFlag::IsGaze );
m_flag |= static_cast< uint32_t >( entry->data().FcAction ) << static_cast< uint32_t >( Common::StatusEffectFlag::FcBuff );
if( entry->data().Forever )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::Permanent );
if( entry->data().CanOff )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::CanStatusOff );
if( entry->data().NotAction )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::LockActions );
if( entry->data().NotControl )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::LockControl );
if( entry->data().NotMove )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::LockMovement );
if( entry->data().NotLookAt )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::IsGaze );
if( entry->data().FcAction )
m_flag |= static_cast< uint32_t >( Common::StatusEffectFlag::FcBuff );
}

View file

@ -104,7 +104,7 @@ void Sapphire::InstanceContent::onPlayerZoneIn( Entity::Player& player )
if( isTerminationReady() ) // wtf
{
Logger::warn( "Entity#{0} Appear for a terminated instance!", player.getId() );
player.exitInstance();
playerMgr().onExitInstance( player );
return;
}
@ -281,7 +281,7 @@ void Sapphire::InstanceContent::onUpdate( uint64_t tickCount )
return;
auto player = it->second;
player->exitInstance();
playerMgr().onExitInstance( *player );
return;
}
}
@ -316,7 +316,7 @@ void Sapphire::InstanceContent::onEventHandlerOrder( Entity::Player& player, uin
{
case 0: // Leave content
{
player.exitInstance();
playerMgr().onExitInstance( player );
break;
}
case 1: // Force leave ( afk timer )
@ -578,7 +578,7 @@ void Sapphire::InstanceContent::onBeforePlayerZoneIn( Sapphire::Entity::Player&
{
// remove any players from the instance who aren't bound on zone in
if( !isPlayerBound( player.getId() ) )
player.exitInstance();
playerMgr().onExitInstance( player );
// if a player has already spawned once inside this instance, don't move them if they happen to zone in again
if( !hasPlayerPreviouslySpawned( player ) )
@ -713,7 +713,7 @@ void Sapphire::InstanceContent::unbindPlayer( uint32_t playerId )
auto it = m_playerMap.find( playerId );
if( it != m_playerMap.end() )
it->second->exitInstance();
playerMgr().onExitInstance( *it->second );
}
void Sapphire::InstanceContent::clearDirector( Entity::Player& player )

View file

@ -158,7 +158,7 @@ void Sapphire::QuestBattle::onUpdate( uint64_t tickCount )
if( ( static_cast< int64_t >( tickCount ) - static_cast< int64_t >( m_instanceFailTime ) ) > 6000 )
{
m_pPlayer->exitInstance();
playerMgr().onExitInstance( *m_pPlayer );
m_pPlayer.reset();
}
break;
@ -322,7 +322,7 @@ void Sapphire::QuestBattle::success()
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onDutyComplete( *this, *m_pPlayer );
player.exitInstance();
playerMgr().onExitInstance( player );
} );
} );