From beed2651e59ed4fedc0a9d4be4d62e10069655d0 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 10 Nov 2026 16:57:15 -0600 Subject: [PATCH] feat(scripts): L_SIMPLE_MOVER_SWITCH, the platforms of the NJ attic switches The script (with L_BASE_SWITCH_ACTIVATED_SERVER) asks the switches in the platform's switchGroup to tell it when they are activated and deactivated, and sends the platform to waypoint 1 on the one and back to waypoint 0 on the other. The NJ monastery's ice and lightning attic platforms (LOTs 14539 and 14376) run it. A switch can now be asked for that (SwitchComponent:: AddActivationCallback). Where live's switch tells its group that it has loaded, the platform here looks for its switches again each second until it has found one. The script was on the list of scripts left out, with a note that its platform does not exist; it is taken off the list. Co-Authored-By: Claude Fable 5.2 --- dGame/dComponents/SwitchComponent.cpp | 2 + dGame/dComponents/SwitchComponent.h | 9 +++++ dScripts/02_server/Map/General/CMakeLists.txt | 1 + .../Map/General/SimpleMoverSwitch.cpp | 37 +++++++++++++++++++ .../02_server/Map/General/SimpleMoverSwitch.h | 13 +++++++ dScripts/CppScripts.cpp | 3 +- 6 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 dScripts/02_server/Map/General/SimpleMoverSwitch.cpp create mode 100644 dScripts/02_server/Map/General/SimpleMoverSwitch.h diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 7c38fba11..205491cfc 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -77,6 +77,7 @@ void SwitchComponent::EntityEnter(Entity* entity) { m_Active = true; if (!m_Parent) return; m_Parent->TriggerEvent(eTriggerEventType::ACTIVATED, entity); + for (const auto& callback : std::vector(m_ActivationCallbacks)) callback(true); const auto grpName = m_Parent->GetVarAsString(u"grp_name"); @@ -114,6 +115,7 @@ void SwitchComponent::Run(float deltaTime) { m_Active = false; if (!m_Parent) return; m_Parent->TriggerEvent(eTriggerEventType::DEACTIVATED, m_Parent); + for (const auto& callback : std::vector(m_ActivationCallbacks)) callback(false); const auto grpName = m_Parent->GetVarAsString(u"grp_name"); diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index 88763647e..b7d9aa0cb 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -8,6 +8,7 @@ #include "QuickBuildComponent.h" #include "BouncerComponent.h" #include +#include #include "Component.h" #include "eReplicaComponentType.h" @@ -72,6 +73,12 @@ public: */ static SwitchComponent* GetClosestSwitch(NiPoint3 position); + /** + * Calls back when the switch is activated (true) and when it is deactivated (false): what an object's script asks + * a switch for with a notification request for ObjectActivated and ObjectDeactivated + */ + void AddActivationCallback(const std::function& callback) { m_ActivationCallbacks.push_back(callback); } + const std::vector& GetFactionsToRespondTo() const { return m_FactionsToRespondTo; } @@ -113,6 +120,8 @@ private: BouncerComponent* m_PetBouncer = nullptr; std::vector m_FactionsToRespondTo{}; + + std::vector> m_ActivationCallbacks; }; #endif // SWITCHCOMPONENT_H diff --git a/dScripts/02_server/Map/General/CMakeLists.txt b/dScripts/02_server/Map/General/CMakeLists.txt index 221faebea..cb07588fe 100644 --- a/dScripts/02_server/Map/General/CMakeLists.txt +++ b/dScripts/02_server/Map/General/CMakeLists.txt @@ -15,6 +15,7 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL "PropertyPlatform.cpp" "QbEnemyStunner.cpp" "QbSpawner.cpp" + "SimpleMoverSwitch.cpp" "SimpleOnceOnlyMoverSpinnerEvent.cpp" "StoryBoxInteractServer.cpp" "TokenConsoleServer.cpp" diff --git a/dScripts/02_server/Map/General/SimpleMoverSwitch.cpp b/dScripts/02_server/Map/General/SimpleMoverSwitch.cpp new file mode 100644 index 000000000..868993735 --- /dev/null +++ b/dScripts/02_server/Map/General/SimpleMoverSwitch.cpp @@ -0,0 +1,37 @@ +#include "SimpleMoverSwitch.h" + +#include "Entity.h" +#include "EntityManager.h" +#include "MovingPlatformComponent.h" +#include "SwitchComponent.h" + +void SimpleMoverSwitch::OnStartup(Entity* self) { + RequestSwitches(self); +} + +// switchStartup: every switch in the group is asked for its ObjectActivated (and from then on, in turn, its +// ObjectDeactivated and ObjectActivated). A switch loaded after the platform tells its group so in live +// (objectTypeLoaded) and is asked then; here the platform looks again until it has found one. +void SimpleMoverSwitch::RequestSwitches(Entity* self) { + const auto group = self->GetVarAsString(u"switchGroup"); + if (group.empty()) return; + + bool found = false; + for (auto* const object : Game::entityManager->GetEntitiesInGroup(group)) { + if (object == self) continue; + auto* const switchComponent = object->GetComponent(); + if (!switchComponent) continue; + found = true; + switchComponent->AddActivationCallback([platformID = self->GetObjectID()](const bool active) { + auto* const platformObject = Game::entityManager->GetEntity(platformID); + if (!platformObject) return; + // notifyObjectActivated: GoToWaypoint{ iPathIndex = 1 }; notifyObjectDeactivated: GoToWaypoint{ iPathIndex = 0 } + if (auto* const platform = platformObject->GetComponent()) platform->GotoWaypoint(active ? 1 : 0); + }); + } + if (!found) self->AddTimer("FindSwitch", 1.0f); +} + +void SimpleMoverSwitch::OnTimerDone(Entity* self, std::string timerName) { + if (timerName == "FindSwitch") RequestSwitches(self); +} diff --git a/dScripts/02_server/Map/General/SimpleMoverSwitch.h b/dScripts/02_server/Map/General/SimpleMoverSwitch.h new file mode 100644 index 000000000..7ac01bf44 --- /dev/null +++ b/dScripts/02_server/Map/General/SimpleMoverSwitch.h @@ -0,0 +1,13 @@ +#pragma once +#include "CppScripts.h" + +// L_SIMPLE_MOVER_SWITCH.lua with L_BASE_SWITCH_ACTIVATED_SERVER.lua: a moving platform that a switch sends to and fro +// (the NJ monastery's ice and lightning attic puzzles). It asks the switches in its switchGroup to tell it when they +// are activated and deactivated, and goes to waypoint 1 on the one and back to waypoint 0 on the other. +class SimpleMoverSwitch : public CppScripts::Script { +public: + void OnStartup(Entity* self) override; + void OnTimerDone(Entity* self, std::string timerName) override; +private: + void RequestSwitches(Entity* self); +}; diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 645792ad5..b927ab6df 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -272,6 +272,7 @@ #include "NjNPCMissionSpinjitzuServer.h" #include "NjJayMissionItems.h" #include "FallingTile.h" +#include "SimpleMoverSwitch.h" #include "SimpleOnceOnlyMoverSpinnerEvent.h" #include "ImaginationShrineServer.h" #include "Lieutenant.h" @@ -680,6 +681,7 @@ 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\\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();}}, {"scripts\\02_server\\Map\\njhub\\L_IMAGINATION_SHRINE_SERVER.lua", []() {return new ImaginationShrineServer();}}, @@ -814,7 +816,6 @@ namespace { "scripts\\ai\\GENERAL\\L_NPC_GENERIC_WANDER_SMALL.lua", // Really old alpha script "scripts\\zone\\LUPs\\Moonbase Intro\\WBL_Enemy_Grabbler.lua", // Really old alpha script, only sets wander variables "scripts\\ai\\NP\\L_NPC_NP_OLD_MAN_SHERLAND.lua", // This NPC doesn't even exist in modern crux, the only place this is used... - "scripts\\02_server\\Map\\General\\L_SIMPLE_MOVER_SWITCH.lua", // This platform does not exist even when moved manually on a client }; };