mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
LWORocketLaunchComponentCommon::LoadConfigData (0x00c55150) reads the
level's targetZone, targetScene, GMLevel, propertyDefaultTargetZone,
rocketLaunchPreCondition, rocketUseLaunchPrecondition and the alternative
landing keys, and the table row replaces them only while
rocketLaunchUseDBDefaults is true, its default (0x00c12ea0). The server
used the row for every pad.
31 pads in the 1.10.64 maps turn the key off. Their zones and scenes are
their rows'; what only the level has are the launch preconditions in use
on the Venture Explorer's four pads ("237;8;127|128;6|55;107;282") and
on Avant Gardens' pads to Nimbus Station, its two properties and the
Venture Explorer. Those are now checked before a launch, as the row's are.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
195 lines
7.4 KiB
C++
195 lines
7.4 KiB
C++
#include "RocketLaunchpadControlComponent.h"
|
|
#include "MasterPackets.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "GameMessages.h"
|
|
#include "ObjectMessages.h"
|
|
#include "ZoneMessages.h"
|
|
#include "CharacterComponent.h"
|
|
#include "dZoneManager.h"
|
|
#include "EntityManager.h"
|
|
#include "Item.h"
|
|
#include "Game.h"
|
|
#include "Logger.h"
|
|
#include "CDClientDatabase.h"
|
|
#include "ChatPackets.h"
|
|
#include "MissionComponent.h"
|
|
#include "PropertyEntranceComponent.h"
|
|
#include "MultiZoneEntranceComponent.h"
|
|
#include "dServer.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "eObjectWorldState.h"
|
|
#include "ServiceType.h"
|
|
#include "MessageType/Master.h"
|
|
|
|
RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
|
RegisterMsg(&RocketLaunchpadControlComponent::OnRequestUse);
|
|
}
|
|
|
|
// The RocketLaunchpadControlComponent row
|
|
void RocketLaunchpadControlComponent::LoadDataFromTemplate() {
|
|
auto query = CDClientDatabase::CreatePreppedStmt(
|
|
"SELECT targetZone, defaultZoneID, targetScene, altLandingPrecondition, altLandingSpawnPointName, gmLevel, useLaunchPrecondition, launchPrecondition FROM RocketLaunchpadControlComponent WHERE id = ?;");
|
|
query.bind(1, m_ComponentID);
|
|
|
|
auto result = query.execQuery();
|
|
|
|
if (!result.eof() && !result.fieldIsNull("targetZone")) {
|
|
m_TargetZone = result.getIntField("targetZone");
|
|
m_DefaultZone = result.getIntField("defaultZoneID");
|
|
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();
|
|
}
|
|
|
|
// The level's keys. LWORocketLaunchComponentCommon::LoadConfigData (0x00c55150) reads them into the fields the row
|
|
// would fill, and the row replaces them only while rocketLaunchUseDBDefaults is true, its default (0x00c12ea0). A pad
|
|
// that turns it off is ruled by its level: 31 pads in the 1.10.64 maps, whose zones and scenes are their rows' but
|
|
// whose launch preconditions only the level has (the Venture Explorer's and Avant Gardens' pads).
|
|
void RocketLaunchpadControlComponent::LoadConfigData() {
|
|
if (!m_Parent->HasVar(u"rocketLaunchUseDBDefaults") || m_Parent->GetVarAs<bool>(u"rocketLaunchUseDBDefaults")) return;
|
|
|
|
if (m_Parent->HasVar(u"targetZone")) m_TargetZone = m_Parent->GetVarAs<int32_t>(u"targetZone");
|
|
m_DefaultZone = m_Parent->GetVarAs<int32_t>(u"propertyDefaultTargetZone");
|
|
m_TargetScene = m_Parent->GetVarAsString(u"targetScene");
|
|
m_GMLevel = m_Parent->GetVarAs<int32_t>(u"GMLevel");
|
|
m_LaunchPrecondition = m_Parent->GetVarAs<bool>(u"rocketUseLaunchPrecondition") ? m_Parent->GetVarAsString(u"rocketLaunchPreCondition") : "";
|
|
m_AltLandingScene = m_Parent->GetVarAsString(u"rocketAltLandingSpawnName");
|
|
delete m_AltPrecondition;
|
|
m_AltPrecondition = new PreconditionExpression(m_Parent->GetVarAsString(u"rocketAltLandingPrecondition"));
|
|
}
|
|
|
|
RocketLaunchpadControlComponent::~RocketLaunchpadControlComponent() {
|
|
delete m_AltPrecondition;
|
|
}
|
|
|
|
GameMessages::FireEventClientSide RocketLaunchpadControlComponent::MakeRocketEquipped(const LWOOBJID launchpad, const LWOOBJID rocket, const LWOOBJID player, const LWOCLONEID cloneId, const int32_t worldIndex) {
|
|
GameMessages::FireEventClientSide rocketEquipped(launchpad, u"RocketEquipped", rocket, player);
|
|
if (cloneId != LWOCLONEID_INVALID) rocketEquipped.param1 = cloneId;
|
|
rocketEquipped.param2 = worldIndex;
|
|
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;
|
|
|
|
if (zone == 0) {
|
|
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();
|
|
|
|
if (!characterComponent || !character) return;
|
|
|
|
auto* rocket = characterComponent->GetRocket(originator);
|
|
if (!rocket) {
|
|
LOG("Unable to find rocket!");
|
|
return;
|
|
}
|
|
|
|
// we have the ability to launch, so now we prep the zone: the clone the player is going to (a property), not an extra
|
|
// clone 0 instance nobody asked for
|
|
TellMasterToPrepZone(zone, cloneId == LWOCLONEID_INVALID ? 0 : cloneId);
|
|
|
|
// Achievement unlocked: "All zones unlocked"
|
|
if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) {
|
|
character->SetTargetScene(m_AltLandingScene);
|
|
} else {
|
|
character->SetTargetScene(m_TargetScene);
|
|
}
|
|
|
|
character->SaveXMLToDatabase();
|
|
|
|
SetSelectedMapId(originator->GetObjectID(), zone);
|
|
|
|
// Equipping the rocket (RocketEquip) sent ChangeObjectWorldState(ATTACHED); live sent it before this event
|
|
MakeRocketEquipped(m_Parent->GetObjectID(), rocket->GetId(), originator->GetObjectID(), cloneId, worldIndex).Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
Game::entityManager->SerializeEntity(originator);
|
|
}
|
|
|
|
bool RocketLaunchpadControlComponent::OnRequestUse(GameMessages::RequestUseEvent& event) {
|
|
if (event.msg.IsMissionChoice()) return false;
|
|
auto* const originator = event.user;
|
|
// If we are have the property or the LUP component, we don't want to immediately launch
|
|
// instead we let their OnUse handlers do their things
|
|
// which components of an Object have their OnUse called when using them
|
|
// so we don't need to call it here
|
|
auto* propertyEntrance = m_Parent->GetComponent<PropertyEntranceComponent>();
|
|
if (propertyEntrance) {
|
|
return true;
|
|
}
|
|
|
|
auto* rocketLaunchLUP = m_Parent->GetComponent<MultiZoneEntranceComponent>();
|
|
if (rocketLaunchLUP) {
|
|
return true;
|
|
}
|
|
|
|
// No rocket no launch
|
|
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
|
|
if (!rocket) {
|
|
return true;
|
|
}
|
|
Launch(originator);
|
|
return true;
|
|
}
|
|
|
|
void RocketLaunchpadControlComponent::OnProximityUpdate(Entity* entering, std::string name, std::string status) {
|
|
// Proximity rockets are handled by item equipment
|
|
}
|
|
|
|
void RocketLaunchpadControlComponent::SetSelectedMapId(LWOOBJID player, LWOMAPID mapID) {
|
|
m_SelectedMapIds[player] = mapID;
|
|
}
|
|
|
|
LWOMAPID RocketLaunchpadControlComponent::GetSelectedMapId(LWOOBJID player) const {
|
|
const auto index = m_SelectedMapIds.find(player);
|
|
|
|
if (index == m_SelectedMapIds.end()) return 0;
|
|
|
|
return index->second;
|
|
}
|
|
|
|
void RocketLaunchpadControlComponent::SetSelectedCloneId(LWOOBJID player, LWOCLONEID cloneId) {
|
|
m_SelectedCloneIds[player] = cloneId;
|
|
}
|
|
|
|
LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player) const {
|
|
const auto index = m_SelectedCloneIds.find(player);
|
|
|
|
if (index == m_SelectedCloneIds.end()) return 0;
|
|
|
|
return index->second;
|
|
}
|
|
|
|
void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID, LWOCLONEID cloneID) {
|
|
MasterPackets::PrepZone request;
|
|
request.zoneID = zoneID;
|
|
request.cloneID = cloneID;
|
|
MasterPackets::SendToMaster(request);
|
|
}
|
|
|
|
|
|
LWOMAPID RocketLaunchpadControlComponent::GetTargetZone() const {
|
|
return m_TargetZone;
|
|
}
|
|
|
|
LWOMAPID RocketLaunchpadControlComponent::GetDefaultZone() const {
|
|
return m_DefaultZone;
|
|
}
|