diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index d6883e17..0bea9fe4 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -1141,6 +1141,25 @@ void InventoryComponent::AddItemSkills(const LOT lot) { SetSkill(slot, skill); } +void InventoryComponent::FixInvisibleItems() { + const auto numberItemsLoadedPerFrame = 12.0f; + const auto callbackTime = 0.125f; + const auto arbitaryInventorySize = 300.0f; // max in live + dlu is less than 300, seems like a good number. + auto* const items = GetInventory(eInventoryType::ITEMS); + if (!items) return; + + // Add an extra update to make sure the client can see all the items. + const auto something = static_cast(std::ceil(items->GetItems().size() / arbitaryInventorySize)) + 1; + LOG_DEBUG("Fixing invisible items with %i updates", something); + + for (int32_t i = 1; i < something + 1; i++) { + // client loads 12 items every 1/8 seconds, we're adding a small hack to fix invisible inventory items due to closing the news screen too fast. + m_Parent->AddCallbackTimer((arbitaryInventorySize / numberItemsLoadedPerFrame) * callbackTime * i, [this]() { + GameMessages::SendUpdateInventoryUi(m_Parent->GetObjectID(), m_Parent->GetSystemAddress()); + }); + } +} + void InventoryComponent::RemoveItemSkills(const LOT lot) { const auto info = Inventory::FindItemComponent(lot); diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 28158ab5..0055fcea 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -404,6 +404,8 @@ public: void UpdateGroup(const GroupUpdate& groupUpdate); void RemoveGroup(const std::string& groupId); + void FixInvisibleItems(); + ~InventoryComponent() override; private: diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index f32d749f..baa3a84e 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -104,6 +104,18 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System break; } + // Currently not actually used for our implementation, however its used right now to get around invisible inventory items in the client. + case MessageType::Game::SELECT_SKILL: { + auto var = entity->GetVar(u"dlu_first_time_load"); + if (var) { + entity->SetVar(u"dlu_first_time_load", false); + InventoryComponent* inventoryComponent = entity->GetComponent(); + + if (inventoryComponent) inventoryComponent->FixInvisibleItems(); + } + break; + } + case MessageType::Game::PLAYER_LOADED: { GameMessages::SendRestoreToPostLoadStats(entity, sysAddr); entity->SetPlayerReadyForUpdates(); diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 8e94cee3..b42e7d01 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -982,7 +982,7 @@ void GameMessages::SendResurrect(Entity* entity) { destroyableComponent->SetImagination(imaginationToRestore); } } - }); + }); CBITSTREAM; CMSGHEADER; @@ -5080,6 +5080,12 @@ void GameMessages::HandleSetFlag(RakNet::BitStream& inStream, Entity* entity) { auto character = entity->GetCharacter(); if (character) character->SetPlayerFlag(iFlagID, bFlag); + + // This is always set the first time a player loads into a world from character select + // and is used to know when to refresh the players inventory items so they show up. + if (iFlagID == ePlayerFlag::IS_NEWS_SCREEN_VISIBLE && bFlag) { + entity->SetVar(u"dlu_first_time_load", true); + } } void GameMessages::HandleRespondToMission(RakNet::BitStream& inStream, Entity* entity) { @@ -5147,12 +5153,12 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream& inStream, Entity* en } if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" - || !player->GetCharacter() - || !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; + || !player->GetCharacter() + || !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; player->AddCallbackTimer(0.5f, [player]() { if (!player) return; GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress()); - }); + }); } void GameMessages::HandleRequestLinkedMission(RakNet::BitStream& inStream, Entity* entity) { @@ -6324,3 +6330,14 @@ void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling auto sysAddr = entity->GetSystemAddress(); SEND_PACKET; } + + +void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(MessageType::Game::UPDATE_INVENTORY_UI); + + SEND_PACKET; +} diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 090fcd4b..1dbe5d81 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -677,6 +677,9 @@ namespace GameMessages { void HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID); + + // This is a client gm however its default values are exactly what we need to get around the invisible inventory item issues. + void SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& sysAddr); }; #endif // GAMEMESSAGES_H