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

autoattack damage stat calc funcs, bnpc stat calculation

This commit is contained in:
NotAdam 2019-04-24 23:25:07 +10:00
parent b6a9e41cbd
commit ec3e93eed9
11 changed files with 253 additions and 95 deletions

View file

@ -1114,6 +1114,7 @@ Sapphire::Data::ClassJob::ClassJob( uint32_t row_id, Sapphire::Data::ExdDataGene
classJobParent = exdData->getField< uint8_t >( row, 26 );
nameEnglish = exdData->getField< std::string >( row, 27 );
itemStartingWeapon = exdData->getField< int32_t >( row, 28 );
primaryStat = exdData->getField< uint8_t >( row, 33 );
limitBreak1 = exdData->getField< uint16_t >( row, 34 );
limitBreak2 = exdData->getField< uint16_t >( row, 35 );
limitBreak3 = exdData->getField< uint16_t >( row, 36 );

View file

@ -1485,6 +1485,7 @@ struct ClassJob
uint8_t classJobParent;
std::string nameEnglish;
int32_t itemStartingWeapon;
uint8_t primaryStat;
uint16_t limitBreak1;
uint16_t limitBreak2;
uint16_t limitBreak3;

View file

@ -22,18 +22,22 @@
#include "Network/PacketWrappers/MoveActorPacket.h"
#include "Navi/NaviProvider.h"
#include "Math/CalcBattle.h"
#include "Math/CalcStats.h"
#include "StatusEffect/StatusEffect.h"
#include "ServerMgr.h"
#include "Session.h"
#include "Math/CalcBattle.h"
#include "Chara.h"
#include "Player.h"
#include "BNpc.h"
#include "BNpcTemplate.h"
#include "Manager/TerritoryMgr.h"
#include "Common.h"
#include "Framework.h"
#include <Logging/Logger.h>
#include <Manager/TerritoryMgr.h>
#include <Manager/NaviMgr.h>
#include <Manager/TerritoryMgr.h>
#include <Manager/RNGMgr.h>
@ -72,7 +76,9 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX
m_levelId = 0;
m_flags = 0;
m_pCurrentZone = pZone;
m_class = ClassJob::Adventurer;
m_pCurrentZone = std::move( pZone );
m_spawnPos = m_pos;
@ -112,6 +118,8 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX
// todo: is this actually good?
//m_naviTargetReachedDistance = m_scale * 2.f;
m_naviTargetReachedDistance = 4.f;
calculateStats();
}
Sapphire::Entity::BNpc::~BNpc() = default;
@ -262,7 +270,7 @@ void Sapphire::Entity::BNpc::sendPositionUpdate()
void Sapphire::Entity::BNpc::hateListClear()
{
auto it = m_hateList.begin();
for( auto listEntry : m_hateList )
for( auto& listEntry : m_hateList )
{
if( isInRangeSet( listEntry->m_pChara ) )
deaggro( listEntry->m_pChara );
@ -681,7 +689,7 @@ void Sapphire::Entity::BNpc::autoAttack( CharaPtr pTarget )
srand( static_cast< uint32_t >( tick ) );
auto pRNGMgr = m_pFw->get< World::Manager::RNGMgr >();
auto damage = static_cast< uint16_t >( pRNGMgr->getRandGenerator< float >( m_level, m_level + m_level * 1.5f ).next() );
auto damage = Math::CalcStats::calculateAutoAttackDamage( *this );
auto effectPacket = std::make_shared< Server::EffectPacket >( getId(), pTarget->getId(), 7 );
effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) );
@ -697,3 +705,37 @@ void Sapphire::Entity::BNpc::autoAttack( CharaPtr pTarget )
}
}
void Sapphire::Entity::BNpc::calculateStats()
{
uint8_t level = getLevel();
uint8_t job = static_cast< uint8_t >( getClass() );
auto pExdData = m_pFw->get< Data::ExdDataGenerated >();
auto classInfo = pExdData->get< Sapphire::Data::ClassJob >( job );
auto paramGrowthInfo = pExdData->get< Sapphire::Data::ParamGrow >( level );
float base = Math::CalcStats::calculateBaseStat( *this );
m_baseStats.str = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierStrength ) / 100 ) );
m_baseStats.dex = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierDexterity ) / 100 ) );
m_baseStats.vit = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierVitality ) / 100 ) );
m_baseStats.inte = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierIntelligence ) / 100 ) );
m_baseStats.mnd = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierMind ) / 100 ) );
m_baseStats.pie = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierPiety ) / 100 ) );
m_baseStats.determination = static_cast< uint32_t >( base );
m_baseStats.pie = static_cast< uint32_t >( base );
m_baseStats.skillSpeed = paramGrowthInfo->baseSpeed;
m_baseStats.spellSpeed = paramGrowthInfo->baseSpeed;
m_baseStats.accuracy = paramGrowthInfo->baseSpeed;
m_baseStats.critHitRate = paramGrowthInfo->baseSpeed;
m_baseStats.attackPotMagic = paramGrowthInfo->baseSpeed;
m_baseStats.healingPotMagic = paramGrowthInfo->baseSpeed;
m_baseStats.tenacity = paramGrowthInfo->baseSpeed;
m_baseStats.attack = m_baseStats.str;
m_baseStats.attackPotMagic = m_baseStats.inte;
m_baseStats.healingPotMagic = m_baseStats.mnd;
}

