1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-25 19:17:45 +00:00

Implement dodge, block, parry.

This commit is contained in:
collett 2020-02-05 18:42:30 +09:00
parent 0903f0d2f1
commit 9a9ce6d02e
8 changed files with 206 additions and 22 deletions

View file

@ -1044,6 +1044,7 @@ namespace Sapphire::Common
MPRestore = 11,
Haste = 12,
InstantCast = 13,
BlockParryRateBonus = 14,
};
enum class ActionTypeFilter : int32_t

View file

@ -475,14 +475,52 @@ void Action::Action::buildEffects()
if( m_lutEntry.damagePotency > 0 )
{
Common::AttackType attackType = static_cast< Common::AttackType >( m_actionData->attackType );
actor->onActionHostile( m_pSource );
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.damageComboPotency : m_lutEntry.damagePotency );
dmg.first = Math::CalcStats::applyDamageReceiveMultiplier( *actor, dmg.first, attackType );
float originalDamage = dmg.first;
bool dodged = false;
float blocked = 0;
float parried = 0;
if( dmg.first > 0 )
{
dodged = Math::CalcStats::calcDodge( *actor );
if( !dodged && dmg.second == Common::ActionHitSeverityType::NormalDamage && actor->isPlayer() )
{
blocked = Math::CalcStats::calcBlock( *actor, dmg.first );
}
if( !dodged && blocked == 0 && dmg.second == Common::ActionHitSeverityType::NormalDamage && actor->isPlayer() )
{
if( isPhysical() )
{
parried = Math::CalcStats::calcParry( *actor, dmg.first );
}
}
}
if( dodged )
dmg.first = 0;
else
{
dmg.first -= blocked;
dmg.first -= parried;
}
if( dmg.first > 0 )
{
actor->onActionHostile( m_pSource );
dmg.first = actor->applyShieldProtection( dmg.first );
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second, dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None );
if( blocked > 0 )
m_effectBuilder->blockedDamage( actor, actor, dmg.first, static_cast< uint16_t >( blocked / originalDamage * 100 ) , dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None );
else if (parried > 0 )
m_effectBuilder->parriedDamage( actor, actor, dmg.first, static_cast< uint16_t >( parried / originalDamage * 100 ), dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None );
else
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second, dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None );
if( m_isAutoAttack && m_pSource->isPlayer() )
{
@ -494,31 +532,38 @@ void Action::Action::buildEffects()
}
}
}
}
auto reflectDmg = Math::CalcStats::calcDamageReflect( m_pSource, actor, dmg.first,
attackType == Common::AttackType::Physical ? Common::ActionTypeFilter::Physical :
( attackType == Common::AttackType::Magical ? Common::ActionTypeFilter::Magical : Common::ActionTypeFilter::Unknown ) );
if( reflectDmg.first > 0 )
auto reflectDmg = Math::CalcStats::calcDamageReflect( m_pSource, actor, dmg.first,
attackType == Common::AttackType::Physical ? Common::ActionTypeFilter::Physical :
( attackType == Common::AttackType::Magical ? Common::ActionTypeFilter::Magical : Common::ActionTypeFilter::Unknown ) );
if( reflectDmg.first > 0 )
{
m_effectBuilder->damage( actor, m_pSource, reflectDmg.first, reflectDmg.second, Common::ActionEffectResultFlag::Reflected );
}
auto absorb = Math::CalcStats::calcAbsorbHP( m_pSource, dmg.first );
if( absorb > 0 )
{
if( absorb > actor->getHp() )
absorb = actor->getHp();
m_effectBuilder->heal( actor, m_pSource, absorb, Common::ActionHitSeverityType::NormalHeal, Common::ActionEffectResultFlag::EffectOnSource );
}
}
else
{
m_effectBuilder->damage( actor, m_pSource, reflectDmg.first, reflectDmg.second, Common::ActionEffectResultFlag::Reflected );
if( dodged )
{
m_effectBuilder->dodge( actor, actor );
}
}
auto absorb = Math::CalcStats::calcAbsorbHP( m_pSource, dmg.first, Common::ActionTypeFilter::All );
if( absorb > 0 )
{
if( absorb > actor->getHp() )
absorb = actor->getHp();
m_effectBuilder->heal( actor, m_pSource, absorb, Common::ActionHitSeverityType::NormalHeal, Common::ActionEffectResultFlag::EffectOnSource );
}
if( isCorrectCombo() && shouldApplyComboSucceedEffect )
if( dmg.first > 0 && isCorrectCombo() && shouldApplyComboSucceedEffect )
{
m_effectBuilder->comboSucceed( actor );
shouldApplyComboSucceedEffect = false;
}
if( !isComboAction() || isCorrectCombo() )
if( dmg.first > 0 && ( !isComboAction() || isCorrectCombo() ) )
{
if ( !m_actionData->preservesCombo ) // this matches retail packet, on all standalone actions even casts.
{

View file

@ -57,6 +57,13 @@ void EffectBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& resto
moveToResultList( target, nextResult );
}
void EffectBuilder::dodge( Entity::CharaPtr& effectTarget, Entity::CharaPtr& dodgingTarget, Common::ActionEffectResultFlag flag )
{
EffectResultPtr nextResult = make_EffectResult( dodgingTarget, 0 );
nextResult->dodge( flag );
moveToResultList( effectTarget, nextResult );
}
void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
{
EffectResultPtr nextResult = make_EffectResult( damagingTarget, Common::Util::getTimeMs() + resultDelayMs );
@ -64,6 +71,20 @@ void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& da
moveToResultList( effectTarget, nextResult );
}
void EffectBuilder::blockedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
{
EffectResultPtr nextResult = make_EffectResult( damagingTarget, Common::Util::getTimeMs() + resultDelayMs );
nextResult->blockedDamage( amount, rate, flag );
moveToResultList( effectTarget, nextResult );
}
void EffectBuilder::parriedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag, uint64_t resultDelayMs )
{
EffectResultPtr nextResult = make_EffectResult( damagingTarget, Common::Util::getTimeMs() + resultDelayMs );
nextResult->parriedDamage( amount, rate, flag );
moveToResultList( effectTarget, nextResult );
}
void EffectBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId )
{
EffectResultPtr nextResult = make_EffectResult( target, 0 );

View file

@ -20,9 +20,17 @@ namespace Sapphire::World::Action
void restoreMP( Entity::CharaPtr& effectTarget, Entity::CharaPtr& restoringTarget, uint32_t amount,
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
void dodge( Entity::CharaPtr& effectTarget, Entity::CharaPtr& dodgingTarget, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount,
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage,
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
void blockedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate,
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
void parriedDamage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, uint16_t rate,
Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None, uint64_t resultDelayMs = 600 );
void startCombo( Entity::CharaPtr& target, uint16_t actionId );

View file

@ -50,6 +50,13 @@ uint64_t EffectResult::getDelay()
return m_delayMs;
}
void EffectResult::dodge( Common::ActionEffectResultFlag flag )
{
m_flag = flag;
m_type = Common::ActionEffectType::Miss;
}
void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
{
m_param0 = static_cast< uint8_t >( severity );
@ -59,6 +66,24 @@ void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severi
m_type = Common::ActionEffectType::Damage;
}
void EffectResult::blockedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag )
{
m_value = amount;
m_flag = flag;
m_param2 = rate;
m_type = Common::ActionEffectType::BlockedDamage;
}
void EffectResult::parriedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag )
{
m_value = amount;
m_flag = flag;
m_param2 = rate;
m_type = Common::ActionEffectType::ParriedDamage;
}
void EffectResult::heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
{
m_param1 = static_cast< uint8_t >( severity );

View file

@ -18,7 +18,10 @@ namespace Sapphire::World::Action
explicit EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, uint64_t delayMs );
explicit EffectResult( Entity::CharaPtr target, uint64_t delayMs );
void dodge( Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void blockedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void parriedDamage( uint32_t amount, uint16_t rate, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void startCombo( uint16_t actionId );

View file

@ -185,13 +185,50 @@ uint32_t CalcStats::calculateMaxHp( PlayerPtr pPlayer, Sapphire::FrameworkPtr pF
return result;
}
float CalcStats::dodgeProbability( const Sapphire::Entity::Chara& chara )
{
// fake value: 5% for players.
return chara.isPlayer() ? 5 : 0;
}
float CalcStats::blockProbability( const Chara& chara )
{
// fake value: 20% for pld.
float result = chara.getClass() == Common::ClassJob::Paladin ? 20 : 0;
/*
auto level = chara.getLevel();
auto blockRate = static_cast< float >( chara.getStatValue( Common::BaseParam::BlockRate ) );
auto levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( ( 30 * blockRate ) / levelVal + 10 );
float result = std::floor( ( 30 * blockRate ) / levelVal + 10 );
*/
for( auto const& entry : chara.getStatusEffectMap() )
{
auto status = entry.second;
auto effectEntry = status->getEffectEntry();
if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) != Common::StatusEffectType::BlockParryRateBonus )
continue;
result += effectEntry.effectValue2;
}
return result;
}
float CalcStats::parryProbability( const Sapphire::Entity::Chara& chara )
{
// fake value: 10% for players.
float result = chara.isPlayer() ? 10 : 0;
for( auto const& entry : chara.getStatusEffectMap() )
{
auto status = entry.second;
auto effectEntry = status->getEffectEntry();
if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) != Common::StatusEffectType::BlockParryRateBonus )
continue;
result += effectEntry.effectValue3;
}
return result;
}
float CalcStats::directHitProbability( const Chara& chara, Sapphire::Common::CritDHBonusFilter filter )
@ -455,6 +492,11 @@ float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara )
return std::floor( ( 30 * blockStrength ) / levelVal + 10 ) / 100.f;
}
float CalcStats::parryStrength( const Sapphire::Entity::Chara& chara )
{
return 0.15f;
}
float CalcStats::autoAttack( const Sapphire::Entity::Chara& chara )
{
// todo: default values for NPCs, not sure what we should have here
@ -780,7 +822,7 @@ std::pair< float, Sapphire::Common::ActionHitSeverityType > CalcStats::calcDamag
return std::pair< float, Sapphire::Common::ActionHitSeverityType >( 0, Sapphire::Common::ActionHitSeverityType::NormalDamage );
}
float CalcStats::calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage, Sapphire::Common::ActionTypeFilter filter )
float CalcStats::calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage )
{
float result = 0;
for( auto const& entry : pChara->getStatusEffectMap() )
@ -799,6 +841,33 @@ float CalcStats::calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage,
return result;
}
bool CalcStats::calcDodge( const Sapphire::Entity::Chara& chara )
{
if( dodgeProbability( chara ) > getRandomNumber0To99() )
{
return true;
}
return false;
}
float CalcStats::calcBlock( const Sapphire::Entity::Chara& chara, float damage )
{
if( blockProbability( chara ) > getRandomNumber0To99() )
{
return damage * blockStrength( chara );
}
return 0;
}
float CalcStats::calcParry( const Sapphire::Entity::Chara& chara, float damage )
{
if( parryProbability( chara ) > getRandomNumber0To99() )
{
return damage * parryStrength( chara );
}
return 0;
}
uint32_t CalcStats::getRandomNumber0To99()
{
return range100( rng );

View file

@ -15,11 +15,15 @@ namespace Sapphire::Math
static uint32_t calculateMaxHp( Sapphire::Entity::PlayerPtr pPlayer, FrameworkPtr pFw );
static float dodgeProbability( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates the probability of a block happening
*/
static float blockProbability( const Sapphire::Entity::Chara& chara );
static float parryProbability( const Sapphire::Entity::Chara& chara );
/*!
* @brief Calculates the probability of a direct hit happening
*/
@ -115,6 +119,8 @@ namespace Sapphire::Math
*/
static float blockStrength( const Sapphire::Entity::Chara& chara );
static float parryStrength( const Sapphire::Entity::Chara& chara );
static float autoAttack( const Sapphire::Entity::Chara& chara );
/*!
@ -146,7 +152,13 @@ namespace Sapphire::Math
static std::pair< float, Sapphire::Common::ActionHitSeverityType > calcDamageReflect( Sapphire::Entity::CharaPtr pCharaAttacker, Sapphire::Entity::CharaPtr pCharaVictim, float damage, Sapphire::Common::ActionTypeFilter filter );
static float calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage, Sapphire::Common::ActionTypeFilter filter );
static float calcAbsorbHP( Sapphire::Entity::CharaPtr pChara, float damage );
static bool calcDodge( const Sapphire::Entity::Chara& chara );
static float calcBlock( const Sapphire::Entity::Chara& chara, float damage );
static float calcParry( const Sapphire::Entity::Chara& chara, float damage );
static uint32_t getRandomNumber0To99();
private: