From 62f25b4b77910a6e187cebfbaee52907d6b7c5ab Mon Sep 17 00:00:00 2001 From: collett Date: Tue, 14 Jan 2020 21:59:05 +0900 Subject: [PATCH] Implement presence of mind. --- src/common/Common.h | 1 + src/world/Action/Action.cpp | 8 ++ src/world/Action/ActionLutData.cpp | 3 + src/world/Actor/BNpc.cpp | 1 + src/world/Actor/Chara.cpp | 18 ++- src/world/Actor/Chara.h | 1 + src/world/Actor/Player.cpp | 5 +- src/world/StatusEffect/StatusEffect.cpp | 146 +++++++++++++++--------- 8 files changed, 123 insertions(+), 60 deletions(-) diff --git a/src/common/Common.h b/src/common/Common.h index e16c7705..e258b3aa 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -1038,6 +1038,7 @@ namespace Sapphire::Common DamageDealtTrigger = 9, Shield = 10, MPRestore = 11, + Haste = 12, }; enum class ActionTypeFilter : int32_t diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 99aa456a..d167dca2 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -79,6 +79,14 @@ bool Action::Action::init() m_castTimeMs = static_cast< uint32_t >( m_actionData->cast100ms * 100 ); m_recastTimeMs = static_cast< uint32_t >( m_actionData->recast100ms * 100 ); + auto actionCategory = static_cast< Common::ActionCategory >( m_actionData->actionCategory ); + if( actionCategory == Common::ActionCategory::Spell || actionCategory == Common::ActionCategory::Weaponskill ) + { + auto haste = m_pSource->getStatValue( Common::BaseParam::Haste ); + m_castTimeMs = static_cast< uint32_t >( m_castTimeMs * ( m_pSource->getStatValue( Common::BaseParam::Haste ) / 100.0f ) ); + m_recastTimeMs = static_cast< uint32_t >( m_recastTimeMs * ( m_pSource->getStatValue( Common::BaseParam::Haste ) / 100.0f ) ); + } + m_cooldownGroup = m_actionData->cooldownGroup; m_range = m_actionData->range; m_effectRange = m_actionData->effectRange; diff --git a/src/world/Action/ActionLutData.cpp b/src/world/Action/ActionLutData.cpp index 4f0eb578..4b44b5e3 100644 --- a/src/world/Action/ActionLutData.cpp +++ b/src/world/Action/ActionLutData.cpp @@ -3508,4 +3508,7 @@ ActionLut::StatusEffectTable ActionLut::m_statusEffectTable = //Lucid Dreaming, ルーシッドドリーム: MPRestore, value 50 { 1204, { 11, 50, 0, 0, 0 } }, + //Presence of Mind, 神速魔: Haste, 20% + { 157, { 12, 20, 0, 0, 0 } }, + }; diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 1d5309ec..54c74953 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -735,6 +735,7 @@ void Sapphire::Entity::BNpc::calculateStats() m_baseStats.pie = static_cast< uint32_t >( base ); m_baseStats.skillSpeed = static_cast< uint32_t >( paramGrowthInfo->baseSpeed ); m_baseStats.spellSpeed = static_cast< uint32_t >( paramGrowthInfo->baseSpeed ); + m_baseStats.haste = 100; m_baseStats.accuracy = static_cast< uint32_t >( paramGrowthInfo->baseSpeed ); m_baseStats.critHitRate = static_cast< uint32_t >( paramGrowthInfo->baseSpeed ); m_baseStats.attackPotMagic = static_cast< uint32_t >( paramGrowthInfo->baseSpeed ); diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index f1ee1c46..db7f3b06 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -494,8 +494,8 @@ void Sapphire::Entity::Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEf if( nextSlot == -1 ) return; - pEffect->applyStatus(); m_statusEffectMap[ nextSlot ] = pEffect; + pEffect->applyStatus(); auto statusEffectAdd = makeZonePacket< FFXIVIpcEffectResult >( getId() ); @@ -594,14 +594,14 @@ void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sen statusEffectFreeSlot( effectSlotId ); + m_statusEffectMap.erase( effectSlotId ); + auto pEffect = pEffectIt->second; pEffect->removeStatus(); if( sendActorControl ) sendToInRangeSet( makeActorControl( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() ); - m_statusEffectMap.erase( effectSlotId ); - if( sendStatusList ) sendStatusEffectUpdate(); } @@ -941,6 +941,18 @@ uint32_t Sapphire::Entity::Chara::getStatValue( Sapphire::Common::BaseParam base break; } + case Common::BaseParam::Haste: + { + value = m_baseStats.haste; + for( auto const& statusIt : m_statusEffectMap ) + { + auto effectEntry = statusIt.second->getEffectEntry(); + if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) == Common::StatusEffectType::Haste ) + value -= effectEntry.effectValue1; + } + break; + } + case Common::BaseParam::CriticalHit: { value = m_baseStats.critHitRate; diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 62388701..fa6c3247 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -47,6 +47,7 @@ namespace Sapphire::Entity uint32_t healingPotMagic = 0; uint32_t determination = 0; uint32_t skillSpeed = 0; + uint32_t haste = 0; uint32_t resistSlow = 0; uint32_t resistSilence = 0; diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index 2171d930..be54088f 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -280,6 +280,7 @@ void Sapphire::Entity::Player::calculateStats() m_baseStats.pie = static_cast< uint32_t >( base ); m_baseStats.skillSpeed = paramGrowthInfo->baseSpeed; m_baseStats.spellSpeed = paramGrowthInfo->baseSpeed; + m_baseStats.haste = 100; m_baseStats.accuracy = paramGrowthInfo->baseSpeed; m_baseStats.critHitRate = paramGrowthInfo->baseSpeed; m_baseStats.attackPotMagic = paramGrowthInfo->baseSpeed; @@ -332,7 +333,7 @@ void Sapphire::Entity::Player::sendStats() statPacket->data().healingMagicPotency = getStatValue( Common::BaseParam::HealingMagicPotency ); statPacket->data().skillSpeed = getStatValue( Common::BaseParam::SkillSpeed ); statPacket->data().spellSpeed = getStatValue( Common::BaseParam::SpellSpeed ); - statPacket->data().haste = 100; + statPacket->data().haste = getStatValue( Common::BaseParam::Haste ); statPacket->data().criticalHit = getStatValue( Common::BaseParam::CriticalHit ); statPacket->data().defense = getStatValue( Common::BaseParam::Defense ); statPacket->data().magicDefense = getStatValue( Common::BaseParam::MagicDefense ); @@ -1128,7 +1129,7 @@ void Sapphire::Entity::Player::update( uint64_t tickCount ) actor->getPos().x, actor->getPos().y, actor->getPos().z ) <= range ) { - if( ( tickCount - m_lastAttack ) > mainWeap->getDelay() ) + if( ( tickCount - m_lastAttack ) > ( static_cast< float >( mainWeap->getDelay() ) * ( getStatValue( Common::BaseParam::Haste ) / 100.0f ) ) ) { m_lastAttack = tickCount; autoAttack( actor->getAsChara() ); diff --git a/src/world/StatusEffect/StatusEffect.cpp b/src/world/StatusEffect/StatusEffect.cpp index 45683f55..30ae2125 100644 --- a/src/world/StatusEffect/StatusEffect.cpp +++ b/src/world/StatusEffect/StatusEffect.cpp @@ -5,6 +5,7 @@ #include +#include "Actor/Player.h" #include "Actor/Chara.h" #include "Actor/Actor.h" @@ -64,31 +65,39 @@ void Sapphire::StatusEffect::StatusEffect::registerTickEffect( uint8_t type, uin std::pair< uint8_t, uint32_t > Sapphire::StatusEffect::StatusEffect::getTickEffect() { - auto statusEffectType = static_cast< Common::StatusEffectType >( m_effectEntry.effectType ); - if( statusEffectType == Common::StatusEffectType::Dot ) + switch( static_cast< Common::StatusEffectType >( m_effectEntry.effectType ) ) { - auto value = m_value; - if( m_cachedSourceCrit > Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) ) + case Common::StatusEffectType::Dot: { - value *= m_cachedSourceCritBonus; + auto value = m_value; + if( m_cachedSourceCrit > Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) ) + { + value *= m_cachedSourceCritBonus; + } + value *= 1.0f + ( ( Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) - 50.0f ) / 1000.0f ); + m_currTickEffect = std::make_pair( 1, value ); + break; } - value *= 1.0f + ( ( Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) - 50.0f ) / 1000.0f ); - m_currTickEffect = std::make_pair( 1, value ); - } - else if( statusEffectType == Common::StatusEffectType::Hot ) - { - auto value = m_value; - if( m_cachedSourceCrit > Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) ) + + case Common::StatusEffectType::Hot: { - value *= m_cachedSourceCritBonus; + auto value = m_value; + if( m_cachedSourceCrit > Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) ) + { + value *= m_cachedSourceCritBonus; + } + value *= 1.0f + ( ( Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) - 50.0f ) / 1000.0f ); + m_currTickEffect = std::make_pair( 2, value ); + break; + } + + default: + { + m_currTickEffect = std::make_pair( 0, 0 ); + break; } - value *= 1.0f + ( ( Sapphire::Math::CalcStats::range100( Sapphire::Math::CalcStats::rng ) - 50.0f ) / 1000.0f ); - m_currTickEffect = std::make_pair( 2, value ); - } - else - { - m_currTickEffect = std::make_pair( 0, 0 ); } + return m_currTickEffect; } @@ -123,59 +132,86 @@ uint16_t Sapphire::StatusEffect::StatusEffect::getParam() const void Sapphire::StatusEffect::StatusEffect::applyStatus() { m_startTime = Util::getTimeMs(); + if( m_lastTick == 0 ) + m_lastTick = m_startTime; + auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >(); - auto statusEffectType = static_cast< Common::StatusEffectType >( m_effectEntry.effectType ); - if( statusEffectType == Common::StatusEffectType::Dot ) + pScriptMgr->onStatusReceive( m_targetActor, m_id ); + + switch( static_cast< Common::StatusEffectType >( m_effectEntry.effectType ) ) { - auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( m_sourceActor ); - auto damage = Sapphire::Math::CalcStats::calcDamageBaseOnPotency( *m_sourceActor, m_effectEntry.effectValue2, wepDmg ); - - for( auto const& entry : m_sourceActor->getStatusEffectMap() ) + case Common::StatusEffectType::Dot: { - auto status = entry.second; - auto effectEntry = status->getEffectEntry(); - if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) != Common::StatusEffectType::DamageMultiplier ) - continue; - if( effectEntry.effectValue1 & m_effectEntry.effectValue1 ) - { - damage *= 1.0f + ( effectEntry.effectValue2 / 100.0f ); - } - } + auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( m_sourceActor ); + auto damage = Sapphire::Math::CalcStats::calcDamageBaseOnPotency( *m_sourceActor, m_effectEntry.effectValue2, wepDmg ); - m_value = Sapphire::Math::CalcStats::applyDamageReceiveMultiplier( *m_targetActor, damage, - m_effectEntry.effectValue1 == static_cast< int32_t >( Common::ActionTypeFilter::Physical ) ? Common::AttackType::Physical : - ( m_effectEntry.effectValue1 == static_cast< int32_t >( Common::ActionTypeFilter::Magical ) ? Common::AttackType::Magical : Common::AttackType::Unknown_0 ) ); - m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Damage ); - m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor ); - } - else if( statusEffectType == Common::StatusEffectType::Hot ) - { - auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( m_sourceActor ); - auto heal = Sapphire::Math::CalcStats::calcHealBaseOnPotency( *m_sourceActor, m_effectEntry.effectValue2, wepDmg ); - - if( m_effectEntry.effectValue1 == 0 ) // this value is always 0 atm, if statement here just in case there is a hot that isn't a "cast" - { for( auto const& entry : m_sourceActor->getStatusEffectMap() ) { auto status = entry.second; auto effectEntry = status->getEffectEntry(); - if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) != Common::StatusEffectType::HealCastMultiplier ) + if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) != Common::StatusEffectType::DamageMultiplier ) continue; - heal *= 1.0f + ( effectEntry.effectValue2 / 100.0f ); + if( effectEntry.effectValue1 & m_effectEntry.effectValue1 ) + { + damage *= 1.0f + ( effectEntry.effectValue2 / 100.0f ); + } } - } - m_value = Sapphire::Math::CalcStats::applyHealingReceiveMultiplier( *m_targetActor, heal ); - m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Heal ); - m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor ); - } - pScriptMgr->onStatusReceive( m_targetActor, m_id ); + m_value = Sapphire::Math::CalcStats::applyDamageReceiveMultiplier( *m_targetActor, damage, + m_effectEntry.effectValue1 == static_cast< int32_t >( Common::ActionTypeFilter::Physical ) ? Common::AttackType::Physical : + ( m_effectEntry.effectValue1 == static_cast< int32_t >( Common::ActionTypeFilter::Magical ) ? Common::AttackType::Magical : Common::AttackType::Unknown_0 ) ); + m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Damage ); + m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor ); + break; + } + + case Common::StatusEffectType::Hot: + { + auto wepDmg = Sapphire::Math::CalcStats::getWeaponDamage( m_sourceActor ); + auto heal = Sapphire::Math::CalcStats::calcHealBaseOnPotency( *m_sourceActor, m_effectEntry.effectValue2, wepDmg ); + + if( m_effectEntry.effectValue1 == 0 ) // this value is always 0 atm, if statement here just in case there is a hot that isn't a "cast" + { + for( auto const& entry : m_sourceActor->getStatusEffectMap() ) + { + auto status = entry.second; + auto effectEntry = status->getEffectEntry(); + if( static_cast< Common::StatusEffectType >( effectEntry.effectType ) != Common::StatusEffectType::HealCastMultiplier ) + continue; + heal *= 1.0f + ( effectEntry.effectValue2 / 100.0f ); + } + } + m_value = Sapphire::Math::CalcStats::applyHealingReceiveMultiplier( *m_targetActor, heal ); + m_cachedSourceCrit = Sapphire::Math::CalcStats::criticalHitProbability( *m_sourceActor, Common::CritDHBonusFilter::Heal ); + m_cachedSourceCritBonus = Sapphire::Math::CalcStats::criticalHitBonus( *m_sourceActor ); + break; + } + + case Common::StatusEffectType::Haste: + { + auto pPlayer = m_targetActor->getAsPlayer(); + if( pPlayer ) + pPlayer->sendStats(); + break; + } + } } void Sapphire::StatusEffect::StatusEffect::removeStatus() { auto pScriptMgr = m_pFw->get< Scripting::ScriptMgr >(); pScriptMgr->onStatusTimeOut( m_targetActor, m_id ); + + switch( static_cast< Common::StatusEffectType >( m_effectEntry.effectType ) ) + { + case Common::StatusEffectType::Haste: + { + auto pPlayer = m_targetActor->getAsPlayer(); + if( pPlayer ) + pPlayer->sendStats(); + break; + } + } } uint32_t Sapphire::StatusEffect::StatusEffect::getId() const