fix: a rail that is off or whose preconditions fail is not ridden

LWORailActivatorComponent::msgCheckUseRequirements (0x00c2c5b0) refuses a
use, after the quick build check, when the row's preconditions fail or
rail_activator_active is off, so a client does not ask. The server started
the rail on any use request. The rows ask for an equipped item (44) or a
completed mission (283 to 286).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-11-10 15:14:44 -06:00
parent dc5f13cdbc
commit b033655e90
3 changed files with 37 additions and 0 deletions

View File

@@ -9,6 +9,7 @@
#include "CombatMessages.h"
#include "MovementMessages.h"
#include "QuickBuildComponent.h"
#include "Preconditions.h"
#include "Game.h"
#include "Logger.h"
#include "RenderComponent.h"
@@ -42,6 +43,7 @@ void RailActivatorComponent::LoadDataFromTemplate() {
m_ShowNameBillboard = tableData.showNameBillboard;
m_CameraLocked = tableData.cameraLocked;
m_CollisionEnabled = tableData.playerCollision;
m_Preconditions = tableData.preconditions;
}
// The level config: a key, when there, replaces the row's value, as StartRailMovement's flags replace the row's in
@@ -71,6 +73,11 @@ bool RailActivatorComponent::OnRequestUse(GameMessages::RequestUseEvent& event)
if (quickBuildComponent != nullptr && quickBuildComponent->GetState() != eQuickBuildState::COMPLETED)
return true;
// LWORailActivatorComponent::msgCheckUseRequirements (0x00c2c5b0): after the quick build, the row's preconditions
// have to pass and the rail has to be on; the client does not send a use otherwise ("-1": the table has none)
if (!m_Active) return true;
if (!m_Preconditions.empty() && m_Preconditions != "-1" && !PreconditionExpression(m_Preconditions).Check(originator)) return true;
if (quickBuildComponent != nullptr) {
// Don't want it to be destroyed while a player is using it
quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() + 10.0f);

View File

@@ -46,6 +46,9 @@ public:
* Whether the rail can be used (level key rail_activator_active; every rail in the live levels has it set)
*/
bool GetActive() const { return m_Active; }
// The players that used the rail and have not left it yet
const std::vector<LWOOBJID>& GetEntitiesOnRail() const { return m_EntitiesOnRail; }
bool GetNoAggro() const { return m_NoAggro; }
bool GetShowNameBillboard() const { return m_ShowNameBillboard; }
private:
@@ -131,6 +134,11 @@ private:
*/
bool m_UseDB;
/**
* The row's preconditions a player has to pass to use the rail
*/
std::string m_Preconditions;
/**
* Client flag
*/

View File

@@ -5,6 +5,7 @@
#include "CDRailActivatorComponent.h"
#include "Entity.h"
#include "MovementMessages.h"
#include "ObjectMessages.h"
#include "dGameMessagesTests/GameMessageTestUtils.h"
#include "RailActivatorComponent.h"
@@ -82,3 +83,24 @@ TEST_F(RailActivatorComponentTests, InactiveRailIsReportedInactive) {
notify.bActive = false;
EXPECT_FALSE(RoundTrip(notify).bActive);
}
// A rail that is off is not ridden: LWORailActivatorComponent::msgCheckUseRequirements (0x00c2c5b0) refuses the use, so a
// client never asks for it
TEST_F(RailActivatorComponentTests, ARailThatIsOffIsNotUsed) {
entity->SetVar<bool>(u"rail_activator_active", false);
auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
Entity user(77, info);
GameMessages::RequestUse use;
GameMessages::RequestUseEvent event(use, user);
EXPECT_TRUE(rail->OnRequestUse(event));
EXPECT_TRUE(rail->GetEntitiesOnRail().empty());
}
TEST_F(RailActivatorComponentTests, ARailThatIsOnIsUsed) {
auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
Entity user(78, info);
GameMessages::RequestUse use;
GameMessages::RequestUseEvent event(use, user);
EXPECT_TRUE(rail->OnRequestUse(event));
EXPECT_EQ(rail->GetEntitiesOnRail(), std::vector<LWOOBJID>{ 78 });
}