1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-23 13:17:45 +00:00

AchievementMgr framework; styling fixes;

This commit is contained in:
Alice Ogeda 2023-01-19 13:03:54 -03:00
parent bd1753423e
commit 34213990bc
15 changed files with 664 additions and 13 deletions

2
.gitignore vendored
View file

@ -27,6 +27,8 @@ src/tools/bin/generated/*
build/ build/
build-*/ build-*/
cmake-build-*/ cmake-build-*/
bin/
out/
# Prerequisites # Prerequisites
*.d *.d

View file

@ -1000,6 +1000,216 @@ namespace Sapphire::Common
InvincibilityIgnoreDamage, InvincibilityIgnoreDamage,
}; };
namespace Achievement
{
enum class Type : uint8_t
{
None,
General,
LinkedAchievement,
Classjob,
Unknown_4,// Materia related? id 304
Unknown_5,// Hunt related? id 1259
QuestUnk_6,
Unknown_7,
Unknown_8,// Map discovery related
QuestUnk_9,
ChocoboRank,
PvPRank,
WolvesDenMatches,
WolvesDenWins,
InstanceContent,
BeastTribeReputation,
Unknown_16,
FrontlineMatches,
FrontlineWinsGC,
FrontlineWinsAll,
AetherCurrent,
};
namespace GeneralSubtype
{
enum AchievementGeneralSubtype : int32_t
{
EnemyDefeatCount = 11,
GilEnemySource = 12,
GilLevequestSource = 13,
FactionLevequestCompleted = 14,
GuildhestCompleted = 15,
MateriaAffixCount = 16,
SpiritboundMateriaCount = 17,
RegionalBattleLevequestCompleted = 18,
LocalTradeLevequestCompleted = 19,
// Legacy subtypes - skipping
#pragma region Synth 1 -50 Recipes
SynthWoodworkingLv01to10Recipes = 23,
SynthWoodworkingLv11to20Recipes = 24,
SynthWoodworkingLv21to30Recipes = 25,
SynthWoodworkingLv31to40Recipes = 26,
SynthWoodworkingLv41to50Recipes = 27,
SynthSmithingLv01to10Recipes = 28,
SynthSmithingLv11to20Recipes = 29,
SynthSmithingLv21to30Recipes = 30,
SynthSmithingLv31to40Recipes = 31,
SynthSmithingLv41to50Recipes = 32,
SynthArmorcraftLv01to10Recipes = 33,
SynthArmorcraftLv11to20Recipes = 34,
SynthArmorcraftLv21to30Recipes = 35,
SynthArmorcraftLv31to40Recipes = 36,
SynthArmorcraftLv41to50Recipes = 37,
SynthGoldsmithingLv01to10Recipes = 38,
SynthGoldsmithingLv11to20Recipes = 39,
SynthGoldsmithingLv21to30Recipes = 40,
SynthGoldsmithingLv31to40Recipes = 41,
SynthGoldsmithingLv41to50Recipes = 42,
SynthLeatherworkingLv01to10Recipes = 43,
SynthLeatherworkingLv11to20Recipes = 44,
SynthLeatherworkingLv21to30Recipes = 45,
SynthLeatherworkingLv31to40Recipes = 46,
SynthLeatherworkingLv41to50Recipes = 47,
SynthClothcraftLv01to10Recipes = 48,
SynthClothcraftLv11to20Recipes = 49,
SynthClothcraftLv21to30Recipes = 50,
SynthClothcraftLv31to40Recipes = 51,
SynthClothcraftLv41to50Recipes = 52,
SynthAlchemyLv01to10Recipes = 53,
SynthAlchemyLv11to20Recipes = 54,
SynthAlchemyLv21to30Recipes = 55,
SynthAlchemyLv31to40Recipes = 56,
SynthAlchemyLv41to50Recipes = 57,
SynthCookingLv01to10Recipes = 58,
SynthCookingLv11to20Recipes = 59,
SynthCookingLv21to30Recipes = 60,
SynthCookingLv31to40Recipes = 61,
SynthCookingLv41to50Recipes = 62,
#pragma endregion
// TODO: Map gathering subtypes 63 to 128
GCStormSeals = 138,
GCSerpentSeals = 139,
GCFlameSeals = 140,
MaelstromLevequestCompleted = 141,
TwinAdderLevequestCompleted = 142,
ImmortalFlamesLevequestCompleted = 143,
MaelstromSupplyCompleted = 144,
TwinAdderSupplyCompleted = 145,
ImmortalFlamesSupplyCompleted = 146,
MaelstromProvisioningCompleted = 147,
TwinAdderProvisioningCompleted = 148,
ImmortalFlamesProvisioningCompleted = 149,
// Legacy subtypes - skipping
FieldLevequestCompleted = 179,
UniqueBattleLevequestCompleted = 180,
// TODO: Map leve subtypes 181 to 201
UniqueFishCaughtCount = 202,
// TODO: Map craft subtypes 203 to 213
UniqueQuestsCompleted = 214,
InstanceContentCompleted = 215, // Instanced dungeons, raids or trials
UniqueInstanceContentCompleted = 216,// Unique instanced dungeons, raids or trials
BindingCoilCompleted = 217,
UniqueGuildhestCompleted = 218,
FateCompleted = 219,
// TODO: Map subtypes 220 to 235
ChocoboRidePimpedOut = 236,
MaelstromUniqueLeveCompleted = 238,
TwinAdderUniqueLeveCompleted = 239,
ImmortalFlamesUniqueLeveCompleted = 240,
EnlistGCMaelstrom = 241,
EnlistGCTwinAdder = 242,
EnlistGCImmortalFlames = 243,
CommendationCount = 244,
// TODO: Map subtypes 245 to 250
ResurrectStrangers = 251,
SecondCoilCompleted = 266,
UniqueBigFishCaught = 267,
RetainerVentureCompleted = 268,
HighLvlDutyPaladinCompleted = 277,
HighLvlDutyWarriorCompleted = 278,
RelicAnimus = 279,
RelicNovus = 280,
HigherGradeMateriaFromTransmutation = 281,
ARRSightseeingLogCount = 284,
InterceptorDroneDefeated = 285,
InterceptorNodeDefeated = 286,
TrainAnotherChocoboCount = 287,
HuntRankBDefeated = 290,
HuntRankADefeated = 291,
HuntRankSDefeated = 292,
LootCofferLeatherMap = 293,
DesynthesizeItemCount = 294,
RelicNexus = 347,
FinalCoilCompleted = 348,
RelicZodiac = 349,
RelicZeta = 350,
TripleTriadUniqueCardCount = 352,
TripleTriadNPCDefeatedCount = 353,
TripleTriadRouletteWin = 354,
TripleTriadTournamentWin = 355,
MgpGoldSaucerAttractionSource = 356,
ChocoboRaceCount = 357,
ChocoboRaceWin = 358,
ChocoboCovering = 359,
ChocoboPedigreeLevel = 360,
ChocoboRating = 361,
FrontlineEnemyDispatched = 362,
GATECompleted = 363,
// TODO: Map subtypes 364 to 368
HighLvlDutyDarkKnightCompleted = 369,
GordiasCompleted = 370,
GordiasSavageCompleted = 371,
// TODO: Map subtypes 372 to X
};
}
}
union AchievementDataKey
{
uint32_t u32;
struct AchievementPackedKey {
uint8_t type;
uint16_t subtype;
uint8_t padding0;
} key;
};
enum HierarchyType : uint8_t enum HierarchyType : uint8_t
{ {
NONE_2 = 0x0, NONE_2 = 0x0,

View file

@ -2,6 +2,7 @@
#include "DbWorker.h" #include "DbWorker.h"
#include <MySqlConnector.h> #include <MySqlConnector.h>
#include "Logging/Logger.h" #include "Logging/Logger.h"
#include <cassert>
#include "PreparedStatement.h" #include "PreparedStatement.h"

View file

@ -235,11 +235,12 @@ namespace Sapphire::Network::ActorControl
ArmoryErrorMsg = 0x201, ArmoryErrorMsg = 0x201,
AchievementPopup = 0x203, AchievementSetRate = 0x202,
AchievementComplete = 0x203,
SetCutsceneFlag = 0x204, SetCutsceneFlag = 0x204,
LogMsg = 0x205, // LogMessage? LogMsg = 0x205, // LogMessage?
AchievementMsg = 0x206, AchievementObtainMsg = 0x206,
SetItemLevel = 0x209, SetItemLevel = 0x209,

View file

@ -328,6 +328,12 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
uint32_t Result; uint32_t Result;
}; };
struct FFXIVIpcAchievement : FFXIVIpcBasePacket< Achievement >
{
uint8_t complete[256]; // bitmask of achievements, up to 8 * 256 (2048) achvs
uint16_t history[5]; // last 5 achievement IDs
};
struct ZoneProtoDownLetterBoxAppendItemBase struct ZoneProtoDownLetterBoxAppendItemBase
{ {
uint32_t CatalogID; uint32_t CatalogID;

View file

@ -447,7 +447,7 @@ void Player::teleport( uint16_t aetheryteId, uint8_t type )
// if it is a teleport in the same zone, we want to do warp instead of moveTerri // if it is a teleport in the same zone, we want to do warp instead of moveTerri
bool sameTerritory = getTerritoryTypeId() == data.TerritoryType; bool sameTerritory = getTerritoryTypeId() == data.TerritoryType;
WarpType warpType; WarpType warpType = WarpType::WARP_TYPE_NORMAL;
// TODO: this should be simplified and a type created in server_common/common.h. // TODO: this should be simplified and a type created in server_common/common.h.
if( type == 1 || type == 2 ) // teleport if( type == 1 || type == 2 ) // teleport
{ {
@ -824,7 +824,7 @@ void Player::setLevelForClass( uint8_t level, Common::ClassJob classjob )
m_classArray[ classJobIndex ] = level; m_classArray[ classJobIndex ] = level;
queuePacket( makeActorControlSelf( getId(), Network::ActorControl::ClassJobUpdate, static_cast< uint8_t >( classjob ), level ) ); Service< World::Manager::PlayerMgr >::ref().onSetLevelForClass( *this, classjob );
} }
void Player::sendModel() void Player::sendModel()
@ -1315,6 +1315,16 @@ void Player::setTitle( uint16_t titleId )
sendToInRangeSet( makeActorControl( getId(), SetTitle, titleId ), true ); sendToInRangeSet( makeActorControl( getId(), SetTitle, titleId ), true );
} }
Player::AchievementList& Player::getAchievementList()
{
return m_achievementList;
}
Player::AchievementDataList& Player::getAchievementDataList()
{
return m_achievementData;
}
void Player::setMaxGearSets( uint8_t amount ) void Player::setMaxGearSets( uint8_t amount )
{ {
if( amount == 1 ) if( amount == 1 )

View file

@ -25,6 +25,8 @@ namespace Sapphire::Entity
class Player : public Chara class Player : public Chara
{ {
public: public:
using AchievementDataList = std::map< uint32_t, uint32_t >;
using AchievementList = std::array< uint8_t, 2048 / 8 >; // up to 2048 achievements
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 >;
@ -365,7 +367,13 @@ namespace Sapphire::Entity
/*! send the players title list */ /*! send the players title list */
void sendTitleList(); void sendTitleList();
/*! set number of gear sets */ /*! get player's achievement list */
AchievementList& getAchievementList();
/*! get player's achievement data list */
AchievementDataList& getAchievementDataList();
/*! set number of gear sets */
void setMaxGearSets( uint8_t amount ); void setMaxGearSets( uint8_t amount );
/*! get number of gear sets */ /*! get number of gear sets */
@ -860,6 +868,8 @@ namespace Sapphire::Entity
uint8_t status; uint8_t status;
} m_retainerInfo[8]{}; } m_retainerInfo[8]{};
AchievementList m_achievementList{};
AchievementDataList m_achievementData{};
uint16_t m_activeTitle{}; uint16_t m_activeTitle{};
TitleList m_titleList{}; TitleList m_titleList{};
HowToList m_howTo{}; HowToList m_howTo{};

View file

@ -0,0 +1,192 @@
#include <Exd/ExdData.h>
#include <Util/Util.h>
#include "AchievementMgr.h"
#include "PlayerMgr.h"
using namespace Sapphire;
using namespace Sapphire::Network;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::World::Manager;
bool AchievementMgr::cacheAchievements()
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto idList = exdData.getIdList< Excel::Achievement >();
for( auto id : idList )
{
auto achvExdData = exdData.getRow< Excel::Achievement >( id );
uint32_t key = achvExdData->data().ConditionType;
auto achvType = static_cast< Common::Achievement::Type >( key );
if( achvType == Common::Achievement::Type::None )
continue;
// verify if achievement type has subtype
if( achvType == Common::Achievement::Type::General ||
achvType == Common::Achievement::Type::Classjob ||
achvType == Common::Achievement::Type::InstanceContent )
{
int32_t subtype = achvExdData->data().ConditionArg[ 0 ];
if( subtype != 0 )
key = ( getKeyFromType( achvType, subtype ) ).u32;
else
continue; // ignore key types with no subtype
}
// map achievement IDs to achv data
m_achievementKeyCacheMap[ key ].emplace_back( id );
// map achievement keys (either type or union key:subtype) to achievement IDs
m_achievementDetailCacheMap[ id ] = std::move( achvExdData );
}
return true;
}
void AchievementMgr::unlockAchievement( Entity::Player& player, uint32_t achievementId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto achvData = exdData.getRow< Excel::Achievement >( achievementId );
// set flag on mask format expected by client
uint16_t index;
uint8_t value;
Common::Util::valueToFlagByteIndexValue( achievementId, value, index );
player.getAchievementList()[ index ] |= value;
// fire packets
Common::Service< World::Manager::PlayerMgr >::ref().onUnlockAchievement( player, achievementId );
// check and add title to player
auto achvTitleId = achvData->data().Title;
if( achvTitleId != 0 )
{
player.addTitle( achvTitleId );
}
handleLinkedAchievementsForId( player, achievementId );
}
bool AchievementMgr::hasAchievementUnlocked( Entity::Player& player, uint32_t achievementId )
{
uint16_t index;
uint8_t value;
Common::Util::valueToFlagByteIndexValue( static_cast< uint16_t >( achievementId ), value, index );
return ( player.getAchievementList()[ index ] & value ) != 0;
}
std::pair< uint32_t, uint32_t > AchievementMgr::getAchievementDataById( Entity::Player& player, uint32_t achievementId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto& achvDataList = player.getAchievementDataList();
auto achvExdData = exdData.getRow< Excel::Achievement >( achievementId )->data();
auto achvType = static_cast< Common::Achievement::Type >( achvExdData.ConditionType );
// get paired type:subtype key for stored data
auto dataKey = getKeyFromType( achvType, achvExdData.ConditionArg[ 0 ] );
// get achievement progress data, if it exists (otherwise pass 0)
uint32_t currProg = 0;
if( achvDataList.count( dataKey.u32 ) )
currProg = achvDataList[ dataKey.u32 ];
// get maximum progress for given achievement, as required by client
uint32_t maxProg = static_cast< uint32_t >( achvExdData.ConditionArg[ 1 ] );
// cap maximum progress display to maximum progress
return { std::min( currProg, maxProg ), maxProg };
}
void AchievementMgr::handleLinkedAchievementsForId( Entity::Player& player, uint32_t achievementId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
const auto& linkedAchievementIdList = getAchievementIdByType( Common::Achievement::Type::LinkedAchievement );
for( auto& achvId : linkedAchievementIdList )
{
// skip if achievement already unlocked
if( hasAchievementUnlocked( player, achvId ) )
continue;
auto pAchv = getAchievementDetail( achvId );
if( !pAchv )
continue;
auto achvExdData = pAchv->data();
// if achievement has other achievements linked to it
if( achvExdData.ConditionType == static_cast< uint8_t >( Common::Achievement::Type::LinkedAchievement ) )
{
// get all linked achievements needed to unlock
std::set< int32_t > linkedAchv{ std::make_move_iterator( std::begin( achvExdData.ConditionArg ) ),
std::make_move_iterator( std::end( achvExdData.ConditionArg ) ) };
// clear empty achievement links
linkedAchv.erase( 0 );
// check if passed achievement ID is tied to this linked achievement
if( !linkedAchv.count( achievementId ) )
continue;
// verify if player has all the required achievements unlocked
bool hasAllAchievements = true;
for( const auto linkedAchvId : linkedAchv )
{
if( linkedAchvId == 0 )
continue;
if( !hasAchievementUnlocked( player, linkedAchvId ) )
{
hasAllAchievements = false;
break;
}
}
// unlock achievement if linked achievement conditions are met
if( hasAllAchievements )
unlockAchievement( player, achvId );
}
}
}
Common::AchievementDataKey AchievementMgr::getKeyFromType( Common::Achievement::Type achvType, int32_t argument )
{
Common::AchievementDataKey dataKey{ 0 };
dataKey.key.type = static_cast< uint8_t >( achvType );
dataKey.key.subtype = static_cast< uint16_t >( argument );
return dataKey;
}
const std::vector< uint32_t >& AchievementMgr::getAchievementIdByType( Common::Achievement::Type type ) const
{
return getAchievementIdByType( static_cast< uint32_t >( type ) );
}
const std::vector< uint32_t >& AchievementMgr::getAchievementIdByType( uint32_t type ) const
{
auto it = m_achievementKeyCacheMap.find( type );
if( it != std::end( m_achievementKeyCacheMap ) )
return it->second;
else
return {};
}
std::shared_ptr< Excel::ExcelStruct< Excel::Achievement > > AchievementMgr::getAchievementDetail( uint32_t achvId ) const
{
auto it = m_achievementDetailCacheMap.find( achvId );
if( it != std::end( m_achievementDetailCacheMap ) )
return it->second;
else
return nullptr;
}

View file

@ -0,0 +1,122 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <type_traits>
#include <Service.h>
#include <Common.h>
#include "Actor/Player.h"
namespace Sapphire::World::Manager
{
class AchievementMgr
{
public:
AchievementMgr() = default;
~AchievementMgr() = default;
bool cacheAchievements();
template < auto AchievementType >
void progressAchievementByType( Entity::Player& player, int32_t argument, uint32_t progressCount = 1 )
{
progressAchievement< decltype( AchievementType ), AchievementType >( std::move( player ), argument, progressCount );
}
bool hasAchievementUnlocked( Entity::Player& player, uint32_t achievementId );
void unlockAchievement( Entity::Player& player, uint32_t achievementId );
/// <summary>
/// get a pair of current progress and maximum count for a given achievement ID
/// </summary>
/// <param name="player"></param>
/// <param name="achievementId"></param>
/// <returns>pair of current and maximum progress</returns>
std::pair< uint32_t, uint32_t > getAchievementDataById( Entity::Player& player, uint32_t achievementId );
private:
// map achievement IDs to achv data
using AchievementDetailCache = std::unordered_map< uint32_t, std::shared_ptr< Excel::ExcelStruct< Excel::Achievement > > >;
// map achievement keys (either type or union key:subtype) to achievement IDs
using AchievementKeyCache = std::unordered_map< uint32_t, std::vector< uint32_t > >;
AchievementDetailCache m_achievementDetailCacheMap;
AchievementKeyCache m_achievementKeyCacheMap;
std::shared_ptr< Excel::ExcelStruct< Excel::Achievement > > getAchievementDetail( uint32_t achvId ) const;
const std::vector< uint32_t >& getAchievementIdByType( Common::Achievement::Type type ) const;
const std::vector< uint32_t >& getAchievementIdByType( uint32_t type ) const;
Common::AchievementDataKey getKeyFromType( Common::Achievement::Type achvType, int32_t argument );
void handleLinkedAchievementsForId( Entity::Player& player, uint32_t achievementId );
template< typename AchievementTypeT, AchievementTypeT achievementType >
inline void progressAchievement( Entity::Player& player, int32_t argument, uint32_t progressCount );
};
template<>
inline void AchievementMgr::progressAchievement< Common::Achievement::Type, Common::Achievement::Type::General >( Entity::Player& player, int32_t subtype, uint32_t progressCount )
{
auto& achvDataList = player.getAchievementDataList();
auto dataKey = getKeyFromType( Common::Achievement::Type::General, subtype );
if( !achvDataList.count( dataKey.u32 ) )
achvDataList[ dataKey.u32 ] = 0;
achvDataList[ dataKey.u32 ] += progressCount;
const auto achvIdList = getAchievementIdByType( dataKey.u32 );
for( auto achvId : achvIdList )
{
if( hasAchievementUnlocked( player, achvId ) )
continue;
auto pAchv = getAchievementDetail( achvId );
if( !pAchv )
continue;
auto achvExdData = pAchv->data();
if( achvExdData.ConditionArg[ 1 ] <= achvDataList[ dataKey.u32 ] )
unlockAchievement( player, achvId );
}
}
template<>
inline void AchievementMgr::progressAchievement< Common::Achievement::Type, Common::Achievement::Type::Classjob >( Entity::Player& player, int32_t classJob, uint32_t level )
{
auto& achvDataList = player.getAchievementDataList();
auto dataKey = getKeyFromType( Common::Achievement::Type::Classjob, classJob );
if( !achvDataList.count( dataKey.u32 ) )
achvDataList[ dataKey.u32 ] = 0;
achvDataList[ dataKey.u32 ] = level;
const auto achvIdList = getAchievementIdByType( dataKey.u32 );
for( auto achvId : achvIdList )
{
if( hasAchievementUnlocked( player, achvId ) )
continue;
auto pAchv = getAchievementDetail( achvId );
if( !pAchv )
continue;
auto achvExdData = pAchv->data();
if( achvExdData.ConditionArg[ 1 ] <= achvDataList[ dataKey.u32 ] )
unlockAchievement( player, achvId );
}
}
}

View file

@ -32,20 +32,23 @@
#include "Territory/HousingZone.h" #include "Territory/HousingZone.h"
#include "Territory/InstanceContent.h" #include "Territory/InstanceContent.h"
#include "Territory/QuestBattle.h" #include "Territory/QuestBattle.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
#include "Manager/PlayerMgr.h" #include "Manager/PlayerMgr.h"
#include "Manager/AchievementMgr.h"
#include "Manager/WarpMgr.h"
#include "Manager/LinkshellMgr.h"
#include "Manager/RNGMgr.h"
#include "Event/EventDefs.h" #include "Event/EventDefs.h"
#include "ContentFinder/ContentFinder.h" #include "ContentFinder/ContentFinder.h"
#include "Manager/LinkshellMgr.h"
#include "Linkshell/Linkshell.h" #include "Linkshell/Linkshell.h"
#include "Manager/WarpMgr.h"
#include "WorldServer.h" #include "WorldServer.h"
#include "Session.h" #include "Session.h"
#include <Manager/RNGMgr.h>
using namespace Sapphire::Network; using namespace Sapphire::Network;
using namespace Sapphire::Network::Packets; using namespace Sapphire::Network::Packets;
@ -554,12 +557,32 @@ void DebugCommandMgr::add( char* data, Entity::Player& player, std::shared_ptr<
pSession->getZoneConnection()->queueOutPacket( effectPacket ); pSession->getZoneConnection()->queueOutPacket( effectPacket );
} }
else if( subCommand == "achv" )
{
uint32_t achvId;
sscanf( params.c_str(), "%u", &achvId );
auto& achvMgr = Common::Service< Manager::AchievementMgr >::ref();
achvMgr.unlockAchievement( player, achvId );
}
else if( subCommand == "achvGeneral" )
{
uint32_t achvSubtype;
uint32_t progress;
sscanf( params.c_str(), "%u %u", &achvSubtype, &progress );
auto& achvMgr = Common::Service< Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::General >( player, achvSubtype, progress );
}
else else
{ {
PlayerMgr::sendUrgent( player, "{0} is not a valid ADD command.", subCommand ); PlayerMgr::sendUrgent( player, "{0} is not a valid ADD command.", subCommand );
} }
} }
void DebugCommandMgr::get( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command ) void DebugCommandMgr::get( char* data, Entity::Player& player, std::shared_ptr< DebugCommand > command )

