2018-03-06 22:22:19 +01:00
# include <Common.h>
# include <Util/Util.h>
# include <Util/UtilMath.h>
# include <Logging/Logger.h>
2021-11-27 00:53:57 +01:00
# include <Exd/ExdData.h>
2019-10-27 08:26:48 +01:00
# include <datReader/DatCategories/bg/LgbTypes.h>
2020-01-06 20:19:42 +11:00
# include <datReader/DatCategories/bg/lgb.h>
2019-10-27 08:26:48 +01:00
2018-10-26 14:11:02 +02:00
# include <cmath>
2021-11-27 00:53:57 +01:00
# include <utility>
2020-02-29 22:30:10 +11:00
# include <Service.h>
2017-12-08 15:38:25 +01:00
# include "Session.h"
2017-08-08 13:53:47 +02:00
# include "Player.h"
2019-01-19 22:56:07 +01:00
# include "BNpc.h"
2017-08-08 13:53:47 +02:00
2019-01-13 17:32:43 +01:00
# include "Manager/HousingMgr.h"
2018-12-01 00:27:16 +11:00
# include "Manager/TerritoryMgr.h"
2019-04-17 22:36:23 +02:00
# include "Manager/RNGMgr.h"
2021-11-27 00:53:57 +01:00
# include "Manager/PlayerMgr.h"
# include "Manager/PartyMgr.h"
2019-01-13 17:32:43 +01:00
2019-07-21 22:33:33 +10:00
# include "Territory/Territory.h"
2018-12-01 00:27:16 +11:00
# include "Territory/ZonePosition.h"
2019-01-13 17:32:43 +01:00
# include "Territory/InstanceContent.h"
2019-10-21 23:24:26 +02:00
# include "Territory/InstanceObjectCache.h"
2018-12-01 00:27:16 +11:00
# include "Territory/Land.h"
2018-11-10 19:00:13 +01:00
2017-12-08 15:38:25 +01:00
# include "Network/GameConnection.h"
2021-11-27 00:53:57 +01:00
# include "Network/PacketContainer.h"
# include "Network/CommonActorControl.h"
2019-10-09 18:14:53 +02:00
# include "Network/PacketWrappers/ActorControlPacket.h"
# include "Network/PacketWrappers/ActorControlSelfPacket.h"
2019-06-20 14:48:24 +10:00
# include "Network/PacketWrappers/PlayerSetupPacket.h"
2017-12-08 15:38:25 +01:00
2021-11-27 00:53:57 +01:00
# include "Network/PacketWrappers/PlayerSpawnPacket.h"
# include "Network/PacketWrappers/EffectPacket.h"
2022-01-06 20:25:50 +01:00
# include "Network/PacketWrappers/EffectPacket1.h"
2021-11-27 00:53:57 +01:00
# include "Network/PacketWrappers/InitZonePacket.h"
# include "Network/PacketWrappers/WarpPacket.h"
2017-12-08 15:38:25 +01:00
2019-03-07 21:58:12 +11:00
# include "Action/Action.h"
2017-12-08 15:38:25 +01:00
# include "Math/CalcStats.h"
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
# include "WorldServer.h"
2018-03-02 07:22:25 -03:00
2018-11-29 16:55:48 +01:00
using namespace Sapphire : : Common ;
using namespace Sapphire : : Network : : Packets ;
2021-11-27 00:53:57 +01:00
using namespace Sapphire : : Network : : Packets : : WorldPackets : : Server ;
2018-11-29 16:55:48 +01:00
using namespace Sapphire : : Network : : ActorControl ;
2018-12-01 00:27:16 +11:00
using namespace Sapphire : : World : : Manager ;
2017-08-08 13:53:47 +02:00
2018-11-29 16:55:48 +01:00
using InventoryMap = std : : map < uint16_t , Sapphire : : ItemContainerPtr > ;
2018-07-24 23:58:08 +02:00
using InvSlotPair = std : : pair < uint16_t , int8_t > ;
using InvSlotPairVec = std : : vector < InvSlotPair > ;
2017-08-08 13:53:47 +02:00
// player constructor
2020-03-01 01:00:57 +11:00
Sapphire : : Entity : : Player : : Player ( ) :
Chara ( ObjKind : : Player ) ,
2021-11-27 00:53:57 +01:00
m_lastDBWrite ( 0 ) ,
2018-08-29 21:40:59 +02:00
m_bIsLogin ( false ) ,
2021-11-27 00:53:57 +01:00
m_characterId ( 0 ) ,
2018-08-29 21:40:59 +02:00
m_modelMainWeapon ( 0 ) ,
m_modelSubWeapon ( 0 ) ,
m_homePoint ( 0 ) ,
m_startTown ( 0 ) ,
m_townWarpFstFlags ( 0 ) ,
m_playTime ( 0 ) ,
2021-11-27 00:53:57 +01:00
m_lastActionTick ( 0 ) ,
2018-08-29 21:40:59 +02:00
m_bInCombat ( false ) ,
m_bLoadingComplete ( false ) ,
m_bMarkedForZoning ( false ) ,
m_zoningType ( Common : : ZoneingType : : None ) ,
m_bAutoattack ( false ) ,
m_markedForRemoval ( false ) ,
m_mount ( 0 ) ,
m_emoteMode ( 0 ) ,
m_directorInitialized ( false ) ,
2019-02-06 08:49:57 +01:00
m_onEnterEventDone ( false ) ,
2020-01-05 17:09:27 +09:00
m_falling ( false ) ,
2021-08-10 15:55:10 +09:00
m_pQueuedAction ( nullptr ) ,
2021-11-27 00:53:57 +01:00
m_partyId ( 0 ) ,
m_onlineStatusCustom ( 0 ) ,
m_onlineStatus ( 0 )
2018-08-29 21:40:59 +02:00
{
m_id = 0 ;
m_currentStance = Stance : : Passive ;
m_onlineStatus = 0 ;
m_queuedZoneing = nullptr ;
m_status = ActorStatus : : Idle ;
m_invincibilityType = InvincibilityType : : InvincibilityNone ;
2019-04-21 23:52:41 +10:00
m_radius = 1.f ;
2018-08-29 21:40:59 +02:00
2021-11-27 00:53:57 +01:00
std : : memset ( m_questTracking . data ( ) , 0 , sizeof ( m_questTracking ) ) ;
2018-08-29 21:40:59 +02:00
memset ( m_name , 0 , sizeof ( m_name ) ) ;
2021-11-27 00:53:57 +01:00
memset ( m_stateFlags . data ( ) , 0 , m_stateFlags . size ( ) ) ;
2018-08-29 21:40:59 +02:00
memset ( m_searchMessage , 0 , sizeof ( m_searchMessage ) ) ;
2021-11-27 00:53:57 +01:00
memset ( m_classArray . data ( ) , 0 , sizeof ( m_classArray . data ( ) ) ) ;
memset ( m_expArray . data ( ) , 0 , sizeof ( m_expArray . data ( ) ) ) ;
2021-12-13 22:36:29 -03:00
for ( uint8_t i = 0 ; i < 80 ; + + i )
2021-11-27 00:53:57 +01:00
{
m_recast [ i ] = 0.0f ;
m_recastMax [ i ] = 0.0f ;
}
2018-08-29 21:40:59 +02:00
2021-12-13 22:36:29 -03:00
for ( auto & i : m_charaLandData )
2018-11-07 11:59:59 +01:00
{
2021-11-27 00:53:57 +01:00
memset ( & i , 0xFF , 8 ) ;
memset ( & i . flags , 0 , 8 ) ;
2018-11-07 11:59:59 +01:00
}
2018-08-29 21:40:59 +02:00
m_objSpawnIndexAllocator . init ( MAX_DISPLAYED_EOBJS ) ;
m_actorSpawnIndexAllocator . init ( MAX_DISPLAYED_ACTORS , true ) ;
2021-11-27 00:53:57 +01:00
initHateSlotQueue ( ) ;
initSpawnIdQueue ( ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : ~ Player ( ) = default ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : unload ( )
2018-03-05 22:10:14 +11:00
{
2021-11-27 00:53:57 +01:00
// do one last update to db
updateSql ( ) ;
// reset the zone, so the zone handler knows to remove the actor
setCurrentZone ( nullptr ) ;
// reset isLogin and loading sequences just in case
setIsLogin ( false ) ;
setLoadingComplete ( false ) ;
// unset player for removal
setMarkedForRemoval ( false ) ;
// send updates to mgrs
if ( getPartyId ( ) ! = 0 )
{
auto & partyMgr = Common : : Service < World : : Manager : : PartyMgr > : : ref ( ) ;
partyMgr . onMemberDisconnect ( * this ) ;
}
syncLastDBWrite ( ) ;
2018-03-05 22:10:14 +11:00
}
2017-08-08 13:53:47 +02:00
// TODO: add a proper calculation based on race / job / level / gear
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getMaxHp ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
return max_hp ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getMaxMp ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
return max_mp ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint16_t Sapphire : : Entity : : Player : : getZoneId ( ) const
2017-08-08 13:53:47 +02:00
{
2018-12-02 15:32:22 +11:00
return m_territoryTypeId ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
uint32_t Sapphire : : Entity : : Player : : getPrevTerritoryId ( ) const
{
return m_prevTerritoryId ;
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getGmRank ( ) const
2017-09-11 18:59:50 +02:00
{
2018-08-29 21:40:59 +02:00
return m_gmRank ;
2017-09-11 18:59:50 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setGmRank ( uint8_t rank )
2017-09-11 18:59:50 +02:00
{
2018-08-29 21:40:59 +02:00
m_gmRank = rank ;
2017-09-11 18:59:50 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : getGmInvis ( ) const
2018-04-18 21:00:20 +10:00
{
2018-08-29 21:40:59 +02:00
return m_gmInvis ;
2018-04-18 21:00:20 +10:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setGmInvis ( bool invis )
2018-04-18 21:00:20 +10:00
{
2018-08-29 21:40:59 +02:00
m_gmInvis = invis ;
2018-04-18 21:00:20 +10:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isActingAsGm ( ) const
2018-07-01 21:31:49 +10:00
{
2018-08-29 21:40:59 +02:00
auto status = getOnlineStatus ( ) ;
2021-11-27 00:53:57 +01:00
return status = = OnlineStatus : : GameMaster | | status = = OnlineStatus : : GameMaster1 | | status = = OnlineStatus : : GameMaster2 ;
2018-07-01 21:31:49 +10:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getMode ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_mode ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setMode ( uint8_t mode )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_mode = mode ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getStartTown ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_startTown ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setMarkedForRemoval ( bool removal )
2017-11-14 23:55:38 +01:00
{
2021-11-27 00:53:57 +01:00
m_markedForRemoval = removal ;
2017-11-14 23:55:38 +01:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isMarkedForRemoval ( ) const
2017-11-14 23:55:38 +01:00
{
2018-08-29 21:40:59 +02:00
return m_markedForRemoval ;
2017-11-14 23:55:38 +01:00
}
2018-11-29 16:55:48 +01:00
Sapphire : : Common : : OnlineStatus Sapphire : : Entity : : Player : : getOnlineStatus ( ) const
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2018-04-18 21:00:20 +10:00
2018-08-29 21:40:59 +02:00
uint32_t statusDisplayOrder = 0xFF14 ;
2021-11-27 00:53:57 +01:00
auto applicableStatus = static_cast < uint32_t > ( OnlineStatus : : Online ) ;
2018-04-19 23:04:31 +10:00
2021-11-27 00:53:57 +01:00
for ( uint32_t i = 0 ; i < std : : numeric_limits < decltype ( m_onlineStatus ) > : : digits ; + + i )
2018-08-29 21:40:59 +02:00
{
2021-11-27 00:53:57 +01:00
bool bit = ( getFullOnlineStatusMask ( ) > > i ) & 1 ;
2018-04-18 21:00:20 +10:00
2018-08-29 21:40:59 +02:00
if ( ! bit )
continue ;
2018-04-18 21:00:20 +10:00
2021-11-27 00:53:57 +01:00
auto pOnlineStatus = exdData . getRow < Component : : Excel : : OnlineStatus > ( i ) ;
2018-08-29 21:40:59 +02:00
if ( ! pOnlineStatus )
continue ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
if ( pOnlineStatus - > data ( ) . ListOrder < statusDisplayOrder )
2018-08-29 21:40:59 +02:00
{
// todo: also check that the status can actually be set here, otherwise we need to ignore it (and ban the player obv)
2021-11-27 00:53:57 +01:00
statusDisplayOrder = pOnlineStatus - > data ( ) . ListOrder ;
2018-08-29 21:40:59 +02:00
applicableStatus = i ;
}
}
2018-04-24 20:34:36 +10:00
2018-08-29 21:40:59 +02:00
return static_cast < OnlineStatus > ( applicableStatus ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setOnlineStatusMask ( uint64_t status )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_onlineStatus = status ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint64_t Sapphire : : Entity : : Player : : getOnlineStatusMask ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_onlineStatus ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
uint64_t Sapphire : : Entity : : Player : : getFullOnlineStatusMask ( ) const
{
return m_onlineStatus | m_onlineStatusCustom ;
}
/*! sets the list of current online status */
void Sapphire : : Entity : : Player : : setOnlineStatusCustomMask ( uint64_t status )
{
m_onlineStatusCustom = status ;
}
uint64_t Sapphire : : Entity : : Player : : getOnlineStatusCustomMask ( ) const
{
return m_onlineStatusCustom ;
}
void Sapphire : : Entity : : Player : : addOnlineStatus ( OnlineStatus status )
{
uint64_t statusValue = 1ull < < static_cast < uint8_t > ( status ) ;
uint64_t newFlags = ( getOnlineStatusMask ( ) & getOnlineStatusCustomMask ( ) ) | statusValue ;
setOnlineStatusMask ( newFlags ) ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onOnlineStatusChanged ( * this , false ) ;
}
void Sapphire : : Entity : : Player : : addOnlineStatus ( const std : : vector < Common : : OnlineStatus > & status )
{
uint64_t newFlags = getOnlineStatusMask ( ) ;
for ( const auto & state : status )
{
uint64_t statusValue = 1ull < < static_cast < uint8_t > ( state ) ;
newFlags | = statusValue ;
}
setOnlineStatusMask ( newFlags ) ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onOnlineStatusChanged ( * this , false ) ;
}
void Sapphire : : Entity : : Player : : removeOnlineStatus ( OnlineStatus status )
{
uint64_t statusValue = 1ull < < static_cast < uint8_t > ( status ) ;
uint64_t newFlags = getOnlineStatusMask ( ) ;
uint64_t newFlagsCustom = getOnlineStatusCustomMask ( ) ;
newFlags & = ~ statusValue ;
newFlagsCustom & = ~ statusValue ;
setOnlineStatusMask ( newFlags ) ;
setOnlineStatusCustomMask ( newFlagsCustom ) ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onOnlineStatusChanged ( * this , false ) ;
}
void Sapphire : : Entity : : Player : : removeOnlineStatus ( const std : : vector < Common : : OnlineStatus > & status )
{
uint64_t newFlags = getOnlineStatusMask ( ) ;
uint64_t newFlagsCustom = getOnlineStatusCustomMask ( ) ;
for ( const auto & state : status )
{
uint64_t statusValue = 1ull < < static_cast < uint8_t > ( state ) ;
newFlags & = ~ statusValue ;
newFlagsCustom & = ~ statusValue ;
}
setOnlineStatusMask ( newFlags ) ;
setOnlineStatusCustomMask ( newFlagsCustom ) ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onOnlineStatusChanged ( * this , false ) ;
}
void Sapphire : : Entity : : Player : : prepareZoning ( uint16_t targetZone , bool fadeOut , uint8_t fadeOutTime , uint16_t animation )
2017-08-08 13:53:47 +02:00
{
2019-07-29 22:22:45 +10:00
auto preparePacket = makeZonePacket < FFXIVIpcPrepareZoning > ( getId ( ) ) ;
2018-08-29 21:40:59 +02:00
preparePacket - > data ( ) . targetZone = targetZone ;
preparePacket - > data ( ) . fadeOutTime = fadeOutTime ;
preparePacket - > data ( ) . animation = animation ;
preparePacket - > data ( ) . fadeOut = static_cast < uint8_t > ( fadeOut ? 1 : 0 ) ;
queuePacket ( preparePacket ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : calculateStats ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint8_t tribe = getLookAt ( Common : : CharaLook : : Tribe ) ;
uint8_t level = getLevel ( ) ;
2021-11-27 00:53:57 +01:00
auto job = static_cast < uint8_t > ( getClass ( ) ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2018-03-09 00:06:44 +01:00
2021-11-27 00:53:57 +01:00
auto classInfo = exdData . getRow < Component : : Excel : : ClassJob > ( job ) ;
auto tribeInfo = exdData . getRow < Component : : Excel : : Tribe > ( tribe ) ;
auto paramGrowthInfo = exdData . getRow < Component : : Excel : : ParamGrow > ( level ) ;
2017-08-08 13:53:47 +02:00
2019-04-24 23:25:07 +10:00
float base = Math : : CalcStats : : calculateBaseStat ( * this ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
auto str = static_cast < uint32_t > ( base * ( static_cast < float > ( classInfo - > data ( ) . STR ) / 100 ) ) + tribeInfo - > data ( ) . STR ;
auto dex = static_cast < uint32_t > ( base * ( static_cast < float > ( classInfo - > data ( ) . DEX ) / 100 ) ) + tribeInfo - > data ( ) . DEX ;
auto vit = static_cast < uint32_t > ( base * ( static_cast < float > ( classInfo - > data ( ) . VIT ) / 100 ) ) + tribeInfo - > data ( ) . VIT ;
auto inte = static_cast < uint32_t > ( base * ( static_cast < float > ( classInfo - > data ( ) . INT_ ) / 100 ) ) + tribeInfo - > data ( ) . INT_ ;
auto mnd = static_cast < uint32_t > ( base * ( static_cast < float > ( classInfo - > data ( ) . MND ) / 100 ) ) + tribeInfo - > data ( ) . MND ;
auto pie = static_cast < uint32_t > ( base * ( static_cast < float > ( classInfo - > data ( ) . PIE ) / 100 ) ) + tribeInfo - > data ( ) . PIE ;
setStatValue ( BaseParam : : Strength , str ) ;
setStatValue ( BaseParam : : Dexterity , dex ) ;
setStatValue ( BaseParam : : Vitality , vit ) ;
setStatValue ( BaseParam : : Intelligence , inte ) ;
setStatValue ( BaseParam : : Mind , mnd ) ;
setStatValue ( BaseParam : : Piety , pie ) ;
auto determination = static_cast < uint32_t > ( base ) ;
auto skillSpeed = paramGrowthInfo - > data ( ) . ParamBase ;
auto spellSpeed = paramGrowthInfo - > data ( ) . ParamBase ;
auto accuracy = paramGrowthInfo - > data ( ) . ParamBase ;
auto critHitRate = paramGrowthInfo - > data ( ) . ParamBase ;
auto parry = paramGrowthInfo - > data ( ) . ParamBase ;
setStatValue ( BaseParam : : Determination , determination ) ;
setStatValue ( BaseParam : : SkillSpeed , skillSpeed ) ;
setStatValue ( BaseParam : : SpellSpeed , spellSpeed ) ;
setStatValue ( BaseParam : : CriticalHit , critHitRate ) ;
setStatValue ( BaseParam : : Accuracy , accuracy ) ;
setStatValue ( BaseParam : : Parry , parry ) ;
2018-01-31 11:43:22 +01:00
2021-11-27 00:53:57 +01:00
setStatValue ( BaseParam : : Haste , 100 ) ;
setStatValue ( BaseParam : : Defense , 0 ) ;
setStatValue ( BaseParam : : MagicDefense , 0 ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
setStatValue ( BaseParam : : FireResistance , classInfo - > data ( ) . Element [ 0 ] ) ;
setStatValue ( BaseParam : : IceResistance , classInfo - > data ( ) . Element [ 1 ] ) ;
setStatValue ( BaseParam : : WindResistance , classInfo - > data ( ) . Element [ 2 ] ) ;
setStatValue ( BaseParam : : EarthResistance , classInfo - > data ( ) . Element [ 3 ] ) ;
setStatValue ( BaseParam : : LightningResistance , classInfo - > data ( ) . Element [ 4 ] ) ;
setStatValue ( BaseParam : : WaterResistance , classInfo - > data ( ) . Element [ 5 ] ) ;
2019-03-20 21:29:34 +01:00
2021-11-27 00:53:57 +01:00
setStatValue ( BaseParam : : AttackPower , str ) ;
setStatValue ( BaseParam : : AttackMagicPotency , inte ) ;
setStatValue ( BaseParam : : HealingMagicPotency , mnd ) ;
2017-08-19 11:28:04 +09:00
2021-12-14 23:05:58 +01:00
max_mp = Math : : CalcStats : : calculateMaxMp ( * this ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
max_hp = Math : : CalcStats : : calculateMaxHp ( * this ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
if ( m_mp > max_mp )
m_mp = max_mp ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
if ( m_hp > max_hp )
m_hp = max_hp ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setAutoattack ( bool mode )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_bAutoattack = mode ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isAutoattackOn ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_bAutoattack ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : sendStats ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onSendStats ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : teleport ( uint16_t aetheryteId , uint8_t type )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2020-03-01 01:00:57 +11:00
auto & terriMgr = Common : : Service < TerritoryMgr > : : ref ( ) ;
2018-03-09 00:06:44 +01:00
2021-11-27 00:53:57 +01:00
auto aetherData = exdData . getRow < Component : : Excel : : Aetheryte > ( aetheryteId ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
if ( ! aetherData )
2018-08-29 21:40:59 +02:00
return ;
2021-11-27 00:53:57 +01:00
const auto & data = aetherData - > data ( ) ;
2018-08-29 21:40:59 +02:00
setStateFlag ( PlayerStateFlag : : BetweenAreas ) ;
2019-10-21 23:24:26 +02:00
2020-03-01 01:00:57 +11:00
auto & instanceObjectCache = Common : : Service < InstanceObjectCache > : : ref ( ) ;
2021-11-27 00:53:57 +01:00
auto pop = instanceObjectCache . getPopRange ( data . TerritoryType , data . PopRange [ 0 ] ) ;
Common : : FFXIVARR_POSITION3 pos { 0.f , 0.f , 0.f } ;
2018-08-29 21:40:59 +02:00
2021-11-27 00:53:57 +01:00
float rot = 0.f ;
2018-08-29 21:40:59 +02:00
2019-10-27 08:26:48 +01:00
if ( pop )
{
2021-11-27 00:53:57 +01:00
PlayerMgr : : sendDebug ( * this , " Teleport: popRange {0} found! " , data . PopRange [ 0 ] ) ;
const auto & transform = pop - > header . transform ;
pos = Common : : FFXIVARR_POSITION3 { transform . translation . x , transform . translation . y , transform . translation . z } ;
auto targetRot = Common : : FFXIVARR_POSITION3 { transform . rotation . x , transform . rotation . y , transform . rotation . z } ;
rot = Common : : Util : : eulerToDirection ( targetRot ) ;
2019-10-27 08:26:48 +01:00
}
else
2018-08-29 21:40:59 +02:00
{
2021-11-27 00:53:57 +01:00
PlayerMgr : : sendDebug ( * this , " Teleport: popRange {0} not found in {1}! " , data . PopRange [ 0 ] , data . TerritoryType ) ;
2018-08-29 21:40:59 +02:00
}
2021-11-27 00:53:57 +01:00
auto townPlace = exdData . getRow < Component : : Excel : : PlaceName > ( data . TelepoName ) ;
auto aetherytePlace = exdData . getRow < Component : : Excel : : PlaceName > ( data . TransferName ) ;
PlayerMgr : : sendDebug ( * this , " Teleport: {0} - {1} ({2}) " ,
townPlace - > getString ( townPlace - > data ( ) . Text . SGL ) ,
aetherytePlace - > getString ( aetherytePlace - > data ( ) . Text . SGL ) ,
data . TerritoryType ) ;
2018-08-29 21:40:59 +02:00
// TODO: this should be simplified and a type created in server_common/common.h.
if ( type = = 1 ) // teleport
{
2021-11-27 00:53:57 +01:00
prepareZoning ( data . TerritoryType , true , 1 , 0 ) ; // TODO: Really?
2019-10-09 18:42:25 +02:00
sendToInRangeSet ( makeActorControl ( getId ( ) , ActorDespawnEffect , 0x04 ) ) ;
2018-08-29 21:40:59 +02:00
setZoningType ( Common : : ZoneingType : : Teleport ) ;
}
else if ( type = = 2 ) // aethernet
{
2021-11-27 00:53:57 +01:00
prepareZoning ( data . TerritoryType , true , 1 , 112 ) ;
2019-10-09 18:42:25 +02:00
sendToInRangeSet ( makeActorControl ( getId ( ) , ActorDespawnEffect , 0x04 ) ) ;
2018-08-29 21:40:59 +02:00
setZoningType ( Common : : ZoneingType : : Teleport ) ;
}
else if ( type = = 3 ) // return
{
2021-11-27 00:53:57 +01:00
prepareZoning ( data . TerritoryType , true , 1 , 111 ) ;
2019-10-09 18:42:25 +02:00
sendToInRangeSet ( makeActorControl ( getId ( ) , ActorDespawnEffect , 0x03 ) ) ;
2018-08-29 21:40:59 +02:00
setZoningType ( Common : : ZoneingType : : Return ) ;
}
2021-11-27 00:53:57 +01:00
m_queuedZoneing = std : : make_shared < QueuedZoning > ( data . TerritoryType , pos , Util : : getTimeMs ( ) , rot ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : forceZoneing ( uint32_t zoneId )
2017-08-08 13:53:47 +02:00
{
2018-10-25 12:44:51 +11:00
m_queuedZoneing = std : : make_shared < QueuedZoning > ( zoneId , getPos ( ) , Util : : getTimeMs ( ) , 0.f ) ;
2018-08-29 21:40:59 +02:00
//performZoning( zoneId, Common::ZoneingType::None, getPos() );
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : returnToHomepoint ( )
2017-08-10 16:31:48 +02:00
{
2018-08-29 21:40:59 +02:00
setZoningType ( Common : : ZoneingType : : Return ) ;
teleport ( getHomepoint ( ) , 3 ) ;
2017-08-10 16:31:48 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setZone ( uint32_t zoneId )
2017-08-08 13:53:47 +02:00
{
2020-03-01 01:00:57 +11:00
auto & teriMgr = Common : : Service < TerritoryMgr > : : ref ( ) ;
2018-08-29 21:40:59 +02:00
m_onEnterEventDone = false ;
2022-01-10 23:50:44 +01:00
auto pZone = teriMgr . getZoneByTerritoryTypeId ( zoneId ) ;
if ( ! teriMgr . movePlayer ( pZone , * this ) )
2018-08-29 21:40:59 +02:00
{
// todo: this will require proper handling, for now just return the player to their previous area
m_pos = m_prevPos ;
m_rot = m_prevRot ;
2018-12-02 15:32:22 +11:00
m_territoryTypeId = m_prevTerritoryTypeId ;
2018-02-04 18:53:19 +01:00
2022-01-10 23:50:44 +01:00
auto pZone1 = teriMgr . getZoneByTerritoryTypeId ( m_territoryTypeId ) ;
if ( ! teriMgr . movePlayer ( pZone1 , * this ) )
2018-08-29 21:40:59 +02:00
return ;
}
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : setInstance ( uint32_t instanceContentId )
2018-01-29 13:05:33 +11:00
{
2018-08-29 21:40:59 +02:00
m_onEnterEventDone = false ;
2020-03-01 01:00:57 +11:00
auto & teriMgr = Common : : Service < TerritoryMgr > : : ref ( ) ;
2019-01-14 22:29:52 +01:00
2020-03-01 01:00:57 +11:00
auto instance = teriMgr . getTerritoryByGuId ( instanceContentId ) ;
2018-08-29 21:40:59 +02:00
if ( ! instance )
return false ;
2018-01-29 13:05:33 +11:00
2018-08-29 21:40:59 +02:00
return setInstance ( instance ) ;
2018-01-29 13:05:33 +11:00
}
2021-11-27 00:53:57 +01:00
bool Sapphire : : Entity : : Player : : setInstance ( const TerritoryPtr & instance )
2018-01-29 13:05:33 +11:00
{
2018-08-29 21:40:59 +02:00
m_onEnterEventDone = false ;
if ( ! instance )
return false ;
2018-01-29 13:05:33 +11:00
2020-03-01 01:00:57 +11:00
auto & teriMgr = Common : : Service < TerritoryMgr > : : ref ( ) ;
2018-12-02 15:32:22 +11:00
2018-08-29 21:40:59 +02:00
// zoning within the same zone won't cause the prev data to be overwritten
2018-12-02 15:32:22 +11:00
if ( instance - > getTerritoryTypeId ( ) ! = m_territoryTypeId )
2018-08-29 21:40:59 +02:00
{
2022-01-10 23:50:44 +01:00
auto pZone = teriMgr . getTerritoryByGuId ( getTerritoryId ( ) ) ;
m_prevTerritoryTypeId = pZone - > getTerritoryTypeId ( ) ;
m_prevTerritoryId = getTerritoryId ( ) ;
2018-08-29 21:40:59 +02:00
m_prevPos = m_pos ;
m_prevRot = m_rot ;
}
2018-01-29 13:05:33 +11:00
2021-11-27 00:53:57 +01:00
return teriMgr . movePlayer ( instance , * this ) ;
2018-01-29 13:05:33 +11:00
}
2021-11-27 00:53:57 +01:00
bool Sapphire : : Entity : : Player : : setInstance ( const TerritoryPtr & instance , Common : : FFXIVARR_POSITION3 pos )
2018-12-30 18:48:45 +11:00
{
2019-01-14 22:29:52 +01:00
m_onEnterEventDone = false ;
2018-12-30 18:48:45 +11:00
if ( ! instance )
return false ;
2020-03-01 01:00:57 +11:00
auto & teriMgr = Common : : Service < TerritoryMgr > : : ref ( ) ;
2018-12-30 18:48:45 +11:00
2019-01-14 22:29:52 +01:00
// zoning within the same zone won't cause the prev data to be overwritten
if ( instance - > getTerritoryTypeId ( ) ! = m_territoryTypeId )
{
2022-01-10 23:50:44 +01:00
auto pZone = teriMgr . getTerritoryByGuId ( getTerritoryId ( ) ) ;
m_prevTerritoryTypeId = pZone - > getTerritoryTypeId ( ) ;
m_prevTerritoryId = getTerritoryId ( ) ;
2019-01-14 22:29:52 +01:00
m_prevPos = m_pos ;
m_prevRot = m_rot ;
}
2018-12-30 18:48:45 +11:00
2021-11-27 00:53:57 +01:00
if ( teriMgr . movePlayer ( instance , * this ) )
2018-12-30 18:48:45 +11:00
{
2021-11-27 00:53:57 +01:00
m_pos = pos ;
2018-12-30 18:48:45 +11:00
return true ;
}
return false ;
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : exitInstance ( )
2018-01-29 13:05:33 +11:00
{
2020-03-01 01:00:57 +11:00
auto & teriMgr = Common : : Service < TerritoryMgr > : : ref ( ) ;
2022-01-10 23:50:44 +01:00
auto pZone = teriMgr . getTerritoryByGuId ( getTerritoryId ( ) ) ;
2021-11-27 00:53:57 +01:00
auto pInstance = pZone - > getAsInstanceContent ( ) ;
2019-01-13 17:32:43 +01:00
2019-04-09 23:17:00 +02:00
resetHp ( ) ;
resetMp ( ) ;
2018-12-02 15:32:22 +11:00
// check if housing zone
2020-03-01 01:00:57 +11:00
if ( teriMgr . isHousingTerritory ( m_prevTerritoryTypeId ) )
2018-12-02 15:32:22 +11:00
{
2021-11-27 00:53:57 +01:00
if ( ! teriMgr . movePlayer ( teriMgr . getZoneByLandSetId ( m_prevTerritoryId ) , * this ) )
2018-12-02 15:32:22 +11:00
return false ;
}
else
{
2022-01-10 23:50:44 +01:00
auto pPrevZone = teriMgr . getZoneByTerritoryTypeId ( m_prevTerritoryTypeId ) ;
if ( ! teriMgr . movePlayer ( pPrevZone , * this ) )
2018-12-02 15:32:22 +11:00
return false ;
}
2018-01-29 13:05:33 +11:00
2018-08-29 21:40:59 +02:00
m_pos = m_prevPos ;
m_rot = m_prevRot ;
2018-12-02 15:32:22 +11:00
m_territoryTypeId = m_prevTerritoryTypeId ;
m_territoryId = m_prevTerritoryId ;
2018-01-29 13:05:33 +11:00
2019-01-13 17:32:43 +01:00
//m_queuedZoneing = std::make_shared< QueuedZoning >( m_territoryTypeId, m_pos, Util::getTimeMs(), m_rot );
2018-08-29 21:40:59 +02:00
return true ;
2018-01-29 13:05:33 +11:00
}
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getPlayTime ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_playTime ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getRace ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return getLookAt ( CharaLook : : Race ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getGender ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return getLookAt ( CharaLook : : Gender ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : initSpawnIdQueue ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_actorSpawnIndexAllocator . freeAllSpawnIndexes ( ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getSpawnIdForActorId ( uint32_t actorId )
2017-08-08 13:53:47 +02:00
{
2019-01-28 21:02:51 +11:00
auto index = m_actorSpawnIndexAllocator . getNextFreeSpawnIndex ( actorId ) ;
if ( index = = m_actorSpawnIndexAllocator . getAllocFailId ( ) )
{
Logger : : warn ( " Failed to spawn Chara#{0} for Player#{1} - no remaining spawn indexes available. "
" Consider lowering InRangeDistance in world config. " ,
actorId , getId ( ) ) ;
2021-11-27 00:53:57 +01:00
PlayerMgr : : sendUrgent ( * this , " Failed to spawn Chara#{0} for you - no remaining spawn slots. See world log. " , actorId ) ;
2019-01-28 21:02:51 +11:00
}
return index ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isActorSpawnIdValid ( uint8_t spawnIndex )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_actorSpawnIndexAllocator . isSpawnIndexValid ( spawnIndex ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : registerAetheryte ( uint8_t aetheryteId )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( aetheryteId , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_aetheryte [ index ] | = value ;
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , LearnTeleport , aetheryteId , 1 ) ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isAetheryteRegistered ( uint8_t aetheryteId ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( aetheryteId , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
return ( m_aetheryte [ index ] & value ) ! = 0 ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : Discovery & Sapphire : : Entity : : Player : : getDiscoveryBitmask ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_discovery ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : discover ( int16_t map_id , int16_t sub_id )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
// map.exd field 12 -> index in one of the two discovery sections, if field 15 is false, need to use 2nd section
2021-11-27 00:53:57 +01:00
// section 1 starts at 0 - 2 bytes each
2018-08-29 21:40:59 +02:00
// section to starts at 320 - 4 bytes long
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2018-03-09 00:06:44 +01:00
2021-11-27 00:53:57 +01:00
int32_t offset ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
auto info = exdData . getRow < Component : : Excel : : Map > ( map_id ) ;
if ( ! info )
2020-03-03 08:37:19 +00:00
{
2021-11-27 00:53:57 +01:00
PlayerMgr : : sendDebug ( * this , " discover(): Could not obtain map data for map_id == {0} " , map_id ) ;
2020-03-03 08:37:19 +00:00
return ;
}
2021-11-27 00:53:57 +01:00
if ( info - > data ( ) . IsUint16Discovery )
offset = 2 * info - > data ( ) . DiscoveryIndex ;
2018-08-29 21:40:59 +02:00
else
2021-11-27 00:53:57 +01:00
offset = 320 + 4 * info - > data ( ) . DiscoveryIndex ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
int32_t index = offset + sub_id / 8 ;
uint8_t bitIndex = sub_id % 8 ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint8_t value = 1 < < bitIndex ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_discovery [ index ] | = value ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint16_t level = getLevel ( ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
uint32_t exp = ( exdData . getRow < Component : : Excel : : ParamGrow > ( level ) - > data ( ) . NextExp * 5 / 100 ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
gainExp ( exp ) ;
2017-08-08 13:53:47 +02:00
2020-03-02 08:52:28 +00:00
// gain 10x additional EXP if entire map is completed
2021-11-27 00:53:57 +01:00
uint32_t mask = info - > data ( ) . DiscoveryFlag ;
2020-03-02 08:52:28 +00:00
uint32_t discoveredAreas ;
2021-11-27 00:53:57 +01:00
if ( info - > data ( ) . IsUint16Discovery )
2020-03-02 08:52:28 +00:00
{
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 ) ;
2017-08-08 13:53:47 +02:00
2020-03-02 08:52:28 +00:00
if ( allDiscovered )
{
gainExp ( exp * 10 ) ;
}
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isNewAdventurer ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_bNewAdventurer ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setNewAdventurer ( bool state )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
//if( !state )
//{
// unsetStateFlag( PlayerStateFlag::NewAdventurer );
//}
//else
//{
// setStateFlag( PlayerStateFlag::NewAdventurer );
//}
m_bNewAdventurer = state ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : resetDiscovery ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
memset ( m_discovery . data ( ) , 0 , m_discovery . size ( ) ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : changePosition ( float x , float y , float z , float o )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
Common : : FFXIVARR_POSITION3 pos { x , y , z } ;
2018-10-25 12:44:51 +11:00
m_queuedZoneing = std : : make_shared < QueuedZoning > ( getZoneId ( ) , pos , Util : : getTimeMs ( ) , o ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : learnAction ( Common : : UnlockEntry unlockId )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
2021-11-27 00:53:57 +01:00
auto unlock = static_cast < uint16_t > ( unlockId ) ;
Util : : valueToFlagByteIndexValue ( unlock , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_unlocks [ index ] | = value ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
queuePacket ( makeActorControlSelf ( getId ( ) , ToggleActionUnlock , unlock , 1 ) ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : learnSong ( uint8_t songId , uint32_t itemId )
2017-10-09 20:09:49 +02:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( songId , value , index ) ;
2017-10-09 20:09:49 +02:00
2018-08-29 21:40:59 +02:00
m_orchestrion [ index ] | = value ;
2017-10-09 20:09:49 +02:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onUnlockOrchestrion ( * this , songId , itemId ) ;
2017-10-09 20:09:49 +02:00
}
2021-11-27 00:53:57 +01:00
bool Sapphire : : Entity : : Player : : isActionLearned ( Common : : UnlockEntry unlockId ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
2021-11-27 00:53:57 +01:00
Util : : valueToFlagByteIndexValue ( static_cast < uint16_t > ( unlockId ) , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
return ( m_unlocks [ index ] & value ) ! = 0 ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : gainExp ( uint32_t amount )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint32_t currentExp = getExp ( ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint16_t level = getLevel ( ) ;
2017-08-08 13:53:47 +02:00
2020-02-27 00:59:28 +09:00
if ( level > = Common : : MAX_PLAYER_LEVEL )
{
setExp ( 0 ) ;
if ( currentExp ! = 0 )
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onGainExp ( * this , 0 ) ;
2020-02-27 00:59:28 +09:00
return ;
}
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
uint32_t neededExpToLevel = exdData . getRow < Component : : Excel : : ParamGrow > ( level ) - > data ( ) . NextExp ;
uint32_t neededExpToLevelPlus1 = exdData . getRow < Component : : Excel : : ParamGrow > ( level + 1 ) - > data ( ) . NextExp ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
if ( ( currentExp + amount ) > = neededExpToLevel )
{
// levelup
2021-11-27 00:53:57 +01:00
amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelPlus1 ?
neededExpToLevelPlus1 - 1 :
2018-08-29 21:40:59 +02:00
( currentExp + amount - neededExpToLevel ) ;
2020-02-27 00:59:28 +09:00
if ( level + 1 > = Common : : MAX_PLAYER_LEVEL )
amount = 0 ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
setExp ( amount ) ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onGainExp ( * this , amount ) ;
levelUp ( ) ;
2018-08-29 21:40:59 +02:00
}
else
{
setExp ( currentExp + amount ) ;
2021-12-21 00:13:50 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onGainExp ( * this , amount ) ;
2018-08-29 21:40:59 +02:00
}
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : levelUp ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_hp = getMaxHp ( ) ;
m_mp = getMaxMp ( ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
setLevel ( getLevel ( ) + 1 ) ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onLevelUp ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2019-01-26 13:40:02 +11:00
void Sapphire : : Entity : : Player : : sendStatusUpdate ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onPlayerHpMpTpChanged ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getLevel ( ) const
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
uint8_t classJobIndex = exdData . getRow < Component : : Excel : : ClassJob > ( static_cast < uint8_t > ( getClass ( ) ) ) - > data ( ) . WorkIndex ;
2018-08-29 21:40:59 +02:00
return static_cast < uint8_t > ( m_classArray [ classJobIndex ] ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
uint8_t Sapphire : : Entity : : Player : : getLevelSync ( ) const
{
// TODO: implement levelSync
return getLevel ( ) ;
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getLevelForClass ( Common : : ClassJob pClass ) const
2017-08-09 14:38:46 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
uint8_t classJobIndex = exdData . getRow < Component : : Excel : : ClassJob > ( static_cast < uint8_t > ( pClass ) ) - > data ( ) . WorkIndex ;
2018-08-29 21:40:59 +02:00
return static_cast < uint8_t > ( m_classArray [ classJobIndex ] ) ;
2017-08-09 14:38:46 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isClassJobUnlocked ( Common : : ClassJob classJob ) const
2018-08-26 16:19:05 +10:00
{
2018-08-29 21:40:59 +02:00
// todo: need to properly check if a job is unlocked, at the moment we just check the class array which will return true for every job if the base class is unlocked
return getLevelForClass ( classJob ) ! = 0 ;
2018-08-26 16:19:05 +10:00
}
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getExp ( ) const
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
uint8_t classJobIndex = exdData . getRow < Component : : Excel : : ClassJob > ( static_cast < uint8_t > ( getClass ( ) ) ) - > data ( ) . WorkIndex ;
2018-08-29 21:40:59 +02:00
return m_expArray [ classJobIndex ] ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setExp ( uint32_t amount )
2018-08-29 21:40:59 +02:00
{
2021-11-27 00:53:57 +01:00
auto exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
uint8_t classJobIndex = exdData . getRow < Component : : Excel : : ClassJob > ( static_cast < uint8_t > ( getClass ( ) ) ) - > data ( ) . WorkIndex ;
2018-08-29 21:40:59 +02:00
m_expArray [ classJobIndex ] = amount ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isInCombat ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_bInCombat ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setInCombat ( bool mode )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
//m_lastAttack = GetTickCount();
m_bInCombat = mode ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setClassJob ( Common : : ClassJob classJob )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_class = classJob ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
if ( getHp ( ) > getMaxHp ( ) )
m_hp = getMaxHp ( ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
if ( getMp ( ) > getMaxMp ( ) )
m_mp = getMaxMp ( ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_tp = 0 ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onPlayerStatusUpdate ( * this ) ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onChangeClass ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setLevel ( uint8_t level )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
uint8_t classJobIndex = exdData . getRow < Component : : Excel : : ClassJob > ( static_cast < uint8_t > ( getClass ( ) ) ) - > data ( ) . WorkIndex ;
2018-08-29 21:40:59 +02:00
m_classArray [ classJobIndex ] = level ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setLevelForClass ( uint8_t level , Common : : ClassJob classjob )
2017-08-09 14:38:46 +02:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
uint8_t classJobIndex = exdData . getRow < Component : : Excel : : ClassJob > ( static_cast < uint8_t > ( getClass ( ) ) ) - > data ( ) . WorkIndex ;
2017-12-16 23:00:04 +11:00
2018-08-29 21:40:59 +02:00
if ( m_classArray [ classJobIndex ] = = 0 )
2021-11-27 00:53:57 +01:00
insertDbClass ( classJobIndex , level ) ;
2017-12-16 23:00:04 +11:00
2018-08-29 21:40:59 +02:00
m_classArray [ classJobIndex ] = level ;
2017-08-09 14:38:46 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : sendModel ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onChangeGear ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getModelForSlot ( Common : : GearModelSlot slot )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_modelEquip [ slot ] ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint64_t Sapphire : : Entity : : Player : : getModelMainWeapon ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_modelMainWeapon ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint64_t Sapphire : : Entity : : Player : : getModelSubWeapon ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_modelSubWeapon ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint64_t Sapphire : : Entity : : Player : : getModelSystemWeapon ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_modelSystemWeapon ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
uint8_t Sapphire : : Entity : : Player : : getAetheryteMaskAt ( uint8_t index ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
if ( index > sizeof ( m_aetheryte ) )
return 0 ;
return m_aetheryte [ index ] ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getBirthDay ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_birthDay ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getBirthMonth ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_birthMonth ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getGuardianDeity ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_guardianDeity ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getLookAt ( uint8_t index ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_customize [ index ] ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setLookAt ( uint8_t index , uint8_t value )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_customize [ index ] = value ;
2017-08-08 13:53:47 +02:00
}
// spawn this player for pTarget
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : spawn ( Entity : : PlayerPtr pTarget )
2017-08-08 13:53:47 +02:00
{
2021-12-02 19:47:31 +01:00
Logger : : debug ( " Spawning {0} for {1} " , getName ( ) , pTarget - > getName ( ) ) ;
2021-12-01 01:03:36 +01:00
auto spawnPacket = std : : make_shared < PlayerSpawnPacket > ( * this , * pTarget ) ;
pTarget - > queuePacket ( spawnPacket ) ;
2017-08-08 13:53:47 +02:00
}
// despawn
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : despawn ( Entity : : PlayerPtr pTarget )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
const auto & pPlayer = pTarget ;
2019-01-04 12:34:19 +01:00
Logger : : debug ( " Despawning {0} for {1} " , getName ( ) , pTarget - > getName ( ) ) ;
2018-02-21 18:06:52 +01:00
2018-08-29 21:40:59 +02:00
pPlayer - > freePlayerSpawnId ( getId ( ) ) ;
2017-08-08 13:53:47 +02:00
2022-01-07 21:22:55 +01:00
pPlayer - > queuePacket ( makeActorControlSelf ( getId ( ) , WarpStart , 0x04 , getId ( ) , 0x01 ) ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : GameObjectPtr Sapphire : : Entity : : Player : : lookupTargetById ( uint64_t targetId )
2017-08-10 22:06:05 +02:00
{
2021-11-27 00:53:57 +01:00
GameObjectPtr targetActor ;
2018-08-29 21:40:59 +02:00
auto inRange = getInRangeActors ( true ) ;
2021-11-27 00:53:57 +01:00
for ( const auto & actor : inRange )
2018-08-29 21:40:59 +02:00
{
if ( actor - > getId ( ) = = targetId )
targetActor = actor ;
}
return targetActor ;
2017-08-10 22:06:05 +02:00
}
2021-11-27 00:53:57 +01:00
uint64_t Sapphire : : Entity : : Player : : getLastDBWrite ( ) const
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
return m_lastDBWrite ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setVoiceId ( uint8_t voiceId )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_voice = voiceId ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setGc ( uint8_t gc )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_gc = gc ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onGcUpdate ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setGcRankAt ( uint8_t index , uint8_t rank )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_gcRank [ index ] = rank ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onGcUpdate ( * this ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
const Sapphire : : Entity : : Player : : StateFlags & Sapphire : : Entity : : Player : : getStateFlags ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_stateFlags ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : hasStateFlag ( Common : : PlayerStateFlag flag ) const
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto iFlag = static_cast < int32_t > ( flag ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( iFlag , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
return ( m_stateFlags [ index ] & value ) ! = 0 ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setStateFlag ( Common : : PlayerStateFlag flag )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
auto prevOnlineStatus = getOnlineStatus ( ) ;
2021-11-27 00:53:57 +01:00
auto iFlag = static_cast < int32_t > ( flag ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( iFlag , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_stateFlags [ index ] | = value ;
2018-01-11 14:59:39 +01:00
2018-08-29 21:40:59 +02:00
auto newOnlineStatus = getOnlineStatus ( ) ;
2021-11-27 00:53:57 +01:00
sendStateFlags ( prevOnlineStatus ! = newOnlineStatus ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setStateFlags ( std : : vector < Common : : PlayerStateFlag > flags )
2017-08-13 18:28:05 +02:00
{
2018-08-29 21:40:59 +02:00
for ( const auto & flag : flags )
{
setStateFlag ( flag ) ;
}
2017-08-13 18:28:05 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : sendStateFlags ( bool updateInRange )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onSendStateFlags ( * this , updateInRange ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : unsetStateFlag ( Common : : PlayerStateFlag flag )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
if ( ! hasStateFlag ( flag ) )
return ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
auto prevOnlineStatus = getOnlineStatus ( ) ;
2018-01-11 14:59:39 +01:00
2021-11-27 00:53:57 +01:00
auto iFlag = static_cast < int32_t > ( flag ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( iFlag , value , index ) ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_stateFlags [ index ] ^ = value ;
2021-11-27 00:53:57 +01:00
2018-08-29 21:40:59 +02:00
auto newOnlineStatus = getOnlineStatus ( ) ;
2021-11-27 00:53:57 +01:00
sendStateFlags ( prevOnlineStatus ! = newOnlineStatus ) ;
2017-08-08 13:53:47 +02:00
}
2019-04-04 23:29:52 +02:00
void Sapphire : : Entity : : Player : : update ( uint64_t tickCount )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
// a zoning is pending, lets do it
2019-04-04 23:29:52 +02:00
if ( m_queuedZoneing & & ( tickCount - m_queuedZoneing - > m_queueTime ) > 800 )
2018-08-29 21:40:59 +02:00
{
Common : : FFXIVARR_POSITION3 targetPos = m_queuedZoneing - > m_targetPosition ;
2022-01-10 23:50:44 +01:00
if ( getTerritoryTypeId ( ) ! = m_queuedZoneing - > m_targetZone )
2018-08-29 21:40:59 +02:00
{
performZoning ( m_queuedZoneing - > m_targetZone , targetPos , m_queuedZoneing - > m_targetRotation ) ;
}
else
{
setPos ( targetPos ) ;
2021-11-27 00:53:57 +01:00
sendToInRangeSet ( makeWarp ( * this , WARP_TYPE_TELEPO , targetPos , m_queuedZoneing - > m_targetRotation ) , true ) ;
2018-08-29 21:40:59 +02:00
}
m_queuedZoneing . reset ( ) ;
return ;
}
if ( m_hp < = 0 & & m_status ! = ActorStatus : : Dead )
die ( ) ;
if ( ! isAlive ( ) )
return ;
2019-04-04 23:29:52 +02:00
m_lastUpdate = tickCount ;
2018-08-29 21:40:59 +02:00
if ( ! checkAction ( ) )
{
if ( m_targetId & & m_currentStance = = Common : : Stance : : Active & & isAutoattackOn ( ) )
{
auto mainWeap = getItemAt ( Common : : GearSet0 , Common : : GearSetSlot : : MainHand ) ;
// @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need...
2021-11-27 00:53:57 +01:00
for ( const auto & actor : m_inRangeActor )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
if ( actor - > getId ( ) = = m_targetId & & actor - > getAsChara ( ) - > isAlive ( ) & & mainWeap )
{
2019-04-25 22:45:23 +10:00
auto chara = actor - > getAsChara ( ) ;
2018-08-29 21:40:59 +02:00
// default autoattack range
2022-01-04 22:51:29 +01:00
float range = 3.f + chara - > getRadius ( ) + getRadius ( ) * 0.5f ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
// default autoattack range for ranged classes
2021-11-27 00:53:57 +01:00
if ( getClass ( ) = = ClassJob : : Machinist | | getClass ( ) = = ClassJob : : Bard | | getClass ( ) = = ClassJob : : Archer )
2022-01-04 22:51:29 +01:00
range = 25.f + chara - > getRadius ( ) + getRadius ( ) * 0.5f ;
2017-08-08 13:53:47 +02:00
2021-11-27 00:53:57 +01:00
if ( Util : : distance ( getPos ( ) , actor - > getPos ( ) ) < = range )
2018-08-29 21:40:59 +02:00
{
2019-04-04 23:29:52 +02:00
if ( ( tickCount - m_lastAttack ) > mainWeap - > getDelay ( ) )
2017-08-08 13:53:47 +02:00
{
2019-04-04 23:29:52 +02:00
m_lastAttack = tickCount ;
2018-08-29 21:40:59 +02:00
autoAttack ( actor - > getAsChara ( ) ) ;
2017-08-08 13:53:47 +02:00
}
2018-08-29 21:40:59 +02:00
}
}
2017-08-08 13:53:47 +02:00
}
2018-08-29 21:40:59 +02:00
}
}
2017-08-08 13:53:47 +02:00
2019-04-04 23:29:52 +02:00
Chara : : update ( tickCount ) ;
2017-08-08 13:53:47 +02:00
}
2022-01-02 22:32:17 +01:00
void Sapphire : : Entity : : Player : : setLastAttack ( uint64_t tickCount )
{
m_lastAttack = tickCount ;
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : freePlayerSpawnId ( uint32_t actorId )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
auto spawnId = m_actorSpawnIndexAllocator . freeUsedSpawnIndex ( actorId ) ;
2017-08-08 13:53:47 +02:00
2019-01-28 20:29:30 +11:00
// actor was never spawned for this player
if ( spawnId = = m_actorSpawnIndexAllocator . getAllocFailId ( ) )
return ;
2019-07-29 22:22:45 +10:00
auto freeActorSpawnPacket = makeZonePacket < FFXIVIpcActorFreeSpawn > ( getId ( ) ) ;
2018-08-29 21:40:59 +02:00
freeActorSpawnPacket - > data ( ) . actorId = actorId ;
freeActorSpawnPacket - > data ( ) . spawnId = spawnId ;
queuePacket ( freeActorSpawnPacket ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : AetheryteList & Sapphire : : Entity : : Player : : getAetheryteArray ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_aetheryte ;
2017-08-08 13:53:47 +02:00
}
/*! set homepoint */
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setHomepoint ( uint8_t aetheryteId )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_homePoint = aetheryteId ;
2017-08-08 13:53:47 +02:00
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , SetHomepoint , aetheryteId ) ) ;
2017-08-08 13:53:47 +02:00
}
/*! get homepoint */
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getHomepoint ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_homePoint ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : ClassList & Sapphire : : Entity : : Player : : getClassArray ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_classArray ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : ExpList & Sapphire : : Entity : : Player : : getExpArray ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_expArray ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : HowToList & Sapphire : : Entity : : Player : : getHowToArray ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_howTo ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
const Sapphire : : Entity : : Player : : UnlockList & Sapphire : : Entity : : Player : : getUnlockBitmask ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_unlocks ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
const Sapphire : : Entity : : Player : : OrchestrionList & Sapphire : : Entity : : Player : : getOrchestrionBitmask ( ) const
2017-10-09 20:09:49 +02:00
{
2018-08-29 21:40:59 +02:00
return m_orchestrion ;
2017-10-09 20:09:49 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : MountList & Sapphire : : Entity : : Player : : getMountGuideBitmask ( )
2017-12-03 17:24:11 +01:00
{
2018-08-29 21:40:59 +02:00
return m_mountGuide ;
2017-12-03 17:24:11 +01:00
}
2021-11-27 00:53:57 +01:00
uint64_t Sapphire : : Entity : : Player : : getCharacterId ( ) const
2021-08-30 10:16:05 +02:00
{
2021-11-27 00:53:57 +01:00
return m_characterId ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getVoiceId ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_voice ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getGc ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_gc ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
const std : : array < uint8_t , 3 > & Sapphire : : Entity : : Player : : getGcRankArray ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_gcRank ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : queuePacket ( Network : : Packets : : FFXIVPacketBasePtr pPacket )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
auto & server = Common : : Service < World : : WorldServer > : : ref ( ) ;
2017-11-28 00:09:36 +01:00
2021-11-27 00:53:57 +01:00
server . queueForPlayer ( getCharacterId ( ) , std : : move ( pPacket ) ) ;
2017-08-22 23:53:20 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isLoadingComplete ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_bLoadingComplete ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setLoadingComplete ( bool bComplete )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_bLoadingComplete = bComplete ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : performZoning ( uint16_t zoneId , const Common : : FFXIVARR_POSITION3 & pos , float rotation )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_pos = pos ;
2018-12-02 15:32:22 +11:00
m_territoryTypeId = zoneId ;
2018-08-29 21:40:59 +02:00
m_bMarkedForZoning = true ;
setRot ( rotation ) ;
setZone ( zoneId ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isMarkedForZoning ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_bMarkedForZoning ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
ZoneingType Sapphire : : Entity : : Player : : getZoningType ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_zoningType ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setZoningType ( Common : : ZoneingType zoneingType )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_zoningType = zoneingType ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setSearchInfo ( uint8_t selectRegion , uint8_t selectClass , const char * searchMessage )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_searchSelectRegion = selectRegion ;
m_searchSelectClass = selectClass ;
memset ( & m_searchMessage [ 0 ] , 0 , sizeof ( searchMessage ) ) ;
strcpy ( & m_searchMessage [ 0 ] , searchMessage ) ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
const char * Sapphire : : Entity : : Player : : getSearchMessage ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return & m_searchMessage [ 0 ] ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getSearchSelectRegion ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_searchSelectRegion ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getSearchSelectClass ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_searchSelectClass ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : updateHowtosSeen ( uint32_t howToId )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
uint8_t index = howToId / 8 ;
uint8_t bitIndex = howToId % 8 ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
uint8_t value = 1 < < bitIndex ;
2017-08-08 13:53:47 +02:00
2018-08-29 21:40:59 +02:00
m_howTo [ index ] | = value ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : initHateSlotQueue ( )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_freeHateSlotQueue = std : : queue < uint8_t > ( ) ;
2021-11-27 00:53:57 +01:00
for ( int32_t i = 1 ; i < 26 ; + + i )
2018-08-29 21:40:59 +02:00
m_freeHateSlotQueue . push ( i ) ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : hateListAdd ( const BNpc & bnpc )
2019-01-19 22:56:07 +01:00
{
if ( ! m_freeHateSlotQueue . empty ( ) )
{
uint8_t hateId = m_freeHateSlotQueue . front ( ) ;
m_freeHateSlotQueue . pop ( ) ;
2021-11-27 00:53:57 +01:00
m_actorIdTohateSlotMap [ bnpc . getId ( ) ] = hateId ;
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onHateListChanged ( * this ) ;
2019-01-19 22:56:07 +01:00
}
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : hateListRemove ( const BNpc & bnpc )
2019-01-19 22:56:07 +01:00
{
auto it = m_actorIdTohateSlotMap . begin ( ) ;
for ( ; it ! = m_actorIdTohateSlotMap . end ( ) ; + + it )
{
2021-11-27 00:53:57 +01:00
if ( it - > first = = bnpc . getId ( ) )
2019-01-19 22:56:07 +01:00
{
uint8_t hateSlot = it - > second ;
m_freeHateSlotQueue . push ( hateSlot ) ;
m_actorIdTohateSlotMap . erase ( it ) ;
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onHateListChanged ( * this ) ;
2019-01-19 22:56:07 +01:00
return ;
}
}
}
2021-11-27 00:53:57 +01:00
bool Sapphire : : Entity : : Player : : hateListHasEntry ( const BNpc & bnpc )
2019-01-19 22:56:07 +01:00
{
2021-11-27 00:53:57 +01:00
return std : : any_of ( m_actorIdTohateSlotMap . begin ( ) , m_actorIdTohateSlotMap . end ( ) ,
[ bnpc ] ( const auto & entry ) { return entry . first = = bnpc . getId ( ) ; } ) ;
2019-01-19 22:56:07 +01:00
}
2021-11-27 00:53:57 +01:00
const std : : map < uint32_t , uint8_t > & Sapphire : : Entity : : Player : : getActorIdToHateSlotMap ( )
2017-08-08 13:53:47 +02:00
{
2021-11-27 00:53:57 +01:00
return m_actorIdTohateSlotMap ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : onMobAggro ( const BNpc & bnpc )
2019-01-19 22:56:07 +01:00
{
2021-11-27 00:53:57 +01:00
hateListAdd ( bnpc ) ;
queuePacket ( makeActorControl ( getId ( ) , SetBattle , 1 ) ) ;
2019-01-19 22:56:07 +01:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : onMobDeaggro ( const BNpc & bnpc )
2019-01-19 22:56:07 +01:00
{
2021-11-27 00:53:57 +01:00
hateListRemove ( bnpc ) ;
2019-01-19 22:56:07 +01:00
if ( m_actorIdTohateSlotMap . empty ( ) )
2021-11-27 00:53:57 +01:00
queuePacket ( makeActorControl ( getId ( ) , SetBattle ) ) ;
2019-01-19 22:56:07 +01:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isLogin ( ) const
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
return m_bIsLogin ;
2017-08-08 13:53:47 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setIsLogin ( bool bIsLogin )
2017-08-08 13:53:47 +02:00
{
2018-08-29 21:40:59 +02:00
m_bIsLogin = bIsLogin ;
2017-08-08 13:53:47 +02:00
}
2021-11-27 00:53:57 +01:00
Sapphire : : Entity : : Player : : TitleList & Sapphire : : Entity : : Player : : getTitleList ( )
2018-02-17 01:20:40 +01:00
{
2018-08-29 21:40:59 +02:00
return m_titleList ;
2018-02-17 01:20:40 +01:00
}
2018-11-29 16:55:48 +01:00
uint16_t Sapphire : : Entity : : Player : : getTitle ( ) const
2017-10-09 02:06:31 -03:00
{
2018-08-29 21:40:59 +02:00
return m_activeTitle ;
2017-10-09 02:06:31 -03:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : addTitle ( uint16_t titleId )
2017-10-09 00:31:31 -03:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( titleId , value , index ) ;
2017-10-09 00:31:31 -03:00
2018-08-29 21:40:59 +02:00
m_titleList [ index ] | = value ;
2017-10-09 00:31:31 -03:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setTitle ( uint16_t titleId )
2017-10-04 23:19:38 -03:00
{
2018-08-29 21:40:59 +02:00
uint16_t index ;
uint8_t value ;
Util : : valueToFlagByteIndexValue ( titleId , value , index ) ;
2017-10-09 01:56:47 -03:00
2018-08-29 21:40:59 +02:00
if ( ( m_titleList [ index ] & value ) = = 0 ) // Player doesn't have title - bail
return ;
2017-10-09 01:56:47 -03:00
2018-08-29 21:40:59 +02:00
m_activeTitle = titleId ;
2017-10-09 01:56:47 -03:00
2019-10-09 18:42:25 +02:00
sendToInRangeSet ( makeActorControl ( getId ( ) , SetTitle , titleId ) , true ) ;
2017-10-04 23:19:38 -03:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setEquipDisplayFlags ( uint16_t state )
2017-10-05 20:24:58 +02:00
{
2021-11-27 00:53:57 +01:00
m_equipDisplayFlags = static_cast < uint8_t > ( state ) ;
2017-10-05 20:24:58 +02:00
}
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getEquipDisplayFlags ( ) const
2017-10-05 20:24:58 +02:00
{
2018-08-29 21:40:59 +02:00
return m_equipDisplayFlags ;
2017-10-05 20:24:58 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setMount ( uint32_t mountId )
2017-10-17 21:31:00 +02:00
{
2021-11-27 00:53:57 +01:00
m_mount = mountId ;
2017-10-18 17:54:17 +02:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onMountUpdate ( * this , m_mount ) ;
2017-10-18 17:54:17 +02:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setCompanion ( uint16_t id )
2019-02-02 23:06:57 +11:00
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2019-02-02 23:06:57 +11:00
2021-11-27 00:53:57 +01:00
auto companion = exdData . getRow < Component : : Excel : : Companion > ( id ) ;
if ( ! id )
return ;
2019-02-02 23:06:57 +11:00
m_companionId = id ;
2019-10-09 18:42:25 +02:00
sendToInRangeSet ( makeActorControl ( getId ( ) , ActorControlType : : ToggleCompanion , id ) , true ) ;
2019-02-02 23:06:57 +11:00
}
uint16_t Sapphire : : Entity : : Player : : getCurrentCompanion ( ) const
{
return m_companionId ;
}
2021-11-27 00:53:57 +01:00
uint8_t Sapphire : : Entity : : Player : : getCurrentMount ( ) const
2017-10-18 17:54:17 +02:00
{
2018-08-29 21:40:59 +02:00
return m_mount ;
2017-10-17 21:31:00 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setPersistentEmote ( uint32_t emoteId )
2018-07-09 01:19:42 -03:00
{
2018-08-29 21:40:59 +02:00
m_emoteMode = emoteId ;
2018-07-09 01:19:42 -03:00
}
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getPersistentEmote ( ) const
2018-07-09 01:19:42 -03:00
{
2018-08-29 21:40:59 +02:00
return m_emoteMode ;
2018-07-09 01:19:42 -03:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : autoAttack ( CharaPtr pTarget )
2017-08-08 13:53:47 +02:00
{
2022-01-10 23:50:44 +01:00
auto teriMgr = Common : : Service < World : : Manager : : TerritoryMgr > : : ref ( ) ;
auto pZone = teriMgr . getTerritoryByGuId ( getTerritoryId ( ) ) ;
2018-08-29 21:40:59 +02:00
auto mainWeap = getItemAt ( Common : : GearSet0 , Common : : GearSetSlot : : MainHand ) ;
2017-08-11 00:58:35 -03:00
2019-01-20 13:36:34 +01:00
pTarget - > onActionHostile ( getAsChara ( ) ) ;
2018-08-29 21:40:59 +02:00
//uint64_t tick = Util::getTimeMs();
//srand(static_cast< uint32_t >(tick));
2017-08-11 00:58:35 -03:00
2020-03-01 01:00:57 +11:00
auto & RNGMgr = Common : : Service < World : : Manager : : RNGMgr > : : ref ( ) ;
auto variation = static_cast < uint32_t > ( RNGMgr . getRandGenerator < float > ( 0 , 3 ) . next ( ) ) ;
2019-04-17 22:36:23 +02:00
2019-07-27 00:37:40 +10:00
auto damage = Math : : CalcStats : : calcAutoAttackDamage ( * this ) ;
2017-08-08 13:53:47 +02:00
2022-01-06 20:25:50 +01:00
auto effectPacket = std : : make_shared < EffectPacket1 > ( getId ( ) , pTarget - > getId ( ) , 7 ) ;
2018-07-21 23:32:10 +10:00
2021-11-27 00:53:57 +01:00
Common : : CalcResultParam entry { } ;
2018-07-21 23:32:10 +10:00
2021-11-27 00:53:57 +01:00
entry . Value = static_cast < int16_t > ( damage . first ) ;
entry . Type = Common : : ActionEffectType : : CALC_RESULT_TYPE_DAMAGE_HP ;
2022-01-06 20:25:50 +01:00
entry . Arg0 = 2 ;
entry . Arg1 = 7 ;
entry . Flag = 128 ;
2018-07-21 23:32:10 +10:00
2021-11-27 00:53:57 +01:00
if ( getClass ( ) = = ClassJob : : Machinist | | getClass ( ) = = ClassJob : : Bard | | getClass ( ) = = ClassJob : : Archer )
{
2022-01-06 20:25:50 +01:00
// effectPacket->setAnimationId( 8 );
2022-01-02 22:32:17 +01:00
//entry.Arg2 = 0x72;
2018-08-29 21:40:59 +02:00
}
else
{
2022-01-06 20:25:50 +01:00
//effectPacket->setAnimationId( 7 );
2022-01-02 22:32:17 +01:00
//entry.Arg2 = 0x73;
2018-08-29 21:40:59 +02:00
}
2017-08-08 13:53:47 +02:00
2022-01-10 23:50:44 +01:00
effectPacket - > setSequence ( pZone - > getNextEffectSequence ( ) ) ;
2022-01-06 20:25:50 +01:00
2021-11-27 00:53:57 +01:00
effectPacket - > setRotation ( Util : : floatToUInt16Rot ( getRot ( ) ) ) ;
2022-01-07 21:22:55 +01:00
effectPacket - > addTargetEffect ( entry ) ;
2017-08-19 11:28:04 +09:00
2021-11-27 00:53:57 +01:00
sendToInRangeSet ( effectPacket , true ) ;
pTarget - > takeDamage ( static_cast < uint32_t > ( damage . first ) ) ;
2017-08-08 13:53:47 +02:00
}
2017-08-14 17:10:19 +02:00
2017-08-19 11:28:04 +09:00
/////////////////////////////
// Content Finder
/////////////////////////////
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getCFPenaltyTimestamp ( ) const
2017-08-19 11:28:04 +09:00
{
2018-08-29 21:40:59 +02:00
return m_cfPenaltyUntil ;
2017-08-19 11:28:04 +09:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setCFPenaltyTimestamp ( uint32_t timestamp )
2017-08-19 11:28:04 +09:00
{
2018-08-29 21:40:59 +02:00
m_cfPenaltyUntil = timestamp ;
2017-08-19 11:28:04 +09:00
}
2018-11-29 16:55:48 +01:00
uint32_t Sapphire : : Entity : : Player : : getCFPenaltyMinutes ( ) const
2017-08-19 11:28:04 +09:00
{
2019-06-02 00:34:22 +10:00
auto currentTimestamp = Common : : Util : : getTimeSeconds ( ) ;
2018-08-29 21:40:59 +02:00
auto endTimestamp = getCFPenaltyTimestamp ( ) ;
2017-08-19 11:28:04 +09:00
2018-08-29 21:40:59 +02:00
// check if penalty timestamp already passed current time
if ( currentTimestamp > endTimestamp )
return 0 ;
2017-08-19 11:28:04 +09:00
2018-08-29 21:40:59 +02:00
auto deltaTime = endTimestamp - currentTimestamp ;
2021-11-27 00:53:57 +01:00
return static_cast < uint32_t > ( std : : ceil ( static_cast < float > ( deltaTime ) / 60 ) ) ;
2017-08-19 11:28:04 +09:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setCFPenaltyMinutes ( uint32_t minutes )
2017-08-19 11:28:04 +09:00
{
2019-06-02 00:34:22 +10:00
auto currentTimestamp = Common : : Util : : getTimeSeconds ( ) ;
2019-01-19 21:20:23 -02:00
setCFPenaltyTimestamp ( currentTimestamp + minutes * 60 ) ;
2017-08-19 11:28:04 +09:00
}
2017-08-20 02:24:19 +02:00
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getOpeningSequence ( ) const
2017-08-20 02:24:19 +02:00
{
2018-08-29 21:40:59 +02:00
return m_openingSequence ;
2017-08-20 02:24:19 +02:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setOpeningSequence ( uint8_t seq )
2017-08-20 02:24:19 +02:00
{
2018-08-29 21:40:59 +02:00
m_openingSequence = seq ;
2017-09-22 22:03:57 +09:00
}
2018-11-29 16:55:48 +01:00
uint16_t Sapphire : : Entity : : Player : : getItemLevel ( ) const
2017-11-16 00:03:36 -02:00
{
2018-08-29 21:40:59 +02:00
return m_itemLevel ;
2017-11-16 00:03:36 -02:00
}
2017-09-22 22:03:57 +09:00
/// Tells client to offset their eorzean time by given timestamp.
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setEorzeaTimeOffset ( uint64_t timestamp )
2017-09-22 22:03:57 +09:00
{
2018-08-29 21:40:59 +02:00
// TODO: maybe change to persistent?
2019-07-29 22:22:45 +10:00
auto packet = makeZonePacket < FFXIVIpcEorzeaTimeOffset > ( getId ( ) ) ;
2018-08-29 21:40:59 +02:00
packet - > data ( ) . timestamp = timestamp ;
2017-09-22 22:03:57 +09:00
2018-08-29 21:40:59 +02:00
// Send to single player
queuePacket ( packet ) ;
2017-09-22 22:03:57 +09:00
}
2018-01-28 22:36:43 +01:00
2021-11-27 00:53:57 +01:00
uint32_t Sapphire : : Entity : : Player : : getPrevTerritoryTypeId ( ) const
2018-01-28 22:36:43 +01:00
{
2021-11-27 00:53:57 +01:00
return m_prevTerritoryTypeId ;
}
2018-02-23 23:47:21 +01:00
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : sendZonePackets ( )
{
2022-01-10 23:50:44 +01:00
auto teriMgr = Common : : Service < World : : Manager : : TerritoryMgr > : : ref ( ) ;
auto pZone = teriMgr . getTerritoryByGuId ( getTerritoryId ( ) ) ;
2021-11-27 00:53:57 +01:00
auto initPacket = makeZonePacket < FFXIVIpcLogin > ( getId ( ) ) ;
initPacket - > data ( ) . playerActorId = getId ( ) ;
2018-08-29 21:40:59 +02:00
queuePacket ( initPacket ) ;
2018-01-28 22:36:43 +01:00
2018-08-29 21:40:59 +02:00
sendInventory ( ) ;
2018-01-28 22:36:43 +01:00
2018-08-29 21:40:59 +02:00
if ( isLogin ( ) )
{
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , SetCharaGearParamUI , m_equipDisplayFlags , 1 ) ) ;
2018-08-29 21:40:59 +02:00
}
2018-01-28 22:36:43 +01:00
2018-08-29 21:40:59 +02:00
// set flags, will be reset automatically by zoning ( only on client side though )
//setStateFlag( PlayerStateFlag::BetweenAreas );
//setStateFlag( PlayerStateFlag::BetweenAreas1 );
2018-01-28 22:36:43 +01:00
2021-11-27 00:53:57 +01:00
if ( isActionLearned ( Common : : UnlockEntry : : HuntingLog ) )
sendHuntingLog ( ) ;
2018-08-29 21:40:59 +02:00
sendStats ( ) ;
2018-01-28 22:36:43 +01:00
2018-08-29 21:40:59 +02:00
// only initialize the UI if the player in fact just logged in.
if ( isLogin ( ) )
{
2021-11-27 00:53:57 +01:00
auto contentFinderList = makeZonePacket < FFXIVIpcContentAttainFlags > ( getId ( ) ) ;
std : : memset ( & contentFinderList - > data ( ) , 0xFF , sizeof ( contentFinderList - > data ( ) ) ) ;
2021-08-16 17:18:29 +09:00
2018-08-29 21:40:59 +02:00
queuePacket ( contentFinderList ) ;
2018-01-28 22:36:43 +01:00
2021-11-27 00:53:57 +01:00
auto statusPacket = makePlayerSetup ( * this ) ;
queuePacket ( statusPacket ) ;
2018-01-28 22:36:43 +01:00
2021-11-27 00:53:57 +01:00
Service < World : : Manager : : PlayerMgr > : : ref ( ) . onPlayerStatusUpdate ( * this ) ;
2018-01-28 22:36:43 +01:00
2018-08-29 21:40:59 +02:00
sendItemLevel ( ) ;
2021-11-27 00:53:57 +01:00
clearSoldItems ( ) ;
2018-08-29 21:40:59 +02:00
}
2018-01-28 22:36:43 +01:00
2020-03-01 01:00:57 +11:00
auto & housingMgr = Common : : Service < HousingMgr > : : ref ( ) ;
2021-11-27 00:53:57 +01:00
if ( Sapphire : : LandPtr pLand = housingMgr . getLandByOwnerId ( getCharacterId ( ) ) )
2018-11-10 19:00:13 +01:00
{
2018-11-27 22:18:50 +11:00
uint32_t state = 0 ;
if ( pLand - > getHouse ( ) )
{
2021-11-27 00:53:57 +01:00
state | = LandFlags : : CHARA_HOUSING_LAND_DATA_FLAG_HOUSE ;
2018-11-27 22:18:50 +11:00
// todo: remove this, debug for now
2021-11-27 00:53:57 +01:00
state | = LandFlags : : CHARA_HOUSING_LAND_DATA_FLAG_AETHERYTE ;
2018-11-27 22:18:50 +11:00
}
2018-12-21 22:23:49 +11:00
setLandFlags ( LandFlagsSlot : : Private , state , pLand - > getLandIdent ( ) ) ;
2018-11-10 19:00:13 +01:00
}
2018-11-27 23:12:26 +11:00
sendLandFlags ( ) ;
2018-11-06 23:25:37 +01:00
2022-01-10 23:50:44 +01:00
queuePacket ( makeInitZone ( * this , * pZone ) ) ;
2018-01-28 22:36:43 +01:00
2022-01-10 23:50:44 +01:00
pZone - > onPlayerZoneIn ( * this ) ;
2018-11-06 13:05:39 +01:00
2018-08-29 21:40:59 +02:00
if ( isLogin ( ) )
{
2021-11-27 00:53:57 +01:00
queuePacket ( makeZonePacket < FFXIVIpcQuestRepeatFlags > ( getId ( ) ) ) ;
queuePacket ( makeZonePacket < FFXIVIpcDailyQuests > ( getId ( ) ) ) ;
2018-08-29 21:40:59 +02:00
}
2018-01-28 22:36:43 +01:00
2021-11-27 00:53:57 +01:00
if ( getPartyId ( ) ! = 0 )
{
auto & partyMgr = Common : : Service < World : : Manager : : PartyMgr > : : ref ( ) ;
partyMgr . onMoveZone ( * this ) ;
}
2018-01-28 22:36:43 +01:00
2018-08-29 21:40:59 +02:00
m_bMarkedForZoning = false ;
2018-01-28 22:36:43 +01:00
}
2018-02-07 00:00:48 +01:00
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setDirectorInitialized ( bool isInitialized )
2018-02-07 00:00:48 +01:00
{
2018-08-29 21:40:59 +02:00
m_directorInitialized = isInitialized ;
2018-02-07 00:00:48 +01:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isDirectorInitialized ( ) const
2018-02-07 00:00:48 +01:00
{
2018-08-29 21:40:59 +02:00
return m_directorInitialized ;
2018-02-07 00:00:48 +01:00
}
2018-02-17 01:20:40 +01:00
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : sendTitleList ( )
2018-02-17 01:20:40 +01:00
{
2021-11-27 00:53:57 +01:00
auto titleListPacket = makeZonePacket < FFXIVIpcTitleList > ( getId ( ) ) ;
memcpy ( titleListPacket - > data ( ) . TitleFlagsArray , getTitleList ( ) . data ( ) , sizeof ( titleListPacket - > data ( ) . TitleFlagsArray ) ) ;
2018-02-17 01:20:40 +01:00
2018-08-29 21:40:59 +02:00
queuePacket ( titleListPacket ) ;
2018-02-17 01:20:40 +01:00
}
2018-02-18 01:50:20 +01:00
2018-08-29 21:40:59 +02:00
void
2018-11-29 16:55:48 +01:00
Sapphire : : Entity : : Player : : sendZoneInPackets ( uint32_t param1 , uint32_t param2 = 0 , uint32_t param3 = 0 , uint32_t param4 = 0 ,
2019-01-09 23:57:49 +01:00
bool shouldSetStatus = false )
2018-04-19 22:22:20 +10:00
{
2021-11-27 00:53:57 +01:00
auto zoneInPacket = makeActorControlSelf ( getId ( ) , Appear , param1 , param2 , param3 , param4 ) ;
2019-10-09 18:42:25 +02:00
auto SetStatusPacket = makeActorControl ( getId ( ) , SetStatus , static_cast < uint8_t > ( Common : : ActorStatus : : Idle ) ) ;
2018-04-19 22:22:20 +10:00
2018-08-29 21:40:59 +02:00
if ( ! getGmInvis ( ) )
2019-01-09 23:57:49 +01:00
sendToInRangeSet ( zoneInPacket ) ;
2018-08-29 21:40:59 +02:00
if ( shouldSetStatus )
2019-01-09 23:57:49 +01:00
sendToInRangeSet ( SetStatusPacket , true ) ;
queuePacket ( zoneInPacket ) ;
2018-04-19 22:22:20 +10:00
2018-08-29 21:40:59 +02:00
setZoningType ( Common : : ZoneingType : : None ) ;
unsetStateFlag ( PlayerStateFlag : : BetweenAreas ) ;
2018-04-19 22:22:20 +10:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : finishZoning ( )
2018-02-18 01:50:20 +01:00
{
2018-08-29 21:40:59 +02:00
switch ( getZoningType ( ) )
{
case ZoneingType : : None :
sendZoneInPackets ( 0x01 ) ;
break ;
2018-02-18 01:50:20 +01:00
2018-08-29 21:40:59 +02:00
case ZoneingType : : Teleport :
sendZoneInPackets ( 0x01 , 0 , 0 , 110 ) ;
break ;
2018-02-18 01:50:20 +01:00
2018-08-29 21:40:59 +02:00
case ZoneingType : : Return :
case ZoneingType : : ReturnDead :
{
if ( getStatus ( ) = = Common : : ActorStatus : : Dead )
2018-02-18 01:50:20 +01:00
{
2018-08-29 21:40:59 +02:00
resetHp ( ) ;
resetMp ( ) ;
setStatus ( Common : : ActorStatus : : Idle ) ;
sendZoneInPackets ( 0x01 , 0x01 , 0 , 111 , true ) ;
2018-02-18 01:50:20 +01:00
}
2018-08-29 21:40:59 +02:00
else
sendZoneInPackets ( 0x01 , 0x00 , 0 , 111 ) ;
}
2018-04-19 22:22:20 +10:00
break ;
2018-02-18 01:50:20 +01:00
2018-08-29 21:40:59 +02:00
case ZoneingType : : FadeIn :
break ;
}
2018-02-18 01:50:20 +01:00
}
2019-02-08 21:20:53 +11:00
void Sapphire : : Entity : : Player : : teleportQuery ( uint16_t aetheryteId )
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2019-03-07 21:58:12 +11:00
// TODO: only register this action if enough gil is in possession
2021-11-27 00:53:57 +01:00
auto targetAetheryte = exdData . getRow < Component : : Excel : : Aetheryte > ( aetheryteId ) ;
2019-03-07 21:58:12 +11:00
if ( targetAetheryte )
{
2021-11-27 00:53:57 +01:00
auto fromAetheryte = exdData . getRow < Component : : Excel : : Aetheryte > (
exdData . getRow < Component : : Excel : : TerritoryType > ( getZoneId ( ) ) - > data ( ) . Aetheryte ) ;
2019-03-07 21:58:12 +11:00
// calculate cost - does not apply for favorite points or homepoints neither checks for aether tickets
auto cost = static_cast < uint16_t > (
2021-11-27 00:53:57 +01:00
( std : : sqrt ( std : : pow ( fromAetheryte - > data ( ) . CostPosX - targetAetheryte - > data ( ) . CostPosX , 2 ) +
std : : pow ( fromAetheryte - > data ( ) . CostPosY - targetAetheryte - > data ( ) . CostPosY , 2 ) ) / 2 ) + 100 ) ;
2019-03-07 21:58:12 +11:00
// cap at 999 gil
cost = std : : min < uint16_t > ( 999 , cost ) ;
bool insufficientGil = getCurrency ( Common : : CurrencyType : : Gil ) < cost ;
// TODO: figure out what param1 really does
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , TeleportStart , insufficientGil ? 2 : 0 , aetheryteId ) ) ;
2019-03-07 21:58:12 +11:00
if ( ! insufficientGil )
{
m_teleportQuery . targetAetheryte = aetheryteId ;
m_teleportQuery . cost = cost ;
}
else
{
clearTeleportQuery ( ) ;
}
}
}
Sapphire : : Common : : PlayerTeleportQuery Sapphire : : Entity : : Player : : getTeleportQuery ( ) const
{
return m_teleportQuery ;
}
void Sapphire : : Entity : : Player : : clearTeleportQuery ( )
{
memset ( & m_teleportQuery , 0x0 , sizeof ( Common : : PlayerTeleportQuery ) ) ;
2018-02-18 01:50:20 +01:00
}
2018-02-23 23:47:21 +01:00
2018-11-29 16:55:48 +01:00
uint8_t Sapphire : : Entity : : Player : : getNextObjSpawnIndexForActorId ( uint32_t actorId )
2018-02-23 23:47:21 +01:00
{
2019-01-28 21:02:51 +11:00
auto index = m_objSpawnIndexAllocator . getNextFreeSpawnIndex ( actorId ) ;
if ( index = = m_objSpawnIndexAllocator . getAllocFailId ( ) )
{
Logger : : warn ( " Failed to spawn EObj#{0} for Player#{1} - no remaining spawn indexes available. "
" Consider lowering InRangeDistance in world config. " ,
actorId , getId ( ) ) ;
2021-11-27 00:53:57 +01:00
PlayerMgr : : sendUrgent ( * this , " Failed to spawn EObj#{0} for you - no remaining spawn slots. See world log. " , actorId ) ;
2019-01-28 21:02:51 +11:00
return index ;
}
return index ;
2018-02-28 23:53:20 +11:00
}
2020-03-16 01:35:49 -07:00
void Sapphire : : Entity : : Player : : setDyeingInfo ( uint32_t itemToDyeContainer , uint32_t itemToDyeSlot , uint32_t dyeBagContainer , uint32_t dyeBagSlot )
2020-03-14 00:31:14 -07:00
{
m_dyeingInfo . itemToDyeContainer = itemToDyeContainer ;
m_dyeingInfo . itemToDyeSlot = itemToDyeSlot ;
m_dyeingInfo . dyeBagContainer = dyeBagContainer ;
m_dyeingInfo . dyeBagSlot = dyeBagSlot ;
}
void Sapphire : : Entity : : Player : : dyeItemFromDyeingInfo ( )
{
uint32_t itemToDyeContainer = m_dyeingInfo . itemToDyeContainer ;
uint32_t itemToDyeSlot = m_dyeingInfo . itemToDyeSlot ;
uint32_t dyeBagContainer = m_dyeingInfo . dyeBagContainer ;
uint32_t dyeBagSlot = m_dyeingInfo . dyeBagSlot ;
sendStateFlags ( ) ; // Retail sends all 0s to unlock player after a dye? Possibly not setting a flag when the action is started in the backend..?
2020-03-16 01:35:49 -07:00
auto itemToDye = getItemAt ( itemToDyeContainer , itemToDyeSlot ) ;
auto dyeToUse = getItemAt ( dyeBagContainer , dyeBagSlot ) ;
2020-03-14 00:31:14 -07:00
2021-11-27 00:53:57 +01:00
if ( ! itemToDye | | ! dyeToUse )
return ;
2020-03-14 00:31:14 -07:00
uint32_t stainColorID = dyeToUse - > getAdditionalData ( ) ;
2020-03-16 01:35:49 -07:00
itemToDye - > setStain ( stainColorID ) ;
2020-03-14 00:31:14 -07:00
2020-03-16 01:35:49 -07:00
// TODO: subtract/remove dye used
2020-03-14 00:31:14 -07:00
2020-03-16 02:08:35 -07:00
insertInventoryItem ( static_cast < Sapphire : : Common : : InventoryType > ( itemToDyeContainer ) , static_cast < uint16_t > ( itemToDyeSlot ) , itemToDye ) ;
2021-11-27 00:53:57 +01:00
writeItem ( itemToDye ) ;
2020-03-14 00:31:14 -07:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : resetObjSpawnIndex ( )
2018-02-28 23:53:20 +11:00
{
2018-08-29 21:40:59 +02:00
m_objSpawnIndexAllocator . freeAllSpawnIndexes ( ) ;
2018-02-28 23:53:20 +11:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : freeObjSpawnIndexForActorId ( uint32_t actorId )
2018-02-28 23:53:20 +11:00
{
2018-08-29 21:40:59 +02:00
auto spawnId = m_objSpawnIndexAllocator . freeUsedSpawnIndex ( actorId ) ;
2018-03-01 23:17:35 +01:00
2019-01-28 20:29:30 +11:00
// obj was never spawned for this player
if ( spawnId = = m_objSpawnIndexAllocator . getAllocFailId ( ) )
return ;
2021-11-27 00:53:57 +01:00
auto freeObjectSpawnPacket = makeZonePacket < FFXIVIpcDeleteObject > ( getId ( ) ) ;
freeObjectSpawnPacket - > data ( ) . Index = spawnId ;
2018-08-29 21:40:59 +02:00
queuePacket ( freeObjectSpawnPacket ) ;
2018-03-02 02:55:02 +11:00
}
2018-02-28 23:53:20 +11:00
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isObjSpawnIndexValid ( uint8_t index )
2018-03-02 02:55:02 +11:00
{
2018-08-29 21:40:59 +02:00
return m_objSpawnIndexAllocator . isSpawnIndexValid ( index ) ;
2018-02-24 23:53:32 +01:00
}
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : setOnEnterEventDone ( bool isDone )
2018-02-24 23:53:32 +01:00
{
2018-08-29 21:40:59 +02:00
m_onEnterEventDone = isDone ;
2018-02-24 23:53:32 +01:00
}
2018-11-29 16:55:48 +01:00
bool Sapphire : : Entity : : Player : : isOnEnterEventDone ( ) const
2018-02-24 23:53:32 +01:00
{
2018-08-29 21:40:59 +02:00
return m_onEnterEventDone ;
2018-02-24 23:53:32 +01:00
}
2018-11-07 11:59:59 +01:00
2018-12-21 22:23:49 +11:00
void Sapphire : : Entity : : Player : : setLandFlags ( uint8_t flagSlot , uint32_t landFlags , Common : : LandIdent ident )
2018-11-07 11:59:59 +01:00
{
2021-11-27 00:53:57 +01:00
auto & server = Common : : Service < World : : WorldServer > : : ref ( ) ;
m_charaLandData [ flagSlot ] . landId = ident ;
m_charaLandData [ flagSlot ] . landId . worldId = server . getWorldId ( ) ;
m_charaLandData [ flagSlot ] . flags = landFlags ;
2018-11-10 22:04:40 +01:00
}
2018-11-07 11:59:59 +01:00
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : sendLandFlags ( )
2018-11-07 11:59:59 +01:00
{
2021-11-27 00:53:57 +01:00
auto landFlags = makeZonePacket < FFXIVIpcCharaHousing > ( getId ( ) ) ;
2018-11-07 11:59:59 +01:00
2021-11-27 00:53:57 +01:00
landFlags - > data ( ) . FcLands [ 0 ] = m_charaLandData [ Common : : LandFlagsSlot : : FreeCompany ] ;
landFlags - > data ( ) . CharaLands [ 0 ] = m_charaLandData [ Common : : LandFlagsSlot : : Private ] ;
2018-11-07 11:59:59 +01:00
2018-11-28 23:29:55 +11:00
queuePacket ( landFlags ) ;
2018-11-10 19:15:04 +01:00
}
2018-11-15 22:30:59 +01:00
2018-11-29 16:55:48 +01:00
void Sapphire : : Entity : : Player : : sendLandFlagsSlot ( Common : : LandFlagsSlot slot )
2018-11-15 22:30:59 +01:00
{
2021-11-27 00:53:57 +01:00
auto landFlags = makeZonePacket < FFXIVIpcCharaHousingLandData > ( getId ( ) ) ;
2018-11-27 22:18:50 +11:00
2021-11-27 00:53:57 +01:00
LandType type ;
2018-11-27 22:18:50 +11:00
2018-11-28 23:29:55 +11:00
switch ( slot )
{
case LandFlagsSlot : : Private :
2021-11-27 00:53:57 +01:00
type = LandType : : Private ;
2018-11-28 23:29:55 +11:00
break ;
case LandFlagsSlot : : FreeCompany :
2021-11-27 00:53:57 +01:00
type = LandType : : FreeCompany ;
2018-11-28 23:29:55 +11:00
break ;
default :
// todo: other/unsupported land types
return ;
}
2021-11-27 00:53:57 +01:00
landFlags - > data ( ) . Flags = static_cast < uint32_t > ( type ) ;
landFlags - > data ( ) . LandId = m_charaLandData [ slot ] . landId ;
2018-11-15 22:30:59 +01:00
2018-11-28 23:29:55 +11:00
queuePacket ( landFlags ) ;
2019-01-04 12:34:19 +01:00
}
2019-03-26 00:04:27 +01:00
Sapphire : : Common : : HuntingLogEntry & Sapphire : : Entity : : Player : : getHuntingLogEntry ( uint8_t index )
{
assert ( index < m_huntingLogEntries . size ( ) ) ;
return m_huntingLogEntries [ index ] ;
}
2019-03-26 23:08:34 +01:00
void Sapphire : : Entity : : Player : : sendHuntingLog ( )
{
2021-11-27 00:53:57 +01:00
auto & exdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
2019-03-26 23:08:34 +01:00
uint8_t count = 0 ;
for ( const auto & entry : m_huntingLogEntries )
{
uint64_t completionFlag = 0 ;
2021-11-27 00:53:57 +01:00
auto huntPacket = makeZonePacket < FFXIVIpcMonsterNoteCategory > ( getId ( ) ) ;
2019-03-26 23:08:34 +01:00
2021-11-27 00:53:57 +01:00
huntPacket - > data ( ) . contextId = - 1 ;
huntPacket - > data ( ) . currentRank = entry . rank ;
huntPacket - > data ( ) . categoryIndex = count ;
2019-03-26 23:08:34 +01:00
for ( int i = 1 ; i < = 10 ; + + i )
{
auto index0 = i - 1 ;
bool allComplete = true ;
auto monsterNoteId = ( count + 1 ) * 10000 + entry . rank * 10 + i ;
2021-11-27 00:53:57 +01:00
auto monsterNote = exdData . getRow < Component : : Excel : : MonsterNote > ( monsterNoteId ) ;
2019-03-26 23:08:34 +01:00
if ( ! monsterNote )
continue ;
const auto huntEntry = entry . entries [ index0 ] ;
for ( int x = 0 ; x < 3 ; + + x )
{
2021-11-27 00:53:57 +01:00
if ( ( huntEntry [ x ] = = monsterNote - > data ( ) . NeededKills [ x ] ) & & monsterNote - > data ( ) . NeededKills [ x ] ! = 0 )
2019-03-26 23:08:34 +01:00
completionFlag | = ( 1ull < < ( index0 * 5 + x ) ) ;
2021-11-27 00:53:57 +01:00
else if ( monsterNote - > data ( ) . NeededKills [ x ] ! = 0 )
2019-03-26 23:08:34 +01:00
allComplete = false ;
}
if ( allComplete )
completionFlag | = ( 1ull < < ( index0 * 5 + 4 ) ) ;
}
2021-11-27 00:53:57 +01:00
memcpy ( huntPacket - > data ( ) . killCount , entry . entries , sizeof ( entry . entries ) ) ;
2019-03-26 23:08:34 +01:00
huntPacket - > data ( ) . completeFlags = completionFlag ;
+ + count ;
queuePacket ( huntPacket ) ;
}
}
2019-03-28 22:58:40 +01:00
void Sapphire : : Entity : : Player : : updateHuntingLog ( uint16_t id )
{
std : : vector < uint32_t > rankRewards { 2500 , 10000 , 20000 , 30000 , 40000 } ;
const auto maxRank = 4 ;
2021-11-27 00:53:57 +01:00
auto & pExdData = Common : : Service < Data : : ExdData > : : ref ( ) ;
auto currentClassId = static_cast < uint8_t > ( getClass ( ) ) ;
2019-03-28 22:58:40 +01:00
2021-11-27 00:53:57 +01:00
auto & logEntry = m_huntingLogEntries [ currentClassId - 1 ] ;
2019-03-28 22:58:40 +01:00
bool logChanged = false ;
2019-04-06 17:17:56 +02:00
// make sure we get the matching base-class if a job is being used
2021-11-27 00:53:57 +01:00
auto currentClass = currentClassId ;
auto classJobInfo = pExdData . getRow < Component : : Excel : : ClassJob > ( currentClass ) ;
2019-04-06 17:17:56 +02:00
if ( ! classJobInfo )
return ;
2019-03-28 22:58:40 +01:00
bool allSectionsComplete = true ;
for ( int i = 1 ; i < = 10 ; + + i )
{
bool sectionComplete = true ;
bool sectionChanged = false ;
2021-11-27 00:53:57 +01:00
auto monsterNoteId = static_cast < uint32_t > ( classJobInfo - > data ( ) . MainClass * 10000 + logEntry . rank * 10 + i ) ;
auto note = pExdData . getRow < Component : : Excel : : MonsterNote > ( monsterNoteId ) ;
2019-04-06 11:44:06 +11:00
// for classes that don't have entries, if the first fails the rest will fail
if ( ! note )
break ;
2019-03-28 22:58:40 +01:00
for ( auto x = 0 ; x < 4 ; + + x )
{
2021-11-27 00:53:57 +01:00
auto note1 = pExdData . getRow < Component : : Excel : : MonsterNoteTarget > ( note - > data ( ) . Target [ x ] ) ;
2021-12-07 00:51:09 +01:00
if ( note1 - > data ( ) . Monster = = id & & logEntry . entries [ i - 1 ] [ x ] < note - > data ( ) . NeededKills [ x ] )
2019-03-28 22:58:40 +01:00
{
logEntry . entries [ i - 1 ] [ x ] + + ;
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , HuntingLogEntryUpdate , monsterNoteId , x , logEntry . entries [ i - 1 ] [ x ] ) ) ;
2019-03-28 22:58:40 +01:00
logChanged = true ;
sectionChanged = true ;
}
2021-12-07 00:51:09 +01:00
if ( logEntry . entries [ i - 1 ] [ x ] ! = note - > data ( ) . NeededKills [ x ] )
2019-03-28 22:58:40 +01:00
sectionComplete = false ;
}
if ( logChanged & & sectionComplete & & sectionChanged )
{
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , HuntingLogSectionFinish , monsterNoteId , i , 0 ) ) ;
2021-11-27 00:53:57 +01:00
gainExp ( note - > data ( ) . RewardExp ) ;
2019-03-28 22:58:40 +01:00
}
if ( ! sectionComplete )
{
allSectionsComplete = false ;
}
}
if ( logChanged & & allSectionsComplete )
{
2019-10-09 18:42:25 +02:00
queuePacket ( makeActorControlSelf ( getId ( ) , HuntingLogRankFinish , 4 , 0 , 0 ) ) ;
2019-03-28 22:58:40 +01:00
gainExp ( rankRewards [ logEntry . rank ] ) ;
if ( logEntry . rank < 4 )
{
logEntry . rank + + ;
memset ( logEntry . entries , 0 , 40 ) ;
2021-11-27 00:53:57 +01:00
queuePacket ( makeActorControlSelf ( getId ( ) , HuntingLogRankUnlock , currentClassId , logEntry . rank + 1 , 0 ) ) ;
2019-03-28 22:58:40 +01:00
}
}
2019-04-07 16:04:36 +10:00
2019-04-06 11:44:06 +11:00
if ( logChanged )
sendHuntingLog ( ) ;
2019-03-28 22:58:40 +01:00
}
2019-04-24 23:25:07 +10:00
void Sapphire : : Entity : : Player : : setActiveLand ( uint8_t land , uint8_t ward )
{
m_activeLand . plot = land ;
m_activeLand . ward = ward ;
}
Sapphire : : Common : : ActiveLand Sapphire : : Entity : : Player : : getActiveLand ( ) const
{
return m_activeLand ;
}
2020-01-05 17:09:27 +09:00
bool Sapphire : : Entity : : Player : : hasQueuedAction ( ) const
{
return m_pQueuedAction ! = nullptr ;
}
void Sapphire : : Entity : : Player : : setQueuedAction ( Sapphire : : World : : Action : : ActionPtr pAction )
{
2020-01-05 20:49:50 +09:00
m_pQueuedAction = std : : move ( pAction ) ; // overwrite previous queued action if any
2020-01-05 17:09:27 +09:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setLastActionTick ( uint64_t tick )
{
m_lastActionTick = tick ;
}
uint64_t Sapphire : : Entity : : Player : : getLastActionTick ( ) const
{
return m_lastActionTick ;
}
void Sapphire : : Entity : : Player : : setRecastGroup ( uint8_t index , float time )
{
m_recast [ index ] = time ;
if ( time > m_recastMax [ index ] )
m_recastMax [ index ] = time ;
}
float Sapphire : : Entity : : Player : : getRecastGroup ( uint8_t index ) const
{
return m_recast [ index ] ;
}
void Sapphire : : Entity : : Player : : sendRecastGroups ( )
{
auto recastGroupPaket = makeZonePacket < FFXIVIpcRecastGroup > ( getId ( ) ) ;
memcpy ( & recastGroupPaket - > data ( ) . Recast , & m_recast , sizeof ( m_recast ) ) ;
memcpy ( & recastGroupPaket - > data ( ) . RecastMax , & m_recastMax , sizeof ( m_recastMax ) ) ;
queuePacket ( recastGroupPaket ) ;
}
void Sapphire : : Entity : : Player : : resetRecastGroups ( )
{
for ( size_t i = 0 ; i < 80 ; + + i )
{
m_recast [ i ] = 0.0f ;
m_recastMax [ i ] = 0.0f ;
}
sendRecastGroups ( ) ;
}
2020-01-05 17:09:27 +09:00
bool Sapphire : : Entity : : Player : : checkAction ( )
{
if ( m_pCurrentAction = = nullptr )
return false ;
if ( m_pCurrentAction - > update ( ) )
{
if ( m_pCurrentAction - > isInterrupted ( ) & & m_pCurrentAction - > getInterruptType ( ) ! = Common : : ActionInterruptType : : DamageInterrupt )
{
// we moved (or whatever not damage interrupt) so we don't want to execute queued cast
m_pQueuedAction = nullptr ;
}
m_pCurrentAction = nullptr ;
if ( hasQueuedAction ( ) )
{
2021-11-27 00:53:57 +01:00
PlayerMgr : : sendDebug ( * this , " Queued skill start: {0} " , m_pQueuedAction - > getId ( ) ) ;
2020-01-05 17:09:27 +09:00
if ( m_pQueuedAction - > hasCastTime ( ) )
{
setCurrentAction ( m_pQueuedAction ) ;
}
m_pQueuedAction - > start ( ) ;
m_pQueuedAction = nullptr ;
}
}
return true ;
2020-03-03 08:37:19 +00:00
}
2020-04-24 19:24:04 +09:00
2021-11-27 00:53:57 +01:00
uint64_t Sapphire : : Entity : : Player : : getPartyId ( ) const
2020-05-11 06:25:25 +09:00
{
2021-11-27 00:53:57 +01:00
return m_partyId ;
2020-05-11 06:25:25 +09:00
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setPartyId ( uint64_t partyId )
2020-05-11 06:25:25 +09:00
{
2021-11-27 00:53:57 +01:00
m_partyId = partyId ;
2020-05-11 06:25:25 +09:00
}
2021-12-01 21:30:12 -03:00
Sapphire : : Entity : : Player : : FriendListIDVec & Sapphire : : Entity : : Player : : getFriendListID ( )
{
return m_friendList ;
}
2021-12-13 22:36:29 -03:00
2021-12-01 21:30:12 -03:00
Sapphire : : Entity : : Player : : FriendListDataVec & Sapphire : : Entity : : Player : : getFriendListData ( )
{
return m_friendInviteList ;
}
2021-12-13 22:36:29 -03:00
Sapphire : : Entity : : Player : : FriendListIDVec & Sapphire : : Entity : : Player : : getBlacklistID ( )
{
return m_blacklist ;
}
2022-01-05 20:56:02 -03:00
void Sapphire : : Entity : : Player : : setFalling ( bool state , const Common : : FFXIVARR_POSITION3 & pos , bool ignoreDamage )
{
bool isFalling = m_falling ;
auto initialPos = m_initialFallPos ;
// update internal values - only use scoped values for old state
m_falling = state ;
m_initialFallPos = pos ;
if ( ignoreDamage )
return ;
// if the player is currently falling and new state is grounded - calc and apply fall dmg
if ( isFalling & & ! state )
{
// calc height difference
auto fallHeight = initialPos . y - pos . y ;
// if we've hit the breakpoint in fall damage (min: 10y)
if ( fallHeight > = 10.f )
{
// calculate how much damage to deal out (max. 20y : 100%)
2022-01-06 20:25:50 +01:00
float deltaMax = std : : min ( fallHeight , 30.f ) ;
2022-01-05 20:56:02 -03:00
// get hp percentage starting from 0.1, increasing to 100% at max height
2022-01-06 20:25:50 +01:00
float hpPer = std : : min ( 0.1f + ( deltaMax - 10.f ) / 20.f , 1.f ) ;
2022-01-05 20:56:02 -03:00
uint32_t damage = getMaxHp ( ) * hpPer ;
// check if player has aggro - if not, player should "live"
if ( m_actorIdTohateSlotMap . empty ( ) )
{
// "trick" client into thinking we took more damage than internally passed to takeDamage, if > playerHp
uint32_t surviveDamage = damage ;
if ( surviveDamage > = getHp ( ) )
{
surviveDamage = ( getHp ( ) - 1 ) ;
}
takeDamage ( surviveDamage ) ;
}
else
{
// no mercy on hated players
takeDamage ( damage ) ;
}
2022-01-06 20:44:35 +01:00
sendToInRangeSet ( makeActorControl ( getId ( ) , SetFallDamage , damage ) , true ) ;
2022-01-05 20:56:02 -03:00
}
}
}
bool Sapphire : : Entity : : Player : : isFalling ( ) const
{
return m_falling ;
}
2021-11-27 00:53:57 +01:00
void Sapphire : : Entity : : Player : : setLastPcSearchResult ( std : : vector < uint32_t > result )
2020-05-11 06:25:25 +09:00
{
2021-11-27 00:53:57 +01:00
m_lastPcSearch = std : : move ( result ) ;
2020-05-11 06:25:25 +09:00
}
2021-11-27 00:53:57 +01:00
std : : vector < uint32_t > & Sapphire : : Entity : : Player : : getLastPcSearchResult ( )
2020-04-24 19:24:04 +09:00
{
2021-11-27 00:53:57 +01:00
return m_lastPcSearch ;
2020-04-24 19:24:04 +09:00
}
2021-11-27 00:53:57 +01:00
const FFXIVARR_POSITION3 & Sapphire : : Entity : : Player : : getPrevPos ( ) const
2020-04-24 19:24:04 +09:00
{
2021-11-27 00:53:57 +01:00
return m_prevPos ;
}
2020-04-24 19:24:04 +09:00
2021-11-27 00:53:57 +01:00
float Sapphire : : Entity : : Player : : getPrevRot ( ) const
{
return m_prevRot ;
2020-04-24 19:24:04 +09:00
}
2021-11-27 00:53:57 +01:00
std : : optional < Sapphire : : World : : Quest > Sapphire : : Entity : : Player : : getQuest ( uint32_t questId )
2020-04-24 19:24:04 +09:00
{
2021-11-27 00:53:57 +01:00
if ( ! hasQuest ( questId ) )
return std : : nullopt ;
auto idx = getQuestIndex ( questId ) ;
auto quest = getQuestByIndex ( idx ) ;
return { quest } ;
}