feat(scripts): L_FIRE_TRANS_ROCKS and its manager, the rocks of the NJ fire transition

L_FIRE_TRANS_ROCKS_MANAGER waits until the rocks of its RockGroup are all
there (NumberOfRocks, 3), sends them to waypoint 1 five seconds later,
and each time all of them have told it they are back down sends them up
again after a second. L_FIRE_TRANS_ROCKS holds a rock that arrived at
waypoint 1 for 3.5 s and sends it back to waypoint 0, where it tells the
manager (PlatformDown). Both act on ArrivedAtDesiredWaypoint and
GoToWaypoint, which the mover has.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-11-10 16:57:15 -06:00
parent beed2651e5
commit 4e5fb276d6
7 changed files with 148 additions and 0 deletions

View File

@@ -5,6 +5,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB
"CavePrisonCage.cpp"
"EnemySkeletonSpawner.cpp"
"FallingTile.cpp"
"FireTransRocks.cpp"
"FireTransRocksManager.cpp"
"FlameJetServer.cpp"
"LightningOrbServer.cpp"
"ImaginationShrineServer.cpp"

View File

@@ -0,0 +1,24 @@
#include "FireTransRocks.h"
#include "Entity.h"
#include "EntityManager.h"
#include "MovingPlatformComponent.h"
void FireTransRocks::OnStartup(Entity* self) {
if (!self->HasVar(u"RockManagerGroup")) self->SetVar<std::u16string>(u"RockManagerGroup", u"FireTransRocksManager");
}
void FireTransRocks::OnArrivedAtDesiredWaypoint(Entity* self, const uint32_t waypointIndex) {
if (waypointIndex == 1) {
self->AddTimer("PlatHold", 3.5f);
return;
}
// The first manager there is hears that the rock is down
const auto managers = Game::entityManager->GetEntitiesInGroup(self->GetVarAsString(u"RockManagerGroup"));
if (!managers.empty()) managers.front()->GetScript()->OnNotifyObject(managers.front(), self, "PlatformDown");
}
void FireTransRocks::OnTimerDone(Entity* self, std::string timerName) {
if (timerName != "PlatHold") return;
if (auto* const platform = self->GetComponent<MovingPlatformComponent>()) platform->GotoWaypoint(0);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "CppScripts.h"
// L_FIRE_TRANS_ROCKS.lua: a rock in the NJ monastery's fire transition, a mover between its bottom (waypoint 0) and its
// top (waypoint 1). At the top it holds 3.5 s and goes back down; at the bottom it tells the rocks' manager.
class FireTransRocks : public CppScripts::Script {
public:
void OnStartup(Entity* self) override;
void OnArrivedAtDesiredWaypoint(Entity* self, uint32_t waypointIndex) override;
void OnTimerDone(Entity* self, std::string timerName) override;
};

View File

@@ -0,0 +1,53 @@
#include "FireTransRocksManager.h"
#include <algorithm>
#include "Entity.h"
#include "EntityManager.h"
#include "MovingPlatformComponent.h"
void FireTransRocksManager::OnStartup(Entity* self) {
self->AddTimer("check", 5.0f);
if (!self->HasVar(u"RockGroup")) self->SetVar<std::u16string>(u"RockGroup", u"FireTransRocks");
if (!self->HasVar(u"NumberOfRocks")) self->SetVar<int32_t>(u"NumberOfRocks", 3);
}
void FireTransRocksManager::CheckForRocks(Entity* self) {
const auto rocks = Game::entityManager->GetEntitiesInGroup(self->GetVarAsString(u"RockGroup"));
// Not all there yet: look again in a second
if (static_cast<int32_t>(rocks.size()) < self->GetVarAs<int32_t>(u"NumberOfRocks")) {
self->AddTimer("check", 1.0f);
return;
}
self->AddTimer("StartPlatforms", 5.0f);
}
void FireTransRocksManager::OnNotifyObject(Entity* self, Entity* sender, const std::string& name, int32_t param1, int32_t param2) {
if (name != "PlatformDown" || !sender) return;
auto down = self->GetVar<std::vector<LWOOBJID>>(u"PlatTable");
if (std::ranges::find(down, sender->GetObjectID()) != down.end()) return;
down.push_back(sender->GetObjectID());
if (static_cast<int32_t>(down.size()) < self->GetVarAs<int32_t>(u"NumberOfRocks")) {
self->SetVar<std::vector<LWOOBJID>>(u"PlatTable", down);
return;
}
// All of them are down
self->AddTimer("restartPlatforms", 1.0f);
}
void FireTransRocksManager::SendRocksUp(Entity* self) {
for (auto* const rock : Game::entityManager->GetEntitiesInGroup(self->GetVarAsString(u"RockGroup"))) {
if (auto* const platform = rock->GetComponent<MovingPlatformComponent>()) platform->GotoWaypoint(1);
}
}
void FireTransRocksManager::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "check") {
CheckForRocks(self);
} else if (timerName == "StartPlatforms") {
SendRocksUp(self);
} else if (timerName == "restartPlatforms") {
self->SetVar<std::vector<LWOOBJID>>(u"PlatTable", {});
SendRocksUp(self);
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "CppScripts.h"
// L_FIRE_TRANS_ROCKS_MANAGER.lua: the manager of the rocks in the NJ monastery's fire transition. Once all the rocks of
// its RockGroup are there (NumberOfRocks, 3) it sends them up 5 s later, and each time all of them are back down it
// sends them up again after 1 s.
class FireTransRocksManager : public CppScripts::Script {
public:
void OnStartup(Entity* self) override;
void OnNotifyObject(Entity* self, Entity* sender, const std::string& name, int32_t param1 = 0, int32_t param2 = 0) override;
void OnTimerDone(Entity* self, std::string timerName) override;
private:
void CheckForRocks(Entity* self);
void SendRocksUp(Entity* self);
};

View File

@@ -272,6 +272,8 @@
#include "NjNPCMissionSpinjitzuServer.h"
#include "NjJayMissionItems.h"
#include "FallingTile.h"
#include "FireTransRocks.h"
#include "FireTransRocksManager.h"
#include "SimpleMoverSwitch.h"
#include "SimpleOnceOnlyMoverSpinnerEvent.h"
#include "ImaginationShrineServer.h"
@@ -681,6 +683,8 @@ namespace {
{"scripts\\02_server\\Map\\General\\Ninjago\\L_RAIL_POST_SERVER.lua", []() {return new NjRailPostServer();}},
{"scripts\\02_server\\Map\\General\\Ninjago\\L_ICE_RAIL_ACTIVATOR_SERVER.lua", []() {return new NjIceRailActivator();}},
{"scripts\\02_server\\Map\\njhub\\L_FALLING_TILE.lua", []() {return new FallingTile();}},
{"scripts\\02_server\\Map\\njhub\\L_FIRE_TRANS_ROCKS.lua", []() {return new FireTransRocks();}},
{"scripts\\02_server\\Map\\njhub\\L_FIRE_TRANS_ROCKS_MANAGER.lua", []() {return new FireTransRocksManager();}},
{"scripts\\02_server\\Map\\General\\L_SIMPLE_MOVER_SWITCH.lua", []() {return new SimpleMoverSwitch();}},
{"scripts\\02_server\\Map\\firetemple\\L_SIMPLE_ONCE_ONLY_MOVER_SPINNER_EVENT.lua", []() {return new SimpleOnceOnlyMoverSpinnerEvent();}},
{"scripts\\02_server\\Enemy\\General\\L_ENEMY_NJ_BUFF_STUN_IMMUNITY.lua", []() {return new EnemyNjBuff();}},

View File

@@ -9,6 +9,7 @@
#include "CDClientDatabase.h"
#include "Entity.h"
#include "GameMessages.h"
#include "02_server/Map/njhub/FireTransRocks.h"
#include "LDFFormat.h"
#include "MovementMessages.h"
#include "MovingPlatformComponent.h"
@@ -643,3 +644,41 @@ TEST_F(MovingPlatformComponentTest, OnceOnlyMoverGoesOnItsSpinnersEvent) {
EXPECT_EQ(mover->mState, 2u);
EXPECT_EQ(mover->mCurrentWaypointIndex, 0u);
}
// L_FIRE_TRANS_ROCKS: a rock that arrived at its top (waypoint 1) holds there (the PlatHold timer) and is then sent
// back down to waypoint 0
TEST_F(MovingPlatformComponentTest, FireTransRockGoesBackDownAfterItsHold) {
Path path;
path.pathType = PathType::MovingPlatform;
path.pathName = "TestRockPath";
path.pathBehavior = PathBehavior::Once;
PathWaypoint first;
first.position = NiPoint3(0.0f, 0.0f, 0.0f);
first.speed = 5.0f;
PathWaypoint second = first;
second.position = NiPoint3(0.0f, 10.0f, 0.0f);
path.pathWaypoints = { first, second };
const_cast<Zone*>(Game::zoneManager->GetZone())->AddPath(path);
info.settings.Insert<bool>(u"startPathingOnLoad", false);
Entity entity(288300744895900119, info);
info.settings.values.clear();
auto* const platform = entity.AddComponent<MovingPlatformComponent>(-1, "TestRockPath");
auto* const mover = platform->GetMoverSubComponent();
FireTransRocks script;
script.OnStartup(&entity);
EXPECT_EQ(entity.GetVarAsString(u"RockManagerGroup"), "FireTransRocksManager");
platform->GotoWaypoint(1);
platform->Run(0.11f);
platform->Run(2.1f);
EXPECT_EQ(mover->mState, 28u);
EXPECT_EQ(mover->mCurrentWaypointIndex, 1u);
script.OnTimerDone(&entity, "PlatHold");
EXPECT_EQ(mover->mDesiredWaypointIndex, 0);
platform->Run(0.11f);
platform->Run(2.1f);
EXPECT_EQ(mover->mState, 28u);
EXPECT_EQ(mover->mCurrentWaypointIndex, 0u);
}