1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-03 09:17:47 +00:00

Merge pull request #886 from hkAlice/teletubbies-vacuum-cleaner

[3.x] refactor player/playermgr; cleanups;
This commit is contained in:
Mordred 2023-02-10 21:15:25 +01:00 committed by GitHub
commit 7886e7d727
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 414 additions and 285 deletions

View file

@ -587,7 +587,7 @@ namespace Sapphire::Common
WaterCluster = 0x12
};
enum struct ZoneingType : uint8_t
enum struct ZoningType : uint8_t
{
None = 1,
Teleport = 2,

View file

@ -2,6 +2,8 @@
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/Action.h>
#include <Manager/PlayerMgr.h>
#include <Service.h>
class ActionReturn6 :
public Sapphire::ScriptAPI::ActionScript
@ -17,7 +19,9 @@ public:
if( !action.getSourceChara()->isPlayer() )
return;
action.getSourceChara()->getAsPlayer()->teleport( action.getSourceChara()->getAsPlayer()->getHomepoint(), 3 );
auto pPlayer = action.getSourceChara()->getAsPlayer();
warpMgr().requestPlayerTeleport( *pPlayer, pPlayer->getHomepoint(), 3 );
}
};

View file

@ -16,26 +16,27 @@ public:
void onExecute( Sapphire::World::Action::Action& action ) override
{
auto player = action.getSourceChara()->getAsPlayer();
auto pPlayer = action.getSourceChara()->getAsPlayer();
if( !player )
if( !pPlayer )
return;
auto teleportQuery = player->getTeleportQuery();
auto teleportQuery = pPlayer->getTeleportQuery();
if( player->getCurrency( Common::CurrencyType::Gil ) < teleportQuery.cost ||
if( pPlayer->getCurrency( Common::CurrencyType::Gil ) < teleportQuery.cost ||
teleportQuery.targetAetheryte == 0 )
{
action.interrupt();
return;
}
player->removeCurrency( Common::CurrencyType::Gil, teleportQuery.cost );
pPlayer->removeCurrency( Common::CurrencyType::Gil, teleportQuery.cost );
player->setZoningType( Common::ZoneingType::Teleport );
player->teleport( teleportQuery.targetAetheryte );
pPlayer->setZoningType( Common::ZoningType::Teleport );
player->clearTeleportQuery();
warpMgr().requestPlayerTeleport( *pPlayer, teleportQuery.targetAetheryte, 1 );
pPlayer->clearTeleportQuery();
}
};

View file

@ -35,7 +35,7 @@ public:
auto destination = result.getResult( 0 );
if( result.numOfResults == 1 && destination != 0 )
{
player.teleport( destination, 2 );
warpMgr().requestPlayerTeleport( player, destination, 2 );
}
} );
}
@ -73,7 +73,7 @@ public:
auto data = result.getResult( 1 );
if( cmd == 4 && data != 0 )
{
player.teleport( data, 2 );
warpMgr().requestPlayerTeleport( player, data, 2 );
}
else if( cmd == 2 ) // register favored destination
{

View file

@ -101,8 +101,8 @@ private:
void Scene00001Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
player.setGc( OrderOfTwinAdder );
player.setGcRankAt( OrderOfTwinAdder, 1 );
playerMgr().onSetGc( player, OrderOfTwinAdder );
playerMgr().onSetGcRank( player, OrderOfTwinAdder, 1 );
Scene00002( quest, player );
}

View file

@ -396,7 +396,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 3, 0 );
quest.setSeq( Seq5 );
player.setMount( Mount0 );
playerMgr().onMountUpdate( player, Mount0 );
}
//////////////////////////////////////////////////////////////////////
@ -490,7 +490,7 @@ private:
void Scene00017Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
player.setMount( Mount0 );
playerMgr().onMountUpdate( player, Mount0 );
}
//////////////////////////////////////////////////////////////////////
@ -562,7 +562,7 @@ private:
void Scene00023Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
player.setMount( Mount0 );
playerMgr().onMountUpdate( player, Mount0 );
}
//////////////////////////////////////////////////////////////////////
@ -611,7 +611,7 @@ private:
{
eventMgr().sendEventNotice( player, getId(), 6, 0 );
quest.setSeq( Seq8 );
player.setMount( 0 );
playerMgr().onMountUpdate( player, 0 );
}
}
@ -646,7 +646,7 @@ private:
void Scene00030Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result )
{
player.setMount( Mount0 );
playerMgr().onMountUpdate( player, Mount0 );
}
//////////////////////////////////////////////////////////////////////

View file

@ -320,7 +320,7 @@ void Action::Action::start()
if( player )
{
player->setStateFlag( PlayerStateFlag::Casting );
Service< World::Manager::PlayerMgr >::ref().onSendStateFlags( *player, PlayerStateFlag::Casting );
}
}
@ -368,14 +368,14 @@ void Action::Action::interrupt()
// things that aren't players don't care about cooldowns and state flags
if( m_pSource->isPlayer() )
{
auto player = m_pSource->getAsPlayer();
auto pPlayer = m_pSource->getAsPlayer();
// todo: reset cooldown for actual player
// reset state flag
//player->unsetStateFlag( PlayerStateFlag::Occupied1 );
player->setLastActionTick( 0 );
player->unsetStateFlag( PlayerStateFlag::Casting );
pPlayer->setLastActionTick( 0 );
Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( *pPlayer, PlayerStateFlag::Casting );
}
if( hasCastTime() )
@ -424,10 +424,10 @@ void Action::Action::execute()
0x219, m_id, m_id, m_id, m_id );
m_pSource->sendToInRangeSet( control, true );*/
if( auto player = m_pSource->getAsPlayer() )
if( auto pPlayer = m_pSource->getAsPlayer(); pPlayer )
{
player->setLastActionTick( 0 );
player->unsetStateFlag( PlayerStateFlag::Casting );
pPlayer->setLastActionTick( 0 );
Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( *pPlayer, PlayerStateFlag::Casting );
}
}

View file

@ -2,6 +2,9 @@
#include <Util/Util.h>
#include <Service.h>
#include <Manager/PlayerMgr.h>
#include "Actor/Chara.h"
#include "Actor/Player.h"
@ -133,7 +136,7 @@ void EffectResult::execute()
case Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT:
{
auto pPlayer = m_target->getAsPlayer();
pPlayer->setMount( m_value );
Common::Service< World::Manager::PlayerMgr >::ref().onMountUpdate( *pPlayer, m_value );
break;
}

View file

