diff --git a/src/common/Common.h b/src/common/Common.h index bceab9c0..c54db478 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/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 9f9429b5..ca796d7f 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -707,7 +707,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/Player.cpp b/src/world/Actor/Player.cpp index 4a1a50f3..53cb131f 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -474,60 +474,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; @@ -621,13 +567,13 @@ bool Player::hasMount( uint32_t mountId ) const void Player::gainExp( uint32_t amount ) { - uint32_t currentExp = getExp(); + uint32_t currentExp = getCurrentExp(); uint16_t level = getLevel(); auto currentClass = static_cast< uint8_t >( getClass() ); if( level >= Common::MAX_PLAYER_LEVEL ) { - setExp( 0 ); + setCurrentExp( 0 ); if( currentExp != 0 ) Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, 0 ); @@ -647,14 +593,14 @@ void Player::gainExp( uint32_t amount ) if( level + 1 >= Common::MAX_PLAYER_LEVEL ) amount = 0; - setExp( amount ); + setCurrentExp( amount ); levelUp(); } else - setExp( currentExp + amount ); + setCurrentExp( currentExp + amount ); Network::Util::Packet::sendActorControlSelf( *this, getId(), GainExpMsg, currentClass, amount ); - Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, getExp() ); + Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, getCurrentExp() ); } void Player::levelUp() @@ -696,14 +642,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; @@ -1591,75 +1537,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().MainClass; - - auto& logEntry = m_huntingLogEntries[ currentClassId - 1 ]; - - 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; diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 4ebea855..fcb18903 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,10 +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 ); + void setCurrentExp( uint32_t amount ); /*! adds exp to the currently active class / job */ void gainExp( uint32_t amount ); @@ -423,9 +423,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(); @@ -781,8 +778,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/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/PlayerMgr.cpp b/src/world/Manager/PlayerMgr.cpp index 15f7ad51..837b4e51 100644 --- a/src/world/Manager/PlayerMgr.cpp +++ b/src/world/Manager/PlayerMgr.cpp @@ -3,7 +3,7 @@ #include #include - +#include #include #include @@ -206,7 +206,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 ) @@ -341,6 +341,99 @@ 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; + + player.setCurrentExp( exp ); + player.levelUp(); + } + 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 /////////// @@ -364,3 +457,75 @@ void PlayerMgr::sendLogMessage( Entity::Player& player, uint32_t messageId, uint { Network::Util::Packet::sendActorControlTarget( player, player.getId(), LogMsg, messageId, param2, param3, param4, param5, param6 ); } + +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().MainClass; + + auto& logEntry = player.getHuntingLogEntry( currentClassId - 1 ); + + 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 ); +} + + + diff --git a/src/world/Manager/PlayerMgr.h b/src/world/Manager/PlayerMgr.h index 4777f699..25322a1a 100644 --- a/src/world/Manager/PlayerMgr.h +++ b/src/world/Manager/PlayerMgr.h @@ -34,6 +34,12 @@ 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 ); + //////////// Helpers static void sendServerNotice( Sapphire::Entity::Player& player, const std::string& message ); diff --git a/src/world/Manager/QuestMgr.cpp b/src/world/Manager/QuestMgr.cpp index d1afbcc5..52aac52e 100644 --- a/src/world/Manager/QuestMgr.cpp +++ b/src/world/Manager/QuestMgr.cpp @@ -9,6 +9,8 @@ #include "Network/GameConnection.h" +#include + #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++ ) { diff --git a/src/world/Network/Handlers/GMCommandHandlers.cpp b/src/world/Network/Handlers/GMCommandHandlers.cpp index 58dfa845..627fbb04 100644 --- a/src/world/Network/Handlers/GMCommandHandlers.cpp +++ b/src/world/Network/Handlers/GMCommandHandlers.cpp @@ -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; @@ -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; } diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index c66b80d0..3912ca60 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -318,7 +318,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 ); } diff --git a/src/world/Network/Util/PacketUtil.cpp b/src/world/Network/Util/PacketUtil.cpp index 842fa03c..046c0a3a 100644 --- a/src/world/Network/Util/PacketUtil.cpp +++ b/src/world/Network/Util/PacketUtil.cpp @@ -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 ); }