From d079f3621b1b7ea49f45fa19b4094d1b1ccd08ac Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Mon, 8 Jun 2026 21:30:22 -0500 Subject: [PATCH] feat: enhance possession mechanics with skill set integration and improved message handling --- dGame/dComponents/InventoryComponent.cpp | 36 ++++------------------ dGame/dComponents/PossessableComponent.cpp | 3 +- dGame/dComponents/PossessableComponent.h | 11 +++++++ dGame/dComponents/PossessorComponent.cpp | 27 +++++++++++----- dGame/dGameMessages/GameMessages.cpp | 25 ++++++++++----- dGame/dGameMessages/GameMessages.h | 9 ++++++ 6 files changed, 64 insertions(+), 47 deletions(-) diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index a7e8d5ed..b6058434 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -989,9 +989,6 @@ void InventoryComponent::UnequipScripts(Item* unequippedItem) { } void InventoryComponent::HandlePossession(Item* item) { - auto* characterComponent = m_Parent->GetComponent(); - if (!characterComponent) return; - auto* possessorComponent = m_Parent->GetComponent(); if (!possessorComponent) return; @@ -1007,52 +1004,31 @@ void InventoryComponent::HandlePossession(Item* item) { return; } - GameMessages::SendSetStunned(m_Parent->GetObjectID(), eStateChangeType::PUSH, m_Parent->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); - - // Set the mount Item ID so that we know what were handling + // Set the mount item ID so that we know what we're handling possessorComponent->SetMountItemID(item->GetId()); GameMessages::SendSetMountInventoryID(m_Parent, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS); - // Create entity to mount - auto startRotation = m_Parent->GetRotation(); - + // Create the mount entity EntityInfo info{}; info.lot = item->GetLot(); info.pos = m_Parent->GetPosition(); - info.rot = startRotation; + info.rot = m_Parent->GetRotation(); info.spawnerID = m_Parent->GetObjectID(); auto* mount = Game::entityManager->CreateEntity(info, nullptr, m_Parent); - // Check to see if the mount is a vehicle, if so, flip it - auto* vehicleComponent = mount->GetComponent(); - if (vehicleComponent) characterComponent->SetIsRacing(true); - - // Setup the destroyable stats - auto* destroyableComponent = mount->GetComponent(); - if (destroyableComponent) { - destroyableComponent->SetIsImmune(true); - } - - // Mount it auto* possessableComponent = mount->GetComponent(); if (possessableComponent) { possessableComponent->SetIsItemSpawned(true); possessableComponent->SetPossessor(m_Parent->GetObjectID()); - // Possess it - possessorComponent->SetPossessable(mount->GetObjectID()); - possessorComponent->SetPossessableType(possessableComponent->GetPossessionType()); } - GameMessages::SendSetJetPackMode(m_Parent, false); + auto* destroyableComponent = mount->GetComponent(); + if (destroyableComponent) destroyableComponent->SetIsImmune(true); - // Make it go to the client Game::entityManager->ConstructEntity(mount); - // Update the possessor - Game::entityManager->SerializeEntity(m_Parent); + possessorComponent->Mount(mount); - // have to unlock the input so it vehicle can be driven - if (vehicleComponent) GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); GameMessages::SendMarkInventoryItemAsActive(m_Parent->GetObjectID(), true, eUnequippableActiveType::MOUNT, item->GetId(), m_Parent->GetSystemAddress()); } diff --git a/dGame/dComponents/PossessableComponent.cpp b/dGame/dComponents/PossessableComponent.cpp index 39e732c6..a38cf5ba 100644 --- a/dGame/dComponents/PossessableComponent.cpp +++ b/dGame/dComponents/PossessableComponent.cpp @@ -10,7 +10,7 @@ PossessableComponent::PossessableComponent(Entity* parent, const int32_t compone m_AnimationFlag = static_cast(item.animationFlag); // Get the possession Type from the CDClient - auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;"); + auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit, skillSet FROM PossessableComponent WHERE id = ?;"); query.bind(1, static_cast(componentID)); @@ -20,6 +20,7 @@ PossessableComponent::PossessableComponent(Entity* parent, const int32_t compone if (!result.eof()) { m_PossessionType = static_cast(result.getIntField("possessionType", 1)); // Default to Attached Visible m_DepossessOnHit = static_cast(result.getIntField("depossessOnHit", 0)); + m_SkillSet = result.getIntField("skillSet", 0); } else { m_PossessionType = ePossessionType::ATTACHED_VISIBLE; m_DepossessOnHit = false; diff --git a/dGame/dComponents/PossessableComponent.h b/dGame/dComponents/PossessableComponent.h index 64a9dd73..776d0fe6 100644 --- a/dGame/dComponents/PossessableComponent.h +++ b/dGame/dComponents/PossessableComponent.h @@ -55,6 +55,12 @@ public: */ bool GetDepossessOnHit() const { return m_DepossessOnHit; }; + /** + * Returns the skill set ID for this possessable (0 = no skill set) + * @return the skill set ID + */ + int32_t GetSkillSet() const { return m_SkillSet; }; + /** * Forcibly depossess the Entity */ @@ -118,4 +124,9 @@ private: * */ bool m_ItemSpawned = false; + + /** + * @brief Skill set ID from PossessableComponent CDClient table (0 = none) + */ + int32_t m_SkillSet = 0; }; diff --git a/dGame/dComponents/PossessorComponent.cpp b/dGame/dComponents/PossessorComponent.cpp index 88ac1c2a..a478260e 100644 --- a/dGame/dComponents/PossessorComponent.cpp +++ b/dGame/dComponents/PossessorComponent.cpp @@ -1,11 +1,10 @@ #include "PossessorComponent.h" #include "PossessableComponent.h" #include "CharacterComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "EntityManager.h" #include "GameMessages.h" #include "eUnequippableActiveType.h" -#include "eControlScheme.h" -#include "eStateChangeType.h" PossessorComponent::PossessorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) { m_Possessable = LWOOBJID_EMPTY; @@ -42,12 +41,17 @@ void PossessorComponent::Mount(Entity* mount) { // Don't do anything if we are busy dismounting if (GetIsDismounting() || !mount) return; - GameMessages::SendSetMountInventoryID(m_Parent, mount->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); auto* possessableComponent = mount->GetComponent(); if (possessableComponent) { - possessableComponent->SetPossessor(m_Parent->GetObjectID()); SetPossessable(mount->GetObjectID()); SetPossessableType(possessableComponent->GetPossessionType()); + if (possessableComponent->GetSkillSet() != 0) { + GameMessages::UseSkillSet useSkillSet; + useSkillSet.target = m_Parent->GetObjectID(); + useSkillSet.possessedId = mount->GetObjectID(); + useSkillSet.setId = possessableComponent->GetSkillSet(); + useSkillSet.Send(m_Parent->GetSystemAddress()); + } } auto characterComponent = m_Parent->GetComponent(); @@ -55,8 +59,9 @@ void PossessorComponent::Mount(Entity* mount) { // GM's to send GameMessages::SendSetJetPackMode(m_Parent, false); - GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); - GameMessages::SendSetStunned(m_Parent->GetObjectID(), eStateChangeType::PUSH, m_Parent->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); + if (mount->GetComponent()) { + GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); + } Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(mount); @@ -72,6 +77,14 @@ void PossessorComponent::Dismount(Entity* mount, bool forceDismount) { if (possessableComponent) { possessableComponent->SetPossessor(LWOOBJID_EMPTY); if (forceDismount) possessableComponent->ForceDepossess(); + if (possessableComponent->GetSkillSet() != 0) { + GameMessages::UseSkillSet useSkillSet; + useSkillSet.target = m_Parent->GetObjectID(); + useSkillSet.possessedId = mount->GetObjectID(); + useSkillSet.setId = possessableComponent->GetSkillSet(); + useSkillSet.bRemove = true; + useSkillSet.Send(m_Parent->GetSystemAddress()); + } } Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(mount); @@ -79,6 +92,4 @@ void PossessorComponent::Dismount(Entity* mount, bool forceDismount) { auto characterComponent = m_Parent->GetComponent(); if (characterComponent) characterComponent->SetIsRacing(false); } - // Make sure we don't have wacky controls - GameMessages::SendSetPlayerControlScheme(m_Parent, eControlScheme::SCHEME_A); } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 3af6cb48..497b69c9 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3946,11 +3946,19 @@ void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objec CMSGHEADER; bitStream.Write(entity->GetObjectID()); bitStream.Write(MessageType::Game::SET_MOUNT_INVENTORY_ID); - bitStream.Write(objectID); + bitStream.Write(objectID != LWOOBJID_EMPTY); + if (objectID != LWOOBJID_EMPTY) bitStream.Write(objectID); SEND_PACKET_BROADCAST; } +void GameMessages::UseSkillSet::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(bRemove); + bitStream.Write(possessedId != LWOOBJID_EMPTY); + if (possessedId != LWOOBJID_EMPTY) bitStream.Write(possessedId); + bitStream.Write(setId != -1); + if (setId != -1) bitStream.Write(setId); +} void GameMessages::HandleDismountComplete(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { // Get the objectID from the bitstream @@ -3986,9 +3994,6 @@ void GameMessages::HandleDismountComplete(RakNet::BitStream& inStream, Entity* e // Update the entity that was possessing Game::entityManager->SerializeEntity(entity); - - // We aren't mounted so remove the stun - GameMessages::SendSetStunned(entity->GetObjectID(), eStateChangeType::POP, UNASSIGNED_SYSTEM_ADDRESS, LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); } } } @@ -3996,10 +4001,14 @@ void GameMessages::HandleDismountComplete(RakNet::BitStream& inStream, Entity* e void GameMessages::HandleAcknowledgePossession(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { Game::entityManager->SerializeEntity(entity); - LWOOBJID objectId{}; - inStream.Read(objectId); - auto* mount = Game::entityManager->GetEntity(objectId); - if (mount) Game::entityManager->SerializeEntity(mount); + bool hasObjectId{}; + inStream.Read(hasObjectId); + if (hasObjectId) { + LWOOBJID objectId{}; + inStream.Read(objectId); + auto* mount = Game::entityManager->GetEntity(objectId); + if (mount) Game::entityManager->SerializeEntity(mount); + } } //Racing diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index b7558d4b..cd826983 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -961,5 +961,14 @@ namespace GameMessages { LWOOBJID childID{}; }; + + struct UseSkillSet : public GameMsg { + UseSkillSet() : GameMsg(MessageType::Game::USE_SKILL_SET) {} + void Serialize(RakNet::BitStream& bitStream) const override; + + bool bRemove{}; + LWOOBJID possessedId{ LWOOBJID_EMPTY }; + int32_t setId{ -1 }; + }; }; #endif // GAMEMESSAGES_H