View file

@ -118,6 +118,8 @@ namespace Sapphire::Entity
bool hasFlag( uint32_t flag ) const;
void setFlag( uint32_t flags );
void calculateStats() override;
private:
uint32_t m_bNpcBaseId;
uint32_t m_bNpcNameId;

View file

@ -719,3 +719,14 @@ float Sapphire::Entity::Chara::getRadius() const
{
return m_radius;
}
Sapphire::Common::BaseParam Sapphire::Entity::Chara::getPrimaryStat() const
{
auto exdData = m_pFw->get< Data::ExdDataGenerated >();
assert( exdData );
auto classJob = exdData->get< Data::ClassJob >( static_cast< uint16_t >( getClass() ) );
assert( classJob );
return static_cast< Sapphire::Common::BaseParam >( classJob->primaryStat );
}

View file

@ -280,6 +280,8 @@ namespace Sapphire::Entity
float getRadius() const;
Common::BaseParam getPrimaryStat() const;
};
}

View file

@ -256,7 +256,7 @@ void Sapphire::Entity::Player::calculateStats()
auto tribeInfo = pExdData->get< Sapphire::Data::Tribe >( tribe );
auto paramGrowthInfo = pExdData->get< Sapphire::Data::ParamGrow >( level );
float base = Math::CalcStats::calculateBaseStat( getAsPlayer() );
float base = Math::CalcStats::calculateBaseStat( *this );
m_baseStats.str = static_cast< uint32_t >( base * ( static_cast< float >( classInfo->modifierStrength ) / 100 ) +
tribeInfo->sTR );
@ -1559,8 +1559,7 @@ void Sapphire::Entity::Player::autoAttack( CharaPtr pTarget )
auto pRNGMgr = m_pFw->get< World::Manager::RNGMgr >();
auto variation = static_cast< uint32_t >( pRNGMgr->getRandGenerator< float >( 0, 3 ).next() );
auto damage = static_cast< uint32_t >( pRNGMgr->getRandGenerator< float >( static_cast< uint32_t > ( getLevel() * 1.5f ),
getLevel() + static_cast< uint32_t >( mainWeap->getAutoAttackDmg() * 2 ) ).next() );
auto damage = Math::CalcStats::calculateAutoAttackDamage( *this );
if( getClass() == ClassJob::Machinist || getClass() == ClassJob::Bard || getClass() == ClassJob::Archer )
{
@ -2116,3 +2115,13 @@ Sapphire::World::SessionPtr Sapphire::Entity::Player::getSession()
return m_pSession;
}
void Sapphire::Entity::Player::setActiveLand( uint8_t land, uint8_t ward )
{
m_activeLand.plot = land;
m_activeLand.ward = ward;
}
Sapphire::Common::ActiveLand Sapphire::Entity::Player::getActiveLand() const
{
return m_activeLand;
}

View file

