diff --git a/src/common/Common.h b/src/common/Common.h index 1d7c5fe2..efb6335b 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -1229,6 +1229,16 @@ namespace Sapphire::Common } whm; }; + enum class LootMessageType : uint8_t + { + GetItem1 = 1, // p1: actorId, p4: itemId (HQ: itemId + 1,000,000 lol), p5: amount + GetItem2 = 3, // p1: actorId, p2: itemId, p3: amount, seems like same thing as GetItem1 but different param position. + FailedToGetLootNoFreeInventorySlot = 5, // p1: actorId + LootRolled = 7, // p1: actorId, p2: itemId, p3: amount + GetGil = 9, // p1: gil + EmptyCoffer = 11, // seems like no param + }; + using PlayerStateFlagList = std::vector< PlayerStateFlag >; } diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 286cb736..8e1a83ff 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -376,8 +376,8 @@ namespace Sapphire::Network::Packets PerformNoteHandler = 0x029B, // updated 4.3 - ShopMessage = 0x00C1, // updated 5.25 + LootMessage = 0x00B1, // updated 5.25 }; //////////////////////////////////////////////////////////////////////////////// diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index d1c8edbd..08d485cc 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -2018,6 +2018,19 @@ namespace Sapphire::Network::Packets::Server uint32_t unknown7; }; + struct FFXIVIpcLootMessage : FFXIVIpcBasePacket< LootMessage > + { + Common::LootMessageType msgType; + uint8_t padding[3]; + uint32_t param1; + uint32_t param2; + uint32_t param3; + uint32_t param4; + uint32_t param5; + uint32_t param6; + uint32_t param7; + }; + } #endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/ diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 6a26da75..926c995c 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -360,7 +360,7 @@ namespace Sapphire::Entity uint32_t getModelForSlot( Common::GearModelSlot slot ); /*! add amount to the currency of type */ - void addCurrency( Common::CurrencyType type, uint32_t amount ); + void addCurrency( Common::CurrencyType type, uint32_t amount, bool sendLootMessage = false ); /*! remove amount from the currency of type */ void removeCurrency( Common::CurrencyType type, uint32_t amount ); @@ -928,8 +928,8 @@ namespace Sapphire::Entity InvSlotPair getFreeBagSlot(); - ItemPtr addItem( uint32_t catalogId, uint32_t quantity = 1, bool isHq = false, bool slient = false, bool canMerge = true ); - ItemPtr addItem( ItemPtr itemToAdd, bool slient = false, bool canMerge = true ); + ItemPtr addItem( uint32_t catalogId, uint32_t quantity = 1, bool isHq = false, bool silent = false, bool canMerge = true, bool sendLootMessage = false ); + ItemPtr addItem( ItemPtr itemToAdd, bool silent = false, bool canMerge = true, bool sendLootMessage = false ); void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); @@ -964,7 +964,7 @@ namespace Sapphire::Entity uint32_t getCrystal( Common::CrystalType type ); /*! add amount to the crystal of type */ - void addCrystal( Common::CrystalType type, uint32_t amount ); + void addCrystal( Common::CrystalType type, uint32_t amount, bool sendLootMessage = false ); /*! remove amount from the crystals of type */ void removeCrystal( Common::CrystalType type, uint32_t amount ); @@ -980,7 +980,7 @@ namespace Sapphire::Entity void setActiveLand( uint8_t land, uint8_t ward ); Common::ActiveLand getActiveLand() const; - Sapphire::ItemPtr dropInventoryItem( Common::InventoryType type, uint16_t slotId, bool slient = false ); + Sapphire::ItemPtr dropInventoryItem( Common::InventoryType type, uint16_t slotId, bool silent = false ); // Job UI ////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index 06ea2cfb..ff316229 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -298,7 +298,7 @@ void Sapphire::Entity::Player::unequipSoulCrystal( ItemPtr pItem ) } // TODO: these next functions are so similar that they could likely be simplified -void Sapphire::Entity::Player::addCurrency( CurrencyType type, uint32_t amount ) +void Sapphire::Entity::Player::addCurrency( CurrencyType type, uint32_t amount, bool sendLootMessage ) { auto slot = static_cast< uint8_t >( static_cast< uint8_t >( type ) - 1 ); auto currItem = m_storageMap[ Currency ]->getItem( slot ); @@ -321,6 +321,22 @@ void Sapphire::Entity::Player::addCurrency( CurrencyType type, uint32_t amount ) Common::InventoryType::Currency, *currItem ); queuePacket( invUpdate ); + + + if( sendLootMessage ) + { + switch( type ) + { + case CurrencyType::Gil: + { + auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() ); + lootMsg->data().msgType = Common::LootMessageType::GetGil; + lootMsg->data().param1 = amount; + queuePacket( lootMsg ); + break; + } + } + } } void Sapphire::Entity::Player::removeCurrency( Common::CurrencyType type, uint32_t amount ) @@ -346,7 +362,7 @@ void Sapphire::Entity::Player::removeCurrency( Common::CurrencyType type, uint32 } -void Sapphire::Entity::Player::addCrystal( Common::CrystalType type, uint32_t amount ) +void Sapphire::Entity::Player::addCrystal( Common::CrystalType type, uint32_t amount, bool sendLootMessage ) { auto currItem = m_storageMap[ Crystal ]->getItem( static_cast< uint8_t >( type ) - 1 ); @@ -371,7 +387,21 @@ void Sapphire::Entity::Player::addCrystal( Common::CrystalType type, uint32_t am Common::InventoryType::Crystal, *currItem ); queuePacket( invUpdate ); - queuePacket( makeActorControlSelf( getId(), ItemObtainIcon, static_cast< uint8_t >( type ) + 1, amount ) ); + + if( sendLootMessage ) + { + auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() ); + lootMsg->data().msgType = Common::LootMessageType::GetItem2; + lootMsg->data().param1 = getId(); + lootMsg->data().param2 = currItem->getId(); + lootMsg->data().param3 = amount; + queuePacket( lootMsg ); + } + + auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() ); + soundEffectPacket->data().sequence = 0xFFFFFFFF; + soundEffectPacket->data().type = 6; + queuePacket( soundEffectPacket ); } void Sapphire::Entity::Player::removeCrystal( Common::CrystalType type, uint32_t amount ) @@ -524,7 +554,7 @@ bool Sapphire::Entity::Player::isObtainable( uint32_t catalogId, uint8_t quantit return true; } -Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool silent, bool canMerge ) +Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool silent, bool canMerge, bool sendLootMessage ) { auto& exdData = Common::Service< Data::ExdDataGenerated >::ref(); auto itemInfo = exdData.get< Sapphire::Data::Item >( itemToAdd->getId() ); @@ -591,13 +621,24 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil item->setStackSize( newStackSize ); writeItem( item ); - auto slotUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), slot, bag, *item ); - queuePacket( slotUpdate ); + if( !silent ) + { + auto slotUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), slot, bag, *item ); + queuePacket( slotUpdate ); + } // return existing stack if we have no overflow - items fit into a preexisting stack if( itemToAdd->getStackSize() == 0 ) { - queuePacket( makeActorControlSelf( getId(), ItemObtainIcon, itemToAdd->getId(), originalQuantity ) ); + if( sendLootMessage ) + { + auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() ); + lootMsg->data().msgType = Common::LootMessageType::GetItem2; + lootMsg->data().param1 = getId(); + lootMsg->data().param2 = itemToAdd->isHq() ? itemToAdd->getId() + 1000000 : itemToAdd->getId(); + lootMsg->data().param3 = originalQuantity; + queuePacket( lootMsg ); + } auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() ); soundEffectPacket->data().sequence = 0xFFFFFFFF; @@ -629,24 +670,32 @@ Sapphire::ItemPtr Sapphire::Entity::Player::addItem( ItemPtr itemToAdd, bool sil { auto invUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), freeBagSlot.second, freeBagSlot.first, *itemToAdd ); queuePacket( invUpdate ); - - queuePacket( makeActorControlSelf( getId(), ItemObtainIcon, itemToAdd->getId(), originalQuantity ) ); - - auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() ); - soundEffectPacket->data().sequence = 0xFFFFFFFF; - soundEffectPacket->data().type = 6; - queuePacket( soundEffectPacket ); } + if( sendLootMessage ) + { + auto lootMsg = makeZonePacket< FFXIVIpcLootMessage >( getId() ); + lootMsg->data().msgType = Common::LootMessageType::GetItem2; + lootMsg->data().param1 = getId(); + lootMsg->data().param2 = itemToAdd->isHq() ? itemToAdd->getId() + 1000000 : itemToAdd->getId(); + lootMsg->data().param3 = originalQuantity; + queuePacket( lootMsg ); + } + + auto soundEffectPacket = makeZonePacket< FFXIVIpcInventoryActionAck >( getId() ); + soundEffectPacket->data().sequence = 0xFFFFFFFF; + soundEffectPacket->data().type = 6; + queuePacket( soundEffectPacket ); + return itemToAdd; } -Sapphire::ItemPtr Sapphire::Entity::Player::addItem( uint32_t catalogId, uint32_t quantity, bool isHq, bool silent, bool canMerge ) +Sapphire::ItemPtr Sapphire::Entity::Player::addItem( uint32_t catalogId, uint32_t quantity, bool isHq, bool silent, bool canMerge, bool sendLootMessage ) { auto item = createItem( catalogId, quantity ); item->setHq( isHq ); - return addItem( item, silent, canMerge ); + return addItem( item, silent, canMerge, sendLootMessage ); } void @@ -929,7 +978,7 @@ uint32_t Sapphire::Entity::Player::getNextInventorySequence() return m_inventorySequence++; } -Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common::InventoryType type, uint16_t slotId, bool slient ) +Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common::InventoryType type, uint16_t slotId, bool silent ) { auto& container = m_storageMap[ type ]; @@ -941,7 +990,7 @@ Sapphire::ItemPtr Sapphire::Entity::Player::dropInventoryItem( Sapphire::Common: container->removeItem( slotId, false ); updateContainer( type, slotId, nullptr ); - if( !slient ) + if( !silent ) { auto invUpdate = std::make_shared< UpdateInventorySlotPacket >( getId(), slotId, static_cast< uint16_t >( type ) ); queuePacket( invUpdate ); diff --git a/src/world/Actor/PlayerQuest.cpp b/src/world/Actor/PlayerQuest.cpp index 854cdc13..f3b29c16 100644 --- a/src/world/Actor/PlayerQuest.cpp +++ b/src/world/Actor/PlayerQuest.cpp @@ -1065,7 +1065,7 @@ bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t opti { for( uint32_t i = 0; i < rewardItemCount; i++ ) { - addItem( questInfo->itemReward0.at( i ), questInfo->itemCountReward0.at( i ) ); + addItem( questInfo->itemReward0.at( i ), questInfo->itemCountReward0.at( i ), false, false, true, true ); } } @@ -1076,14 +1076,14 @@ bool Sapphire::Entity::Player::giveQuestRewards( uint32_t questId, uint32_t opti auto itemId = questInfo->itemReward1.at( i ); if( itemId == optionalChoice ) { - addItem( itemId, questInfo->itemCountReward1.at( i ) ); + addItem( itemId, questInfo->itemCountReward1.at( i ), false, false, true, true ); break; } } } if( gilReward > 0 ) - addCurrency( CurrencyType::Gil, gilReward ); + addCurrency( CurrencyType::Gil, gilReward, true ); return true; } diff --git a/src/world/Network/Handlers/GMCommandHandlers.cpp b/src/world/Network/Handlers/GMCommandHandlers.cpp index 175c2ab9..2b5e5965 100644 --- a/src/world/Network/Handlers/GMCommandHandlers.cpp +++ b/src/world/Network/Handlers/GMCommandHandlers.cpp @@ -345,14 +345,23 @@ void Sapphire::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACK player.sendUrgent( "Syntaxerror." ); return; } + if( param1 <= 0x12 ) // crystal + { + targetPlayer->addCrystal( static_cast< Common::CrystalType >( param1 ), quantity, true ); + } + else // item + { + // decode using the epic SE style HQ item id + bool isHq = param1 > 1000000; - if( !targetPlayer->addItem( param1, quantity ) ) - player.sendUrgent( "Item #{0} could not be added to inventory.", param1 ); - break; + if( !targetPlayer->addItem( isHq ? param1 - 1000000 : param1, quantity, isHq, false, true, true ) ) + player.sendUrgent( "Item #{0} could not be added to inventory.", isHq ? param1 - 1000000 : param1 ); + break; + } } case GmCommand::Gil: { - targetPlayer->addCurrency( CurrencyType::Gil, param1 ); + targetPlayer->addCurrency( CurrencyType::Gil, param1, true ); player.sendNotice( "Added {0} Gil for {1}", param1, targetPlayer->getName() ); break; }