From bbb3e10b88bed64e0f2d43a5b7091c28cd01f346 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 26 Sep 2026 23:36:37 -0700 Subject: [PATCH] Add DoDamage behavior and dont try and start the same node multiple times --- dGame/dComponents/DestroyableComponent.cpp | 2 +- dGame/dComponents/ModelComponent.cpp | 21 +++++++++++++++---- dGame/dComponents/ModelComponent.h | 7 +++++-- .../PropertyManagementComponent.cpp | 4 ++-- .../dComponents/PropertyManagementComponent.h | 2 +- dGame/dGameMessages/GameMessageHandler.cpp | 2 +- dGame/dPropertyBehaviors/PropertyBehavior.cpp | 8 +++---- dGame/dPropertyBehaviors/PropertyBehavior.h | 4 ++-- dGame/dPropertyBehaviors/State.cpp | 8 +++---- dGame/dPropertyBehaviors/State.h | 4 ++-- dGame/dPropertyBehaviors/Strip.cpp | 20 ++++++++++++++---- dGame/dPropertyBehaviors/Strip.h | 6 ++++-- dWorldServer/WorldServer.cpp | 2 +- 13 files changed, 60 insertions(+), 30 deletions(-) diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 877fe4fa8..c561ce85f 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -588,7 +588,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 // Client does the same check, so we're doing it too auto* const modelComponent = m_Parent->GetComponent(); if (modelComponent) { - modelComponent->OnHit(); + modelComponent->OnHit(source); // Don't actually deal the damage so the model doesn't die return; } diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index c403b92fc..78de197b5 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -16,6 +16,7 @@ #include "SimplePhysicsComponent.h" #include "eMissionTaskType.h" #include "eObjectBits.h" +#include "DestroyableComponent.h" #include "Database.h" #include "DluAssert.h" @@ -77,6 +78,7 @@ bool ModelComponent::OnRequestUse(GameMessages::RequestUse& requestUse) { void ModelComponent::Update(float deltaTime) { if (m_IsPaused) return; + m_DamageCooldown -= deltaTime; // Arrived once this frame's movement reached or passed the target if (m_Move.target && (*m_Move.target - m_Parent->GetPosition()).DotProduct(m_Move.targetDirection) <= 0.0f) { @@ -388,13 +390,13 @@ void ModelComponent::ResetRotationState(const NiQuaternion& newBase) { SyncAngularVelocity(); } -void ModelComponent::OnChatMessageReceived(const std::string& sMessage) { - for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage); +void ModelComponent::OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender) { + for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage, sender); } -void ModelComponent::OnHit() { +void ModelComponent::OnHit(const LWOOBJID attacker) { for (auto& behavior : m_Behaviors) { - behavior.OnHit(); + behavior.OnHit(attacker); } } @@ -438,3 +440,14 @@ bool ModelComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& re return true; } + +void ModelComponent::DoDamage(const LWOOBJID target) { + // dont do damage if we've done damage recently + if (m_DamageCooldown > 0.0f) return; + m_DamageCooldown = 1.0f; + auto* const initiator = Game::entityManager->GetEntity(target); + if (initiator) { + auto* const destComp = initiator->GetComponent(); + if (destComp) destComp->Damage(1, GetParent()->GetObjectID()); + } +} diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index 153f533b0..1e6fdf9ef 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -173,9 +173,9 @@ public: // Degrees per second of the active rotation on axis. float GetAngularSpeed(const int axis) const noexcept { return std::abs(m_Rotation.velocity[axis]); } - void OnChatMessageReceived(const std::string& sMessage); + void OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender); - void OnHit(); + void OnHit(const LWOOBJID attacker); // Whether or not to restart at the end of the frame void RestartAtEndOfFrame() { m_RestartAtEndOfFrame = true; } @@ -187,6 +187,7 @@ public: // Decrements the number of strips listening for an attack. // If this is the last strip removing an attack, it will reset the factions to the default of -1. void RemoveAttack(); + void DoDamage(const LWOOBJID target); private: // Degrees per second per unit of behavior speed static constexpr float BASE_ANGULAR_SPEED = 15.0f; @@ -285,4 +286,6 @@ private: RotationState m_Rotation; MoveState m_Move; + + float m_DamageCooldown { 0.0f }; }; diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index f38b58ca1..ee10c0691 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -802,13 +802,13 @@ const std::map& PropertyManagementComponent::GetModels() con return models; } -void PropertyManagementComponent::OnChatMessageReceived(const std::string& sMessage) const { +void PropertyManagementComponent::OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender) const { for (const auto& modelID : models | std::views::keys) { auto* const model = Game::entityManager->GetEntity(modelID); if (!model) continue; auto* const modelComponent = model->GetComponent(); if (!modelComponent) continue; - modelComponent->OnChatMessageReceived(sMessage); + modelComponent->OnChatMessageReceived(sMessage, sender); } } diff --git a/dGame/dComponents/PropertyManagementComponent.h b/dGame/dComponents/PropertyManagementComponent.h index c13fe9911..0e217a4fe 100644 --- a/dGame/dComponents/PropertyManagementComponent.h +++ b/dGame/dComponents/PropertyManagementComponent.h @@ -165,7 +165,7 @@ public: LWOOBJID GetId() const noexcept { return propertyId; } - void OnChatMessageReceived(const std::string& sMessage) const; + void OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender) const; private: /** * This diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 1918ca2ef..ef53cbd1b 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -86,7 +86,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System return; } } - + msg->target = objectID; msg->Deserialize(inStream); msg->Handle(*entity, sysAddr); return; diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp index c19c4d2fe..f30a622d7 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.cpp +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -211,14 +211,14 @@ void PropertyBehavior::Update(float deltaTime, ModelComponent& modelComponent) { } } -void PropertyBehavior::OnChatMessageReceived(const std::string& sMessage) { +void PropertyBehavior::OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender) { auto& activeState = GetActiveState(); - activeState.OnChatMessageReceived(sMessage); + activeState.OnChatMessageReceived(sMessage, sender); } -void PropertyBehavior::OnHit() { +void PropertyBehavior::OnHit(const LWOOBJID attacker) { auto& activeState = GetActiveState(); - activeState.OnHit(); + activeState.OnHit(attacker); } State& PropertyBehavior::GetActiveState() { diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.h b/dGame/dPropertyBehaviors/PropertyBehavior.h index 4fca613bc..9e0712829 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.h +++ b/dGame/dPropertyBehaviors/PropertyBehavior.h @@ -46,8 +46,8 @@ public: void Deserialize(const tinyxml2::XMLElement& behavior); void Update(float deltaTime, ModelComponent& modelComponent); - void OnChatMessageReceived(const std::string& sMessage); - void OnHit(); + void OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender); + void OnHit(const LWOOBJID attacker); private: State& GetActiveState(); diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp index 82d7ae0ba..2ffe10d0c 100644 --- a/dGame/dPropertyBehaviors/State.cpp +++ b/dGame/dPropertyBehaviors/State.cpp @@ -167,10 +167,10 @@ void State::Update(float deltaTime, ModelComponent& modelComponent, UpdateResult for (auto& strip : m_Strips) strip.Update(deltaTime, modelComponent, updateResult); } -void State::OnChatMessageReceived(const std::string& sMessage) { - for (auto& strip : m_Strips) strip.OnChatMessageReceived(sMessage); +void State::OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender) { + for (auto& strip : m_Strips) strip.OnChatMessageReceived(sMessage, sender); } -void State::OnHit() { - for (auto& strip : m_Strips) strip.OnHit(); +void State::OnHit(const LWOOBJID attacker) { + for (auto& strip : m_Strips) strip.OnHit(attacker); } diff --git a/dGame/dPropertyBehaviors/State.h b/dGame/dPropertyBehaviors/State.h index 436bb2105..50e00ffc3 100644 --- a/dGame/dPropertyBehaviors/State.h +++ b/dGame/dPropertyBehaviors/State.h @@ -24,8 +24,8 @@ public: void Update(float deltaTime, ModelComponent& modelComponent, UpdateResult& updateResult); - void OnChatMessageReceived(const std::string& sMessage); - void OnHit(); + void OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender); + void OnHit(const LWOOBJID attacker); private: // The strips contained within this state. diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index 57d7bdf9a..5074ef4ab 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -9,6 +9,7 @@ #include "PropertyManagementComponent.h" #include "PlayerManager.h" #include "SimplePhysicsComponent.h" +#include "DestroyableComponent.h" #include "dChatFilter.h" @@ -97,6 +98,7 @@ void Strip::HandleMsg(GameMessages::RequestUse& msg) { if (nextAction.GetType() == "OnInteract") { IncrementAction(); m_WaitingForAction = false; + m_StripInitiatorID = msg.target; } } @@ -114,23 +116,25 @@ void Strip::HandleMsg(GameMessages::ResetModelToDefaults& msg) { m_MovingToStart = false; } -void Strip::OnChatMessageReceived(const std::string& sMessage) { +void Strip::OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender) { if (m_PausedTime > 0.0f || !HasMinimumActions()) return; const auto& nextAction = GetNextAction(); if (nextAction.GetType() == "OnChat" && nextAction.GetValueParameterString() == sMessage) { IncrementAction(); m_WaitingForAction = false; + m_StripInitiatorID = sender; } } -void Strip::OnHit() { +void Strip::OnHit(const LWOOBJID attacker) { if (m_PausedTime > 0.0f || !HasMinimumActions()) return; const auto& nextAction = GetNextAction(); if (nextAction.GetType() == "OnAttack") { IncrementAction(); m_WaitingForAction = false; + m_StripInitiatorID = attacker; } } @@ -263,9 +267,9 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent, Up // In case a word is removed from the whitelist after it was approved const auto modelName = "%[Objects_" + std::to_string(entity.GetLOT()) + "_name]"; if (isOk) ChatPackets::SendChatMessage(UNASSIGNED_SYSTEM_ADDRESS, 12, modelName, entity.GetObjectID(), false, GeneralUtils::ASCIIToUTF16(valueStr)); - PropertyManagementComponent::Instance()->OnChatMessageReceived(valueStr.data()); + PropertyManagementComponent::Instance()->OnChatMessageReceived(valueStr.data(), m_StripInitiatorID); } else if (nextActionType == "PrivateMessage") { - PropertyManagementComponent::Instance()->OnChatMessageReceived(valueStr.data()); + PropertyManagementComponent::Instance()->OnChatMessageReceived(valueStr.data(), m_StripInitiatorID); } else if (nextActionType == "PlaySound") { GameMessages::PlayBehaviorSound sound; sound.target = modelComponent.GetParent()->GetObjectID(); @@ -282,6 +286,8 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent, Up Spawn(10497, entity); // Maelstrom Pirate property } else if (nextActionType == "SpawnRonin") { Spawn(10498, entity); // Dark Ronin property + } else if (nextActionType == "DoDamage") { + modelComponent.DoDamage(m_StripInitiatorID); } else if (nextActionType == "DropImagination") { for (; numberAsInt > 0; numberAsInt--) SpawnDrop(935, entity); // 1 Imagination powerup } else if (nextActionType == "DropHealth") { @@ -411,6 +417,7 @@ void Strip::Update(float deltaTime, ModelComponent& modelComponent, UpdateResult // Check for trigger blocks and if not a trigger block proc this blocks action if (m_NextActionIndex == 0) { + m_StripInitiatorID = LWOOBJID_EMPTY; LOG("Behavior strip started %s", nextAction.GetType().data()); m_Speed = DEFAULT_SPEED; if (nextAction.GetType() == "OnInteract") { @@ -431,6 +438,11 @@ void Strip::Update(float deltaTime, ModelComponent& modelComponent, UpdateResult IncrementAction(); m_PausedFromOnTimer = false; } + } else { + // in case we run into an unimplemented action or one that isnt a start node + // mark as waiting for action so we dont waste time re-starting the same logic and serializing + // every frame + m_WaitingForAction = true; } Game::entityManager->SerializeEntity(entity); diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h index e9ace639c..0ac937ff1 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -46,8 +46,8 @@ public: // 2 actions are required for strips to work bool HasMinimumActions() const { return m_Actions.size() >= 2; } - void OnChatMessageReceived(const std::string& sMessage); - void OnHit(); + void OnChatMessageReceived(const std::string& sMessage, const LWOOBJID sender); + void OnHit(const LWOOBJID attacker); private: // Indicates this Strip is waiting for an action to be taken upon it to progress to its actions bool m_WaitingForAction{ false }; @@ -90,6 +90,8 @@ private: // Speed applied to moves and rotations started by this strip float m_Speed{ DEFAULT_SPEED }; + + LWOOBJID m_StripInitiatorID{ LWOOBJID_EMPTY }; }; #endif //!__STRIP__H__ diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 15b9cd6f8..d34e612bc 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -1417,7 +1417,7 @@ void HandlePacket(Packet* packet) { std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message); LOG("%s: %s", playerName.c_str(), sMessage.c_str()); ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message); - if (PropertyManagementComponent::Instance()) PropertyManagementComponent::Instance()->OnChatMessageReceived(sMessage); + if (PropertyManagementComponent::Instance()) PropertyManagementComponent::Instance()->OnChatMessageReceived(sMessage, lastChar->GetObjectID()); } break;