@ -933,6 +933,8 @@ namespace Sapphire::Entity
/*! calculate and return player ilvl based off equipped gear */
uint16_t calculateEquippedGearItemLevel();
ItemPtr getEquippedWeapon();
/*! return the current amount of currency of type */
uint32_t getCurrency( Common::CurrencyType type );
@ -964,6 +966,8 @@ namespace Sapphire::Entity
Sapphire::ItemPtr dropInventoryItem( Common::InventoryType type, uint16_t slotId );
//////////////////////////////////////////////////////////////////////////////////////////////////////
Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index );
void sendHuntingLog();
@ -974,8 +978,6 @@ namespace Sapphire::Entity
World::SessionPtr getSession();
//////////////////////////////////////////////////////////////////////////////////////////////////////
uint64_t m_lastMoveTime;
uint8_t m_lastMoveflag;
bool m_falling;

View file

@ -831,17 +831,6 @@ void Sapphire::Entity::Player::discardItem( uint16_t fromInventoryId, uint8_t fr
queuePacket( invTransFinPacket );
}
void Sapphire::Entity::Player::setActiveLand( uint8_t land, uint8_t ward )
{
m_activeLand.plot = land;
m_activeLand.ward = ward;
}
Sapphire::Common::ActiveLand Sapphire::Entity::Player::getActiveLand() const
{
return m_activeLand;
}
uint16_t Sapphire::Entity::Player::calculateEquippedGearItemLevel()
{
uint32_t iLvlResult = 0;
@ -871,6 +860,10 @@ uint16_t Sapphire::Entity::Player::calculateEquippedGearItemLevel()
return static_cast< uint16_t >( std::min( static_cast< int32_t >( iLvlResult / 13 ), 9999 ) );
}
Sapphire::ItemPtr Sapphire::Entity::Player::getEquippedWeapon()
{
return m_storageMap[ GearSet0 ]->getItem( GearSetSlot::MainHand );
}
uint8_t Sapphire::Entity::Player::getFreeSlotsInBags()
{

View file

@ -2,11 +2,13 @@
#include <Exd/ExdDataGenerated.h>
#include <Common.h>
#include <Logging/Logger.h>
#include "Actor/Chara.h"
#include "Actor/Player.h"
#include "Inventory/Item.h"
#include "CalcStats.h"
#include "Framework.h"
@ -108,10 +110,10 @@ const int levelTable[71][7] =
// 3 Versions. SB and HW are linear, ARR is polynomial.
// Originally from Player.cpp, calculateStats().
float CalcStats::calculateBaseStat( PlayerPtr pPlayer )
float CalcStats::calculateBaseStat( const Chara& chara )
{
float base = 0.0f;
uint8_t level = pPlayer->getLevel();
uint8_t level = chara.getLevel();
if( level > 70 )
level = 70;
@ -138,7 +140,7 @@ uint32_t CalcStats::calculateMaxHp( PlayerPtr pPlayer, Sapphire::FrameworkPtr pF
uint8_t level = pPlayer->getLevel();
auto vitMod = pPlayer->getBonusStat( Common::BaseParam::Vitality );
float baseStat = calculateBaseStat( pPlayer );
float baseStat = calculateBaseStat( *pPlayer );
uint16_t vitStat = pPlayer->getStats().vit + static_cast< uint16_t >( vitMod );
uint16_t hpMod = paramGrowthInfo->hpModifier;
uint16_t jobModHp = classInfo->modifierHitPoints;
@ -173,7 +175,7 @@ uint32_t CalcStats::calculateMaxMp( PlayerPtr pPlayer, Sapphire::FrameworkPtr pF
auto pieMod = pPlayer->getBonusStat( Common::BaseParam::Piety );
float baseStat = calculateBaseStat( pPlayer );
float baseStat = calculateBaseStat( *pPlayer );
uint16_t piety = pPlayer->getStats().pie + pieMod;
uint16_t pietyScalar = paramGrowthInfo->mpModifier;
uint16_t jobModMp = classInfo->modifierManaPoints;
@ -308,49 +310,67 @@ float CalcStats::potency( uint16_t potency )
return potency / 100.f;
}
//float CalcStats::weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage )
//{
// const auto& baseStats = chara.getStats();
// auto level = chara.getLevel();
//
// auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] );
//
// float jobAttribute = 1.f;
//
// // todo: fix this
// return 1.f
//}
float CalcStats::weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage )
{
const auto& baseStats = chara.getStats();
auto level = chara.getLevel();
// todo: this is all retarded, needs to be per weapon and etcetc
//uint32_t CalcStats::getPrimaryClassJobAttribute( const Sapphire::Entity::Chara& chara )
//{
//
//}
auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] );
uint32_t jobAttribute = 1;
switch( chara.getPrimaryStat() )
{
case Common::BaseParam::Intelligence:
{
jobAttribute = baseStats.healingPotMagic;
break;
}
case Common::BaseParam::Mind:
{
jobAttribute = baseStats.attackPotMagic;
break;
}
default:
{
jobAttribute = baseStats.attack;
break;
}
}
return std::floor( ( ( mainVal * jobAttribute ) / 1000.f ) + weaponDamage );
}
float CalcStats::calcAttackPower( uint32_t attackPower )
{
return std::floor( ( 125.f * ( attackPower - 292.f ) / 292.f ) + 100.f ) / 100.f;
}
float CalcStats::magicAttackPower( const Sapphire::Entity::Chara& chara )
{
const auto& baseStats = chara.getStats();
return calcAttackPower( baseStats.attackPotMagic );
}
float CalcStats::healingMagicPower( const Sapphire::Entity::Chara& chara )
{
const auto& baseStats = chara.getStats();
return calcAttackPower( baseStats.healingPotMagic );
}
float CalcStats::attackPower( const Sapphire::Entity::Chara& chara )
{
const auto& baseStats = chara.getStats();
// todo: this is wrong
if( chara.isBattleNpc() )
return calcAttackPower( baseStats.attack );
switch( chara.getPrimaryStat() )
{
case Common::BaseParam::Mind:
{
return calcAttackPower( baseStats.healingPotMagic );
}
case Common::BaseParam::Intelligence:
{
return calcAttackPower( baseStats.attackPotMagic );
}
default:
{
return calcAttackPower( baseStats.attack );
}
}
}
float CalcStats::determination( const Sapphire::Entity::Chara& chara )
@ -386,18 +406,10 @@ float CalcStats::speed( const Sapphire::Entity::Chara& chara )
uint32_t speedVal = 0;
// check whether we use spellspeed or skillspeed
// todo: this is kinda shitty though
switch( chara.getClass() )
switch( chara.getPrimaryStat() )
{
case Common::ClassJob::Arcanist:
case Common::ClassJob::Astrologian:
case Common::ClassJob::Whitemage:
case Common::ClassJob::Redmage:
case Common::ClassJob::Bluemage:
case Common::ClassJob::Blackmage:
case Common::ClassJob::Summoner:
case Common::ClassJob::Scholar:
case Common::ClassJob::Thaumaturge:
case Common::BaseParam::Intelligence:
case Common::BaseParam::Mind:
speedVal = baseStats.spellSpeed;
break;
@ -439,10 +451,42 @@ float CalcStats::magicDefence( const Sapphire::Entity::Chara& chara )
return std::floor( 15.f * baseStats.magicDefense ) / 100.f;
}
//float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara )
//{
//
//}
float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara )
{
auto level = chara.getLevel();
auto blockStrength = static_cast< float >( chara.getBonusStat( Common::BaseParam::BlockStrength ) );
auto levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( ( 30 * blockStrength ) / levelVal + 10 ) / 100.f;
}
float CalcStats::autoAttack( const Sapphire::Entity::Chara& chara )
{
// todo: default values for NPCs, not sure what we should have here
float autoAttackDelay = 2.f;
float weaponDamage = 10.f;
// fetch actual auto attack delay if its a player
if( chara.isPlayer() )
{
// todo: ew
auto pPlayer = const_cast< Entity::Chara& >( chara ).getAsPlayer();
assert( pPlayer );
auto pItem = pPlayer->getEquippedWeapon();
assert( pItem );
autoAttackDelay = pItem->getDelay() / 1000.f;
weaponDamage = pItem->getWeaponDmg();
}
auto level = chara.getLevel();
auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] );
auto innerCalc = std::floor( ( ( mainVal * primaryStatValue( chara ) ) / 1000.f ) + weaponDamage );
return std::floor( innerCalc * ( autoAttackDelay / 3.f ) );
}
float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara )
{
@ -450,3 +494,62 @@ float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara )
return std::floor( 100.f * ( baseStats.healingPotMagic - 292.f ) / 264.f + 100.f ) / 100.f;
}
////////
float CalcStats::calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara )
{
// D = ⌊ f(ptc) × f(aa) × f(ap) × f(det) × f(tnc) × traits ⌋ × f(ss) ⌋ ×
// f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ × buff_1 ⌋ × buff... ⌋
auto pot = potency( AUTO_ATTACK_POTENCY );
auto aa = autoAttack( chara );
//auto ap = attackPower( chara );
auto ap = 1.f;
auto det = determination( chara );
auto ten = tenacity( chara );
Logger::info( "auto attack: pot: {} aa: {} ap: {} det: {} ten: {}", pot, aa, ap, det, ten );
auto factor = std::floor( pot * aa * ap * det * ten );
// todo: traits
factor = std::floor( factor * speed( chara ) );
// todo: surely this aint right?
//factor = std::floor( factor * criticalHitProbability( chara ) );
//factor = std::floor( factor * directHitProbability( chara ) );
// todo: random 0.95 - 1.05 factor
// todo: buffs
return factor;
}
uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara )
{
const auto& baseStats = chara.getStats();
switch( chara.getPrimaryStat() )
{
default:
return 1;
case Common::BaseParam::Intelligence:
return baseStats.inte;
case Common::BaseParam::Mind:
return baseStats.mnd;
case Common::BaseParam::Strength:
return baseStats.str;
case Common::BaseParam::Vitality:
return baseStats.vit;
case Common::BaseParam::Dexterity:
return baseStats.dex;
}
}

