mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-28 20:27:46 +00:00
More aoe stuff.
This commit is contained in:
parent
03df9ab4d5
commit
84a868aae8
3 changed files with 147 additions and 51 deletions
|
@ -537,33 +537,6 @@ namespace Sapphire::Network::Packets::Server
|
||||||
/* 0012 */ uint32_t unknown_12;
|
/* 0012 */ uint32_t unknown_12;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Structural representation of the packet sent by the server
|
|
||||||
* for battle actions
|
|
||||||
*/
|
|
||||||
struct EffectHeader
|
|
||||||
{
|
|
||||||
uint64_t animationTargetId; // who the animation targets
|
|
||||||
|
|
||||||
uint32_t actionId; // what the casting player casts, shown in battle log/ui
|
|
||||||
uint32_t globalEffectCounter; // seems to only increment on retail?
|
|
||||||
|
|
||||||
float animationLockTime; // maybe? doesn't seem to do anything
|
|
||||||
uint32_t someTargetId; // always 00 00 00 E0, 0x0E000000 is the internal def for INVALID TARGET ID
|
|
||||||
|
|
||||||
uint16_t hiddenAnimation; // if 0, always shows animation, otherwise hides it. counts up by 1 for each animation skipped on a caster
|
|
||||||
uint16_t rotation;
|
|
||||||
uint16_t actionAnimationId; // the animation that is played by the casting character
|
|
||||||
uint8_t variation; // variation in the animation
|
|
||||||
Common::ActionEffectDisplayType effectDisplayType;
|
|
||||||
|
|
||||||
uint8_t unknown20; // is read by handler, runs code which gets the LODWORD of animationLockTime (wtf?)
|
|
||||||
uint8_t effectCount; // ignores effects if 0, otherwise parses all of them
|
|
||||||
uint16_t padding_21;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FFXIVIpcEffect : FFXIVIpcBasePacket< Effect >
|
struct FFXIVIpcEffect : FFXIVIpcBasePacket< Effect >
|
||||||
{
|
{
|
||||||
uint64_t animationTargetId; // who the animation targets
|
uint64_t animationTargetId; // who the animation targets
|
||||||
|
@ -608,17 +581,35 @@ namespace Sapphire::Network::Packets::Server
|
||||||
template< int size >
|
template< int size >
|
||||||
struct FFXIVIpcAoeEffect
|
struct FFXIVIpcAoeEffect
|
||||||
{
|
{
|
||||||
EffectHeader header;
|
uint64_t animationTargetId; // who the animation targets
|
||||||
|
|
||||||
Common::EffectEntry effects[size];
|
uint32_t actionId; // what the casting player casts, shown in battle log/ui
|
||||||
|
uint32_t globalEffectCounter; // seems to only increment on retail?
|
||||||
|
|
||||||
|
float animationLockTime; // maybe? doesn't seem to do anything
|
||||||
|
uint32_t someTargetId; // always 00 00 00 E0, 0x0E000000 is the internal def for INVALID TARGET ID
|
||||||
|
|
||||||
|
uint16_t hiddenAnimation; // if 0, always shows animation, otherwise hides it. counts up by 1 for each animation skipped on a caster
|
||||||
|
uint16_t rotation;
|
||||||
|
uint16_t actionAnimationId; // the animation that is played by the casting character
|
||||||
|
uint8_t variation; // variation in the animation
|
||||||
|
Common::ActionEffectDisplayType effectDisplayType;
|
||||||
|
|
||||||
|
uint8_t unknown20; // is read by handler, runs code which gets the LODWORD of animationLockTime (wtf?)
|
||||||
|
uint8_t effectCount; // ignores effects if 0, otherwise parses all of them
|
||||||
|
uint16_t padding_21[3];
|
||||||
|
uint16_t padding;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
Common::EffectEntry entries[8];
|
||||||
|
} effects[size];
|
||||||
|
|
||||||
uint16_t padding_6A[3];
|
uint16_t padding_6A[3];
|
||||||
|
|
||||||
uint32_t effectTargetId[size];
|
uint64_t effectTargetId[size];
|
||||||
Common::FFXIVARR_POSITION3 position;
|
uint16_t unkFlag[3]; // all 0x7FFF
|
||||||
uint32_t effectFlags;
|
uint16_t unk[3];
|
||||||
|
|
||||||
uint32_t padding_78;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFXIVIpcAoeEffect8 :
|
struct FFXIVIpcAoeEffect8 :
|
||||||
|
|
|
@ -459,6 +459,10 @@ void Action::Action::buildEffects()
|
||||||
m_lutEntry.curePotency, m_lutEntry.restoreMPPercentage );
|
m_lutEntry.curePotency, m_lutEntry.restoreMPPercentage );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when aoe, these effects are in the target whatever is hit first
|
||||||
|
bool shouldRestoreMP = true;
|
||||||
|
bool shouldShowComboEffect = true;
|
||||||
|
|
||||||
for( auto& actor : m_hitActors )
|
for( auto& actor : m_hitActors )
|
||||||
{
|
{
|
||||||
if( m_lutEntry.potency > 0 )
|
if( m_lutEntry.potency > 0 )
|
||||||
|
@ -469,30 +473,28 @@ void Action::Action::buildEffects()
|
||||||
if( dmg.first > 0 )
|
if( dmg.first > 0 )
|
||||||
actor->onActionHostile( m_pSource );
|
actor->onActionHostile( m_pSource );
|
||||||
|
|
||||||
if( isCorrectCombo() )
|
if( isCorrectCombo() && shouldShowComboEffect )
|
||||||
{
|
{
|
||||||
m_effectBuilder->comboVisualEffect( actor );
|
m_effectBuilder->comboVisualEffect( actor );
|
||||||
|
shouldShowComboEffect = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !isComboAction() || isCorrectCombo() )
|
if( !isComboAction() || isCorrectCombo() )
|
||||||
{
|
{
|
||||||
if( m_lutEntry.curePotency > 0 ) // actions with self heal
|
if( m_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, m_lutEntry.curePotency );
|
m_effectBuilder->selfHeal( actor, m_pSource, m_lutEntry.curePotency );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_lutEntry.restoreMPPercentage > 0 )
|
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
|
||||||
{
|
{
|
||||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMp() * m_lutEntry.restoreMPPercentage / 100 );
|
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100 );
|
||||||
|
shouldRestoreMP = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !m_actionData->preservesCombo ) // we need something like m_actionData->hasNextComboAction
|
if( !m_actionData->preservesCombo ) // we need something like m_actionData->hasNextComboAction
|
||||||
{
|
{
|
||||||
m_effectBuilder->startCombo( actor, getId() );
|
m_effectBuilder->startCombo( actor, getId() ); // this is on all targets hit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,15 +503,17 @@ void Action::Action::buildEffects()
|
||||||
// todo: calcHealing()
|
// todo: calcHealing()
|
||||||
m_effectBuilder->healTarget( actor, m_lutEntry.curePotency );
|
m_effectBuilder->healTarget( actor, m_lutEntry.curePotency );
|
||||||
|
|
||||||
if( m_lutEntry.restoreMPPercentage > 0 )
|
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
|
||||||
{
|
{
|
||||||
// always restore caster mp I don't think there are any actions that can restore target MP post 5.0
|
// always restore caster mp I don't think there are any actions that can restore target MP post 5.0
|
||||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMp() * m_lutEntry.restoreMPPercentage / 100 );
|
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100 );
|
||||||
|
shouldRestoreMP = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( m_lutEntry.restoreMPPercentage > 0 )
|
else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
|
||||||
{
|
{
|
||||||
m_effectBuilder->restoreMP( m_pSource, m_pSource, m_pSource->getMp() * m_lutEntry.restoreMPPercentage / 100 );
|
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100 );
|
||||||
|
shouldRestoreMP = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,6 +688,9 @@ bool Action::Action::snapshotAffectedActors( std::vector< Entity::CharaPtr >& ac
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( actors.size() == 32 )
|
||||||
|
break; // cannot add more than 32 targets
|
||||||
}
|
}
|
||||||
|
|
||||||
if( auto player = m_pSource->getAsPlayer() )
|
if( auto player = m_pSource->getAsPlayer() )
|
||||||
|
|
|
@ -109,14 +109,113 @@ void EffectBuilder::comboVisualEffect( Entity::CharaPtr& target )
|
||||||
|
|
||||||
void EffectBuilder::buildAndSendPackets()
|
void EffectBuilder::buildAndSendPackets()
|
||||||
{
|
{
|
||||||
|
auto targetCount = m_resolvedEffects.size();
|
||||||
|
assert( targetCount <= 32 );
|
||||||
Logger::debug( "EffectBuilder result: " );
|
Logger::debug( "EffectBuilder result: " );
|
||||||
Logger::debug( "Targets afflicted: {}", m_resolvedEffects.size() );
|
Logger::debug( "Targets afflicted: {}", targetCount );
|
||||||
|
|
||||||
|
if( targetCount > 1 ) // use AoeEffect packets
|
||||||
|
{
|
||||||
|
int packetSize = targetCount <= 8 ? 8 :
|
||||||
|
( targetCount <= 16 ? 16 :
|
||||||
|
( targetCount <= 24 ? 24 : 32 ) );
|
||||||
|
|
||||||
|
using EffectHeader = Server::FFXIVIpcAoeEffect< 8 >; // dummy type to access header part of the packet
|
||||||
|
|
||||||
|
FFXIVPacketBasePtr pPacket = nullptr;
|
||||||
|
EffectHeader* pHeader;
|
||||||
|
Common::EffectEntry* pEntry;
|
||||||
|
uint64_t* pEffectTargetId;
|
||||||
|
uint16_t* pFlag;
|
||||||
|
switch( packetSize )
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
{
|
||||||
|
auto p = makeZonePacket< Server::FFXIVIpcAoeEffect8 >( m_sourceChara->getId() );
|
||||||
|
pHeader = ( EffectHeader* )( &( p->data() ) );
|
||||||
|
pEntry = ( Common::EffectEntry* )( &( p->data().effects ) );
|
||||||
|
pEffectTargetId = ( uint64_t* )( &( p->data().effectTargetId ) );
|
||||||
|
pFlag = ( uint16_t* )( &( p->data().unkFlag ) );
|
||||||
|
pPacket = std::move( p );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 16:
|
||||||
|
{
|
||||||
|
auto p = makeZonePacket< Server::FFXIVIpcAoeEffect16 >( m_sourceChara->getId() );
|
||||||
|
pHeader = ( EffectHeader* )( &( p->data() ) );
|
||||||
|
pEntry = ( Common::EffectEntry* )( &( p->data().effects ) );
|
||||||
|
pEffectTargetId = ( uint64_t* )( &( p->data().effectTargetId ) );
|
||||||
|
pFlag = ( uint16_t* )( &( p->data().unkFlag ) );
|
||||||
|
pPacket = std::move( p );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 24:
|
||||||
|
{
|
||||||
|
auto p = makeZonePacket< Server::FFXIVIpcAoeEffect24 >( m_sourceChara->getId() );
|
||||||
|
pHeader = ( EffectHeader* )( &( p->data() ) );
|
||||||
|
pEntry = ( Common::EffectEntry* )( &( p->data().effects ) );
|
||||||
|
pEffectTargetId = ( uint64_t* )( &( p->data().effectTargetId ) );
|
||||||
|
pFlag = ( uint16_t* )( &( p->data().unkFlag ) );
|
||||||
|
pPacket = std::move( p );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 32:
|
||||||
|
{
|
||||||
|
auto p = makeZonePacket< Server::FFXIVIpcAoeEffect32 >( m_sourceChara->getId() );
|
||||||
|
pHeader = ( EffectHeader* )( &( p->data() ) );
|
||||||
|
pEntry = ( Common::EffectEntry* )( &( p->data().effects ) );
|
||||||
|
pEffectTargetId = ( uint64_t* )( &( p->data().effectTargetId ) );
|
||||||
|
pFlag = ( uint16_t* )( &( p->data().unkFlag ) );
|
||||||
|
pPacket = std::move( p );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert( pPacket != nullptr );
|
||||||
|
|
||||||
|
pHeader->actionAnimationId = m_sourceChara->getId();
|
||||||
|
pHeader->actionId = m_actionId;
|
||||||
|
pHeader->actionAnimationId = static_cast< uint16_t >( m_actionId );
|
||||||
|
pHeader->animationTargetId = m_sourceChara->getId();
|
||||||
|
pHeader->someTargetId = 0xE0000000;
|
||||||
|
pHeader->rotation = Common::Util::floatToUInt16Rot( m_sourceChara->getRot() );
|
||||||
|
pHeader->effectDisplayType = Common::ActionEffectDisplayType::ShowActionName;
|
||||||
|
pHeader->effectCount = static_cast< uint8_t >( targetCount );
|
||||||
|
pHeader->hiddenAnimation = 1;
|
||||||
|
pHeader->globalEffectCounter = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
||||||
|
|
||||||
|
uint8_t targetIndex = 0;
|
||||||
for( auto it = m_resolvedEffects.begin(); it != m_resolvedEffects.end(); )
|
for( auto it = m_resolvedEffects.begin(); it != m_resolvedEffects.end(); )
|
||||||
{
|
{
|
||||||
auto resultList = it->second;
|
auto resultList = it->second;
|
||||||
assert( resultList->size() > 0 );
|
assert( resultList->size() > 0 );
|
||||||
auto firstResult = resultList->data()[ 0 ];
|
auto firstResult = resultList->data()[ 0 ];
|
||||||
|
pEffectTargetId[ targetIndex ] = firstResult->getTarget()->getId();
|
||||||
|
Logger::debug( " - id: {}", pEffectTargetId[ targetIndex ] );
|
||||||
|
|
||||||
|
for( auto i = 0; i < resultList->size(); i++ )
|
||||||
|
{
|
||||||
|
auto result = resultList->data()[ i ];
|
||||||
|
pEntry[ targetIndex * 8 + i ] = result->buildEffectEntry();
|
||||||
|
m_sourceChara->getCurrentTerritory()->addEffectResult( std::move( result ) );
|
||||||
|
}
|
||||||
|
resultList->clear();
|
||||||
|
|
||||||
|
it = m_resolvedEffects.erase( it );
|
||||||
|
|
||||||
|
targetIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pFlag[0] = 0x7FFF;
|
||||||
|
pFlag[1] = 0x7FFF;
|
||||||
|
pFlag[2] = 0x7FFF;
|
||||||
|
|
||||||
|
m_sourceChara->sendToInRangeSet( pPacket, true );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto resultList = m_resolvedEffects.begin()->second;
|
||||||
|
assert( resultList->size() > 0 );
|
||||||
|
auto firstResult = resultList->data()[ 0 ];
|
||||||
Logger::debug( " - id: {}", firstResult->getTarget()->getId() );
|
Logger::debug( " - id: {}", firstResult->getTarget()->getId() );
|
||||||
|
|
||||||
auto seq = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
auto seq = m_sourceChara->getCurrentTerritory()->getNextEffectSequence();
|
||||||
|
@ -129,7 +228,6 @@ void EffectBuilder::buildAndSendPackets()
|
||||||
{
|
{
|
||||||
auto result = resultList->data()[ i ];
|
auto result = resultList->data()[ i ];
|
||||||
effectPacket->addEffect( result->buildEffectEntry() );
|
effectPacket->addEffect( result->buildEffectEntry() );
|
||||||
// add effect to territory
|
|
||||||
m_sourceChara->getCurrentTerritory()->addEffectResult( std::move( result ) );
|
m_sourceChara->getCurrentTerritory()->addEffectResult( std::move( result ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +235,6 @@ void EffectBuilder::buildAndSendPackets()
|
||||||
|
|
||||||
m_sourceChara->sendToInRangeSet( effectPacket, true );
|
m_sourceChara->sendToInRangeSet( effectPacket, true );
|
||||||
|
|
||||||
it = m_resolvedEffects.erase( it );
|
m_resolvedEffects.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue