From 84a868aae8ec3ee9ddcf9beb7421f551127a78cd Mon Sep 17 00:00:00 2001 From: collett Date: Sun, 5 Jan 2020 01:04:30 +0900 Subject: [PATCH] More aoe stuff. --- .../Network/PacketDef/Zone/ServerZoneDef.h | 59 ++++------ src/world/Action/Action.cpp | 31 +++-- src/world/Action/EffectBuilder.cpp | 108 +++++++++++++++++- 3 files changed, 147 insertions(+), 51 deletions(-) diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index caf1c48a..47b90a82 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -537,33 +537,6 @@ namespace Sapphire::Network::Packets::Server /* 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 > { uint64_t animationTargetId; // who the animation targets @@ -608,17 +581,35 @@ namespace Sapphire::Network::Packets::Server template< int size > 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]; - uint32_t effectTargetId[size]; - Common::FFXIVARR_POSITION3 position; - uint32_t effectFlags; - - uint32_t padding_78; + uint64_t effectTargetId[size]; + uint16_t unkFlag[3]; // all 0x7FFF + uint16_t unk[3]; }; struct FFXIVIpcAoeEffect8 : diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index a5e15b88..cd4b9b2b 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -459,6 +459,10 @@ void Action::Action::buildEffects() 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 ) { if( m_lutEntry.potency > 0 ) @@ -469,30 +473,28 @@ void Action::Action::buildEffects() if( dmg.first > 0 ) actor->onActionHostile( m_pSource ); - if( isCorrectCombo() ) + if( isCorrectCombo() && shouldShowComboEffect ) { m_effectBuilder->comboVisualEffect( actor ); + shouldShowComboEffect = false; } if( !isComboAction() || isCorrectCombo() ) { 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 ); } - 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 { - 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() 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 - 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; } } + + if ( actors.size() == 32 ) + break; // cannot add more than 32 targets } if( auto player = m_pSource->getAsPlayer() ) diff --git a/src/world/Action/EffectBuilder.cpp b/src/world/Action/EffectBuilder.cpp index 41881071..f7ad86c8 100644 --- a/src/world/Action/EffectBuilder.cpp +++ b/src/world/Action/EffectBuilder.cpp @@ -109,12 +109,111 @@ void EffectBuilder::comboVisualEffect( Entity::CharaPtr& target ) void EffectBuilder::buildAndSendPackets() { + auto targetCount = m_resolvedEffects.size(); + assert( targetCount <= 32 ); Logger::debug( "EffectBuilder result: " ); - Logger::debug( "Targets afflicted: {}", m_resolvedEffects.size() ); + Logger::debug( "Targets afflicted: {}", targetCount ); - for( auto it = m_resolvedEffects.begin(); it != m_resolvedEffects.end(); ) + if( targetCount > 1 ) // use AoeEffect packets { - auto resultList = it->second; + 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(); ) + { + auto resultList = it->second; + assert( resultList->size() > 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() ); @@ -129,7 +228,6 @@ void EffectBuilder::buildAndSendPackets() { auto result = resultList->data()[ i ]; effectPacket->addEffect( result->buildEffectEntry() ); - // add effect to territory m_sourceChara->getCurrentTerritory()->addEffectResult( std::move( result ) ); } @@ -137,6 +235,6 @@ void EffectBuilder::buildAndSendPackets() m_sourceChara->sendToInRangeSet( effectPacket, true ); - it = m_resolvedEffects.erase( it ); + m_resolvedEffects.clear(); } } \ No newline at end of file