diff --git a/src/common/CommonGen.h b/src/common/CommonGen.h index 69a1063f..c93a77ea 100644 --- a/src/common/CommonGen.h +++ b/src/common/CommonGen.h @@ -169,12 +169,7 @@ enum class ClassJob : uint8_t Ninja = 30, Machinist = 31, Darkknight = 32, - Astrologian = 33, - Samurai = 34, - Redmage = 35, - Bluemage = 36, - Gunbreaker = 37, - Dancer = 38, + Astrologian = 33 }; /////////////////////////////////////////////////////////// diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index aa880e08..7ade7aa1 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -375,14 +375,13 @@ void Action::Action::execute() assert( m_pSource ); // subtract costs first, if somehow the caster stops meeting those requirements cancel the cast - /* - // TODO: need to be fixed + if( !consumeResources() ) { interrupt(); return; } - */ + auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); @@ -685,7 +684,7 @@ bool Action::Action::primaryCostCheck( bool subtractCosts ) { auto curMp = m_pSource->getMp(); - auto cost = m_primaryCost * 100; + auto cost = Math::CalcStats::calculateMpCost( *m_pSource, m_primaryCost ); if( curMp < static_cast< uint32_t >( cost ) ) return false; diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index f71efbce..b4f517d7 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -132,7 +132,6 @@ Sapphire::Common::Role Sapphire::Entity::Chara::getRole() const case ClassJob::Paladin: case ClassJob::Warrior: case ClassJob::Darkknight: - case ClassJob::Gunbreaker: return Role::Tank; case ClassJob::Pugilist: @@ -141,13 +140,11 @@ Sapphire::Common::Role Sapphire::Entity::Chara::getRole() const case ClassJob::Dragoon: case ClassJob::Rogue: case ClassJob::Ninja: - case ClassJob::Samurai: return Role::Melee; case ClassJob::Archer: case ClassJob::Bard: case ClassJob::Machinist: - case ClassJob::Dancer: return Role::RangedPhysical; case ClassJob::Conjurer: @@ -160,8 +157,6 @@ Sapphire::Common::Role Sapphire::Entity::Chara::getRole() const case ClassJob::Blackmage: case ClassJob::Arcanist: case ClassJob::Summoner: - case ClassJob::Redmage: - case ClassJob::Bluemage: return Role::RangedMagical; case ClassJob::Carpenter: diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index bc64228c..dcc6b914 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -398,7 +398,7 @@ void Sapphire::Entity::Player::calculateStats() setStatValue( BaseParam::AttackMagicPotency, inte ); setStatValue( BaseParam::HealingMagicPotency, mnd ); - max_mp = 10000; + max_mp = Math::CalcStats::calculateMaxMp( *this ); max_hp = Math::CalcStats::calculateMaxHp( *this ); @@ -904,7 +904,6 @@ void Sapphire::Entity::Player::setInCombat( bool mode ) void Sapphire::Entity::Player::setClassJob( Common::ClassJob classJob ) { m_class = classJob; - uint8_t level = getLevel(); if( getHp() > getMaxHp() ) m_hp = getMaxHp(); diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index f61bef8e..87a7f12f 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -18,8 +18,8 @@ using namespace Sapphire::Math; using namespace Sapphire::Entity; using namespace Sapphire::World::Manager; -const int levelTable[81][6] = -{ +const int levelTable[61][6] = +{ // MAIN,SUB,DIV,HP,ELMT,THREAT { 1, 1, 1, 1, 1, 1 }, { 20, 56, 56, 86, 52, 2 }, @@ -82,29 +82,6 @@ const int levelTable[81][6] = { 215, 351, 755, 2388, 279, 186 }, { 217, 352, 806, 2492, 280, 200 }, { 218, 354, 858, 2600, 282, 215 }, - { 224, 355, 941, 2700, 283, 232 }, - { 228, 356, 1032, 2800, 284, 250 }, - { 236, 357, 1133, 2900, 286, 269 }, - { 244, 358, 1243, 3000, 287, 290 }, - { 252, 359, 1364, 3100, 288, 313 }, - { 260, 360, 1497, 3200, 290, 337 }, - { 268, 361, 1643, 3300, 292, 363 }, - { 276, 362, 1802, 3400, 293, 392 }, - { 284, 363, 1978, 3500, 294, 422 }, - { 292, 364, 2170, 3600, 295, 455 }, - - // todo: add proper shbr values - hp/elmt/threat - // sub/div added from http://theoryjerks.akhmorning.com/resources/levelmods/ - { 296, 365, 2263, 3600, 295, 466 }, - { 300, 366, 2360, 3600, 295, 466 }, - { 305, 367, 2461, 3600, 295, 466 }, - { 310, 368, 2566, 3600, 295, 466 }, - { 315, 370, 2676, 3600, 295, 466 }, - { 320, 372, 2790, 3600, 295, 466 }, - { 325, 374, 2910, 3600, 295, 466 }, - { 330, 376, 3034, 3600, 295, 466 }, - { 335, 378, 3164, 3600, 295, 466 }, - { 340, 380, 3300, 3600, 569, 569 }, }; std::random_device CalcStats::dev; @@ -166,16 +143,6 @@ uint32_t CalcStats::calculateMaxHp( Player& player ) uint16_t jobModHp = classInfo->data().Hp; float approxBaseHp = 0.0f; // Read above - // These values are not precise. - /* - if( level >= 60 ) - approxBaseHp = static_cast< float >( 2600 + ( level - 60 ) * 100 ); - else if( level >= 50 ) - approxBaseHp = 1700 + ( ( level - 50 ) * ( 1700 * 1.04325f ) ); - else - approxBaseHp = paramGrowthInfo->mpModifier * 0.7667f; - */ - // just use the table at least better than what it was approxBaseHp = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::HP ] ); uint16_t result = static_cast< uint16_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) + @@ -184,6 +151,101 @@ uint32_t CalcStats::calculateMaxHp( Player& player ) return result; } +uint32_t CalcStats::calculateMaxMp( Player& player ) +{ + auto& exdData = Common::Service< Data::ExdData >::ref(); + + auto classInfo = exdData.getRow< Component::Excel::ClassJob >( static_cast< uint8_t >( player.getClass() ) ); + auto paramGrowthInfo = exdData.getRow< Component::Excel::ParamGrow >( player.getLevel() ); + + if( !classInfo || !paramGrowthInfo ) + return 0; + + float baseStat = calculateBaseStat( player ); + uint16_t piety = player.getStats()[ static_cast< uint32_t >( Common::BaseParam::Piety ) ]; + uint16_t pietyScalar = paramGrowthInfo->data().ParamBase; + uint16_t jobModMp = classInfo->data().Mp; + uint16_t baseMp = paramGrowthInfo->data().Mp; + + uint16_t result = static_cast< uint16_t >( std::floor( floor( piety - baseStat ) * ( pietyScalar / 100 ) + baseMp ) * + jobModMp / 100 ); + + return result; +} + +uint16_t CalcStats::calculateMpCost( const Sapphire::Entity::Chara& chara, uint16_t baseCost ) +{ + auto level = chara.getLevel(); + + // each level range is 1-10, 11-20, 21-30, ... therefore: + // level 50 should be in the 4th group, not the 5t + // dividing by 10 on the border will break this unless we subtract 1 + auto levelGroup = std::max< uint8_t >( level - 1, 1 ) / 10; + + float cost = baseCost; + + // thanks to andrew for helping me figure this shit out + // played with this some more and it seems to be accurate for everything i've tried + switch( levelGroup ) + { + // level 1-10 + case 0: + { + // r^2 = 0.9999 + cost = 0.0952f * level + 0.9467f; + break; + } + + // level 11-20 + case 1: + { + // r^2 = 1 + cost = 0.19f * level; + break; + } + + // level 21-30 + case 2: + { + // r^2 = 1 + cost = 0.38f * level - 3.8f; + break; + } + + // level 31-40 + case 3: + { + // r^2 = 1 + cost = 0.6652f * level - 12.358f; + break; + } + + // level 41-50 + case 4: + { + // r^2 = 1 + cost = 1.2352f * level - 35.159f; + break; + } + + // level 51-60 + case 5: + { + // r^2 = 1 + cost = 0.0654f * std::exp( 0.1201f * level ); + break; + } + + default: + { + return 0; + } + } + + return static_cast< uint16_t >( std::round( cost * baseCost ) ); +} + + float CalcStats::blockProbability( const Chara& chara ) { auto level = chara.getLevel(); diff --git a/src/world/Math/CalcStats.h b/src/world/Math/CalcStats.h index 5e84440f..eb4c59e2 100644 --- a/src/world/Math/CalcStats.h +++ b/src/world/Math/CalcStats.h @@ -17,6 +17,17 @@ namespace Sapphire::Math static uint32_t calculateMaxHp( Sapphire::Entity::Player& player ); + static uint32_t calculateMaxMp( Sapphire::Entity::Player& player ); + + /*! + * @brief Calculates the MP cost of a spell given its base cost + * + * @param chara The Chara that is casting the action + * @param baseCost The action cost + * @return The total MP to be consumed by a successful cast + */ + static uint16_t calculateMpCost( const Sapphire::Entity::Chara& chara, uint16_t baseCost ); + /*! * @brief Calculates the probability of a block happening */