From ec207838d431ed10a1b71d9cbf43a1b59835aaa5 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sun, 8 May 2022 19:57:36 -0500 Subject: [PATCH] Proper Rocket Holding Sanity checks on Prop and LUP launchpads to not open if no valid rocket Add serialization for sending item configs so that rockets show for other players --- dGame/dComponents/CharacterComponent.cpp | 51 +++++++++ dGame/dComponents/CharacterComponent.h | 21 ++++ dGame/dComponents/InventoryComponent.cpp | 29 ++++- .../dComponents/PropertyEntranceComponent.cpp | 22 ++-- .../dComponents/RocketLaunchLupComponent.cpp | 19 +--- .../RocketLaunchpadControlComponent.cpp | 103 ++++-------------- .../RocketLaunchpadControlComponent.h | 10 +- dGame/dGameMessages/GameMessages.cpp | 12 +- dGame/dInventory/EquippedItem.h | 8 +- dWorldServer/WorldServer.cpp | 13 ++- 10 files changed, 156 insertions(+), 132 deletions(-) diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 815a3d59..f7941af8 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -13,6 +13,7 @@ #include "PossessorComponent.h" #include "VehiclePhysicsComponent.h" #include "GameMessages.h" +#include "Item.h" CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) { m_Character = character; @@ -448,6 +449,56 @@ void CharacterComponent::SetLastRocketConfig(std::u16string config) { m_LastRocketConfig = config; } +Item* CharacterComponent::GetRocket(Entity* player) { + Item* rocket = nullptr; + + auto* inventoryComponent = player->GetComponent(); + + if (!inventoryComponent) return rocket; + + // Select the rocket + if (!rocket){ + rocket = inventoryComponent->FindItemById(GetLastRocketItemID()); + } + + if (!rocket) { + rocket = inventoryComponent->FindItemByLot(6416); + } + + if (!rocket) { + Game::logger->Log("CharacterComponent", "Unable to find rocket to equip!\n"); + return rocket; + } + return rocket; +} + +Item* CharacterComponent::RocketEquip(Entity* player) { + Item* rocket = GetRocket(player); + if (!rocket) return rocket; + + // build and define the rocket config + for (LDFBaseData* data : rocket->GetConfig()) { + if (data->GetKey() == u"assemblyPartLOTs") { + std::string newRocketStr = data->GetValueAsString() + ";"; + GeneralUtils::ReplaceInString(newRocketStr, "+", ";"); + SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr)); + } + } + + // Store the last used rocket item's ID + SetLastRocketItemID(rocket->GetId()); + // carry the rocket + rocket->Equip(true); + return rocket; +} + +void CharacterComponent::RocketUnEquip(Entity* player) { + Item* rocket = GetRocket(player); + if (!rocket) return; + // We don't want to carry it anymore + rocket->UnEquip(); +} + void CharacterComponent::TrackMissionCompletion(bool isAchievement) { UpdatePlayerStatistic(MissionsCompleted); diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 04245bf9..99c8a098 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -5,6 +5,7 @@ #include "RakNetTypes.h" #include "Character.h" #include "Component.h" +#include "Item.h" #include #include "CDMissionsTable.h" #include "tinyxml2.h" @@ -74,6 +75,26 @@ public: */ void SetLastRocketConfig(std::u16string config); + /** + * Find a player's rocket + * @param player the entity that triggered the event + * @return rocket + */ + Item* GetRocket(Entity* player); + + /** + * Equip a player's rocket + * @param player the entity that triggered the event + * @return rocket + */ + Item* RocketEquip(Entity* player); + + /** + * Find a player's rocket and unequip it + * @param player the entity that triggered the event + */ + void RocketUnEquip(Entity* player); + /** * Gets the current level of the entity * @return the current level of the entity diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index d2292168..7235d2db 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -1,4 +1,4 @@ -#include "InventoryComponent.h" +#include "InventoryComponent.h" #include @@ -794,10 +794,29 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b outBitStream->Write(item.slot != 0); if (item.slot != 0) outBitStream->Write(item.slot); - + outBitStream->Write0(); - - outBitStream->Write0(); //TODO: This is supposed to be true and write the assemblyPartLOTs when they're present. + + bool flag = !item.config.empty(); + outBitStream->Write(flag); + if (flag) { + RakNet::BitStream ldfStream; + ldfStream.Write(item.config.size()); // Key count + for (LDFBaseData* data : item.config) { + if (data->GetKey() == u"assemblyPartLOTs") { + std::string newRocketStr = data->GetValueAsString() + ";"; + GeneralUtils::ReplaceInString(newRocketStr, "+", ";"); + LDFData* ldf_data = new LDFData(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(newRocketStr)); + ldf_data->WriteToPacket(&ldfStream); + delete ldf_data; + } else { + data->WriteToPacket(&ldfStream); + } + } + outBitStream->Write(ldfStream.GetNumberOfBytesUsed() + 1); + outBitStream->Write(0); // Don't compress + outBitStream->Write(ldfStream); + } outBitStream->Write1(); } @@ -1043,7 +1062,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) GenerateProxies(item); - UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot() }); + UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot(), item->GetConfig() }); ApplyBuff(item); diff --git a/dGame/dComponents/PropertyEntranceComponent.cpp b/dGame/dComponents/PropertyEntranceComponent.cpp index 728cdc9e..6779d1c3 100644 --- a/dGame/dComponents/PropertyEntranceComponent.cpp +++ b/dGame/dComponents/PropertyEntranceComponent.cpp @@ -1,4 +1,4 @@ -#include "PropertyEntranceComponent.h" +#include "PropertyEntranceComponent.h" #include @@ -8,6 +8,7 @@ #include "PropertyManagementComponent.h" #include "PropertySelectQueryProperty.h" #include "RocketLaunchpadControlComponent.h" +#include "CharacterComponent.h" #include "UserManager.h" #include "dLogger.h" @@ -22,20 +23,25 @@ PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entit this->m_PropertyName = entry.propertyName; } -void PropertyEntranceComponent::OnUse(Entity* entity) -{ - GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress()); +void PropertyEntranceComponent::OnUse(Entity* entity) { + auto* characterComponent = entity->GetComponent(); + if (!characterComponent) return; - AMFArrayValue args; + auto* rocket = entity->GetComponent()->RocketEquip(entity); + if (!rocket) return; + + GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress()); + + AMFArrayValue args; auto* state = new AMFStringValue(); state->SetStringValue("property_menu"); args.InsertValue("state", state); - GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args); + GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args); - delete state; + delete state; } void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr) @@ -75,7 +81,7 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId); - launcher->Launch(entity, LWOOBJID_EMPTY, launcher->GetTargetZone(), cloneId); + launcher->Launch(entity, launcher->GetTargetZone(), cloneId); } PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) { diff --git a/dGame/dComponents/RocketLaunchLupComponent.cpp b/dGame/dComponents/RocketLaunchLupComponent.cpp index a2caa0bb..fa2a5b16 100644 --- a/dGame/dComponents/RocketLaunchLupComponent.cpp +++ b/dGame/dComponents/RocketLaunchLupComponent.cpp @@ -1,8 +1,8 @@ #include "RocketLaunchLupComponent.h" #include "CDClientDatabase.h" #include "RocketLaunchpadControlComponent.h" +#include "InventoryComponent.h" #include "CharacterComponent.h" -#include "Item.h" RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) { m_Parent = parent; @@ -21,20 +21,11 @@ RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(p RocketLaunchLupComponent::~RocketLaunchLupComponent() {} void RocketLaunchLupComponent::OnUse(Entity* originator) { - // the LUP world menu is just the property menu, the client knows how to handle it - GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress()); - - // get the rocket to "equip" it so we are holding it - // taken from the RocketLaunchControlComponent - auto* inventoryComponent = originator->GetComponent(); - auto* characterComponent = originator->GetComponent(); - - if (!inventoryComponent || !characterComponent) return; - - Item* rocket = inventoryComponent->FindItemById(characterComponent->GetLastRocketItemID()); + auto* rocket = originator->GetComponent()->RocketEquip(originator); if (!rocket) return; - rocket->Equip(true); + // the LUP world menu is just the property menu, the client knows how to handle it + GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress()); } void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) { @@ -45,5 +36,5 @@ void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) if (!rocketLaunchpadControlComponent) return; - rocketLaunchpadControlComponent->Launch(originator, LWOOBJID_EMPTY, m_LUPWorlds[index], 0); + rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0); } diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.cpp b/dGame/dComponents/RocketLaunchpadControlComponent.cpp index 049a44b6..8585815f 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.cpp +++ b/dGame/dComponents/RocketLaunchpadControlComponent.cpp @@ -13,6 +13,7 @@ #include "ChatPackets.h" #include "MissionComponent.h" #include "PropertyEntranceComponent.h" +#include "RocketLaunchLupComponent.h" #include "dServer.h" #include "dMessageIdentifiers.h" #include "PacketUtils.h" @@ -40,19 +41,7 @@ RocketLaunchpadControlComponent::~RocketLaunchpadControlComponent() { delete m_AltPrecondition; } -void RocketLaunchpadControlComponent::RocketEquip(Entity* entity, LWOOBJID rocketID) { - if (m_PlayersInRadius.find(entity->GetObjectID()) != m_PlayersInRadius.end()) { - Launch(entity, rocketID); - - //Go ahead and save the player - //This causes a double-save, but it should prevent players from not being saved - //before the next world server starts loading their data. - if (entity->GetCharacter()) - entity->GetCharacter()->SaveXMLToDatabase(); - } -} - -void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID optionalRocketID, LWOMAPID mapId, LWOCLONEID cloneId) { +void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId) { auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId; if (zone == 0) @@ -60,53 +49,22 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option return; } - TellMasterToPrepZone(zone); - // This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready - auto* inventoryComponent = originator->GetComponent(); auto* characterComponent = originator->GetComponent(); - auto* character = originator->GetCharacter(); - if (inventoryComponent == nullptr || characterComponent == nullptr || character == nullptr) { + if (!characterComponent || !character) return; + + auto* rocket = characterComponent->GetRocket(originator); + if (!rocket) { + Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket!\n"); return; } - // Select the rocket - - Item* rocket = nullptr; - - if (optionalRocketID != LWOOBJID_EMPTY) - { - rocket = inventoryComponent->FindItemById(optionalRocketID); - } - - if (rocket == nullptr) - { - rocket = inventoryComponent->FindItemById(characterComponent->GetLastRocketItemID()); - } - - if (rocket == nullptr) - { - rocket = inventoryComponent->FindItemByLot(6416); - } - - if (rocket == nullptr) - { - Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket (%llu)!\n", optionalRocketID); - - return; - } - - if (rocket->GetConfig().empty()) // Sanity check - { - rocket->SetCount(0, false, false); - - return; - } + // we have the ability to launch, so now we prep the zone + TellMasterToPrepZone(zone); // Achievement unlocked: "All zones unlocked" - if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) { character->SetTargetScene(m_AltLandingScene); } @@ -114,27 +72,6 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option character->SetTargetScene(m_TargetScene); } - if (characterComponent) { - for (LDFBaseData* data : rocket->GetConfig()) { - if (data->GetKey() == u"assemblyPartLOTs") { - std::string newRocketStr; - for (char character : data->GetValueAsString()) { - if (character == '+') { - newRocketStr.push_back(';'); - } - else { - newRocketStr.push_back(character); - } - } - newRocketStr.push_back(';'); - characterComponent->SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr)); - } - } - } - - // Store the last used rocket item's ID - characterComponent->SetLastRocketItemID(rocket->GetId()); - characterComponent->UpdatePlayerStatistic(RocketsUsed); character->SaveXMLToDatabase(); @@ -143,23 +80,31 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID()); - rocket->Equip(true); - GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS); EntityManager::Instance()->SerializeEntity(originator); } void RocketLaunchpadControlComponent::OnUse(Entity* originator) { + // If we are have the property or the LUP component, we don't want to immediately launch + // instead we let their OnUse handlers do their things + // which components of an Object have their OnUse called when using them + // so we don't need to call it here auto* propertyEntrance = m_Parent->GetComponent(); - - if (propertyEntrance != nullptr) - { - propertyEntrance->OnUse(originator); - + if (propertyEntrance) { return; } + auto* rocketLaunchLUP = m_Parent->GetComponent(); + if (rocketLaunchLUP) { + return; + } + + // No rocket no launch + auto* rocket = originator->GetComponent()->RocketEquip(originator); + if (!rocket) { + return; + } Launch(originator); } diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.h b/dGame/dComponents/RocketLaunchpadControlComponent.h index c9ee0691..7fdf4b32 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.h +++ b/dGame/dComponents/RocketLaunchpadControlComponent.h @@ -22,21 +22,13 @@ public: RocketLaunchpadControlComponent(Entity* parent, int rocketId); ~RocketLaunchpadControlComponent() override; - /** - * Launches the passed entity using the passed rocket and saves their data - * @param entity the entity to launch - * @param rocketID the ID of the rocket to use - */ - void RocketEquip(Entity* entity, LWOOBJID rocketID); - /** * Launches some entity to another world * @param originator the entity to launch - * @param optionalRocketID the ID of the rocket to launch * @param mapId the world to go to * @param cloneId the clone ID (for properties) */ - void Launch(Entity* originator, LWOOBJID optionalRocketID = LWOOBJID_EMPTY, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID); + void Launch(Entity* originator, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID); /** * Handles an OnUse event from some entity, preparing it for launch to some other world diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 4ab67e09..a813f6d3 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -5329,21 +5329,11 @@ void GameMessages::HandleEquipItem(RakNet::BitStream* inStream, Entity* entity) Item* item = inv->FindItemById(objectID); if (!item) return; - /*if (item->GetLot() == 6416) { // if it's a rocket - std::vector rocketPads = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_ROCKET_LAUNCH); - for (Entity* rocketPad : rocketPads) { - RocketLaunchpadControlComponent* rocketComp = static_cast(rocketPad->GetComponent(COMPONENT_TYPE_ROCKET_LAUNCH)); - if (rocketComp) { - rocketComp->RocketEquip(entity, objectID); - } - } - } - else*/ { + item->Equip(); EntityManager::Instance()->SerializeEntity(entity); } -} void GameMessages::HandleUnequipItem(RakNet::BitStream* inStream, Entity* entity) { bool immediate; diff --git a/dGame/dInventory/EquippedItem.h b/dGame/dInventory/EquippedItem.h index 0d1d191f..cf5ba253 100644 --- a/dGame/dInventory/EquippedItem.h +++ b/dGame/dInventory/EquippedItem.h @@ -1,6 +1,7 @@ -#pragma once +#pragma once #include "dCommonVars.h" +#include "LDFFormat.h" /** * An item that's equipped, generally as a smaller return type than the regular Item class @@ -26,4 +27,9 @@ struct EquippedItem * The slot this item is stored in */ uint32_t slot = 0; + + /** + * The configuration of the item with any extra data + */ + std::vector config = {}; }; diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 9d0c84cc..eb829f29 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -969,13 +969,16 @@ void HandlePacket(Packet* packet) { { GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, NiQuaternion::IDENTITY); } - - EntityManager::Instance()->ConstructAllEntities(packet->systemAddress); - player->GetComponent()->SetLastRocketConfig(u""); - + EntityManager::Instance()->ConstructAllEntities(packet->systemAddress); + + auto* characterComponent = player->GetComponent(); + if (characterComponent) { + player->GetComponent()->RocketUnEquip(player); + } + c->SetRetroactiveFlags(); - + player->RetroactiveVaultSize(); player->GetCharacter()->SetTargetScene("");