View file

@ -10,7 +10,9 @@ namespace Sapphire::Math
class CalcStats
{
public:
static float calculateBaseStat( Sapphire::Entity::PlayerPtr pPlayer );
static const uint32_t AUTO_ATTACK_POTENCY = 100;
static float calculateBaseStat( const Entity::Chara& chara );
static uint32_t calculateMaxMp( Sapphire::Entity::PlayerPtr pPlayer, FrameworkPtr pFw );
@ -52,33 +54,17 @@ namespace Sapphire::Math
*
* @param chara The source/casting character.
* @param weaponDamage the weapons physical or magic damage
* @param isMagicDamage true if the damage is magical, otherwise it's treated as physical damage
*/
static float weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage );
static float weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage );
/*!
* @brief Calculates the contribution of physical attack power to damage dealt
* @brief Calculates the contribution of attack power to damage dealt with consideration for the primary stat
* @todo Only works at level 70
*
* @param chara The source/casting character.
*/
static float attackPower( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates the contribution of magical attack power to damage dealt
* @todo Only works at level 70
*
* @param chara The source/casting character.
*/
static float magicAttackPower( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates the contribution of healing magic power to healing dealt
*
* @param chara The source/casting character.
*/
static float healingMagicPower( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates determinations contribution to damage and healing output.
*
@ -131,6 +117,8 @@ namespace Sapphire::Math
*/
static float blockStrength( const Sapphire::Entity::Chara& chara );
static float autoAttack( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates the multiplier that healing magic potency affects healing output
*
@ -140,9 +128,11 @@ namespace Sapphire::Math
*/
static float healingMagicPotency( const Sapphire::Entity::Chara& chara );
private:
////////////////////////////////////////////
static uint32_t getPrimaryClassJobAttribute( const Sapphire::Entity::Chara& chara );
static float calculateAutoAttackDamage( const Sapphire::Entity::Chara& chara );
private:
/*!
* @brief Has the main attack power calculation allowing for de-duplication of functions.
@ -151,6 +141,8 @@ namespace Sapphire::Math
*/
static float calcAttackPower( uint32_t attackPower );
static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara );
};
}