mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-30 16:17:46 +00:00
Merge pull request #850 from hkAlice/3.3
[3.x] quest achievement implementation; prototype history; fix achv exd for 3.3; fix issue with achv list range;
This commit is contained in:
commit
30ed230187
9 changed files with 109 additions and 10 deletions
3
deps/datReader/Exd/Structs.h
vendored
3
deps/datReader/Exd/Structs.h
vendored
|
@ -838,9 +838,10 @@ namespace Excel
|
||||||
uint16_t Priority;
|
uint16_t Priority;
|
||||||
uint8_t Category;
|
uint8_t Category;
|
||||||
uint8_t Point;
|
uint8_t Point;
|
||||||
|
int8_t UnknownConditional; // previously 2.3~3.05 padding0[0]
|
||||||
uint8_t ConditionType;
|
uint8_t ConditionType;
|
||||||
uint8_t Detail;
|
uint8_t Detail;
|
||||||
int8_t padding0[2];
|
int8_t padding0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 200982 */
|
/* 200982 */
|
||||||
|
|
|
@ -1014,10 +1014,10 @@ namespace Sapphire::Common
|
||||||
Classjob,
|
Classjob,
|
||||||
Unknown_4,// Materia related? id 304
|
Unknown_4,// Materia related? id 304
|
||||||
Unknown_5,// Hunt related? id 1259
|
Unknown_5,// Hunt related? id 1259
|
||||||
QuestUnk_6,
|
Quest, // Quests that need all required args met
|
||||||
Unknown_7,
|
Unknown_7,
|
||||||
Unknown_8,// Map discovery related
|
Unknown_8,// Map discovery related
|
||||||
QuestUnk_9,
|
QuestAny,// Quests that need any required args met
|
||||||
ChocoboRank,
|
ChocoboRank,
|
||||||
PvPRank,
|
PvPRank,
|
||||||
WolvesDenMatches,
|
WolvesDenMatches,
|
||||||
|
|
|
@ -1323,6 +1323,11 @@ Player::AchievementDataList& Player::getAchievementDataList()
|
||||||
return m_achievementData;
|
return m_achievementData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player::AchievementHistory& Player::getAchievementHistory()
|
||||||
|
{
|
||||||
|
return m_achievementHistory;
|
||||||
|
}
|
||||||
|
|
||||||
void Player::setMaxGearSets( uint8_t amount )
|
void Player::setMaxGearSets( uint8_t amount )
|
||||||
{
|
{
|
||||||
if( amount == 1 )
|
if( amount == 1 )
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace Sapphire::Entity
|
||||||
public:
|
public:
|
||||||
using AchievementDataList = std::map< uint32_t, uint32_t >;
|
using AchievementDataList = std::map< uint32_t, uint32_t >;
|
||||||
using AchievementList = std::array< uint8_t, 2048 / 8 >; // up to 2048 achievements
|
using AchievementList = std::array< uint8_t, 2048 / 8 >; // up to 2048 achievements
|
||||||
|
using AchievementHistory = std::array< uint16_t, 5 >;
|
||||||
using TitleList = std::array< uint8_t, 48 >;
|
using TitleList = std::array< uint8_t, 48 >;
|
||||||
using HowToList = std::array< uint8_t, 34 >;
|
using HowToList = std::array< uint8_t, 34 >;
|
||||||
using MinionList = std::array< uint8_t, 40 >;
|
using MinionList = std::array< uint8_t, 40 >;
|
||||||
|
@ -373,6 +374,9 @@ namespace Sapphire::Entity
|
||||||
/*! get player's achievement data list */
|
/*! get player's achievement data list */
|
||||||
AchievementDataList& getAchievementDataList();
|
AchievementDataList& getAchievementDataList();
|
||||||
|
|
||||||
|
/*! get player's achievement data history */
|
||||||
|
AchievementHistory& getAchievementHistory();
|
||||||
|
|
||||||
/*! set number of gear sets */
|
/*! set number of gear sets */
|
||||||
void setMaxGearSets( uint8_t amount );
|
void setMaxGearSets( uint8_t amount );
|
||||||
|
|
||||||
|
@ -870,6 +874,7 @@ namespace Sapphire::Entity
|
||||||
|
|
||||||
AchievementList m_achievementList{};
|
AchievementList m_achievementList{};
|
||||||
AchievementDataList m_achievementData{};
|
AchievementDataList m_achievementData{};
|
||||||
|
AchievementHistory m_achievementHistory{};
|
||||||
uint16_t m_activeTitle{};
|
uint16_t m_activeTitle{};
|
||||||
TitleList m_titleList{};
|
TitleList m_titleList{};
|
||||||
HowToList m_howTo{};
|
HowToList m_howTo{};
|
||||||
|
|
|
@ -19,10 +19,10 @@ void Sapphire::Entity::Player::finishQuest( uint16_t questId, uint32_t optionalC
|
||||||
|
|
||||||
auto& questMgr = Common::Service< World::Manager::QuestMgr >::ref();
|
auto& questMgr = Common::Service< World::Manager::QuestMgr >::ref();
|
||||||
|
|
||||||
|
updateQuestsCompleted( questId );
|
||||||
|
|
||||||
//@todo should probably be changed to a bool function in case reward can not be obtained as quests will not complete in that case
|
//@todo should probably be changed to a bool function in case reward can not be obtained as quests will not complete in that case
|
||||||
questMgr.onCompleteQuest( *this, questId, optionalChoice );
|
questMgr.onCompleteQuest( *this, questId, optionalChoice );
|
||||||
|
|
||||||
updateQuestsCompleted( questId );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sapphire::Entity::Player::unfinishQuest( uint16_t questId )
|
void Sapphire::Entity::Player::unfinishQuest( uint16_t questId )
|
||||||
|
|
|
@ -57,6 +57,13 @@ void AchievementMgr::unlockAchievement( Entity::Player& player, uint32_t achieve
|
||||||
|
|
||||||
player.getAchievementList()[ index ] |= value;
|
player.getAchievementList()[ index ] |= value;
|
||||||
|
|
||||||
|
// handle player achievement history
|
||||||
|
// todo: verify retail behavior due to client copying the last achievement unlocked
|
||||||
|
/* auto& achvHistory = player.getAchievementHistory();
|
||||||
|
|
||||||
|
std::rotate( achvHistory.rbegin(), achvHistory.rbegin() + 1, achvHistory.rend() );
|
||||||
|
achvHistory[ 0 ] = achievementId;*/
|
||||||
|
|
||||||
// fire packets
|
// fire packets
|
||||||
Common::Service< World::Manager::PlayerMgr >::ref().onUnlockAchievement( player, achievementId );
|
Common::Service< World::Manager::PlayerMgr >::ref().onUnlockAchievement( player, achievementId );
|
||||||
|
|
||||||
|
@ -74,7 +81,7 @@ bool AchievementMgr::hasAchievementUnlocked( Entity::Player& player, uint32_t ac
|
||||||
{
|
{
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
Common::Util::valueToFlagByteIndexValue( static_cast< uint16_t >( achievementId ), value, index );
|
Common::Util::valueToFlagByteIndexValue( achievementId, value, index );
|
||||||
|
|
||||||
return ( player.getAchievementList()[ index ] & value ) != 0;
|
return ( player.getAchievementList()[ index ] & value ) != 0;
|
||||||
}
|
}
|
||||||
|
@ -138,9 +145,6 @@ void AchievementMgr::handleLinkedAchievementsForId( Entity::Player& player, uint
|
||||||
bool hasAllAchievements = true;
|
bool hasAllAchievements = true;
|
||||||
for( const auto linkedAchvId : linkedAchv )
|
for( const auto linkedAchvId : linkedAchv )
|
||||||
{
|
{
|
||||||
if( linkedAchvId == 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( !hasAchievementUnlocked( player, linkedAchvId ) )
|
if( !hasAchievementUnlocked( player, linkedAchvId ) )
|
||||||
{
|
{
|
||||||
hasAllAchievements = false;
|
hasAllAchievements = false;
|
||||||
|
|
|
@ -119,4 +119,83 @@ namespace Sapphire::World::Manager
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline void AchievementMgr::progressAchievement< Common::Achievement::Type, Common::Achievement::Type::Quest >( Entity::Player& player, int32_t questId, uint32_t unused )
|
||||||
|
{
|
||||||
|
auto& achvDataList = player.getAchievementDataList();
|
||||||
|
|
||||||
|
// get achievements that need all achv in args completed
|
||||||
|
const auto questAchvAllList = getAchievementIdByType( Common::Achievement::Type::Quest );
|
||||||
|
// get achievements that need any of the achvs in args completed
|
||||||
|
const auto questAchvAnyList = getAchievementIdByType( Common::Achievement::Type::QuestAny );
|
||||||
|
|
||||||
|
// handle achv type for all quests in args completed
|
||||||
|
for( auto achvId : questAchvAllList )
|
||||||
|
{
|
||||||
|
if( hasAchievementUnlocked( player, achvId ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto pAchv = getAchievementDetail( achvId );
|
||||||
|
|
||||||
|
auto achvExdData = pAchv->data();
|
||||||
|
|
||||||
|
std::set< uint16_t > linkedQuests{ std::make_move_iterator( std::begin( achvExdData.ConditionArg ) ),
|
||||||
|
std::make_move_iterator( std::end( achvExdData.ConditionArg ) ) };
|
||||||
|
|
||||||
|
// clear empty achievement links
|
||||||
|
linkedQuests.erase( 0 );
|
||||||
|
|
||||||
|
// check if passed quest ID is tied to this achievement
|
||||||
|
if( !linkedQuests.count( questId ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// verify if player has all the required quests completed
|
||||||
|
bool hasAllAchievements = true;
|
||||||
|
for( const auto questId : linkedQuests )
|
||||||
|
{
|
||||||
|
if( !player.isQuestCompleted( questId ) )
|
||||||
|
{
|
||||||
|
hasAllAchievements = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlock achievement if all required quests are completed
|
||||||
|
if( hasAllAchievements )
|
||||||
|
unlockAchievement( player, achvId );
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle achv type for all quests in args completed
|
||||||
|
for( auto achvId : questAchvAnyList )
|
||||||
|
{
|
||||||
|
if( hasAchievementUnlocked( player, achvId ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto pAchv = getAchievementDetail( achvId );
|
||||||
|
|
||||||
|
auto achvExdData = pAchv->data();
|
||||||
|
|
||||||
|
std::set< int32_t > linkedQuests{ std::make_move_iterator( std::begin( achvExdData.ConditionArg ) ),
|
||||||
|
std::make_move_iterator( std::end( achvExdData.ConditionArg ) ) };
|
||||||
|
|
||||||
|
// clear empty quest ids
|
||||||
|
linkedQuests.erase( 0 );
|
||||||
|
|
||||||
|
// check if passed quest ID is tied to this achievement
|
||||||
|
if( !linkedQuests.count( questId ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// verify if player has any of the required quests completed
|
||||||
|
bool hasAllAchievements = true;
|
||||||
|
for( const auto questId : linkedQuests )
|
||||||
|
{
|
||||||
|
if( player.isQuestCompleted( questId ) )
|
||||||
|
{
|
||||||
|
unlockAchievement( player, achvId );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,8 @@ void PlayerMgr::onSendAchievementList( Entity::Player& player )
|
||||||
auto& server = Common::Service< World::WorldServer >::ref();
|
auto& server = Common::Service< World::WorldServer >::ref();
|
||||||
|
|
||||||
auto achvPacket = makeZonePacket< FFXIVIpcAchievement >( player.getId() );
|
auto achvPacket = makeZonePacket< FFXIVIpcAchievement >( player.getId() );
|
||||||
std::memcpy( &achvPacket->data().complete[ 0 ], &player.getAchievementList()[ 0 ], sizeof( &achvPacket->data().complete ) );
|
std::memcpy( &achvPacket->data().complete[ 0 ], &player.getAchievementList()[ 0 ], sizeof( achvPacket->data().complete ) );
|
||||||
|
std::memcpy( &achvPacket->data().history[ 0 ], &player.getAchievementHistory()[ 0 ], sizeof( achvPacket->data().history ) );
|
||||||
|
|
||||||
server.queueForPlayer( player.getCharacterId(), achvPacket );
|
server.queueForPlayer( player.getCharacterId(), achvPacket );
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Network/PacketWrappers/Notice2Packet.h"
|
#include "Network/PacketWrappers/Notice2Packet.h"
|
||||||
|
|
||||||
#include "QuestMgr.h"
|
#include "QuestMgr.h"
|
||||||
|
#include "AchievementMgr.h"
|
||||||
|
|
||||||
#include "Actor/Player.h"
|
#include "Actor/Player.h"
|
||||||
|
|
||||||
|
@ -41,6 +42,9 @@ void QuestMgr::onCompleteQuest( Entity::Player& player, uint16_t questId, uint32
|
||||||
server.queueForPlayer( player.getCharacterId(), questFinishPacket );
|
server.queueForPlayer( player.getCharacterId(), questFinishPacket );
|
||||||
|
|
||||||
giveQuestRewards( player, questId, optionalChoice );
|
giveQuestRewards( player, questId, optionalChoice );
|
||||||
|
|
||||||
|
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
|
||||||
|
achvMgr.progressAchievementByType< Common::Achievement::Type::Quest >( player, questId );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuestMgr::onRemoveQuest( Entity::Player &player, uint8_t questIndex )
|
void QuestMgr::onRemoveQuest( Entity::Player &player, uint8_t questIndex )
|
||||||
|
|
Loading…
Add table
Reference in a new issue