1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-24 13:47:46 +00:00
sapphire/src/world/Actor/Player.cpp

1882 lines
48 KiB
C++
Raw Normal View History

2018-03-06 22:22:19 +01:00
#include <Common.h>
#include <Util/Util.h>
#include <Util/UtilMath.h>
#include <Logging/Logger.h>
#include <Exd/ExdData.h>
2018-10-26 14:11:02 +02:00
#include <cmath>
#include <utility>
2020-02-29 22:30:10 +11:00
#include <Service.h>
#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
#include "Manager/TerritoryMgr.h"
#include "Manager/RNGMgr.h"
#include "Manager/PlayerMgr.h"
#include "Manager/PartyMgr.h"
2022-01-30 14:44:17 +01:00
#include "Manager/WarpMgr.h"
2023-02-10 08:56:58 +01:00
#include "Manager/FreeCompanyMgr.h"
#include "Manager/MapMgr.h"
#include "Manager/MgrUtil.h"
2023-02-20 11:24:02 +01:00
#include "Manager/ActionMgr.h"
2023-03-06 10:12:29 +01:00
#include "Manager/AchievementMgr.h"
#include "Territory/InstanceContent.h"
2018-11-10 19:00:13 +01:00
#include "Network/GameConnection.h"
#include "Network/PacketContainer.h"
#include "Network/CommonActorControl.h"
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Network/PacketWrappers/ActorControlSelfPacket.h"
#include "Network/PacketWrappers/PlayerSpawnPacket.h"
#include "Network/PacketWrappers/EffectPacket1.h"
#include "Network/PacketWrappers/InitZonePacket.h"
#include "Network/Util/PacketUtil.h"
#include "Action/Action.h"
#include "Math/CalcStats.h"
2017-08-08 13:53:47 +02:00
#include "WorldServer.h"
#include <Manager/TaskMgr.h>
#include <Task/ActionIntegrityTask.h>
2022-02-05 23:31:35 +01:00
using namespace Sapphire;
using namespace Sapphire::Common;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::WorldPackets::Server;
using namespace Sapphire::Network::ActorControl;
using namespace Sapphire::World::Manager;
2022-02-05 23:31:35 +01:00
using namespace Sapphire::Entity;
2017-08-08 13:53:47 +02:00
using InventoryMap = std::map< uint16_t, Sapphire::ItemContainerPtr >;
using InvSlotPair = std::pair< uint16_t, int8_t >;
using InvSlotPairVec = std::vector< InvSlotPair >;
2017-08-08 13:53:47 +02:00
// player constructor
2022-02-05 23:31:35 +01:00
Player::Player() :
2020-03-01 01:00:57 +11:00
Chara( ObjKind::Player ),
m_lastDBWrite( 0 ),
m_bIsLogin( false ),
m_characterId( 0 ),
m_modelMainWeapon( 0 ),
m_modelSubWeapon( 0 ),
m_homePoint( 0 ),
m_startTown( 0 ),
m_townWarpFstFlags( 0 ),
m_playTime( 0 ),
m_lastActionTick( 0 ),
m_bInCombat( false ),
m_bLoadingComplete( false ),
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 ),
m_pQueuedAction( nullptr ),
m_partyId( 0 ),
m_onlineStatusCustom( 0 ),
m_onlineStatus( 0 ),
m_bIsConnected( false )
{
m_id = 0;
m_currentStance = Stance::Passive;
m_onlineStatus = 0;
m_status = ActorStatus::Idle;
m_invincibilityType = InvincibilityType::InvincibilityNone;
m_radius = 1.f;
memset( m_name, 0, sizeof( m_name ) );
memset( m_searchMessage, 0, sizeof( m_searchMessage ) );
2023-01-17 08:28:06 +01:00
std::fill( std::begin( m_questTracking ), std::end( m_questTracking ), 0 );
2023-02-20 15:25:57 +01:00
std::fill( std::begin( m_condition ), std::end( m_condition ), 0 );
2023-01-17 08:28:06 +01:00
std::fill( std::begin( m_classArray ), std::end( m_classArray ), 0 );
std::fill( std::begin( m_expArray ), std::end( m_expArray ), 0 );
2021-12-13 22:36:29 -03:00
for( uint8_t i = 0; i < 80; ++i )
{
m_recast[ i ] = 0.0f;
m_recastMax[ i ] = 0.0f;
}
2021-12-13 22:36:29 -03:00
for( auto& i : m_charaLandData )
2018-11-07 11:59:59 +01:00
{
memset( &i, 0xFF, 8 );
2023-01-23 22:58:25 +01:00
memset( &i.landFlags, 0, 8 );
2018-11-07 11:59:59 +01:00
}
m_objSpawnIndexAllocator.init( MAX_DISPLAYED_EOBJS );
m_actorSpawnIndexAllocator.init( MAX_DISPLAYED_ACTORS, true );
initHateSlotQueue();
initSpawnIdQueue();
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::~Player() = default;
2017-08-08 13:53:47 +02:00
2022-02-05 23:31:35 +01:00
void Player::unload()
2018-03-05 22:10:14 +11:00
{
// do one last update to db
updateSql();
// reset isLogin and loading sequences just in case
setIsLogin( false );
setConnected( false );
setLoadingComplete( false );
// unset player for removal
setMarkedForRemoval( false );
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
2022-02-05 23:31:35 +01:00
uint32_t Player::getMaxHp()
2017-08-08 13:53:47 +02:00
{
2023-02-21 07:58:53 +01:00
return m_maxHp;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getMaxMp()
2017-08-08 13:53:47 +02:00
{
2023-02-21 07:58:53 +01:00
return m_maxMp;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getPrevTerritoryId() const
{
return m_prevTerritoryId;
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getGmRank() const
2017-09-11 18:59:50 +02:00
{
return m_gmRank;
2017-09-11 18:59:50 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setGmRank( uint8_t rank )
2017-09-11 18:59:50 +02:00
{
m_gmRank = rank;
2017-09-11 18:59:50 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::getGmInvis() const
{
return m_gmInvis;
}
2022-02-05 23:31:35 +01:00
void Player::setGmInvis( bool invis )
{
m_gmInvis = invis;
}
2022-02-05 23:31:35 +01:00
bool Player::isActingAsGm() const
2018-07-01 21:31:49 +10:00
{
auto status = getOnlineStatus();
return status == OnlineStatus::GameMaster || status == OnlineStatus::GameMaster1 || status == OnlineStatus::GameMaster2;
2018-07-01 21:31:49 +10:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getMode() const
2017-08-08 13:53:47 +02:00
{
return m_mode;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setMode( uint8_t mode )
2017-08-08 13:53:47 +02:00
{
m_mode = mode;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getStartTown() const
2017-08-08 13:53:47 +02:00
{
return m_startTown;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setMarkedForRemoval( bool removal )
{
m_markedForRemoval = removal;
}
2022-02-05 23:31:35 +01:00
bool Player::isMarkedForRemoval() const
{
return m_markedForRemoval;
}
2022-02-05 23:31:35 +01:00
Common::OnlineStatus Player::getOnlineStatus() const
2017-08-08 13:53:47 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
uint32_t statusDisplayOrder = 0xFF14;
auto applicableStatus = isConnected() ? static_cast< uint32_t >( OnlineStatus::Online ) : static_cast< uint32_t >( OnlineStatus::Offline );
2018-04-19 23:04:31 +10:00
for( uint32_t i = 0; i < std::numeric_limits< decltype( m_onlineStatus ) >::digits; ++i )
{
bool bit = ( getFullOnlineStatusMask() >> i ) & 1;
if( !bit )
continue;
2022-01-27 21:24:54 +01:00
auto pOnlineStatus = exdData.getRow< Excel::OnlineStatus >( i );
if( !pOnlineStatus )
continue;
2017-08-08 13:53:47 +02:00
if( pOnlineStatus->data().ListOrder < statusDisplayOrder )
{
// todo: also check that the status can actually be set here, otherwise we need to ignore it (and ban the player obv)
statusDisplayOrder = pOnlineStatus->data().ListOrder;
applicableStatus = i;
}
}
return static_cast< OnlineStatus >( applicableStatus );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setOnlineStatusMask( uint64_t status )
2017-08-08 13:53:47 +02:00
{
m_onlineStatus = status;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getOnlineStatusMask() const
2017-08-08 13:53:47 +02:00
{
return m_onlineStatus;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getFullOnlineStatusMask() const
{
return m_onlineStatus | m_onlineStatusCustom;
}
/*! sets the list of current online status */
2022-02-05 23:31:35 +01:00
void Player::setOnlineStatusCustomMask( uint64_t status )
{
m_onlineStatusCustom = status;
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getOnlineStatusCustomMask() const
{
return m_onlineStatusCustom;
}
2022-02-05 23:31:35 +01:00
void Player::addOnlineStatus( OnlineStatus status )
{
uint64_t statusValue = 1ull << static_cast< uint8_t >( status );
uint64_t newFlags = ( getOnlineStatusMask() & getOnlineStatusCustomMask() ) | statusValue;
setOnlineStatusMask( newFlags );
Network::Util::Packet::sendOnlineStatus( *this );
}
2022-02-05 23:31:35 +01:00
void 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 );
Network::Util::Packet::sendOnlineStatus( *this );
}
2022-02-05 23:31:35 +01:00
void 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 );
Network::Util::Packet::sendOnlineStatus( *this );
}
2022-02-05 23:31:35 +01:00
void 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 );
Network::Util::Packet::sendOnlineStatus( *this );
}
2022-02-05 23:31:35 +01:00
void Player::calculateStats()
2017-08-08 13:53:47 +02:00
{
2023-02-10 17:14:09 -03:00
calculateBonusStats();
uint8_t tribe = getLookAt( Common::CharaLook::Tribe );
uint8_t level = getLevel();
auto job = static_cast< uint8_t >( getClass() );
auto deity = getGuardianDeity();
2017-08-08 13:53:47 +02:00
auto& exdData = Common::Service< Data::ExdData >::ref();
2018-03-09 00:06:44 +01:00
2022-01-27 21:24:54 +01:00
auto classInfo = exdData.getRow< Excel::ClassJob >( job );
auto tribeInfo = exdData.getRow< Excel::Tribe >( tribe );
auto deityInfo = exdData.getRow< Excel::GuardianDeity >( deity );
2022-01-27 21:24:54 +01:00
auto paramGrowthInfo = exdData.getRow< Excel::ParamGrow >( level );
2017-08-08 13:53:47 +02:00
float base = Math::CalcStats::calculateBaseStat( *this );
2017-08-08 13:53:47 +02: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
setStatValue( BaseParam::Haste, 100 );
setStatValue( BaseParam::Defense, 0 );
setStatValue( BaseParam::MagicDefense, 0 );
2017-08-08 13:53:47 +02: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] );
setStatValue( BaseParam::AttackPower, str );
setStatValue( BaseParam::AttackMagicPotency, inte );
setStatValue( BaseParam::HealingMagicPotency, mnd );
setStatValue( BaseParam::PiercingResistance, 0 );
2023-02-21 07:58:53 +01:00
m_maxMp = Math::CalcStats::calculateMaxMp( *this );
2017-08-08 13:53:47 +02:00
2023-02-21 07:58:53 +01:00
m_maxHp = Math::CalcStats::calculateMaxHp( *this );
2017-08-08 13:53:47 +02:00
2023-02-21 07:58:53 +01:00
if( m_mp > m_maxMp )
m_mp = m_maxMp;
2017-08-08 13:53:47 +02:00
2023-02-21 07:58:53 +01:00
if( m_hp > m_maxHp )
m_hp = m_maxHp;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setAutoattack( bool mode )
2017-08-08 13:53:47 +02:00
{
m_bAutoattack = mode;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::isAutoattackOn() const
2017-08-08 13:53:47 +02:00
{
return m_bAutoattack;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::exitInstance()
{
2022-01-30 14:44:17 +01:00
auto& warpMgr = Common::Service< WarpMgr >::ref();
resetHp();
resetMp();
2023-02-20 11:24:02 +01:00
warpMgr.requestMoveTerritory( *this, WarpType::WARP_TYPE_CONTENT_END_RETURN, getPrevTerritoryId(), getPrevPos(), getPrevRot() );
return true;
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getPlayTime() const
2017-08-08 13:53:47 +02:00
{
return m_playTime;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getRace() const
2017-08-08 13:53:47 +02:00
{
return getLookAt( CharaLook::Race );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getGender() const
2017-08-08 13:53:47 +02:00
{
return getLookAt( CharaLook::Gender );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::initSpawnIdQueue()
2017-08-08 13:53:47 +02:00
{
m_actorSpawnIndexAllocator.freeAllSpawnIndexes();
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getSpawnIdForActorId( uint32_t actorId )
2017-08-08 13:53:47 +02: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() );
PlayerMgr::sendUrgent( *this, "Failed to spawn Chara#{0} for you - no remaining spawn slots. See world log.", actorId );
}
return index;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::isActorSpawnIdValid( uint8_t spawnIndex )
2017-08-08 13:53:47 +02:00
{
return m_actorSpawnIndexAllocator.isSpawnIndexValid( spawnIndex );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::registerAetheryte( uint8_t aetheryteId )
2017-08-08 13:53:47 +02:00
{
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( aetheryteId, value, index );
2017-08-08 13:53:47 +02:00
m_aetheryte[ index ] |= value;
Network::Util::Packet::sendActorControlSelf( *this, getId(), LearnTeleport, aetheryteId, 1 );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::isAetheryteRegistered( uint8_t aetheryteId ) const
2017-08-08 13:53:47 +02:00
{
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( aetheryteId, value, index );
2017-08-08 13:53:47 +02:00
return ( m_aetheryte[ index ] & value ) != 0;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::Discovery& Player::getDiscoveryBitmask()
2017-08-08 13:53:47 +02:00
{
return m_discovery;
2017-08-08 13:53:47 +02:00
}
void Player::discover( int16_t mapId, int16_t subId )
2017-08-08 13:53:47 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2018-03-09 00:06:44 +01:00
int32_t offset;
2017-08-08 13:53:47 +02:00
auto info = exdData.getRow< Excel::Map >( mapId );
if( !info )
{
PlayerMgr::sendDebug( *this, "discover(): Could not obtain map data for map_id == {0}", mapId );
return;
}
const auto& mapData = info->data();
2017-08-08 13:53:47 +02:00
if( mapData.IsUint16Discovery )
offset = 2 * mapData.DiscoveryIndex;
else
offset = 320 + 4 * mapData.DiscoveryIndex;
2017-08-08 13:53:47 +02:00
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( subId, value, index );
2017-08-08 13:53:47 +02:00
m_discovery[ offset + index ] |= value;
2017-08-08 13:53:47 +02:00
uint16_t level = getLevel();
2017-08-08 13:53:47 +02:00
2022-01-27 21:24:54 +01:00
uint32_t exp = ( exdData.getRow< Excel::ParamGrow >( level )->data().NextExp * 5 / 100 );
gainExp( exp );
2017-08-08 13:53:47 +02:00
// gain 10x additional EXP if entire map is completed
uint32_t mask = mapData.DiscoveryFlag;
uint32_t discoveredAreas;
if( info->data().IsUint16Discovery )
{
discoveredAreas = ( m_discovery[ offset + 1 ] << 8 ) | m_discovery[ offset ];
}
else
{
discoveredAreas = ( m_discovery[ offset + 3 ] << 24 ) |
( m_discovery[ offset + 2 ] << 16 ) |
( m_discovery[ offset + 1 ] << 8 ) |
m_discovery[ offset ];
}
bool allDiscovered = ( ( discoveredAreas & mask ) == mask );
2017-08-08 13:53:47 +02:00
if( allDiscovered )
{
gainExp( exp * 10 );
}
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::isNewAdventurer() const
2017-08-08 13:53:47 +02:00
{
return m_bNewAdventurer;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setNewAdventurer( bool state )
2017-08-08 13:53:47 +02:00
{
//if( !state )
//{
// unsetStateFlag( PlayerStateFlag::NewAdventurer );
//}
//else
//{
// setStateFlag( PlayerStateFlag::NewAdventurer );
//}
m_bNewAdventurer = state;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::resetDiscovery()
2017-08-08 13:53:47 +02:00
{
memset( m_discovery.data(), 0, m_discovery.size() );
2017-08-08 13:53:47 +02:00
}
void Player::setRewardFlag( Common::UnlockEntry unlockId )
2017-08-08 13:53:47 +02:00
{
uint16_t index;
uint8_t value;
auto unlock = static_cast< uint16_t >( unlockId );
Util::valueToFlagByteIndexValue( unlock, value, index );
2017-08-08 13:53:47 +02:00
m_unlocks[ index ] |= value;
2017-08-08 13:53:47 +02:00
Network::Util::Packet::sendActorControlSelf( *this, getId(), SetRewardFlag, unlock, 1 );
2017-08-08 13:53:47 +02:00
}
void Player::fillRewardFlags()
{
memset( m_unlocks.data(), 0xFF, m_unlocks.size() );
}
[3.x] WIP: Initial Action and StatusEffect implementation (#958) * Check statuses to determine valid lut entry * Add duration field to statuses * Rename buildEffects to make more sense * Add basic generic handler for applying statuseffects * Add more modifiers * Add basic modifier impl for Chara * Apply/remove modifiers for statuseffects * Add some example statuses to lut * Fix windows build error * Don't clear tick effect * Add status entry for Maim * Apply status effects properly for self when having a target * Fix hasStatusEffect to prevent duplicates * Basic dot/hot ticks implemented * Update HP on tick effects * Apply effect to correct target * Add method to simplify applying statuses to self * Add job actions for warrior * Add some actions and statuses for war * Add even more modifiers * Add statuseffect cost type * Add option to not send statusremove order * Change delModifier assert to return early instead * Add option for scripts to enable the generic/lut handler * Add enums for common action values * fix indentation * Fix modifier name for Defiance * Remove status tick logging * Move modifiers to statuseffect * Add ParryPercent modifier * Remove wrath when Defiance ends * Apply modifiers in applyStatus * Remove unused method * Persistence for cross-class skills * Add flags to StatusEffects * Some exd struct fixes * Some aoe work * Add flags to lut * Add missing changeclass * Add SET_STATUS_ME to ActionIntegrity * Improve offensive action check * Add flag to overloaded applyStatusEffectSelf * indentation fix * Some calculation work * Null-check ActionResultBuilder --------- Co-authored-by: Lucy <44952533+Skyliegirl33@users.noreply.github.com> Co-authored-by: Mordred <30826167+SapphireMordred@users.noreply.github.com>
2024-06-21 04:27:01 +02:00
void Player::setBorrowAction( uint8_t slot, uint32_t action )
{
if( slot > Common::ARRSIZE_BORROWACTION )
return;
auto& borrowAction = getBorrowAction();
borrowAction[ slot ] = action;
}
Player::BorrowAction& Player::getBorrowAction()
{
auto& exdData = Common::Service< Data::ExdData >::ref();
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast<uint8_t>( getClass() ) )->data().WorkIndex;
return m_borrowActions[ classJobIndex ];
}
2022-02-05 23:31:35 +01:00
void Player::learnSong( uint8_t songId, uint32_t itemId )
2017-10-09 20:09:49 +02:00
{
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( songId, value, index );
2017-10-09 20:09:49 +02:00
m_orchestrion[ index ] |= value;
2023-03-06 13:27:56 +01:00
Network::Util::Packet::sendActorControlSelf( *this, getId(), ToggleOrchestrionUnlock, songId, 1, itemId );
2017-10-09 20:09:49 +02:00
}
2022-02-10 18:50:44 +01:00
bool Player::hasReward( Common::UnlockEntry unlockId ) const
2017-08-08 13:53:47 +02:00
{
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( static_cast< uint16_t >( unlockId ), value, index );
2017-08-08 13:53:47 +02:00
return ( m_unlocks[ index ] & value ) != 0;
2017-08-08 13:53:47 +02:00
}
bool Player::hasMount( uint32_t mountId ) const
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto mount = exdData.getRow< Excel::Mount >( mountId );
if( !mount || mount->data().MountOrder == -1 || mount->data().Model == 0 )
return false;
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( mount->data().MountOrder, value, index );
return m_mountGuide[ index ] & value;
}
2022-02-05 23:31:35 +01:00
void Player::gainExp( uint32_t amount )
2017-08-08 13:53:47 +02:00
{
uint32_t currentExp = getExp();
uint16_t level = getLevel();
2023-03-06 13:27:56 +01:00
auto currentClass = static_cast< uint8_t >( getClass() );
2017-08-08 13:53:47 +02:00
if( level >= Common::MAX_PLAYER_LEVEL )
{
setExp( 0 );
if( currentExp != 0 )
Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, 0 );
return;
}
auto& exdData = Common::Service< Data::ExdData >::ref();
2017-08-08 13:53:47 +02:00
2022-01-27 21:24:54 +01:00
uint32_t neededExpToLevel = exdData.getRow< Excel::ParamGrow >( level )->data().NextExp;
uint32_t neededExpToLevelPlus1 = exdData.getRow< Excel::ParamGrow >( level + 1 )->data().NextExp;
2017-08-08 13:53:47 +02:00
if( ( currentExp + amount ) >= neededExpToLevel )
{
// levelup
2023-03-06 13:27:56 +01:00
amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelPlus1 ? neededExpToLevelPlus1 - 1 : ( currentExp + amount - neededExpToLevel );
if( level + 1 >= Common::MAX_PLAYER_LEVEL )
amount = 0;
2017-08-08 13:53:47 +02:00
setExp( amount );
levelUp();
}
else
setExp( currentExp + amount );
Network::Util::Packet::sendActorControlSelf( *this, getId(), GainExpMsg, currentClass, amount );
Network::Util::Packet::sendActorControlSelf( *this, getId(), UpdateUiExp, currentClass, getExp() );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::levelUp()
2017-08-08 13:53:47 +02:00
{
m_hp = getMaxHp();
m_mp = getMaxMp();
2017-08-08 13:53:47 +02:00
setLevel( getLevel() + 1 );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getLevel() const
2017-08-08 13:53:47 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2022-01-27 21:24:54 +01:00
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex;
return static_cast< uint8_t >( m_classArray[ classJobIndex ] );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getLevelSync() const
{
// TODO: implement levelSync
return getLevel();
}
uint8_t Player::getLevelForClass( Common::ClassJob classJobId ) const
2017-08-09 14:38:46 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( classJobId ) )->data().WorkIndex;
return static_cast< uint8_t >( m_classArray[ classJobIndex ] );
2017-08-09 14:38:46 +02:00
}
2023-02-11 17:20:21 +01:00
Common::ClassJob Player::getFirstClass() const
{
return m_firstClass;
}
2022-02-05 23:31:35 +01:00
bool Player::isClassJobUnlocked( Common::ClassJob classJob ) const
{
// 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;
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getExp() const
2017-08-08 13:53:47 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2022-01-27 21:24:54 +01:00
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex;
return m_expArray[ classJobIndex ];
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setExp( uint32_t amount )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2022-01-27 21:24:54 +01:00
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex;
m_expArray[ classJobIndex ] = amount;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::isInCombat() const
2017-08-08 13:53:47 +02:00
{
return m_bInCombat;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setInCombat( bool mode )
2017-08-08 13:53:47 +02:00
{
//m_lastAttack = GetTickCount();
m_bInCombat = mode;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setClassJob( Common::ClassJob classJob )
2017-08-08 13:53:47 +02:00
{
m_class = classJob;
2017-08-08 13:53:47 +02:00
if( getHp() > getMaxHp() )
m_hp = getMaxHp();
2017-08-08 13:53:47 +02:00
if( getMp() > getMaxMp() )
m_mp = getMaxMp();
2017-08-08 13:53:47 +02:00
m_tp = 0;
2017-08-08 13:53:47 +02:00
[3.x] WIP: Initial Action and StatusEffect implementation (#958) * Check statuses to determine valid lut entry * Add duration field to statuses * Rename buildEffects to make more sense * Add basic generic handler for applying statuseffects * Add more modifiers * Add basic modifier impl for Chara * Apply/remove modifiers for statuseffects * Add some example statuses to lut * Fix windows build error * Don't clear tick effect * Add status entry for Maim * Apply status effects properly for self when having a target * Fix hasStatusEffect to prevent duplicates * Basic dot/hot ticks implemented * Update HP on tick effects * Apply effect to correct target * Add method to simplify applying statuses to self * Add job actions for warrior * Add some actions and statuses for war * Add even more modifiers * Add statuseffect cost type * Add option to not send statusremove order * Change delModifier assert to return early instead * Add option for scripts to enable the generic/lut handler * Add enums for common action values * fix indentation * Fix modifier name for Defiance * Remove status tick logging * Move modifiers to statuseffect * Add ParryPercent modifier * Remove wrath when Defiance ends * Apply modifiers in applyStatus * Remove unused method * Persistence for cross-class skills * Add flags to StatusEffects * Some exd struct fixes * Some aoe work * Add flags to lut * Add missing changeclass * Add SET_STATUS_ME to ActionIntegrity * Improve offensive action check * Add flag to overloaded applyStatusEffectSelf * indentation fix * Some calculation work * Null-check ActionResultBuilder --------- Co-authored-by: Lucy <44952533+Skyliegirl33@users.noreply.github.com> Co-authored-by: Mordred <30826167+SapphireMordred@users.noreply.github.com>
2024-06-21 04:27:01 +02:00
Network::Util::Packet::sendChangeClass( *this );
Network::Util::Packet::sendStatusUpdate( *this );
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), ClassJobChange, 4 );
Network::Util::Packet::sendHudParam( *this );
Service< World::Manager::MapMgr >::ref().updateQuests( *this );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setLevel( uint8_t level )
2017-08-08 13:53:47 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2022-01-27 21:24:54 +01:00
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) )->data().WorkIndex;
m_classArray[ classJobIndex ] = level;
2023-03-06 13:27:56 +01:00
calculateStats();
Network::Util::Packet::sendBaseParams( *this );
Network::Util::Packet::sendHudParam( *this );
Network::Util::Packet::sendStatusUpdate( *this );
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), LevelUpEffect, static_cast< uint8_t >( getClass() ), getLevel(), getLevel() - 1 );
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( *this, static_cast< uint32_t >( getClass() ) );
Service< World::Manager::MapMgr >::ref().updateQuests( *this );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setLevelForClass( uint8_t level, Common::ClassJob classjob )
2017-08-09 14:38:46 +02:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2022-01-27 21:24:54 +01:00
uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( classjob ) )->data().WorkIndex;
if( m_classArray[ classJobIndex ] == 0 )
insertDbClass( classJobIndex, level );
m_classArray[ classJobIndex ] = level;
2022-01-14 19:06:23 +01:00
Network::Util::Packet::sendActorControlSelf( *this, getId(), ClassJobUpdate, static_cast< uint8_t >( classjob ), getLevelForClass( classjob ) );
2017-08-09 14:38:46 +02:00
2023-03-06 10:12:29 +01:00
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( *this, static_cast< uint32_t >( classjob ) );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getModelForSlot( Common::GearModelSlot slot )
2017-08-08 13:53:47 +02:00
{
return m_modelEquip[ slot ];
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getModelMainWeapon() const
2017-08-08 13:53:47 +02:00
{
return m_modelMainWeapon;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getModelSubWeapon() const
2017-08-08 13:53:47 +02:00
{
return m_modelSubWeapon;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getModelSystemWeapon() const
2017-08-08 13:53:47 +02:00
{
return m_modelSystemWeapon;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getAetheryteMaskAt( uint8_t index ) const
2017-08-08 13:53:47 +02:00
{
if( index > sizeof( m_aetheryte ) )
return 0;
return m_aetheryte[ index ];
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getBirthDay() const
2017-08-08 13:53:47 +02:00
{
return m_birthDay;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getBirthMonth() const
2017-08-08 13:53:47 +02:00
{
return m_birthMonth;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getGuardianDeity() const
2017-08-08 13:53:47 +02:00
{
return m_guardianDeity;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getLookAt( uint8_t index ) const
2017-08-08 13:53:47 +02:00
{
return m_customize[ index ];
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setLookAt( uint8_t index, uint8_t value )
2017-08-08 13:53:47 +02:00
{
m_customize[ index ] = value;
2017-08-08 13:53:47 +02:00
}
// spawn this player for pTarget
2022-02-05 23:31:35 +01:00
void 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 );
server().queueForPlayer( pTarget->getCharacterId(), spawnPacket );
2017-08-08 13:53:47 +02:00
}
// despawn
2022-02-05 23:31:35 +01:00
void Player::despawn( Entity::PlayerPtr pTarget )
2017-08-08 13:53:47 +02: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
pPlayer->freePlayerSpawnId( getId() );
Network::Util::Packet::sendActorControlSelf( *this, getId(), WarpStart, 4, getId(), 1 );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
GameObjectPtr Player::lookupTargetById( uint64_t targetId )
2017-08-10 22:06:05 +02:00
{
GameObjectPtr targetActor;
auto inRange = getInRangeActors( true );
for( const auto& actor : inRange )
{
if( actor->getId() == targetId )
targetActor = actor;
}
return targetActor;
2017-08-10 22:06:05 +02:00
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getLastDBWrite() const
2017-08-08 13:53:47 +02:00
{
return m_lastDBWrite;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setVoiceId( uint8_t voiceId )
2017-08-08 13:53:47 +02:00
{
m_voice = voiceId;
2017-08-08 13:53:47 +02:00
}
void Player::setGrandCompany( uint8_t gc )
2017-08-08 13:53:47 +02:00
{
m_gc = gc;
if( m_gcRank[ gc ] == 0 )
m_gcRank[ gc ] = 1;
Network::Util::Packet::sendGrandCompany( *this );
2017-08-08 13:53:47 +02:00
}
void Player::setGrandCompanyRankAt( uint8_t index, uint8_t rank )
2017-08-08 13:53:47 +02:00
{
m_gcRank[ index ] = rank;
Network::Util::Packet::sendGrandCompany( *this );
2017-08-08 13:53:47 +02:00
}
const Player::Condition& Player::getConditions() const
2017-08-08 13:53:47 +02:00
{
2023-02-20 15:25:57 +01:00
return m_condition;
2017-08-08 13:53:47 +02:00
}
bool Player::hasCondition( Common::PlayerCondition flag ) const
2017-08-08 13:53:47 +02:00
{
auto iFlag = static_cast< int32_t >( flag );
2017-08-08 13:53:47 +02:00
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( iFlag, value, index );
2017-08-08 13:53:47 +02:00
2023-02-20 15:25:57 +01:00
return ( m_condition[ index ] & value ) != 0;
2017-08-08 13:53:47 +02:00
}
2023-02-20 15:25:57 +01:00
void Player::setCondition( Common::PlayerCondition flag )
2017-08-08 13:53:47 +02:00
{
auto iFlag = static_cast< int32_t >( flag );
2017-08-08 13:53:47 +02:00
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( iFlag, value, index );
2017-08-08 13:53:47 +02:00
2023-02-20 15:25:57 +01:00
m_condition[ index ] |= value;
Network::Util::Packet::sendCondition( *this );
2023-03-06 10:12:29 +01:00
}
void Player::setConditions( const std::vector< Common::PlayerCondition >& flags )
{
for( auto flag : flags )
{
auto iFlag = static_cast< int32_t >( flag );
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( iFlag, value, index );
m_condition[ index ] |= value;
}
Network::Util::Packet::sendCondition( *this );
2017-08-08 13:53:47 +02:00
}
2023-02-20 15:25:57 +01:00
void Player::removeCondition( Common::PlayerCondition flag )
2017-08-08 13:53:47 +02:00
{
if( !hasCondition( flag ) )
return;
2017-08-08 13:53:47 +02:00
auto iFlag = static_cast< int32_t >( flag );
2017-08-08 13:53:47 +02:00
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( iFlag, value, index );
2017-08-08 13:53:47 +02:00
2023-02-20 15:25:57 +01:00
m_condition[ index ] ^= value;
Network::Util::Packet::sendCondition( *this );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::update( uint64_t tickCount )
2017-08-08 13:53:47 +02:00
{
// todo: better way to handle this override chara update
Service< World::Manager::PlayerMgr >::ref().onUpdate( *this, tickCount );
2017-08-08 13:53:47 +02:00
Chara::update( tickCount );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::freePlayerSpawnId( uint32_t actorId )
2017-08-08 13:53:47 +02:00
{
auto spawnId = m_actorSpawnIndexAllocator.freeUsedSpawnIndex( actorId );
2017-08-08 13:53:47 +02:00
// actor was never spawned for this player
if( spawnId == m_actorSpawnIndexAllocator.getAllocFailId() )
return;
Network::Util::Packet::sendDeletePlayer( *this, actorId, spawnId );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::AetheryteList& Player::getAetheryteArray()
2017-08-08 13:53:47 +02:00
{
return m_aetheryte;
2017-08-08 13:53:47 +02:00
}
/*! set homepoint */
2022-02-05 23:31:35 +01:00
void Player::setHomepoint( uint8_t aetheryteId )
2017-08-08 13:53:47 +02:00
{
m_homePoint = aetheryteId;
Network::Util::Packet::sendActorControlSelf( *this, getId(), SetHomepoint, aetheryteId );
2017-08-08 13:53:47 +02:00
}
/*! get homepoint */
2022-02-05 23:31:35 +01:00
uint8_t Player::getHomepoint() const
2017-08-08 13:53:47 +02:00
{
return m_homePoint;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::ClassList& Player::getClassArray()
2017-08-08 13:53:47 +02:00
{
return m_classArray;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::ExpList& Player::getExpArray()
2017-08-08 13:53:47 +02:00
{
return m_expArray;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::HowToList& Player::getHowToArray()
2017-08-08 13:53:47 +02:00
{
return m_howTo;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
const Player::UnlockList& Player::getUnlockBitmask() const
2017-08-08 13:53:47 +02:00
{
return m_unlocks;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
const Player::OrchestrionList& Player::getOrchestrionBitmask() const
2017-10-09 20:09:49 +02:00
{
return m_orchestrion;
2022-01-16 22:48:55 +01:00
}
void Player::setOrchestrionBitmask( const Player::OrchestrionList& orchestrion )
{
m_orchestrion = orchestrion;
}
2022-02-05 23:31:35 +01:00
void Player::unlockMount( uint32_t mountId )
2022-01-16 22:48:55 +01:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
2022-01-27 21:24:54 +01:00
auto mount = exdData.getRow< Excel::Mount >( mountId );
2022-01-16 22:48:55 +01:00
2022-01-16 23:19:10 +01:00
if( mount->data().MountOrder == -1 )
2022-01-16 22:48:55 +01:00
return;
m_mountGuide[ mount->data().MountOrder / 8 ] |= ( 1 << ( mount->data().MountOrder % 8 ) );
Network::Util::Packet::sendActorControlSelf( *this, getId(), SetMountBitmask, mount->data().MountOrder, 1 );
2017-10-09 20:09:49 +02:00
}
2023-01-23 21:39:12 +01:00
void Player::unlockCompanion( uint32_t companionId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto companion = exdData.getRow< Excel::Companion >( companionId );
//if( companion->data(). == -1 )
// return;
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( companionId, value, index );
m_minionGuide[ index ] |= value;
Network::Util::Packet::sendActorControlSelf( *this, getId(), LearnCompanion, companionId, 1 );
2023-01-23 21:39:12 +01:00
}
Player::MinionList& Player::getMinionGuideBitmask()
{
return m_minionGuide;
}
2022-02-05 23:31:35 +01:00
Player::MountList& Player::getMountGuideBitmask()
{
return m_mountGuide;
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getCharacterId() const
2021-08-30 10:16:05 +02:00
{
return m_characterId;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getVoiceId() const
2017-08-08 13:53:47 +02:00
{
return m_voice;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getGc() const
2017-08-08 13:53:47 +02:00
{
return m_gc;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
const std::array< uint8_t, 3 >& Player::getGcRankArray() const
2017-08-08 13:53:47 +02:00
{
return m_gcRank;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
bool Player::isLoadingComplete() const
2017-08-08 13:53:47 +02:00
{
return m_bLoadingComplete;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setLoadingComplete( bool bComplete )
2017-08-08 13:53:47 +02:00
{
m_bLoadingComplete = bComplete;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setSearchInfo( uint8_t selectRegion, uint8_t selectClass, const char* searchMessage )
2017-08-08 13:53:47 +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
}
2022-02-05 23:31:35 +01:00
const char* Player::getSearchMessage() const
2017-08-08 13:53:47 +02:00
{
return &m_searchMessage[ 0 ];
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getSearchSelectRegion() const
2017-08-08 13:53:47 +02:00
{
return m_searchSelectRegion;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getSearchSelectClass() const
2017-08-08 13:53:47 +02:00
{
return m_searchSelectClass;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::updateHowtosSeen( uint32_t howToId )
2017-08-08 13:53:47 +02:00
{
uint8_t index = howToId / 8;
uint8_t bitIndex = howToId % 8;
2017-08-08 13:53:47 +02:00
uint8_t value = 1 << bitIndex;
2017-08-08 13:53:47 +02:00
m_howTo[ index ] |= value;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::initHateSlotQueue()
2017-08-08 13:53:47 +02:00
{
m_freeHateSlotQueue = std::queue< uint8_t >();
for( int32_t i = 1; i < 26; ++i )
m_freeHateSlotQueue.push( i );
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void 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();
m_actorIdTohateSlotMap[ bnpc.getId() ] = hateId;
Network::Util::Packet::sendHateList( *this );
2019-01-19 22:56:07 +01:00
}
}
2022-02-05 23:31:35 +01:00
void Player::hateListRemove( const BNpc& bnpc )
2019-01-19 22:56:07 +01:00
{
auto it = m_actorIdTohateSlotMap.begin();
for( ; it != m_actorIdTohateSlotMap.end(); ++it )
{
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 );
Network::Util::Packet::sendHateList( *this );
2019-01-19 22:56:07 +01:00
return;
}
}
}
2022-02-05 23:31:35 +01:00
bool Player::hateListHasEntry( const BNpc& bnpc )
2019-01-19 22:56:07 +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
}
2022-02-05 23:31:35 +01:00
const std::map< uint32_t, uint8_t >& Player::getActorIdToHateSlotMap()
2017-08-08 13:53:47 +02:00
{
return m_actorIdTohateSlotMap;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::onMobAggro( const BNpc& bnpc )
2019-01-19 22:56:07 +01:00
{
hateListAdd( bnpc );
2023-04-30 19:36:24 +09:00
setCondition( PlayerCondition::InCombat );
Network::Util::Packet::sendActorControl( *this, getId(), SetBattle, 1 );
2019-01-19 22:56:07 +01:00
}
2022-02-05 23:31:35 +01:00
void Player::onMobDeaggro( const BNpc& bnpc )
2019-01-19 22:56:07 +01:00
{
hateListRemove( bnpc );
2019-01-19 22:56:07 +01:00
if( m_actorIdTohateSlotMap.empty() )
2023-04-30 19:36:24 +09:00
{
removeCondition( PlayerCondition::InCombat );
Network::Util::Packet::sendActorControl( *this, getId(), SetBattle, 0 );
2023-04-30 19:36:24 +09:00
}
2019-01-19 22:56:07 +01:00
}
2022-02-05 23:31:35 +01:00
bool Player::isLogin() const
2017-08-08 13:53:47 +02:00
{
return m_bIsLogin;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setIsLogin( bool bIsLogin )
2017-08-08 13:53:47 +02:00
{
m_bIsLogin = bIsLogin;
2017-08-08 13:53:47 +02:00
}
2022-02-05 23:31:35 +01:00
Player::TitleList& Player::getTitleList()
2018-02-17 01:20:40 +01:00
{
return m_titleList;
2018-02-17 01:20:40 +01:00
}
2022-02-05 23:31:35 +01:00
uint16_t Player::getTitle() const
2017-10-09 02:06:31 -03:00
{
return m_activeTitle;
2017-10-09 02:06:31 -03:00
}
2022-02-05 23:31:35 +01:00
void Player::addTitle( uint16_t titleId )
2017-10-09 00:31:31 -03:00
{
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( titleId, value, index );
2017-10-09 00:31:31 -03:00
m_titleList[ index ] |= value;
2017-10-09 00:31:31 -03:00
}
2022-02-05 23:31:35 +01:00
void Player::setTitle( uint16_t titleId )
2017-10-04 23:19:38 -03:00
{
uint16_t index;
uint8_t value;
Util::valueToFlagByteIndexValue( titleId, value, index );
if( ( m_titleList[ index ] & value ) == 0 && titleId != 0 ) // Player doesn't have title and is not "no title" - bail
return;
m_activeTitle = titleId;
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), SetTitle, titleId );
2017-10-04 23:19:38 -03:00
}
2023-01-27 18:54:54 -03:00
const Player::AchievementData& Player::getAchievementData() const
{
return m_achievementData;
}
2023-01-27 18:54:54 -03:00
void Player::setAchievementData( const Player::AchievementData& achievementData )
2023-01-23 22:58:07 -03:00
{
2023-01-27 18:54:54 -03:00
m_achievementData = achievementData;
2023-01-23 22:58:07 -03:00
}
2022-02-05 23:31:35 +01:00
void Player::setMaxGearSets( uint8_t amount )
2022-01-14 19:06:23 +01:00
{
m_equippedMannequin = amount;
Network::Util::Packet::sendActorControlSelf( *this, getId(), SetMaxGearSets, m_equippedMannequin );
2022-01-14 19:06:23 +01:00
}
2023-02-12 23:11:26 +01:00
void Player::addGearSet()
{
uint8_t amount = 1;
if( getMaxGearSets() == 0 )
{
// unlock 5 gearsets the first time
amount = 5;
setRewardFlag( UnlockEntry::GearSets );
}
setMaxGearSets( getMaxGearSets() + amount );
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getMaxGearSets() const
2022-01-14 19:06:23 +01:00
{
return m_equippedMannequin;
}
void Player::setConfigFlags( uint16_t state )
{
m_configFlags = static_cast< uint8_t >( state );
Network::Util::Packet::sendConfigFlags( *this );
}
uint8_t Player::getConfigFlags() const
{
return m_configFlags;
}
2022-02-05 23:31:35 +01:00
void Player::setMount( uint32_t mountId )
{
m_mount = mountId;
Network::Util::Packet::sendMount( *this );
2017-10-18 17:54:17 +02:00
}
2022-02-05 23:31:35 +01:00
void Player::setCompanion( uint8_t id )
{
2023-03-06 10:12:29 +01:00
auto& exdData = Common::Service< Data::ExdData >::ref();
auto companion = exdData.getRow< Excel::Companion >( id );
if( !companion )
return;
m_companionId = id;
2023-03-06 10:12:29 +01:00
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), ToggleCompanion, id );
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getCurrentCompanion() const
{
return m_companionId;
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getCurrentMount() const
2017-10-18 17:54:17 +02:00
{
return m_mount;
}
2022-02-05 23:31:35 +01:00
void Player::setPersistentEmote( uint32_t emoteId )
{
m_emoteMode = emoteId;
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getPersistentEmote() const
{
return m_emoteMode;
}
2022-02-05 23:31:35 +01:00
void Player::autoAttack( CharaPtr pTarget )
2017-08-08 13:53:47 +02:00
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
2023-02-20 11:24:02 +01:00
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref();
auto& exdData = Common::Service< Data::ExdData >::ref();
auto pZone = teriMgr.getTerritoryByGuId( getTerritoryId() );
auto mainWeap = getItemAt( Common::GearSet0, Common::GearSetSlot::MainHand );
pTarget->onActionHostile( getAsChara() );
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() );
actionMgr.handleTargetedAction( *this, 7, pTarget->getId(), 0 );
2023-02-20 11:24:02 +01:00
2017-08-08 13:53:47 +02:00
}
2017-08-14 17:10:19 +02:00
/////////////////////////////
// Content Finder
/////////////////////////////
2022-02-05 23:31:35 +01:00
uint32_t Player::getCFPenaltyTimestamp() const
{
return m_cfPenaltyUntil;
}
2022-02-05 23:31:35 +01:00
void Player::setCFPenaltyTimestamp( uint32_t timestamp )
{
m_cfPenaltyUntil = timestamp;
}
2022-02-05 23:31:35 +01:00
uint32_t Player::getCFPenaltyMinutes() const
{
auto currentTimestamp = Common::Util::getTimeSeconds();
auto endTimestamp = getCFPenaltyTimestamp();
// check if penalty timestamp already passed current time
if( currentTimestamp > endTimestamp )
return 0;
auto deltaTime = endTimestamp - currentTimestamp;
return static_cast< uint32_t > ( std::ceil( static_cast< float > ( deltaTime ) / 60 ) );
}
2022-02-05 23:31:35 +01:00
void Player::setCFPenaltyMinutes( uint32_t minutes )
{
auto currentTimestamp = Common::Util::getTimeSeconds();
setCFPenaltyTimestamp( currentTimestamp + minutes * 60 );
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getOpeningSequence() const
{
return m_openingSequence;
}
2022-02-05 23:31:35 +01:00
void Player::setOpeningSequence( uint8_t seq )
{
m_openingSequence = seq;
}
2022-02-05 23:31:35 +01:00
uint16_t Player::getItemLevel() const
{
return m_itemLevel;
}
/// Tells client to offset their eorzean time by given timestamp.
2022-02-05 23:31:35 +01:00
void Player::setEorzeaTimeOffset( uint64_t timestamp )
{
// TODO: maybe change to persistent?
2019-07-29 22:22:45 +10:00
auto packet = makeZonePacket< FFXIVIpcEorzeaTimeOffset >( getId() );
packet->data().timestamp = timestamp;
// Send to single player
server().queueForPlayer( getCharacterId(), packet );
}
2018-01-28 22:36:43 +01:00
2022-02-05 23:31:35 +01:00
uint32_t Player::getPrevTerritoryTypeId() const
2018-01-28 22:36:43 +01:00
{
return m_prevTerritoryTypeId;
}
2022-02-05 23:31:35 +01:00
void Player::setDirectorInitialized( bool isInitialized )
{
m_directorInitialized = isInitialized;
}
2022-02-05 23:31:35 +01:00
bool Player::isDirectorInitialized() const
{
return m_directorInitialized;
}
2018-02-17 01:20:40 +01:00
void Player::teleportQuery( uint16_t aetheryteId, bool useAetheryteTicket )
2019-02-08 21:20:53 +11:00
{
auto& exdData = Common::Service< Data::ExdData >::ref();
// TODO: only register this action if enough gil is in possession
2022-01-27 21:24:54 +01:00
auto targetAetheryte = exdData.getRow< Excel::Aetheryte >( aetheryteId );
if( !targetAetheryte )
return;
auto fromAetheryte = exdData.getRow< Excel::Aetheryte >( exdData.getRow< Excel::TerritoryType >( getTerritoryTypeId() )->data().Aetheryte );
// calculate cost - does not apply for favorite points or homepoints
// if using aetheryte ticket, cost is 0
2024-05-18 11:37:32 -07:00
auto cost = useAetheryteTicket ? 0 : static_cast< uint16_t > (
( std::sqrt( std::pow( fromAetheryte->data().CostPosX - targetAetheryte->data().CostPosX, 2 ) +
2024-05-18 11:37:32 -07:00
std::pow( fromAetheryte->data().CostPosY - targetAetheryte->data().CostPosY, 2 ) ) / 2 ) + 100 );
// cap at 999 gil
cost = std::min< uint16_t >( 999, cost );
bool insufficientGil = getCurrency( Common::CurrencyType::Gil ) < cost;
Network::Util::Packet::sendActorControlSelf( *this, getId(), OnExecuteTelepo, insufficientGil ? 2 : 0, aetheryteId );
if( !insufficientGil )
{
m_teleportQuery.targetAetheryte = aetheryteId;
m_teleportQuery.cost = cost;
m_teleportQuery.useAetheryteTicket = useAetheryteTicket;
}
else
{
clearTeleportQuery();
}
}
2022-02-05 23:31:35 +01:00
Sapphire::Common::PlayerTeleportQuery Player::getTeleportQuery() const
{
return m_teleportQuery;
}
2022-02-05 23:31:35 +01:00
void Player::clearTeleportQuery()
{
memset( &m_teleportQuery, 0x0, sizeof( Common::PlayerTeleportQuery ) );
2018-02-18 01:50:20 +01:00
}
2022-02-05 23:31:35 +01:00
uint8_t Player::getNextObjSpawnIndexForActorId( uint32_t actorId )
{
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() );
PlayerMgr::sendUrgent( *this, "Failed to spawn EObj#{0} for you - no remaining spawn slots. See world log.", actorId );
return index;
}
return index;
}
2022-02-05 23:31:35 +01:00
void Player::setDyeingInfo( uint32_t itemToDyeContainer, uint32_t itemToDyeSlot, uint32_t dyeBagContainer, uint32_t dyeBagSlot )
{
m_dyeingInfo.itemToDyeContainer = itemToDyeContainer;
m_dyeingInfo.itemToDyeSlot = itemToDyeSlot;
m_dyeingInfo.dyeBagContainer = dyeBagContainer;
m_dyeingInfo.dyeBagSlot = dyeBagSlot;
}
2022-02-05 23:31:35 +01:00
void 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;
2023-03-06 10:12:29 +01:00
setCondition( Common::PlayerCondition::None1 );
2020-03-16 01:35:49 -07:00
auto itemToDye = getItemAt( itemToDyeContainer, itemToDyeSlot );
auto dyeToUse = getItemAt( dyeBagContainer, dyeBagSlot );
if( !itemToDye || !dyeToUse )
return;
if( !removeItem( dyeToUse->getId() ) )
return;
uint32_t stainColorID = dyeToUse->getAdditionalData();
bool shouldDye = stainColorID != 0;
bool invalidateGearSet = stainColorID != itemToDye->getStain();
2020-03-16 01:35:49 -07:00
itemToDye->setStain( stainColorID );
2020-03-16 02:08:35 -07:00
insertInventoryItem( static_cast< Sapphire::Common::InventoryType >( itemToDyeContainer ), static_cast< uint16_t >( itemToDyeSlot ), itemToDye );
writeItem( itemToDye );
Network::Util::Packet::sendActorControlSelf( *this, getId(), DyeMsg, itemToDye->getId(), shouldDye, invalidateGearSet );
}
2023-02-09 20:56:26 +01:00
void Player::setGlamouringInfo( uint32_t itemToGlamourContainer, uint32_t itemToGlamourSlot, uint32_t glamourBagContainer, uint32_t glamourBagSlot, bool shouldGlamour )
{
m_glamouringInfo.itemToGlamourContainer = itemToGlamourContainer;
m_glamouringInfo.itemToGlamourSlot = itemToGlamourSlot;
m_glamouringInfo.glamourBagContainer = glamourBagContainer;
m_glamouringInfo.glamourBagSlot = glamourBagSlot;
m_glamouringInfo.shouldGlamour = shouldGlamour;
}
void Player::glamourItemFromGlamouringInfo()
{
auto& playerMgr = Service< World::Manager::PlayerMgr >::ref();
uint32_t itemToGlamourContainer = m_glamouringInfo.itemToGlamourContainer;
uint32_t itemToGlamourSlot = m_glamouringInfo.itemToGlamourSlot;
uint32_t glamourBagContainer = m_glamouringInfo.glamourBagContainer;
uint32_t glamourBagSlot = m_glamouringInfo.glamourBagSlot;
bool shouldGlamour = m_glamouringInfo.shouldGlamour;
Network::Util::Packet::sendCondition( *this );
2023-02-09 20:56:26 +01:00
auto itemToGlamour = getItemAt( itemToGlamourContainer, itemToGlamourSlot );
auto glamourToUse = getItemAt( glamourBagContainer, glamourBagSlot );
//auto prismToUse = getItemAt( glamourBagContainer, glamourBagSlot );
if( !itemToGlamour )
return;
//if( !removeItem( prismToUse->getId() ) )
// return;
uint32_t patternID = itemToGlamour->getPattern();
bool invalidateGearSet = shouldGlamour ? patternID != glamourToUse->getId() : true;
if( shouldGlamour )
{
itemToGlamour->setPattern( glamourToUse->getId() );
itemToGlamour->setStain( glamourToUse->getStain() );
}
else
{
itemToGlamour->setPattern( 0 );
itemToGlamour->setStain( 0 );
}
itemToGlamour->setGlamModelIds();
insertInventoryItem( static_cast< Sapphire::Common::InventoryType >( itemToGlamourContainer ), static_cast< uint16_t >( itemToGlamourSlot ), itemToGlamour );
writeItem( itemToGlamour );
if( shouldGlamour )
Network::Util::Packet::sendActorControlSelf( *this, getId(), GlamourCastMsg, itemToGlamour->getId(), glamourToUse->getId(), invalidateGearSet );
2023-02-09 20:56:26 +01:00
else
Network::Util::Packet::sendActorControlSelf( *this, getId(), GlamourRemoveMsg, itemToGlamour->getId(), invalidateGearSet );
2023-02-09 20:56:26 +01:00
}
2022-02-05 23:31:35 +01:00
void Player::resetObjSpawnIndex()
{
m_objSpawnIndexAllocator.freeAllSpawnIndexes();
}
2022-02-05 23:31:35 +01:00
void Player::freeObjSpawnIndexForActorId( uint32_t actorId )
{
auto spawnId = m_objSpawnIndexAllocator.freeUsedSpawnIndex( actorId );
2018-03-01 23:17:35 +01:00
// obj was never spawned for this player
if( spawnId == m_objSpawnIndexAllocator.getAllocFailId() )
return;
Network::Util::Packet::sendDeleteObject( *this, spawnId );
}
2022-02-05 23:31:35 +01:00
bool Player::isObjSpawnIndexValid( uint8_t index )
{
return m_objSpawnIndexAllocator.isSpawnIndexValid( index );
2018-02-24 23:53:32 +01:00
}
2022-02-05 23:31:35 +01:00
void Player::setOnEnterEventDone( bool isDone )
2018-02-24 23:53:32 +01:00
{
m_onEnterEventDone = isDone;
2018-02-24 23:53:32 +01:00
}
2022-02-05 23:31:35 +01:00
bool Player::isOnEnterEventDone() const
2018-02-24 23:53:32 +01:00
{
return m_onEnterEventDone;
2018-02-24 23:53:32 +01:00
}
2018-11-07 11:59:59 +01:00
2022-02-05 23:31:35 +01:00
void Player::setLandFlags( uint8_t flagSlot, uint32_t landFlags, Common::LandIdent ident )
2018-11-07 11:59:59 +01:00
{
auto& server = Common::Service< World::WorldServer >::ref();
m_charaLandData[ flagSlot ].landId = ident;
m_charaLandData[ flagSlot ].landId.worldId = static_cast< int16_t >( server.getWorldId() );
2023-01-23 22:58:25 +01:00
m_charaLandData[ flagSlot ].landFlags = landFlags;
2018-11-10 22:04:40 +01:00
}
2018-11-07 11:59:59 +01:00
2022-02-05 23:31:35 +01:00
Sapphire::Common::HuntingLogEntry& Player::getHuntingLogEntry( uint8_t index )
2019-03-26 00:04:27 +01:00
{
assert( index < m_huntingLogEntries.size() );
return m_huntingLogEntries[ index ];
}
2019-03-26 23:08:34 +01:00
2022-02-05 23:31:35 +01:00
void Player::updateHuntingLog( uint16_t id )
{
std::vector< uint32_t > rankRewards{ 2500, 10000, 20000, 30000, 40000 };
const auto maxRank = 4;
auto& pExdData = Common::Service< Data::ExdData >::ref();
2023-01-19 14:34:53 -06:00
// make sure we get the matching base-class if a job is being used
auto classJobInfo = pExdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( getClass() ) );
if( !classJobInfo )
return;
auto currentClassId = classJobInfo->data().MainClass;
auto& logEntry = m_huntingLogEntries[ currentClassId - 1 ];
bool logChanged = false;
bool allSectionsComplete = true;
for( int i = 1; i <= 10; ++i )
{
bool sectionComplete = true;
bool sectionChanged = false;
auto monsterNoteId = static_cast< uint32_t >( classJobInfo->data().MainClass * 10000 + logEntry.rank * 10 + i );
2022-01-27 21:24:54 +01:00
auto note = pExdData.getRow< Excel::MonsterNote >( monsterNoteId );
// for classes that don't have entries, if the first fails the rest will fail
if( !note )
break;
for( auto x = 0; x < 4; ++x )
{
2022-01-27 21:24:54 +01:00
auto note1 = pExdData.getRow< 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 ] )
{
logEntry.entries[ i - 1 ][ x ]++;
Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogEntryUpdate, monsterNoteId, x, logEntry.entries[ i - 1 ][ x ] );
logChanged = true;
sectionChanged = true;
}
2021-12-07 00:51:09 +01:00
if( logEntry.entries[ i - 1 ][ x ] != note->data().NeededKills[ x ] )
sectionComplete = false;
}
if( logChanged && sectionComplete && sectionChanged )
{
Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogSectionFinish, monsterNoteId, i, 0 );
gainExp( note->data().RewardExp );
}
if( !sectionComplete )
{
allSectionsComplete = false;
}
}
if( logChanged && allSectionsComplete )
{
Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogRankFinish, 4 );
gainExp( rankRewards[ logEntry.rank ] );
if( logEntry.rank < 4 )
{
logEntry.rank++;
memset( logEntry.entries, 0, 40 );
Network::Util::Packet::sendActorControlSelf( *this, getId(), HuntingLogRankUnlock, currentClassId, logEntry.rank + 1, 0 );
}
}
if( logChanged )
Network::Util::Packet::sendHuntingLog( *this );
}
2022-02-05 23:31:35 +01:00
void Player::setActiveLand( uint8_t land, uint8_t ward )
{
m_activeLand.plot = land;
m_activeLand.ward = ward;
}
2022-02-05 23:31:35 +01:00
Sapphire::Common::ActiveLand Player::getActiveLand() const
{
return m_activeLand;
}
2020-01-05 17:09:27 +09:00
2022-02-05 23:31:35 +01:00
bool Player::hasQueuedAction() const
2020-01-05 17:09:27 +09:00
{
return m_pQueuedAction != nullptr;
}
2022-02-05 23:31:35 +01:00
void Player::setQueuedAction( Sapphire::World::Action::ActionPtr pAction )
2020-01-05 17:09:27 +09:00
{
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
}
2022-02-05 23:31:35 +01:00
void Player::setLastActionTick( uint64_t tick )
{
m_lastActionTick = tick;
}
2022-02-05 23:31:35 +01:00
uint64_t Player::getLastActionTick() const
{
return m_lastActionTick;
}
2022-02-05 23:31:35 +01:00
void Player::setRecastGroup( uint8_t index, float time )
{
m_recast[ index ] = time;
if( time > m_recastMax[ index ] )
m_recastMax[ index ] = time;
}
2022-02-05 23:31:35 +01:00
float Player::getRecastGroup( uint8_t index ) const
{
return m_recast[ index ];
}
2023-03-06 10:12:29 +01:00
const std::array< float, 80 >& Player::getRecastGroups() const
{
2023-03-06 10:12:29 +01:00
return m_recast;
}
const std::array< float, 80 >& Player::getRecastGroupsMax() const
{
return m_recastMax;
}
2022-02-05 23:31:35 +01:00
void Player::resetRecastGroups()
{
for( size_t i = 0; i < 80; ++i )
{
m_recast[ i ] = 0.0f;
m_recastMax[ i ] = 0.0f;
}
Network::Util::Packet::sendRecastGroups( *this );
}
2022-02-05 23:31:35 +01:00
bool Player::checkAction()
2020-01-05 17:09:27 +09:00
{
if( m_pCurrentAction == nullptr )
return false;
if( m_pCurrentAction->update() )
{
if( m_pCurrentAction->isInterrupted() && m_pCurrentAction->getInterruptType() != Common::ActionInterruptType::DamageInterrupt )
m_pQueuedAction = nullptr;
m_pCurrentAction = nullptr;
if( hasQueuedAction() )
{
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-04-24 19:24:04 +09:00
2022-02-05 23:31:35 +01:00
uint64_t Player::getPartyId() const
{
return m_partyId;
}
2022-02-05 23:31:35 +01:00
void Player::setPartyId( uint64_t partyId )
{
m_partyId = partyId;
}
2023-02-20 11:24:02 +01:00
Player::FriendListIDVec& Player::getFriendListId()
{
return m_friendList;
}
2021-12-13 22:36:29 -03:00
2022-02-05 23:31:35 +01:00
Player::FriendListDataVec& Player::getFriendListData()
{
return m_friendInviteList;
}
2023-02-20 11:24:02 +01:00
Player::FriendListIDVec& Player::getBlacklistId()
2021-12-13 22:36:29 -03:00
{
return m_blacklist;
}
2022-02-05 23:31:35 +01:00
void 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 )
{
2023-02-10 15:21:48 -03:00
// calculate how much damage to deal out (max. 30y : 100%)
float deltaMax = std::min( fallHeight, 30.f );
// get hp percentage starting from 0.1, increasing to 100% at max height
float hpPer = std::min( 0.1f + ( deltaMax - 10.f ) / 20.f, 1.f );
2022-01-27 18:35:19 -03:00
auto damage = static_cast< uint32_t >( 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 );
}
Network::Util::Packet::sendActorControl( getInRangePlayerIds( true ), getId(), SetFallDamage, damage );
// todo: this used to work without refreshing the entire UI state
// does 3.x use some sort of fall integrity?
Network::Util::Packet::sendHudParam( *this );
}
}
}
2022-02-05 23:31:35 +01:00
bool Player::isFalling() const
{
return m_falling;
}
2022-02-05 23:31:35 +01:00
void Player::setLastPcSearchResult( std::vector< uint32_t > result )
{
m_lastPcSearch = std::move( result );
}
2022-02-05 23:31:35 +01:00
std::vector< uint32_t >& Player::getLastPcSearchResult()
2020-04-24 19:24:04 +09:00
{
return m_lastPcSearch;
2020-04-24 19:24:04 +09:00
}
2022-02-05 23:31:35 +01:00
const FFXIVARR_POSITION3& Player::getPrevPos() const
2020-04-24 19:24:04 +09:00
{
return m_prevPos;
}
2020-04-24 19:24:04 +09:00
2022-02-05 23:31:35 +01:00
float Player::getPrevRot() const
{
return m_prevRot;
2020-04-24 19:24:04 +09:00
}
2022-02-05 23:31:35 +01:00
bool Player::isConnected() const
{
return m_bIsConnected;
}
2022-02-05 23:31:35 +01:00
void Player::setConnected( bool isConnected )
{
m_bIsConnected = isConnected;
}
2022-01-30 14:44:17 +01:00
2022-02-05 23:31:35 +01:00
void Player::updatePrevTerritory()
2022-01-30 14:44:17 +01:00
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
2023-01-26 22:39:50 +01:00
if( teriMgr.isDefaultTerritory( getTerritoryTypeId() ) || teriMgr.isHousingTerritory( getTerritoryTypeId() ) )
2022-01-30 14:44:17 +01:00
{
m_prevTerritoryTypeId = getTerritoryTypeId();
m_prevTerritoryId = getTerritoryId();
m_prevPos = m_pos;
m_prevRot = m_rot;
}
}
2023-01-27 11:13:57 +01:00
const CharaLandData& Entity::Player::getCharaLandData( Common::LandFlagsSlot slot ) const
{
return m_charaLandData[ slot ];
}