Files
DarkflameServer/tests/dGameTests/SpiderQueenTests.cpp
Aaron Kimbrell 4900f11cf1 fix(scripts): the Spider Queen screams from the mountain when a spiderling dies (issue 307)
The zone script never handed the boss her landing target and scream emitter: ZoneAgProperty::ProcessGroupObjects
was empty and nothing answered the boss client script's "QueryZoneScript" event. As the live scripts: the boss asks
the zone ("RetrieveZoneData"), the zone stores the first object of Land_Target and Spider_Scream on her as
LandingTarget and ScreamEmitter (looking again every 0.3 s until they are spawned), and each spiderling death sends
NotifyClientObject "EmitScream" with the emitter, which the boss's client script plays as the scream. The landing
skill and camera shake now come from the landing target, not the boss.

Check in game: AG Spider Queen (property or instance): kill a spiderling: the scream plays from the mountain; when
she comes back down, the landing blast hits around the landing spot and the camera shakes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:52:54 -05:00

96 lines
3.4 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());
}