diff --git a/src/common/Util/UtilMath.cpp b/src/common/Util/UtilMath.cpp index 2b9084a9..f2e8cc2c 100644 --- a/src/common/Util/UtilMath.cpp +++ b/src/common/Util/UtilMath.cpp @@ -15,6 +15,11 @@ float Sapphire::Util::distance( float x, float y, float z, float x1, float y1, f return sqrtf( distanceSq( x, y, z, x1, y1, z1 ) ); } +float Sapphire::Util::distance( const Common::FFXIVARR_POSITION3& pos1, const Common::FFXIVARR_POSITION3& pos2 ) +{ + return sqrtf( distanceSq( pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z ) ); +} + float Sapphire::Util::distance2DSq( float x, float y, float x1, float y1 ) { float deltaX = x - x1; diff --git a/src/common/Util/UtilMath.h b/src/common/Util/UtilMath.h index a726b5d1..5d34ee92 100644 --- a/src/common/Util/UtilMath.h +++ b/src/common/Util/UtilMath.h @@ -11,6 +11,7 @@ namespace Sapphire::Util float distanceSq( float x, float y, float z, float x1, float y1, float z1 ); float distance( float x, float y, float z, float x1, float y1, float z1 ); + float distance( const Common::FFXIVARR_POSITION3& pos1, const Common::FFXIVARR_POSITION3& pos2 ); float distance2DSq( float x, float y, float x1, float y1 ); diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 4049bf97..faff1d45 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -88,6 +88,7 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX memcpy( m_customize, pTemplate->getCustomize(), sizeof( m_customize ) ); memcpy( m_modelEquip, pTemplate->getModelEquip(), sizeof( m_modelEquip ) ); + m_lastTickTime = 0; } Sapphire::Entity::BNpc::~BNpc() @@ -162,11 +163,9 @@ void Sapphire::Entity::BNpc::step() return; auto stepPos = m_naviLastPath[ m_naviPathStep ]; - auto distanceToStep = Util::distance( getPos().x, getPos().y, getPos().z, - stepPos.x, stepPos.y, stepPos.z ); - auto distanceToDest = Util::distance( getPos().x, getPos().y, getPos().z, - m_naviTarget.x, m_naviTarget.y, m_naviTarget.z ); + auto distanceToStep = Util::distance( getPos(), stepPos ); + auto distanceToDest = Util::distance( getPos(), m_naviTarget ); if( distanceToStep <= 4 && m_naviPathStep < m_naviLastPath.size() - 1 ) { @@ -178,15 +177,16 @@ void Sapphire::Entity::BNpc::step() // This is probably not a good way to do it but works fine for now float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI; - float speed = 1.7f; + auto delta = static_cast< float >( Util::getTimeMs() - m_lastUpdate ) / 1000.f; + + float speed = 7.5f * delta; if( m_state == BNpcState::Roaming ) - speed *= 0.5f; + speed *= 0.27f; - if( distanceToDest <= distanceToStep + speed ) - { - speed = distanceToDest; - } + // this seems to fix it but i don't know why :( + if( speed > distanceToDest ) + speed = distanceToDest / delta; auto x = ( cosf( angle ) * speed ); auto y = stepPos.y; @@ -201,7 +201,7 @@ void Sapphire::Entity::BNpc::step() bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) { - if( Util::distance( getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z ) <= 4 ) + if( Util::distance( getPos(), pos ) <= 4 ) { // Reached destination m_naviLastPath.clear(); @@ -233,7 +233,27 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) Logger::debug( "No path found from x{0} y{1} z{2} to x{3} y{4} z{5} in {6}", getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z, m_pCurrentZone->getInternalName() ); + hateListClear(); + + if( m_state == BNpcState::Roaming ) + { + Logger::warn( "BNpc Base#{0} Name#{1} unable to path from x{2} y{3} z{4} while roaming. " + "Possible pathing error in area. Returning BNpc to spawn position x{5} y{6} z{7}.", + m_bNpcBaseId, m_bNpcNameId, + getPos().x, getPos().y, getPos().z, + m_spawnPos.x, m_spawnPos.y, m_spawnPos.z ); + + m_lastRoamTargetReached = Util::getTimeSeconds(); + m_state = BNpcState::Idle; + + m_naviLastPath.clear(); + + setPos( m_spawnPos ); + sendPositionUpdate(); + + return true; + } } @@ -368,6 +388,14 @@ void Sapphire::Entity::BNpc::deaggro( Sapphire::Entity::CharaPtr pChara ) } } +void Sapphire::Entity::BNpc::onTick() +{ + if( m_state == BNpcState::Retreat ) + { + regainHp(); + } +} + void Sapphire::Entity::BNpc::update( int64_t currTime ) { const uint8_t minActorDistance = 4; @@ -386,10 +414,6 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) { setInvincibilityType( InvincibilityType::InvincibilityIgnoreDamage ); - if( std::difftime( currTime, m_lastTickTime ) > 3000 ) - regainHp( currTime ); - - // slowly restore hp every tick if( moveTo( m_spawnPos ) ) { setInvincibilityType( InvincibilityType::InvincibilityNone ); @@ -491,12 +515,13 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) } } } + + + Chara::update( currTime ); } -void Sapphire::Entity::BNpc::regainHp( int64_t currTime ) +void Sapphire::Entity::BNpc::regainHp() { - this->m_lastTickTime = currTime; - if( this->m_hp < this->getMaxHp() ) { auto addHp = static_cast< uint32_t >( this->getMaxHp() * 0.1f + 1 ); diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index fd2e1844..40b6cae4 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -82,6 +82,7 @@ namespace Sapphire::Entity void deaggro( CharaPtr pChara ); void update( int64_t currTime ) override; + void onTick() override; void onActionHostile( CharaPtr pSource ) override; @@ -90,7 +91,7 @@ namespace Sapphire::Entity uint32_t getTimeOfDeath() const; void setTimeOfDeath( uint32_t timeOfDeath ); - void regainHp( int64_t currTime ); + void regainHp(); void checkAggro( uint32_t range ); diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 6e2272cc..3c888b35 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -280,6 +280,18 @@ bool Sapphire::Entity::Chara::checkAction() } +void Sapphire::Entity::Chara::update( int64_t currTime ) +{ + if( std::difftime( currTime, m_lastTickTime ) > 3000 ) + { + onTick(); + + m_lastTickTime = currTime; + } + + m_lastUpdate = currTime; +} + /*! Change the current target and propagate to in range players diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 1adf919b..896404b5 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -68,11 +68,11 @@ namespace Sapphire::Entity protected: char m_name[34]; /*! Last tick time for the actor ( in ms ) */ - uint64_t m_lastTickTime; + int64_t m_lastTickTime; /*! Last time the actor performed an autoAttack ( in ms ) */ uint64_t m_lastAttack; /*! Last time the actor was updated ( in ms ) */ - uint64_t m_lastUpdate; + int64_t m_lastUpdate; /*! Current stance of the actor */ Common::Stance m_currentStance; /*! Current staus of the actor */ @@ -237,7 +237,7 @@ namespace Sapphire::Entity virtual bool checkAction(); - virtual void update( int64_t currTime ) {}; + virtual void update( int64_t currTime ); Action::ActionPtr getCurrentAction() const; diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index db5e56fb..f7e2c317 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -1043,7 +1043,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag ) void Sapphire::Entity::Player::update( int64_t currTime ) { - // a zoning is pending, lets do it if( m_queuedZoneing && ( currTime - m_queuedZoneing->m_queueTime ) > 800 ) { @@ -1115,13 +1114,7 @@ void Sapphire::Entity::Player::update( int64_t currTime ) } } - if( ( currTime - m_lastTickTime ) > 3000 ) - { - // add 3 seconds to total play time - m_playTime += 3; - m_lastTickTime = currTime; - onTick(); - } + Chara::update( currTime ); } void Sapphire::Entity::Player::onMobKill( uint16_t nameId ) diff --git a/src/world/Actor/PlayerEvent.cpp b/src/world/Actor/PlayerEvent.cpp index 089cc5c0..6b0865eb 100644 --- a/src/world/Actor/PlayerEvent.cpp +++ b/src/world/Actor/PlayerEvent.cpp @@ -355,6 +355,9 @@ void Sapphire::Entity::Player::onDeath() void Sapphire::Entity::Player::onTick() { + // add 3 seconds to total play time + m_playTime += 3; + bool sendUpdate = false; if( !isAlive() || !isLoadingComplete() ) diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index c11896ec..8547849f 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -8,7 +8,7 @@ namespace Sapphire::World::Navi { - const int32_t MAX_POLYS = 8; + const int32_t MAX_POLYS = 32; const int32_t MAX_SMOOTH = 2048; const int32_t NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET' diff --git a/src/world/Network/Handlers/GMCommandHandlers.cpp b/src/world/Network/Handlers/GMCommandHandlers.cpp index 1526789b..8468540c 100644 --- a/src/world/Network/Handlers/GMCommandHandlers.cpp +++ b/src/world/Network/Handlers/GMCommandHandlers.cpp @@ -399,13 +399,38 @@ void Sapphire::Network::GameConnection::gm1Handler( FrameworkPtr pFw, } case GmCommand::GC: { + if( param1 > 3 ) + { + player.sendUrgent( "Invalid Grand Company ID: {0}", param1 ); + return; + } + targetPlayer->setGc( 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 ) + { + auto gcRankIdx = static_cast< uint8_t >( param1 ) - 1; + if( targetPlayer->getGcRankArray()[ gcRankIdx ] == 0 ) + { + player.setGcRankAt( gcRankIdx, 1 ); + } + } + player.sendNotice( "GC for {0} was set to {1}", targetPlayer->getName(), targetPlayer->getGc() ); break; } case GmCommand::GCRank: { - targetPlayer->setGcRankAt( targetPlayer->getGc() - 1, param1 ); + auto gcId = targetPlayer->getGc() - 1; + + if( gcId > 2 ) + { + player.sendUrgent( "{0} has an invalid Grand Company ID: {0}", targetPlayer->getName(), gcId ); + return; + } + + targetPlayer->setGcRankAt( gcId, param1 ); player.sendNotice( "GC Rank for {0} for GC {1} was set to {2}", targetPlayer->getName(), targetPlayer->getGc(), targetPlayer->getGcRankArray()[ targetPlayer->getGc() - 1 ] ); break; diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 27869ab1..e1bfb528 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -393,7 +393,7 @@ void Sapphire::Zone::updateBNpcs( int64_t tickCount ) m_lastMobUpdate = tickCount; uint32_t currTime = Sapphire::Util::getTimeSeconds(); - for( auto entry : m_bNpcMap ) + for( const auto& entry : m_bNpcMap ) { Entity::BNpcPtr pBNpc = entry.second;