diff --git a/src/common/Common.h b/src/common/Common.h index ef55588f..389c6093 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -57,6 +57,11 @@ namespace Sapphire::Common French = 8 }; + enum TellFlags : uint8_t + { + GmTellMsg = 0x4, + }; + enum ObjKind : uint8_t { None = 0x00, diff --git a/src/common/Network/PacketDef/Chat/ServerChatDef.h b/src/common/Network/PacketDef/Chat/ServerChatDef.h index 9d7ac65c..755ad3c8 100644 --- a/src/common/Network/PacketDef/Chat/ServerChatDef.h +++ b/src/common/Network/PacketDef/Chat/ServerChatDef.h @@ -17,7 +17,7 @@ struct FFXIVIpcTell : FFXIVIpcBasePacket< Tell > { uint64_t contentId; uint16_t worldId; - uint8_t preName; + uint8_t flags; char receipientName[32]; char msg[1029]; }; diff --git a/src/world/Actor/Actor.cpp b/src/world/Actor/Actor.cpp index f3a70534..fa3d3124 100644 --- a/src/world/Actor/Actor.cpp +++ b/src/world/Actor/Actor.cpp @@ -57,18 +57,22 @@ Sapphire::Common::FFXIVARR_POSITION3& Sapphire::Entity::Actor::getPos() return m_pos; } -void Sapphire::Entity::Actor::setPos( float x, float y, float z ) +void Sapphire::Entity::Actor::setPos( float x, float y, float z, bool broadcastUpdate ) { m_pos.x = x; m_pos.y = y; m_pos.z = z; - m_pCurrentZone->updateActorPosition( *this ); + + if( broadcastUpdate ) + m_pCurrentZone->updateActorPosition( *this ); } -void Sapphire::Entity::Actor::setPos( const Sapphire::Common::FFXIVARR_POSITION3& pos ) +void Sapphire::Entity::Actor::setPos( const Sapphire::Common::FFXIVARR_POSITION3& pos, bool broadcastUpdate ) { m_pos = pos; - m_pCurrentZone->updateActorPosition( *this ); + + if( broadcastUpdate ) + m_pCurrentZone->updateActorPosition( *this ); } float Sapphire::Entity::Actor::getRot() const diff --git a/src/world/Actor/Actor.h b/src/world/Actor/Actor.h index 62d47765..c5686b68 100644 --- a/src/world/Actor/Actor.h +++ b/src/world/Actor/Actor.h @@ -59,9 +59,9 @@ namespace Sapphire::Entity Common::FFXIVARR_POSITION3& getPos(); - void setPos( const Common::FFXIVARR_POSITION3& pos ); + void setPos( const Common::FFXIVARR_POSITION3& pos, bool broadcastUpdate = true ); - void setPos( float x, float y, float z ); + void setPos( float x, float y, float z, bool broadcastUpdate = true ); float getRot() const; diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 8395f549..32a1465f 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -90,18 +90,35 @@ 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; + auto exdData = m_pFw->get< Data::ExdDataGenerated >(); + assert( exdData ); + + auto bNpcBaseData = exdData->get< Data::BNpcBase >( m_bNpcBaseId ); + assert( bNpcBaseData ); + + m_scale = bNpcBaseData->scale; + + // todo: is this actually good? + m_naviTargetReachedDistance = m_scale * 2.f; } -Sapphire::Entity::BNpc::~BNpc() -{ -} +Sapphire::Entity::BNpc::~BNpc() = default; uint8_t Sapphire::Entity::BNpc::getAggressionMode() const { return m_aggressionMode; } +float Sapphire::Entity::BNpc::getNaviTargetReachedDistance() const +{ + return m_naviTargetReachedDistance; +} + +float Sapphire::Entity::BNpc::getScale() const +{ + return m_scale; +} + uint8_t Sapphire::Entity::BNpc::getEnemyType() const { return m_enemyType; @@ -145,7 +162,7 @@ void Sapphire::Entity::BNpc::spawn( PlayerPtr pTarget ) void Sapphire::Entity::BNpc::despawn( PlayerPtr pTarget ) { pTarget->freePlayerSpawnId( getId() ); - pTarget->queuePacket( makeActorControl143( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ) ); + pTarget->queuePacket( makeActorControl143( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ) ); } Sapphire::Entity::BNpcState Sapphire::Entity::BNpc::getState() const @@ -203,7 +220,11 @@ void Sapphire::Entity::BNpc::step() bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) { - if( Util::distance( getPos(), pos ) <= 4 ) + // do this first, this will update local actor position and the position of other actors + // and then this npc will then path from the position after pushing/being pushed + pushNearbyBNpcs(); + + if( Util::distance( getPos(), pos ) <= m_naviTargetReachedDistance ) { // Reached destination m_naviLastPath.clear(); @@ -402,7 +423,6 @@ void Sapphire::Entity::BNpc::onTick() void Sapphire::Entity::BNpc::update( int64_t currTime ) { const uint8_t minActorDistance = 4; - const uint8_t aggroRange = 8; const uint8_t maxDistanceToOrigin = 40; const uint32_t roamTick = 20; @@ -441,7 +461,7 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) m_state = BNpcState::Idle; } - checkAggro( aggroRange ); + checkAggro(); } break; @@ -462,7 +482,7 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) m_state = BNpcState::Roaming; } - checkAggro( aggroRange ); + checkAggro(); } case BNpcState::Combat: @@ -574,7 +594,7 @@ void Sapphire::Entity::BNpc::setTimeOfDeath( uint32_t timeOfDeath ) m_timeOfDeath = timeOfDeath; } -void Sapphire::Entity::BNpc::checkAggro( uint32_t range ) +void Sapphire::Entity::BNpc::checkAggro() { // passive mobs should ignore players unless aggro'd if( m_aggressionMode == 1 ) @@ -582,14 +602,61 @@ void Sapphire::Entity::BNpc::checkAggro( uint32_t range ) CharaPtr pClosestChara = getClosestChara(); - if( pClosestChara && pClosestChara->isAlive() ) + if( pClosestChara && pClosestChara->isAlive() && pClosestChara->isPlayer() ) { + // will use this range if chara level is lower than bnpc, otherwise diminishing equation applies + float range = 13.f; + + if( pClosestChara->getLevel() > m_level ) + { + auto levelDiff = std::abs( pClosestChara->getLevel() - this->getLevel() ); + + if( levelDiff >= 10 ) + range = 0.f; + else + range = std::max< float >( 0.f, range - std::pow( 1.53f, levelDiff * 0.6f ) ); + } + auto distance = Util::distance( getPos().x, getPos().y, getPos().z, pClosestChara->getPos().x, pClosestChara->getPos().y, pClosestChara->getPos().z ); - if( distance < range && pClosestChara->isPlayer() ) + if( distance < range ) + { aggro( pClosestChara ); + } } } + +void Sapphire::Entity::BNpc::pushNearbyBNpcs() +{ + for( auto& bNpc : m_inRangeBNpc ) + { + auto pos = bNpc->getPos(); + auto distance = Util::distance( m_pos, bNpc->getPos() ); + + // todo: not sure what's good here + auto factor = bNpc->getNaviTargetReachedDistance(); + + auto delta = static_cast< float >( Util::getTimeMs() - bNpc->getLastUpdateTime() ) / 1000.f; + delta = std::min< float >( factor, delta ); + + // too far away, ignore it + if( distance > factor ) + continue; + + auto angle = Util::calcAngFrom( m_pos.x, m_pos.y, pos.x, pos.y ) + PI; + + auto x = ( cosf( angle ) ); + auto z = ( sinf( angle ) ); + + bNpc->setPos( pos.x + ( x * factor * delta ), + pos.y, + pos.z + ( z * factor * delta ), false ); + +// setPos( m_pos.x + ( xBase * -pushDistance ), +// m_pos.y, +// m_pos.z + ( zBase * -pushDistance ) ); + } +} \ No newline at end of file diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index 40b6cae4..429203ec 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -60,6 +60,9 @@ namespace Sapphire::Entity uint8_t getAggressionMode() const; + float getScale() const; + float getNaviTargetReachedDistance() const; + // return true if it reached the position bool moveTo( const Common::FFXIVARR_POSITION3& pos ); @@ -93,7 +96,9 @@ namespace Sapphire::Entity void regainHp(); - void checkAggro( uint32_t range ); + void checkAggro(); + + void pushNearbyBNpcs(); private: uint32_t m_bNpcBaseId; @@ -108,6 +113,9 @@ namespace Sapphire::Entity uint32_t m_displayFlags; uint8_t m_level; + float m_scale; + float m_naviTargetReachedDistance; + uint32_t m_timeOfDeath; uint32_t m_lastRoamTargetReached; diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index ad81acf6..8002db79 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -40,6 +40,10 @@ Sapphire::Entity::Chara::Chara( ObjKind type, FrameworkPtr pFw ) : m_targetId( INVALID_GAME_OBJECT_ID64 ), m_pFw( std::move( std::move( pFw ) ) ) { + + m_lastTickTime = 0; + m_lastUpdate = 0; + // initialize the free slot queue for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ ) { @@ -791,3 +795,7 @@ bool Sapphire::Entity::Chara::hasStatusEffect( uint32_t id ) return m_statusEffectMap.find( id ) != m_statusEffectMap.end(); } +int64_t Sapphire::Entity::Chara::getLastUpdateTime() const +{ + return m_lastUpdate; +} diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 896404b5..fcf38ded 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -118,6 +118,8 @@ namespace Sapphire::Entity virtual void calculateStats() {}; + int64_t getLastUpdateTime() const; + /// Status effect functions void addStatusEffect( StatusEffect::StatusEffectPtr pEffect ); diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index 4728f667..b1bd4530 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -636,14 +636,14 @@ void Sapphire::Network::GameConnection::tellHandler( FrameworkPtr pFw, return; } - if( pTargetPlayer->hasStateFlag( PlayerStateFlag::BoundByDuty ) ) + if( pTargetPlayer->hasStateFlag( PlayerStateFlag::BoundByDuty ) && !player.isActingAsGm() ) { // send error for player bound by duty // TODO: implement me return; } - if( pTargetPlayer->getOnlineStatus() == OnlineStatus::Busy ) + if( pTargetPlayer->getOnlineStatus() == OnlineStatus::Busy && !player.isActingAsGm() ) { // send error for player being busy // TODO: implement me ( i've seen this done with packet type 67 i think ) @@ -656,16 +656,13 @@ void Sapphire::Network::GameConnection::tellHandler( FrameworkPtr pFw, // TODO: world id from server tellPacket->data().contentId = player.getContentId(); tellPacket->data().worldId = 67; - // TODO: do these have a meaning? - //tellPacket.data().u1 = 0x92CD7337; - //tellPacket.data().u2a = 0x2E; - //tellPacket.data().u2b = 0x40; + if( player.isActingAsGm() ) { - tellPacket->data().preName = 0x04; + tellPacket->data().flags |= TellFlags::GmTellMsg; } - pTargetPlayer->queueChatPacket( tellPacket ); + pTargetPlayer->queueChatPacket( tellPacket ); } void Sapphire::Network::GameConnection::performNoteHandler( FrameworkPtr pFw, diff --git a/src/world/Territory/Zone.h b/src/world/Territory/Zone.h index ee37ce0a..1e0d359f 100644 --- a/src/world/Territory/Zone.h +++ b/src/world/Territory/Zone.h @@ -49,7 +49,7 @@ namespace Sapphire Common::Weather m_currentWeather; Common::Weather m_weatherOverride; - uint64_t m_lastMobUpdate; + int64_t m_lastMobUpdate; FestivalPair m_currentFestival;