diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 0c3f8cb1..0b93fdaf 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -395,6 +395,7 @@ namespace Sapphire::Network::Packets PerformNoteHandler = 0x029B, // updated 4.3 WorldInteractionHandler = 0x00A9, // updated 5.35 hotfix + Dive = 0x02CC, // updated 5.35 hotfix }; //////////////////////////////////////////////////////////////////////////////// diff --git a/src/common/Network/PacketDef/Zone/ClientZoneDef.h b/src/common/Network/PacketDef/Zone/ClientZoneDef.h index bfadc021..19b4fff5 100644 --- a/src/common/Network/PacketDef/Zone/ClientZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ClientZoneDef.h @@ -417,6 +417,15 @@ struct FFXIVIpcDisbandPartyHandler : }; +struct FFXIVIpcDive : + FFXIVIpcBasePacket< Dive > +{ + float unknown; + Common::FFXIVARR_POSITION3 posTarget; + Common::FFXIVARR_POSITION3 posOriginal; + uint32_t padding; +}; + } #endif //_CORE_NETWORK_PACKETS_ZONE_CLIENT_IPC_H diff --git a/src/world/Network/GameConnection.cpp b/src/world/Network/GameConnection.cpp index f8603f17..82b401ee 100644 --- a/src/world/Network/GameConnection.cpp +++ b/src/world/Network/GameConnection.cpp @@ -132,6 +132,7 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH &GameConnection::marketBoardRequestItemListings ); setZoneHandler( ClientZoneIpcType::WorldInteractionHandler, "WorldInteractionHandler", &GameConnection::worldInteractionhandler ); + setZoneHandler( ClientZoneIpcType::Dive, "Dive", &GameConnection::diveHandler ); setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler ); diff --git a/src/world/Network/GameConnection.h b/src/world/Network/GameConnection.h index 8f343dbc..8a3aabc0 100644 --- a/src/world/Network/GameConnection.h +++ b/src/world/Network/GameConnection.h @@ -189,6 +189,7 @@ namespace Sapphire::Network DECLARE_HANDLER( worldInteractionhandler ); + DECLARE_HANDLER( diveHandler ); }; } diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index ed917a69..af472089 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -731,65 +731,158 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets:: const auto packet = ZoneChannelPacket< Client::FFXIVIpcWorldInteractionHandler >( inPacket ); auto action = packet.data().action; player.sendDebug( "WorldInteraction {}", action ); - if( action == 0x1F5 ) + switch( action ) { - auto emote = packet.data().param1; - if( emote == 0x32 || emote == 0x33 ) // "/sit" + case 0x1F5: // emote { - auto param4 = packet.data().param4; - auto& exdData = Common::Service< Data::ExdDataGenerated >::ref(); - auto emoteData = exdData.get< Data::Emote >( emote ); - - if( !emoteData ) - return; - - player.setPos( packet.data().position ); - if( emote == 0x32 && player.hasInRangeActor() ) + auto emote = packet.data().param1; + if( emote == 0x32 || emote == 0x33 ) // "/sit" { - auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); - setpos->data().r16 = param4; - setpos->data().waitForLoad = 18; - setpos->data().unknown1 = 1; - setpos->data().x = packet.data().position.x; - setpos->data().y = packet.data().position.y; - setpos->data().z = packet.data().position.z; - player.sendToInRangeSet( setpos, false ); - } - player.sendToInRangeSet( makeActorControlTarget( player.getId(), ActorControl::ActorControlType::Emote, emote, 0, 0, param4, 0xE0000000 ), true ); + auto param4 = packet.data().param4; + auto& exdData = Common::Service< Data::ExdDataGenerated >::ref(); + auto emoteData = exdData.get< Data::Emote >( emote ); - if( emote == 0x32 && emoteData->emoteMode != 0 ) - { - player.setStance( Common::Stance::Passive ); - player.setAutoattack( false ); - player.setPersistentEmote( emoteData->emoteMode ); - player.setStatus( Common::ActorStatus::EmoteMode ); + if( !emoteData ) + break; + + player.setPos( packet.data().position ); + if( emote == 0x32 && player.hasInRangeActor() ) + { + auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); + setpos->data().r16 = param4; + setpos->data().waitForLoad = 18; + setpos->data().unknown1 = 1; + setpos->data().x = packet.data().position.x; + setpos->data().y = packet.data().position.y; + setpos->data().z = packet.data().position.z; + player.sendToInRangeSet( setpos, false ); + } + player.sendToInRangeSet( makeActorControlTarget( player.getId(), ActorControl::ActorControlType::Emote, emote, 0, 0, param4, 0xE0000000 ), true ); + + if( emote == 0x32 && emoteData->emoteMode != 0 ) + { + player.setStance( Common::Stance::Passive ); + player.setAutoattack( false ); + player.setPersistentEmote( emoteData->emoteMode ); + player.setStatus( Common::ActorStatus::EmoteMode ); + } } + break; + } + case 0x1F8: + { + if( player.getPersistentEmote() > 0 ) + { + auto param2 = packet.data().param2; + + player.setPos( packet.data().position ); + if( player.hasInRangeActor() ) + { + auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); + setpos->data().r16 = param2; + setpos->data().waitForLoad = 18; + setpos->data().unknown1 = 2; + setpos->data().x = packet.data().position.x; + setpos->data().y = packet.data().position.y; + setpos->data().z = packet.data().position.z; + player.sendToInRangeSet( setpos, false ); + } + + player.setPersistentEmote( 0 ); + player.emoteInterrupt(); + player.setStatus( Common::ActorStatus::Idle ); + auto pSetStatusPacket = makeActorControl( player.getId(), SetStatus, static_cast< uint8_t >( Common::ActorStatus::Idle ) ); + player.sendToInRangeSet( pSetStatusPacket ); + } + break; + } + case 0x25E: // coming out from water + case 0xD1: // underwater town portal + { + auto p = makeZonePacket< FFXIVIpcPrepareZoning >( player.getId() ); + p->data().targetZone = player.getCurrentTerritory()->getTerritoryTypeId(); + p->data().param4 = action == 0xD1 ? 14 : 227; + p->data().hideChar = action == 0xD1 ? 2 : 1; + p->data().fadeOut = action == 0xD1 ? 24 : 25; + p->data().fadeOutTime = 1; + p->data().unknown = action == 0xD1 ? 4 : 6; + auto x = packet.data().position.x; + auto y = packet.data().position.y; + auto z = packet.data().position.z; + auto rot = player.getRot(); + if( action == 0xD1 ) + { + auto exitRange = packet.data().param1; + + auto& instanceObjectCache = Common::Service< InstanceObjectCache >::ref(); + auto exit = instanceObjectCache.getExitRange( p->data().targetZone, exitRange ); + if( exit ) + { + player.sendDebug( "exitRange {0} found!", exitRange ); + auto destZone = exit->data.destTerritoryType; + if( destZone == 0 ) + destZone = p->data().targetZone; + else + p->data().targetZone = destZone; + auto pop = instanceObjectCache.getPopRange( destZone, exit->data.destInstanceObjectId ); + if( pop ) + { + player.sendDebug( "popRange {0} found!", exit->data.destInstanceObjectId ); + x = pop->header.transform.translation.x; + y = pop->header.transform.translation.y; + z = pop->header.transform.translation.z; + //rot = pop->header.transform.rotation.y; all x/y/z not correct, maybe we don't need it since we have to be facing the portal anyway? + } + else + { + player.sendUrgent( "popRange {0} not found in {1}!", exit->data.destInstanceObjectId, destZone ); + } + } + else + { + player.sendUrgent( "exitRange {0} not found in {1}!", exitRange, p->data().targetZone ); + } + } + player.queuePacket( p ); + player.setPos( x, y, z, true ); + player.setRot( rot ); + auto setPos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); + setPos->data().r16 = Common::Util::floatToUInt16Rot( player.getRot() ); + setPos->data().x = x; + setPos->data().y = y; + setPos->data().z = z; + setPos->data().waitForLoad = action == 0xD1 ? 24 : 25; + setPos->data().unknown1 = 0; + player.queuePacket( setPos ); // this packet needs a delay of 0.8 second to wait for the client finishing its water animation otherwise it looks odd. + break; } } - else if( action == 0x1F8 ) - { - if( player.getPersistentEmote() > 0 ) - { - auto param2 = packet.data().param2; +} - player.setPos( packet.data().position ); - if( player.hasInRangeActor() ) - { - auto setpos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); - setpos->data().r16 = param2; - setpos->data().waitForLoad = 18; - setpos->data().unknown1 = 2; - setpos->data().x = packet.data().position.x; - setpos->data().y = packet.data().position.y; - setpos->data().z = packet.data().position.z; - player.sendToInRangeSet( setpos, false ); - } +void Sapphire::Network::GameConnection::diveHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player ) +{ + const auto packetIn = ZoneChannelPacket< Client::FFXIVIpcDive >( inPacket ); + auto p = makeZonePacket< FFXIVIpcPrepareZoning >( player.getId() ); + p->data().targetZone = player.getCurrentTerritory()->getTerritoryTypeId(); + p->data().param4 = 218; + p->data().hideChar = 1; + p->data().fadeOut = 25; + p->data().fadeOutTime = 1; + p->data().unknown = 6; + player.queuePacket( p ); - player.setPersistentEmote( 0 ); - player.emoteInterrupt(); - player.setStatus( Common::ActorStatus::Idle ); - auto pSetStatusPacket = makeActorControl( player.getId(), SetStatus, static_cast< uint8_t >( Common::ActorStatus::Idle ) ); - player.sendToInRangeSet( pSetStatusPacket ); - } - } + player.setStance( Common::Stance::Passive ); + + auto x = packetIn.data().posTarget.x; + auto y = packetIn.data().posTarget.y; + auto z = packetIn.data().posTarget.z; + player.setPos( x, y, z, true ); + auto setPos = makeZonePacket< FFXIVIpcActorSetPos >( player.getId() ); + setPos->data().r16 = Common::Util::floatToUInt16Rot( player.getRot() ); + setPos->data().x = x; + setPos->data().y = y; + setPos->data().z = z; + setPos->data().waitForLoad = 25; + setPos->data().unknown1 = 0; + player.queuePacket( setPos ); // need delay, same as above. } \ No newline at end of file