mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-28 20:27:46 +00:00
Attempt to fix combo actions.
This commit is contained in:
parent
f6bff46c9a
commit
56296d295a
8 changed files with 132 additions and 36 deletions
|
@ -629,6 +629,7 @@ namespace Sapphire::Common
|
||||||
* @param value The actionid that starts/continues the combo. eg, 3617 will start a spinning slash and/or syphon strike combo
|
* @param value The actionid that starts/continues the combo. eg, 3617 will start a spinning slash and/or syphon strike combo
|
||||||
*/
|
*/
|
||||||
StartActionCombo = 28,
|
StartActionCombo = 28,
|
||||||
|
ComboVisualEffect = 29,
|
||||||
Knockback = 33,
|
Knockback = 33,
|
||||||
Mount = 38,
|
Mount = 38,
|
||||||
VFX = 59, // links to VFX sheet
|
VFX = 59, // links to VFX sheet
|
||||||
|
|
|
@ -336,7 +336,7 @@ void Action::Action::execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isComboAction() )
|
if( isCorrectCombo() )
|
||||||
{
|
{
|
||||||
auto player = m_pSource->getAsPlayer();
|
auto player = m_pSource->getAsPlayer();
|
||||||
|
|
||||||
|
@ -354,7 +354,7 @@ void Action::Action::execute()
|
||||||
|
|
||||||
// set currently casted action as the combo action if it interrupts a combo
|
// set currently casted action as the combo action if it interrupts a combo
|
||||||
// ignore it otherwise (ogcds, etc.)
|
// ignore it otherwise (ogcds, etc.)
|
||||||
if( !m_actionData->preservesCombo )
|
if( !m_actionData->preservesCombo && ( !isComboAction() || isCorrectCombo() ) )
|
||||||
{
|
{
|
||||||
m_pSource->setLastComboActionId( getId() );
|
m_pSource->setLastComboActionId( getId() );
|
||||||
}
|
}
|
||||||
|
@ -418,19 +418,40 @@ void Action::Action::buildEffects()
|
||||||
|
|
||||||
for( auto& actor : m_hitActors )
|
for( auto& actor : m_hitActors )
|
||||||
{
|
{
|
||||||
// todo: this is shit
|
if( lutEntry.potency > 0 )
|
||||||
if( lutEntry.curePotency > 0 )
|
|
||||||
{
|
{
|
||||||
|
auto dmg = calcDamage( isCorrectCombo() ? lutEntry.comboPotency : lutEntry.potency );
|
||||||
m_effectBuilder->healTarget( actor, lutEntry.curePotency );
|
|
||||||
}
|
|
||||||
|
|
||||||
else if( lutEntry.potency > 0 )
|
|
||||||
{
|
|
||||||
auto dmg = calcDamage( lutEntry.potency );
|
|
||||||
m_effectBuilder->damageTarget( actor, dmg.first, dmg.second );
|
m_effectBuilder->damageTarget( actor, dmg.first, dmg.second );
|
||||||
|
|
||||||
if ( dmg.first > 0 )
|
if ( dmg.first > 0 )
|
||||||
actor->onActionHostile( m_pSource );
|
actor->onActionHostile( m_pSource );
|
||||||
|
|
||||||
|
if ( isCorrectCombo() )
|
||||||
|
{
|
||||||
|
m_effectBuilder->comboVisualEffect( actor );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !isComboAction() || isCorrectCombo() )
|
||||||
|
{
|
||||||
|
if( lutEntry.curePotency > 0 ) // actions with self heal
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Calling m_effectBuilder->healTarget( m_pSource, lutEntry.curePotency ) seems to work fine,
|
||||||
|
but it will end up sending two Effect packets to the client. However on retail everything is in one single packet.
|
||||||
|
*/
|
||||||
|
m_effectBuilder->selfHeal( actor, m_pSource, lutEntry.curePotency );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !m_actionData->preservesCombo ) // we need something like m_actionData->hasNextComboAction
|
||||||
|
{
|
||||||
|
m_effectBuilder->startCombo( actor, getId() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( lutEntry.curePotency > 0 )
|
||||||
|
{
|
||||||
|
// todo: calcHealing()
|
||||||
|
m_effectBuilder->healTarget( actor, lutEntry.curePotency );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +534,7 @@ void Action::Action::setAdditionalData( uint32_t data )
|
||||||
m_additionalData = data;
|
m_additionalData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Action::Action::isComboAction() const
|
bool Action::Action::isCorrectCombo() const
|
||||||
{
|
{
|
||||||
auto lastActionId = m_pSource->getLastComboActionId();
|
auto lastActionId = m_pSource->getLastComboActionId();
|
||||||
|
|
||||||
|
@ -525,6 +546,11 @@ bool Action::Action::isComboAction() const
|
||||||
return m_actionData->actionCombo == lastActionId;
|
return m_actionData->actionCombo == lastActionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Action::Action::isComboAction() const
|
||||||
|
{
|
||||||
|
return m_actionData->actionCombo != 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool Action::Action::primaryCostCheck( bool subtractCosts )
|
bool Action::Action::primaryCostCheck( bool subtractCosts )
|
||||||
{
|
{
|
||||||
switch( m_primaryCostType )
|
switch( m_primaryCostType )
|
||||||
|
|
|
@ -45,6 +45,8 @@ namespace Sapphire::World::Action
|
||||||
uint32_t getAdditionalData() const;
|
uint32_t getAdditionalData() const;
|
||||||
void setAdditionalData( uint32_t data );
|
void setAdditionalData( uint32_t data );
|
||||||
|
|
||||||
|
bool isCorrectCombo() const;
|
||||||
|
|
||||||
bool isComboAction() const;
|
bool isComboAction() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -31,18 +31,17 @@ uint64_t EffectBuilder::getResultDelayMs()
|
||||||
return Common::Util::getTimeMs() + 850;
|
return Common::Util::getTimeMs() + 850;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectResultPtr EffectBuilder::getResult( Entity::CharaPtr& chara )
|
std::shared_ptr< std::vector< EffectResultPtr > > EffectBuilder::getResultList( Entity::CharaPtr& chara )
|
||||||
{
|
{
|
||||||
auto it = m_resolvedEffects.find( chara->getId() );
|
auto it = m_resolvedEffects.find( chara->getId() );
|
||||||
if( it == m_resolvedEffects.end() )
|
if( it == m_resolvedEffects.end() )
|
||||||
{
|
{
|
||||||
// create a new one and return it
|
// create a new one and return it
|
||||||
// todo: this feels kinda dirty but makes for easy work
|
auto resultList = std::make_shared< std::vector< EffectResultPtr > >();
|
||||||
auto result = make_EffectResult( chara, getResultDelayMs() );
|
|
||||||
|
|
||||||
m_resolvedEffects[ chara->getId() ] = result;
|
m_resolvedEffects[ chara->getId() ] = resultList;
|
||||||
|
|
||||||
return result;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
|
@ -50,18 +49,52 @@ EffectResultPtr EffectBuilder::getResult( Entity::CharaPtr& chara )
|
||||||
|
|
||||||
void EffectBuilder::healTarget( Entity::CharaPtr& target, uint32_t amount, Common::ActionHitSeverityType severity )
|
void EffectBuilder::healTarget( Entity::CharaPtr& target, uint32_t amount, Common::ActionHitSeverityType severity )
|
||||||
{
|
{
|
||||||
auto result = getResult( target );
|
auto resultList = getResultList( target );
|
||||||
assert( result );
|
assert( resultList );
|
||||||
|
|
||||||
result->heal( amount, severity );
|
EffectResultPtr nextResult = make_EffectResult( target, getResultDelayMs() );
|
||||||
|
nextResult->heal( amount, severity, false );
|
||||||
|
resultList->push_back( std::move( nextResult ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectBuilder::selfHeal( Entity::CharaPtr& target, Entity::CharaPtr& source, uint32_t amount, Common::ActionHitSeverityType severity )
|
||||||
|
{
|
||||||
|
auto resultList = getResultList( target );
|
||||||
|
assert( resultList );
|
||||||
|
|
||||||
|
EffectResultPtr nextResult = make_EffectResult( source, getResultDelayMs() ); // heal the source actor
|
||||||
|
nextResult->heal( amount, severity, true );
|
||||||
|
resultList->push_back( std::move( nextResult ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBuilder::damageTarget( Entity::CharaPtr& target, uint32_t amount, Common::ActionHitSeverityType severity )
|
void EffectBuilder::damageTarget( Entity::CharaPtr& target, uint32_t amount, Common::ActionHitSeverityType severity )
|
||||||
{
|
{
|
||||||
auto result = getResult( target );
|
auto resultList = getResultList( target );
|
||||||
assert( result );
|
assert( resultList );
|
||||||
|
|
||||||
result->damage( amount, severity );
|
EffectResultPtr nextResult = make_EffectResult( target, getResultDelayMs() );
|
||||||
|
nextResult->damage( amount, severity );
|
||||||
|
resultList->push_back( std::move( nextResult ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId )
|
||||||
|
{
|
||||||
|
auto resultList = getResultList( target );
|
||||||
|
assert( resultList );
|
||||||
|
|
||||||
|
EffectResultPtr nextResult = make_EffectResult( target, 0 );
|
||||||
|
nextResult->startCombo( actionId );
|
||||||
|
resultList->push_back( std::move( nextResult ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectBuilder::comboVisualEffect( Entity::CharaPtr& target )
|
||||||
|
{
|
||||||
|
auto resultList = getResultList( target );
|
||||||
|
assert( resultList );
|
||||||
|
|
||||||
|
EffectResultPtr nextResult = make_EffectResult( target, 0 );
|
||||||
|
nextResult->comboVisualEffect();
|
||||||
|
resultList->push_back( std::move( nextResult ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBuilder::buildAndSendPackets()
|
void EffectBuilder::buildAndSendPackets()
|
||||||
|
@ -71,22 +104,29 @@ void EffectBuilder::buildAndSendPackets()
|
||||||
|
|
||||||
for( auto it = m_resolvedEffects.begin(); it != m_resolvedEffects.end(); )
|
for( auto it = m_resolvedEffects.begin(); it != m_resolvedEffects.end(); )
|
||||||
{
|
{
|
||||||
auto result = it->second;
|
auto resultList = it->second;
|
||||||
Logger::debug( " - id: {}", result->getTarget()->getId() );
|
assert( resultList->size() > 0 );
|
||||||
|
auto firstResult = resultList->data()[ 0 ];
|
||||||
|
Logger::debug( " - id: {}", firstResult->getTarget()->getId() );
|
||||||
|
|
||||||
auto seq = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
auto seq = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
||||||
|
|
||||||
auto effectPacket = std::make_shared< Server::EffectPacket >( m_sourceChara->getId(), result->getTarget()->getId(), m_actionId );
|
auto effectPacket = std::make_shared< Server::EffectPacket >( m_sourceChara->getId(), firstResult->getTarget()->getId(), m_actionId );
|
||||||
effectPacket->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) );
|
effectPacket->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) );
|
||||||
effectPacket->setSequence( seq, m_sequence );
|
effectPacket->setSequence( seq, m_sequence );
|
||||||
|
|
||||||
effectPacket->addEffect( result->buildEffectEntry() );
|
for( int i = 0; i < resultList->size(); i++ )
|
||||||
|
{
|
||||||
|
auto result = resultList->data()[ i ];
|
||||||
|
effectPacket->addEffect( result->buildEffectEntry() );
|
||||||
|
// add effect to territory
|
||||||
|
m_sourceChara->getCurrentTerritory()->addEffectResult( std::move( result ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
resultList->clear();
|
||||||
|
|
||||||
m_sourceChara->sendToInRangeSet( effectPacket, true );
|
m_sourceChara->sendToInRangeSet( effectPacket, true );
|
||||||
|
|
||||||
// add effect to territory
|
|
||||||
m_sourceChara->getCurrentTerritory()->addEffectResult( std::move( result ) );
|
|
||||||
|
|
||||||
it = m_resolvedEffects.erase( it );
|
it = m_resolvedEffects.erase( it );
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,14 +15,21 @@ namespace Sapphire::World::Action
|
||||||
void healTarget( Entity::CharaPtr& target, uint32_t amount,
|
void healTarget( Entity::CharaPtr& target, uint32_t amount,
|
||||||
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal );
|
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal );
|
||||||
|
|
||||||
|
void selfHeal( Entity::CharaPtr& target, Entity::CharaPtr& source, uint32_t amount,
|
||||||
|
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal );
|
||||||
|
|
||||||
void damageTarget( Entity::CharaPtr& target, uint32_t amount,
|
void damageTarget( Entity::CharaPtr& target, uint32_t amount,
|
||||||
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage );
|
Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage );
|
||||||
|
|
||||||
|
void startCombo( Entity::CharaPtr& target, uint16_t actionId );
|
||||||
|
|
||||||
|
void comboVisualEffect( Entity::CharaPtr& target );
|
||||||
|
|
||||||
void buildAndSendPackets();
|
void buildAndSendPackets();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EffectResultPtr getResult( Entity::CharaPtr& chara );
|
std::shared_ptr< std::vector< EffectResultPtr > > getResultList( Entity::CharaPtr& chara );
|
||||||
|
|
||||||
uint64_t getResultDelayMs();
|
uint64_t getResultDelayMs();
|
||||||
|
|
||||||
|
@ -31,7 +38,7 @@ namespace Sapphire::World::Action
|
||||||
uint16_t m_sequence;
|
uint16_t m_sequence;
|
||||||
|
|
||||||
Entity::CharaPtr m_sourceChara;
|
Entity::CharaPtr m_sourceChara;
|
||||||
std::unordered_map< uint32_t, EffectResultPtr > m_resolvedEffects;
|
std::unordered_map< uint32_t, std::shared_ptr< std::vector< EffectResultPtr > > > m_resolvedEffects;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ EffectResult::EffectResult( Entity::CharaPtr target, uint64_t runAfter ) :
|
||||||
m_value( 0 ),
|
m_value( 0 ),
|
||||||
m_severity( Common::ActionHitSeverityType::NormalDamage ),
|
m_severity( Common::ActionHitSeverityType::NormalDamage ),
|
||||||
m_type( Common::ActionEffectType::Nothing ),
|
m_type( Common::ActionEffectType::Nothing ),
|
||||||
m_param( 0 )
|
m_param( 0 ),
|
||||||
|
m_flag( 0 )
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,14 +48,28 @@ void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severi
|
||||||
m_type = Common::ActionEffectType::Damage;
|
m_type = Common::ActionEffectType::Damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectResult::heal( uint32_t amount, Sapphire::Common::ActionHitSeverityType severity )
|
void EffectResult::heal( uint32_t amount, Sapphire::Common::ActionHitSeverityType severity, bool isSelfHeal )
|
||||||
{
|
{
|
||||||
m_severity = severity;
|
m_severity = severity;
|
||||||
m_value = amount;
|
m_value = amount;
|
||||||
|
m_flag = isSelfHeal ? 0x80 : 0; // flag == 0x80 displays healing text at source actor
|
||||||
|
|
||||||
m_type = Common::ActionEffectType::Heal;
|
m_type = Common::ActionEffectType::Heal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectResult::startCombo( uint16_t actionId )
|
||||||
|
{
|
||||||
|
m_value = actionId;
|
||||||
|
m_flag = 0x80;
|
||||||
|
|
||||||
|
m_type = Common::ActionEffectType::StartActionCombo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectResult::comboVisualEffect()
|
||||||
|
{
|
||||||
|
m_type = Common::ActionEffectType::ComboVisualEffect;
|
||||||
|
}
|
||||||
|
|
||||||
Common::EffectEntry EffectResult::buildEffectEntry() const
|
Common::EffectEntry EffectResult::buildEffectEntry() const
|
||||||
{
|
{
|
||||||
Common::EffectEntry entry{};
|
Common::EffectEntry entry{};
|
||||||
|
@ -64,6 +79,7 @@ Common::EffectEntry EffectResult::buildEffectEntry() const
|
||||||
entry.hitSeverity = m_severity;
|
entry.hitSeverity = m_severity;
|
||||||
entry.effectType = m_type;
|
entry.effectType = m_type;
|
||||||
entry.param = m_param;
|
entry.param = m_param;
|
||||||
|
entry.flags = m_flag;
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ namespace Sapphire::World::Action
|
||||||
explicit EffectResult( Entity::CharaPtr target, uint64_t delayMs );
|
explicit EffectResult( Entity::CharaPtr target, uint64_t delayMs );
|
||||||
|
|
||||||
void damage( uint32_t amount, Common::ActionHitSeverityType severity );
|
void damage( uint32_t amount, Common::ActionHitSeverityType severity );
|
||||||
void heal( uint32_t amount, Common::ActionHitSeverityType severity );
|
void heal( uint32_t amount, Common::ActionHitSeverityType severity, bool isSelfHeal );
|
||||||
|
void startCombo( uint16_t actionId );
|
||||||
|
void comboVisualEffect();
|
||||||
|
|
||||||
Entity::CharaPtr getTarget() const;
|
Entity::CharaPtr getTarget() const;
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ namespace Sapphire::World::Action
|
||||||
|
|
||||||
uint32_t m_value;
|
uint32_t m_value;
|
||||||
uint8_t m_param;
|
uint8_t m_param;
|
||||||
|
uint8_t m_flag;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,14 @@ namespace Sapphire::Network::Packets::Server
|
||||||
m_data.effectTargetId = targetId;
|
m_data.effectTargetId = targetId;
|
||||||
|
|
||||||
m_data.effectDisplayType = Common::ActionEffectDisplayType::ShowActionName;
|
m_data.effectDisplayType = Common::ActionEffectDisplayType::ShowActionName;
|
||||||
|
|
||||||
|
std::memset(m_data.effects, 0, sizeof(Common::EffectEntry) * 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEffect( const Common::EffectEntry& effect )
|
void addEffect( const Common::EffectEntry& effect )
|
||||||
{
|
{
|
||||||
assert( m_data.effectCount <= 8 );
|
assert( m_data.effectCount <= 8 );
|
||||||
|
|
||||||
std::memset( m_data.effects, 0, sizeof( Common::EffectEntry ) * 8 );
|
|
||||||
std::memcpy( &m_data.effects[ m_data.effectCount * 8 ], &effect, sizeof( Common::EffectEntry ) );
|
std::memcpy( &m_data.effects[ m_data.effectCount * 8 ], &effect, sizeof( Common::EffectEntry ) );
|
||||||
m_data.effectCount++;
|
m_data.effectCount++;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue