1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-29 07:37:45 +00:00

cleanup stat calculation code, slightly better working aa dmg calc

This commit is contained in:
NotAdam 2019-04-25 21:57:41 +10:00
parent 8c468dd38c
commit 2ab91b89dc
5 changed files with 191 additions and 72 deletions

View file

@ -21,7 +21,7 @@
#include "Action/Action.h" #include "Action/Action.h"
#include "ServerMgr.h" #include "ServerMgr.h"
#include "Session.h" #include "Session.h"
#include "Math/CalcBattle.h" #include "Math/CalcStats.h"
#include "Chara.h" #include "Chara.h"
#include "Player.h" #include "Player.h"
#include "Manager/TerritoryMgr.h" #include "Manager/TerritoryMgr.h"
@ -729,4 +729,132 @@ Sapphire::Common::BaseParam Sapphire::Entity::Chara::getPrimaryStat() const
assert( classJob ); assert( classJob );
return static_cast< Sapphire::Common::BaseParam >( classJob->primaryStat ); return static_cast< Sapphire::Common::BaseParam >( classJob->primaryStat );
}
uint32_t Sapphire::Entity::Chara::getStatValue( Sapphire::Common::BaseParam baseParam ) const
{
uint32_t value = 0;
switch( baseParam )
{
case Common::BaseParam::Strength:
{
value = m_baseStats.str;
break;
}
case Common::BaseParam::Dexterity:
{
value = m_baseStats.dex;
break;
}
case Common::BaseParam::Vitality:
{
value = m_baseStats.vit;
break;
}
case Common::BaseParam::Intelligence:
{
value = m_baseStats.inte;
break;
}
case Common::BaseParam::Mind:
{
value = m_baseStats.mnd;
break;
}
case Common::BaseParam::Piety:
{
value = m_baseStats.pie;
break;
}
case Common::BaseParam::Determination:
{
value = m_baseStats.determination;
break;
}
case Common::BaseParam::HP:
{
value = m_baseStats.max_hp;
break;
}
case Common::BaseParam::MP:
{
value = m_baseStats.max_mp;
break;
}
case Common::BaseParam::AttackPower:
{
value = m_baseStats.attack;
break;
}
case Common::BaseParam::AttackMagicPotency:
{
value = m_baseStats.attackPotMagic;
break;
}
case Common::BaseParam::HealingMagicPotency:
{
value = m_baseStats.healingPotMagic;
break;
}
case Common::BaseParam::SkillSpeed:
{
value = m_baseStats.skillSpeed;
break;
}
case Common::BaseParam::SpellSpeed:
{
value = m_baseStats.spellSpeed;
break;
}
case Common::BaseParam::CriticalHit:
{
value = m_baseStats.critHitRate;
break;
}
case Common::BaseParam::Defense:
{
value = m_baseStats.defense;
break;
}
case Common::BaseParam::MagicDefense:
{
value = m_baseStats.magicDefense;
break;
}
case Common::BaseParam::Tenacity:
{
value = m_baseStats.tenacity;
break;
}
// todo: not sure if this is right?
case Common::BaseParam::DirectHitRate:
{
value = m_baseStats.accuracy;
break;
}
default:
break;
}
return value + getBonusStat( baseParam );
} }

View file

@ -191,6 +191,8 @@ namespace Sapphire::Entity
ActorStats getStats() const; ActorStats getStats() const;
uint32_t getStatValue( Common::BaseParam baseParam ) const;
uint32_t getHp() const; uint32_t getHp() const;
uint32_t getHpPercent() const; uint32_t getHpPercent() const;

View file