@ -4,9 +4,12 @@
#include <Exd/Structs.h>
#include <Network/CommonActorControl.h>
#include <Manager/PlayerMgr.h>
#include <Manager/EventMgr.h>
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Network/PacketWrappers/ActorControlSelfPacket.h"
#include <Network/PacketWrappers/ActorControlPacket.h>
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
#include <Network/CommonActorControl.h>
#include "Actor/Player.h"
@ -14,7 +17,6 @@
#include "WorldServer.h"
#include "Session.h"
#include "Network/GameConnection.h"
#include "Manager/EventMgr.h"
#include "EventAction.h"
@ -54,9 +56,11 @@ void Action::EventAction::start()
if( m_pSource->isPlayer() )
{
auto pPlayer = m_pSource->getAsPlayer();
m_pSource->sendToInRangeSet( control, true );
if( m_pSource->getAsPlayer()->hasStateFlag( PlayerStateFlag::InNpcEvent ) )
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::InNpcEvent );
if( pPlayer->hasStateFlag( PlayerStateFlag::InNpcEvent ) )
Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( *pPlayer, PlayerStateFlag::InNpcEvent );
}
else
m_pSource->sendToInRangeSet( control );

View file

@ -2,17 +2,19 @@
#include <Exd/ExdData.h>
#include "Actor/Player.h"
#include <Actor/Player.h>
#include <Manager/PlayerMgr.h>
#include <Network/GameConnection.h>
#include <Network/CommonActorControl.h>
#include "Network/PacketWrappers/ActorControlSelfPacket.h"
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
#include <Service.h>
#include <Util/UtilMath.h>
#include "WorldServer.h"
#include "Session.h"
#include "Network/GameConnection.h"
using namespace Sapphire;
using namespace Sapphire::Network::Packets;
@ -59,20 +61,19 @@ void MountAction::start()
m_pSource->sendToInRangeSet( castPacket, true );
player->setStateFlag( Common::PlayerStateFlag::Casting );
Common::Service< World::Manager::PlayerMgr >::ref().onSetStateFlag( *player, Common::PlayerStateFlag::Casting );
auto actionStartPkt = makeActorControlSelf( m_pSource->getId(), ActorControlType::ActionStart, 1, getId(), m_recastTimeMs / 10 );
auto& server = Common::Service< World::WorldServer >::ref();
server.queueForPlayer( m_pSource->getAsPlayer()->getCharacterId(), actionStartPkt );
}
void MountAction::execute()
{
assert( m_pSource );
m_pSource->getAsPlayer()->unsetStateFlag( Common::PlayerStateFlag::Casting );
Common::Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( *m_pSource->getAsPlayer(), Common::PlayerStateFlag::Casting );
m_effectBuilder->mount( m_pSource, m_mountId );
m_effectBuilder->buildAndSendPackets( { m_pSource } );
}

View file

@ -27,8 +27,8 @@ namespace Sapphire::Entity
using ActorStatsArray = std::array< uint32_t, STAT_ARRAY_SIZE >;
ActorStatsArray m_baseStats;
ActorStatsArray m_bonusStats;
ActorStatsArray m_baseStats{ 0 };
ActorStatsArray m_bonusStats{ 0 };
protected:
char m_name[34];

View file