View file

@ -8,6 +8,7 @@
#include <Manager/TerritoryMgr.h> #include <Manager/TerritoryMgr.h>
#include <Manager/HousingMgr.h> #include <Manager/HousingMgr.h>
#include <Manager/AchievementMgr.h>
#include "Script/ScriptMgr.h" #include "Script/ScriptMgr.h"
#include "WorldServer.h" #include "WorldServer.h"
@ -77,6 +78,37 @@ void PlayerMgr::onSendStateFlags( Entity::Player& player, bool updateInRange )
static_cast< uint8_t >( player.getOnlineStatus() ) ), true ); static_cast< uint8_t >( player.getOnlineStatus() ) ), true );
} }
void PlayerMgr::onSendAchievementList( Entity::Player& player )
{
auto& server = Common::Service< World::WorldServer >::ref();
auto achvPacket = makeZonePacket< FFXIVIpcAchievement >( player.getId() );
std::memcpy( &achvPacket->data().complete[ 0 ], &player.getAchievementList()[ 0 ], sizeof( &achvPacket->data().complete ) );
server.queueForPlayer( player.getCharacterId(), achvPacket );
}
void PlayerMgr::onSendAchievementProgress( Entity::Player& player, uint32_t achievementId )
{
auto& server = Common::Service< World::WorldServer >::ref();
auto& achvMgr = Common::Service< Manager::AchievementMgr >::ref();
auto achvProgress = achvMgr.getAchievementDataById( player, achievementId );
auto pAchvProgressPacket = makeActorControl( player.getId(), AchievementSetRate, achievementId, achvProgress.first, achvProgress.second );
server.queueForPlayer( player.getCharacterId(), pAchvProgressPacket );
}
void PlayerMgr::onUnlockAchievement( Entity::Player& player, uint32_t achievementId )
{
auto& server = Common::Service< World::WorldServer >::ref();
onSendAchievementList( player );
server.queueForPlayer( player.getCharacterId(), makeActorControl( player.getId(), AchievementComplete, achievementId ) );
server.queueForPlayer( player.getCharacterId(), makeActorControl( player.getId(), AchievementObtainMsg, achievementId ) );
}
void PlayerMgr::onSendStats( Entity::Player& player ) void PlayerMgr::onSendStats( Entity::Player& player )
{ {
std::array< uint32_t, 50 > statParams; std::array< uint32_t, 50 > statParams;
@ -144,8 +176,23 @@ void PlayerMgr::onLevelUp( Entity::Player& player )
player.sendToInRangeSet( makeActorControl( player.getId(), LevelUpEffect, static_cast< uint8_t >( player.getClass() ), player.sendToInRangeSet( makeActorControl( player.getId(), LevelUpEffect, static_cast< uint8_t >( player.getClass() ),
player.getLevel(), player.getLevel() - 1 ), true ); player.getLevel(), player.getLevel() - 1 ), true );
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( player, static_cast< uint8_t >( player.getClass() ), player.getLevel() );
} }
void PlayerMgr::onSetLevelForClass( Entity::Player& player, Common::ClassJob classJob )
{
auto& server = Common::Service< World::WorldServer >::ref();
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
server.queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), Network::ActorControl::ClassJobUpdate,
static_cast< uint8_t >( classJob ), player.getLevelForClass( classJob ) ) );
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( player, static_cast< uint8_t >( classJob ), player.getLevel() );
}
void PlayerMgr::onGainExp( Entity::Player& player, uint32_t exp ) void PlayerMgr::onGainExp( Entity::Player& player, uint32_t exp )
{ {
auto& server = Common::Service< World::WorldServer >::ref(); auto& server = Common::Service< World::WorldServer >::ref();

View file

@ -22,12 +22,20 @@ class PlayerMgr
void onChangeClass( Sapphire::Entity::Player& player ); void onChangeClass( Sapphire::Entity::Player& player );
void onSendAchievementList( Sapphire::Entity::Player& player );
void onSendAchievementProgress( Sapphire::Entity::Player& player, uint32_t achievementId );
void onUnlockAchievement( Sapphire::Entity::Player& player, uint32_t achievementId );
void onPlayerHpMpTpChanged( Sapphire::Entity::Player& player ); void onPlayerHpMpTpChanged( Sapphire::Entity::Player& player );
void onPlayerItemLevelUpdate( Sapphire::Entity::Player& player ); void onPlayerItemLevelUpdate( Sapphire::Entity::Player& player );
void onLevelUp( Sapphire::Entity::Player& player ); void onLevelUp( Sapphire::Entity::Player& player );
void onSetLevelForClass( Sapphire::Entity::Player& player, Common::ClassJob classJob );
void onGainExp( Sapphire::Entity::Player& player, uint32_t exp ); void onGainExp( Sapphire::Entity::Player& player, uint32_t exp );
void onUnlockOrchestrion( Sapphire::Entity::Player& player, uint8_t songId, uint32_t itemId ); void onUnlockOrchestrion( Sapphire::Entity::Player& player, uint8_t songId, uint32_t itemId );

View file

@ -539,7 +539,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
else else
{ {
auto& warpMgr = Common::Service< WarpMgr >::ref(); auto& warpMgr = Common::Service< WarpMgr >::ref();
warpMgr.requestMoveTerritory( *targetPlayer, WarpType::WARP_TYPE_GM, teriMgr.getZoneByTerritoryTypeId( param1 )->getGuId(), targetPlayer->getPos(), 0); warpMgr.requestMoveTerritory( *targetPlayer, WarpType::WARP_TYPE_GM, teriMgr.getZoneByTerritoryTypeId( param1 )->getGuId(), targetPlayer->getPos(), 0 );
} }
PlayerMgr::sendServerNotice( player, "{0} was warped to zone {1}", targetPlayer->getName(), param1, pZone->getName() ); PlayerMgr::sendServerNotice( player, "{0} was warped to zone {1}", targetPlayer->getName(), param1, pZone->getName() );

View file

@ -600,7 +600,16 @@ void Sapphire::Network::GameConnection::commandHandler( const Packets::FFXIVARR_
player.setIsLogin( false ); player.setIsLogin( false );
break; break;
} }
case PacketCommand::ACHIEVEMENT_REQUEST_RATE:
{
Service< World::Manager::PlayerMgr >::ref().onSendAchievementProgress( player, param11 );
break;
}
case PacketCommand::ACHIEVEMENT_REQUEST:
{
Service< World::Manager::PlayerMgr >::ref().onSendAchievementList( player );
break;
}
case PacketCommand::TELEPO_INQUIRY: // Teleport case PacketCommand::TELEPO_INQUIRY: // Teleport
{ {

View file

@ -19,7 +19,6 @@
#include "Session.h" #include "Session.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
#include "Manager/LinkshellMgr.h"
#include "Manager/TaskMgr.h" #include "Manager/TaskMgr.h"
#include "Task/TestTask.h" #include "Task/TestTask.h"
@ -29,6 +28,7 @@
#include <Database/ZoneDbConnection.h> #include <Database/ZoneDbConnection.h>
#include <Database/DbWorkerPool.h> #include <Database/DbWorkerPool.h>
#include <Service.h> #include <Service.h>
#include "Manager/AchievementMgr.h"
#include "Manager/LinkshellMgr.h" #include "Manager/LinkshellMgr.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
#include "Manager/HousingMgr.h" #include "Manager/HousingMgr.h"
@ -186,6 +186,16 @@ void WorldServer::run( int32_t argc, char* argv[] )
} }
Common::Service< Manager::LinkshellMgr >::set( pLsMgr ); Common::Service< Manager::LinkshellMgr >::set( pLsMgr );
auto pAchvMgr = std::make_shared< Manager::AchievementMgr >();
Logger::info( "AchievementMgr: Caching data" );
if( !pAchvMgr->cacheAchievements() )
{
Logger::fatal( "Unable to cache achievements!" );
return;
}
Common::Service< Manager::AchievementMgr >::set( pAchvMgr );
Logger::info( "Setting up InstanceObjectCache" ); Logger::info( "Setting up InstanceObjectCache" );
auto pInstanceObjCache = std::make_shared< Sapphire::InstanceObjectCache >(); auto pInstanceObjCache = std::make_shared< Sapphire::InstanceObjectCache >();
Common::Service< Sapphire::InstanceObjectCache >::set( pInstanceObjCache ); Common::Service< Sapphire::InstanceObjectCache >::set( pInstanceObjCache );