@ -313,28 +313,28 @@ void Sapphire::Entity::Player::sendStats()
{ {
auto statPacket = makeZonePacket< FFXIVIpcPlayerStats >( getId() ); auto statPacket = makeZonePacket< FFXIVIpcPlayerStats >( getId() );
statPacket->data().strength = m_baseStats.str + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Strength ) ]; statPacket->data().strength = getStatValue( Common::BaseParam::Strength );
statPacket->data().dexterity = m_baseStats.dex + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Dexterity ) ]; statPacket->data().dexterity = getStatValue( Common::BaseParam::Dexterity );
statPacket->data().vitality = m_baseStats.vit + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Vitality ) ]; statPacket->data().vitality = getStatValue( Common::BaseParam::Vitality );
statPacket->data().intelligence = m_baseStats.inte + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Intelligence ) ]; statPacket->data().intelligence = getStatValue( Common::BaseParam::Intelligence );
statPacket->data().mind = m_baseStats.mnd + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Mind ) ]; statPacket->data().mind = getStatValue( Common::BaseParam::Mind );
statPacket->data().piety = m_baseStats.pie + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Piety ) ]; statPacket->data().piety = getStatValue( Common::BaseParam::Piety );
statPacket->data().determination = m_baseStats.determination + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Determination ) ]; statPacket->data().determination = getStatValue( Common::BaseParam::Determination );
statPacket->data().hp = m_baseStats.max_hp + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::HP ) ]; statPacket->data().hp = getStatValue( Common::BaseParam::HP );
statPacket->data().mp = m_baseStats.max_mp + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MP ) ]; statPacket->data().mp = getStatValue( Common::BaseParam::MP );
statPacket->data().accuracy = m_baseStats.accuracy; statPacket->data().accuracy = m_baseStats.accuracy;
statPacket->data().attack = m_baseStats.attack + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::AttackPower ) ]; statPacket->data().attack = getStatValue( Common::BaseParam::AttackPower );
statPacket->data().attackMagicPotency = m_baseStats.attackPotMagic + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::AttackMagicPotency ) ]; statPacket->data().attackMagicPotency = getStatValue( Common::BaseParam::AttackMagicPotency );
statPacket->data().healingMagicPotency = m_baseStats.healingPotMagic + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::HealingMagicPotency ) ]; statPacket->data().healingMagicPotency = getStatValue( Common::BaseParam::HealingMagicPotency );
statPacket->data().skillSpeed = m_baseStats.skillSpeed + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::SkillSpeed ) ]; statPacket->data().skillSpeed = getStatValue( Common::BaseParam::SkillSpeed );
statPacket->data().spellSpeed = m_baseStats.spellSpeed + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::SpellSpeed ) ]; statPacket->data().spellSpeed = getStatValue( Common::BaseParam::SpellSpeed );
statPacket->data().spellSpeed1 = m_baseStats.spellSpeed + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::SpellSpeed ) ]; statPacket->data().spellSpeed1 = getStatValue( Common::BaseParam::SpellSpeed );
statPacket->data().spellSpeedMod = 100; statPacket->data().spellSpeedMod = 100;
statPacket->data().criticalHitRate = m_baseStats.critHitRate + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::CriticalHit ) ]; statPacket->data().criticalHitRate = getStatValue( Common::BaseParam::CriticalHit );
statPacket->data().defense = m_baseStats.defense + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Defense ) ]; statPacket->data().defense = getStatValue( Common::BaseParam::Defense );
statPacket->data().magicDefense = m_baseStats.magicDefense + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::MagicDefense ) ]; statPacket->data().magicDefense = getStatValue( Common::BaseParam::MagicDefense );
statPacket->data().tenacity = m_baseStats.tenacity + m_bonusStats[ static_cast< uint8_t >( Common::BaseParam::Tenacity ) ]; statPacket->data().tenacity = getStatValue( Common::BaseParam::Tenacity );
queuePacket( statPacket ); queuePacket( statPacket );
} }

View file

