mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-30 08:07:46 +00:00
Map support
This commit is contained in:
parent
475f560982
commit
e8c7c83197
11 changed files with 1148 additions and 9 deletions
|
@ -215,7 +215,19 @@ namespace Sapphire::Network::ActorControl
|
|||
SetFavorite = 0x1FC,
|
||||
LearnTeleport = 0x1FD,
|
||||
|
||||
OpenRecommendationGuide = 0x200,
|
||||
/*!
|
||||
* param1 = event type bitmask
|
||||
* 1 = Quest
|
||||
* 2 = GuildLeveAssignment
|
||||
* 4 = GuildOrderGuide
|
||||
* 8 = TripleTriad
|
||||
* 16 = CustomTalk
|
||||
* 32 = PreHandler
|
||||
*/
|
||||
BeginMapUpdate = 0x1FF,
|
||||
FinishMapUpdate = 0x200,
|
||||
|
||||
//OpenRecommendationGuide = 0x200,
|
||||
ArmoryErrorMsg = 0x201,
|
||||
|
||||
AchievementPopup = 0x203,
|
||||
|
|
|
@ -282,6 +282,14 @@ namespace Sapphire::Network::Packets
|
|||
DailyQuests = 0x0331, // updated 5.58
|
||||
DailyQuestRepeatFlags = 0x01D1, // updated 5.58
|
||||
|
||||
MapUpdate = 0x03A2, // updated 5.58
|
||||
MapUpdate4 = 0x0284, // updated 5.58
|
||||
MapUpdate8 = 0x01BC, // updated 5.58
|
||||
MapUpdate16 = 0x02D1, // updated 5.58
|
||||
MapUpdate32 = 0x00DB, // updated 5.58
|
||||
MapUpdate64 = 0x0368, // updated 5.58
|
||||
MapUpdate128 = 0x0349, // updated 5.58
|
||||
|
||||
/// Doman Mahjong //////////////////////////////////////
|
||||
MahjongOpenGui = 0x02A4, // only available in mahjong instance
|
||||
MahjongNextRound = 0x02BD, // initial hands(baipai), # of riichi(wat), winds, honba, score and stuff
|
||||
|
|
|
@ -928,12 +928,12 @@ namespace Sapphire::Network::Packets::Server
|
|||
|
||||
uint8_t unknown5;
|
||||
uint32_t unknown8;
|
||||
uint16_t festivalId;
|
||||
uint16_t additionalFestivalId;
|
||||
uint32_t unknown9;
|
||||
uint32_t unknown10;
|
||||
uint32_t festivalId;
|
||||
uint32_t unknown11;
|
||||
uint32_t unknown12[4];
|
||||
uint32_t unknown12[3];
|
||||
uint32_t additionalFestivalId;
|
||||
uint32_t unknown13[3];
|
||||
Common::FFXIVARR_POSITION3 pos;
|
||||
uint32_t unknown14[3];
|
||||
|
@ -2289,6 +2289,60 @@ namespace Sapphire::Network::Packets::Server
|
|||
uint16_t padding3;
|
||||
} actors[2];
|
||||
};
|
||||
|
||||
//For quests this is only used for pre-accepted ones. Accepted quests are getting handled by the client.
|
||||
template< int ArgCount >
|
||||
struct FFXIVIpcMapUpdateN
|
||||
{
|
||||
uint8_t entryCount;
|
||||
uint8_t padding[ 3 ];
|
||||
uint32_t iconIds[ ArgCount ];
|
||||
uint32_t levelIds[ ArgCount ];
|
||||
uint32_t eventIds[ ArgCount ]; // possible event ids for this: Quest, GuildLeveAssignment, GuildOrderGuide, TripleTriad, CustomTalk, PreHandler
|
||||
uint8_t additionalData[ ArgCount ]; // use unknown
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate :
|
||||
FFXIVIpcBasePacket< MapUpdate >,
|
||||
FFXIVIpcMapUpdateN< 2 >
|
||||
{
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate4 :
|
||||
FFXIVIpcBasePacket< MapUpdate4 >,
|
||||
FFXIVIpcMapUpdateN< 4 >
|
||||
{
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate8 :
|
||||
FFXIVIpcBasePacket< MapUpdate8 >,
|
||||
FFXIVIpcMapUpdateN< 8 >
|
||||
{
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate16 :
|
||||
FFXIVIpcBasePacket< MapUpdate16 >,
|
||||
FFXIVIpcMapUpdateN< 16 >
|
||||
{
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate32 :
|
||||
FFXIVIpcBasePacket< MapUpdate32 >,
|
||||
FFXIVIpcMapUpdateN< 32 >
|
||||
{
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate64 :
|
||||
FFXIVIpcBasePacket< MapUpdate64 >,
|
||||
FFXIVIpcMapUpdateN< 64 >
|
||||
{
|
||||
};
|
||||
|
||||
struct FFXIVIpcMapUpdate128 :
|
||||
FFXIVIpcBasePacket< MapUpdate128 >,
|
||||
FFXIVIpcMapUpdateN< 128 >
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
#endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "Manager/HousingMgr.h"
|
||||
#include "Manager/TerritoryMgr.h"
|
||||
#include "Manager/RNGMgr.h"
|
||||
#include "Manager/MapMgr.h"
|
||||
|
||||
#include "Territory/Territory.h"
|
||||
#include "Territory/ZonePosition.h"
|
||||
|
@ -730,7 +731,7 @@ void Sapphire::Entity::Player::learnSong( uint8_t songId, uint32_t itemId )
|
|||
queuePacket( makeActorControlSelf( getId(), ToggleOrchestrionUnlock, songId, 1, itemId ) );
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::isActionLearned( uint8_t actionId ) const
|
||||
bool Sapphire::Entity::Player::isActionLearned( uint16_t actionId ) const
|
||||
{
|
||||
uint16_t index;
|
||||
uint8_t value;
|
||||
|
@ -1286,6 +1287,17 @@ const uint8_t* Sapphire::Entity::Player::getMountGuideBitmask() const
|
|||
return m_mountGuide;
|
||||
}
|
||||
|
||||
const bool Sapphire::Entity::Player::hasMount( int16_t mountId ) const
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
auto mount = exdData.get< Data::Mount >( mountId );
|
||||
|
||||
if( mount->order == -1 || mount->modelChara == 0 )
|
||||
return false;
|
||||
|
||||
return m_mountGuide[ mount->order / 8 ] & ( 1 << ( mount->order % 8 ) );
|
||||
}
|
||||
|
||||
uint64_t Sapphire::Entity::Player::getContentId() const
|
||||
{
|
||||
return m_contentId;
|
||||
|
@ -1890,6 +1902,8 @@ Sapphire::Entity::Player::sendZoneInPackets( uint32_t param1, uint32_t param2 =
|
|||
|
||||
setZoningType( Common::ZoneingType::None );
|
||||
unsetStateFlag( PlayerStateFlag::BetweenAreas );
|
||||
|
||||
Common::Service< MapMgr >::ref().updateAll( *this );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::finishZoning()
|
||||
|
|
|
@ -194,6 +194,8 @@ namespace Sapphire::Entity
|
|||
/*! remove a given quest */
|
||||
void removeQuest( uint16_t questId );
|
||||
|
||||
bool isQuestCompleted( uint16_t questId );
|
||||
|
||||
/*! add a quest to the completed quests mask */
|
||||
void updateQuestsCompleted( uint32_t questId );
|
||||
|
||||
|
@ -644,7 +646,7 @@ namespace Sapphire::Entity
|
|||
void learnSong( uint8_t songId, uint32_t itemId );
|
||||
|
||||
/*! check if an action is already unlocked in the bitmask. */
|
||||
bool isActionLearned( uint8_t actionId ) const;
|
||||
bool isActionLearned( uint16_t actionId ) const;
|
||||
|
||||
/*! return a const pointer to the unlock bitmask array */
|
||||
const uint8_t* getUnlockBitmask() const;
|
||||
|
@ -655,6 +657,8 @@ namespace Sapphire::Entity
|
|||
/*! return a const pointer to the mount guide bitmask array */
|
||||
const uint8_t* getMountGuideBitmask() const;
|
||||
|
||||
const bool hasMount( int16_t mountId ) const;
|
||||
|
||||
bool checkAction() override;
|
||||
|
||||
bool hasQueuedAction() const;
|
||||
|
@ -950,6 +954,7 @@ namespace Sapphire::Entity
|
|||
uint16_t calculateEquippedGearItemLevel();
|
||||
|
||||
ItemPtr getEquippedWeapon();
|
||||
ItemPtr getEquippedSecondaryWeapon();
|
||||
|
||||
/*! return the current amount of currency of type */
|
||||
uint32_t getCurrency( Common::CurrencyType type );
|
||||
|
|
|
@ -939,6 +939,11 @@ Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedWeapon()
|
|||
return m_storageMap[ GearSet0 ]->getItem( GearSetSlot::MainHand );
|
||||
}
|
||||
|
||||
Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedSecondaryWeapon()
|
||||
{
|
||||
return m_storageMap[ InventoryType::GearSet0 ]->getItem( GearSetSlot::OffHand );
|
||||
}
|
||||
|
||||
uint8_t Sapphire::Entity::Player::getFreeSlotsInBags()
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "Network/GameConnection.h"
|
||||
#include "Network/PacketWrappers/QuestMessagePacket.h"
|
||||
|
||||
#include "Manager/MapMgr.h"
|
||||
|
||||
#include "Session.h"
|
||||
|
||||
using namespace Sapphire::Common;
|
||||
|
@ -42,6 +44,11 @@ void Sapphire::Entity::Player::removeQuest( uint16_t questId )
|
|||
|
||||
if( ( idx != -1 ) && ( m_activeQuests[ idx ] != nullptr ) )
|
||||
{
|
||||
std::shared_ptr< QuestActive > pQuest = m_activeQuests[ idx ];
|
||||
m_activeQuests[ idx ].reset();
|
||||
|
||||
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
|
||||
|
||||
auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() );
|
||||
questUpdatePacket->data().slot = static_cast< uint8_t >( idx );
|
||||
questUpdatePacket->data().questInfo.c.questId = 0;
|
||||
|
@ -54,9 +61,6 @@ void Sapphire::Entity::Player::removeQuest( uint16_t questId )
|
|||
m_questTracking[ ii ] = -1;
|
||||
}
|
||||
|
||||
std::shared_ptr< QuestActive > pQuest = m_activeQuests[ idx ];
|
||||
m_activeQuests[ idx ].reset();
|
||||
|
||||
m_questIdToQuestIdx.erase( questId );
|
||||
m_questIdxToQuestId.erase( idx );
|
||||
|
||||
|
@ -916,6 +920,8 @@ void Sapphire::Entity::Player::updateQuest( uint16_t questId, uint8_t sequence )
|
|||
m_questIdToQuestIdx[ questId ] = idx;
|
||||
m_questIdxToQuestId[ idx ] = questId;
|
||||
|
||||
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
|
||||
|
||||
auto questUpdatePacket = makeZonePacket< FFXIVIpcQuestUpdate >( getId() );
|
||||
questUpdatePacket->data().slot = idx;
|
||||
questUpdatePacket->data().questInfo = *pNewQuest;
|
||||
|
@ -1013,6 +1019,11 @@ Sapphire::Entity::Player::sendQuestMessage( uint32_t questId, int8_t msgId, uint
|
|||
}
|
||||
|
||||
|
||||
bool Sapphire::Entity::Player::isQuestCompleted( uint16_t questId )
|
||||
{
|
||||
return ( m_questCompleteFlags[ questId / 8 ] & ( 0x80 >> ( questId % 8 ) ) );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::updateQuestsCompleted( uint32_t questId )
|
||||
{
|
||||
uint16_t index = questId / 8;
|
||||
|
@ -1032,6 +1043,8 @@ void Sapphire::Entity::Player::removeQuestsCompleted( uint32_t questId )
|
|||
|
||||
m_questCompleteFlags[ index ] ^= value;
|
||||
|
||||
Common::Service< World::Manager::MapMgr >::ref().updateQuests( *this );
|
||||
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t optionalChoice )
|
||||
|
|
|
@ -78,6 +78,8 @@ namespace Sapphire::Event
|
|||
FcTalk = 0x001F,
|
||||
Adventure = 0x0021,
|
||||
DailyQuestSupply = 0x0022,
|
||||
TripleTriad = 0x0023,
|
||||
PreHandler = 0x0036,
|
||||
ICDirector = 0x8003,
|
||||
PublicContentDirector = 0x8004,
|
||||
QuestBattleDirector = 0x8006,
|
||||
|
|
889
src/world/Manager/MapMgr.cpp
Normal file
889
src/world/Manager/MapMgr.cpp
Normal file
|
@ -0,0 +1,889 @@
|
|||
#include <Common.h>
|
||||
#include <Service.h>
|
||||
|
||||
#include <datReader/DatCategories/bg/lgb.h>
|
||||
#include <Exd/ExdDataGenerated.h>
|
||||
|
||||
#include <Event/EventHandler.h>
|
||||
|
||||
#include <Network/CommonActorControl.h>
|
||||
#include <Network/GameConnection.h>
|
||||
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
||||
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
|
||||
|
||||
#include <Logging/Logger.h>
|
||||
#include <Util/Util.cpp>
|
||||
|
||||
#include <Actor/Player.h>
|
||||
#include <Inventory/Item.h>
|
||||
|
||||
#include "MapMgr.h"
|
||||
#include "TerritoryMgr.h"
|
||||
|
||||
#include "ServerMgr.h"
|
||||
|
||||
using namespace Sapphire::Event;
|
||||
using namespace Sapphire::Network::Packets;
|
||||
using namespace Sapphire::Network::Packets::Server;
|
||||
|
||||
Sapphire::World::Manager::MapMgr::MapMgr()
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
for( auto quest : exdData.m_QuestDat.get_rows() )
|
||||
{
|
||||
if( exdData.getField< std::string >( quest.second, 1 ).empty() ) // id
|
||||
continue;
|
||||
|
||||
auto& questData = m_questData[ quest.first ];
|
||||
|
||||
questData.previousQuestJoin = exdData.getField< uint8_t >( quest.second, 8 );
|
||||
questData.previousQuestKeys[ 0 ] = exdData.getField< uint32_t >( quest.second, 9 );
|
||||
questData.previousQuest0Sequence = exdData.getField< uint8_t >( quest.second, 10 );
|
||||
questData.previousQuestKeys[ 1 ] = exdData.getField< uint32_t >( quest.second, 11 );
|
||||
questData.previousQuestKeys[ 2 ] = exdData.getField< uint32_t >( quest.second, 12 );
|
||||
|
||||
questData.questLockJoin = exdData.getField< uint8_t >( quest.second, 13 );
|
||||
questData.questLockKeys[ 0 ] = exdData.getField< uint32_t >( quest.second, 14 );
|
||||
questData.questLockKeys[ 1 ] = exdData.getField< uint32_t >( quest.second, 15 );
|
||||
|
||||
questData.classJobRequirements[ 0 ].classJobLevel = exdData.getField< uint16_t >( quest.second, 4 );
|
||||
auto classJobsCategory = exdData.get< Data::ClassJobCategory >( exdData.getField< uint8_t >( quest.second, 3 ) );
|
||||
for( int32_t i = 0; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
questData.classJobRequirements[ 0 ].classJobCategoryMask.set( i, ( &classJobsCategory->aDV )[ i ] );
|
||||
|
||||
questData.classJobRequirements[ 1 ].classJobLevel = exdData.getField< uint16_t >( quest.second, 7 );
|
||||
classJobsCategory = exdData.get< Data::ClassJobCategory >( exdData.getField< uint8_t >( quest.second, 6 ) );
|
||||
for( int32_t i = 0; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
questData.classJobRequirements[ 1 ].classJobCategoryMask.set( i, ( &classJobsCategory->aDV )[ i ] );
|
||||
|
||||
questData.column18 = exdData.getField< uint8_t >( quest.second, 18 );
|
||||
|
||||
questData.classJobUnlock = exdData.getField< uint8_t >( quest.second, 19 );
|
||||
|
||||
questData.requiredGC = exdData.getField< uint8_t >( quest.second, 20 );
|
||||
questData.requiredGCRank = exdData.getField< uint8_t >( quest.second, 21 );
|
||||
|
||||
questData.startTown = exdData.getField< uint8_t >( quest.second, 17 );
|
||||
|
||||
questData.header = exdData.getField< uint16_t >( quest.second, 16 );
|
||||
|
||||
questData.instanceContentJoin = exdData.getField< uint8_t >( quest.second, 22 );
|
||||
questData.instanceContent[ 0 ] = exdData.getField< uint32_t >( quest.second, 23 );
|
||||
questData.instanceContent[ 1 ] = exdData.getField< uint32_t >( quest.second, 24 );
|
||||
questData.instanceContent[ 2 ] = exdData.getField< uint32_t >( quest.second, 25 );
|
||||
|
||||
questData.festival = exdData.getField< uint8_t >( quest.second, 26 );
|
||||
questData.festivalBegin = exdData.getField< uint8_t >( quest.second, 27 );
|
||||
questData.festivalEnd = exdData.getField< uint8_t >( quest.second, 28 );
|
||||
questData.bellStart = exdData.getField< uint16_t >( quest.second, 29 );
|
||||
questData.bellEnd = exdData.getField< uint16_t >( quest.second, 30 );
|
||||
|
||||
questData.repeatIntervalType = exdData.getField< uint8_t >( quest.second, 44 );
|
||||
questData.questRepeatFlag = exdData.getField< uint8_t >( quest.second, 45 );
|
||||
|
||||
questData.beastTribe = exdData.getField< uint8_t >( quest.second, 31 );
|
||||
questData.beastReputationRank = exdData.getField< uint8_t >( quest.second, 32 );
|
||||
questData.beastReputationValue = exdData.getField< uint16_t >( quest.second, 33 );
|
||||
|
||||
questData.mount = exdData.getField< int32_t >( quest.second, 36 );
|
||||
|
||||
questData.satisfactionNpc = exdData.getField< uint8_t >( quest.second, 34 );
|
||||
questData.satisfactionRank = exdData.getField< uint8_t >( quest.second, 35 );
|
||||
|
||||
questData.issuer = exdData.getField< uint32_t >( quest.second, 39 );
|
||||
|
||||
questData.deliveryQuest = exdData.getField< uint8_t >( quest.second, 38 );
|
||||
|
||||
questData.expansion = exdData.getField< uint8_t >( quest.second, 2 );
|
||||
|
||||
questData.type = exdData.getField< uint8_t >(quest.second, 47);
|
||||
|
||||
questData.isRepeatable = exdData.getField< bool >( quest.second, 43 );
|
||||
questData.isHouseRequired = exdData.getField< bool >( quest.second, 37 );
|
||||
|
||||
questData.iconValid = exdData.get< Data::EventIconType >( exdData.getField< uint8_t >( quest.second, 1512 ) )->mapIconAvailable + 1 + questData.isRepeatable;
|
||||
questData.iconInvalid = exdData.get< Data::EventIconType >( exdData.getField< uint8_t >( quest.second, 1512 ) )->mapIconInvalid + 1 + questData.isRepeatable;
|
||||
|
||||
uint32_t issuerLevelId = exdData.getField< uint32_t >( quest.second, 40 );
|
||||
if( issuerLevelId != 0 )
|
||||
{
|
||||
if( count++ % 100 == 0 )
|
||||
std::cout << ".";
|
||||
|
||||
auto level = exdData.get< Data::Level >( issuerLevelId );
|
||||
auto territory = level->territory;
|
||||
|
||||
if( territory == 0 )
|
||||
territory = exdData.get< Data::Map >( level->map )->territoryType;
|
||||
|
||||
EventData eventData;
|
||||
eventData.iconId = questData.iconValid;
|
||||
eventData.levelId = issuerLevelId;
|
||||
eventData.actorId = questData.issuer;
|
||||
|
||||
m_mapData[ territory ].emplace( quest.first, eventData );
|
||||
}
|
||||
}
|
||||
|
||||
auto& serverMgr = Common::Service< World::ServerMgr >::ref();
|
||||
auto m_gameData = new xiv::dat::GameData( serverMgr.getConfig().global.general.dataPath );
|
||||
|
||||
std::set< std::string > bgSet;
|
||||
|
||||
for( auto territoryTypeId : exdData.getTerritoryTypeIdList() )
|
||||
{
|
||||
auto territoryType = exdData.get< Data::TerritoryType >( territoryTypeId );
|
||||
if( territoryType->bg.empty() )
|
||||
continue;
|
||||
|
||||
if( bgSet.find( territoryType->bg ) != bgSet.end() )
|
||||
continue;
|
||||
else
|
||||
bgSet.insert( territoryType->bg );
|
||||
|
||||
std::string planeventLgbPath( "bg/" + territoryType->bg.substr( 0, territoryType->bg.rfind( '/' ) ) + "/planevent.lgb" );
|
||||
auto planeventFile = m_gameData->getFile( planeventLgbPath );
|
||||
auto planeventData = planeventFile->access_data_sections().at( 0 );
|
||||
LGB_FILE planeventLgb( &planeventData[ 0 ], "planevent" );
|
||||
|
||||
for( const auto& group : planeventLgb.groups )
|
||||
{
|
||||
for( const auto& pEntry : group.entries )
|
||||
{
|
||||
if( pEntry->getType() == LgbEntryType::EventNpc )
|
||||
{
|
||||
auto pNpc = reinterpret_cast< LGB_ENPC_ENTRY* >( pEntry.get() );
|
||||
|
||||
auto eNpcData = exdData.get< Data::ENpcBase >( pNpc->data.enpcId )->eNpcData;
|
||||
|
||||
for( auto npcData : eNpcData )
|
||||
{
|
||||
if( npcData == 0 )
|
||||
continue; // Some npcs have data gaps, so we have to iterate through the entire array
|
||||
|
||||
if( count++ % 1000 == 0 )
|
||||
std::cout << ".";
|
||||
|
||||
EventData eventData;
|
||||
eventData.levelId = pNpc->data.instanceId;
|
||||
eventData.actorId = pNpc->data.enpcId;
|
||||
|
||||
auto eventHandlerType = static_cast< EventHandler::EventHandlerType >( npcData >> 16 );
|
||||
|
||||
switch( eventHandlerType )
|
||||
{
|
||||
case EventHandler::EventHandlerType::GuildLeveAssignment:
|
||||
{
|
||||
eventData.iconId = exdData.get< Data::EventIconType >( 5 )->mapIconAvailable + 1;
|
||||
|
||||
m_mapData[ territoryTypeId ].insert( std::make_pair( npcData, eventData ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::CustomTalk:
|
||||
{
|
||||
// Include only the beginner arena icon yet. There a few other ones, that aren't referenced in the game files (Some examples are: The Triple Triad Tournament npc which has multiple icons and the ocean fishing icon)
|
||||
if( npcData == 721223 )
|
||||
{
|
||||
auto customTalk = exdData.get< Data::CustomTalk >( npcData );
|
||||
|
||||
eventData.iconId = customTalk->iconMap;
|
||||
|
||||
m_mapData[ territoryTypeId ].insert( std::make_pair( npcData, eventData ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::GuildOrderGuide:
|
||||
{
|
||||
eventData.iconId = exdData.get< Data::EventIconType >( 6 )->mapIconAvailable + 1;
|
||||
|
||||
m_mapData[ territoryTypeId ].insert( std::make_pair( npcData, eventData ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::TripleTriad:
|
||||
{
|
||||
eventData.iconId = exdData.get< Data::EventIconType >( 7 )->mapIconAvailable + 1;
|
||||
|
||||
m_mapData[ territoryTypeId ].insert( std::make_pair( npcData, eventData ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::PreHandler:
|
||||
{
|
||||
//I think this is used in Bozja and Zadnor, need evidence
|
||||
//m_mapData[ territoryTypeId ].insert( std::make_pair( eventData, 0 ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::MapMgr::updateAll( Entity::Player& player )
|
||||
{
|
||||
auto& mapData = m_mapData[ player.getTerritoryTypeId() ];
|
||||
std::multimap< uint32_t, EventData, less > newMapData;
|
||||
|
||||
for( auto eventData : mapData )
|
||||
{
|
||||
switch( static_cast< EventHandler::EventHandlerType >( eventData.first >> 16 ) )
|
||||
{
|
||||
case EventHandler::EventHandlerType::Quest:
|
||||
{
|
||||
if( isQuestAvailable( player, eventData ) )
|
||||
newMapData.insert( eventData );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::GuildLeveAssignment:
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
auto guildLeve = exdData.get< Data::GuildleveAssignment >( eventData.first );
|
||||
|
||||
if( player.isActionLearned( 5 ) )
|
||||
{
|
||||
if( player.isQuestCompleted( guildLeve->quest[ 0 ] ) )
|
||||
{
|
||||
if( eventData.first >= 393239 && eventData.first <= 393247 )
|
||||
{
|
||||
if( player.getGc() != 0 )
|
||||
newMapData.insert( eventData );
|
||||
}
|
||||
else
|
||||
{
|
||||
newMapData.insert( eventData );
|
||||
}
|
||||
}
|
||||
else if( eventData.first == 393217 || eventData.first == 393223 || eventData.first == 393225 ) // Leve npc locations: Bentbranch / Horizon / Swiftperch
|
||||
{
|
||||
if( player.isQuestCompleted( 220 ) || player.isQuestCompleted( 687 ) || player.isQuestCompleted( 693 ) )
|
||||
newMapData.insert( eventData );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::CustomTalk:
|
||||
{
|
||||
newMapData.insert( eventData );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::GuildOrderGuide:
|
||||
{
|
||||
if( player.isActionLearned( 7 ) )
|
||||
newMapData.insert( eventData );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EventHandler::EventHandlerType::TripleTriad:
|
||||
{
|
||||
if( eventData.first == 2293771 ) // Triple Triad Master npc for now only
|
||||
newMapData.insert( eventData );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendPackets( player, newMapData, All );
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::MapMgr::updateQuests( Entity::Player& player )
|
||||
{
|
||||
auto& mapData = m_mapData[ player.getTerritoryTypeId() ];
|
||||
std::multimap< uint32_t, EventData, less > newMapData;
|
||||
|
||||
for( auto& eventData : mapData )
|
||||
{
|
||||
if( ( eventData.first >> 16 ) == static_cast< uint16_t >( EventHandler::EventHandlerType::Quest ) )
|
||||
{
|
||||
if( isQuestAvailable( player, eventData ) )
|
||||
newMapData.insert( eventData );
|
||||
}
|
||||
}
|
||||
|
||||
sendPackets( player, newMapData, Quest );;
|
||||
}
|
||||
|
||||
bool Sapphire::World::Manager::MapMgr::isQuestAvailable( Entity::Player& player, std::pair< const uint32_t, EventData >& eventData )
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
|
||||
auto& quest = m_questData[ eventData.first ];
|
||||
|
||||
if( ( player.isQuestCompleted( eventData.first ) && ( !quest.isRepeatable && eventData.first != 67114 ) ) || player.hasQuest( eventData.first ) ||
|
||||
( quest.repeatIntervalType == 1 && quest.questRepeatFlag == 0 ) ) // Don't show daily beast tribe quests on the map yet.
|
||||
return false;
|
||||
|
||||
if( quest.classJobUnlock )
|
||||
{
|
||||
if( quest.column18 == 3 )
|
||||
if( static_cast< uint8_t >( player.getClass() ) != quest.classJobUnlock )
|
||||
return false;
|
||||
else if( quest.column18 == 4 )
|
||||
if ( static_cast< uint8_t >( player.getClass() ) == quest.classJobUnlock )
|
||||
return false;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Was this really ever used?
|
||||
if( quest.startTown )
|
||||
{
|
||||
if( quest.startTown != player.getStartTown() )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Common::CURRENT_EXPANSION_ID < quest.expansion )
|
||||
return false;
|
||||
|
||||
if( quest.mount )
|
||||
{
|
||||
if( !player.hasMount( quest.mount ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( eventData.first == 65968 ) // Quest: A Legend for a Legend
|
||||
{
|
||||
uint16_t requiredMounts[] = { 28, 29, 30, 31, 40, 43 };
|
||||
|
||||
for( int32_t i = 0; i < 6; i++ )
|
||||
{
|
||||
if( !player.hasMount( requiredMounts[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( eventData.first == 67086 ) // Quest: Fiery Wings, Fiery Hearts
|
||||
{
|
||||
uint16_t requiredMounts[] = { 75, 76, 77, 78, 90, 98, 104 };
|
||||
|
||||
for( int32_t i = 0; i < 6; i++ )
|
||||
{
|
||||
if( !player.hasMount( requiredMounts[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( eventData.first == 68736 ) // Quest: A Lone Wolf No More
|
||||
{
|
||||
uint16_t requiredMounts[] = { 115, 116, 133, 144, 158, 172, 182 };
|
||||
|
||||
for( int32_t i = 0; i < 6; i++ )
|
||||
{
|
||||
if( !player.hasMount( requiredMounts[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( quest.requiredGC || quest.requiredGCRank )
|
||||
{
|
||||
if( quest.requiredGC != player.getGc() )
|
||||
return false;
|
||||
|
||||
if( quest.requiredGCRank > player.getGcRankArray()[ player.getGc() - 1 ] )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
}
|
||||
|
||||
if( quest.header != 0 )
|
||||
{
|
||||
if ( !player.isActionLearned( quest.header ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
//Required previous quests for ARR relic
|
||||
if( eventData.first == 66971 ) // Quest: Up in Arms
|
||||
{
|
||||
for( int32_t i = 0; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
{
|
||||
auto classJob = exdData.get< Data::ClassJob >( i );
|
||||
|
||||
if( player.isQuestCompleted( classJob->relicQuest ) )
|
||||
break;
|
||||
|
||||
if( i == Common::CLASSJOB_TOTAL )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( eventData.first == 65897 ) // Quest: His Dark Materia
|
||||
{
|
||||
// Quests in the following order: A Ponze of Flesh, Labor of Love, Method in His Malice, A Treasured Mother
|
||||
if( !player.isQuestCompleted( 357 ) || !player.isQuestCompleted( 358 ) || !player.isQuestCompleted( 359 ) || !player.isQuestCompleted( 360 ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( quest.previousQuestJoin == 1 )
|
||||
{
|
||||
for( int32_t i = 0; i < 3; i++ )
|
||||
{
|
||||
if( quest.previousQuestKeys[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
if( !player.isQuestCompleted( quest.previousQuestKeys[ i ] ) )
|
||||
{
|
||||
if( i == 0 && quest.previousQuest0Sequence != 0 )
|
||||
{
|
||||
if( player.getQuestSeq( quest.previousQuestKeys[ i ] ) < quest.previousQuest0Sequence )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( quest.previousQuestJoin == 2 )
|
||||
{
|
||||
for( int32_t i = 0; i < 3; i++ )
|
||||
{
|
||||
if( quest.previousQuestKeys[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
if( player.isQuestCompleted( quest.previousQuestKeys[ i ] ) )
|
||||
break;
|
||||
|
||||
if( i == 2 )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( quest.questLockJoin == 1 )
|
||||
{
|
||||
for( int32_t i = 0; i < 2; i++ )
|
||||
{
|
||||
if( quest.questLockKeys[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
if( !player.isQuestCompleted( quest.questLockKeys[ i ] ) && !player.hasQuest( quest.questLockKeys[ i ] ) )
|
||||
break;
|
||||
|
||||
if( i == 1 )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( quest.questLockJoin == 2 )
|
||||
{
|
||||
for( int32_t i = 0; i < 2; i++ )
|
||||
{
|
||||
if( quest.questLockKeys[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
if( player.isQuestCompleted( quest.questLockKeys[ i ] ) || player.hasQuest( quest.questLockKeys[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( quest.instanceContentJoin == 1 )
|
||||
{
|
||||
for( int32_t i = 0; i < 3; i++ )
|
||||
{
|
||||
if( quest.instanceContent[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
}
|
||||
}
|
||||
else if( quest.instanceContentJoin == 2 )
|
||||
{
|
||||
for( int32_t i = 0; i < 3; i++ )
|
||||
{
|
||||
if( quest.instanceContent[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
if( quest.festival )
|
||||
{
|
||||
auto& territoryMgr = Common::Service< Manager::TerritoryMgr >::ref();
|
||||
auto& festival = territoryMgr.getCurrentFestival();
|
||||
|
||||
if( quest.festival != festival.first && quest.festival != festival.second )
|
||||
return false;
|
||||
|
||||
// Don't show festivals with begin state other than 0 yet
|
||||
if( quest.festivalBegin != 0 )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( quest.bellStart || quest.bellEnd )
|
||||
{
|
||||
uint64_t curEorzeaTime = Util::getEorzeanTimeStamp();
|
||||
uint32_t convTime = 100 * (curEorzeaTime / 3600 % 24) + curEorzeaTime / 60 % 60;
|
||||
|
||||
if( quest.bellStart <= quest.bellEnd )
|
||||
{
|
||||
if( convTime < quest.bellStart || convTime >= quest.bellEnd )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( convTime < quest.bellStart && convTime >= quest.bellEnd )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
if( !quest.classJobRequirements[0].classJobCategoryMask.test( static_cast< uint8_t >( player.getClass() ) ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
if( player.getQuestSeq( eventData.first ) || ( quest.type & 1 ) == 0 )
|
||||
{
|
||||
for( int32_t i = 1; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
{
|
||||
if( quest.classJobRequirements[0].classJobCategoryMask.test( i ) )
|
||||
{
|
||||
if( player.getLevelForClass( static_cast< Common::ClassJob >( i ) ) >= quest.classJobRequirements[0].classJobLevel )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i == Common::CLASSJOB_TOTAL )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( player.getLevel() < quest.classJobRequirements[0].classJobLevel )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( quest.classJobRequirements[1].classJobCategoryMask.any() )
|
||||
{
|
||||
for( int32_t i = 1; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
{
|
||||
if( quest.classJobRequirements[1].classJobCategoryMask.test( i ) )
|
||||
{
|
||||
if( player.getLevelForClass( static_cast< Common::ClassJob >( i ) ) >= quest.classJobRequirements[1].classJobLevel )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i == Common::CLASSJOB_TOTAL )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for( int32_t i = 0; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
{
|
||||
auto classJob = exdData.get< Data::ClassJob >( i );
|
||||
|
||||
if( classJob->relicQuest == eventData.first )
|
||||
{
|
||||
for( int32_t j = 0; i <= Common::CLASSJOB_TOTAL; i++ )
|
||||
{
|
||||
classJob = exdData.get< Data::ClassJob >( i );
|
||||
|
||||
if( player.hasQuest( classJob->relicQuest ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( quest.beastTribe )
|
||||
return false;
|
||||
|
||||
if( quest.satisfactionNpc )
|
||||
return false;
|
||||
|
||||
auto isRelicEquipped = [ &player, &eventData ]( uint32_t* mainWeaponId, uint32_t secondaryWeaponId )
|
||||
{
|
||||
for( int32_t i = 0; i < 10; i++ )
|
||||
{
|
||||
if( i == 0 )
|
||||
{
|
||||
if( player.getEquippedSecondaryWeapon() == nullptr )
|
||||
{
|
||||
if( player.getEquippedWeapon()->getId() == mainWeaponId[ i ] && secondaryWeaponId == 0 )
|
||||
return true;
|
||||
}
|
||||
else if( player.getEquippedWeapon()->getId() == mainWeaponId[ i ] && player.getEquippedSecondaryWeapon()->getId() == secondaryWeaponId )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( player.getEquippedWeapon()->getId() == mainWeaponId[ i ] )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
switch( eventData.first )
|
||||
{
|
||||
case 65742: // Quest: Mmmmmm, Soulglazed Relics
|
||||
{
|
||||
uint32_t relicItemIds[] = { 7863, 7864, 7865, 7866, 7867, 9253, 7868, 7869, 7870, 7871 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 7872 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 65892: // Quest: Wherefore Art Thou, Zodiac
|
||||
case 65897: // Quest: His Dark Materia
|
||||
{
|
||||
uint32_t relicItemIds[] = { 8649, 8650, 8651, 8652, 8653, 9254, 8654, 8655, 8656, 8657 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 8658 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 66096: // Quest: Rise and Shine
|
||||
{
|
||||
uint32_t relicItemIds[] = { 9491, 9492, 9493, 9494, 9495, 9501, 9496, 9497, 9498, 9499 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 9500 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 66097: // Quest: The Vital Title
|
||||
{
|
||||
uint32_t relicItemIds[] = { 10054, 10055, 10056, 10057, 10058, 10064, 10059, 10060, 10061, 10062 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 10063 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 66971: // Quest: Up in Arms
|
||||
{
|
||||
uint32_t relicItemIds[] = { 6257, 6258, 6259, 6260, 6261, 9250, 6262, 6263, 6264, 6265 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 0 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 66972: // Quest: Trials of the Braves
|
||||
{
|
||||
uint32_t relicItemIds[] = { 7824, 7825, 7826, 7827, 7828, 9251, 7829, 7830, 7831, 7832 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 7833 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 66998: // Quest: Celestial Radiance
|
||||
case 67000: // Quest: Star Light, Star Bright
|
||||
{
|
||||
uint32_t relicItemIds[] = { 7834, 7835, 7836, 7837, 7838, 9252, 7839, 7840, 7841, 7842 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 7843 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 67823: // Quest: The Vital Title
|
||||
{
|
||||
uint32_t relicItemIds[] = { 12124, 12133, 12142, 12151, 12160, 12169, 12178, 12187, 12196 };
|
||||
|
||||
if( !isRelicEquipped( relicItemIds, 12213 ) )
|
||||
eventData.second.iconId = quest.iconInvalid;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( eventData.first >= 67001 && eventData.first <= 67003 ) // Quest: Call of the Wild
|
||||
{
|
||||
// Quests in the following order: Martial Perfection / Feathers and Folly / Like Clutchfather, Like Son / Revenge of the Furred / Spread Your Wings and Soar
|
||||
if( !player.isQuestCompleted( 1221 ) || !player.isQuestCompleted( 1256 ) || !player.isQuestCompleted( 1378 ) || !player.isQuestCompleted( 1324 ) || !player.isQuestCompleted( 1493 ) )
|
||||
return false;
|
||||
}
|
||||
else if( eventData.first == 67918 ) // Quest: When Good Dragons Go Bad
|
||||
{
|
||||
// Quests in the following order: The Nest of Honor / A Symbiotic Friendship / The Zenith of Craftsmanship / Heavensward
|
||||
if( !player.isQuestCompleted( 2225 ) || !player.isQuestCompleted( 2260 ) || !player.isQuestCompleted( 2327 ) || !player.isQuestCompleted( 1669 ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( eventData.first >= 66968 && eventData.first <= 66970 ) // Quest: An Ill-conceived Venture
|
||||
return false;
|
||||
|
||||
if( quest.isHouseRequired )
|
||||
return false;
|
||||
|
||||
if( quest.deliveryQuest )
|
||||
return false;
|
||||
|
||||
if( eventData.first == 67114 ) // Quest: The Ties That Bind
|
||||
return false;
|
||||
|
||||
if( eventData.first == 66112 ) // Quest: Like Sire Like Fledgling
|
||||
{
|
||||
if( !( player.getHowToArray()[ 198 / 8 ] & ( 1 << ( 198 % 8 ) ) ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( player.getQuestSeq( eventData.first ) == 0 )
|
||||
{
|
||||
auto questAccept = exdData.get< Data::QuestAcceptAdditionCondition >( eventData.first );
|
||||
|
||||
if( questAccept )
|
||||
{
|
||||
for( int32_t i = 0; i < 2; i++ )
|
||||
{
|
||||
if( ( &questAccept->requirement0 )[ i ] >= 65536 )
|
||||
{
|
||||
if( !player.isActionLearned( 245 ) && !player.isQuestCompleted( ( &questAccept->requirement0 )[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !player.isActionLearned( ( &questAccept->requirement0 )[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Quests in the following order: Open and Inviting / The Adventurer with All the Cards
|
||||
if( eventData.first == 69566 || eventData.first == 69617 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sapphire::World::Manager::MapMgr::isTripleTriadAvailable( Entity::Player& player, std::pair< const uint32_t, EventData >& eventData )
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
auto tripleTriad = exdData.get< Data::TripleTriad >( eventData.first );
|
||||
|
||||
if( tripleTriad->previousQuestJoin == 1 )
|
||||
{
|
||||
for( int32_t i = 0; i < 3; i++ )
|
||||
{
|
||||
if( tripleTriad->previousQuest[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
if( !player.isQuestCompleted( tripleTriad->previousQuest[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( tripleTriad->previousQuestJoin == 2 )
|
||||
{
|
||||
for( int32_t i = 0; i < 3; i++ )
|
||||
{
|
||||
if( tripleTriad->previousQuest[ i ] == 0 )
|
||||
continue;
|
||||
|
||||
if( player.isQuestCompleted( tripleTriad->previousQuest[ i ] ) )
|
||||
break;
|
||||
|
||||
if( i == 2 )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::MapMgr::fillPacket( std::multimap< uint32_t, EventData, less >& mapData, uint32_t* iconIds, uint32_t* levelIds, uint32_t* eventIds )
|
||||
{
|
||||
int32_t i = 0;
|
||||
for( auto& eventData : mapData )
|
||||
{
|
||||
iconIds[ i ] = eventData.second.iconId;
|
||||
levelIds[ i ] = eventData.second.levelId;
|
||||
eventIds[ i ] = eventData.first;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::World::Manager::MapMgr::sendPackets( Entity::Player& player, std::multimap< uint32_t, EventData, less >& mapData, UpdateMode updateMode )
|
||||
{
|
||||
player.queuePacket( makeActorControlSelf( player.getId(), Network::ActorControl::BeginMapUpdate, updateMode ) );
|
||||
|
||||
if( mapData.size() <= 2 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
else if( mapData.size() <= 4 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate4 >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
else if( mapData.size() <= 8 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate8 >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
else if( mapData.size() <= 16 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate16 >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
else if( mapData.size() <= 32 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate32 >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
else if( mapData.size() <= 64 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate64 >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
else if( mapData.size() <= 128 )
|
||||
{
|
||||
auto mapUpdatePacket = makeZonePacket< FFXIVIpcMapUpdate128 >( player.getId() );
|
||||
mapUpdatePacket->data().entryCount = mapData.size();
|
||||
|
||||
fillPacket( mapData, mapUpdatePacket->data().iconIds, mapUpdatePacket->data().levelIds, mapUpdatePacket->data().eventIds );
|
||||
|
||||
player.queuePacket( mapUpdatePacket );
|
||||
}
|
||||
|
||||
player.queuePacket( makeActorControlSelf( player.getId(), Network::ActorControl::FinishMapUpdate ) );
|
||||
}
|
132
src/world/Manager/MapMgr.h
Normal file
132
src/world/Manager/MapMgr.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
#ifndef SAPPHIRE_MAPMGR_H
|
||||
#define SAPPHIRE_MAPMGR_H
|
||||
|
||||
#include "ForwardsZone.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
|
||||
namespace Sapphire::World::Manager
|
||||
{
|
||||
|
||||
class MapMgr
|
||||
{
|
||||
public:
|
||||
enum UpdateMode : uint8_t
|
||||
{
|
||||
Quest = 1,
|
||||
GuildLeveAssignment = 2,
|
||||
GuildOrderGuide = 4,
|
||||
TripleTriad = 8,
|
||||
CustomTalk = 16,
|
||||
PreHandler = 32,
|
||||
|
||||
All = 0x3F
|
||||
};
|
||||
|
||||
MapMgr();
|
||||
|
||||
void updateAll( Entity::Player& player );
|
||||
void updateQuests( Entity::Player& player );
|
||||
|
||||
private:
|
||||
struct EventData
|
||||
{
|
||||
uint32_t iconId;
|
||||
uint32_t levelId;
|
||||
uint32_t actorId;
|
||||
};
|
||||
|
||||
struct QuestData
|
||||
{
|
||||
uint8_t previousQuestJoin; // 1 = requires all previous quest, 2 = requires any previous quest
|
||||
uint32_t previousQuestKeys[3];
|
||||
uint8_t previousQuest0Sequence;
|
||||
|
||||
uint8_t questLockJoin; // 1 = only locks when all previous quests are done, 2 = locks when any previous quest is done
|
||||
uint32_t questLockKeys[2];
|
||||
|
||||
struct
|
||||
{
|
||||
std::bitset< Common::CLASSJOB_TOTAL + 1 > classJobCategoryMask;
|
||||
uint16_t classJobLevel;
|
||||
} classJobRequirements[2];
|
||||
|
||||
uint8_t column18;
|
||||
uint8_t classJobUnlock;
|
||||
|
||||
uint8_t requiredGC;
|
||||
uint8_t requiredGCRank;
|
||||
|
||||
uint8_t startTown;
|
||||
|
||||
uint16_t header;
|
||||
|
||||
uint8_t instanceContentJoin; // 1 = requires all needed instances to be completed, 2 = requires any needed instance to be completed
|
||||
uint32_t instanceContent[3];
|
||||
|
||||
uint8_t festival;
|
||||
uint8_t festivalBegin;
|
||||
uint8_t festivalEnd;
|
||||
uint16_t bellStart;
|
||||
uint16_t bellEnd;
|
||||
|
||||
uint8_t repeatIntervalType;
|
||||
uint8_t questRepeatFlag;
|
||||
|
||||
uint8_t beastTribe;
|
||||
uint8_t beastReputationRank;
|
||||
uint16_t beastReputationValue;
|
||||
|
||||
int32_t mount;
|
||||
|
||||
uint8_t satisfactionNpc;
|
||||
uint8_t satisfactionRank;
|
||||
|
||||
uint32_t issuer;
|
||||
|
||||
uint8_t deliveryQuest;
|
||||
|
||||
uint8_t expansion;
|
||||
|
||||
uint8_t type;
|
||||
|
||||
bool isRepeatable;
|
||||
bool isHouseRequired;
|
||||
|
||||
uint32_t iconValid;
|
||||
uint32_t iconInvalid;
|
||||
};
|
||||
|
||||
struct less
|
||||
{
|
||||
constexpr bool operator()( const uint32_t& _Left, const uint32_t& _Right ) const
|
||||
{
|
||||
const uint16_t left = _Left;
|
||||
const uint16_t right = _Right;
|
||||
|
||||
if( left == right )
|
||||
{
|
||||
const uint16_t typeLeft = _Left >> 16;
|
||||
const uint16_t typeRight = _Right >> 16;
|
||||
|
||||
return typeLeft < typeRight;
|
||||
}
|
||||
|
||||
return left < right;
|
||||
}
|
||||
};
|
||||
|
||||
std::map< uint16_t, std::multimap< uint32_t, EventData, less > > m_mapData;
|
||||
std::map< uint32_t, QuestData > m_questData;
|
||||
|
||||
bool isQuestAvailable( Entity::Player& player, std::pair< const uint32_t, EventData >& eventData );
|
||||
bool isTripleTriadAvailable( Entity::Player& player, std::pair< const uint32_t, EventData >& eventData );
|
||||
|
||||
void fillPacket( std::multimap< uint32_t, EventData, less >& mapData, uint32_t* iconIds, uint32_t* levelIds, uint32_t* eventIds );
|
||||
void sendPackets( Entity::Player& player, std::multimap< uint32_t, EventData, less >& mapData, UpdateMode updateMode );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SAPPHIRE_MAPMGR_H
|
|
@ -41,6 +41,7 @@
|
|||
#include "Manager/RNGMgr.h"
|
||||
#include "Manager/NaviMgr.h"
|
||||
#include "Manager/ActionMgr.h"
|
||||
#include "Manager/MapMgr.h"
|
||||
|
||||
#include "Territory/InstanceObjectCache.h"
|
||||
|
||||
|
@ -173,6 +174,10 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] )
|
|||
auto pInstanceObjCache = std::make_shared< Sapphire::InstanceObjectCache >();
|
||||
Common::Service< Sapphire::InstanceObjectCache >::set( pInstanceObjCache );
|
||||
|
||||
Logger::info( "MapMgr: Caching map data" );
|
||||
auto pMapMgr = std::make_shared< Manager::MapMgr >();
|
||||
Common::Service< Manager::MapMgr >::set( pMapMgr );
|
||||
|
||||
auto pActionMgr = std::make_shared< Manager::ActionMgr >();
|
||||
Common::Service< Manager::ActionMgr >::set( pActionMgr );
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue