2017-12-08 15:38:25 +01:00
|
|
|
#include <Server_Common/Common.h>
|
|
|
|
#include <Server_Common/Util/Util.h>
|
|
|
|
#include <Server_Common/Util/UtilMath.h>
|
|
|
|
#include <Server_Common/Config/XMLConfig.h>
|
|
|
|
#include <Server_Common/Network/GamePacket.h>
|
|
|
|
#include <Server_Common/Logging/Logger.h>
|
|
|
|
#include <Server_Common/Exd/ExdData.h>
|
|
|
|
#include <Server_Common/Network/PacketContainer.h>
|
|
|
|
|
|
|
|
#include "Session.h"
|
2017-08-08 13:53:47 +02:00
|
|
|
#include "Player.h"
|
|
|
|
#include "BattleNpc.h"
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
#include "Zone/ZoneMgr.h"
|
|
|
|
#include "Zone/Zone.h"
|
|
|
|
|
|
|
|
#include "ServerZone.h"
|
|
|
|
|
|
|
|
#include "Network/GameConnection.h"
|
|
|
|
#include "Network/PacketWrappers/ActorControlPacket142.h"
|
|
|
|
#include "Network/PacketWrappers/ActorControlPacket143.h"
|
|
|
|
#include "Network/PacketWrappers/InitUIPacket.h"
|
|
|
|
#include "Network/PacketWrappers/ServerNoticePacket.h"
|
|
|
|
#include "Network/PacketWrappers/ChatPacket.h"
|
|
|
|
#include "Network/PacketWrappers/ModelEquipPacket.h"
|
|
|
|
#include "Network/PacketWrappers/ActorSpawnPacket.h"
|
|
|
|
#include "Network/PacketWrappers/UpdateHpMpTpPacket.h"
|
|
|
|
#include "Network/PacketWrappers/PlayerStateFlagsPacket.h"
|
|
|
|
#include "Network/PacketWrappers/PlayerSpawnPacket.h"
|
|
|
|
|
|
|
|
#include "Script/ScriptManager.h"
|
|
|
|
|
|
|
|
#include "Inventory/Item.h"
|
|
|
|
|
|
|
|
#include "Inventory/Inventory.h"
|
|
|
|
#include "Event/Event.h"
|
|
|
|
#include "Action/Action.h"
|
|
|
|
#include "Action/EventAction.h"
|
|
|
|
#include "Action/EventItemAction.h"
|
|
|
|
#include "Zone/ZonePosition.h"
|
|
|
|
#include "Math/CalcStats.h"
|
|
|
|
#include "Math/CalcBattle.h"
|
2017-08-08 13:53:47 +02:00
|
|
|
#include <boost/make_shared.hpp>
|
|
|
|
|
|
|
|
extern Core::Logger g_log;
|
|
|
|
extern Core::ServerZone g_serverZone;
|
|
|
|
extern Core::ZoneMgr g_zoneMgr;
|
|
|
|
extern Core::Data::ExdData g_exdData;
|
|
|
|
extern Core::Scripting::ScriptManager g_scriptMgr;
|
|
|
|
|
|
|
|
using namespace Core::Common;
|
|
|
|
using namespace Core::Network::Packets;
|
|
|
|
using namespace Core::Network::Packets::Server;
|
|
|
|
|
|
|
|
// player constructor
|
|
|
|
Core::Entity::Player::Player() :
|
2017-12-05 11:21:56 +01:00
|
|
|
Actor(),
|
2017-08-08 13:53:47 +02:00
|
|
|
m_lastWrite( 0 ),
|
|
|
|
m_lastPing( 0 ),
|
|
|
|
m_bIsLogin( false ),
|
|
|
|
m_contentId( 0 ),
|
|
|
|
m_modelMainWeapon( 0 ),
|
|
|
|
m_modelSubWeapon( 0 ),
|
|
|
|
m_homePoint( 0 ),
|
|
|
|
m_startTown( 0 ),
|
|
|
|
m_townWarpFstFlags( 0 ),
|
|
|
|
m_playTime( 0 ),
|
|
|
|
m_bInCombat( false ),
|
|
|
|
m_bLoadingComplete( false ),
|
|
|
|
m_bMarkedForZoning( false ),
|
|
|
|
m_zoningType( Common::ZoneingType::None ),
|
2017-11-14 23:55:38 +01:00
|
|
|
m_bAutoattack( false ),
|
2017-11-21 17:39:10 +11:00
|
|
|
m_markedForRemoval( false ),
|
|
|
|
m_mount( 0 )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
m_id = 0;
|
2017-11-26 01:12:26 +01:00
|
|
|
m_objKind = ObjKind::Player;
|
2017-08-08 13:53:47 +02:00
|
|
|
m_currentStance = Stance::Passive;
|
|
|
|
m_onlineStatus = 0;
|
|
|
|
m_queuedZoneing = nullptr;
|
|
|
|
m_status = ActorStatus::Idle;
|
2017-10-01 01:14:43 +02:00
|
|
|
m_invincibilityType = InvincibilityType::InvincibilityNone;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
memset( m_questTracking, 0, sizeof( m_questTracking ) );
|
|
|
|
memset( m_name, 0, sizeof( m_name ) );
|
|
|
|
memset( m_stateFlags, 0, sizeof( m_stateFlags ) );
|
|
|
|
memset( m_searchMessage, 0, sizeof( m_searchMessage ) );
|
2017-10-23 23:55:25 +02:00
|
|
|
memset( m_classArray, 0, sizeof( m_classArray ) );
|
|
|
|
memset( m_expArray, 0, sizeof( m_expArray ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::Player::~Player()
|
|
|
|
{
|
2017-11-14 23:55:38 +01:00
|
|
|
g_log.debug( "PlayerObj destroyed" );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: add a proper calculation based on race / job / level / gear
|
|
|
|
uint32_t Core::Entity::Player::getMaxHp()
|
|
|
|
{
|
|
|
|
return m_baseStats.max_hp;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::Player::getMaxMp()
|
|
|
|
{
|
|
|
|
return m_baseStats.max_mp;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t Core::Entity::Player::getZoneId() const
|
|
|
|
{
|
|
|
|
return m_zoneId;
|
|
|
|
}
|
|
|
|
|
2017-09-11 18:59:50 +02:00
|
|
|
uint8_t Core::Entity::Player::getGmRank() const
|
|
|
|
{
|
|
|
|
return m_gmRank;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setGmRank( uint8_t rank )
|
|
|
|
{
|
|
|
|
m_gmRank = rank;
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
uint8_t Core::Entity::Player::getMode() const
|
|
|
|
{
|
|
|
|
return m_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setMode( uint8_t mode )
|
|
|
|
{
|
|
|
|
m_mode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getStartTown() const
|
|
|
|
{
|
|
|
|
return m_startTown;
|
|
|
|
}
|
|
|
|
|
2017-11-14 23:55:38 +01:00
|
|
|
void Core::Entity::Player::setMarkedForRemoval()
|
|
|
|
{
|
|
|
|
m_markedForRemoval = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isMarkedForRemoval() const
|
|
|
|
{
|
|
|
|
return m_markedForRemoval;
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
Core::Common::OnlineStatus Core::Entity::Player::getOnlineStatus()
|
|
|
|
{
|
|
|
|
uint64_t newMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::NewAdventurer );
|
2017-11-18 01:25:55 +01:00
|
|
|
uint64_t afkMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::AwayfromKeyboard );
|
2017-08-08 13:53:47 +02:00
|
|
|
uint64_t busyMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::Busy );
|
|
|
|
uint64_t dcMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::Disconnected );
|
2017-11-18 01:25:55 +01:00
|
|
|
uint64_t meldMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::LookingtoMeldMateria );
|
|
|
|
uint64_t ptMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::LookingforParty );
|
|
|
|
uint64_t rpMask = uint64_t( 1 ) << static_cast< uint32_t >( OnlineStatus::Roleplaying );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
OnlineStatus status = OnlineStatus::Online;
|
|
|
|
|
|
|
|
//if( hasStateFlag( Common::PlayerStateFlag::NewAdventurer ) )
|
|
|
|
if( m_onlineStatus & newMask )
|
|
|
|
status = OnlineStatus::NewAdventurer;
|
|
|
|
|
|
|
|
if( m_onlineStatus & afkMask )
|
2017-11-18 01:25:55 +01:00
|
|
|
status = OnlineStatus::AwayfromKeyboard;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_onlineStatus & busyMask )
|
|
|
|
status = OnlineStatus::Busy;
|
|
|
|
|
|
|
|
if( m_onlineStatus & dcMask )
|
|
|
|
status = OnlineStatus::Disconnected;
|
|
|
|
|
|
|
|
if( m_onlineStatus & meldMask )
|
2017-11-18 01:25:55 +01:00
|
|
|
status = OnlineStatus::LookingtoMeldMateria;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_onlineStatus & ptMask )
|
2017-11-18 01:25:55 +01:00
|
|
|
status = OnlineStatus::LookingforParty;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-16 22:59:53 +02:00
|
|
|
if( m_onlineStatus & rpMask )
|
2017-11-18 01:25:55 +01:00
|
|
|
status = OnlineStatus::Roleplaying;
|
2017-08-16 22:59:53 +02:00
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
if( hasStateFlag( PlayerStateFlag::WatchingCutscene ) || hasStateFlag( PlayerStateFlag::WatchingCutscene1 ) )
|
2017-11-18 01:25:55 +01:00
|
|
|
status = OnlineStatus::ViewingCutscene;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
// TODO: add all the logic for returning the proper online status, there probably is a better way for this alltogether
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setOnlineStatusMask( uint64_t status )
|
|
|
|
{
|
|
|
|
m_onlineStatus = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Core::Entity::Player::getOnlineStatusMask() const
|
|
|
|
{
|
|
|
|
return m_onlineStatus;
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:08:10 +02:00
|
|
|
void Core::Entity::Player::prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime, uint16_t animation )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcPrepareZoning > preparePacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
preparePacket.data().targetZone = targetZone;
|
2017-08-30 23:08:10 +02:00
|
|
|
preparePacket.data().fadeOutTime = fadeOutTime;
|
|
|
|
preparePacket.data().animation = animation;
|
2017-10-01 18:38:58 +02:00
|
|
|
preparePacket.data().fadeOut = static_cast< uint8_t >( fadeOut ? 1 : 0 );
|
2017-08-08 13:53:47 +02:00
|
|
|
queuePacket( preparePacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::calculateStats()
|
|
|
|
{
|
|
|
|
uint8_t tribe = getLookAt( Common::CharaLook::Tribe );
|
|
|
|
uint8_t level = getLevel();
|
2017-11-18 01:25:55 +01:00
|
|
|
uint8_t job = static_cast< uint8_t >( getClass() );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
auto classInfoIt = g_exdData.m_classJobInfoMap.find( job );
|
|
|
|
auto tribeInfoIt = g_exdData.m_tribeInfoMap.find( tribe );
|
|
|
|
auto paramGrowthInfoIt = g_exdData.m_paramGrowthInfoMap.find( level );
|
|
|
|
|
|
|
|
if( tribeInfoIt == g_exdData.m_tribeInfoMap.end() ||
|
2017-08-19 11:28:04 +09:00
|
|
|
classInfoIt == g_exdData.m_classJobInfoMap.end() ||
|
2017-08-08 13:53:47 +02:00
|
|
|
paramGrowthInfoIt == g_exdData.m_paramGrowthInfoMap.end() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto tribeInfo = tribeInfoIt->second;
|
|
|
|
auto classInfo = classInfoIt->second;
|
|
|
|
auto paramGrowthInfo = paramGrowthInfoIt->second;
|
|
|
|
|
|
|
|
// TODO: put formula somewhere else...
|
2017-11-21 03:19:08 -02:00
|
|
|
float base = Math::CalcStats::calculateBaseStat( getAsPlayer() );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-09-15 00:56:29 -03:00
|
|
|
m_baseStats.str = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_str ) / 100 ) + tribeInfo.mod_str );
|
|
|
|
m_baseStats.dex = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_dex ) / 100 ) + tribeInfo.mod_dex );
|
|
|
|
m_baseStats.vit = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_vit ) / 100 ) + tribeInfo.mod_vit );
|
|
|
|
m_baseStats.inte = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_int ) / 100 ) + tribeInfo.mod_int );
|
|
|
|
m_baseStats.mnd = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_mnd ) / 100 ) + tribeInfo.mod_mnd );
|
|
|
|
m_baseStats.pie = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_pie ) / 100 ) + tribeInfo.mod_pie );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-11-16 00:03:36 -02:00
|
|
|
m_baseStats.skillSpeed = paramGrowthInfo.base_secondary;
|
|
|
|
m_baseStats.spellSpeed = paramGrowthInfo.base_secondary;
|
|
|
|
m_baseStats.accuracy = paramGrowthInfo.base_secondary;
|
|
|
|
m_baseStats.critHitRate = paramGrowthInfo.base_secondary;
|
|
|
|
m_baseStats.attackPotMagic = paramGrowthInfo.base_secondary;
|
2017-08-08 13:53:47 +02:00
|
|
|
m_baseStats.healingPotMagic = paramGrowthInfo.base_secondary;
|
2017-11-16 00:03:36 -02:00
|
|
|
m_baseStats.tenacity = paramGrowthInfo.base_secondary;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-11-21 03:19:08 -02:00
|
|
|
m_baseStats.max_mp = Math::CalcStats::calculateMaxMp( getAsPlayer() );
|
2017-08-19 11:28:04 +09:00
|
|
|
|
2017-11-21 03:19:08 -02:00
|
|
|
m_baseStats.max_hp = Math::CalcStats::calculateMaxHp( getAsPlayer() );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_mp > m_baseStats.max_mp )
|
|
|
|
m_mp = m_baseStats.max_mp;
|
|
|
|
|
|
|
|
if( m_hp > m_baseStats.max_hp )
|
|
|
|
m_hp = m_baseStats.max_hp;
|
|
|
|
|
|
|
|
|
2017-09-15 00:56:29 -03:00
|
|
|
m_baseStats.determination = static_cast< uint32_t >( base );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Core::Entity::Player::setAutoattack(bool mode)
|
|
|
|
{
|
|
|
|
m_bAutoattack = mode;
|
|
|
|
m_lastAttack = Util::getTimeMs();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isAutoattackOn() const
|
|
|
|
{
|
|
|
|
return m_bAutoattack;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendStats()
|
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcPlayerStats > statPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
statPacket.data().strength = m_baseStats.str;
|
|
|
|
statPacket.data().dexterity = m_baseStats.dex;
|
|
|
|
statPacket.data().vitality = m_baseStats.vit;
|
|
|
|
statPacket.data().intelligence = m_baseStats.inte;
|
|
|
|
statPacket.data().mind = m_baseStats.mnd;
|
|
|
|
statPacket.data().piety = m_baseStats.pie;
|
|
|
|
statPacket.data().determination = m_baseStats.determination;
|
|
|
|
statPacket.data().hp = m_baseStats.max_hp;
|
|
|
|
statPacket.data().mp = m_baseStats.max_mp;
|
|
|
|
statPacket.data().accuracy = m_baseStats.accuracy;
|
|
|
|
statPacket.data().attack = m_baseStats.attack;
|
|
|
|
statPacket.data().attackMagicPotency = m_baseStats.attackPotMagic;
|
|
|
|
statPacket.data().healingMagicPotency = m_baseStats.healingPotMagic;
|
|
|
|
statPacket.data().skillSpeed = m_baseStats.skillSpeed;
|
|
|
|
statPacket.data().spellSpeed = m_baseStats.spellSpeed;
|
|
|
|
statPacket.data().spellSpeed1 = m_baseStats.spellSpeed;
|
|
|
|
statPacket.data().spellSpeedMod = 100;
|
|
|
|
|
|
|
|
statPacket.data().criticalHitRate = m_baseStats.spellSpeed;
|
|
|
|
statPacket.data().defense = m_baseStats.spellSpeed;
|
|
|
|
statPacket.data().magicDefense = m_baseStats.spellSpeed;
|
|
|
|
statPacket.data().attack = m_baseStats.spellSpeed;
|
|
|
|
|
|
|
|
queuePacket( statPacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type )
|
|
|
|
{
|
|
|
|
auto data = g_exdData.getAetheryteInfo( aetheryteId );
|
|
|
|
|
2017-09-13 11:46:17 +02:00
|
|
|
if( data == nullptr )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2017-09-13 11:46:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setStateFlag( PlayerStateFlag::BetweenAreas );
|
|
|
|
sendStateFlags();
|
|
|
|
|
|
|
|
auto z_pos = g_zoneMgr.getZonePosition( data->levelId );
|
|
|
|
|
|
|
|
Common::FFXIVARR_POSITION3 pos;
|
|
|
|
pos.x = 0;
|
|
|
|
pos.y = 0;
|
|
|
|
pos.z = 0;
|
|
|
|
float rot = 0;
|
|
|
|
|
|
|
|
if( z_pos != nullptr )
|
|
|
|
{
|
|
|
|
pos = z_pos->getTargetPosition();
|
|
|
|
rot = z_pos->getTargetRotation();
|
|
|
|
}
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-09-13 11:46:17 +02:00
|
|
|
sendDebug( "Teleport: " + data->placename + " " + data->placename_aethernet +
|
|
|
|
"(" + std::to_string( data->levelId ) + ")" );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-09-13 11:46:17 +02:00
|
|
|
// TODO: this should be simplified and a type created in server_common/common.h.
|
|
|
|
if( type == 1 ) // teleport
|
|
|
|
{
|
|
|
|
prepareZoning( data->target_zone, true, 1, 112 );
|
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), ActorDespawnEffect, 0x04 ) );
|
|
|
|
setZoningType( Common::ZoneingType::Teleport );
|
|
|
|
}
|
|
|
|
else if( type == 2 ) // aethernet
|
|
|
|
{
|
|
|
|
prepareZoning( data->target_zone, true, 1, 112 );
|
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), ActorDespawnEffect, 0x04 ) );
|
|
|
|
setZoningType( Common::ZoneingType::Teleport );
|
|
|
|
}
|
|
|
|
else if( type == 3 ) // return
|
|
|
|
{
|
|
|
|
prepareZoning( data->target_zone, true, 1, 111 );
|
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), ActorDespawnEffect, 0x03 ) );
|
|
|
|
setZoningType( Common::ZoneingType::Return );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
2017-09-13 11:46:17 +02:00
|
|
|
|
|
|
|
m_queuedZoneing = boost::make_shared< QueuedZoning >( data->target_zone, pos, Util::getTimeMs(), rot );
|
|
|
|
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::forceZoneing( uint32_t zoneId )
|
|
|
|
{
|
2017-10-19 16:18:16 -07:00
|
|
|
m_queuedZoneing = boost::make_shared< QueuedZoning >( zoneId, getPos(), Util::getTimeMs(), 0.f );
|
2017-08-08 13:53:47 +02:00
|
|
|
//performZoning( zoneId, Common::ZoneingType::None, getPos() );
|
|
|
|
}
|
|
|
|
|
2017-08-10 16:31:48 +02:00
|
|
|
void Core::Entity::Player::returnToHomepoint()
|
|
|
|
{
|
|
|
|
setZoningType( Common::ZoneingType::Return );
|
|
|
|
teleport( getHomepoint(), 3 );
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
void Core::Entity::Player::setZone( uint32_t zoneId )
|
|
|
|
{
|
|
|
|
auto pPlayer = getAsPlayer();
|
|
|
|
|
|
|
|
auto pZone = g_zoneMgr.getZone( zoneId );
|
|
|
|
|
|
|
|
|
|
|
|
if( !pZone /*|| ( ( pZone == m_pCurrentZone ) && m_lastPing )*/ )
|
|
|
|
{
|
|
|
|
g_log.error( "Zone " + std::to_string( zoneId ) + " not found on this server." );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_zoneId = zoneId;
|
|
|
|
|
|
|
|
// mark character as zoning in progress
|
|
|
|
setLoadingComplete( false );
|
|
|
|
|
|
|
|
if( m_lastPing != 0 )
|
|
|
|
m_pCurrentZone->removeActor( shared_from_this() );
|
|
|
|
|
|
|
|
m_pCurrentZone = pZone;
|
|
|
|
m_pCurrentZone->pushActor( shared_from_this() );
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcInit > initPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
initPacket.data().charId = getId();
|
|
|
|
queuePacket( initPacket );
|
|
|
|
|
|
|
|
sendInventory();
|
|
|
|
|
2017-10-05 20:24:58 +02:00
|
|
|
if( isLogin() )
|
|
|
|
{
|
2017-10-06 00:13:29 +02:00
|
|
|
queuePacket(ActorControlPacket143( getId(), SetCharaGearParamUI, m_equipDisplayFlags, 1 ) );
|
2017-10-05 20:24:58 +02:00
|
|
|
}
|
|
|
|
|
2017-08-19 11:28:04 +09:00
|
|
|
// set flags, will be reset automatically by zoning ( only on client side though )
|
2017-08-08 13:53:47 +02:00
|
|
|
pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas );
|
|
|
|
pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas1 );
|
|
|
|
pPlayer->sendStateFlags();
|
|
|
|
|
|
|
|
pPlayer->sendStats();
|
|
|
|
|
|
|
|
// only initialize the UI if the player in fact just logged in.
|
|
|
|
if( isLogin() )
|
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcCFAvailableContents > contentFinderList( getId() );
|
2017-10-01 18:38:58 +02:00
|
|
|
for( auto i = 0; i < sizeof( contentFinderList.data().contents ); i++ )
|
2017-08-10 00:41:37 +09:00
|
|
|
{
|
|
|
|
// unlock all contents for now
|
|
|
|
contentFinderList.data().contents[i] = 0xFF;
|
|
|
|
}
|
|
|
|
queuePacket( contentFinderList );
|
|
|
|
|
2017-12-08 11:46:47 +01:00
|
|
|
Server::InitUIPacket initUIPacket( *pPlayer );
|
2017-08-08 13:53:47 +02:00
|
|
|
queuePacket( initUIPacket );
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcPlayerClassInfo > classInfoPacket( getId() );
|
2017-11-18 01:25:55 +01:00
|
|
|
classInfoPacket.data().classId = static_cast< uint8_t >( getClass() );
|
2017-08-08 13:53:47 +02:00
|
|
|
classInfoPacket.data().unknown = 1;
|
|
|
|
classInfoPacket.data().level = getLevel();
|
|
|
|
classInfoPacket.data().level1 = getLevel();
|
|
|
|
queuePacket( classInfoPacket );
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVGCAffiliation > gcAffPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
gcAffPacket.data().gcId = m_gc;
|
|
|
|
gcAffPacket.data().gcRank[0] = m_gcRank[0];
|
|
|
|
gcAffPacket.data().gcRank[1] = m_gcRank[1];
|
|
|
|
gcAffPacket.data().gcRank[2] = m_gcRank[2];
|
|
|
|
queuePacket( gcAffPacket );
|
2017-11-16 00:03:36 -02:00
|
|
|
|
|
|
|
m_itemLevel = getInventory()->calculateEquippedGearItemLevel();
|
|
|
|
sendItemLevel();
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcInitZone > initZonePacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
initZonePacket.data().zoneId = getCurrentZone()->getLayoutId();
|
|
|
|
initZonePacket.data().weatherId = static_cast< uint8_t >( getCurrentZone()->getCurrentWeather() );
|
2017-08-23 17:39:59 +02:00
|
|
|
initZonePacket.data().bitmask = 0x1;
|
|
|
|
initZonePacket.data().unknown5 = 0x2A;
|
2017-08-08 13:53:47 +02:00
|
|
|
initZonePacket.data().pos.x = getPos().x;
|
|
|
|
initZonePacket.data().pos.y = getPos().y;
|
|
|
|
initZonePacket.data().pos.z = getPos().z;
|
|
|
|
queuePacket( initZonePacket );
|
|
|
|
|
|
|
|
if( isLogin() )
|
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVARR_IPC_UNK322 > unk322( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
queuePacket( unk322 );
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVARR_IPC_UNK320 > unk320( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
queuePacket( unk320 );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( getLastPing() == 0 )
|
|
|
|
sendQuestInfo();
|
|
|
|
|
|
|
|
m_bMarkedForZoning = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::Player::getPlayTime() const
|
|
|
|
{
|
|
|
|
return m_playTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getRace() const
|
|
|
|
{
|
|
|
|
return getLookAt( CharaLook::Race );
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getGender() const
|
|
|
|
{
|
|
|
|
return getLookAt( CharaLook::Gender );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::initSpawnIdQueue()
|
|
|
|
{
|
|
|
|
while( !m_freeSpawnIdQueue.empty() )
|
|
|
|
{
|
|
|
|
m_freeSpawnIdQueue.pop();
|
|
|
|
}
|
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
for( int32_t i = 1; i < MAX_DISPLAYED_ACTORS; i++ )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
m_freeSpawnIdQueue.push( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getSpawnIdForActorId( uint32_t actorId )
|
|
|
|
{
|
|
|
|
if( m_freeSpawnIdQueue.empty() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint8_t spawnId = m_freeSpawnIdQueue.front();
|
|
|
|
m_freeSpawnIdQueue.pop();
|
|
|
|
m_playerIdToSpawnIdMap[actorId] = spawnId;
|
|
|
|
return spawnId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId )
|
|
|
|
{
|
|
|
|
m_playerIdToSpawnIdMap[actorId] = spawnId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::registerAetheryte( uint8_t aetheryteId )
|
|
|
|
{
|
|
|
|
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( aetheryteId, value, index );
|
|
|
|
|
|
|
|
m_aetheryte[index] |= value;
|
|
|
|
queuePacket( ActorControlPacket143( getId(), LearnTeleport, aetheryteId, 1 ) );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isAetheryteRegistered( uint8_t aetheryteId ) const
|
|
|
|
{
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( aetheryteId, value, index );
|
|
|
|
|
2017-09-15 00:56:29 -03:00
|
|
|
return ( m_aetheryte[index] & value ) != 0;
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t * Core::Entity::Player::getDiscoveryBitmask()
|
|
|
|
{
|
|
|
|
return m_discovery;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::discover( int16_t map_id, int16_t sub_id )
|
|
|
|
{
|
|
|
|
// map.exd field 12 -> index in one of the two discovery sections, if field 15 is false, need to use 2nd section
|
|
|
|
// section 1 starts at 4 - 2 bytes each
|
|
|
|
|
2017-08-19 11:28:04 +09:00
|
|
|
// section to starts at 320 - 4 bytes long
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
int32_t offset = 4;
|
|
|
|
|
|
|
|
auto info = g_exdData.m_zoneInfoMap[getCurrentZone()->getId()];
|
|
|
|
if( info.is_two_byte )
|
|
|
|
offset = 4 + 2 * info.discovery_index;
|
|
|
|
else
|
|
|
|
offset = 324 + 4 * info.discovery_index;
|
|
|
|
|
|
|
|
int32_t index = offset + sub_id / 8;
|
|
|
|
uint8_t bitIndex = sub_id % 8;
|
|
|
|
|
|
|
|
uint8_t value = 1 << bitIndex;
|
|
|
|
|
|
|
|
m_discovery[index] |= value;
|
|
|
|
|
|
|
|
uint16_t level = getLevel();
|
|
|
|
|
|
|
|
uint32_t exp = ( g_exdData.m_paramGrowthInfoMap[level].needed_exp * 5 / 100 );
|
|
|
|
|
|
|
|
gainExp( exp );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isNewAdventurer() const
|
|
|
|
{
|
|
|
|
return m_bNewAdventurer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setNewAdventurer( bool state )
|
|
|
|
{
|
2017-08-13 18:28:05 +02:00
|
|
|
//if( !state )
|
|
|
|
//{
|
|
|
|
// unsetStateFlag( PlayerStateFlag::NewAdventurer );
|
|
|
|
//}
|
|
|
|
//else
|
|
|
|
//{
|
|
|
|
// setStateFlag( PlayerStateFlag::NewAdventurer );
|
|
|
|
//}
|
2017-08-08 13:53:47 +02:00
|
|
|
sendStateFlags();
|
|
|
|
m_bNewAdventurer = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::resetDiscovery()
|
|
|
|
{
|
|
|
|
memset( m_discovery, 0, sizeof( m_discovery ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::changePosition( float x, float y, float z, float o )
|
|
|
|
{
|
|
|
|
Common::FFXIVARR_POSITION3 pos;
|
|
|
|
pos.x = x;
|
|
|
|
pos.y = y;
|
|
|
|
pos.z = z;
|
|
|
|
m_queuedZoneing = boost::make_shared<QueuedZoning>( getZoneId(), pos, Util::getTimeMs(), o );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::learnAction( uint8_t actionId )
|
|
|
|
{
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( actionId, value, index );
|
|
|
|
|
|
|
|
m_unlocks[index] |= value;
|
|
|
|
|
|
|
|
queuePacket( ActorControlPacket143( getId(), ToggleActionUnlock, actionId, 1 ) );
|
|
|
|
}
|
|
|
|
|
2017-10-09 20:17:43 +02:00
|
|
|
void Core::Entity::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 );
|
|
|
|
|
|
|
|
m_orchestrion[index] |= value;
|
|
|
|
|
2017-10-09 20:17:43 +02:00
|
|
|
queuePacket( ActorControlPacket143( getId(), ToggleOrchestrionUnlock, songId, 1, itemId ) );
|
2017-10-09 20:09:49 +02:00
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
bool Core::Entity::Player::isActionLearned( uint8_t actionId ) const
|
|
|
|
{
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( actionId, value, index );
|
|
|
|
|
2017-09-15 00:56:29 -03:00
|
|
|
return ( m_unlocks[index] & value ) != 0;
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::gainExp( uint32_t amount )
|
|
|
|
{
|
|
|
|
uint32_t currentExp = getExp();
|
|
|
|
|
|
|
|
uint16_t level = getLevel();
|
|
|
|
|
|
|
|
uint32_t neededExpToLevel = g_exdData.m_paramGrowthInfoMap[level].needed_exp;
|
|
|
|
|
|
|
|
uint32_t neededExpToLevelplus1 = g_exdData.m_paramGrowthInfoMap[level + 1].needed_exp;
|
|
|
|
|
|
|
|
queuePacket( ActorControlPacket143( getId(), GainExpMsg, static_cast< uint8_t >( getClass() ), amount ) );
|
|
|
|
|
2017-08-11 00:58:35 -03:00
|
|
|
if( level >= 70 ) // temporary fix for leveling over levelcap
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), amount ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( currentExp + amount ) >= neededExpToLevel )
|
|
|
|
{
|
|
|
|
// levelup
|
2017-10-01 18:38:58 +02:00
|
|
|
amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelplus1 ?
|
|
|
|
neededExpToLevelplus1 - 1 :
|
|
|
|
( currentExp + amount - neededExpToLevel );
|
2017-08-08 13:53:47 +02:00
|
|
|
setExp( amount );
|
|
|
|
gainLevel();
|
|
|
|
queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), amount ) );
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), currentExp + amount ) );
|
|
|
|
setExp( currentExp + amount );
|
|
|
|
}
|
|
|
|
|
|
|
|
sendStatusUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::gainLevel()
|
|
|
|
{
|
|
|
|
setLevel( getLevel() + 1 );
|
|
|
|
|
|
|
|
calculateStats();
|
|
|
|
sendStats();
|
|
|
|
sendStatusUpdate();
|
|
|
|
|
|
|
|
m_hp = getMaxHp();
|
|
|
|
m_mp = getMaxMp();
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcStatusEffectList > effectListPacket( getId() );
|
2017-11-18 01:25:55 +01:00
|
|
|
effectListPacket.data().classId = static_cast< uint8_t > ( getClass() );
|
|
|
|
effectListPacket.data().classId1 = static_cast< uint8_t > ( getClass() );
|
2017-08-08 13:53:47 +02:00
|
|
|
effectListPacket.data().level = getLevel();
|
|
|
|
effectListPacket.data().current_hp = getMaxHp();
|
|
|
|
effectListPacket.data().current_mp = getMaxMp();
|
|
|
|
effectListPacket.data().currentTp = 1000;
|
|
|
|
effectListPacket.data().max_hp = getMaxHp();
|
|
|
|
effectListPacket.data().max_mp = getMaxMp();
|
|
|
|
sendToInRangeSet( effectListPacket, true );
|
|
|
|
|
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), LevelUpEffect, static_cast< uint8_t >( getClass() ),
|
|
|
|
getLevel(), getLevel() - 1 ), true );
|
|
|
|
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcUpdateClassInfo > classInfoPacket( getId() );
|
2017-11-18 01:25:55 +01:00
|
|
|
classInfoPacket.data().classId = static_cast< uint8_t > ( getClass() );
|
|
|
|
classInfoPacket.data().classId1 = static_cast< uint8_t > ( getClass() );
|
2017-08-08 13:53:47 +02:00
|
|
|
classInfoPacket.data().level = getLevel();
|
|
|
|
classInfoPacket.data().nextLevelIndex = getLevel();
|
|
|
|
classInfoPacket.data().currentExp = getExp();
|
|
|
|
queuePacket( classInfoPacket );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::unlock()
|
|
|
|
{
|
2017-12-08 11:46:47 +01:00
|
|
|
queuePacket( PlayerStateFlagsPacket( *getAsPlayer(), PlayerStateFlagList{} ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendStatusUpdate( bool toSelf )
|
|
|
|
{
|
|
|
|
// CGamePacket* pPE = new CGamePacket(0x140, 0x0128, getId(), getId());
|
|
|
|
|
|
|
|
//pPE->setInt8At(0x20, static_cast<uint8_t>(getClass()));
|
|
|
|
|
|
|
|
// pPE->setInt8At(0x21, getLevel());
|
|
|
|
// pPE->setInt8At(0x22, getLevel());
|
|
|
|
|
|
|
|
// // current exp
|
|
|
|
// pPE->setInt32At(0x28, getExp());
|
|
|
|
|
|
|
|
// // rested exp
|
|
|
|
// //pPE->setInt32At(0x2C, m_hp);
|
|
|
|
|
|
|
|
// pPE->setInt32At(0x24, m_hp);
|
|
|
|
// pPE->setInt32At(0x28, getMaxHp());
|
|
|
|
// pPE->setInt16At(0x2C, m_mp);
|
|
|
|
// pPE->setInt16At(0x2E, getMaxMp());
|
|
|
|
// pPE->setInt16At(0x30, m_tp);
|
|
|
|
|
|
|
|
// sendToInRangeSet(pPE, toSelf);
|
|
|
|
|
|
|
|
sendToInRangeSet( UpdateHpMpTpPacket( shared_from_this() ), true );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getLevel() const
|
|
|
|
{
|
|
|
|
uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx;
|
|
|
|
return static_cast< uint8_t >( m_classArray[classJobIndex] );
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
uint8_t Core::Entity::Player::getLevelForClass( Common::ClassJob pClass ) const
|
2017-08-09 14:38:46 +02:00
|
|
|
{
|
2017-08-10 00:41:37 +09:00
|
|
|
uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( pClass )].exp_idx;
|
|
|
|
return static_cast< uint8_t >( m_classArray[classJobIndex] );
|
2017-08-09 14:38:46 +02:00
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
uint32_t Core::Entity::Player::getExp() const
|
|
|
|
{
|
|
|
|
uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx;
|
|
|
|
return m_expArray[classJobIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setExp( uint32_t amount )
|
|
|
|
{
|
|
|
|
uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( getClass() )].exp_idx;
|
|
|
|
m_expArray[classJobIndex] = amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isInCombat() const
|
|
|
|
{
|
|
|
|
return m_bInCombat;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setInCombat( bool mode )
|
|
|
|
{
|
|
|
|
//m_lastAttack = GetTickCount();
|
|
|
|
m_bInCombat = mode;
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::setClassJob( Common::ClassJob classJob )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
m_class = classJob;
|
|
|
|
uint8_t level = getLevel();
|
|
|
|
|
|
|
|
if( getHp() > getMaxHp() )
|
|
|
|
m_hp = getMaxHp();
|
|
|
|
|
|
|
|
if( getMp() > getMaxMp() )
|
|
|
|
m_mp = getMaxMp();
|
|
|
|
|
|
|
|
m_tp = 0;
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcPlayerClassInfo > classInfoPacket( getId() );
|
2017-11-18 01:25:55 +01:00
|
|
|
classInfoPacket.data().classId = static_cast< uint8_t >( getClass() );
|
2017-08-08 13:53:47 +02:00
|
|
|
classInfoPacket.data().level = getLevel();
|
|
|
|
queuePacket( classInfoPacket );
|
|
|
|
|
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), ClassJobChange, 0x04 ), true );
|
|
|
|
|
|
|
|
sendStatusUpdate( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setLevel( uint8_t level )
|
|
|
|
{
|
2017-11-18 01:25:55 +01:00
|
|
|
uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( static_cast< uint8_t >( getClass() ) )].exp_idx;
|
2017-08-08 13:53:47 +02:00
|
|
|
m_classArray[classJobIndex] = level;
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::setLevelForClass( uint8_t level, Common::ClassJob classjob )
|
2017-08-09 14:38:46 +02:00
|
|
|
{
|
|
|
|
uint8_t classJobIndex = g_exdData.m_classJobInfoMap[static_cast< uint8_t >( classjob )].exp_idx;
|
|
|
|
m_classArray[classJobIndex] = level;
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
void Core::Entity::Player::sendModel()
|
|
|
|
{
|
2017-12-08 11:46:47 +01:00
|
|
|
ModelEquipPacket modelEquip( *getAsPlayer() );
|
2017-08-08 13:53:47 +02:00
|
|
|
sendToInRangeSet( modelEquip, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::Player::getModelForSlot( Inventory::EquipSlot slot )
|
|
|
|
{
|
|
|
|
return m_modelEquip[slot];
|
|
|
|
}
|
|
|
|
|
2017-10-02 16:00:26 +02:00
|
|
|
void Core::Entity::Player::setModelForSlot( Inventory::EquipSlot slot, uint32_t val )
|
|
|
|
{
|
|
|
|
m_modelEquip[slot] = val;
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
uint64_t Core::Entity::Player::getModelMainWeapon() const
|
|
|
|
{
|
|
|
|
return m_modelMainWeapon;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Core::Entity::Player::getModelSubWeapon() const
|
|
|
|
{
|
|
|
|
return m_modelSubWeapon;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Core::Entity::Player::getModelSystemWeapon() const
|
|
|
|
{
|
|
|
|
return m_modelSystemWeapon;
|
|
|
|
}
|
|
|
|
|
|
|
|
int8_t Core::Entity::Player::getAetheryteMaskAt( uint8_t index ) const
|
|
|
|
{
|
2017-12-07 23:59:03 +01:00
|
|
|
if( index > sizeof( m_aetheryte ) )
|
2017-08-08 13:53:47 +02:00
|
|
|
return 0;
|
|
|
|
return m_aetheryte[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getBirthDay() const
|
|
|
|
{
|
|
|
|
return m_birthDay;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getBirthMonth() const
|
|
|
|
{
|
|
|
|
return m_birthMonth;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getGuardianDeity() const
|
|
|
|
{
|
|
|
|
return m_guardianDeity;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getLookAt( uint8_t index ) const
|
|
|
|
{
|
|
|
|
return m_customize[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setLookAt( uint8_t index, uint8_t value )
|
|
|
|
{
|
|
|
|
m_customize[index] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// spawn this player for pTarget
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::spawn( Entity::PlayerPtr pTarget )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
g_log.debug( "[" + std::to_string( pTarget->getId() ) + "] Spawning " +
|
|
|
|
getName() + " for " +
|
|
|
|
pTarget->getName() );
|
|
|
|
|
2017-12-08 11:46:47 +01:00
|
|
|
PlayerSpawnPacket spawnActor( *getAsPlayer(), *pTarget );
|
2017-08-08 13:53:47 +02:00
|
|
|
pTarget->queuePacket( spawnActor );
|
|
|
|
}
|
|
|
|
|
|
|
|
// despawn
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::despawn( Entity::ActorPtr pTarget )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
auto pPlayer = pTarget->getAsPlayer();
|
|
|
|
|
|
|
|
pPlayer->freePlayerSpawnId( getId() );
|
|
|
|
|
|
|
|
pPlayer->queuePacket( ActorControlPacket143( getId(), DespawnZoneScreenMsg, 0x04, getId(), 0x01 ) );
|
|
|
|
}
|
|
|
|
|
2017-08-10 22:06:05 +02:00
|
|
|
Core::Entity::ActorPtr Core::Entity::Player::lookupTargetById( uint64_t targetId )
|
|
|
|
{
|
2017-12-08 15:38:25 +01:00
|
|
|
ActorPtr targetActor;
|
2017-08-10 22:06:05 +02:00
|
|
|
auto inRange = getInRangeActors( true );
|
|
|
|
for( auto actor : inRange )
|
|
|
|
{
|
|
|
|
if( actor->getId() == targetId )
|
|
|
|
targetActor = actor;
|
|
|
|
}
|
|
|
|
return targetActor;
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
void Core::Entity::Player::setLastPing( uint32_t ping )
|
|
|
|
{
|
|
|
|
m_lastPing = ping;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::Player::getLastPing() const
|
|
|
|
{
|
|
|
|
return m_lastPing;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setVoiceId( uint8_t voiceId )
|
|
|
|
{
|
|
|
|
m_voice = voiceId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setGc( uint8_t gc )
|
|
|
|
{
|
|
|
|
m_gc = gc;
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVGCAffiliation > gcAffPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
gcAffPacket.data().gcId = m_gc;
|
|
|
|
gcAffPacket.data().gcRank[0] = m_gcRank[0];
|
|
|
|
gcAffPacket.data().gcRank[1] = m_gcRank[1];
|
|
|
|
gcAffPacket.data().gcRank[2] = m_gcRank[2];
|
|
|
|
queuePacket( gcAffPacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setGcRankAt( uint8_t index, uint8_t rank )
|
|
|
|
{
|
|
|
|
m_gcRank[index] = rank;
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVGCAffiliation > gcAffPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
gcAffPacket.data().gcId = m_gc;
|
|
|
|
gcAffPacket.data().gcRank[0] = m_gcRank[0];
|
|
|
|
gcAffPacket.data().gcRank[1] = m_gcRank[1];
|
|
|
|
gcAffPacket.data().gcRank[2] = m_gcRank[2];
|
|
|
|
queuePacket( gcAffPacket );
|
|
|
|
}
|
|
|
|
|
2017-10-01 18:38:58 +02:00
|
|
|
const uint8_t* Core::Entity::Player::getStateFlags() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_stateFlags;
|
|
|
|
}
|
|
|
|
|
2017-08-10 22:06:05 +02:00
|
|
|
bool Core::Entity::Player::actionHasCastTime( uint32_t actionId ) //TODO: Add logic for special cases
|
|
|
|
{
|
2017-09-18 19:07:41 -03:00
|
|
|
auto actionInfoPtr = g_exdData.getActionInfo( actionId );
|
|
|
|
if( actionInfoPtr->is_instant )
|
2017-08-15 16:19:55 +02:00
|
|
|
return false;
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
return actionInfoPtr->cast_time != 0;
|
2017-08-15 16:19:55 +02:00
|
|
|
|
2017-08-10 22:06:05 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
bool Core::Entity::Player::hasStateFlag( Common::PlayerStateFlag flag ) const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t iFlag = static_cast< uint32_t >( flag );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( iFlag, value, index );
|
|
|
|
|
2017-09-15 00:56:29 -03:00
|
|
|
return ( m_stateFlags[index] & value ) != 0;
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t iFlag = static_cast< uint32_t >( flag );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( iFlag, value, index );
|
|
|
|
|
|
|
|
m_stateFlags[index] |= value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-08-13 18:28:05 +02:00
|
|
|
void Core::Entity::Player::setStateFlags( std::vector< Common::PlayerStateFlag > flags )
|
|
|
|
{
|
|
|
|
for( const auto& flag : flags )
|
|
|
|
{
|
|
|
|
int iFlag = static_cast< uint32_t >( flag );
|
|
|
|
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( iFlag, value, index );
|
|
|
|
|
|
|
|
m_stateFlags[index] |= value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
void Core::Entity::Player::sendStateFlags()
|
|
|
|
{
|
2017-12-08 11:46:47 +01:00
|
|
|
queuePacket( PlayerStateFlagsPacket( *getAsPlayer() ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
if( !hasStateFlag( flag ) )
|
|
|
|
return;
|
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t iFlag = static_cast< uint32_t >( flag );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( iFlag, value, index );
|
|
|
|
|
|
|
|
m_stateFlags[index] ^= value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::update( int64_t currTime )
|
|
|
|
{
|
|
|
|
|
|
|
|
// a zoning is pending, lets do it
|
|
|
|
if( m_queuedZoneing && ( currTime - m_queuedZoneing->m_queueTime ) > 800 )
|
|
|
|
{
|
|
|
|
Common::FFXIVARR_POSITION3 targetPos = m_queuedZoneing->m_targetPosition;
|
|
|
|
if( getCurrentZone()->getId() != m_queuedZoneing->m_targetZone )
|
|
|
|
{
|
2017-08-13 23:39:04 +02:00
|
|
|
performZoning( m_queuedZoneing->m_targetZone, targetPos, m_queuedZoneing->m_targetRotation);
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcActorSetPos > setActorPosPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
setActorPosPacket.data().r16 = Math::Util::floatToUInt16Rot( m_queuedZoneing->m_targetRotation );
|
|
|
|
setActorPosPacket.data().waitForLoad = 0x04;
|
|
|
|
setActorPosPacket.data().x = targetPos.x;
|
|
|
|
setActorPosPacket.data().y = targetPos.y;
|
|
|
|
setActorPosPacket.data().z = targetPos.z;
|
|
|
|
sendToInRangeSet( setActorPosPacket, true );
|
|
|
|
setPosition( targetPos );
|
|
|
|
}
|
|
|
|
m_queuedZoneing.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_hp <= 0 && m_status != ActorStatus::Dead )
|
|
|
|
die();
|
|
|
|
|
|
|
|
if( !isAlive() )
|
|
|
|
return;
|
|
|
|
|
2017-12-05 11:21:56 +01:00
|
|
|
updateStatusEffects();
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
m_lastUpdate = currTime;
|
|
|
|
|
|
|
|
if( !checkAction() )
|
|
|
|
{
|
2017-10-01 18:38:58 +02:00
|
|
|
if( m_targetId && m_currentStance == Entity::Actor::Stance::Active && isAutoattackOn() )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
auto mainWeap = m_pInventory->getItemAt( Inventory::GearSet0, Inventory::EquipSlot::MainHand );
|
|
|
|
|
2017-10-01 18:38:58 +02:00
|
|
|
// @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need...
|
2017-08-08 13:53:47 +02:00
|
|
|
for( auto actor : m_inRangeActors )
|
|
|
|
{
|
2017-10-01 18:38:58 +02:00
|
|
|
if( actor->getId() == m_targetId && actor->isAlive() && mainWeap )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
// default autoattack range
|
|
|
|
// TODO make this dependant on bnpc size
|
|
|
|
uint32_t range = 7;
|
|
|
|
|
|
|
|
// default autoattack range for ranged classes
|
2017-11-18 01:25:55 +01:00
|
|
|
if( getClass() == ClassJob::Machinist ||
|
|
|
|
getClass() == ClassJob::Bard ||
|
|
|
|
getClass() == ClassJob::Archer )
|
2017-08-08 13:53:47 +02:00
|
|
|
range = 25;
|
|
|
|
|
|
|
|
|
|
|
|
if( Math::Util::distance(getPos().x, getPos().y, getPos().z,
|
2017-12-07 23:59:03 +01:00
|
|
|
actor->getPos().x, actor->getPos().y, actor->getPos().z) <= range )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
if( ( currTime - m_lastAttack ) > mainWeap->getDelay() )
|
|
|
|
{
|
|
|
|
m_lastAttack = currTime;
|
|
|
|
autoAttack( actor );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ( currTime - m_lastTickTime ) > 3000 )
|
|
|
|
{
|
|
|
|
// add 3 seconds to total play time
|
|
|
|
m_playTime += 3;
|
|
|
|
m_lastTickTime = currTime;
|
|
|
|
onTick();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::onMobKill( uint16_t nameId )
|
|
|
|
{
|
2017-12-08 11:46:47 +01:00
|
|
|
g_scriptMgr.onMobKill( *getAsPlayer(), nameId );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::freePlayerSpawnId( uint32_t actorId )
|
|
|
|
{
|
|
|
|
uint8_t spawnId = m_playerIdToSpawnIdMap[actorId];
|
|
|
|
m_playerIdToSpawnIdMap.erase( actorId );
|
|
|
|
m_freeSpawnIdQueue.push( spawnId );
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcActorFreeSpawn > freeActorSpawnPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
freeActorSpawnPacket.data().actorId = actorId;
|
|
|
|
freeActorSpawnPacket.data().spawnId = spawnId;
|
|
|
|
queuePacket( freeActorSpawnPacket );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t * Core::Entity::Player::getAetheryteArray()
|
|
|
|
{
|
|
|
|
return m_aetheryte;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! set homepoint */
|
|
|
|
void Core::Entity::Player::setHomepoint( uint8_t aetheryteId )
|
|
|
|
{
|
|
|
|
m_homePoint = aetheryteId;
|
|
|
|
|
|
|
|
queuePacket( ActorControlPacket143( getId(), SetHomepoint, aetheryteId ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! get homepoint */
|
|
|
|
uint8_t Core::Entity::Player::getHomepoint() const
|
|
|
|
{
|
|
|
|
return m_homePoint;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
uint16_t* Core::Entity::Player::getClassArray()
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_classArray;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint16_t* Core::Entity::Player::getClassArray() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_classArray;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint8_t* Core::Entity::Player::getLookArray() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_customize;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint32_t* Core::Entity::Player::getModelArray() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_modelEquip;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
uint32_t* Core::Entity::Player::getExpArray()
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_expArray;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint32_t* Core::Entity::Player::getExpArray() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_expArray;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
uint8_t* Core::Entity::Player::getHowToArray()
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_howTo;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint8_t* Core::Entity::Player::getHowToArray() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_howTo;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint8_t* Core::Entity::Player::getUnlockBitmask() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_unlocks;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint8_t* Core::Entity::Player::getOrchestrionBitmask() const
|
2017-10-09 20:09:49 +02:00
|
|
|
{
|
|
|
|
return m_orchestrion;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint8_t* Core::Entity::Player::getMountGuideBitmask() const
|
2017-12-03 17:24:11 +01:00
|
|
|
{
|
|
|
|
return m_mountGuide;
|
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
uint64_t Core::Entity::Player::getContentId() const
|
|
|
|
{
|
|
|
|
return m_contentId;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getVoiceId() const
|
|
|
|
{
|
|
|
|
return m_voice;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getGc() const
|
|
|
|
{
|
|
|
|
return m_gc;
|
|
|
|
}
|
|
|
|
|
2017-12-07 23:59:03 +01:00
|
|
|
const uint8_t* Core::Entity::Player::getGcRankArray() const
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
return m_gcRank;
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::queuePacket( Network::Packets::GamePacketPtr pPacket )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
auto pSession = g_serverZone.getSession( m_id );
|
|
|
|
|
2017-11-28 00:09:36 +01:00
|
|
|
if( !pSession )
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto pZoneCon = pSession->getZoneConnection();
|
|
|
|
|
|
|
|
if( pZoneCon )
|
|
|
|
pZoneCon->queueOutPacket( pPacket );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::queueChatPacket( Network::Packets::GamePacketPtr pPacket )
|
2017-08-22 23:53:20 +02:00
|
|
|
{
|
|
|
|
auto pSession = g_serverZone.getSession( m_id );
|
|
|
|
|
2017-11-28 00:09:36 +01:00
|
|
|
if( !pSession )
|
|
|
|
return;
|
2017-08-22 23:53:20 +02:00
|
|
|
|
2017-11-28 00:09:36 +01:00
|
|
|
auto pChatCon = pSession->getChatConnection();
|
|
|
|
|
|
|
|
if( pChatCon )
|
|
|
|
pChatCon->queueOutPacket( pPacket );
|
2017-08-22 23:53:20 +02:00
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
bool Core::Entity::Player::isLoadingComplete() const
|
|
|
|
{
|
|
|
|
return m_bLoadingComplete;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setLoadingComplete( bool bComplete )
|
|
|
|
{
|
|
|
|
m_bLoadingComplete = bComplete;
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::performZoning( uint16_t zoneId, const Common::FFXIVARR_POSITION3 &pos, float rotation )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
m_pos = pos;
|
|
|
|
m_zoneId = zoneId;
|
|
|
|
m_bMarkedForZoning = true;
|
|
|
|
setRotation( rotation );
|
|
|
|
setZone( zoneId );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isMarkedForZoning() const
|
|
|
|
{
|
|
|
|
return m_bMarkedForZoning;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZoneingType Core::Entity::Player::getZoningType() const
|
|
|
|
{
|
|
|
|
return m_zoningType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setZoningType( Common::ZoneingType zoneingType )
|
|
|
|
{
|
|
|
|
m_zoningType = zoneingType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setSearchInfo( uint8_t selectRegion, uint8_t selectClass, const char* searchMessage )
|
|
|
|
{
|
|
|
|
m_searchSelectRegion = selectRegion;
|
|
|
|
m_searchSelectClass = selectClass;
|
|
|
|
memset( &m_searchMessage[0], 0, sizeof( searchMessage ) );
|
|
|
|
strcpy( &m_searchMessage[0], searchMessage );
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* Core::Entity::Player::getSearchMessage() const
|
|
|
|
{
|
|
|
|
return &m_searchMessage[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getSearchSelectRegion() const
|
|
|
|
{
|
|
|
|
return m_searchSelectRegion;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getSearchSelectClass() const
|
|
|
|
{
|
|
|
|
return m_searchSelectClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendNotice( const std::string& message ) //Purple Text
|
|
|
|
{
|
|
|
|
queuePacket( ServerNoticePacket( getId(), message ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendUrgent( const std::string& message ) //Red Text
|
|
|
|
{
|
2017-12-08 11:46:47 +01:00
|
|
|
queuePacket( ChatPacket( *getAsPlayer(), ChatType::ServerUrgent, message ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendDebug( const std::string& message ) //Grey Text
|
|
|
|
{
|
2017-12-08 11:46:47 +01:00
|
|
|
queuePacket( ChatPacket( *getAsPlayer(), ChatType::ServerDebug, message ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::updateHowtosSeen( uint32_t howToId )
|
|
|
|
{
|
|
|
|
uint8_t index = howToId / 8;
|
|
|
|
uint8_t bitIndex = howToId % 8;
|
|
|
|
|
|
|
|
uint8_t value = 1 << bitIndex;
|
|
|
|
|
|
|
|
m_howTo[index] |= value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::onMobAggro( BattleNpcPtr pBNpc )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
hateListAdd( pBNpc );
|
|
|
|
|
|
|
|
queuePacket( ActorControlPacket142( getId(), ToggleAggro, 1 ) );
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::onMobDeaggro( BattleNpcPtr pBNpc )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
hateListRemove( pBNpc );
|
|
|
|
|
|
|
|
if( m_actorIdTohateSlotMap.empty() )
|
|
|
|
queuePacket( ActorControlPacket142( getId(), ToggleAggro ) );
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::hateListAdd( BattleNpcPtr pBNpc )
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
{
|
2017-08-13 23:39:04 +02:00
|
|
|
if( m_freeHateSlotQueue.empty() )
|
|
|
|
return;
|
|
|
|
uint8_t hateId = m_freeHateSlotQueue.front();
|
|
|
|
m_freeHateSlotQueue.pop();
|
|
|
|
m_actorIdTohateSlotMap[pBNpc->getId()] = hateId;
|
|
|
|
sendHateList();
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
void Core::Entity::Player::hateListRemove( BattleNpcPtr pBNpc )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
auto it = m_actorIdTohateSlotMap.begin();
|
|
|
|
for( ; it != m_actorIdTohateSlotMap.end(); ++it )
|
|
|
|
{
|
|
|
|
if( it->first == pBNpc->getId() )
|
|
|
|
{
|
|
|
|
uint8_t hateSlot = it->second;
|
|
|
|
m_freeHateSlotQueue.push( hateSlot );
|
|
|
|
m_actorIdTohateSlotMap.erase( it );
|
|
|
|
sendHateList();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
bool Core::Entity::Player::hateListHasMob( BattleNpcPtr pBNpc )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
auto it = m_actorIdTohateSlotMap.begin();
|
|
|
|
for( ; it != m_actorIdTohateSlotMap.end(); ++it )
|
|
|
|
{
|
|
|
|
if( it->first == pBNpc->getId() )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::initHateSlotQueue()
|
|
|
|
{
|
|
|
|
m_freeHateSlotQueue = std::queue< uint8_t >();
|
2017-08-11 22:56:30 +01:00
|
|
|
for( int32_t i = 1; i < 26; i++ )
|
2017-08-08 13:53:47 +02:00
|
|
|
m_freeHateSlotQueue.push( i );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::sendHateList()
|
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcHateList > hateListPacket( getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
hateListPacket.data().numEntries = m_actorIdTohateSlotMap.size();
|
|
|
|
auto it = m_actorIdTohateSlotMap.begin();
|
2017-08-11 22:56:30 +01:00
|
|
|
for( int32_t i = 0; it != m_actorIdTohateSlotMap.end(); ++it, i++ )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
hateListPacket.data().entry[i].actorId = it->first;
|
|
|
|
hateListPacket.data().entry[i].hatePercent = 100;
|
|
|
|
}
|
|
|
|
queuePacket( hateListPacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::Player::isLogin() const
|
|
|
|
{
|
|
|
|
return m_bIsLogin;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setIsLogin( bool bIsLogin )
|
|
|
|
{
|
|
|
|
m_bIsLogin = bIsLogin;
|
|
|
|
}
|
|
|
|
|
2017-12-08 15:38:25 +01:00
|
|
|
uint8_t* Core::Entity::Player::getTitleList()
|
2017-10-09 00:31:31 -03:00
|
|
|
{
|
|
|
|
return m_titleList;
|
|
|
|
}
|
|
|
|
|
2017-10-09 14:13:23 +02:00
|
|
|
uint16_t Core::Entity::Player::getTitle() const
|
2017-10-09 02:06:31 -03:00
|
|
|
{
|
2017-11-02 17:54:10 -02:00
|
|
|
return m_activeTitle;
|
2017-10-09 02:06:31 -03:00
|
|
|
}
|
|
|
|
|
2017-10-09 00:31:31 -03:00
|
|
|
void Core::Entity::Player::addTitle( uint16_t titleId )
|
|
|
|
{
|
2017-10-09 01:56:47 -03:00
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( titleId, value, index );
|
2017-10-09 00:31:31 -03:00
|
|
|
|
2017-10-09 01:56:47 -03:00
|
|
|
m_titleList[index] |= value;
|
2017-10-09 00:31:31 -03:00
|
|
|
}
|
|
|
|
|
2017-10-04 23:30:45 -03:00
|
|
|
void Core::Entity::Player::setTitle( uint16_t titleId )
|
2017-10-04 23:19:38 -03:00
|
|
|
{
|
2017-10-09 01:56:47 -03:00
|
|
|
uint16_t index;
|
|
|
|
uint8_t value;
|
|
|
|
Util::valueToFlagByteIndexValue( titleId, value, index );
|
|
|
|
|
|
|
|
if ( ( m_titleList[index] & value ) == 0 ) // Player doesn't have title - bail
|
|
|
|
return;
|
|
|
|
|
2017-11-02 17:54:10 -02:00
|
|
|
m_activeTitle = titleId;
|
2017-10-09 01:56:47 -03:00
|
|
|
|
2017-10-04 23:19:38 -03:00
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true );
|
|
|
|
}
|
|
|
|
|
2017-10-06 00:13:29 +02:00
|
|
|
void Core::Entity::Player::setEquipDisplayFlags( uint8_t state )
|
2017-10-05 20:24:58 +02:00
|
|
|
{
|
2017-10-06 00:13:29 +02:00
|
|
|
m_equipDisplayFlags = state;
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcEquipDisplayFlags > paramPacket( getId() );
|
2017-10-06 00:13:29 +02:00
|
|
|
paramPacket.data().bitmask = m_equipDisplayFlags;
|
2017-10-05 20:24:58 +02:00
|
|
|
sendToInRangeSet( paramPacket, true );
|
|
|
|
}
|
|
|
|
|
2017-10-06 00:13:29 +02:00
|
|
|
uint8_t Core::Entity::Player::getEquipDisplayFlags() const
|
2017-10-05 20:24:58 +02:00
|
|
|
{
|
2017-10-06 00:13:29 +02:00
|
|
|
return m_equipDisplayFlags;
|
2017-10-05 20:24:58 +02:00
|
|
|
}
|
|
|
|
|
2017-10-17 21:31:00 +02:00
|
|
|
void Core::Entity::Player::mount( uint32_t id )
|
|
|
|
{
|
2017-11-21 17:39:10 +11:00
|
|
|
m_mount = id;
|
2017-10-18 17:54:17 +02:00
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Mounted )), true );
|
|
|
|
sendToInRangeSet( ActorControlPacket143( getId(), 0x39e, 12 ), true ); //?
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcMount > mountPacket( getId() );
|
2017-11-21 17:39:10 +11:00
|
|
|
mountPacket.data().id = id;
|
|
|
|
sendToInRangeSet( mountPacket, true );
|
2017-10-18 17:54:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::dismount()
|
|
|
|
{
|
2017-11-28 00:09:36 +01:00
|
|
|
sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus,
|
|
|
|
static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle )), true );
|
2017-10-18 17:54:17 +02:00
|
|
|
sendToInRangeSet( ActorControlPacket143( getId(), ActorControlType::Dismount, 1 ), true );
|
2017-11-21 17:39:10 +11:00
|
|
|
m_mount = 0;
|
2017-10-18 17:54:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getCurrentMount() const
|
|
|
|
{
|
2017-11-21 17:39:10 +11:00
|
|
|
return m_mount;
|
2017-10-17 21:31:00 +02:00
|
|
|
}
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
void Core::Entity::Player::autoAttack( ActorPtr pTarget )
|
|
|
|
{
|
|
|
|
|
2017-11-28 00:09:36 +01:00
|
|
|
auto mainWeap = m_pInventory->getItemAt( Inventory::GearSet0,
|
|
|
|
Inventory::EquipSlot::MainHand );
|
2017-08-11 00:58:35 -03:00
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
pTarget->onActionHostile( shared_from_this() );
|
|
|
|
//uint64_t tick = Util::getTimeMs();
|
|
|
|
//srand(static_cast< uint32_t >(tick));
|
2017-08-11 00:58:35 -03:00
|
|
|
|
2017-09-15 00:56:29 -03:00
|
|
|
uint32_t damage = static_cast< uint32_t >( mainWeap->getAutoAttackDmg() );
|
2017-08-08 13:53:47 +02:00
|
|
|
uint32_t variation = 0 + rand() % 3;
|
|
|
|
|
2017-11-28 00:09:36 +01:00
|
|
|
if( getClass() == ClassJob::Machinist ||
|
|
|
|
getClass() == ClassJob::Bard ||
|
|
|
|
getClass() == ClassJob::Archer )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcEffect > effectPacket(getId());
|
2017-08-08 13:53:47 +02:00
|
|
|
effectPacket.data().targetId = pTarget->getId();
|
|
|
|
effectPacket.data().actionAnimationId = 8;
|
|
|
|
// effectPacket.data().unknown_2 = variation;
|
|
|
|
effectPacket.data().numEffects = 1;
|
|
|
|
effectPacket.data().unknown_61 = 1;
|
|
|
|
effectPacket.data().unknown_62 = 1;
|
|
|
|
effectPacket.data().actionTextId = 8;
|
|
|
|
effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation());
|
|
|
|
effectPacket.data().effectTargetId = pTarget->getId();
|
|
|
|
effectPacket.data().effectTarget = pTarget->getId();
|
2017-09-19 00:09:37 -03:00
|
|
|
effectPacket.data().effects[0].value = damage;
|
|
|
|
effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage;
|
|
|
|
effectPacket.data().effects[0].hitSeverity = Common::ActionHitSeverityType::NormalDamage;
|
2017-08-08 13:53:47 +02:00
|
|
|
effectPacket.data().effects[0].unknown_3 = 7;
|
|
|
|
|
|
|
|
sendToInRangeSet(effectPacket, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcEffect > effectPacket(getId());
|
2017-08-08 13:53:47 +02:00
|
|
|
effectPacket.data().targetId = pTarget->getId();
|
|
|
|
effectPacket.data().actionAnimationId = 7;
|
2017-11-28 00:09:36 +01:00
|
|
|
// effectPacket.data().unknown_2 = variation;
|
2017-08-08 13:53:47 +02:00
|
|
|
effectPacket.data().numEffects = 1;
|
|
|
|
effectPacket.data().unknown_61 = 1;
|
|
|
|
effectPacket.data().unknown_62 = 1;
|
|
|
|
effectPacket.data().actionTextId = 7;
|
|
|
|
effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation());
|
|
|
|
effectPacket.data().effectTarget = pTarget->getId();
|
2017-09-19 00:09:37 -03:00
|
|
|
effectPacket.data().effects[0].value = damage;
|
|
|
|
effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage;
|
|
|
|
effectPacket.data().effects[0].hitSeverity = Common::ActionHitSeverityType::NormalDamage;
|
2017-08-08 13:53:47 +02:00
|
|
|
effectPacket.data().effects[0].unknown_3 = 71;
|
|
|
|
|
|
|
|
sendToInRangeSet(effectPacket, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
pTarget->takeDamage(damage);
|
2017-08-19 11:28:04 +09:00
|
|
|
|
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
|
|
|
|
/////////////////////////////
|
|
|
|
uint32_t Core::Entity::Player::getCFPenaltyTimestamp() const
|
|
|
|
{
|
|
|
|
return m_cfPenaltyUntil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setCFPenaltyTimestamp( uint32_t timestamp )
|
|
|
|
{
|
|
|
|
m_cfPenaltyUntil = timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::Player::getCFPenaltyMinutes() const
|
|
|
|
{
|
|
|
|
auto currentTimestamp = Core::Util::getTimeSeconds();
|
|
|
|
auto endTimestamp = getCFPenaltyTimestamp();
|
|
|
|
|
|
|
|
// check if penalty timestamp already passed current time
|
2017-11-28 00:09:36 +01:00
|
|
|
if( currentTimestamp > endTimestamp )
|
2017-08-19 11:28:04 +09:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
auto deltaTime = endTimestamp - currentTimestamp;
|
|
|
|
return static_cast< uint32_t > ( ceil( static_cast< float > (deltaTime) / 60 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setCFPenaltyMinutes( uint32_t minutes )
|
|
|
|
{
|
|
|
|
auto currentTimestamp = Core::Util::getTimeSeconds();
|
2017-11-28 00:09:36 +01:00
|
|
|
setCFPenaltyTimestamp( static_cast< uint32_t >( currentTimestamp + minutes * 60 ) );
|
2017-08-19 11:28:04 +09:00
|
|
|
}
|
2017-08-20 02:24:19 +02:00
|
|
|
|
|
|
|
uint8_t Core::Entity::Player::getOpeningSequence() const
|
|
|
|
{
|
|
|
|
return m_openingSequence;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::Player::setOpeningSequence( uint8_t seq )
|
|
|
|
{
|
|
|
|
m_openingSequence = seq;
|
2017-09-22 22:03:57 +09:00
|
|
|
}
|
|
|
|
|
2017-11-16 00:03:36 -02:00
|
|
|
uint16_t Core::Entity::Player::getItemLevel() const
|
|
|
|
{
|
|
|
|
return m_itemLevel;
|
|
|
|
}
|
|
|
|
|
2017-09-22 22:03:57 +09:00
|
|
|
/// Tells client to offset their eorzean time by given timestamp.
|
|
|
|
void Core::Entity::Player::setEorzeaTimeOffset( uint64_t timestamp )
|
|
|
|
{
|
|
|
|
// TODO: maybe change to persistent?
|
2017-11-21 18:43:09 +01:00
|
|
|
ZoneChannelPacket< FFXIVIpcEorzeaTimeOffset > packet ( getId() );
|
2017-09-22 22:03:57 +09:00
|
|
|
packet.data().timestamp = timestamp;
|
|
|
|
|
|
|
|
// Send to single player
|
|
|
|
queuePacket( packet );
|
|
|
|
}
|