@ -270,7 +270,7 @@ uint16_t CalcStats::calculateMpCost( const Sapphire::Entity::Chara& chara, uint1
float CalcStats::blockProbability( const Chara& chara ) float CalcStats::blockProbability( const Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
auto blockRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::BlockRate ) ); auto blockRate = static_cast< float >( chara.getStatValue( Common::BaseParam::BlockRate ) );
auto levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( ( 30 * blockRate ) / levelVal + 10 ); return std::floor( ( 30 * blockRate ) / levelVal + 10 );
@ -281,8 +281,7 @@ float CalcStats::directHitProbability( const Chara& chara )
const auto& baseStats = chara.getStats(); const auto& baseStats = chara.getStats();
auto level = chara.getLevel(); auto level = chara.getLevel();
float dhRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::DirectHitRate ) ) + float dhRate = chara.getStatValue( Common::BaseParam::DirectHitRate );
baseStats.accuracy;
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
@ -295,8 +294,7 @@ float CalcStats::criticalHitProbability( const Chara& chara )
const auto& baseStats = chara.getStats(); const auto& baseStats = chara.getStats();
auto level = chara.getLevel(); auto level = chara.getLevel();
float chRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::CriticalHit ) ) + float chRate = chara.getStatValue( Common::BaseParam::CriticalHit );
baseStats.critHitRate;
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
@ -348,61 +346,70 @@ float CalcStats::calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t
auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
// todo: not sure if its ( ap - mv ) / mv or ( ap - mv ) / dv
return std::floor( ( 125.f * ( attackPower - mainVal ) / divVal ) + 100.f ) / 100.f; return std::floor( ( 125.f * ( attackPower - mainVal ) / divVal ) + 100.f ) / 100.f;
} }
float CalcStats::attackPower( const Sapphire::Entity::Chara& chara ) float CalcStats::getPrimaryAttackPower( const Sapphire::Entity::Chara& chara )
{ {
const auto& baseStats = chara.getStats(); const auto& baseStats = chara.getStats();
// todo: this is wrong
if( chara.isBattleNpc() )
return calcAttackPower( chara, baseStats.attack );
switch( chara.getPrimaryStat() ) switch( chara.getPrimaryStat() )
{ {
case Common::BaseParam::Mind: case Common::BaseParam::Mind:
{ {
return calcAttackPower( chara, baseStats.healingPotMagic ); return healingMagicPower( chara );
} }
case Common::BaseParam::Intelligence: case Common::BaseParam::Intelligence:
{ {
return calcAttackPower( chara, baseStats.attackPotMagic ); return magicAttackPower( chara );
} }
default: default:
{ {
return calcAttackPower( chara, baseStats.attack ); return attackPower( chara );
} }
} }
} }
float CalcStats::attackPower( const Sapphire::Entity::Chara& chara )
{
return calcAttackPower( chara, chara.getStatValue( Common::BaseParam::AttackPower ) );
}
float CalcStats::magicAttackPower( const Sapphire::Entity::Chara& chara )
{
return calcAttackPower( chara, chara.getStatValue( Common::BaseParam::AttackMagicPotency ) );
}
float CalcStats::healingMagicPower( const Sapphire::Entity::Chara& chara )
{
return calcAttackPower( chara, chara.getStatValue( Common::BaseParam::HealingMagicPotency ) );
}
float CalcStats::determination( const Sapphire::Entity::Chara& chara ) float CalcStats::determination( const Sapphire::Entity::Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
const auto& baseStats = chara.getStats();
auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( 130.f * ( baseStats.determination - mainVal ) / divVal + 1000.f ) / 1000.f; return std::floor( 130.f * ( chara.getStatValue( Common::BaseParam::Determination ) - mainVal ) / divVal + 1000.f ) / 1000.f;
} }
float CalcStats::tenacity( const Sapphire::Entity::Chara& chara ) float CalcStats::tenacity( const Sapphire::Entity::Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
const auto& baseStats = chara.getStats();
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( 100.f * ( baseStats.tenacity - subVal ) / divVal + 1000.f ) / 1000.f; return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::Tenacity ) - subVal ) / divVal + 1000.f ) / 1000.f;
} }
float CalcStats::speed( const Sapphire::Entity::Chara& chara ) float CalcStats::speed( const Sapphire::Entity::Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
const auto& baseStats = chara.getStats();
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
@ -414,11 +421,11 @@ float CalcStats::speed( const Sapphire::Entity::Chara& chara )
{ {
case Common::BaseParam::Intelligence: case Common::BaseParam::Intelligence:
case Common::BaseParam::Mind: case Common::BaseParam::Mind:
speedVal = baseStats.spellSpeed; speedVal = chara.getStatValue( Common::BaseParam::SpellSpeed );
break; break;
default: default:
speedVal = baseStats.skillSpeed; speedVal = chara.getStatValue( Common::BaseParam::SkillSpeed );
} }
return std::floor( 130.f * ( speedVal - subVal ) / divVal + 1000.f ) / 1000.f; return std::floor( 130.f * ( speedVal - subVal ) / divVal + 1000.f ) / 1000.f;
@ -427,32 +434,29 @@ float CalcStats::speed( const Sapphire::Entity::Chara& chara )
float CalcStats::criticalHitBonus( const Sapphire::Entity::Chara& chara ) float CalcStats::criticalHitBonus( const Sapphire::Entity::Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
const auto& baseStats = chara.getStats();
auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( 200.f * ( baseStats.critHitRate - subVal ) / divVal + 1400.f ) / 1000.f; return std::floor( 200.f * ( chara.getStatValue( Common::BaseParam::CriticalHit ) - subVal ) / divVal + 1400.f ) / 1000.f;
} }
float CalcStats::physicalDefence( const Sapphire::Entity::Chara& chara ) float CalcStats::physicalDefence( const Sapphire::Entity::Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
const auto& baseStats = chara.getStats();
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( 15.f * baseStats.defense ) / 100.f; return std::floor( 15.f * chara.getStatValue( Common::BaseParam::Defense ) ) / 100.f;
} }
float CalcStats::magicDefence( const Sapphire::Entity::Chara& chara ) float CalcStats::magicDefence( const Sapphire::Entity::Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
const auto& baseStats = chara.getStats();
auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( 15.f * baseStats.magicDefense ) / 100.f; return std::floor( 15.f * chara.getStatValue( Common::BaseParam::MagicDefense ) ) / 100.f;
} }
float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara ) float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara )
@ -494,9 +498,7 @@ float CalcStats::autoAttack( const Sapphire::Entity::Chara& chara )
float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara ) float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara )
{ {
const auto& baseStats = chara.getStats(); return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::HealingMagicPotency ) - 292.f ) / 264.f + 100.f ) / 100.f;
return std::floor( 100.f * ( baseStats.healingPotMagic - 292.f ) / 264.f + 100.f ) / 100.f;
} }
float CalcStats::calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara ) float CalcStats::calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara )
@ -506,11 +508,11 @@ float CalcStats::calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara
auto pot = potency( AUTO_ATTACK_POTENCY ); auto pot = potency( AUTO_ATTACK_POTENCY );
auto aa = autoAttack( chara ); auto aa = autoAttack( chara );
auto ap = attackPower( chara ); auto ap = getPrimaryAttackPower( chara );
auto det = determination( chara ); auto det = determination( chara );
auto ten = tenacity( chara ); auto ten = tenacity( chara );
Logger::info( "auto attack: pot: {} aa: {} ap: {} det: {} ten: {}", pot, aa, ap, det, ten ); Logger::debug( "auto attack: pot: {} aa: {} ap: {} det: {} ten: {}", pot, aa, ap, det, ten );
auto factor = std::floor( pot * aa * ap * det * ten ); auto factor = std::floor( pot * aa * ap * det * ten );
@ -531,24 +533,5 @@ float CalcStats::calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara
uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara ) uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara )
{ {
const auto& baseStats = chara.getStats(); return chara.getStatValue( chara.getPrimaryStat() );
switch( chara.getPrimaryStat() )
{
case Common::BaseParam::Strength:
default:
return baseStats.str;
case Common::BaseParam::Intelligence:
return baseStats.inte;
case Common::BaseParam::Mind:
return baseStats.mnd;
case Common::BaseParam::Vitality:
return baseStats.vit;
case Common::BaseParam::Dexterity:
return baseStats.dex;
}
} }

View file

@ -63,8 +63,14 @@ namespace Sapphire::Math
* *
* @param chara The source/casting character. * @param chara The source/casting character.
*/ */
static float getPrimaryAttackPower( const Sapphire::Entity::Chara& chara );
static float attackPower( const Sapphire::Entity::Chara& chara ); static float attackPower( const Sapphire::Entity::Chara& chara );
static float magicAttackPower( const Sapphire::Entity::Chara& chara );
static float healingMagicPower( const Sapphire::Entity::Chara& chara );
/*! /*!
* @brief Calculates determinations contribution to damage and healing output. * @brief Calculates determinations contribution to damage and healing output.
* *
@ -132,6 +138,7 @@ namespace Sapphire::Math
static float calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara ); static float calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara );
static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara );
private: private:
/*! /*!
@ -141,7 +148,6 @@ namespace Sapphire::Math
*/ */
static float calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t attackPower ); static float calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t attackPower );
static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara );
}; };