From d244ad437bff82d5ad0b247986a2e87d5da79c59 Mon Sep 17 00:00:00 2001 From: Mordred Date: Thu, 30 Dec 2021 13:57:08 +0100 Subject: [PATCH] Spawn overworld mobs with accurate MaxHp --- src/world/Actor/BNpc.cpp | 16 +++++++++-- src/world/Actor/BNpc.h | 15 +++++++++++ src/world/Math/CalcStats.cpp | 44 +++++++++++++++++++++++++------ src/world/Math/CalcStats.h | 1 + src/world/Territory/Territory.cpp | 2 ++ 5 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 60db084b..1111561f 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -95,7 +95,7 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstance m_modelChara = bNpcBaseData->data().Model; m_enemyType = bNpcBaseData->data().Battalion; - m_class = ClassJob::Adventurer; + m_class = ClassJob::Gladiator; m_pCurrentTerritory = std::move( pZone ); @@ -109,6 +109,9 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstance m_hp = m_maxHp; m_mp = 200; + if( m_level <= BnpcBaseHp.size() ) + m_maxHp = BnpcBaseHp[ m_level - 1 ]; + m_state = BNpcState::Idle; m_status = ActorStatus::Idle; @@ -153,6 +156,8 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstance m_naviTargetReachedDistance = 4.f; calculateStats(); + + } Sapphire::Entity::BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstanceObject > pInfo, TerritoryPtr pZone, uint32_t hp, Common::BNpcType type ) : @@ -198,7 +203,7 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, std::shared_ptr< Common::BNPCInstance m_modelChara = bNpcBaseData->data().Model; m_enemyType = bNpcBaseData->data().Battalion; - m_class = ClassJob::Adventurer; + m_class = ClassJob::Gladiator; m_pCurrentTerritory = std::move( pZone ); @@ -962,3 +967,10 @@ uint32_t Sapphire::Entity::BNpc::getLayoutId() const { return m_layoutId; } + +void Sapphire::Entity::BNpc::init() +{ + m_maxHp = Sapphire::Math::CalcStats::calculateMaxHp( *getAsChara() ); + m_hp = m_maxHp; + max_hp = m_maxHp; +} diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index 95d92ccf..9078b97d 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -39,6 +39,19 @@ namespace Sapphire::Entity Untargetable = 32, }; + const std::array< uint32_t, 50 > BnpcBaseHp = + { 44,51, 59, 68, 91, + 108, 126, 143, 160, 192, + 217, 243, 268, 293, 319, + 344, 369, 394, 420, 413, + 458, 493, 532, 568, 594, + 641, 677,714, 750, 780, + 887, 965, 1055, 1142, 1220, + 1306, 1409, 1515, 1587, 1601, + 1703, 1789, 1872, 2008, 2112, + 2180, 2314, 2383, 2501, 2589 + }; + /*! \class BNpc \brief Base class for all BNpcs @@ -55,6 +68,8 @@ namespace Sapphire::Entity virtual ~BNpc() override; + void init(); + void spawn( PlayerPtr pTarget ) override; void despawn( PlayerPtr pTarget ) override; diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index 87a7f12f..bf5c7106 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -151,6 +151,36 @@ uint32_t CalcStats::calculateMaxHp( Player& player ) return result; } +uint32_t CalcStats::calculateMaxHp( Chara& chara ) +{ + auto& exdData = Common::Service< Data::ExdData >::ref(); + // TODO: Replace ApproxBaseHP with something that can get us an accurate BaseHP. + // Is there any way to pull reliable BaseHP without having to manually use a pet for every level, and using the values from a table? + // More info here: https://docs.google.com/spreadsheets/d/1de06KGT0cNRUvyiXNmjNgcNvzBCCQku7jte5QxEQRbs/edit?usp=sharing + + auto classInfo = exdData.getRow< Component::Excel::ClassJob >( static_cast< uint8_t >( chara.getClass() ) ); + auto paramGrowthInfo = exdData.getRow< Component::Excel::ParamGrow >( chara.getLevel() ); + + if( !classInfo || !paramGrowthInfo ) + return 0; + + uint8_t level = chara.getLevel(); + + auto vitMod = chara.getBonusStat( Common::BaseParam::Vitality ); + float baseStat = calculateBaseStat( chara ); + uint16_t vitStat = static_cast< uint16_t >( chara.getStatValue( Common::BaseParam::Vitality ) ) + static_cast< uint16_t >( 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 ) ) + + floor( hpMod / 100.0f * ( vitStat - baseStat ) ) ); + + return result; +} + uint32_t CalcStats::calculateMaxMp( Player& player ) { auto& exdData = Common::Service< Data::ExdData >::ref(); @@ -311,7 +341,8 @@ float CalcStats::autoAttackPotency( const Sapphire::Entity::Chara& chara ) } // factors in f(PTC) in order to not lose precision - return std::floor( aaPotency / 3.f * autoAttackDelay ) / 100.f; + //return std::floor( aaPotency / 3.f * autoAttackDelay ) / 100.f; + return std::floor( aaPotency / 100.f ); } float CalcStats::weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage ) @@ -498,7 +529,7 @@ float CalcStats::autoAttack( const Sapphire::Entity::Chara& chara ) auto level = chara.getLevel(); auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); - auto innerCalc = std::floor( ( mainVal * primaryStatValue( chara ) / 1000.f ) + weaponDamage ); + auto innerCalc = std::floor( ( mainVal * static_cast< float >( primaryStatValue( chara ) ) / 1000.f ) + weaponDamage ); return std::floor( innerCalc * ( autoAttackDelay / 3.f ) ); } @@ -518,12 +549,9 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA auto ap = getPrimaryAttackPower( chara ); auto det = determination( chara ); - auto ten = 1.f; - if( chara.getRole() == Common::Role::Tank ) - ten = tenacity( chara ); // todo: everything after tenacity - auto factor = std::floor( pot * aa * ap * det * ten ); + auto factor = std::floor( pot * aa * ap * det ); Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage; // todo: traits @@ -552,14 +580,14 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoA if( auto player = const_cast< Entity::Chara& >( chara ).getAsPlayer() ) { - PlayerMgr::sendDebug( *player, format, pot, aa, ap, det, ten, factor ); + PlayerMgr::sendDebug( *player, format, pot, aa, ap, det, 1, factor ); } else { // Logger::debug( format, pot, aa, ap, det, ten, factor ); } - return std::pair( factor, hitType ); + return std::pair( factor * 3, hitType ); } std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ) diff --git a/src/world/Math/CalcStats.h b/src/world/Math/CalcStats.h index eb4c59e2..87625067 100644 --- a/src/world/Math/CalcStats.h +++ b/src/world/Math/CalcStats.h @@ -16,6 +16,7 @@ namespace Sapphire::Math static float calculateBaseStat( const Entity::Chara& chara ); static uint32_t calculateMaxHp( Sapphire::Entity::Player& player ); + static uint32_t calculateMaxHp( Sapphire::Entity::Chara& chara ); static uint32_t calculateMaxMp( Sapphire::Entity::Player& player ); diff --git a/src/world/Territory/Territory.cpp b/src/world/Territory/Territory.cpp index 32887e27..caf84feb 100644 --- a/src/world/Territory/Territory.cpp +++ b/src/world/Territory/Territory.cpp @@ -41,6 +41,7 @@ #include "Manager/RNGMgr.h" #include "Manager/NaviMgr.h" +#include "Math/CalcStats.h" using namespace Sapphire::Common; using namespace Sapphire::Network::Packets; @@ -833,6 +834,7 @@ void Sapphire::Territory::updateSpawnPoints() { auto& server = Common::Service< World::WorldServer >::ref(); auto pBNpc = std::make_shared< Entity::BNpc >( getNextActorId(), spawn.infoPtr, shared_from_this() ); + pBNpc->init(); spawn.bnpcPtr = pBNpc; pushActor( pBNpc );