diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index bc99cf3e..ca4f90fa 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -2153,6 +2153,64 @@ Sapphire::Entity::Player::FriendListIDVec& Sapphire::Entity::Player::getBlacklis return m_blacklist; } +void Sapphire::Entity::Player::setFalling( bool state, const Common::FFXIVARR_POSITION3& pos, bool ignoreDamage ) +{ + bool isFalling = m_falling; + auto initialPos = m_initialFallPos; + + // update internal values - only use scoped values for old state + m_falling = state; + m_initialFallPos = pos; + + if( ignoreDamage ) + return; + + // if the player is currently falling and new state is grounded - calc and apply fall dmg + if( isFalling && !state ) + { + // calc height difference + auto fallHeight = initialPos.y - pos.y; + + // if we've hit the breakpoint in fall damage (min: 10y) + if( fallHeight >= 10.f ) + { + // calculate how much damage to deal out (max. 20y : 100%) + float deltaMax = std::min( fallHeight, 20.f ); + + // get hp percentage starting from 0.1, increasing to 100% at max height + float hpPer = std::min( 0.1f + ( deltaMax - 10.f ) / 10.f, 1.f ); + + uint32_t damage = getMaxHp() * hpPer; + + // check if player has aggro - if not, player should "live" + if( m_actorIdTohateSlotMap.empty() ) + { + // "trick" client into thinking we took more damage than internally passed to takeDamage, if > playerHp + uint32_t surviveDamage = damage; + + if( surviveDamage >= getHp() ) + { + surviveDamage = ( getHp() - 1 ); + } + + takeDamage( surviveDamage ); + } + else + { + // no mercy on hated players + takeDamage( damage ); + } + + sendToInRangeSet( makeActorControl( getId(), DmgTakenMsg, damage ), true ); + } + } +} + +bool Sapphire::Entity::Player::isFalling() const +{ + return m_falling; +} + void Sapphire::Entity::Player::setLastPcSearchResult( std::vector< uint32_t > result ) { m_lastPcSearch = std::move( result ); diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 0830e567..cc76b759 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -821,7 +821,9 @@ namespace Sapphire::Entity uint64_t m_lastMoveTime{}; uint8_t m_lastMoveflag{}; - bool m_falling; + + void setFalling( bool state, const Common::FFXIVARR_POSITION3& pos, bool ignoreDamage = false ); + bool isFalling() const; // todo: sort this requestkey pcsearch mess void setLastPcSearchResult( std::vector< uint32_t > result ); @@ -844,6 +846,10 @@ namespace Sapphire::Entity uint8_t m_mode{}; + // falling logic + bool m_falling; + Common::FFXIVARR_POSITION3 m_initialFallPos{}; + bool m_markedForRemoval; bool m_directorInitialized; diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index 32ba3e2d..017f1780 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -232,13 +232,10 @@ void Sapphire::Network::GameConnection::moveHandler( const Packets::FFXIVARR_PAC if( ( player.getCurrentAction() != nullptr ) && bPosChanged ) player.getCurrentAction()->setInterrupted( Common::ActionInterruptType::RegularInterrupt ); - // if no one is in range, don't bother trying to send a position update - if( !player.hasInRangeActor() ) - return; - auto clientAnimationType = data.flag; auto animationState = data.flag; auto animationType = data.flag2; + auto animColType = data.flag2; auto headRotation = data.flag_unshared; uint8_t orgAnimationType = animationType; uint8_t unknownRotation = 0; @@ -262,34 +259,42 @@ void Sapphire::Network::GameConnection::moveHandler( const Packets::FFXIVARR_PAC } if( animationType & MoveType::Jumping ) { - if( animationState == MoveState::LeaveCollision ) + + if( animColType == MoveState::LeaveCollision ) { if( orgAnimationType & clientAnimationType ) animationType += 0x10; else animationType += 0x04; } - if( animationState == MoveState::StartFalling ) - player.m_falling = true; - if( animationState == MoveState::EnterCollision ) + + if( animColType == MoveState::LeaveCollision || animColType == MoveState::StartFalling ) { - animationType = 2; - player.m_falling = false; + player.setFalling( true, { data.pos.x, data.pos.y, data.pos.z } ); } } - if( player.m_falling ) + if( animColType == MoveState::EnterCollision ) + { + animationType = 2; + player.setFalling( false, { data.pos.x, data.pos.y, data.pos.z } ); + } + + if( player.isFalling() ) { animationType += 0x10; unknownRotation = 0x7F; } - uint64_t currentTime = Util::getTimeMs(); player.m_lastMoveTime = currentTime; player.m_lastMoveflag = animationType; + // if no one is in range, don't bother trying to send a position update + if( !player.hasInRangeActor() ) + return; + //auto movePacket = std::make_shared< MoveActorPacket >( player, headRotation, animationType, animationState, animationSpeed, unknownRotation ); auto movePacket = std::make_shared< MoveActorPacket >( player, headRotation, data.flag, data.flag2, animationSpeed, unknownRotation ); player.sendToInRangeSet( movePacket );