From 0278123a0c3bc9c0063310c53c7509cbdb3225bd Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Mon, 31 Mar 2025 21:30:33 -0700 Subject: [PATCH] most of gameplay tab works --- dCommon/DluAssert.h | 2 +- dGame/dComponents/ModelComponent.cpp | 24 ++++- dGame/dComponents/ModelComponent.h | 5 + dGame/dGameMessages/GameMessageHandler.cpp | 6 +- dGame/dGameMessages/GameMessages.cpp | 95 +++++++++---------- dGame/dGameMessages/GameMessages.h | 21 +++- dGame/dPropertyBehaviors/PropertyBehavior.cpp | 18 ++++ dGame/dPropertyBehaviors/PropertyBehavior.h | 10 ++ dGame/dPropertyBehaviors/State.cpp | 9 ++ dGame/dPropertyBehaviors/State.h | 6 ++ dGame/dPropertyBehaviors/Strip.cpp | 52 +++++++++- dGame/dPropertyBehaviors/Strip.h | 12 +++ 12 files changed, 199 insertions(+), 61 deletions(-) diff --git a/dCommon/DluAssert.h b/dCommon/DluAssert.h index c54dd54e..f099443a 100644 --- a/dCommon/DluAssert.h +++ b/dCommon/DluAssert.h @@ -4,7 +4,7 @@ #include #ifdef _DEBUG -# define DluAssert(expression) assert(expression) +# define DluAssert(expression) do { assert(expression) } while(0) #else # define DluAssert(expression) #endif diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index 3a273c9b..5af8bd51 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -10,12 +10,26 @@ #include "SimplePhysicsComponent.h" #include "Database.h" +#include "EntityInfo.h" ModelComponent::ModelComponent(Entity* parent) : Component(parent) { m_OriginalPosition = m_Parent->GetDefaultPosition(); m_OriginalRotation = m_Parent->GetDefaultRotation(); m_userModelID = m_Parent->GetVarAs(u"userModelID"); + RegisterMsg(MessageType::Game::REQUEST_USE, this, &ModelComponent::OnRequestUse); +} + +bool ModelComponent::OnRequestUse(GameMessages::GameMsg& msg) { + auto& requestUse = static_cast(msg); + for (auto& behavior : m_Behaviors) behavior.HandleMsg(requestUse); + return true; +} + +void ModelComponent::Update(float deltaTime) { + for (auto& behavior : m_Behaviors) { + behavior.Update(deltaTime, *this); + } } void ModelComponent::LoadBehaviors() { @@ -29,9 +43,9 @@ void ModelComponent::LoadBehaviors() { LOG_DEBUG("Loading behavior %d", behaviorId.value()); auto& inserted = m_Behaviors.emplace_back(); inserted.SetBehaviorId(*behaviorId); - + const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value()); - + tinyxml2::XMLDocument behaviorXml; auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size()); LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str()); @@ -56,14 +70,14 @@ void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialU //actual model component: outBitStream.Write1(); // Yes we are writing model info - outBitStream.Write0(); // Is pickable + outBitStream.Write1(); // Is pickable outBitStream.Write(2); // Physics type outBitStream.Write(m_OriginalPosition); // Original position outBitStream.Write(m_OriginalRotation); // Original rotation outBitStream.Write1(); // We are writing behavior info - outBitStream.Write(0); // Number of behaviors - outBitStream.Write1(); // Is this model paused + outBitStream.Write(m_Behaviors.size()); // Number of behaviors + outBitStream.Write0(); // Is this model paused if (bIsInitialUpdate) outBitStream.Write0(); // We are not writing model editing info } diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index 12ef7744..65a10631 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -30,6 +30,9 @@ public: ModelComponent(Entity* parent); void LoadBehaviors(); + void Update(float deltaTime) override; + + bool OnRequestUse(GameMessages::GameMsg& msg); void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; @@ -114,6 +117,8 @@ public: std::array, 5> GetBehaviorsForSave() const; + const std::vector& GetBehaviors() const { return m_Behaviors; }; + private: /** * The behaviors of the model diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index ef890401..b201f999 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -45,6 +45,7 @@ namespace { using namespace GameMessages; using MessageCreator = std::function()>; std::map g_MessageHandlers = { + { REQUEST_USE, []() { return std::make_unique(); }}, { REQUEST_SERVER_OBJECT_INFO, []() { return std::make_unique(); } }, { SHOOTING_GALLERY_FIRE, []() { return std::make_unique(); } }, }; @@ -118,11 +119,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System break; } - case MessageType::Game::REQUEST_USE: { - GameMessages::HandleRequestUse(inStream, entity, sysAddr); - break; - } - case MessageType::Game::SET_FLAG: { GameMessages::HandleSetFlag(inStream, entity); break; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 6fd7576f..6bca2239 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -4954,54 +4954,6 @@ void GameMessages::HandleQuickBuildCancel(RakNet::BitStream& inStream, Entity* e quickBuildComponent->CancelQuickBuild(Game::entityManager->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY); } -void GameMessages::HandleRequestUse(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { - bool bIsMultiInteractUse = false; - unsigned int multiInteractID; - int multiInteractType; - bool secondary; - LWOOBJID objectID; - - inStream.Read(bIsMultiInteractUse); - inStream.Read(multiInteractID); - inStream.Read(multiInteractType); - inStream.Read(objectID); - inStream.Read(secondary); - - Entity* interactedObject = Game::entityManager->GetEntity(objectID); - - if (interactedObject == nullptr) { - LOG("Object %llu tried to interact, but doesn't exist!", objectID); - - return; - } - - if (interactedObject->GetLOT() == 9524) { - entity->GetCharacter()->SetBuildMode(true); - } - - if (bIsMultiInteractUse) { - if (multiInteractType == 0) { - auto* missionOfferComponent = static_cast(interactedObject->GetComponent(eReplicaComponentType::MISSION_OFFER)); - - if (missionOfferComponent != nullptr) { - missionOfferComponent->OfferMissions(entity, multiInteractID); - } - } else { - interactedObject->OnUse(entity); - } - } else { - interactedObject->OnUse(entity); - } - - //Perform use task if possible: - auto missionComponent = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); - - if (missionComponent == nullptr) return; - - missionComponent->Progress(eMissionTaskType::TALK_TO_NPC, interactedObject->GetLOT(), interactedObject->GetObjectID()); - missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID()); -} - void GameMessages::HandlePlayEmote(RakNet::BitStream& inStream, Entity* entity) { int emoteID; LWOOBJID targetID; @@ -6443,4 +6395,51 @@ namespace GameMessages { auto* handlingEntity = Game::entityManager->GetEntity(targetForReport); if (handlingEntity) handlingEntity->HandleMsg(*this); } + + bool RequestUse::Deserialize(RakNet::BitStream& stream) { + if (!stream.Read(bIsMultiInteractUse)) return false; + if (!stream.Read(multiInteractID)) return false; + if (!stream.Read(multiInteractType)) return false; + if (!stream.Read(object)) return false; + if (!stream.Read(secondary)) return false; + return true; + } + + void RequestUse::Handle(Entity& entity, const SystemAddress& sysAddr) { + Entity* interactedObject = Game::entityManager->GetEntity(object); + + if (interactedObject == nullptr) { + LOG("Object %llu tried to interact, but doesn't exist!", object); + + return; + } + + if (interactedObject->GetLOT() == 9524) { + entity.GetCharacter()->SetBuildMode(true); + } + + if (bIsMultiInteractUse) { + if (multiInteractType == 0) { + auto* missionOfferComponent = static_cast(interactedObject->GetComponent(eReplicaComponentType::MISSION_OFFER)); + + if (missionOfferComponent != nullptr) { + missionOfferComponent->OfferMissions(&entity, multiInteractID); + } + } else { + interactedObject->OnUse(&entity); + } + } else { + interactedObject->OnUse(&entity); + } + + interactedObject->HandleMsg(*this); + + //Perform use task if possible: + auto missionComponent = entity.GetComponent(); + + if (!missionComponent) return; + + missionComponent->Progress(eMissionTaskType::TALK_TO_NPC, interactedObject->GetLOT(), interactedObject->GetObjectID()); + missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID()); + } } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index c3889674..97c92ae4 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -631,7 +631,6 @@ namespace GameMessages { void HandleFireEventServerSide(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandleRequestPlatformResync(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandleQuickBuildCancel(RakNet::BitStream& inStream, Entity* entity); - void HandleRequestUse(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandlePlayEmote(RakNet::BitStream& inStream, Entity* entity); void HandleModularBuildConvertModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandleSetFlag(RakNet::BitStream& inStream, Entity* entity); @@ -782,6 +781,26 @@ namespace GameMessages { bool Deserialize(RakNet::BitStream& bitStream) override; void Handle(Entity& entity, const SystemAddress& sysAddr) override; }; + + struct RequestUse : public GameMsg { + RequestUse() : GameMsg(MessageType::Game::REQUEST_USE) {} + + bool Deserialize(RakNet::BitStream& stream) override; + void Handle(Entity& entity, const SystemAddress& sysAddr) override; + + LWOOBJID object{}; + + bool secondary{ false }; + + // Set to true if this coming from a multi-interaction UI on the client. + bool bIsMultiInteractUse{}; + + // Used only for multi-interaction + unsigned int multiInteractID{}; + + // Used only for multi-interaction, is of the enum type InteractionType + int multiInteractType{}; + }; }; #endif // GAMEMESSAGES_H diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp index 5bdb5827..4ab5f607 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.cpp +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -4,9 +4,13 @@ #include "BehaviorStates.h" #include "ControlBehaviorMsgs.h" #include "tinyxml2.h" +#include "ModelComponent.h" + +#include PropertyBehavior::PropertyBehavior() { m_LastEditedState = BehaviorState::HOME_STATE; + m_ActiveState = BehaviorState::HOME_STATE; } template<> @@ -84,6 +88,11 @@ void PropertyBehavior::HandleMsg(AddMessage& msg) { isLoot = m_BehaviorId != 7965; }; +template<> +void PropertyBehavior::HandleMsg(GameMessages::RequestUse& msg) { + m_States[m_ActiveState].HandleMsg(msg); +} + void PropertyBehavior::SendBehaviorListToClient(AMFArrayValue& args) const { args.Insert("id", std::to_string(m_BehaviorId)); args.Insert("name", m_Name); @@ -153,3 +162,12 @@ void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) { m_States[static_cast(stateId)].Deserialize(*stateElement); } } + +const State& PropertyBehavior::GetState(const BehaviorState state) { + DluAssert(state >= BehaviorState::HOME_STATE && state <= BehaviorState::STAR_STATE); + return m_States[state]; +} + +void PropertyBehavior::Update(float deltaTime, ModelComponent& modelComponent) { + for (auto& state : m_States | std::views::values) state.Update(deltaTime, modelComponent); +} diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.h b/dGame/dPropertyBehaviors/PropertyBehavior.h index 01eb1968..893306b2 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.h +++ b/dGame/dPropertyBehaviors/PropertyBehavior.h @@ -10,6 +10,7 @@ namespace tinyxml2 { enum class BehaviorState : uint32_t; class AMFArrayValue; +class ModelComponent; /** * Represents the Entity of a Property Behavior and holds data associated with the behavior @@ -31,8 +32,17 @@ public: void Serialize(tinyxml2::XMLElement& behavior) const; void Deserialize(const tinyxml2::XMLElement& behavior); + + const std::map& GetStates() const { return m_States; } + const State& GetState(const BehaviorState state); + const State& GetActiveState() const { return m_States.at(m_ActiveState); } + State& GetActiveStateMut() { return m_States.at(m_ActiveState); } + + void Update(float deltaTime, ModelComponent& modelComponent); private: + BehaviorState m_ActiveState; + // The states this behavior has. std::map m_States; diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp index 1fb072c1..a18a5716 100644 --- a/dGame/dPropertyBehaviors/State.cpp +++ b/dGame/dPropertyBehaviors/State.cpp @@ -117,6 +117,11 @@ void State::HandleMsg(MigrateActionsMessage& msg) { } }; +template<> +void State::HandleMsg(GameMessages::RequestUse& msg) { + for (auto& strip : m_Strips) strip.HandleMsg(msg); +} + bool State::IsEmpty() const { for (const auto& strip : m_Strips) { if (!strip.IsEmpty()) return false; @@ -152,3 +157,7 @@ void State::Deserialize(const tinyxml2::XMLElement& state) { strip.Deserialize(*stripElement); } } + +void State::Update(float deltaTime, ModelComponent& modelComponent) { + for (auto& strip : m_Strips) strip.Update(deltaTime, modelComponent); +} diff --git a/dGame/dPropertyBehaviors/State.h b/dGame/dPropertyBehaviors/State.h index 3e8c827f..babc9725 100644 --- a/dGame/dPropertyBehaviors/State.h +++ b/dGame/dPropertyBehaviors/State.h @@ -8,6 +8,7 @@ namespace tinyxml2 { } class AMFArrayValue; +class ModelComponent; class State { public: @@ -19,6 +20,11 @@ public: void Serialize(tinyxml2::XMLElement& state) const; void Deserialize(const tinyxml2::XMLElement& state); + + const std::vector& GetStrips() const { return m_Strips; } + std::vector& GetStripsMut() { return m_Strips; } + + void Update(float deltaTime, ModelComponent& modelComponent); private: std::vector m_Strips; }; diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index d6dc050b..18648996 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -3,6 +3,9 @@ #include "Amf3.h" #include "ControlBehaviorMsgs.h" #include "tinyxml2.h" +#include "dEntity/EntityInfo.h" +#include "ModelComponent.h" +#include "PlayerManager.h" template <> void Strip::HandleMsg(AddStripMessage& msg) { @@ -75,7 +78,54 @@ void Strip::HandleMsg(MigrateActionsMessage& msg) { } else { m_Actions.insert(m_Actions.begin() + msg.GetDstActionIndex(), msg.GetMigratedActions().begin(), msg.GetMigratedActions().end()); } -}; +} + +template<> +void Strip::HandleMsg(GameMessages::RequestUse& msg) { + if (m_Actions[m_NextActionIndex].GetType() == "OnInteract") IncrementAction(); +} + +void Strip::IncrementAction() { + if (m_Actions.empty()) return; + m_NextActionIndex++; + m_NextActionIndex %= m_Actions.size(); +} + +void Strip::Spawn(LOT lot, Entity& entity) { + EntityInfo info{}; + info.lot = lot; // Dark Ronin property + info.pos = entity.GetPosition(); + info.rot = NiQuaternionConstant::IDENTITY; + info.spawnerID = entity.GetObjectID(); + Game::entityManager->ConstructEntity(Game::entityManager->CreateEntity(info, nullptr, &entity)); + IncrementAction(); +} + +// Spawns a specific drop for all +void Strip::SpawnDrop(LOT dropLOT, Entity& entity) { + for (auto* const player : PlayerManager::GetAllPlayers()) { + GameMessages::SendDropClientLoot(player, entity.GetObjectID(), dropLOT, 0, entity.GetPosition()); + } + IncrementAction(); +} + +void Strip::Update(float deltaTime, ModelComponent& modelComponent) { + auto& entity = *modelComponent.GetParent(); + auto number = static_cast(GetNextAction().GetValueParameterDouble()); + if (GetNextAction().GetType() == "SpawnStromling") { + Spawn(10495, entity); + } else if (GetNextAction().GetType() == "SpawnPirate") { + Spawn(10497, entity); + } else if (GetNextAction().GetType() == "SpawnRonin") { + Spawn(10498, entity); + } else if (GetNextAction().GetType() == "DropImagination") { + for (; number > 0; number--) SpawnDrop(935, entity); + } else if (GetNextAction().GetType() == "DropHealth") { + for (; number > 0; number--) SpawnDrop(177, entity); + } else if (GetNextAction().GetType() == "DropArmor") { + for (; number > 0; number--) SpawnDrop(6431, entity); + } +} void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const { m_Position.SendBehaviorBlocksToClient(args); diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h index 8fd7d0fe..a93665f5 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -4,6 +4,8 @@ #include "Action.h" #include "StripUiPosition.h" +#include "DluAssert.h" + #include namespace tinyxml2 { @@ -11,6 +13,7 @@ namespace tinyxml2 { } class AMFArrayValue; +class ModelComponent; class Strip { public: @@ -22,7 +25,16 @@ public: void Serialize(tinyxml2::XMLElement& strip) const; void Deserialize(const tinyxml2::XMLElement& strip); + + const std::vector& GetActions() const { return m_Actions; } + const Action& GetNextAction() const { DluAssert(m_NextActionIndex < m_Actions.size()); return m_Actions[m_NextActionIndex]; } + + void IncrementAction(); + void Spawn(LOT object, Entity& entity); + void Update(float deltaTime, ModelComponent& modelComponent); + void SpawnDrop(LOT dropLOT, Entity& entity); private: + size_t m_NextActionIndex{ 0 }; std::vector m_Actions; StripUiPosition m_Position; };