diff --git a/src/common/Common.h b/src/common/Common.h index ed59e820..95ca262e 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -639,9 +639,9 @@ namespace Sapphire::Common enum class ActionHitSeverityType : uint8_t { NormalDamage = 0, - CritHeal = 0, + NormalHeal = 0, CritDamage = 1, - NormalHeal = 1, + CritHeal = 1, DirectHitDamage = 2, CritDirectHitDamage = 3 }; @@ -669,7 +669,7 @@ namespace Sapphire::Common { Common::ActionEffectType effectType; Common::ActionHitSeverityType hitSeverity; - uint8_t unk; + Common::ActionHitSeverityType healSeverity; /*! * @brief Shows an additional percentage in the battle log * diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 2718465d..1f3de39a 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -425,9 +425,30 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage( } } - auto dmg = Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg ); + return Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg ); +} - return std::make_pair( dmg, Common::ActionHitSeverityType::NormalDamage ); +std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcHealing( uint32_t potency ) +{ + auto wepDmg = 1.f; + + if( auto player = m_pSource->getAsPlayer() ) + { + auto item = player->getEquippedWeapon(); + assert( item ); + + auto role = player->getRole(); + if( role == Common::Role::RangedMagical || role == Common::Role::Healer ) + { + wepDmg = item->getMagicalDmg(); + } + else + { + wepDmg = item->getPhysicalDmg(); + } + } + + return Math::CalcStats::calcActionHealing( *m_pSource, potency, wepDmg ); } void Action::Action::buildEffects() @@ -486,7 +507,8 @@ void Action::Action::buildEffects() { if( m_lutEntry.curePotency > 0 ) // actions with self heal { - m_effectBuilder->heal( actor, m_pSource, m_lutEntry.curePotency, Common::ActionHitSeverityType::NormalHeal, Common::ActionEffectResultFlag::EffectOnSource ); + auto heal = calcHealing( m_lutEntry.curePotency ); + m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource ); } if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP ) @@ -503,8 +525,8 @@ void Action::Action::buildEffects() } else if( m_lutEntry.curePotency > 0 ) { - // todo: calcHealing() - m_effectBuilder->heal( actor, actor, m_lutEntry.curePotency ); + auto heal = calcHealing( m_lutEntry.curePotency ); + m_effectBuilder->heal( actor, actor, heal.first, heal.second ); if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP ) { diff --git a/src/world/Action/EffectResult.cpp b/src/world/Action/EffectResult.cpp index 9525a361..f52839a0 100644 --- a/src/world/Action/EffectResult.cpp +++ b/src/world/Action/EffectResult.cpp @@ -12,7 +12,8 @@ EffectResult::EffectResult( Entity::CharaPtr target, uint64_t runAfter ) : m_target( std::move( target ) ), m_delayMs( runAfter ), m_value( 0 ), - m_severity( Common::ActionHitSeverityType::NormalDamage ), + m_hitSeverity( Common::ActionHitSeverityType::NormalDamage ), + m_healSeverity( Common::ActionHitSeverityType::NormalHeal ), m_type( Common::ActionEffectType::Nothing ), m_param( 0 ), m_flag( Common::ActionEffectResultFlag::None ) @@ -37,7 +38,7 @@ uint64_t EffectResult::getDelay() void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) { - m_severity = severity; + m_hitSeverity = severity; m_value = amount; m_flag = flag; @@ -46,7 +47,7 @@ void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severi void EffectResult::heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) { - m_severity = severity; + m_healSeverity = severity; m_value = amount; m_flag = flag; @@ -89,7 +90,8 @@ Common::EffectEntry EffectResult::buildEffectEntry() const // todo: that retarded shit so > u16 max numbers work entry.value = getValue(); - entry.hitSeverity = m_severity; + entry.hitSeverity = m_hitSeverity; + entry.healSeverity = m_healSeverity; entry.effectType = m_type; entry.param = m_param; entry.flags = static_cast< uint8_t >( m_flag ); diff --git a/src/world/Action/EffectResult.h b/src/world/Action/EffectResult.h index ebb617c2..ae9ae1aa 100644 --- a/src/world/Action/EffectResult.h +++ b/src/world/Action/EffectResult.h @@ -37,7 +37,8 @@ namespace Sapphire::World::Action Entity::CharaPtr m_target; - Common::ActionHitSeverityType m_severity; + Common::ActionHitSeverityType m_hitSeverity; + Common::ActionHitSeverityType m_healSeverity; Common::ActionEffectType m_type; uint32_t m_value; diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 38d9a15c..36c07766 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -697,15 +697,15 @@ void Sapphire::Entity::BNpc::autoAttack( CharaPtr pTarget ) auto effectPacket = std::make_shared< Server::EffectPacket >( getId(), pTarget->getId(), 7 ); effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) ); Common::EffectEntry effectEntry{}; - effectEntry.value = static_cast< int16_t >( damage ); + effectEntry.value = static_cast< int16_t >( damage.first ); effectEntry.effectType = ActionEffectType::Damage; - effectEntry.hitSeverity = ActionHitSeverityType::NormalDamage; + effectEntry.hitSeverity = damage.second; effectEntry.param = 0x71; effectPacket->addEffect( effectEntry ); sendToInRangeSet( effectPacket ); - pTarget->takeDamage( static_cast< uint16_t >( damage ) ); + pTarget->takeDamage( static_cast< uint16_t >( damage.first ) ); } } diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index 5bca5e76..d23172bc 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -1585,9 +1585,9 @@ void Sapphire::Entity::Player::autoAttack( CharaPtr pTarget ) effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) ); Common::EffectEntry entry{}; - entry.value = damage; + entry.value = damage.first; entry.effectType = Common::ActionEffectType::Damage; - entry.hitSeverity = Common::ActionHitSeverityType::NormalDamage; + entry.hitSeverity = damage.second; entry.param = 0x72; effectPacket->addEffect( entry ); @@ -1600,9 +1600,9 @@ void Sapphire::Entity::Player::autoAttack( CharaPtr pTarget ) effectPacket->setRotation( Util::floatToUInt16Rot( getRot() ) ); Common::EffectEntry entry{}; - entry.value = damage; + entry.value = damage.first; entry.effectType = Common::ActionEffectType::Damage; - entry.hitSeverity = Common::ActionHitSeverityType::NormalDamage; + entry.hitSeverity = damage.second; entry.param = 0x73; effectPacket->addEffect( entry ); @@ -1611,7 +1611,7 @@ void Sapphire::Entity::Player::autoAttack( CharaPtr pTarget ) } - pTarget->takeDamage( damage ); + pTarget->takeDamage( damage.first ); } diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index 06834e1d..166b9815 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -104,6 +104,10 @@ const int levelTable[81][6] = { 340, 380, 3300, 3600, 569, 569 }, }; +std::random_device CalcStats::dev; +std::mt19937 CalcStats::rng( dev() ); +std::uniform_int_distribution< std::mt19937::result_type > CalcStats::range100( 0, 99 ); + /* Class used for battle-related formulas and calculations. Big thanks to the Theoryjerks group! @@ -438,7 +442,7 @@ float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara ) return std::floor( 100.f * ( chara.getStatValue( Common::BaseParam::HealingMagicPotency ) - 292.f ) / 264.f + 100.f ) / 100.f; } -float CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ) +std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcAutoAttackDamage( 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... ⌋ @@ -454,6 +458,29 @@ float CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ) // todo: everything after tenacity auto factor = std::floor( pot * aa * ap * det * ten ); + Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage; + + // todo: traits + + factor = std::floor( factor * speed( chara ) ); + + if( criticalHitProbability( chara ) > range100( rng ) ) + { + factor *= criticalHitBonus( chara ); + hitType = Sapphire::Common::ActionHitSeverityType::CritDamage; + } + + if( directHitProbability( chara ) > range100( rng ) ) + { + factor *= 1.25f; + hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ? + Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage : + Sapphire::Common::ActionHitSeverityType::DirectHitDamage; + } + + factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f ); + + // todo: buffs constexpr auto format = "auto attack: pot: {} aa: {} ap: {} det: {} ten: {} = {}"; @@ -466,22 +493,10 @@ float CalcStats::calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ) Logger::debug( format, pot, aa, ap, det, ten, factor ); } - // 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; + return std::pair( factor, hitType ); } -float CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ) +std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ) { // D = ⌊ f(pot) × f(wd) × f(ap) × f(det) × f(tnc) × traits ⌋ // × f(chr) ⌋ × f(dhr) ⌋ × rand[ 0.95, 1.05 ] ⌋ buff_1 ⌋ × buff_1 ⌋ × buff... ⌋ @@ -496,6 +511,25 @@ float CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_ ten = tenacity( chara ); auto factor = std::floor( pot * wd * ap * det * ten ); + Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalDamage; + + if( criticalHitProbability( chara ) > range100( rng ) ) + { + factor *= criticalHitBonus( chara ); + hitType = Sapphire::Common::ActionHitSeverityType::CritDamage; + } + + if( directHitProbability( chara ) > range100( rng ) ) + { + factor *= 1.25f; + hitType = hitType == Sapphire::Common::ActionHitSeverityType::CritDamage ? + Sapphire::Common::ActionHitSeverityType::CritDirectHitDamage : + Sapphire::Common::ActionHitSeverityType::DirectHitDamage; + } + + factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f ); + + // todo: buffs constexpr auto format = "dmg: pot: {} ({}) wd: {} ({}) ap: {} det: {} ten: {} = {}"; @@ -508,9 +542,24 @@ float CalcStats::calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_ Logger::debug( format, pot, ptc, wd, wepDmg, ap, det, ten, factor ); } - // todo: the rest + return std::pair( factor, hitType ); +} - return factor; +std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ) +{ + // lol just for testing + auto factor = std::floor( ptc * chara.getLevel() / 4.0f ); + Sapphire::Common::ActionHitSeverityType hitType = Sapphire::Common::ActionHitSeverityType::NormalHeal; + + if( criticalHitProbability( chara ) > range100( rng ) ) + { + factor *= criticalHitBonus( chara ); + hitType = Sapphire::Common::ActionHitSeverityType::CritHeal; + } + + factor *= 1.0f + ( ( range100( rng ) - 50.0f ) / 1000.0f ); + + return std::pair( factor, hitType ); } uint32_t CalcStats::primaryStatValue( const Sapphire::Entity::Chara& chara ) diff --git a/src/world/Math/CalcStats.h b/src/world/Math/CalcStats.h index 1b22238a..ce095b53 100644 --- a/src/world/Math/CalcStats.h +++ b/src/world/Math/CalcStats.h @@ -1,6 +1,7 @@ #ifndef _CALCSTATS_H #define _CALCSTATS_H +#include #include #include "Forwards.h" @@ -128,9 +129,11 @@ namespace Sapphire::Math //////////////////////////////////////////// - static float calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ); + static std::pair< float, Common::ActionHitSeverityType > calcAutoAttackDamage( const Sapphire::Entity::Chara& chara ); - static float calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ); + static std::pair< float, Common::ActionHitSeverityType > calcActionDamage( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ); + + static std::pair< float, Common::ActionHitSeverityType > calcActionHealing( const Sapphire::Entity::Chara& chara, uint32_t ptc, float wepDmg ); static uint32_t primaryStatValue( const Sapphire::Entity::Chara& chara ); private: @@ -142,7 +145,9 @@ namespace Sapphire::Math */ static float calcAttackPower( const Sapphire::Entity::Chara& chara, uint32_t attackPower ); - + static std::random_device dev; + static std::mt19937 rng; + static std::uniform_int_distribution< std::mt19937::result_type > range100; }; }