1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-03 17:27:47 +00:00

add invuln to EffectBuilder

This commit is contained in:
collett 2023-03-08 19:57:03 +09:00
parent 0cf8009c3a
commit 17b06e31d8
9 changed files with 120 additions and 24 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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 } },
};

View file

@ -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;

View file

@ -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:

View file

@ -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{};

View file

@ -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;

View file

@ -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;

View file

@ -240,6 +240,10 @@ namespace Sapphire::Entity
void die();
bool hasInvulnerableEffect() const;
bool hasCannotDieEffect() const;
Common::ActorStatus getStatus() const;
void setStatus( Common::ActorStatus status );