mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-10-31 04:32:06 +00:00 
			
		
		
		
	feat: OnAttack behavior (#1853)
Adds the `OnAttack` property behavior starting node. Tested that having the node allows the model to be attacked to trigger the start of behaviors
This commit is contained in:
		| @@ -30,6 +30,7 @@ | ||||
| #include "CharacterComponent.h" | ||||
| #include "PossessableComponent.h" | ||||
| #include "PossessorComponent.h" | ||||
| #include "ModelComponent.h" | ||||
| #include "InventoryComponent.h" | ||||
| #include "dZoneManager.h" | ||||
| #include "WorldConfig.h" | ||||
| @@ -82,6 +83,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { | ||||
| 	m_DamageCooldownTimer = 0.0f; | ||||
|  | ||||
| 	RegisterMsg<GetObjectReportInfo>(this, &DestroyableComponent::OnGetObjectReportInfo); | ||||
| 	RegisterMsg<GameMessages::SetFaction>(this, &DestroyableComponent::OnSetFaction); | ||||
| } | ||||
|  | ||||
| DestroyableComponent::~DestroyableComponent() { | ||||
| @@ -579,6 +581,14 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// Client does the same check, so we're doing it too | ||||
| 	auto* const modelComponent = m_Parent->GetComponent<ModelComponent>(); | ||||
| 	if (modelComponent) { | ||||
| 		modelComponent->OnHit(); | ||||
| 		// Don't actually deal the damage so the model doesn't die | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// If this entity has damage reduction, reduce the damage to a minimum of 1 | ||||
| 	if (m_DamageReduction > 0 && damage > 0) { | ||||
| 		if (damage > m_DamageReduction) { | ||||
| @@ -1089,3 +1099,11 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) { | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool DestroyableComponent::OnSetFaction(GameMessages::GameMsg& msg) { | ||||
| 	auto& modifyFaction = static_cast<GameMessages::SetFaction&>(msg); | ||||
| 	m_DirtyHealth = true; | ||||
| 	Game::entityManager->SerializeEntity(m_Parent); | ||||
| 	SetFaction(modifyFaction.factionID, modifyFaction.bIgnoreChecks); | ||||
| 	return true; | ||||
| } | ||||
|   | ||||
| @@ -469,6 +469,7 @@ public: | ||||
| 	void DoHardcoreModeDrops(const LWOOBJID source); | ||||
|  | ||||
| 	bool OnGetObjectReportInfo(GameMessages::GameMsg& msg); | ||||
| 	bool OnSetFaction(GameMessages::GameMsg& msg); | ||||
|  | ||||
| 	static Implementation<bool, const Entity*> IsEnemyImplentation; | ||||
| 	static Implementation<bool, const Entity*> IsFriendImplentation; | ||||
|   | ||||
| @@ -15,14 +15,15 @@ | ||||
| #include "DluAssert.h" | ||||
|  | ||||
| ModelComponent::ModelComponent(Entity* parent) : Component(parent) { | ||||
| 	using namespace GameMessages; | ||||
| 	m_OriginalPosition = m_Parent->GetDefaultPosition(); | ||||
| 	m_OriginalRotation = m_Parent->GetDefaultRotation(); | ||||
| 	m_IsPaused = false; | ||||
| 	m_NumListeningInteract = 0; | ||||
|  | ||||
| 	m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID"); | ||||
| 	RegisterMsg(MessageType::Game::REQUEST_USE, this, &ModelComponent::OnRequestUse); | ||||
| 	RegisterMsg(MessageType::Game::RESET_MODEL_TO_DEFAULTS, this, &ModelComponent::OnResetModelToDefaults); | ||||
| 	RegisterMsg<RequestUse>(this, &ModelComponent::OnRequestUse); | ||||
| 	RegisterMsg<ResetModelToDefaults>(this, &ModelComponent::OnResetModelToDefaults); | ||||
| } | ||||
|  | ||||
| bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) { | ||||
| @@ -40,6 +41,14 @@ bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) { | ||||
| 	m_Speed = 3.0f; | ||||
| 	m_NumListeningInteract = 0; | ||||
| 	m_NumActiveUnSmash = 0; | ||||
|  | ||||
| 	m_NumActiveAttack = 0; | ||||
| 	GameMessages::SetFaction set{}; | ||||
| 	set.target = m_Parent->GetObjectID(); | ||||
| 	set.factionID = -1; // Default faction for smashables | ||||
| 	set.bIgnoreChecks = true; // Remove the attack faction | ||||
| 	set.Send(); | ||||
|  | ||||
| 	m_Dirty = true; | ||||
| 	Game::entityManager->SerializeEntity(GetParent()); | ||||
|  | ||||
| @@ -297,3 +306,35 @@ void ModelComponent::SetVelocity(const NiPoint3& velocity) const { | ||||
| void ModelComponent::OnChatMessageReceived(const std::string& sMessage) { | ||||
| 	for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage); | ||||
| } | ||||
|  | ||||
| void ModelComponent::OnHit() { | ||||
| 	for (auto& behavior : m_Behaviors) { | ||||
| 		behavior.OnHit(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ModelComponent::AddAttack() { | ||||
| 	LOG_DEBUG("Adding attack %i", m_NumActiveAttack); | ||||
| 	m_Dirty = true; | ||||
| 	if (m_NumActiveAttack == 0) { | ||||
| 		GameMessages::SetFaction set{}; | ||||
| 		set.target = m_Parent->GetObjectID(); | ||||
| 		set.factionID = 6; // Default faction for smashables | ||||
| 		set.Send(); | ||||
| 	} | ||||
| 	m_NumActiveAttack++; | ||||
| } | ||||
|  | ||||
| void ModelComponent::RemoveAttack() { | ||||
| 	LOG_DEBUG("Removing attack %i", m_NumActiveAttack); | ||||
| 	DluAssert(m_NumActiveAttack > 0); | ||||
| 	m_Dirty = true; | ||||
| 	m_NumActiveAttack--; | ||||
| 	if (m_NumActiveAttack == 0) { | ||||
| 		GameMessages::SetFaction set{}; | ||||
| 		set.target = m_Parent->GetObjectID(); | ||||
| 		set.factionID = -1; // Default faction for smashables | ||||
| 		set.bIgnoreChecks = true; // Remove the attack faction | ||||
| 		set.Send(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -146,11 +146,21 @@ public: | ||||
|  | ||||
| 	void OnChatMessageReceived(const std::string& sMessage); | ||||
|  | ||||
| 	void OnHit(); | ||||
|  | ||||
| 	// Sets the speed of the model | ||||
| 	void SetSpeed(const float newSpeed) { m_Speed = newSpeed; } | ||||
|  | ||||
| 	// Whether or not to restart at the end of the frame | ||||
| 	void RestartAtEndOfFrame() { m_RestartAtEndOfFrame = true; } | ||||
|  | ||||
| 	// Increments the number of strips listening for an attack. | ||||
| 	// If this is the first strip adding an attack, it will set the factions to the correct values. | ||||
| 	void AddAttack(); | ||||
|  | ||||
| 	// 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(); | ||||
| private: | ||||
|  | ||||
| 	// Loads a behavior from the database. | ||||
| @@ -168,6 +178,9 @@ private: | ||||
| 	// The number of strips listening for a RequestUse GM to come in. | ||||
| 	uint32_t m_NumListeningInteract{}; | ||||
|  | ||||
| 	// The number of strips listening for an attack. | ||||
| 	uint32_t m_NumActiveAttack{}; | ||||
|  | ||||
| 	// Whether or not the model is paused and should reject all interactions regarding behaviors. | ||||
| 	bool m_IsPaused{}; | ||||
| 	/** | ||||
|   | ||||
| @@ -863,5 +863,13 @@ namespace GameMessages { | ||||
|  | ||||
| 		NiPoint3 pos{}; | ||||
| 	}; | ||||
|  | ||||
| 	struct SetFaction : public GameMsg { | ||||
| 		SetFaction() : GameMsg(MessageType::Game::SET_FACTION) {} | ||||
|  | ||||
| 		int32_t factionID{}; | ||||
|  | ||||
| 		bool bIgnoreChecks{ false }; | ||||
| 	}; | ||||
| }; | ||||
| #endif // GAMEMESSAGES_H | ||||
|   | ||||
| @@ -184,3 +184,7 @@ void PropertyBehavior::Update(float deltaTime, ModelComponent& modelComponent) { | ||||
| void PropertyBehavior::OnChatMessageReceived(const std::string& sMessage) { | ||||
| 	for (auto& state : m_States | std::views::values) state.OnChatMessageReceived(sMessage); | ||||
| } | ||||
|  | ||||
| void PropertyBehavior::OnHit() { | ||||
| 	for (auto& state : m_States | std::views::values) state.OnHit(); | ||||
| } | ||||
|   | ||||
| @@ -42,6 +42,7 @@ public: | ||||
|  | ||||
| 	void Update(float deltaTime, ModelComponent& modelComponent); | ||||
| 	void OnChatMessageReceived(const std::string& sMessage); | ||||
| 	void OnHit(); | ||||
|  | ||||
| private: | ||||
| 	// The current active behavior state. Behaviors can only be in ONE state at a time. | ||||
|   | ||||
| @@ -170,3 +170,7 @@ void State::Update(float deltaTime, ModelComponent& modelComponent) { | ||||
| void State::OnChatMessageReceived(const std::string& sMessage) { | ||||
| 	for (auto& strip : m_Strips) strip.OnChatMessageReceived(sMessage); | ||||
| } | ||||
|  | ||||
| void State::OnHit() { | ||||
| 	for (auto& strip : m_Strips) strip.OnHit(); | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ public: | ||||
| 	void Update(float deltaTime, ModelComponent& modelComponent); | ||||
|  | ||||
| 	void OnChatMessageReceived(const std::string& sMessage); | ||||
| 	void OnHit(); | ||||
| private: | ||||
|  | ||||
| 	// The strips contained within this state. | ||||
|   | ||||
| @@ -118,6 +118,16 @@ void Strip::OnChatMessageReceived(const std::string& sMessage) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Strip::OnHit() { | ||||
| 	if (m_PausedTime > 0.0f || !HasMinimumActions()) return; | ||||
|  | ||||
| 	const auto& nextAction = GetNextAction(); | ||||
| 	if (nextAction.GetType() == "OnAttack") { | ||||
| 		IncrementAction(); | ||||
| 		m_WaitingForAction = false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Strip::IncrementAction() { | ||||
| 	if (m_Actions.empty()) return; | ||||
| 	m_NextActionIndex++; | ||||
| @@ -259,6 +269,8 @@ void Strip::RemoveStates(ModelComponent& modelComponent) const { | ||||
| 	if (prevActionType == "OnInteract") { | ||||
| 		modelComponent.RemoveInteract(); | ||||
| 		Game::entityManager->SerializeEntity(modelComponent.GetParent()); | ||||
| 	} else if (prevActionType == "OnAttack") { | ||||
| 		modelComponent.RemoveAttack(); | ||||
| 	} else if (prevActionType == "UnSmash") { | ||||
| 		modelComponent.RemoveUnSmash(); | ||||
| 	} | ||||
| @@ -336,13 +348,14 @@ void Strip::Update(float deltaTime, ModelComponent& modelComponent) { | ||||
| 	if (m_NextActionIndex == 0) { | ||||
| 		if (nextAction.GetType() == "OnInteract") { | ||||
| 			modelComponent.AddInteract(); | ||||
| 			Game::entityManager->SerializeEntity(entity); | ||||
| 			m_WaitingForAction = true; | ||||
|  | ||||
| 		} else if (nextAction.GetType() == "OnChat") { | ||||
| 			Game::entityManager->SerializeEntity(entity); | ||||
| 			m_WaitingForAction = true; | ||||
| 			// logic here if needed | ||||
| 		} else if (nextAction.GetType() == "OnAttack") { | ||||
| 			modelComponent.AddAttack(); | ||||
| 		} | ||||
| 		Game::entityManager->SerializeEntity(entity); | ||||
| 		m_WaitingForAction = true; | ||||
| 	} else { // should be a normal block | ||||
| 		ProcNormalAction(deltaTime, modelComponent); | ||||
| 	} | ||||
|   | ||||
| @@ -42,6 +42,7 @@ public: | ||||
| 	bool HasMinimumActions() const { return m_Actions.size() >= 2; } | ||||
| 	 | ||||
| 	void OnChatMessageReceived(const std::string& sMessage); | ||||
| 	void OnHit(); | ||||
| private: | ||||
| 	// Indicates this Strip is waiting for an action to be taken upon it to progress to its actions | ||||
| 	bool m_WaitingForAction{ false }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 David Markowitz
					David Markowitz