mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-04 01:34:07 +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