mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-22 05:27:19 +00:00
Adding damage cooldown/"invincibility frames" as in Live (#1276)
* Added cooldown handling * Made most of the logs hidden outside of debug mode * removed weird submodule * kill this phantom submodule * updated to reflect reviewed feedback * Added IsCooldownImmune() method to DestroyableComponent * friggin typo * Implemented non-pending changes and added cooldown immunity functions to DestroyableComponentTests * add trailing linebreak * another typo :( * flipped cooldown test order (not leaving immune) * Clean up comment and add DestroyableComponent test
This commit is contained in:
parent
2c9a98313a
commit
411dce7457
@ -3,6 +3,8 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "EntityManager.h"
|
#include "EntityManager.h"
|
||||||
|
#include "dZoneManager.h"
|
||||||
|
#include "WorldConfig.h"
|
||||||
#include "DestroyableComponent.h"
|
#include "DestroyableComponent.h"
|
||||||
#include "BehaviorContext.h"
|
#include "BehaviorContext.h"
|
||||||
#include "eBasicAttackSuccessTypes.h"
|
#include "eBasicAttackSuccessTypes.h"
|
||||||
@ -13,8 +15,15 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
|||||||
|
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
if (destroyableComponent != nullptr) {
|
if (destroyableComponent != nullptr) {
|
||||||
PlayFx(u"onhit", entity->GetObjectID());
|
PlayFx(u"onhit", entity->GetObjectID()); //This damage animation doesn't seem to play consistently
|
||||||
destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID);
|
destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID);
|
||||||
|
|
||||||
|
//Handle player damage cooldown
|
||||||
|
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||||
|
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||||
|
LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_OnSuccess->Handle(context, bitStream, branch);
|
this->m_OnSuccess->Handle(context, bitStream, branch);
|
||||||
@ -72,6 +81,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isImmune) {
|
if (isImmune) {
|
||||||
|
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||||
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -178,11 +188,15 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isImmune = destroyableComponent->IsImmune();
|
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||||
|
LOG_DEBUG("Damage cooldown timer currently %f s", destroyableComponent->GetDamageCooldownTimer());
|
||||||
|
|
||||||
|
const bool isImmune = (destroyableComponent->IsImmune()) || (destroyableComponent->IsCooldownImmune());
|
||||||
|
|
||||||
bitStream->Write(isImmune);
|
bitStream->Write(isImmune);
|
||||||
|
|
||||||
if (isImmune) {
|
if (isImmune) {
|
||||||
|
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||||
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -203,6 +217,12 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
|
|
||||||
bitStream->Write(isSuccess);
|
bitStream->Write(isSuccess);
|
||||||
|
|
||||||
|
//Handle player damage cooldown
|
||||||
|
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||||
|
LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime);
|
||||||
|
}
|
||||||
|
|
||||||
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
if (healthDamageDealt >= 1) {
|
if (healthDamageDealt >= 1) {
|
||||||
@ -236,6 +256,8 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BasicAttackBehavior::Load() {
|
void BasicAttackBehavior::Load() {
|
||||||
|
this->m_DontApplyImmune = GetBoolean("dont_apply_immune");
|
||||||
|
|
||||||
this->m_MinDamage = GetInt("min damage");
|
this->m_MinDamage = GetInt("min damage");
|
||||||
if (this->m_MinDamage == 0) this->m_MinDamage = 1;
|
if (this->m_MinDamage == 0) this->m_MinDamage = 1;
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Load() override;
|
void Load() override;
|
||||||
private:
|
private:
|
||||||
|
bool m_DontApplyImmune;
|
||||||
|
|
||||||
uint32_t m_MinDamage;
|
uint32_t m_MinDamage;
|
||||||
|
|
||||||
uint32_t m_MaxDamage;
|
uint32_t m_MaxDamage;
|
||||||
|
@ -73,6 +73,8 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
|||||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||||
m_ImmuneToPullToPointCount = 0;
|
m_ImmuneToPullToPointCount = 0;
|
||||||
m_DeathBehavior = -1;
|
m_DeathBehavior = -1;
|
||||||
|
|
||||||
|
m_DamageCooldownTimer = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
DestroyableComponent::~DestroyableComponent() {
|
DestroyableComponent::~DestroyableComponent() {
|
||||||
@ -179,6 +181,10 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DestroyableComponent::Update(float deltaTime) {
|
||||||
|
m_DamageCooldownTimer -= deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||||
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
@ -464,6 +470,10 @@ bool DestroyableComponent::IsImmune() const {
|
|||||||
return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0;
|
return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DestroyableComponent::IsCooldownImmune() const {
|
||||||
|
return m_DamageCooldownTimer > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
bool DestroyableComponent::IsKnockbackImmune() const {
|
bool DestroyableComponent::IsKnockbackImmune() const {
|
||||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||||
@ -546,7 +556,8 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsImmune()) {
|
if (IsImmune() || IsCooldownImmune()) {
|
||||||
|
LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID()); //Immune is succesfully proc'd
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ public:
|
|||||||
DestroyableComponent(Entity* parentEntity);
|
DestroyableComponent(Entity* parentEntity);
|
||||||
~DestroyableComponent() override;
|
~DestroyableComponent() override;
|
||||||
|
|
||||||
|
void Update(float deltaTime) override;
|
||||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||||
@ -166,6 +167,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool IsImmune() const;
|
bool IsImmune() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this entity is currently immune to attacks due to a damage cooldown period
|
||||||
|
*/
|
||||||
|
bool IsCooldownImmune() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets if this entity has GM immunity, making it not killable
|
* Sets if this entity has GM immunity, making it not killable
|
||||||
* @param value the GM immunity of this entity
|
* @param value the GM immunity of this entity
|
||||||
@ -416,8 +422,13 @@ public:
|
|||||||
const bool GetImmuneToQuickbuildInterrupt() { return m_ImmuneToQuickbuildInterruptCount > 0; };
|
const bool GetImmuneToQuickbuildInterrupt() { return m_ImmuneToQuickbuildInterruptCount > 0; };
|
||||||
const bool GetImmuneToPullToPoint() { return m_ImmuneToPullToPointCount > 0; };
|
const bool GetImmuneToPullToPoint() { return m_ImmuneToPullToPointCount > 0; };
|
||||||
|
|
||||||
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
// Damage cooldown setters/getters
|
||||||
|
void SetDamageCooldownTimer(float value) { m_DamageCooldownTimer = value; }
|
||||||
|
float GetDamageCooldownTimer() { return m_DamageCooldownTimer; }
|
||||||
|
|
||||||
|
// Death behavior setters/getters
|
||||||
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
||||||
|
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to reset all stats to the default stats based on items and completed missions
|
* Utility to reset all stats to the default stats based on items and completed missions
|
||||||
@ -605,6 +616,11 @@ private:
|
|||||||
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
||||||
*/
|
*/
|
||||||
int32_t m_DeathBehavior;
|
int32_t m_DeathBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage immunity cooldown timer. Set to a value that then counts down to create a damage cooldown for players
|
||||||
|
*/
|
||||||
|
float m_DamageCooldownTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DESTROYABLECOMPONENT_H
|
#endif // DESTROYABLECOMPONENT_H
|
||||||
|
@ -1490,6 +1490,24 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Testing basic attack immunity
|
||||||
|
if (chatCommand == "attackimmune" && args.size() >= 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||||
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
|
int32_t state = false;
|
||||||
|
|
||||||
|
if (!GeneralUtils::TryParse(args[0], state)) {
|
||||||
|
ChatPackets::SendSystemMessage(sysAddr, u"Invalid state.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destroyableComponent != nullptr) {
|
||||||
|
destroyableComponent->SetIsImmune(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (chatCommand == "buff" && args.size() >= 2 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
if (chatCommand == "buff" && args.size() >= 2 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||||
auto* buffComponent = entity->GetComponent<BuffComponent>();
|
auto* buffComponent = entity->GetComponent<BuffComponent>();
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|ban|`/ban <username>`|Bans a user from the server.|4|
|
|ban|`/ban <username>`|Bans a user from the server.|4|
|
||||||
|approveproperty|`/approveproperty`|Approves the property the player is currently visiting.|5|
|
|approveproperty|`/approveproperty`|Approves the property the player is currently visiting.|5|
|
||||||
|mute|`/mute <username> (days) (hours)`|Mute player for the given amount of time. If no time is given, the mute is indefinite.|6|
|
|mute|`/mute <username> (days) (hours)`|Mute player for the given amount of time. If no time is given, the mute is indefinite.|6|
|
||||||
|
|attackimmune|`/attackimmune <value>`|Sets the character's immunity to basic attacks state, where value can be one of "1", to make yourself immune to basic attack damage, or "0" to undo.|8|
|
||||||
|gmimmune|`/gmimmunve <value>`|Sets the character's GMImmune state, where value can be one of "1", to make yourself immune to damage, or "0" to undo.|8|
|
|gmimmune|`/gmimmunve <value>`|Sets the character's GMImmune state, where value can be one of "1", to make yourself immune to damage, or "0" to undo.|8|
|
||||||
|gminvis|`/gminvis`|Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8.|8|
|
|gminvis|`/gminvis`|Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8.|8|
|
||||||
|setname|`/setname <name>`|Sets a temporary name for your player. The name resets when you log out.|8|
|
|setname|`/setname <name>`|Sets a temporary name for your player. The name resets when you log out.|8|
|
||||||
|
@ -536,3 +536,22 @@ TEST_F(DestroyableTest, DestroyableComponentImmunityTest) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the Damage cooldown timer of DestroyableComponent
|
||||||
|
*/
|
||||||
|
TEST_F(DestroyableTest, DestroyableComponentDamageCooldownTest) {
|
||||||
|
// Test the damage immune timer state (anything above 0.0f)
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(1.0f);
|
||||||
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 1.0f);
|
||||||
|
ASSERT_TRUE(destroyableComponent->IsCooldownImmune());
|
||||||
|
|
||||||
|
// Test that the Update() function correctly decrements the damage cooldown timer
|
||||||
|
destroyableComponent->Update(0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.5f);
|
||||||
|
ASSERT_TRUE(destroyableComponent->IsCooldownImmune());
|
||||||
|
|
||||||
|
// Test the non damage immune timer state (anything below or equal to 0.0f)
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f);
|
||||||
|
ASSERT_FALSE(destroyableComponent->IsCooldownImmune());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user