@ -70,7 +70,7 @@ Player::Player() :
m_lastActionTick( 0 ),
m_bInCombat( false ),
m_bLoadingComplete( false ),
m_zoningType( Common::ZoneingType::None ),
m_zoningType( Common::ZoningType::None ),
m_bAutoattack( false ),
m_markedForRemoval( false ),
m_mount( 0 ),
@ -317,6 +317,8 @@ void Player::removeOnlineStatus( const std::vector< Common::OnlineStatus >& stat
void Player::calculateStats()
{
calculateBonusStats();
uint8_t tribe = getLookAt( Common::CharaLook::Tribe );
uint8_t level = getLevel();
auto job = static_cast< uint8_t >( getClass() );
@ -403,72 +405,6 @@ void Player::sendStats()
Service< World::Manager::PlayerMgr >::ref().onSendStats( *this );
}
void Player::teleport( uint16_t aetheryteId, uint8_t type )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto& teriMgr = Common::Service< TerritoryMgr >::ref();
auto& warpMgr = Common::Service< WarpMgr >::ref();
auto aetherData = exdData.getRow< Excel::Aetheryte >( aetheryteId );
if( !aetherData )
return;
const auto& data = aetherData->data();
auto& instanceObjectCache = Common::Service< InstanceObjectCache >::ref();
auto pop = instanceObjectCache.getPopRangeInfo( data.PopRange[ 0 ] );
Common::FFXIVARR_POSITION3 pos{ 0.f, 0.f, 0.f };
float rot = 0.f;
if( pop )
{
PlayerMgr::sendDebug( *this, "Teleport: popRange {0} found!", data.PopRange[ 0 ] );
pos = pop->m_pos;
rot = pop->m_rotation;
}
else
{
PlayerMgr::sendDebug( *this, "Teleport: popRange {0} not found in {1}!", data.PopRange[ 0 ], data.TerritoryType );
}
auto townPlace = exdData.getRow< Excel::PlaceName >( data.TelepoName );
auto aetherytePlace = exdData.getRow< Excel::PlaceName >( data.TransferName );
PlayerMgr::sendDebug( *this, "Teleport: {0} - {1} ({2})",
townPlace->getString( townPlace->data().Text.SGL ),
aetherytePlace->getString( aetherytePlace->data().Text.SGL ),
data.TerritoryType );
// if it is a teleport in the same zone, we want to do warp instead of moveTerri
bool sameTerritory = getTerritoryTypeId() == data.TerritoryType;
WarpType warpType = WarpType::WARP_TYPE_NORMAL;
// TODO: this should be simplified and a type created in server_common/common.h.
if( type == 1 || type == 2 ) // teleport
{
warpType = WarpType::WARP_TYPE_TELEPO;
setZoningType( Common::ZoneingType::Teleport );
}
else if( type == 3 ) // return
{
warpType = WarpType::WARP_TYPE_HOME_POINT;
setZoningType( Common::ZoneingType::Return );
}
if( sameTerritory )
warpMgr.requestWarp( *this, warpType, pos, rot );
else
{
auto pTeri = teriMgr.getZoneByTerritoryTypeId( data.TerritoryType );
if( !pTeri )
return;
warpMgr.requestMoveTerritory( *this, warpType, pTeri->getGuId(), pos, rot );
}
}
void Player::forceZoneing( uint32_t zoneId )
{
auto& teriMgr = Common::Service< TerritoryMgr >::ref();
@ -670,8 +606,6 @@ void Player::learnSong( uint8_t songId, uint32_t itemId )
Util::valueToFlagByteIndexValue( songId, value, index );
m_orchestrion[ index ] |= value;
Service< World::Manager::PlayerMgr >::ref().onUnlockOrchestrion( *this, songId, itemId );
}
bool Player::hasReward( Common::UnlockEntry unlockId ) const
@ -735,6 +669,7 @@ void Player::levelUp()
void Player::sendStatusUpdate()
{
// todo: overrides are funky
Service< World::Manager::PlayerMgr >::ref().onPlayerHpMpTpChanged( *this );
}
@ -926,15 +861,11 @@ void Player::setVoiceId( uint8_t voiceId )
void Player::setGc( uint8_t gc )
{
m_gc = gc;
Service< World::Manager::PlayerMgr >::ref().onGcUpdate( *this );
}
void Player::setGcRankAt( uint8_t index, uint8_t rank )
{
m_gcRank[ index ] = rank;
Service< World::Manager::PlayerMgr >::ref().onGcUpdate( *this );
}
const Player::StateFlags& Player::getStateFlags() const
@ -955,7 +886,6 @@ bool Player::hasStateFlag( Common::PlayerStateFlag flag ) const
void Player::setStateFlag( Common::PlayerStateFlag flag )
{
auto prevOnlineStatus = getOnlineStatus();
auto iFlag = static_cast< int32_t >( flag );
uint16_t index;
@ -963,22 +893,6 @@ void Player::setStateFlag( Common::PlayerStateFlag flag )
Util::valueToFlagByteIndexValue( iFlag, value, index );
m_stateFlags[ index ] |= value;
auto newOnlineStatus = getOnlineStatus();
sendStateFlags( prevOnlineStatus != newOnlineStatus );
}
void Player::setStateFlags( std::vector< Common::PlayerStateFlag > flags )
{
for( const auto& flag : flags )
{
setStateFlag( flag );
}
}
void Player::sendStateFlags( bool updateInRange )
{
Service< World::Manager::PlayerMgr >::ref().onSendStateFlags( *this, updateInRange );
}
void Player::unsetStateFlag( Common::PlayerStateFlag flag )
@ -986,8 +900,6 @@ void Player::unsetStateFlag( Common::PlayerStateFlag flag )
if( !hasStateFlag( flag ) )
return;
auto prevOnlineStatus = getOnlineStatus();
auto iFlag = static_cast< int32_t >( flag );
uint16_t index;
@ -995,60 +907,21 @@ void Player::unsetStateFlag( Common::PlayerStateFlag flag )
Util::valueToFlagByteIndexValue( iFlag, value, index );
m_stateFlags[ index ] ^= value;
auto newOnlineStatus = getOnlineStatus();
sendStateFlags( prevOnlineStatus != newOnlineStatus );
}
void Player::update( uint64_t tickCount )
{
if( m_hp <= 0 && m_status != ActorStatus::Dead )
{
die();
Service< World::Manager::PlayerMgr >::ref().onDeath( *this );
}
if( !isAlive() )
return;
m_lastUpdate = tickCount;
if( !checkAction() )
{
if( m_targetId && m_currentStance == Common::Stance::Active && isAutoattackOn() )
{
auto mainWeap = getItemAt( Common::GearSet0, Common::GearSetSlot::MainHand );
// @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need...
for( const auto& actor : m_inRangeActor )
{
if( actor->getId() == m_targetId && actor->getAsChara()->isAlive() && mainWeap )
{
auto chara = actor->getAsChara();
// default autoattack range
float range = 3.f + chara->getRadius() + getRadius() * 0.5f;
// default autoattack range for ranged classes
if( getClass() == ClassJob::Machinist || getClass() == ClassJob::Bard || getClass() == ClassJob::Archer )
range = 25.f + chara->getRadius() + getRadius() * 0.5f;
if( Util::distance( getPos(), actor->getPos() ) <= range )
{
if( ( tickCount - m_lastAttack ) > mainWeap->getDelay() )
{
m_lastAttack = tickCount;
autoAttack( actor->getAsChara() );
}
}
}
}
}
}
// todo: better way to handle this override chara update
Service< World::Manager::PlayerMgr >::ref().onUpdate( *this, tickCount );
Chara::update( tickCount );
}
uint64_t Player::getLastAttack() const
{
return m_lastAttack;
}
void Player::setLastAttack( uint64_t tickCount )
{
m_lastAttack = tickCount;
@ -1112,6 +985,11 @@ const Player::OrchestrionList& Player::getOrchestrionBitmask() const
return m_orchestrion;
}
void Player::setOrchestrionBitmask( const Player::OrchestrionList& orchestrion )
{
m_orchestrion = orchestrion;
}
void Player::unlockMount( uint32_t mountId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
@ -1189,12 +1067,12 @@ void Player::setLoadingComplete( bool bComplete )
m_bLoadingComplete = bComplete;
}
ZoneingType Player::getZoningType() const
ZoningType Player::getZoningType() const
{
return m_zoningType;
}
void Player::setZoningType( Common::ZoneingType zoneingType )
void Player::setZoningType( Common::ZoningType zoneingType )
{
m_zoningType = zoneingType;
}
@ -1373,21 +1251,11 @@ uint8_t Player::getEquipDisplayFlags() const
void Player::setMount( uint32_t mountId )
{
m_mount = mountId;
Service< World::Manager::PlayerMgr >::ref().onMountUpdate( *this, m_mount );
}
void Player::setCompanion( uint8_t id )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto companion = exdData.getRow< Excel::Companion >( id );
if( !id )
return;
m_companionId = id;
Service< World::Manager::PlayerMgr >::ref().onCompanionUpdate( *this, m_companionId );
}
uint8_t Player::getCurrentCompanion() const
@ -1617,7 +1485,8 @@ void Player::dyeItemFromDyeingInfo()
uint32_t dyeBagContainer = m_dyeingInfo.dyeBagContainer;
uint32_t dyeBagSlot = m_dyeingInfo.dyeBagSlot;
sendStateFlags(); // Retail sends all 0s to unlock player after a dye? Possibly not setting a flag when the action is started in the backend..?
Service< World::Manager::PlayerMgr >::ref().onSendStateFlags( *this, true ); // Retail sends all 0s to unlock player after a dye? Possibly not setting a flag when the action is started in the backend..?
auto itemToDye = getItemAt( itemToDyeContainer, itemToDyeSlot );
auto dyeToUse = getItemAt( dyeBagContainer, dyeBagSlot );
@ -1936,7 +1805,7 @@ void Player::setFalling( bool state, const Common::FFXIVARR_POSITION3& pos, bool
// if we've hit the breakpoint in fall damage (min: 10y)
if( fallHeight >= 10.f )
{
// calculate how much damage to deal out (max. 20y : 100%)
// 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

View file

@ -83,6 +83,10 @@ namespace Sapphire::Entity
/*! Event called on every session iteration */
void update( uint64_t tickCount ) override;
/*! get last attack tick for player */
uint64_t getLastAttack() const;
/*! set last attack tick for player */
void setLastAttack( uint64_t tickCount );
// Quest
@ -343,9 +347,6 @@ namespace Sapphire::Entity
uint64_t getFullOnlineStatusMask() const;
/*! perform a teleport of a specified type ( teleport,return,aethernet ) */
void teleport( uint16_t aetheryteId, uint8_t type = 1 );
/*! query teleport of a specified type */
void teleportQuery( uint16_t aetheryteId );
@ -418,7 +419,7 @@ namespace Sapphire::Entity
/*! check if aetheryte is already registered */
bool isAetheryteRegistered( uint8_t aetheryteId ) const;
/*! return a const pointer to the aetheryte unlock bitmask array */
/*! return aetheryte mask */
uint8_t getAetheryteMaskAt( uint8_t index ) const;
/*! return a pointer to the aetheryte unlock bitmask array */
@ -433,13 +434,13 @@ namespace Sapphire::Entity
/*! discover subarea subid fo map map_id, also send udpate packet */
void discover( int16_t map_id, int16_t sub_id );
/*! return a pointer to the discovery bitmask array */
/*! return a reference to the discovery bitmask array */
Discovery& getDiscoveryBitmask();
/*! helper/debug function to reset all discovered areas */
void resetDiscovery();
/*! get a pointer to the howto bitmask array */
/*! get a reference to the howto bitmask array */
HowToList& getHowToArray();
/*! update bitmask for how-to's seen */
@ -454,22 +455,25 @@ namespace Sapphire::Entity
/*! check if an action is already unlocked in the bitmask. */
bool hasReward( Common::UnlockEntry unlockId ) const;
/*! return a const pointer to the unlock bitmask array */
/*! return a const reference to the unlock bitmask array */
const UnlockList& getUnlockBitmask() const;
/*! return a const pointer to the orchestrion bitmask array */
/*! return a const reference to the orchestrion bitmask array */
const OrchestrionList& getOrchestrionBitmask() const;
/*! set orchestrion bitmask array */
void setOrchestrionBitmask( const OrchestrionList& orchestrion );
/*! unlock a mount */
void unlockMount( uint32_t mountId );
/*! unlock a companion */
void unlockCompanion( uint32_t companionId );
/*! return a const pointer to the minion guide bitmask array */
/*! return a reference to the minion guide bitmask array */
MinionList& getMinionGuideBitmask();
/*! return a const pointer to the setMount guide bitmask array */
/*! return a reference to the setMount guide bitmask array */
MountList& getMountGuideBitmask();
bool checkAction() override;
@ -567,9 +571,6 @@ namespace Sapphire::Entity
/*! send current models ( equipment ) */
void sendModel();
/*! send active state flags */
void sendStateFlags( bool updateInRange = true );
/*! send status update */
void sendStatusUpdate() override;
@ -582,9 +583,9 @@ namespace Sapphire::Entity
/*! set the loading complete bool */
void setLoadingComplete( bool bComplete );
Common::ZoneingType getZoningType() const;
Common::ZoningType getZoningType() const;
void setZoningType( Common::ZoneingType zoneingType );
void setZoningType( Common::ZoningType zoneingType );
void setSearchInfo( uint8_t selectRegion, uint8_t selectClass, const char* searchMessage );
@ -742,6 +743,10 @@ namespace Sapphire::Entity
/*! calculate and return player ilvl based off equipped gear */
uint16_t calculateEquippedGearItemLevel();
/*! calculate bonus stats from gear */
void calculateBonusStats();
ItemPtr getEquippedWeapon();
/*! return the current amount of currency of type */
@ -921,7 +926,7 @@ namespace Sapphire::Entity
bool m_bIsConnected;
Common::ZoneingType m_zoningType;
Common::ZoningType m_zoningType;
bool m_bNewAdventurer{};
uint64_t m_onlineStatus;

View file

@ -224,16 +224,6 @@ void Sapphire::Entity::Player::equipItem( Common::GearSetSlot equipSlotId, Item&
updateModels( equipSlotId, item );
auto baseParams = item.getBaseParams();
for( auto i = 0; i < 6; ++i )
{
if( baseParams[ i ].baseParam != static_cast< uint8_t >( Common::BaseParam::None ) )
m_bonusStats[ baseParams[ i ].baseParam ] += baseParams[ i ].value;
}
m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ] += item.getDefense();
m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ] += item.getDefenseMag();
calculateStats();
if( sendUpdate )
{
@ -254,16 +244,6 @@ void Sapphire::Entity::Player::unequipItem( Common::GearSetSlot equipSlotId, Ite
if ( equipSlotId == SoulCrystal )
unequipSoulCrystal();
auto baseParams = item.getBaseParams();
for( auto i = 0; i < 6; ++i )
{
if( baseParams[ i ].baseParam != static_cast< uint8_t >( Common::BaseParam::None ) )
m_bonusStats[ baseParams[ i ].baseParam ] -= baseParams[ i ].value;
}
m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ] -= item.getDefense();
m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ] -= item.getDefenseMag();
calculateStats();
if( sendUpdate )
@ -999,6 +979,38 @@ uint16_t Sapphire::Entity::Player::calculateEquippedGearItemLevel()
return ilvl;
}
void Sapphire::Entity::Player::calculateBonusStats()
{
m_bonusStats.fill( 0 );
auto gearSetMap = m_storageMap[ GearSet0 ]->getItemMap();
auto it = gearSetMap.begin();
while( it != gearSetMap.end() )
{
auto pItem = it->second;
if( pItem && pItem->getCategory() != Common::ItemUICategory::SoulCrystal )
{
auto baseParams = pItem->getBaseParams();
for( auto i = 0; i < 6; ++i )
{
auto itemBaseParam = baseParams[ i ].baseParam;
auto itemBaseVal = baseParams[ i ].value;
if( itemBaseParam != static_cast< uint8_t >( Common::BaseParam::None ) )
m_bonusStats[ itemBaseParam ] += itemBaseVal;
}
m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ] += pItem->getDefense();
m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ] += pItem->getDefenseMag();
}
it++;
}
}
Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedWeapon()
{
return m_storageMap[ GearSet0 ]->getItem( GearSetSlot::MainHand );

View file

@ -132,7 +132,7 @@ namespace Sapphire::World::Manager
if( !pAchv )
continue;
auto achvExdData = pAchv->data();
auto& achvExdData = pAchv->data();
if( achvExdData.ConditionArg[ 1 ] <= static_cast< int32_t >( achvData.progressData[ dataKey.u32 ] ) )
unlockAchievement( player, achvId );

View file

@ -217,7 +217,7 @@ void DebugCommandMgr::set( char* data, Entity::Player& player, std::shared_ptr<
int32_t aetheryteId;
sscanf( params.c_str(), "%i", &aetheryteId );
player.teleport( static_cast< uint16_t >( aetheryteId ) );
Common::Service< WarpMgr >::ref().requestPlayerTeleport( player, static_cast< uint16_t >( aetheryteId ), 1 );
}
else if( ( subCommand == "discovery" ) && ( !params.empty() ) )
{
@ -269,8 +269,8 @@ void DebugCommandMgr::set( char* data, Entity::Player& player, std::shared_ptr<
int32_t id;
sscanf( params.c_str(), "%d", &id );
player.setMount( 0 );
player.setMount( static_cast< uint32_t >( id ));
Common::Service< World::Manager::PlayerMgr >::ref().onMountUpdate( player, 0 );
Common::Service< World::Manager::PlayerMgr >::ref().onMountUpdate( player, id );
}
else if( subCommand == "weatheroverride" || subCommand == "wo" )
{

View file

@ -555,12 +555,12 @@ void EventMgr::eventFinish( Sapphire::Entity::Player& player, uint32_t eventId,
}
if( player.hasStateFlag( Common::PlayerStateFlag::WatchingCutscene ) )
player.unsetStateFlag( Common::PlayerStateFlag::WatchingCutscene );
Common::Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( player, Common::PlayerStateFlag::WatchingCutscene );
player.removeEvent( pEvent->getId() );
if( freePlayer == 1 )
player.unsetStateFlag( Common::PlayerStateFlag::InNpcEvent );
Common::Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( player, Common::PlayerStateFlag::InNpcEvent );
}
void EventMgr::eventStart( Entity::Player& player, uint64_t actorId, uint32_t eventId, Event::EventHandler::EventType eventType, uint8_t eventParam1,
@ -571,7 +571,7 @@ void EventMgr::eventStart( Entity::Player& player, uint64_t actorId, uint32_t ev
newEvent->setEventFinishCallback( std::move( callback ) );
player.addEvent( newEvent );
player.setStateFlag( Common::PlayerStateFlag::InNpcEvent );
Common::Service< World::Manager::PlayerMgr >::ref().onSetStateFlag( player, Common::PlayerStateFlag::InNpcEvent );
server.queueForPlayer( player.getCharacterId(), std::make_shared< EventStartPacket >( player.getId(), actorId,
eventId, eventType, eventParam1, eventParam2 ) );
@ -836,7 +836,7 @@ Sapphire::Event::EventHandlerPtr EventMgr::bootstrapSceneEvent( Entity::Player&
}
if( flags & CONDITION_CUTSCENE )
player.setStateFlag( Common::PlayerStateFlag::WatchingCutscene );
Common::Service< World::Manager::PlayerMgr >::ref().onSetStateFlag( player, Common::PlayerStateFlag::WatchingCutscene );
return pEvent;
}

View file

@ -13,8 +13,8 @@
#include <Manager/HousingMgr.h>
#include <Manager/FreeCompanyMgr.h>
#include "Script/ScriptMgr.h"
#include "WorldServer.h"
#include <Script/ScriptMgr.h>
#include <WorldServer.h>
#include <Common.h>
#include <Network/PacketContainer.h>
@ -23,19 +23,23 @@
#include <Network/PacketDef/Zone/ServerZoneDef.h>
#include <Network/PacketWrappers/ActorControlPacket.h>
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
#include "Network/PacketWrappers/ActorControlTargetPacket.h"
#include "Network/PacketWrappers/InitZonePacket.h"
#include <Network/PacketWrappers/ActorControlTargetPacket.h>
#include <Network/PacketWrappers/InitZonePacket.h>
#include <Network/PacketWrappers/ModelEquipPacket.h>
#include "Network/PacketWrappers/PlayerSetupPacket.h"
#include <Network/PacketWrappers/PlayerSetupPacket.h>
#include <Network/PacketWrappers/PlayerStateFlagsPacket.h>
#include <Network/PacketWrappers/UpdateHpMpTpPacket.h>
#include "Network/PacketWrappers/ServerNoticePacket.h"
#include "Network/PacketWrappers/ChatPacket.h"
#include "Network/PacketWrappers/HudParamPacket.h"
#include <Network/PacketWrappers/ServerNoticePacket.h>
#include <Network/PacketWrappers/ChatPacket.h>
#include <Network/PacketWrappers/HudParamPacket.h>
#include <Actor/Player.h>
#include <Actor/BNpc.h>
#include <Inventory/Item.h>
#include <Util/UtilMath.h>
using namespace Sapphire;
using namespace Sapphire::World::Manager;
using namespace Sapphire::Network::Packets;
@ -163,7 +167,11 @@ void PlayerMgr::onPlayerStatusUpdate( Entity::Player& player )
void PlayerMgr::onPlayerHpMpTpChanged( Entity::Player& player )
{
auto& server = Common::Service< World::WorldServer >::ref();
player.sendToInRangeSet( std::make_shared< UpdateHpMpTpPacket >( player ), true );
auto hudParamPacket = makeHudParam( player );
server.queueForPlayer( player.getCharacterId(), hudParamPacket );
}
void PlayerMgr::onPlayerItemLevelUpdate( Entity::Player& player )
@ -213,6 +221,8 @@ void PlayerMgr::onGainExp( Entity::Player& player, uint32_t exp )
void PlayerMgr::onUnlockOrchestrion( Entity::Player& player, uint8_t songId, uint32_t itemId )
{
player.learnSong( songId, itemId );
auto& server = Common::Service< World::WorldServer >::ref();
server.queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), ToggleOrchestrionUnlock, songId, 1, itemId ) );
}
@ -225,21 +235,47 @@ void PlayerMgr::onChangeGear( Entity::Player& player )
void PlayerMgr::onGcUpdate( Entity::Player& player )
{
auto& server = Common::Service< World::WorldServer >::ref();
auto gcAffPacket = makeZonePacket< FFXIVIpcGrandCompany >( player.getId() );
gcAffPacket->data().ActiveCompanyId = player.getGc();
gcAffPacket->data().MaelstromRank = player.getGcRankArray()[ 0 ];
gcAffPacket->data().TwinAdderRank = player.getGcRankArray()[ 1 ];
gcAffPacket->data().ImmortalFlamesRank = player.getGcRankArray()[ 2 ];
server.queueForPlayer( player.getCharacterId(), gcAffPacket );
}
void PlayerMgr::onSetGc( Entity::Player& player, uint8_t gc )
{
player.setGc( gc );
onGcUpdate( player );
}
void PlayerMgr::onSetGcRank( Entity::Player& player, uint8_t gc, uint8_t rank )
{
player.setGcRankAt( gc, rank );
onGcUpdate( player );
}
void PlayerMgr::onCompanionUpdate( Entity::Player& player, uint8_t companionId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto companion = exdData.getRow< Excel::Companion >( companionId );
if( !companion )
return;
player.setCompanion( companionId );
player.sendToInRangeSet( makeActorControl( player.getId(), ActorControlType::ToggleCompanion, companionId ), true );
}
void PlayerMgr::onMountUpdate( Entity::Player& player, uint32_t mountId )
{
Common::Service< World::Manager::PlayerMgr >::ref().onMountUpdate( player, mountId );
if( mountId != 0 )
{
player.sendToInRangeSet( makeActorControl( player.getId(), ActorControlType::SetStatus,
@ -310,13 +346,13 @@ void PlayerMgr::onHateListChanged( Entity::Player& player )
server.queueForPlayer( player.getCharacterId(), { hateListPacket, hateRankPacket } );
}
void PlayerMgr::onChangeClass( Entity::Player &player )
void PlayerMgr::onChangeClass( Entity::Player& player )
{
player.sendToInRangeSet( makeActorControl( player.getId(), ClassJobChange, 0x04 ), true );
player.sendStatusUpdate();
onPlayerHpMpTpChanged( player );
}
void PlayerMgr::onLogin( Entity::Player &player )
void PlayerMgr::onLogin( Entity::Player& player )
{
auto& server = Common::Service< World::WorldServer >::ref();
@ -330,7 +366,7 @@ void PlayerMgr::onLogin( Entity::Player &player )
}
}
void PlayerMgr::onDeath( Entity::Player &player )
void PlayerMgr::onDeath( Entity::Player& player )
{
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPlayerDeath( player );
@ -427,6 +463,73 @@ void PlayerMgr::onZone( Sapphire::Entity::Player& player )
}
void PlayerMgr::onUpdate( Entity::Player& player, uint64_t tickCount )
{
if( player.getHp() <= 0 && player.getStatus() != Common::ActorStatus::Dead )
{
player.die();
onDeath( player );
}
if( !player.isAlive() )
return;
if( !player.checkAction() )
{
if( player.getTargetId() && player.getStance() == Common::Stance::Active && player.isAutoattackOn() )
{
auto mainWeap = player.getItemAt( Common::GearSet0, Common::GearSetSlot::MainHand );
// @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need...
for( const auto& actor : player.getInRangeActors() )
{
if( actor->getId() == player.getTargetId() && actor->getAsChara()->isAlive() && mainWeap )
{
auto chara = actor->getAsChara();
// default autoattack range
float range = 3.f + chara->getRadius() + player.getRadius() * 0.5f;
// default autoattack range for ranged classes
auto classJob = player.getClass();
if( classJob == Common::ClassJob::Machinist || classJob == Common::ClassJob::Bard || classJob == Common::ClassJob::Archer )
range = 25.f + chara->getRadius() + player.getRadius() * 0.5f;
if( Common::Util::distance( player.getPos(), actor->getPos() ) <= range )
{
if( ( tickCount - player.getLastAttack() ) > mainWeap->getDelay() )
{
player.setLastAttack( tickCount );
player.autoAttack( actor->getAsChara() );
}
}
}
}
}
}
}
void PlayerMgr::onSetStateFlag( Sapphire::Entity::Player& player, Common::PlayerStateFlag flag )
{
auto prevOnlineStatus = player.getOnlineStatus();
player.setStateFlag( flag );
auto newOnlineStatus = player.getOnlineStatus();
onSendStateFlags( player, prevOnlineStatus != newOnlineStatus );
}
void PlayerMgr::onUnsetStateFlag( Sapphire::Entity::Player& player, Common::PlayerStateFlag flag )
{
auto prevOnlineStatus = player.getOnlineStatus();
player.unsetStateFlag( flag );
auto newOnlineStatus = player.getOnlineStatus();
onSendStateFlags( player, prevOnlineStatus != newOnlineStatus );
}
////////// Helper ///////////

View file

@ -44,6 +44,10 @@ class PlayerMgr
void onGcUpdate( Sapphire::Entity::Player& player );
void onSetGc( Sapphire::Entity::Player& player, uint8_t gc );
void onSetGcRank( Sapphire::Entity::Player& player, uint8_t gc, uint8_t rank );
void onCompanionUpdate( Entity::Player& player, uint8_t companionId );
void onMountUpdate( Sapphire::Entity::Player& player, uint32_t mountId );
@ -58,6 +62,12 @@ class PlayerMgr
void onZone( Sapphire::Entity::Player& player );
void onUpdate( Sapphire::Entity::Player& player, uint64_t tickCount );
void onSetStateFlag( Sapphire::Entity::Player& player, Common::PlayerStateFlag flag );
void onUnsetStateFlag( Sapphire::Entity::Player& player, Common::PlayerStateFlag flag );
//////////// Helpers
static void sendServerNotice( Sapphire::Entity::Player& player, const std::string& message );

View file

@ -6,14 +6,20 @@
#include <WorldServer.h>
#include <Logging/Logger.h>
#include <Exd/ExdData.h>
#include "Task/MoveTerritoryTask.h"
#include "Task/WarpTask.h"
#include <Network/CommonActorControl.h>
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
#include <Network/PacketWrappers/ActorControlPacket.h>
#include <Manager/PlayerMgr.h>
#include "Territory/Territory.h"
#include "Network/PacketWrappers/ActorControlSelfPacket.h"
#include "Network/PacketWrappers/ActorControlPacket.h"
#include "Actor/Player.h"
#include <Territory/InstanceObjectCache.h>
using namespace Sapphire::World::Manager;
using namespace Sapphire::World;
@ -42,7 +48,7 @@ void WarpMgr::requestMoveTerritory( Entity::Player& player, Common::WarpType war
player.sendToInRangeSet( makeActorControl( player.getId(), WarpStart, warpType, 1, pTeri->getTerritoryTypeId() ), true );
player.sendToInRangeSet( makeActorControl( player.getId(), ActorDespawnEffect, warpType ) );
player.setStateFlag( PlayerStateFlag::BetweenAreas );
Common::Service< PlayerMgr >::ref().onSetStateFlag( player, PlayerStateFlag::BetweenAreas );
auto moveTerritoryPacket = makeZonePacket< FFXIVIpcMoveTerritory >( player.getId() );
moveTerritoryPacket->data().index = -1;
@ -61,13 +67,28 @@ void WarpMgr::requestMoveTerritory( Entity::Player& player, Common::WarpType war
taskMgr.queueTask( makeMoveTerritoryTask( player, warpType, targetTerritoryId, targetPos, targetRot, 2000 ) );
}
void WarpMgr::requestWarp( Entity::Player& player, Common::WarpType warpType, Common::FFXIVARR_POSITION3 targetPos, float targetRot )
{
m_entityIdToWarpInfoMap[ player.getId() ] = { 0, warpType, targetPos, targetRot };
player.sendToInRangeSet( makeActorControl( player.getId(), WarpStart, warpType, 1, 0, player.getTerritoryTypeId(), 1 ), true );
player.sendToInRangeSet( makeActorControl( player.getId(), ActorDespawnEffect, warpType ) );
auto& taskMgr = Common::Service< TaskMgr >::ref();
taskMgr.queueTask( makeWarpTask( player, warpType, targetPos, targetRot, 1000 ) );
}
void WarpMgr::finishWarp( Entity::Player& player )
{
auto& playerMgr = Common::Service< PlayerMgr >::ref();
WarpType warpType = WarpType::WARP_TYPE_NORMAL;
auto it = m_entityIdToWarpInfoMap.find( player.getId() );
if( it != m_entityIdToWarpInfoMap.end() )
warpType = it->second.m_warpType;
uint32_t vfxType = 0; // seems to only be used for raise animation?
switch( warpType )
{
case WarpType::WARP_TYPE_REISE:
@ -78,34 +99,93 @@ void WarpMgr::finishWarp( Entity::Player& player )
player.resetHp();
player.resetMp();
player.setStatus( Common::ActorStatus::Idle );
vfxType = 1;
}
}
}
auto zoneInPacket = makeActorControlSelf( player.getId(), Appear, warpType, 0, 0, 0 );
auto SetStatusPacket = makeActorControl( player.getId(), SetStatus, static_cast< uint8_t >( Common::ActorStatus::Idle ) );
player.setZoningType( Common::ZoneingType::None );
auto zoneInPacket = makeActorControlSelf( player.getId(), Appear, warpType, vfxType, 0, 0 );
auto setStatusPacket = makeActorControl( player.getId(), SetStatus, static_cast< uint8_t >( Common::ActorStatus::Idle ) );
player.setZoningType( Common::ZoningType::None );
if( !player.getGmInvis() )
player.sendToInRangeSet( zoneInPacket );
player.sendToInRangeSet( SetStatusPacket, true );
player.sendToInRangeSet( setStatusPacket, true );
auto& server = Common::Service< WorldServer >::ref();
server.queueForPlayer( player.getCharacterId(), zoneInPacket );
player.unsetStateFlag( PlayerStateFlag::BetweenAreas );
playerMgr.onUnsetStateFlag( player, PlayerStateFlag::BetweenAreas );
}
void WarpMgr::requestWarp( Entity::Player& player, Common::WarpType warpType, Common::FFXIVARR_POSITION3 targetPos, float targetRot )
void WarpMgr::requestPlayerTeleport( Entity::Player& player, uint16_t aetheryteId, uint8_t teleportType )
{
m_entityIdToWarpInfoMap[ player.getId() ] = { 0, warpType, targetPos, targetRot };
auto& exdData = Common::Service< Data::ExdData >::ref();
auto& teriMgr = Common::Service< TerritoryMgr >::ref();
player.sendToInRangeSet( makeActorControl( player.getId(), WarpStart, warpType, 1, 0, player.getTerritoryTypeId(), 1 ), true );
player.sendToInRangeSet( makeActorControl( player.getId(), ActorDespawnEffect, warpType ) );
auto aetherData = exdData.getRow< Excel::Aetheryte >( aetheryteId );
auto& taskMgr = Common::Service< TaskMgr >::ref();
taskMgr.queueTask( makeWarpTask( player, warpType, targetPos, targetRot, 1000 ) );
if( !aetherData )
return;
const auto& data = aetherData->data();
auto& instanceObjectCache = Common::Service< InstanceObjectCache >::ref();
auto pop = instanceObjectCache.getPopRangeInfo( data.PopRange[ 0 ] );
Common::FFXIVARR_POSITION3 pos{ 0.f, 0.f, 0.f };
float rot = 0.f;
if( pop )
{
PlayerMgr::sendDebug( player, "Teleport: popRange {0} found!", data.PopRange[ 0 ] );
pos = pop->m_pos;
rot = pop->m_rotation;
}
else
{
PlayerMgr::sendDebug( player, "Teleport: popRange {0} not found in {1}!", data.PopRange[ 0 ], data.TerritoryType );
}
auto townPlace = exdData.getRow< Excel::PlaceName >( data.TelepoName );
auto aetherytePlace = exdData.getRow< Excel::PlaceName >( data.TransferName );
PlayerMgr::sendDebug( player, "Teleport: {0} - {1} ({2})",
townPlace->getString( townPlace->data().Text.SGL ),
aetherytePlace->getString( aetherytePlace->data().Text.SGL ),
data.TerritoryType );
// if it is a teleport in the same zone, we want to do warp instead of moveTerri
bool sameTerritory = player.getTerritoryTypeId() == data.TerritoryType;
WarpType warpType = WarpType::WARP_TYPE_NORMAL;
// TODO: should teleport type be a separate enum?
if( teleportType == 1 || teleportType == 2 ) // teleport
{
warpType = WarpType::WARP_TYPE_TELEPO;
player.setZoningType( Common::ZoningType::Teleport );
}
else if( teleportType == 3 ) // return
{
warpType = WarpType::WARP_TYPE_HOME_POINT;
player.setZoningType( Common::ZoningType::Return );
}
else if( teleportType == 4 ) // return
{
warpType = WarpType::WARP_TYPE_REISE;
player.setZoningType( Common::ZoningType::ReturnDead );
}
if( sameTerritory )
requestWarp( player, warpType, pos, rot );
else
{
auto pTeri = teriMgr.getZoneByTerritoryTypeId( data.TerritoryType );
if( !pTeri )
return;
requestMoveTerritory( player, warpType, pTeri->getGuId(), pos, rot );
}
}

View file

@ -21,10 +21,40 @@ namespace Sapphire::World::Manager
{
public:
WarpMgr() = default;
/// <summary>
/// request to move a player to specified territorytype and position, with given WarpType
/// </summary>
/// <param name="player"></param>
/// <param name="warpType"></param>
/// <param name="targetTerritoryId"></param>
/// <param name="targetPos"></param>
/// <param name="targetRot"></param>
void requestMoveTerritory( Entity::Player& player, Common::WarpType warpType, uint32_t targetTerritoryId, Common::FFXIVARR_POSITION3 targetPos, float targetRot );
/// <summary>
/// handle player state pre-warp and tells client to warp player
/// </summary>
/// <param name="player"></param>
/// <param name="warpType"></param>
/// <param name="targetPos"></param>
/// <param name="targetRot"></param>
void requestWarp( Entity::Player& player, Common::WarpType warpType, Common::FFXIVARR_POSITION3 targetPos, float targetRot );
/// <summary>
/// handle player state post-warp after client is done loading
/// </summary>
/// <param name="player"></param>
void finishWarp( Entity::Player& player );
/// <summary>
/// teleport a player to specified aetheryte and teleport type (teleport, return, etc)
/// </summary>
/// <param name="player"></param>
/// <param name="aetheryteId"></param>
/// <param name="teleportType"></param>
void requestPlayerTeleport( Entity::Player& player, uint16_t aetheryteId, uint8_t teleportType );
private:
std::unordered_map< uint32_t, WarpInfo > m_entityIdToWarpInfoMap;

View file

@ -139,14 +139,15 @@ uint32_t CalcStats::calculateMaxHp( Player& player )
auto vitMod = player.getBonusStat( Common::BaseParam::Vitality );
float baseStat = calculateBaseStat( player );
uint16_t vitStat = static_cast< uint16_t >( player.getStatValue( Common::BaseParam::Vitality ) ) + static_cast< uint16_t >( vitMod );
auto baseParamVit = player.getStatValue( Common::BaseParam::Vitality );
auto vitStat = player.getStatValue( Common::BaseParam::Vitality ) + vitMod;
uint16_t hpMod = paramGrowthInfo->data().ParamBase;
uint16_t jobModHp = classInfo->data().Hp;
float approxBaseHp = 0.0f; // Read above
approxBaseHp = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::HP ] );
uint16_t result = static_cast< uint16_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) +
auto result = static_cast< uint32_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) +
floor( hpMod / 100.0f * ( vitStat - baseStat ) ) );
return result;
@ -176,7 +177,7 @@ uint32_t CalcStats::calculateMaxHp( Chara& chara )
approxBaseHp = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::HP ] );
uint16_t result = static_cast< uint16_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) +
auto result = static_cast< uint32_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) +
floor( hpMod / 100.0f * ( vitStat - baseStat ) ) );
return result;

