From ec3e93eed995046f093f830da72c52e29b3b6737 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Wed, 24 Apr 2019 23:25:07 +1000 Subject: [PATCH] autoattack damage stat calc funcs, bnpc stat calculation --- src/common/Exd/ExdDataGenerated.cpp | 1 + src/common/Exd/ExdDataGenerated.h | 1 + src/world/Actor/BNpc.cpp | 54 +++++++- src/world/Actor/BNpc.h | 2 + src/world/Actor/Chara.cpp | 11 ++ src/world/Actor/Chara.h | 2 + src/world/Actor/Player.cpp | 15 +- src/world/Actor/Player.h | 6 +- src/world/Actor/PlayerInventory.cpp | 15 +- src/world/Math/CalcStats.cpp | 207 +++++++++++++++++++++------- src/world/Math/CalcStats.h | 34 ++--- 11 files changed, 253 insertions(+), 95 deletions(-) diff --git a/src/common/Exd/ExdDataGenerated.cpp b/src/common/Exd/ExdDataGenerated.cpp index c7485692..fa51b627 100644 --- a/src/common/Exd/ExdDataGenerated.cpp +++ b/src/common/Exd/ExdDataGenerated.cpp @@ -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 ); diff --git a/src/common/Exd/ExdDataGenerated.h b/src/common/Exd/ExdDataGenerated.h index 97c6122b..bcbba9b2 100644 --- a/src/common/Exd/ExdDataGenerated.h +++ b/src/common/Exd/ExdDataGenerated.h @@ -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; diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 841ff2b9..9f7bd41f 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -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 + +#include #include #include #include @@ -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() ) ); @@ -696,4 +704,38 @@ void Sapphire::Entity::BNpc::autoAttack( CharaPtr pTarget ) pTarget->takeDamage( damage ); } +} + +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; } \ No newline at end of file diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index 6a38ecfa..8f14e024 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -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; diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index b4e9303d..968698b0 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -718,4 +718,15 @@ void Sapphire::Entity::Chara::setAgentId( uint32_t agentId ) 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 ); } \ No newline at end of file diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index d882a18a..4e2d8e9b 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -280,6 +280,8 @@ namespace Sapphire::Entity float getRadius() const; + Common::BaseParam getPrimaryStat() const; + }; } diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index f6d32053..1547a860 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -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; +} diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index b7becacb..3fbf9f88 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -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; diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index 97fec07b..3d25ad4f 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -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() { diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index 859cedec..99c536c8 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -2,11 +2,13 @@ #include #include +#include #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(); - return calcAttackPower( baseStats.attack ); + // 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,14 +451,105 @@ 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 ) { const auto& baseStats = chara.getStats(); 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; + } } \ No newline at end of file diff --git a/src/world/Math/CalcStats.h b/src/world/Math/CalcStats.h index 6aab838d..11d9fac1 100644 --- a/src/world/Math/CalcStats.h +++ b/src/world/Math/CalcStats.h @@ -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 ); + }; }