mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
Every hit started both the rapid fire shooter and the rain of fire timers, from stage 1 on, so the two specials ran in every stage and on top of each other, each turning the boss's AI off and on again under the other's animations. They also wrote the "stoppedFlag" that the no-players-around attack stop uses, which could leave her stopped for good. As the live script: after she comes back down, a skill manager fires the rapid fire shooter in stage 2 and the rain of fire in stage 3 only, again 10 to 15 s after each ends; for 3.1 s after her melee smash (skill 322) a due special waits and fires when the smash ends. The rain of fire keeps her from attacking until its last impact. Check in game: Spider Queen fight: no specials before the first spiderling wave; stage 2 only rapid fire, stage 3 only rain of fire; she never freezes in the smash animation and keeps attacking after each special. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
4.3 KiB
C++
121 lines
4.3 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include "GameDependencies.h"
|
|
#include "Entity.h"
|
|
#include "ObjectMessages.h"
|
|
#include "dGameMessagesTests/GameMessageTestUtils.h"
|
|
|
|
#include "02_server/Enemy/AG/BossSpiderQueenEnemyServer.h"
|
|
#include "02_server/Map/Property/AG_Small/ZoneAgProperty.h"
|
|
|
|
// The AG property's Spider Queen fight (L_ZONE_AG_PROPERTY.lua, L_BOSS_SPIDER_QUEEN_ENEMY_SERVER.lua).
|
|
|
|
using namespace GameMessageTestUtils;
|
|
|
|
class SpiderQueenTest : public GameDependenciesTest {
|
|
protected:
|
|
static constexpr LWOOBJID ZONE = 70368744177662;
|
|
static constexpr LWOOBJID BOSS = 0x1000;
|
|
static constexpr LWOOBJID LAND_TARGET = 0x1001;
|
|
static constexpr LWOOBJID SCREAM_EMITTER = 0x1002;
|
|
static constexpr LWOOBJID SPIDERLING = 0x1003;
|
|
|
|
std::vector<std::unique_ptr<Entity>> entities;
|
|
Entity* zone = nullptr;
|
|
Entity* boss = nullptr;
|
|
ZoneAgProperty zoneScript;
|
|
BossSpiderQueenEnemyServer bossScript;
|
|
|
|
void SetUp() override {
|
|
SetUpDependencies();
|
|
zone = Add(ZONE, 0, {});
|
|
boss = Add(BOSS, 14381, { "SpiderBoss" });
|
|
zoneScript.SetGameVariables(zone);
|
|
}
|
|
|
|
void TearDown() override {
|
|
for (const auto& entity : entities) Game::entityManager->_removeEntity(entity->GetObjectID());
|
|
entities.clear();
|
|
TearDownDependencies();
|
|
}
|
|
|
|
Entity* Add(const LWOOBJID id, const LOT lot, const std::vector<std::string>& groups) {
|
|
info.lot = lot;
|
|
auto* const entity = entities.emplace_back(std::make_unique<Entity>(id, info)).get();
|
|
entity->GetGroups() = groups;
|
|
Game::entityManager->_addEntity(entity);
|
|
return entity;
|
|
}
|
|
};
|
|
|
|
TEST_F(SpiderQueenTest, ZoneHandsTheBossItsLandingTargetAndScreamEmitter) {
|
|
Add(LAND_TARGET, 14382, { "Land_Target" });
|
|
Add(SCREAM_EMITTER, 14383, { "Spider_Scream" });
|
|
|
|
zoneScript.BaseOnFireEventServerSide(zone, boss, "RetrieveZoneData");
|
|
|
|
EXPECT_EQ(zone->GetVar<LWOOBJID>(u"SpiderBossID"), BOSS);
|
|
EXPECT_EQ(boss->GetVar<LWOOBJID>(u"LandingTarget"), LAND_TARGET);
|
|
EXPECT_EQ(boss->GetVar<LWOOBJID>(u"ScreamEmitter"), SCREAM_EMITTER);
|
|
zone->Update(0.0f);
|
|
EXPECT_FALSE(zone->HasTimer("ProcessGroupObj_LandingTarget"));
|
|
EXPECT_FALSE(zone->HasTimer("ProcessGroupObj_ScreamEmitter"));
|
|
}
|
|
|
|
TEST_F(SpiderQueenTest, ZoneLooksAgainWhileTheObjectsAreNotSpawned) {
|
|
zoneScript.BaseOnFireEventServerSide(zone, boss, "RetrieveZoneData");
|
|
|
|
EXPECT_EQ(boss->GetVar<LWOOBJID>(u"LandingTarget"), LWOOBJID_EMPTY);
|
|
zone->Update(0.0f); // starts the pending timers
|
|
EXPECT_TRUE(zone->HasTimer("ProcessGroupObj_LandingTarget"));
|
|
EXPECT_TRUE(zone->HasTimer("ProcessGroupObj_ScreamEmitter"));
|
|
|
|
Add(SCREAM_EMITTER, 14383, { "Spider_Scream" });
|
|
zoneScript.OnTimerDone(zone, "ProcessGroupObj_ScreamEmitter");
|
|
EXPECT_EQ(boss->GetVar<LWOOBJID>(u"ScreamEmitter"), SCREAM_EMITTER);
|
|
}
|
|
|
|
TEST_F(SpiderQueenTest, ASpiderlingDeathMakesTheBossScreamFromTheMountain) {
|
|
boss->SetVar<LWOOBJID>(u"ScreamEmitter", SCREAM_EMITTER);
|
|
auto* const spiderling = Add(SPIDERLING, 16197, { "BabySpider" });
|
|
|
|
const auto packets = Capture([&] { bossScript.OnNotifyObject(boss, spiderling, "SpiderlingDied", 0, 0); });
|
|
const auto sent = SentGameMessages<GameMessages::NotifyClientObject>(packets);
|
|
ASSERT_EQ(sent.size(), 1u);
|
|
EXPECT_EQ(sent[0].target, BOSS);
|
|
EXPECT_EQ(sent[0].name, u"EmitScream");
|
|
EXPECT_EQ(sent[0].paramObj, SCREAM_EMITTER);
|
|
EXPECT_TRUE(packets[0].broadcast);
|
|
}
|
|
|
|
TEST_F(SpiderQueenTest, OnlySpiderlingsMakeTheBossScream) {
|
|
auto* const other = Add(SPIDERLING, 16196, {});
|
|
const auto packets = Capture([&] { bossScript.OnNotifyObject(boss, other, "SpiderlingDied", 0, 0); });
|
|
EXPECT_TRUE(packets.empty());
|
|
}
|
|
|
|
TEST_F(SpiderQueenTest, TheMeleeSmashLocksTheSpecialsForItsLength) {
|
|
bossScript.OnSkillCast(boss, 303);
|
|
EXPECT_FALSE(boss->GetBoolean(u"bSpecialLock"));
|
|
|
|
bossScript.OnSkillCast(boss, 322);
|
|
EXPECT_TRUE(boss->GetBoolean(u"bSpecialLock"));
|
|
boss->Update(0.0f);
|
|
EXPECT_TRUE(boss->HasTimer("UnlockSpecials"));
|
|
|
|
// A special due during the smash waits for it
|
|
bossScript.SpiderSkillManager(boss, true);
|
|
EXPECT_TRUE(boss->GetBoolean(u"bSpecialQueued"));
|
|
|
|
bossScript.OnTimerDone(boss, "UnlockSpecials");
|
|
EXPECT_FALSE(boss->GetBoolean(u"bSpecialLock"));
|
|
EXPECT_FALSE(boss->GetBoolean(u"bSpecialQueued"));
|
|
}
|
|
|
|
TEST_F(SpiderQueenTest, NoSpecialsBeforeTheFirstWave) {
|
|
// Stage 1 has no special attack; the skill manager does nothing
|
|
const auto packets = Capture([&] { bossScript.SpiderSkillManager(boss, true); });
|
|
EXPECT_TRUE(packets.empty());
|
|
EXPECT_FALSE(boss->GetBoolean(u"isSpecialAttacking"));
|
|
}
|