From 17b06e31d85a52a7ce30fd9b448f5cf6a43c9c6b Mon Sep 17 00:00:00 2001 From: collett Date: Wed, 8 Mar 2023 19:57:03 +0900 Subject: [PATCH] add invuln to EffectBuilder --- src/common/Common.h | 26 ++++++++-------- src/world/Action/Action.cpp | 49 +++++++++++++++++++++++++----- src/world/Action/ActionLutData.cpp | 14 ++++++++- src/world/Action/EffectBuilder.cpp | 7 +++++ src/world/Action/EffectBuilder.h | 2 ++ src/world/Action/EffectResult.cpp | 7 +++++ src/world/Action/EffectResult.h | 1 + src/world/Actor/Chara.cpp | 34 +++++++++++++++++++-- src/world/Actor/Chara.h | 4 +++ 9 files changed, 120 insertions(+), 24 deletions(-) diff --git a/src/common/Common.h b/src/common/Common.h index 1a154d36..26dfce34 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -691,22 +691,20 @@ namespace Sapphire::Common Unknown_0 = 9, MpLoss = 10, MpGain = 11, - TpLoss = 12, - //TpGain = 13, // everything below shifted one up since 5.2 - GpGain = 13, - ApplyStatusEffectTarget = 14, - ApplyStatusEffectSource = 15, // effect entry on target but buff applies to source, like storm's eye + //TpLoss = 12, // these don't seem to have any effect now + //TpGain = 13, + //GpGain = 14, + ApplyStatusEffectTarget = 14, // CALC_RESULT_TYPE_SET_STATUS + ApplyStatusEffectSource = 15, // CALC_RESULT_TYPE_SET_STATUS_ME + StatusLoss = 16, + StatusLoss2 = 17, // different flying text + StatusLoss3 = 18, // looks the same as 16 StatusNoEffect = 20, - /*! - * @brief Tells the client that it should show combo indicators on actions. - * - * @param flags Required to be 128, doesn't show combo rings on hotbars otherwise - * @param value The actionid that starts/continues the combo. eg, 3617 will start a spinning slash and/or syphon strike combo - */ + // hate related effects here Provoke = 24, StartActionCombo = 27, - ComboSucceed = 28, // two more values inserted between this and Mount since 5.2 - Knockback = 33, + ComboSucceed = 28, + //Knockback = 33, Mount = 40, VFX = 59, // links to VFX sheet }; @@ -1123,6 +1121,8 @@ namespace Sapphire::Common MPRestorePerGCD = 15, AlwaysCombo = 16, PotencyMultiplier = 17, + Invulnerable = 18, + CannotDie = 19, }; enum class ActionTypeFilter : int32_t diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 8dfc6a43..5b068d83 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -518,6 +518,8 @@ void Action::Action::buildEffects() } if( !shouldHitThisTarget ) continue; + + bool shouldTriggerActionBonus = true; if( m_lutEntry.damagePotency > 0 ) { Common::AttackType attackType = static_cast< Common::AttackType >( m_actionData->attackType ); @@ -541,6 +543,12 @@ void Action::Action::buildEffects() float blocked = 0; float parried = 0; + if( actor->hasInvulnerableEffect() ) + { + dmg.first = 0; + shouldTriggerActionBonus = false; + } + if( dmg.first > 0 ) { dodged = Math::CalcStats::calcDodge( *actor ); @@ -560,7 +568,10 @@ void Action::Action::buildEffects() } if( dodged ) + { dmg.first = 0; + shouldTriggerActionBonus = false; + } else { dmg.first -= blocked; @@ -576,7 +587,6 @@ void Action::Action::buildEffects() m_effectBuilder->parriedDamage( actor, actor, dmg.first, static_cast< uint16_t >( parried / originalDamage * 100 ), dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 ); else m_effectBuilder->damage( actor, actor, dmg.first, dmg.second, dmg.first == 0 ? Common::ActionEffectResultFlag::Absorbed : Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 ); - auto reflectDmg = Math::CalcStats::calcDamageReflect( m_pSource, actor, dmg.first, getActionTypeFilterFromAttackType( attackType ) ); if( reflectDmg.first > 0 ) { @@ -599,11 +609,11 @@ void Action::Action::buildEffects() } else { - // todo: no effect or invulnerable + m_effectBuilder->invulnerable( actor ); } } - if( !dodged ) + if( shouldTriggerActionBonus ) { if( ( !isComboAction() || isCorrectCombo() ) ) { @@ -702,7 +712,7 @@ void Action::Action::buildEffects() } } - if( m_lutEntry.healPotency > 0 ) + if( m_lutEntry.healPotency > 0 && actor->getObjKind() == m_pSource->getObjKind() /* is friendly target, this will do for now */) { auto heal = calcHealing( m_lutEntry.healPotency ); heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *actor, heal.first ); @@ -711,8 +721,15 @@ void Action::Action::buildEffects() if( m_lutEntry.targetStatus != 0 ) { - if( !isComboAction() || isCorrectCombo() ) - m_effectBuilder->applyStatusEffect( actor, m_pSource, m_lutEntry.targetStatus, m_lutEntry.targetStatusDuration, m_lutEntry.targetStatusParam, getExecutionDelay() + victimCounter * 100 ); + if( shouldTriggerActionBonus || actor->getObjKind() == m_pSource->getObjKind() /* is friendly target, this will do for now */ ) + { + if( !isComboAction() || isCorrectCombo() ) + m_effectBuilder->applyStatusEffect( actor, m_pSource, m_lutEntry.targetStatus, m_lutEntry.targetStatusDuration, m_lutEntry.targetStatusParam, getExecutionDelay() + victimCounter * 100 ); + } + else if( actor->hasInvulnerableEffect() ) + { + m_effectBuilder->invulnerable( actor, m_lutEntry.targetStatus ); + } } } @@ -722,7 +739,7 @@ void Action::Action::buildEffects() { if( firstValidVictim ) m_effectBuilder->applyStatusEffect( firstValidVictim, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam, getExecutionDelay(), true ); - else + else if ( m_lutEntry.damagePotency == 0 ) // only non-offensive actions can apply self status without a valid victim m_effectBuilder->applyStatusEffect( m_pSource, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam, getExecutionDelay() ); } } @@ -756,14 +773,23 @@ bool Action::Action::playerPreCheck( Entity::Player& player ) { // lol if( !player.isAlive() ) + { + player.sendUrgent( "Action::playerPreCheck not alive." ); return false; + } // npc actions/non player actions if( m_actionData->classJob == -1 && !m_actionData->isRoleAction ) + { + player.sendUrgent( "Action::playerPreCheck action not allowed." ); return false; + } if( player.getLevel() < m_actionData->classJobLevel ) + { + player.sendUrgent( "Action::playerPreCheck level too low." ); return false; + } auto currentClass = player.getClass(); auto actionClass = static_cast< Common::ClassJob >( m_actionData->classJob ); @@ -778,11 +804,17 @@ bool Action::Action::playerPreCheck( Entity::Player& player ) return false; if( classJob->classJobParent != m_actionData->classJob ) + { + player.sendUrgent( "Action::playerPreCheck class mismatch." ); return false; + } } if( !m_actionData->canTargetSelf && getTargetId() == m_pSource->getId() ) + { + player.sendUrgent( "Action::playerPreCheck cannot target self." ); return false; + } // todo: does this need to check for party/alliance stuff or it's just same type? // todo: m_pTarget doesn't exist at this stage because we only fill it when we snapshot targets @@ -798,7 +830,10 @@ bool Action::Action::playerPreCheck( Entity::Player& player ) if( !hasResources() ) + { + player.sendUrgent( "Action::playerPreCheck not enough resource." ); return false; + } return true; } diff --git a/src/world/Action/ActionLutData.cpp b/src/world/Action/ActionLutData.cpp index 8b5ec2fb..f4ec9195 100644 --- a/src/world/Action/ActionLutData.cpp +++ b/src/world/Action/ActionLutData.cpp @@ -88,7 +88,8 @@ ActionLut::Lut ActionLut::m_actionLut = { 3540, { 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, 0, 0, 0 } }, //Clemency, クレメンシー - { 3541, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + //has heal: potency 1200 + { 3541, { 0, 0, 0, 1200, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, //Royal Authority, ロイヤルアソリティ //has damage: potency 100, combo potency 550, directional potency 0 @@ -3545,4 +3546,15 @@ ActionLut::StatusEffectTable ActionLut::m_statusEffectTable = //Shifu, 士風: Haste, 13% { 1299, { 12, 13, 0, 0, 0 } }, + //Hallowed Ground, インビンシブル: Invulnerable + { 82, { 18, 0, 0, 0, 0 } }, + + //Superbolide, ボーライド: Invulnerable + { 1838, { 18, 0, 0, 0, 0 } }, + + //Holmgang, ホルムギャング: CannotDie + { 409, { 19, 0, 0, 0, 0 } }, + + //Walking Dead, ウォーキングデッド: CannotDie + { 811, { 19, 0, 0, 0, 0 } }, }; diff --git a/src/world/Action/EffectBuilder.cpp b/src/world/Action/EffectBuilder.cpp index b570fbcb..e28c3f51 100644 --- a/src/world/Action/EffectBuilder.cpp +++ b/src/world/Action/EffectBuilder.cpp @@ -134,6 +134,13 @@ void Sapphire::World::Action::EffectBuilder::provoke( Entity::CharaPtr& target ) moveToResultList( target, nextResult ); } +void Sapphire::World::Action::EffectBuilder::invulnerable( Entity::CharaPtr& target, uint16_t status ) +{ + EffectResultPtr nextResult = make_EffectResult( target, 0 ); + nextResult->invulnerable( status ); + moveToResultList( target, nextResult ); +} + void EffectBuilder::setAnimationLock( float animationLock ) { m_animationLock = animationLock; diff --git a/src/world/Action/EffectBuilder.h b/src/world/Action/EffectBuilder.h index 78f020a2..46eb3438 100644 --- a/src/world/Action/EffectBuilder.h +++ b/src/world/Action/EffectBuilder.h @@ -45,6 +45,8 @@ namespace Sapphire::World::Action void provoke( Entity::CharaPtr& target ); + void invulnerable( Entity::CharaPtr& target, uint16_t status = 0 ); + void buildAndSendPackets(); private: diff --git a/src/world/Action/EffectResult.cpp b/src/world/Action/EffectResult.cpp index c9a3cb3b..a031a5fd 100644 --- a/src/world/Action/EffectResult.cpp +++ b/src/world/Action/EffectResult.cpp @@ -155,6 +155,13 @@ void Sapphire::World::Action::EffectResult::provoke() m_type = Common::ActionEffectType::Provoke; } +void Sapphire::World::Action::EffectResult::invulnerable( uint16_t status ) +{ + m_value = status; + + m_type = Common::ActionEffectType::Invulnerable; +} + Common::EffectEntry EffectResult::buildEffectEntry() const { Common::EffectEntry entry{}; diff --git a/src/world/Action/EffectResult.h b/src/world/Action/EffectResult.h index 52ac289d..33160e6d 100644 --- a/src/world/Action/EffectResult.h +++ b/src/world/Action/EffectResult.h @@ -31,6 +31,7 @@ namespace Sapphire::World::Action void statusNoEffect( uint16_t statusId ); void mount( uint16_t mountId ); void provoke(); + void invulnerable( uint16_t status ); Entity::CharaPtr getSource() const; Entity::CharaPtr getTarget() const; diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 011f1f6a..cdc7c414 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -396,6 +396,12 @@ in range */ void Sapphire::Entity::Chara::takeDamage( uint32_t damage ) { + if( hasInvulnerableEffect() ) + return; + + if( hasCannotDieEffect() ) + damage = std::min( damage, m_hp - 1 ); + if( damage >= m_hp ) { switch( m_invincibilityType ) @@ -414,10 +420,11 @@ void Sapphire::Entity::Chara::takeDamage( uint32_t damage ) break; } } - else + else if ( damage > 0 ) + { m_hp -= damage; - - sendStatusUpdate(); + sendStatusUpdate(); + } } /*! @@ -998,6 +1005,27 @@ void Sapphire::Entity::Chara::sendVisualEffect() sendToInRangeSet( pPacket, true ); } +bool Sapphire::Entity::Chara::hasInvulnerableEffect() const +{ + for( auto effectIt : m_statusEffectMap ) + { + if( effectIt.second->getEffectEntry().getType() == StatusEffectType::Invulnerable ) + return true; + } + return false; +} + +bool Sapphire::Entity::Chara::hasCannotDieEffect() const +{ + for( auto effectIt : m_statusEffectMap ) + { + if( effectIt.second->getEffectEntry().getType() == StatusEffectType::CannotDie ) + return true; + } + return false; +} + + void Sapphire::Entity::Chara::onTick() { uint32_t thisTickDmg = 0; diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index c5be04b6..d503da22 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -240,6 +240,10 @@ namespace Sapphire::Entity void die(); + bool hasInvulnerableEffect() const; + + bool hasCannotDieEffect() const; + Common::ActorStatus getStatus() const; void setStatus( Common::ActorStatus status );