mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat(combat): dead enemies are stunned right after Die
Live sent SetStunned to every dead object with a combat AI in the packet right after its Die: 7,098 of 7,105 such deaths (7,424 decoded stuns, all the same: no originator, push, can't attack, move or turn, ignore immunity). Objects without combat AI (smashables, 9,393 deaths) never got one. DLU sent none, so a dying enemy could keep turning or start an attack while its death plays. Check: kill enemies mid-attack and while they chase you; they stop moving, turning and attacking the moment they die, and the death and loot still play normally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -764,6 +764,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
die.lootOwnerID = source;
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
// Live stunned every dead enemy right after its Die (7,098 of 7,105 deaths of objects with combat AI): no
|
||||
// originator, push, can't attack, move or turn, ignoring immunity. Nothing else that dies gets it.
|
||||
if (m_Parent->HasComponent(eReplicaComponentType::BASE_COMBAT_AI)) {
|
||||
GameMessages::SetStunned stun;
|
||||
stun.target = m_Parent->GetObjectID();
|
||||
stun.StateChangeType = eStateChangeType::PUSH;
|
||||
stun.bCantAttack = true;
|
||||
stun.bCantMove = true;
|
||||
stun.bCantTurn = true;
|
||||
stun.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
// Live told the killer right after Die, before the loot: EnemiesSmashed for an NPC, SmashablesSmashed for another
|
||||
// smashable. Racing counts its own (RacingSmashablesSmashed).
|
||||
if (owner && owner != m_Parent) {
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
#include "eStateChangeType.h"
|
||||
#include "Character.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "dGameMessagesTests/GameMessageTestUtils.h"
|
||||
#include "PacketTestUtils.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
@@ -666,3 +670,39 @@ TEST_F(DestroyableTest, SmashWithoutAttackHasNoDirection) {
|
||||
EXPECT_EQ(die->directionRelative_AngleY, 0.0f);
|
||||
EXPECT_EQ(die->directionRelative_Force, 0.0f);
|
||||
}
|
||||
|
||||
// Live stunned every dead enemy in the packet right after its Die; bytes are a live SetStunned with the ID replaced.
|
||||
TEST_F(DestroyableTest, DeadEnemyIsStunnedAfterDie) {
|
||||
if (!CDClientDatabase::isConnected) {
|
||||
CDClientDatabase::Connect(":memory:");
|
||||
for (const auto* table : {
|
||||
"ComponentsRegistry (id INTEGER, component_type INTEGER, component_id INTEGER)",
|
||||
"BaseCombatAIComponent (id INTEGER, aggroRadius REAL, tetherSpeed REAL, pursuitSpeed REAL, softTetherRadius REAL, hardTetherRadius REAL, minRoundLength REAL, maxRoundLength REAL, combatRoundLength REAL)",
|
||||
"ObjectSkills (objectTemplate INTEGER, skillID INTEGER, castOnType INTEGER, AICombatWeight INTEGER)",
|
||||
"SkillBehavior (skillID INTEGER, behaviorID INTEGER)",
|
||||
}) {
|
||||
CDClientDatabase::ExecuteDML(std::string("CREATE TABLE ") + table + ";");
|
||||
}
|
||||
}
|
||||
Entity enemy(0x0102030405060708LL, info);
|
||||
enemy.AddComponent<BaseCombatAIComponent>(-1);
|
||||
auto* destroyable = enemy.AddComponent<DestroyableComponent>(-1);
|
||||
destroyable->SetMaxHealth(4.0f);
|
||||
destroyable->SetHealth(4);
|
||||
|
||||
const auto sent = PacketTestUtils::Capture([&] { destroyable->Smash(LWOOBJID_EMPTY); });
|
||||
const auto ids = GameMessageTestUtils::SentGameMessageIds(sent);
|
||||
const auto die = std::find(ids.begin(), ids.end(), MessageType::Game::DIE);
|
||||
ASSERT_NE(die, ids.end());
|
||||
ASSERT_NE(die + 1, ids.end());
|
||||
EXPECT_EQ(*(die + 1), MessageType::Game::SET_STUNNED);
|
||||
|
||||
const auto stuns = GameMessageTestUtils::SentGameMessages<GameMessages::SetStunned>(sent);
|
||||
ASSERT_EQ(stuns.size(), 1u);
|
||||
EXPECT_PACKET_EQ(PacketTestUtils::FromHex("53 05 00 0c 00 00 00 00 08 07 06 05 04 03 02 01 c6 00 00 00 00 00 40 50 80", 193), GameMessageTestUtils::StructPacket(stuns[0]));
|
||||
}
|
||||
|
||||
TEST_F(DestroyableTest, DeadSmashableIsNotStunned) {
|
||||
const auto sent = PacketTestUtils::Capture([&] { destroyableComponent->Smash(LWOOBJID_EMPTY); });
|
||||
EXPECT_TRUE(GameMessageTestUtils::SentGameMessages<GameMessages::SetStunned>(sent).empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user