View file

@ -327,14 +327,14 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
if( param2 == 0 )
{
for( uint8_t i = 0; i < 255; i++ )
targetActor->getAsPlayer()->learnSong( i, 0 );
Service< World::Manager::PlayerMgr >::ref().onUnlockOrchestrion( *targetPlayer, i, 0 );
PlayerMgr::sendServerNotice( player, "All Songs for {0} were turned on.", targetPlayer->getName());
PlayerMgr::sendServerNotice( player, "All Songs for {0} were turned on.", targetPlayer->getName() );
}
else
{
targetActor->getAsPlayer()->learnSong( static_cast< uint8_t >( param2 ), 0 );
PlayerMgr::sendServerNotice( player, "Song {0} for {1} was turned on.", param2, targetPlayer->getName());
Service< World::Manager::PlayerMgr >::ref().onUnlockOrchestrion( *targetPlayer, static_cast< uint8_t >( param2 ), 0 );
PlayerMgr::sendServerNotice( player, "Song {0} for {1} was turned on.", param2, targetPlayer->getName() );
}
}
@ -417,7 +417,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
return;
}
targetPlayer->setGc( static_cast< uint8_t >( param1 ) );
Service< World::Manager::PlayerMgr >::ref().onSetGc( player, static_cast< uint8_t >( param1 ) );
// if we're changing them to a GC, check if they have a rank and if not, set it to the lowest rank
if( param1 > 0 )
@ -442,7 +442,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
return;
}
targetPlayer->setGcRankAt( static_cast< uint8_t >( gcId ), static_cast< uint8_t >( param1 ) );
Service< World::Manager::PlayerMgr >::ref().onSetGcRank( player, static_cast< uint8_t >( gcId ), static_cast< uint8_t >( param1 ) );
PlayerMgr::sendServerNotice( player, "GC Rank for {0} for GC {1} was set to {2}", targetPlayer->getName(), targetPlayer->getGc(),
targetPlayer->getGcRankArray()[ targetPlayer->getGc() - 1 ] );
break;
@ -533,7 +533,7 @@ void Sapphire::Network::GameConnection::gmCommandHandler( const Packets::FFXIVAR
}
if( doTeleport )
{
player.teleport( teleport );
warpMgr.requestPlayerTeleport( player, teleport, 1 );
}
else
{

View file

@ -460,17 +460,17 @@ void Sapphire::Network::GameConnection::commandHandler( const Packets::FFXIVARR_
}
case PacketCommand::CANCEL_MOUNT:
{
player.setMount( 0 );
Service< World::Manager::PlayerMgr >::ref().onMountUpdate( player, 0 );
break;
}
case PacketCommand::COMPANION:
{
player.setCompanion( static_cast< uint8_t >( param1 ) );
Common::Service< World::Manager::PlayerMgr >::ref().onCompanionUpdate( player, static_cast< uint8_t >( param1 ) );
break;
}
case PacketCommand::COMPANION_CANCEL:
{
player.setCompanion( 0 );
Common::Service< World::Manager::PlayerMgr >::ref().onCompanionUpdate( player, 0 );
break;
}
case PacketCommand::REQUEST_STATUS_RESET: // Remove status (clicking it off)
@ -585,14 +585,15 @@ void Sapphire::Network::GameConnection::commandHandler( const Packets::FFXIVARR_
}
case PacketCommand::REVIVE: // return dead / accept raise
{
auto& warpMgr = Service< WarpMgr >::ref();
switch( static_cast < ResurrectType >( param1 ) )
{
case ResurrectType::RaiseSpell:
// todo: handle raise case (set position to raiser, apply weakness status, set hp/mp/tp as well as packet)
player.teleport( player.getHomepoint(), 3 );
warpMgr.requestPlayerTeleport( player, player.getHomepoint(), 5 );
break;
case ResurrectType::Return:
player.teleport( player.getHomepoint(), 3 );
warpMgr.requestPlayerTeleport( player, player.getHomepoint(), 4 );
break;
default:
break;

View file

@ -7,7 +7,7 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
{
/**
* @brief The Ping response packet.
* @brief HUD stats packet.
*/
class HudParamPacket : public ZoneChannelPacket< FFXIVIpcHudParam >
{

View file

@ -110,7 +110,7 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
m_data.ActiveType = player.getStance();
m_data.Flag = 0;
if( player.getZoningType() != Common::ZoneingType::None || player.getGmInvis() )
if( player.getZoningType() != Common::ZoningType::None || player.getGmInvis() )
{
m_data.Flag |= static_cast< uint16_t >( Common::DisplayFlags::Invisible );
}

View file

@ -138,6 +138,11 @@ namespace Sapphire::ScriptAPI
virtual void onExecute( Sapphire::World::Action::Action& action );
virtual void onInterrupt( Sapphire::World::Action::Action& action );
World::Manager::WarpMgr& warpMgr()
{
return Common::Service< World::Manager::WarpMgr >::ref();
}
};
/*!

View file

@ -99,7 +99,7 @@ void Sapphire::InstanceContent::onPlayerZoneIn( Entity::Player& player )
}
// mark player as "bound by duty"
player.setStateFlag( PlayerStateFlag::BoundByDuty );
Common::Service< World::Manager::PlayerMgr >::ref().onSetStateFlag( player, PlayerStateFlag::BoundByDuty );
sendDirectorInit( player );
}
@ -614,7 +614,7 @@ void Sapphire::InstanceContent::clearDirector( Entity::Player& player )
player.setDirectorInitialized( false );
// remove "bound by duty" state
player.unsetStateFlag( PlayerStateFlag::BoundByDuty );
Service< World::Manager::PlayerMgr >::ref().onUnsetStateFlag( player, PlayerStateFlag::BoundByDuty );
}
uint32_t Sapphire::InstanceContent::getExpireValue()