mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-04 09:44:10 +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 "CharacterComponent.h"
|
||||||
#include "PossessableComponent.h"
|
#include "PossessableComponent.h"
|
||||||
#include "PossessorComponent.h"
|
#include "PossessorComponent.h"
|
||||||
|
#include "ModelComponent.h"
|
||||||
#include "InventoryComponent.h"
|
#include "InventoryComponent.h"
|
||||||
#include "dZoneManager.h"
|
#include "dZoneManager.h"
|
||||||
#include "WorldConfig.h"
|
#include "WorldConfig.h"
|
||||||
@@ -82,6 +83,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
|||||||
m_DamageCooldownTimer = 0.0f;
|
m_DamageCooldownTimer = 0.0f;
|
||||||
|
|
||||||
RegisterMsg<GetObjectReportInfo>(this, &DestroyableComponent::OnGetObjectReportInfo);
|
RegisterMsg<GetObjectReportInfo>(this, &DestroyableComponent::OnGetObjectReportInfo);
|
||||||
|
RegisterMsg<GameMessages::SetFaction>(this, &DestroyableComponent::OnSetFaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
DestroyableComponent::~DestroyableComponent() {
|
DestroyableComponent::~DestroyableComponent() {
|
||||||
@@ -579,6 +581,14 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
|||||||
return;
|
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 this entity has damage reduction, reduce the damage to a minimum of 1
|
||||||
if (m_DamageReduction > 0 && damage > 0) {
|
if (m_DamageReduction > 0 && damage > 0) {
|
||||||
if (damage > m_DamageReduction) {
|
if (damage > m_DamageReduction) {
|
||||||
@@ -1089,3 +1099,11 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
|||||||
|
|
||||||
return true;
|
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);
|
void DoHardcoreModeDrops(const LWOOBJID source);
|
||||||
|
|
||||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||||
|
bool OnSetFaction(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
static Implementation<bool, const Entity*> IsEnemyImplentation;
|
static Implementation<bool, const Entity*> IsEnemyImplentation;
|
||||||
static Implementation<bool, const Entity*> IsFriendImplentation;
|
static Implementation<bool, const Entity*> IsFriendImplentation;
|
||||||
|
@@ -15,14 +15,15 @@
|
|||||||
#include "DluAssert.h"
|
#include "DluAssert.h"
|
||||||
|
|
||||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||||
|
using namespace GameMessages;
|
||||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||||
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
||||||
m_IsPaused = false;
|
m_IsPaused = false;
|
||||||
m_NumListeningInteract = 0;
|
m_NumListeningInteract = 0;
|
||||||
|
|
||||||
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
|
||||||
RegisterMsg(MessageType::Game::REQUEST_USE, this, &ModelComponent::OnRequestUse);
|
RegisterMsg<RequestUse>(this, &ModelComponent::OnRequestUse);
|
||||||
RegisterMsg(MessageType::Game::RESET_MODEL_TO_DEFAULTS, this, &ModelComponent::OnResetModelToDefaults);
|
RegisterMsg<ResetModelToDefaults>(this, &ModelComponent::OnResetModelToDefaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
|
bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
|
||||||
@@ -40,6 +41,14 @@ bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
|
|||||||
m_Speed = 3.0f;
|
m_Speed = 3.0f;
|
||||||
m_NumListeningInteract = 0;
|
m_NumListeningInteract = 0;
|
||||||
m_NumActiveUnSmash = 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;
|
m_Dirty = true;
|
||||||
Game::entityManager->SerializeEntity(GetParent());
|
Game::entityManager->SerializeEntity(GetParent());
|
||||||
|
|
||||||
@@ -297,3 +306,35 @@ void ModelComponent::SetVelocity(const NiPoint3& velocity) const {
|
|||||||
void ModelComponent::OnChatMessageReceived(const std::string& sMessage) {
|
void ModelComponent::OnChatMessageReceived(const std::string& sMessage) {
|
||||||
for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(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 OnChatMessageReceived(const std::string& sMessage);
|
||||||
|
|
||||||
|
void OnHit();
|
||||||
|
|
||||||
// Sets the speed of the model
|
// Sets the speed of the model
|
||||||
void SetSpeed(const float newSpeed) { m_Speed = newSpeed; }
|
void SetSpeed(const float newSpeed) { m_Speed = newSpeed; }
|
||||||
|
|
||||||
// Whether or not to restart at the end of the frame
|
// Whether or not to restart at the end of the frame
|
||||||
void RestartAtEndOfFrame() { m_RestartAtEndOfFrame = true; }
|
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:
|
private:
|
||||||
|
|
||||||
// Loads a behavior from the database.
|
// Loads a behavior from the database.
|
||||||
@@ -168,6 +178,9 @@ private:
|
|||||||
// The number of strips listening for a RequestUse GM to come in.
|
// The number of strips listening for a RequestUse GM to come in.
|
||||||
uint32_t m_NumListeningInteract{};
|
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.
|
// Whether or not the model is paused and should reject all interactions regarding behaviors.
|
||||||
bool m_IsPaused{};
|
bool m_IsPaused{};
|
||||||
/**
|
/**
|
||||||
|
@@ -863,5 +863,13 @@ namespace GameMessages {
|
|||||||
|
|
||||||
NiPoint3 pos{};
|
NiPoint3 pos{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SetFaction : public GameMsg {
|
||||||
|
SetFaction() : GameMsg(MessageType::Game::SET_FACTION) {}
|
||||||
|
|
||||||
|
int32_t factionID{};
|
||||||
|
|
||||||
|
bool bIgnoreChecks{ false };
|
||||||
|
};
|
||||||
};
|
};
|
||||||
#endif // GAMEMESSAGES_H
|
#endif // GAMEMESSAGES_H
|
||||||
|
@@ -184,3 +184,7 @@ void PropertyBehavior::Update(float deltaTime, ModelComponent& modelComponent) {
|
|||||||
void PropertyBehavior::OnChatMessageReceived(const std::string& sMessage) {
|
void PropertyBehavior::OnChatMessageReceived(const std::string& sMessage) {
|
||||||
for (auto& state : m_States | std::views::values) state.OnChatMessageReceived(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 Update(float deltaTime, ModelComponent& modelComponent);
|
||||||
void OnChatMessageReceived(const std::string& sMessage);
|
void OnChatMessageReceived(const std::string& sMessage);
|
||||||
|
void OnHit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The current active behavior state. Behaviors can only be in ONE state at a time.
|
// 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) {
|
void State::OnChatMessageReceived(const std::string& sMessage) {
|
||||||
for (auto& strip : m_Strips) strip.OnChatMessageReceived(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 Update(float deltaTime, ModelComponent& modelComponent);
|
||||||
|
|
||||||
void OnChatMessageReceived(const std::string& sMessage);
|
void OnChatMessageReceived(const std::string& sMessage);
|
||||||
|
void OnHit();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// The strips contained within this state.
|
// 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() {
|
void Strip::IncrementAction() {
|
||||||
if (m_Actions.empty()) return;
|
if (m_Actions.empty()) return;
|
||||||
m_NextActionIndex++;
|
m_NextActionIndex++;
|
||||||
@@ -259,6 +269,8 @@ void Strip::RemoveStates(ModelComponent& modelComponent) const {
|
|||||||
if (prevActionType == "OnInteract") {
|
if (prevActionType == "OnInteract") {
|
||||||
modelComponent.RemoveInteract();
|
modelComponent.RemoveInteract();
|
||||||
Game::entityManager->SerializeEntity(modelComponent.GetParent());
|
Game::entityManager->SerializeEntity(modelComponent.GetParent());
|
||||||
|
} else if (prevActionType == "OnAttack") {
|
||||||
|
modelComponent.RemoveAttack();
|
||||||
} else if (prevActionType == "UnSmash") {
|
} else if (prevActionType == "UnSmash") {
|
||||||
modelComponent.RemoveUnSmash();
|
modelComponent.RemoveUnSmash();
|
||||||
}
|
}
|
||||||
@@ -336,13 +348,14 @@ void Strip::Update(float deltaTime, ModelComponent& modelComponent) {
|
|||||||
if (m_NextActionIndex == 0) {
|
if (m_NextActionIndex == 0) {
|
||||||
if (nextAction.GetType() == "OnInteract") {
|
if (nextAction.GetType() == "OnInteract") {
|
||||||
modelComponent.AddInteract();
|
modelComponent.AddInteract();
|
||||||
Game::entityManager->SerializeEntity(entity);
|
|
||||||
m_WaitingForAction = true;
|
|
||||||
|
|
||||||
} else if (nextAction.GetType() == "OnChat") {
|
} else if (nextAction.GetType() == "OnChat") {
|
||||||
|
// logic here if needed
|
||||||
|
} else if (nextAction.GetType() == "OnAttack") {
|
||||||
|
modelComponent.AddAttack();
|
||||||
|
}
|
||||||
Game::entityManager->SerializeEntity(entity);
|
Game::entityManager->SerializeEntity(entity);
|
||||||
m_WaitingForAction = true;
|
m_WaitingForAction = true;
|
||||||
}
|
|
||||||
} else { // should be a normal block
|
} else { // should be a normal block
|
||||||
ProcNormalAction(deltaTime, modelComponent);
|
ProcNormalAction(deltaTime, modelComponent);
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,7 @@ public:
|
|||||||
bool HasMinimumActions() const { return m_Actions.size() >= 2; }
|
bool HasMinimumActions() const { return m_Actions.size() >= 2; }
|
||||||
|
|
||||||
void OnChatMessageReceived(const std::string& sMessage);
|
void OnChatMessageReceived(const std::string& sMessage);
|
||||||
|
void OnHit();
|
||||||
private:
|
private:
|
||||||
// Indicates this Strip is waiting for an action to be taken upon it to progress to its actions
|
// Indicates this Strip is waiting for an action to be taken upon it to progress to its actions
|
||||||
bool m_WaitingForAction{ false };
|
bool m_WaitingForAction{ false };
|
||||||
|
Reference in New Issue
Block a user