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:
parent
0cf8009c3a
commit
17b06e31d8
9 changed files with 120 additions and 24 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 } },
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -240,6 +240,10 @@ namespace Sapphire::Entity
|
|||
|
||||
void die();
|
||||
|
||||
bool hasInvulnerableEffect() const;
|
||||
|
||||
bool hasCannotDieEffect() const;
|
||||
|
||||
Common::ActorStatus getStatus() const;
|
||||
|
||||
void setStatus( Common::ActorStatus status );
|
||||
|
|
Loading…
Add table
Reference in a new issue