fix: a launch pad checks its GM level and launch precondition

LWORocketLaunchComponentCommon does not launch a player under the row's
gmLevel (msgFireEventClientSide 0x00cb78b0) or, with useLaunchPrecondition,
one that fails the row's launchPrecondition (msgCheckUseRequirements
0x00c7d0d0). The server launched on any request. Rows: the test launcher
asks for GM level 9, the pads to Gnarled Forest and Forbidden Valley for
mission 474, Starbase 3001's for mission 956.

The alternative landing precondition pointer also starts out null for a
pad without a row.

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 38fe226e6f
commit 53968599d2
3 changed files with 52 additions and 2 deletions

View File

@@ -30,7 +30,7 @@ RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent,
// The RocketLaunchpadControlComponent row
void RocketLaunchpadControlComponent::LoadDataFromTemplate() {
auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT targetZone, defaultZoneID, targetScene, altLandingPrecondition, altLandingSpawnPointName FROM RocketLaunchpadControlComponent WHERE id = ?;");
"SELECT targetZone, defaultZoneID, targetScene, altLandingPrecondition, altLandingSpawnPointName, gmLevel, useLaunchPrecondition, launchPrecondition FROM RocketLaunchpadControlComponent WHERE id = ?;");
query.bind(1, m_ComponentID);
auto result = query.execQuery();
@@ -41,6 +41,8 @@ void RocketLaunchpadControlComponent::LoadDataFromTemplate() {
m_TargetScene = result.getStringField("targetScene");
m_AltPrecondition = new PreconditionExpression(result.getStringField("altLandingPrecondition"));
m_AltLandingScene = result.getStringField("altLandingSpawnPointName");
m_GMLevel = result.getIntField("gmLevel", 0);
if (result.getIntField("useLaunchPrecondition", 0) != 0) m_LaunchPrecondition = result.getStringField("launchPrecondition", "");
}
result.finalize();
@@ -57,6 +59,11 @@ GameMessages::FireEventClientSide RocketLaunchpadControlComponent::MakeRocketEqu
return rocketEquipped;
}
bool RocketLaunchpadControlComponent::MayLaunch(Entity& player) const {
if (static_cast<int32_t>(player.GetGMLevel()) < m_GMLevel) return false;
return m_LaunchPrecondition.empty() || PreconditionExpression(m_LaunchPrecondition).Check(&player);
}
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId, int32_t worldIndex) {
auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId;
@@ -64,6 +71,8 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId,
return;
}
if (!MayLaunch(*originator)) return;
// This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready
auto* characterComponent = originator->GetComponent<CharacterComponent>();
auto* character = originator->GetCharacter();