mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-26 15:37:20 +00:00
Use better naming
- Remove use of Base. It is implied if you inherit that the class inherited from is a Base. - Fix compilation errors from said change.
This commit is contained in:
parent
9121bf41c5
commit
ec9278286b
@ -1,5 +0,0 @@
|
||||
#include "BaseRacingControlComponent.h"
|
||||
|
||||
BaseRacingControlComponent::BaseRacingControlComponent(Entity* parent, int32_t componentId) : ScriptedActivityComponent(parent, componentId) {
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#ifndef __BASERACINGCONTROLCOMPONENT__H__
|
||||
#define __BASERACINGCONTROLCOMPONENT__H__
|
||||
|
||||
#include "ScriptedActivityComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class BaseRacingControlComponent : public ScriptedActivityComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL;
|
||||
BaseRacingControlComponent(Entity* parent, int32_t componentId);
|
||||
};
|
||||
|
||||
|
||||
#endif //!__BASERACINGCONTROLCOMPONENT__H__
|
@ -1,7 +1,7 @@
|
||||
set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
|
||||
"ActivityComponent.cpp"
|
||||
"BaseCombatAIComponent.cpp"
|
||||
"BaseRacingControlComponent.cpp"
|
||||
"RacingControlComponent.cpp"
|
||||
"BouncerComponent.cpp"
|
||||
"BuffComponent.cpp"
|
||||
"BuildBorderComponent.cpp"
|
||||
@ -34,7 +34,7 @@ set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
|
||||
"PropertyManagementComponent.cpp"
|
||||
"PropertyVendorComponent.cpp"
|
||||
"ProximityMonitorComponent.cpp"
|
||||
"RacingControlComponent.cpp"
|
||||
"VehicleRacingControlComponent.cpp"
|
||||
"RacingSoundTriggerComponent.cpp"
|
||||
"RacingStatsComponent.cpp"
|
||||
"RailActivatorComponent.cpp"
|
||||
|
@ -4,6 +4,9 @@ Legend
|
||||
├~~ Loaded with Parent
|
||||
├-> SubComponent
|
||||
├-? idk lol, but related
|
||||
|
||||
originalName -> newName
|
||||
|
||||
```
|
||||
|
||||
|
||||
@ -14,8 +17,8 @@ LWOActivityComponent
|
||||
├── LWOMiniGameControlComponent
|
||||
├── LWOShootingGalleryComponent
|
||||
├── LWOScriptedActivityComponent
|
||||
| └── LWOBaseRacingControlComponent
|
||||
| ├── LWORacingControlComponent
|
||||
| └── LWOBaseRacingControlComponent -> RacingControlComponent
|
||||
| ├── LWORacingControlComponent -> VehicleRacingControlComponent
|
||||
| └── LWOGateRushControlComponent
|
||||
LWOBaseCombatAIComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "GateRushControlComponent.h"
|
||||
|
||||
GateRushControlComponent::GateRushControlComponent(Entity* parent, int32_t componentId) : BaseRacingControlComponent(parent, componentId) {
|
||||
GateRushControlComponent::GateRushControlComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) {
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#ifndef __GATERUSHCONTROLCOMPONENT__H__
|
||||
#define __GATERUSHCONTROLCOMPONENT__H__
|
||||
|
||||
#include "BaseRacingControlComponent.h"
|
||||
#include "RacingControlComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class GateRushControlComponent : public BaseRacingControlComponent {
|
||||
class GateRushControlComponent : public RacingControlComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::GATE_RUSH_CONTROL;
|
||||
GateRushControlComponent(Entity* parent, int32_t componentId);
|
||||
|
@ -1,880 +1,5 @@
|
||||
/**
|
||||
* Thanks to Simon for his early research on the racing system.
|
||||
*/
|
||||
|
||||
#include "RacingControlComponent.h"
|
||||
|
||||
#include "CharacterComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "GameMessages.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "Item.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "ModuleAssemblyComponent.h"
|
||||
#include "Player.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "eRacingTaskParam.h"
|
||||
#include "Spawner.h"
|
||||
#include "dServer.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "dConfig.h"
|
||||
#include "Loot.h"
|
||||
#include "eMissionTaskType.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
RacingControlComponent::RacingControlComponent(Entity* parent, int32_t componentId) : ScriptedActivityComponent(parent, componentId) {
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327950288
|
||||
#endif
|
||||
|
||||
RacingControlComponent::RacingControlComponent(Entity* parent, int32_t componentId) : BaseRacingControlComponent(parent, componentId) {
|
||||
m_PathName = u"MainPath";
|
||||
m_RemainingLaps = 3;
|
||||
m_LeadingPlayer = LWOOBJID_EMPTY;
|
||||
m_RaceBestTime = 0;
|
||||
m_RaceBestLap = 0;
|
||||
m_Started = false;
|
||||
m_StartTimer = 0;
|
||||
m_Loaded = false;
|
||||
m_LoadedPlayers = 0;
|
||||
m_LoadTimer = 0;
|
||||
m_Finished = 0;
|
||||
m_StartTime = 0;
|
||||
m_EmptyTimer = 0;
|
||||
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
|
||||
|
||||
m_MainWorld = 1200;
|
||||
const auto worldID = Game::server->GetZoneID();
|
||||
if (dZoneManager::Instance()->CheckIfAccessibleZone((worldID/10)*10)) m_MainWorld = (worldID/10)*10;
|
||||
|
||||
m_ActivityID = 42;
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.instanceMapID == worldID); });
|
||||
for (CDActivities activity : activities) m_ActivityID = activity.ActivityID;
|
||||
}
|
||||
|
||||
RacingControlComponent::~RacingControlComponent() {}
|
||||
|
||||
void RacingControlComponent::OnPlayerLoaded(Entity* player) {
|
||||
// If the race has already started, send the player back to the main world.
|
||||
if (m_Loaded) {
|
||||
auto* playerInstance = dynamic_cast<Player*>(player);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto objectID = player->GetObjectID();
|
||||
|
||||
m_LoadedPlayers++;
|
||||
|
||||
Game::logger->Log("RacingControlComponent", "Loading player %i",
|
||||
m_LoadedPlayers);
|
||||
|
||||
m_LobbyPlayers.push_back(objectID);
|
||||
}
|
||||
|
||||
void RacingControlComponent::LoadPlayerVehicle(Entity* player,
|
||||
uint32_t positionNumber, bool initialLoad) {
|
||||
// Load the player's vehicle.
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the player's vehicle.
|
||||
|
||||
auto* item = inventoryComponent->FindItemByLot(8092);
|
||||
|
||||
if (item == nullptr) {
|
||||
Game::logger->Log("RacingControlComponent", "Failed to find item");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the vehicle's starting position.
|
||||
|
||||
auto* path = dZoneManager::Instance()->GetZone()->GetPath(
|
||||
GeneralUtils::UTF16ToWTF8(m_PathName));
|
||||
|
||||
auto spawnPointEntities = EntityManager::Instance()->GetEntitiesByLOT(4843);
|
||||
auto startPosition = NiPoint3::ZERO;
|
||||
auto startRotation = NiQuaternion::IDENTITY;
|
||||
const std::string placementAsString = std::to_string(positionNumber);
|
||||
for (auto entity : spawnPointEntities) {
|
||||
if (!entity) continue;
|
||||
if (entity->GetVarAsString(u"placement") == placementAsString) {
|
||||
startPosition = entity->GetPosition();
|
||||
startRotation = entity->GetRotation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the player is at the correct position.
|
||||
|
||||
GameMessages::SendTeleport(player->GetObjectID(), startPosition,
|
||||
startRotation, player->GetSystemAddress(), true);
|
||||
|
||||
// Spawn the vehicle entity.
|
||||
|
||||
EntityInfo info{};
|
||||
info.lot = 8092;
|
||||
info.pos = startPosition;
|
||||
info.rot = startRotation;
|
||||
info.spawnerID = m_ParentEntity->GetObjectID();
|
||||
|
||||
auto* carEntity =
|
||||
EntityManager::Instance()->CreateEntity(info, nullptr, m_ParentEntity);
|
||||
|
||||
// Make the vehicle a child of the racing controller.
|
||||
m_ParentEntity->AddChild(carEntity);
|
||||
|
||||
auto* destroyableComponent = carEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
// Setup the vehicle stats.
|
||||
if (destroyableComponent != nullptr) {
|
||||
destroyableComponent->SetMaxImagination(60);
|
||||
destroyableComponent->SetImagination(0);
|
||||
}
|
||||
|
||||
// Setup the vehicle as being possessed by the player.
|
||||
auto* possessableComponent = carEntity->GetComponent<PossessableComponent>();
|
||||
|
||||
if (possessableComponent != nullptr) {
|
||||
possessableComponent->SetPossessor(player->GetObjectID());
|
||||
}
|
||||
|
||||
// Load the vehicle's assemblyPartLOTs for display.
|
||||
auto* moduleAssemblyComponent = carEntity->GetComponent<ModuleAssemblyComponent>();
|
||||
|
||||
if (moduleAssemblyComponent) {
|
||||
moduleAssemblyComponent->SetSubKey(item->GetSubKey());
|
||||
moduleAssemblyComponent->SetUseOptionalParts(false);
|
||||
|
||||
for (auto* config : item->GetConfig()) {
|
||||
if (config->GetKey() == u"assemblyPartLOTs") {
|
||||
moduleAssemblyComponent->SetAssemblyPartsLOTs(
|
||||
GeneralUtils::ASCIIToUTF16(config->GetValueAsString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the player as possessing the vehicle.
|
||||
auto* possessorComponent = player->GetComponent<PossessorComponent>();
|
||||
|
||||
if (possessorComponent != nullptr) {
|
||||
possessorComponent->SetPossessable(carEntity->GetObjectID());
|
||||
possessorComponent->SetPossessableType(ePossessionType::ATTACHED_VISIBLE); // for racing it's always Attached_Visible
|
||||
}
|
||||
|
||||
// Set the player's current activity as racing.
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->SetIsRacing(true);
|
||||
}
|
||||
|
||||
// Init the player's racing entry.
|
||||
if (initialLoad) {
|
||||
m_RacingPlayers.push_back(
|
||||
{ player->GetObjectID(),
|
||||
carEntity->GetObjectID(),
|
||||
static_cast<uint32_t>(m_RacingPlayers.size()),
|
||||
false,
|
||||
{},
|
||||
startPosition,
|
||||
startRotation,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0 });
|
||||
}
|
||||
|
||||
// Construct and serialize everything when done.
|
||||
|
||||
EntityManager::Instance()->ConstructEntity(carEntity);
|
||||
EntityManager::Instance()->SerializeEntity(player);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
GameMessages::SendRacingSetPlayerResetInfo(
|
||||
m_ParentEntity->GetObjectID(), 0, 0, player->GetObjectID(), startPosition, 1,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
const auto playerID = player->GetObjectID();
|
||||
|
||||
// Reset the player to the start position during downtime, in case something
|
||||
// went wrong.
|
||||
m_ParentEntity->AddCallbackTimer(1, [this, playerID]() {
|
||||
auto* player = EntityManager::Instance()->GetEntity(playerID);
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendRacingResetPlayerToLastReset(
|
||||
m_ParentEntity->GetObjectID(), playerID, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
});
|
||||
|
||||
GameMessages::SendSetJetPackMode(player, false);
|
||||
|
||||
// Set the vehicle's state.
|
||||
GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(),
|
||||
m_ParentEntity->GetObjectID(),
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendVehicleSetWheelLockState(carEntity->GetObjectID(), false,
|
||||
initialLoad,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
// Make sure everything has the correct position.
|
||||
GameMessages::SendTeleport(player->GetObjectID(), startPosition,
|
||||
startRotation, player->GetSystemAddress(), true);
|
||||
GameMessages::SendTeleport(carEntity->GetObjectID(), startPosition,
|
||||
startRotation, player->GetSystemAddress(), true);
|
||||
}
|
||||
|
||||
void RacingControlComponent::OnRacingClientReady(Entity* player) {
|
||||
// Notify the other players that this player is ready.
|
||||
|
||||
for (auto& racingPlayer : m_RacingPlayers) {
|
||||
if (racingPlayer.playerID != player->GetObjectID()) {
|
||||
if (racingPlayer.playerLoaded) {
|
||||
GameMessages::SendRacingPlayerLoaded(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
racingPlayer.vehicleID, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
racingPlayer.playerLoaded = true;
|
||||
|
||||
GameMessages::SendRacingPlayerLoaded(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
racingPlayer.vehicleID, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void RacingControlComponent::OnRequestDie(Entity* player) {
|
||||
// Sent by the client when they collide with something which should smash
|
||||
// them.
|
||||
|
||||
for (auto& racingPlayer : m_RacingPlayers) {
|
||||
if (racingPlayer.playerID != player->GetObjectID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* vehicle =
|
||||
EntityManager::Instance()->GetEntity(racingPlayer.vehicleID);
|
||||
|
||||
if (!vehicle) return;
|
||||
|
||||
if (!racingPlayer.noSmashOnReload) {
|
||||
racingPlayer.smashedTimes++;
|
||||
GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true,
|
||||
eKillType::VIOLENT, u"", 0, 0, 90.0f, false, true, 0);
|
||||
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
uint32_t respawnImagination = 0;
|
||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||
// Do not actually change the value yet. Do that on respawn.
|
||||
if (destroyableComponent) {
|
||||
respawnImagination = static_cast<int32_t>(ceil(destroyableComponent->GetImagination() / 2.0f / 10.0f)) * 10.0f;
|
||||
GameMessages::SendSetResurrectRestoreValues(vehicle, -1, -1, respawnImagination);
|
||||
}
|
||||
|
||||
// Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else...
|
||||
vehicle->AddCallbackTimer(2.0f, [=]() {
|
||||
if (!vehicle || !this->m_ParentEntity) return;
|
||||
GameMessages::SendRacingResetPlayerToLastReset(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendVehicleStopBoost(vehicle, player->GetSystemAddress(), true);
|
||||
|
||||
GameMessages::SendRacingSetPlayerResetInfo(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.lap,
|
||||
racingPlayer.respawnIndex, player->GetObjectID(),
|
||||
racingPlayer.respawnPosition, racingPlayer.respawnIndex + 1,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendResurrect(vehicle);
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||
if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination);
|
||||
EntityManager::Instance()->SerializeEntity(vehicle);
|
||||
});
|
||||
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->UpdatePlayerStatistic(RacingTimesWrecked);
|
||||
}
|
||||
} else {
|
||||
GameMessages::SendRacingSetPlayerResetInfo(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.lap,
|
||||
racingPlayer.respawnIndex, player->GetObjectID(),
|
||||
racingPlayer.respawnPosition, racingPlayer.respawnIndex + 1,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::SendRacingResetPlayerToLastReset(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RacingControlComponent::OnRacingPlayerInfoResetFinished(Entity* player) {
|
||||
// When the player has respawned.
|
||||
|
||||
for (auto& racingPlayer : m_RacingPlayers) {
|
||||
if (racingPlayer.playerID != player->GetObjectID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* vehicle =
|
||||
EntityManager::Instance()->GetEntity(racingPlayer.vehicleID);
|
||||
|
||||
if (vehicle == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
racingPlayer.noSmashOnReload = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t button, const std::string& id) {
|
||||
auto* data = GetPlayerData(player->GetObjectID());
|
||||
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == "rewardButton") {
|
||||
if (data->collectedRewards) {
|
||||
return;
|
||||
}
|
||||
|
||||
data->collectedRewards = true;
|
||||
|
||||
// Calculate the score, different loot depending on player count
|
||||
const auto score = m_LoadedPlayers * 10 + data->finished;
|
||||
|
||||
LootGenerator::Instance().GiveActivityLoot(player, m_ParentEntity, m_ActivityID, score);
|
||||
|
||||
// Giving rewards
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_ParentEntity->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
|
||||
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent == nullptr) return;
|
||||
|
||||
missionComponent->Progress(eMissionTaskType::RACING, 0, (LWOOBJID)eRacingTaskParam::COMPETED_IN_RACE); // Progress task for competing in a race
|
||||
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, (LWOOBJID)eRacingTaskParam::SAFE_DRIVER); // Finish a race without being smashed.
|
||||
|
||||
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
||||
if (m_SoloRacing || m_LoadedPlayers > 2) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, data->finished, (LWOOBJID)eRacingTaskParam::FINISH_WITH_PLACEMENT); // Finish in 1st place on a race
|
||||
if (data->finished == 1) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS); // Finish in 1st place on multiple tracks.
|
||||
missionComponent->Progress(eMissionTaskType::RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::WIN_RACE_IN_WORLD); // Finished first place in specific world.
|
||||
}
|
||||
if (data->finished == m_LoadedPlayers) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::LAST_PLACE_FINISH); // Finished first place in specific world.
|
||||
}
|
||||
}
|
||||
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
|
||||
auto* vehicle = EntityManager::Instance()->GetEntity(data->vehicleID);
|
||||
|
||||
if (vehicle == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exiting race
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_ParentEntity->GetObjectID(), 3, 0, LWOOBJID_EMPTY, u"",
|
||||
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* playerInstance = dynamic_cast<Player*>(player);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
|
||||
vehicle->Kill();
|
||||
}
|
||||
}
|
||||
|
||||
void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream,
|
||||
bool bIsInitialUpdate,
|
||||
unsigned int& flags) {
|
||||
// BEGIN Scripted Activity
|
||||
|
||||
outBitStream->Write1();
|
||||
|
||||
outBitStream->Write(static_cast<uint32_t>(m_RacingPlayers.size()));
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
outBitStream->Write(player.playerID);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
outBitStream->Write(player.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// END Scripted Activity
|
||||
|
||||
outBitStream->Write1(); // Dirty?
|
||||
outBitStream->Write(static_cast<uint16_t>(m_RacingPlayers.size()));
|
||||
|
||||
outBitStream->Write(!m_RacingPlayers.empty());
|
||||
if (!m_RacingPlayers.empty()) {
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
outBitStream->Write1(); // Has more date
|
||||
|
||||
outBitStream->Write(player.playerID);
|
||||
outBitStream->Write(player.vehicleID);
|
||||
outBitStream->Write(player.playerIndex);
|
||||
outBitStream->Write(player.playerLoaded);
|
||||
}
|
||||
|
||||
outBitStream->Write0(); // No more data
|
||||
}
|
||||
|
||||
outBitStream->Write(!m_RacingPlayers.empty());
|
||||
if (!m_RacingPlayers.empty()) {
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
outBitStream->Write1(); // Has more date
|
||||
|
||||
outBitStream->Write(player.playerID);
|
||||
outBitStream->Write<uint32_t>(0);
|
||||
}
|
||||
|
||||
outBitStream->Write0(); // No more data
|
||||
}
|
||||
|
||||
outBitStream->Write1(); // Dirty?
|
||||
|
||||
outBitStream->Write(m_RemainingLaps);
|
||||
|
||||
outBitStream->Write(static_cast<uint16_t>(m_PathName.size()));
|
||||
for (const auto character : m_PathName) {
|
||||
outBitStream->Write(character);
|
||||
}
|
||||
|
||||
outBitStream->Write1(); // ???
|
||||
outBitStream->Write1(); // ???
|
||||
|
||||
outBitStream->Write(m_LeadingPlayer);
|
||||
outBitStream->Write(m_RaceBestLap);
|
||||
outBitStream->Write(m_RaceBestTime);
|
||||
}
|
||||
|
||||
RacingPlayerInfo* RacingControlComponent::GetPlayerData(LWOOBJID playerID) {
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
if (player.playerID == playerID) {
|
||||
return &player;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RacingControlComponent::Update(float deltaTime) {
|
||||
// This method is a mess.
|
||||
|
||||
// Pre-load routine
|
||||
if (!m_Loaded) {
|
||||
// Check if any players has disconnected before loading in
|
||||
for (size_t i = 0; i < m_LobbyPlayers.size(); i++) {
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(m_LobbyPlayers[i]);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
--m_LoadedPlayers;
|
||||
|
||||
m_LobbyPlayers.erase(m_LobbyPlayers.begin() + i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_LoadedPlayers >= 2 || (m_LoadedPlayers == 1 && m_SoloRacing)) {
|
||||
m_LoadTimer += deltaTime;
|
||||
} else {
|
||||
m_EmptyTimer += deltaTime;
|
||||
}
|
||||
|
||||
// If a player happens to be left alone for more then 30 seconds without
|
||||
// anyone else loading in, send them back to the main world
|
||||
if (m_EmptyTimer >= 30) {
|
||||
for (const auto player : m_LobbyPlayers) {
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* playerInstance = dynamic_cast<Player*>(playerEntity);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
}
|
||||
|
||||
m_LobbyPlayers.clear();
|
||||
}
|
||||
|
||||
// From the first 2 players loading in the rest have a max of 15 seconds
|
||||
// to load in, can raise this if it's too low
|
||||
if (m_LoadTimer >= 15) {
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Loading all players...");
|
||||
|
||||
for (size_t positionNumber = 0; positionNumber < m_LobbyPlayers.size(); positionNumber++) {
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Loading player now!");
|
||||
|
||||
auto* player =
|
||||
EntityManager::Instance()->GetEntity(m_LobbyPlayers[positionNumber]);
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Loading player now NOW!");
|
||||
|
||||
LoadPlayerVehicle(player, positionNumber + 1, true);
|
||||
|
||||
m_Loaded = true;
|
||||
}
|
||||
|
||||
m_Loaded = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The players who will be participating have loaded
|
||||
if (!m_Started) {
|
||||
// Check if anyone has disconnected during this period
|
||||
for (size_t i = 0; i < m_RacingPlayers.size(); i++) {
|
||||
auto* playerEntity = EntityManager::Instance()->GetEntity(
|
||||
m_RacingPlayers[i].playerID);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
m_RacingPlayers.erase(m_RacingPlayers.begin() + i);
|
||||
|
||||
--m_LoadedPlayers;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If less then 2 players are left, send the rest back to the main world
|
||||
if (m_LoadedPlayers < 2 && !(m_LoadedPlayers == 1 && m_SoloRacing)) {
|
||||
for (const auto player : m_LobbyPlayers) {
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* playerInstance = dynamic_cast<Player*>(playerEntity);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all players have send a ready message
|
||||
|
||||
int32_t readyPlayers = 0;
|
||||
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
if (player.playerLoaded) {
|
||||
++readyPlayers;
|
||||
}
|
||||
}
|
||||
|
||||
if (readyPlayers >= m_LoadedPlayers) {
|
||||
// Setup for racing
|
||||
if (m_StartTimer == 0) {
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_ParentEntity->GetObjectID(), 1, 0, LWOOBJID_EMPTY, u"",
|
||||
LWOOBJID_EMPTY, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
auto* vehicle =
|
||||
EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicle != nullptr && playerEntity != nullptr) {
|
||||
GameMessages::SendTeleport(
|
||||
player.playerID, player.respawnPosition,
|
||||
player.respawnRotation,
|
||||
playerEntity->GetSystemAddress(), true);
|
||||
|
||||
vehicle->SetPosition(player.respawnPosition);
|
||||
vehicle->SetRotation(player.respawnRotation);
|
||||
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr) {
|
||||
destroyableComponent->SetImagination(0);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(vehicle);
|
||||
EntityManager::Instance()->SerializeEntity(
|
||||
playerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn imagination pickups
|
||||
auto* minSpawner = dZoneManager::Instance()->GetSpawnersByName(
|
||||
"ImaginationSpawn_Min")[0];
|
||||
auto* medSpawner = dZoneManager::Instance()->GetSpawnersByName(
|
||||
"ImaginationSpawn_Med")[0];
|
||||
auto* maxSpawner = dZoneManager::Instance()->GetSpawnersByName(
|
||||
"ImaginationSpawn_Max")[0];
|
||||
|
||||
minSpawner->Activate();
|
||||
|
||||
if (m_LoadedPlayers > 2) {
|
||||
medSpawner->Activate();
|
||||
}
|
||||
|
||||
if (m_LoadedPlayers > 4) {
|
||||
maxSpawner->Activate();
|
||||
}
|
||||
|
||||
// Reset players to their start location, without smashing them
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
auto* vehicleEntity =
|
||||
EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicleEntity == nullptr || playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
player.noSmashOnReload = true;
|
||||
|
||||
OnRequestDie(playerEntity);
|
||||
}
|
||||
}
|
||||
// This 6 seconds seems to be hardcoded in the client, start race
|
||||
// after that amount of time
|
||||
else if (m_StartTimer >= 6) {
|
||||
// Activate the players movement
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
auto* vehicleEntity =
|
||||
EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicleEntity == nullptr || playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GameMessages::SendVehicleUnlockInput(
|
||||
player.vehicleID, false, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
// Start the race
|
||||
GameMessages::SendActivityStart(m_ParentEntity->GetObjectID(),
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
m_Started = true;
|
||||
|
||||
Game::logger->Log("RacingControlComponent", "Starting race");
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
m_StartTime = std::time(nullptr);
|
||||
}
|
||||
|
||||
m_StartTimer += deltaTime;
|
||||
} else {
|
||||
m_StartTimer = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Race routines
|
||||
auto* path = dZoneManager::Instance()->GetZone()->GetPath(
|
||||
GeneralUtils::UTF16ToWTF8(m_PathName));
|
||||
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
auto* vehicle = EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicle == nullptr || playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto vehiclePosition = vehicle->GetPosition();
|
||||
|
||||
// If the player is this far below the map, safe to assume they should
|
||||
// be smashed by death plane
|
||||
if (vehiclePosition.y < -500) {
|
||||
GameMessages::SendDie(vehicle, m_ParentEntity->GetObjectID(),
|
||||
LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0,
|
||||
true, false, 0);
|
||||
|
||||
OnRequestDie(playerEntity);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop through all the waypoints and see if the player has reached a
|
||||
// new checkpoint
|
||||
uint32_t respawnIndex = 0;
|
||||
for (const auto& waypoint : path->pathWaypoints) {
|
||||
if (player.lap == 3) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (player.respawnIndex == respawnIndex) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& position = waypoint.position;
|
||||
|
||||
if (std::abs((int)respawnIndex - (int)player.respawnIndex) > 10 &&
|
||||
player.respawnIndex != path->pathWaypoints.size() - 1) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Vector3::DistanceSquared(position, vehiclePosition) > 50 * 50) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only go upwards, except if we've lapped
|
||||
// Not sure how we are supposed to check if they've reach a
|
||||
// checkpoint, within 50 units seems safe
|
||||
if (!(respawnIndex > player.respawnIndex ||
|
||||
player.respawnIndex == path->pathWaypoints.size() - 1)) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some offset up to make they don't fall through the terrain on a
|
||||
// respawn, seems to fix itself to the track anyhow
|
||||
player.respawnPosition = position + NiPoint3::UNIT_Y * 5;
|
||||
player.respawnRotation = vehicle->GetRotation();
|
||||
player.respawnIndex = respawnIndex;
|
||||
|
||||
// Reached the start point, lapped
|
||||
if (respawnIndex == 0) {
|
||||
time_t lapTime = std::time(nullptr) - (player.lap == 0 ? m_StartTime : player.lapTime);
|
||||
|
||||
// Cheating check
|
||||
if (lapTime < 40) {
|
||||
continue;
|
||||
}
|
||||
|
||||
player.lap++;
|
||||
|
||||
player.lapTime = std::time(nullptr);
|
||||
|
||||
if (player.bestLapTime == 0 || player.bestLapTime > lapTime) {
|
||||
player.bestLapTime = lapTime;
|
||||
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Best lap time (%llu)", lapTime);
|
||||
}
|
||||
|
||||
auto* missionComponent = playerEntity->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
|
||||
// Progress lap time tasks
|
||||
missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, (LWOOBJID)eRacingTaskParam::LAP_TIME);
|
||||
|
||||
if (player.lap == 3) {
|
||||
m_Finished++;
|
||||
player.finished = m_Finished;
|
||||
|
||||
const auto raceTime =
|
||||
(std::time(nullptr) - m_StartTime);
|
||||
|
||||
player.raceTime = raceTime;
|
||||
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Completed time %llu, %llu",
|
||||
raceTime, raceTime * 1000);
|
||||
|
||||
// Entire race time
|
||||
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, (LWOOBJID)eRacingTaskParam::TOTAL_TRACK_TIME);
|
||||
|
||||
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackRaceCompleted(m_Finished == 1);
|
||||
}
|
||||
|
||||
// TODO: Figure out how to update the GUI leaderboard.
|
||||
}
|
||||
}
|
||||
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Lapped (%i) in (%llu)", player.lap,
|
||||
lapTime);
|
||||
}
|
||||
|
||||
Game::logger->Log("RacingControlComponent",
|
||||
"Reached point (%i)/(%i)", player.respawnIndex,
|
||||
path->pathWaypoints.size());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string RacingControlComponent::FormatTimeString(time_t time) {
|
||||
int32_t min = time / 60;
|
||||
time -= min * 60;
|
||||
int32_t sec = time;
|
||||
|
||||
std::string minText;
|
||||
std::string secText;
|
||||
|
||||
if (min <= 0) {
|
||||
minText = "0";
|
||||
} else {
|
||||
minText = std::to_string(min);
|
||||
}
|
||||
|
||||
if (sec <= 0) {
|
||||
secText = "00";
|
||||
} else if (sec <= 9) {
|
||||
secText = "0" + std::to_string(sec);
|
||||
} else {
|
||||
secText = std::to_string(sec);
|
||||
}
|
||||
|
||||
return minText + ":" + secText + ".00";
|
||||
}
|
||||
|
@ -1,254 +1,16 @@
|
||||
/**
|
||||
* Thanks to Simon for his early research on the racing system.
|
||||
*/
|
||||
#ifndef __RACINGCONTROLCOMPONENT__H__
|
||||
#define __RACINGCONTROLCOMPONENT__H__
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "BaseRacingControlComponent.h"
|
||||
#include "ScriptedActivityComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Information for each player in the race
|
||||
*/
|
||||
struct RacingPlayerInfo {
|
||||
class Entity;
|
||||
|
||||
/**
|
||||
* The ID of the player
|
||||
*/
|
||||
LWOOBJID playerID;
|
||||
|
||||
/**
|
||||
* The ID of the car the player is driving
|
||||
*/
|
||||
LWOOBJID vehicleID;
|
||||
|
||||
/**
|
||||
* The index of this player in the list of players
|
||||
*/
|
||||
uint32_t playerIndex;
|
||||
|
||||
/**
|
||||
* Whether the player has finished loading or not
|
||||
*/
|
||||
bool playerLoaded;
|
||||
|
||||
/**
|
||||
* Scripted activity component score
|
||||
*/
|
||||
float data[10]{};
|
||||
|
||||
/**
|
||||
* Point that the player will respawn at if they smash their car
|
||||
*/
|
||||
NiPoint3 respawnPosition;
|
||||
|
||||
/**
|
||||
* Rotation that the player will respawn at if they smash their car
|
||||
*/
|
||||
NiQuaternion respawnRotation;
|
||||
|
||||
/**
|
||||
* The index in the respawn point the player is now at
|
||||
*/
|
||||
uint32_t respawnIndex;
|
||||
|
||||
/**
|
||||
* The number of laps the player has completed
|
||||
*/
|
||||
uint32_t lap;
|
||||
|
||||
/**
|
||||
* Whether or not the player has finished the race
|
||||
*/
|
||||
uint32_t finished;
|
||||
|
||||
/**
|
||||
* Unused
|
||||
*/
|
||||
uint16_t reachedPoints;
|
||||
|
||||
/**
|
||||
* The fastest lap time of the player
|
||||
*/
|
||||
time_t bestLapTime = 0;
|
||||
|
||||
/**
|
||||
* The current lap time of the player
|
||||
*/
|
||||
time_t lapTime = 0;
|
||||
|
||||
/**
|
||||
* The number of times this player smashed their car
|
||||
*/
|
||||
uint32_t smashedTimes = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the player should be smashed if the game is reloaded
|
||||
*/
|
||||
bool noSmashOnReload = false;
|
||||
|
||||
/**
|
||||
* Whether or not this player has collected their rewards from completing the race
|
||||
*/
|
||||
bool collectedRewards = false;
|
||||
|
||||
/**
|
||||
* Unused
|
||||
*/
|
||||
time_t raceTime = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component that's attached to a manager entity in each race zone that loads player vehicles, keep scores, etc.
|
||||
*/
|
||||
class RacingControlComponent : public BaseRacingControlComponent {
|
||||
class RacingControlComponent : public ScriptedActivityComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL;
|
||||
|
||||
RacingControlComponent(Entity* parentEntity, int32_t componentId);
|
||||
~RacingControlComponent();
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void Update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Invoked when a player loads into the zone.
|
||||
*/
|
||||
void OnPlayerLoaded(Entity* player);
|
||||
|
||||
/**
|
||||
* Initalize the player's vehicle.
|
||||
*
|
||||
* @param player The player who's vehicle to initialize.
|
||||
* @param initialLoad Is this the first time the player is loading in this race?
|
||||
*/
|
||||
void LoadPlayerVehicle(Entity* player, uint32_t positionNumber, bool initialLoad = false);
|
||||
|
||||
/**
|
||||
* Invoked when the client says it has loaded in.
|
||||
*/
|
||||
void OnRacingClientReady(Entity* player);
|
||||
|
||||
/**
|
||||
* Invoked when the client says it should be smashed.
|
||||
*/
|
||||
void OnRequestDie(Entity* player);
|
||||
|
||||
/**
|
||||
* Invoked when the player has finished respawning.
|
||||
*/
|
||||
void OnRacingPlayerInfoResetFinished(Entity* player);
|
||||
|
||||
/**
|
||||
* Invoked when the player responds to the GUI.
|
||||
*/
|
||||
void HandleMessageBoxResponse(Entity* player, int32_t button, const std::string& id);
|
||||
|
||||
/**
|
||||
* Get the racing data from a player's LWOOBJID.
|
||||
*/
|
||||
RacingPlayerInfo* GetPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Formats a time to a string, currently unused
|
||||
* @param time the time to format
|
||||
* @return the time formatted as string
|
||||
*/
|
||||
static std::string FormatTimeString(time_t time);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* The players that are currently racing
|
||||
*/
|
||||
std::vector<RacingPlayerInfo> m_RacingPlayers;
|
||||
|
||||
/**
|
||||
* The paths that are followed for the camera scenes
|
||||
*/
|
||||
std::u16string m_PathName;
|
||||
|
||||
/**
|
||||
* The ID of the activity for participating in this race
|
||||
*/
|
||||
uint32_t m_ActivityID;
|
||||
|
||||
/**
|
||||
* The world the players return to when they finish the race
|
||||
*/
|
||||
uint32_t m_MainWorld;
|
||||
|
||||
/**
|
||||
* The number of laps that are remaining for the winning player
|
||||
*/
|
||||
uint16_t m_RemainingLaps;
|
||||
|
||||
/**
|
||||
* The ID of the player that's currently winning the race
|
||||
*/
|
||||
LWOOBJID m_LeadingPlayer;
|
||||
|
||||
/**
|
||||
* The overall best lap from all the players
|
||||
*/
|
||||
float m_RaceBestLap;
|
||||
|
||||
/**
|
||||
* The overall best time from all the players
|
||||
*/
|
||||
float m_RaceBestTime;
|
||||
|
||||
/**
|
||||
* Whether or not the race has started
|
||||
*/
|
||||
bool m_Started;
|
||||
|
||||
/**
|
||||
* The time left until the race will start
|
||||
*/
|
||||
float m_StartTimer;
|
||||
|
||||
/**
|
||||
* The time left for loading the players
|
||||
*/
|
||||
float m_LoadTimer;
|
||||
|
||||
/**
|
||||
* Whether or not all players have loaded
|
||||
*/
|
||||
bool m_Loaded;
|
||||
|
||||
/**
|
||||
* The number of loaded players
|
||||
*/
|
||||
uint32_t m_LoadedPlayers;
|
||||
|
||||
/**
|
||||
* All the players that are in the lobby, loaded or not
|
||||
*/
|
||||
std::vector<LWOOBJID> m_LobbyPlayers;
|
||||
|
||||
/**
|
||||
* The number of players that have finished the race
|
||||
*/
|
||||
uint32_t m_Finished;
|
||||
|
||||
/**
|
||||
* The time the race was started
|
||||
*/
|
||||
time_t m_StartTime;
|
||||
|
||||
/**
|
||||
* Timer for tracking how long a player was alone in this race
|
||||
*/
|
||||
float m_EmptyTimer;
|
||||
|
||||
bool m_SoloRacing;
|
||||
|
||||
/**
|
||||
* Value for message box response to know if we are exiting the race via the activity dialogue
|
||||
*/
|
||||
const int32_t m_ActivityExitConfirm = 1;
|
||||
RacingControlComponent(Entity* parent, int32_t componentId);
|
||||
};
|
||||
|
||||
|
||||
#endif //!__RACINGCONTROLCOMPONENT__H__
|
||||
|
880
dGame/dComponents/VehicleRacingControlComponent.cpp
Normal file
880
dGame/dComponents/VehicleRacingControlComponent.cpp
Normal file
@ -0,0 +1,880 @@
|
||||
/**
|
||||
* Thanks to Simon for his early research on the racing system.
|
||||
*/
|
||||
|
||||
#include "VehicleRacingControlComponent.h"
|
||||
|
||||
#include "CharacterComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "GameMessages.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "Item.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "ModuleAssemblyComponent.h"
|
||||
#include "Player.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "eRacingTaskParam.h"
|
||||
#include "Spawner.h"
|
||||
#include "dServer.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "dConfig.h"
|
||||
#include "Loot.h"
|
||||
#include "eMissionTaskType.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327950288
|
||||
#endif
|
||||
|
||||
VehicleRacingControlComponent::VehicleRacingControlComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) {
|
||||
m_PathName = u"MainPath";
|
||||
m_RemainingLaps = 3;
|
||||
m_LeadingPlayer = LWOOBJID_EMPTY;
|
||||
m_RaceBestTime = 0;
|
||||
m_RaceBestLap = 0;
|
||||
m_Started = false;
|
||||
m_StartTimer = 0;
|
||||
m_Loaded = false;
|
||||
m_LoadedPlayers = 0;
|
||||
m_LoadTimer = 0;
|
||||
m_Finished = 0;
|
||||
m_StartTime = 0;
|
||||
m_EmptyTimer = 0;
|
||||
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
|
||||
|
||||
m_MainWorld = 1200;
|
||||
const auto worldID = Game::server->GetZoneID();
|
||||
if (dZoneManager::Instance()->CheckIfAccessibleZone((worldID/10)*10)) m_MainWorld = (worldID/10)*10;
|
||||
|
||||
m_ActivityID = 42;
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.instanceMapID == worldID); });
|
||||
for (CDActivities activity : activities) m_ActivityID = activity.ActivityID;
|
||||
}
|
||||
|
||||
VehicleRacingControlComponent::~VehicleRacingControlComponent() {}
|
||||
|
||||
void VehicleRacingControlComponent::OnPlayerLoaded(Entity* player) {
|
||||
// If the race has already started, send the player back to the main world.
|
||||
if (m_Loaded) {
|
||||
auto* playerInstance = dynamic_cast<Player*>(player);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto objectID = player->GetObjectID();
|
||||
|
||||
m_LoadedPlayers++;
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent", "Loading player %i",
|
||||
m_LoadedPlayers);
|
||||
|
||||
m_LobbyPlayers.push_back(objectID);
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::LoadPlayerVehicle(Entity* player,
|
||||
uint32_t positionNumber, bool initialLoad) {
|
||||
// Load the player's vehicle.
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the player's vehicle.
|
||||
|
||||
auto* item = inventoryComponent->FindItemByLot(8092);
|
||||
|
||||
if (item == nullptr) {
|
||||
Game::logger->Log("VehicleRacingControlComponent", "Failed to find item");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the vehicle's starting position.
|
||||
|
||||
auto* path = dZoneManager::Instance()->GetZone()->GetPath(
|
||||
GeneralUtils::UTF16ToWTF8(m_PathName));
|
||||
|
||||
auto spawnPointEntities = EntityManager::Instance()->GetEntitiesByLOT(4843);
|
||||
auto startPosition = NiPoint3::ZERO;
|
||||
auto startRotation = NiQuaternion::IDENTITY;
|
||||
const std::string placementAsString = std::to_string(positionNumber);
|
||||
for (auto entity : spawnPointEntities) {
|
||||
if (!entity) continue;
|
||||
if (entity->GetVarAsString(u"placement") == placementAsString) {
|
||||
startPosition = entity->GetPosition();
|
||||
startRotation = entity->GetRotation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the player is at the correct position.
|
||||
|
||||
GameMessages::SendTeleport(player->GetObjectID(), startPosition,
|
||||
startRotation, player->GetSystemAddress(), true);
|
||||
|
||||
// Spawn the vehicle entity.
|
||||
|
||||
EntityInfo info{};
|
||||
info.lot = 8092;
|
||||
info.pos = startPosition;
|
||||
info.rot = startRotation;
|
||||
info.spawnerID = m_ParentEntity->GetObjectID();
|
||||
|
||||
auto* carEntity =
|
||||
EntityManager::Instance()->CreateEntity(info, nullptr, m_ParentEntity);
|
||||
|
||||
// Make the vehicle a child of the racing controller.
|
||||
m_ParentEntity->AddChild(carEntity);
|
||||
|
||||
auto* destroyableComponent = carEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
// Setup the vehicle stats.
|
||||
if (destroyableComponent != nullptr) {
|
||||
destroyableComponent->SetMaxImagination(60);
|
||||
destroyableComponent->SetImagination(0);
|
||||
}
|
||||
|
||||
// Setup the vehicle as being possessed by the player.
|
||||
auto* possessableComponent = carEntity->GetComponent<PossessableComponent>();
|
||||
|
||||
if (possessableComponent != nullptr) {
|
||||
possessableComponent->SetPossessor(player->GetObjectID());
|
||||
}
|
||||
|
||||
// Load the vehicle's assemblyPartLOTs for display.
|
||||
auto* moduleAssemblyComponent = carEntity->GetComponent<ModuleAssemblyComponent>();
|
||||
|
||||
if (moduleAssemblyComponent) {
|
||||
moduleAssemblyComponent->SetSubKey(item->GetSubKey());
|
||||
moduleAssemblyComponent->SetUseOptionalParts(false);
|
||||
|
||||
for (auto* config : item->GetConfig()) {
|
||||
if (config->GetKey() == u"assemblyPartLOTs") {
|
||||
moduleAssemblyComponent->SetAssemblyPartsLOTs(
|
||||
GeneralUtils::ASCIIToUTF16(config->GetValueAsString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the player as possessing the vehicle.
|
||||
auto* possessorComponent = player->GetComponent<PossessorComponent>();
|
||||
|
||||
if (possessorComponent != nullptr) {
|
||||
possessorComponent->SetPossessable(carEntity->GetObjectID());
|
||||
possessorComponent->SetPossessableType(ePossessionType::ATTACHED_VISIBLE); // for racing it's always Attached_Visible
|
||||
}
|
||||
|
||||
// Set the player's current activity as racing.
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->SetIsRacing(true);
|
||||
}
|
||||
|
||||
// Init the player's racing entry.
|
||||
if (initialLoad) {
|
||||
m_RacingPlayers.push_back(
|
||||
{ player->GetObjectID(),
|
||||
carEntity->GetObjectID(),
|
||||
static_cast<uint32_t>(m_RacingPlayers.size()),
|
||||
false,
|
||||
{},
|
||||
startPosition,
|
||||
startRotation,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0 });
|
||||
}
|
||||
|
||||
// Construct and serialize everything when done.
|
||||
|
||||
EntityManager::Instance()->ConstructEntity(carEntity);
|
||||
EntityManager::Instance()->SerializeEntity(player);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
GameMessages::SendRacingSetPlayerResetInfo(
|
||||
m_ParentEntity->GetObjectID(), 0, 0, player->GetObjectID(), startPosition, 1,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
const auto playerID = player->GetObjectID();
|
||||
|
||||
// Reset the player to the start position during downtime, in case something
|
||||
// went wrong.
|
||||
m_ParentEntity->AddCallbackTimer(1, [this, playerID]() {
|
||||
auto* player = EntityManager::Instance()->GetEntity(playerID);
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendRacingResetPlayerToLastReset(
|
||||
m_ParentEntity->GetObjectID(), playerID, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
});
|
||||
|
||||
GameMessages::SendSetJetPackMode(player, false);
|
||||
|
||||
// Set the vehicle's state.
|
||||
GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(),
|
||||
m_ParentEntity->GetObjectID(),
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendVehicleSetWheelLockState(carEntity->GetObjectID(), false,
|
||||
initialLoad,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
// Make sure everything has the correct position.
|
||||
GameMessages::SendTeleport(player->GetObjectID(), startPosition,
|
||||
startRotation, player->GetSystemAddress(), true);
|
||||
GameMessages::SendTeleport(carEntity->GetObjectID(), startPosition,
|
||||
startRotation, player->GetSystemAddress(), true);
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::OnRacingClientReady(Entity* player) {
|
||||
// Notify the other players that this player is ready.
|
||||
|
||||
for (auto& racingPlayer : m_RacingPlayers) {
|
||||
if (racingPlayer.playerID != player->GetObjectID()) {
|
||||
if (racingPlayer.playerLoaded) {
|
||||
GameMessages::SendRacingPlayerLoaded(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
racingPlayer.vehicleID, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
racingPlayer.playerLoaded = true;
|
||||
|
||||
GameMessages::SendRacingPlayerLoaded(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
racingPlayer.vehicleID, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::OnRequestDie(Entity* player) {
|
||||
// Sent by the client when they collide with something which should smash
|
||||
// them.
|
||||
|
||||
for (auto& racingPlayer : m_RacingPlayers) {
|
||||
if (racingPlayer.playerID != player->GetObjectID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* vehicle =
|
||||
EntityManager::Instance()->GetEntity(racingPlayer.vehicleID);
|
||||
|
||||
if (!vehicle) return;
|
||||
|
||||
if (!racingPlayer.noSmashOnReload) {
|
||||
racingPlayer.smashedTimes++;
|
||||
GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true,
|
||||
eKillType::VIOLENT, u"", 0, 0, 90.0f, false, true, 0);
|
||||
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
uint32_t respawnImagination = 0;
|
||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||
// Do not actually change the value yet. Do that on respawn.
|
||||
if (destroyableComponent) {
|
||||
respawnImagination = static_cast<int32_t>(ceil(destroyableComponent->GetImagination() / 2.0f / 10.0f)) * 10.0f;
|
||||
GameMessages::SendSetResurrectRestoreValues(vehicle, -1, -1, respawnImagination);
|
||||
}
|
||||
|
||||
// Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else...
|
||||
vehicle->AddCallbackTimer(2.0f, [=]() {
|
||||
if (!vehicle || !this->m_ParentEntity) return;
|
||||
GameMessages::SendRacingResetPlayerToLastReset(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendVehicleStopBoost(vehicle, player->GetSystemAddress(), true);
|
||||
|
||||
GameMessages::SendRacingSetPlayerResetInfo(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.lap,
|
||||
racingPlayer.respawnIndex, player->GetObjectID(),
|
||||
racingPlayer.respawnPosition, racingPlayer.respawnIndex + 1,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendResurrect(vehicle);
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||
if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination);
|
||||
EntityManager::Instance()->SerializeEntity(vehicle);
|
||||
});
|
||||
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->UpdatePlayerStatistic(RacingTimesWrecked);
|
||||
}
|
||||
} else {
|
||||
GameMessages::SendRacingSetPlayerResetInfo(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.lap,
|
||||
racingPlayer.respawnIndex, player->GetObjectID(),
|
||||
racingPlayer.respawnPosition, racingPlayer.respawnIndex + 1,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::SendRacingResetPlayerToLastReset(
|
||||
m_ParentEntity->GetObjectID(), racingPlayer.playerID,
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::OnRacingPlayerInfoResetFinished(Entity* player) {
|
||||
// When the player has respawned.
|
||||
|
||||
for (auto& racingPlayer : m_RacingPlayers) {
|
||||
if (racingPlayer.playerID != player->GetObjectID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* vehicle =
|
||||
EntityManager::Instance()->GetEntity(racingPlayer.vehicleID);
|
||||
|
||||
if (vehicle == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
racingPlayer.noSmashOnReload = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t button, const std::string& id) {
|
||||
auto* data = GetPlayerData(player->GetObjectID());
|
||||
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == "rewardButton") {
|
||||
if (data->collectedRewards) {
|
||||
return;
|
||||
}
|
||||
|
||||
data->collectedRewards = true;
|
||||
|
||||
// Calculate the score, different loot depending on player count
|
||||
const auto score = m_LoadedPlayers * 10 + data->finished;
|
||||
|
||||
LootGenerator::Instance().GiveActivityLoot(player, m_ParentEntity, m_ActivityID, score);
|
||||
|
||||
// Giving rewards
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_ParentEntity->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
|
||||
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent == nullptr) return;
|
||||
|
||||
missionComponent->Progress(eMissionTaskType::RACING, 0, (LWOOBJID)eRacingTaskParam::COMPETED_IN_RACE); // Progress task for competing in a race
|
||||
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, (LWOOBJID)eRacingTaskParam::SAFE_DRIVER); // Finish a race without being smashed.
|
||||
|
||||
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
||||
if (m_SoloRacing || m_LoadedPlayers > 2) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, data->finished, (LWOOBJID)eRacingTaskParam::FINISH_WITH_PLACEMENT); // Finish in 1st place on a race
|
||||
if (data->finished == 1) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS); // Finish in 1st place on multiple tracks.
|
||||
missionComponent->Progress(eMissionTaskType::RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::WIN_RACE_IN_WORLD); // Finished first place in specific world.
|
||||
}
|
||||
if (data->finished == m_LoadedPlayers) {
|
||||
missionComponent->Progress(eMissionTaskType::RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::LAST_PLACE_FINISH); // Finished first place in specific world.
|
||||
}
|
||||
}
|
||||
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
|
||||
auto* vehicle = EntityManager::Instance()->GetEntity(data->vehicleID);
|
||||
|
||||
if (vehicle == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exiting race
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_ParentEntity->GetObjectID(), 3, 0, LWOOBJID_EMPTY, u"",
|
||||
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* playerInstance = dynamic_cast<Player*>(player);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
|
||||
vehicle->Kill();
|
||||
}
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::Serialize(RakNet::BitStream* outBitStream,
|
||||
bool bIsInitialUpdate,
|
||||
unsigned int& flags) {
|
||||
// BEGIN Scripted Activity
|
||||
|
||||
outBitStream->Write1();
|
||||
|
||||
outBitStream->Write(static_cast<uint32_t>(m_RacingPlayers.size()));
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
outBitStream->Write(player.playerID);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
outBitStream->Write(player.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// END Scripted Activity
|
||||
|
||||
outBitStream->Write1(); // Dirty?
|
||||
outBitStream->Write(static_cast<uint16_t>(m_RacingPlayers.size()));
|
||||
|
||||
outBitStream->Write(!m_RacingPlayers.empty());
|
||||
if (!m_RacingPlayers.empty()) {
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
outBitStream->Write1(); // Has more date
|
||||
|
||||
outBitStream->Write(player.playerID);
|
||||
outBitStream->Write(player.vehicleID);
|
||||
outBitStream->Write(player.playerIndex);
|
||||
outBitStream->Write(player.playerLoaded);
|
||||
}
|
||||
|
||||
outBitStream->Write0(); // No more data
|
||||
}
|
||||
|
||||
outBitStream->Write(!m_RacingPlayers.empty());
|
||||
if (!m_RacingPlayers.empty()) {
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
outBitStream->Write1(); // Has more date
|
||||
|
||||
outBitStream->Write(player.playerID);
|
||||
outBitStream->Write<uint32_t>(0);
|
||||
}
|
||||
|
||||
outBitStream->Write0(); // No more data
|
||||
}
|
||||
|
||||
outBitStream->Write1(); // Dirty?
|
||||
|
||||
outBitStream->Write(m_RemainingLaps);
|
||||
|
||||
outBitStream->Write(static_cast<uint16_t>(m_PathName.size()));
|
||||
for (const auto character : m_PathName) {
|
||||
outBitStream->Write(character);
|
||||
}
|
||||
|
||||
outBitStream->Write1(); // ???
|
||||
outBitStream->Write1(); // ???
|
||||
|
||||
outBitStream->Write(m_LeadingPlayer);
|
||||
outBitStream->Write(m_RaceBestLap);
|
||||
outBitStream->Write(m_RaceBestTime);
|
||||
}
|
||||
|
||||
RacingPlayerInfo* VehicleRacingControlComponent::GetPlayerData(LWOOBJID playerID) {
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
if (player.playerID == playerID) {
|
||||
return &player;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VehicleRacingControlComponent::Update(float deltaTime) {
|
||||
// This method is a mess.
|
||||
|
||||
// Pre-load routine
|
||||
if (!m_Loaded) {
|
||||
// Check if any players has disconnected before loading in
|
||||
for (size_t i = 0; i < m_LobbyPlayers.size(); i++) {
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(m_LobbyPlayers[i]);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
--m_LoadedPlayers;
|
||||
|
||||
m_LobbyPlayers.erase(m_LobbyPlayers.begin() + i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_LoadedPlayers >= 2 || (m_LoadedPlayers == 1 && m_SoloRacing)) {
|
||||
m_LoadTimer += deltaTime;
|
||||
} else {
|
||||
m_EmptyTimer += deltaTime;
|
||||
}
|
||||
|
||||
// If a player happens to be left alone for more then 30 seconds without
|
||||
// anyone else loading in, send them back to the main world
|
||||
if (m_EmptyTimer >= 30) {
|
||||
for (const auto player : m_LobbyPlayers) {
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* playerInstance = dynamic_cast<Player*>(playerEntity);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
}
|
||||
|
||||
m_LobbyPlayers.clear();
|
||||
}
|
||||
|
||||
// From the first 2 players loading in the rest have a max of 15 seconds
|
||||
// to load in, can raise this if it's too low
|
||||
if (m_LoadTimer >= 15) {
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Loading all players...");
|
||||
|
||||
for (size_t positionNumber = 0; positionNumber < m_LobbyPlayers.size(); positionNumber++) {
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Loading player now!");
|
||||
|
||||
auto* player =
|
||||
EntityManager::Instance()->GetEntity(m_LobbyPlayers[positionNumber]);
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Loading player now NOW!");
|
||||
|
||||
LoadPlayerVehicle(player, positionNumber + 1, true);
|
||||
|
||||
m_Loaded = true;
|
||||
}
|
||||
|
||||
m_Loaded = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The players who will be participating have loaded
|
||||
if (!m_Started) {
|
||||
// Check if anyone has disconnected during this period
|
||||
for (size_t i = 0; i < m_RacingPlayers.size(); i++) {
|
||||
auto* playerEntity = EntityManager::Instance()->GetEntity(
|
||||
m_RacingPlayers[i].playerID);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
m_RacingPlayers.erase(m_RacingPlayers.begin() + i);
|
||||
|
||||
--m_LoadedPlayers;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If less then 2 players are left, send the rest back to the main world
|
||||
if (m_LoadedPlayers < 2 && !(m_LoadedPlayers == 1 && m_SoloRacing)) {
|
||||
for (const auto player : m_LobbyPlayers) {
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player);
|
||||
|
||||
if (playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* playerInstance = dynamic_cast<Player*>(playerEntity);
|
||||
|
||||
playerInstance->SendToZone(m_MainWorld);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all players have send a ready message
|
||||
|
||||
int32_t readyPlayers = 0;
|
||||
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
if (player.playerLoaded) {
|
||||
++readyPlayers;
|
||||
}
|
||||
}
|
||||
|
||||
if (readyPlayers >= m_LoadedPlayers) {
|
||||
// Setup for racing
|
||||
if (m_StartTimer == 0) {
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
m_ParentEntity->GetObjectID(), 1, 0, LWOOBJID_EMPTY, u"",
|
||||
LWOOBJID_EMPTY, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
for (const auto& player : m_RacingPlayers) {
|
||||
auto* vehicle =
|
||||
EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicle != nullptr && playerEntity != nullptr) {
|
||||
GameMessages::SendTeleport(
|
||||
player.playerID, player.respawnPosition,
|
||||
player.respawnRotation,
|
||||
playerEntity->GetSystemAddress(), true);
|
||||
|
||||
vehicle->SetPosition(player.respawnPosition);
|
||||
vehicle->SetRotation(player.respawnRotation);
|
||||
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr) {
|
||||
destroyableComponent->SetImagination(0);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(vehicle);
|
||||
EntityManager::Instance()->SerializeEntity(
|
||||
playerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn imagination pickups
|
||||
auto* minSpawner = dZoneManager::Instance()->GetSpawnersByName(
|
||||
"ImaginationSpawn_Min")[0];
|
||||
auto* medSpawner = dZoneManager::Instance()->GetSpawnersByName(
|
||||
"ImaginationSpawn_Med")[0];
|
||||
auto* maxSpawner = dZoneManager::Instance()->GetSpawnersByName(
|
||||
"ImaginationSpawn_Max")[0];
|
||||
|
||||
minSpawner->Activate();
|
||||
|
||||
if (m_LoadedPlayers > 2) {
|
||||
medSpawner->Activate();
|
||||
}
|
||||
|
||||
if (m_LoadedPlayers > 4) {
|
||||
maxSpawner->Activate();
|
||||
}
|
||||
|
||||
// Reset players to their start location, without smashing them
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
auto* vehicleEntity =
|
||||
EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicleEntity == nullptr || playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
player.noSmashOnReload = true;
|
||||
|
||||
OnRequestDie(playerEntity);
|
||||
}
|
||||
}
|
||||
// This 6 seconds seems to be hardcoded in the client, start race
|
||||
// after that amount of time
|
||||
else if (m_StartTimer >= 6) {
|
||||
// Activate the players movement
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
auto* vehicleEntity =
|
||||
EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicleEntity == nullptr || playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GameMessages::SendVehicleUnlockInput(
|
||||
player.vehicleID, false, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
// Start the race
|
||||
GameMessages::SendActivityStart(m_ParentEntity->GetObjectID(),
|
||||
UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
m_Started = true;
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent", "Starting race");
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
m_StartTime = std::time(nullptr);
|
||||
}
|
||||
|
||||
m_StartTimer += deltaTime;
|
||||
} else {
|
||||
m_StartTimer = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Race routines
|
||||
auto* path = dZoneManager::Instance()->GetZone()->GetPath(
|
||||
GeneralUtils::UTF16ToWTF8(m_PathName));
|
||||
|
||||
for (auto& player : m_RacingPlayers) {
|
||||
auto* vehicle = EntityManager::Instance()->GetEntity(player.vehicleID);
|
||||
auto* playerEntity =
|
||||
EntityManager::Instance()->GetEntity(player.playerID);
|
||||
|
||||
if (vehicle == nullptr || playerEntity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto vehiclePosition = vehicle->GetPosition();
|
||||
|
||||
// If the player is this far below the map, safe to assume they should
|
||||
// be smashed by death plane
|
||||
if (vehiclePosition.y < -500) {
|
||||
GameMessages::SendDie(vehicle, m_ParentEntity->GetObjectID(),
|
||||
LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0,
|
||||
true, false, 0);
|
||||
|
||||
OnRequestDie(playerEntity);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop through all the waypoints and see if the player has reached a
|
||||
// new checkpoint
|
||||
uint32_t respawnIndex = 0;
|
||||
for (const auto& waypoint : path->pathWaypoints) {
|
||||
if (player.lap == 3) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (player.respawnIndex == respawnIndex) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& position = waypoint.position;
|
||||
|
||||
if (std::abs((int)respawnIndex - (int)player.respawnIndex) > 10 &&
|
||||
player.respawnIndex != path->pathWaypoints.size() - 1) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Vector3::DistanceSquared(position, vehiclePosition) > 50 * 50) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only go upwards, except if we've lapped
|
||||
// Not sure how we are supposed to check if they've reach a
|
||||
// checkpoint, within 50 units seems safe
|
||||
if (!(respawnIndex > player.respawnIndex ||
|
||||
player.respawnIndex == path->pathWaypoints.size() - 1)) {
|
||||
++respawnIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some offset up to make they don't fall through the terrain on a
|
||||
// respawn, seems to fix itself to the track anyhow
|
||||
player.respawnPosition = position + NiPoint3::UNIT_Y * 5;
|
||||
player.respawnRotation = vehicle->GetRotation();
|
||||
player.respawnIndex = respawnIndex;
|
||||
|
||||
// Reached the start point, lapped
|
||||
if (respawnIndex == 0) {
|
||||
time_t lapTime = std::time(nullptr) - (player.lap == 0 ? m_StartTime : player.lapTime);
|
||||
|
||||
// Cheating check
|
||||
if (lapTime < 40) {
|
||||
continue;
|
||||
}
|
||||
|
||||
player.lap++;
|
||||
|
||||
player.lapTime = std::time(nullptr);
|
||||
|
||||
if (player.bestLapTime == 0 || player.bestLapTime > lapTime) {
|
||||
player.bestLapTime = lapTime;
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Best lap time (%llu)", lapTime);
|
||||
}
|
||||
|
||||
auto* missionComponent = playerEntity->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
|
||||
// Progress lap time tasks
|
||||
missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, (LWOOBJID)eRacingTaskParam::LAP_TIME);
|
||||
|
||||
if (player.lap == 3) {
|
||||
m_Finished++;
|
||||
player.finished = m_Finished;
|
||||
|
||||
const auto raceTime =
|
||||
(std::time(nullptr) - m_StartTime);
|
||||
|
||||
player.raceTime = raceTime;
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Completed time %llu, %llu",
|
||||
raceTime, raceTime * 1000);
|
||||
|
||||
// Entire race time
|
||||
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, (LWOOBJID)eRacingTaskParam::TOTAL_TRACK_TIME);
|
||||
|
||||
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackRaceCompleted(m_Finished == 1);
|
||||
}
|
||||
|
||||
// TODO: Figure out how to update the GUI leaderboard.
|
||||
}
|
||||
}
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Lapped (%i) in (%llu)", player.lap,
|
||||
lapTime);
|
||||
}
|
||||
|
||||
Game::logger->Log("VehicleRacingControlComponent",
|
||||
"Reached point (%i)/(%i)", player.respawnIndex,
|
||||
path->pathWaypoints.size());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string VehicleRacingControlComponent::FormatTimeString(time_t time) {
|
||||
int32_t min = time / 60;
|
||||
time -= min * 60;
|
||||
int32_t sec = time;
|
||||
|
||||
std::string minText;
|
||||
std::string secText;
|
||||
|
||||
if (min <= 0) {
|
||||
minText = "0";
|
||||
} else {
|
||||
minText = std::to_string(min);
|
||||
}
|
||||
|
||||
if (sec <= 0) {
|
||||
secText = "00";
|
||||
} else if (sec <= 9) {
|
||||
secText = "0" + std::to_string(sec);
|
||||
} else {
|
||||
secText = std::to_string(sec);
|
||||
}
|
||||
|
||||
return minText + ":" + secText + ".00";
|
||||
}
|
254
dGame/dComponents/VehicleRacingControlComponent.h
Normal file
254
dGame/dComponents/VehicleRacingControlComponent.h
Normal file
@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Thanks to Simon for his early research on the racing system.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "RacingControlComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Information for each player in the race
|
||||
*/
|
||||
struct RacingPlayerInfo {
|
||||
|
||||
/**
|
||||
* The ID of the player
|
||||
*/
|
||||
LWOOBJID playerID;
|
||||
|
||||
/**
|
||||
* The ID of the car the player is driving
|
||||
*/
|
||||
LWOOBJID vehicleID;
|
||||
|
||||
/**
|
||||
* The index of this player in the list of players
|
||||
*/
|
||||
uint32_t playerIndex;
|
||||
|
||||
/**
|
||||
* Whether the player has finished loading or not
|
||||
*/
|
||||
bool playerLoaded;
|
||||
|
||||
/**
|
||||
* Scripted activity component score
|
||||
*/
|
||||
float data[10]{};
|
||||
|
||||
/**
|
||||
* Point that the player will respawn at if they smash their car
|
||||
*/
|
||||
NiPoint3 respawnPosition;
|
||||
|
||||
/**
|
||||
* Rotation that the player will respawn at if they smash their car
|
||||
*/
|
||||
NiQuaternion respawnRotation;
|
||||
|
||||
/**
|
||||
* The index in the respawn point the player is now at
|
||||
*/
|
||||
uint32_t respawnIndex;
|
||||
|
||||
/**
|
||||
* The number of laps the player has completed
|
||||
*/
|
||||
uint32_t lap;
|
||||
|
||||
/**
|
||||
* Whether or not the player has finished the race
|
||||
*/
|
||||
uint32_t finished;
|
||||
|
||||
/**
|
||||
* Unused
|
||||
*/
|
||||
uint16_t reachedPoints;
|
||||
|
||||
/**
|
||||
* The fastest lap time of the player
|
||||
*/
|
||||
time_t bestLapTime = 0;
|
||||
|
||||
/**
|
||||
* The current lap time of the player
|
||||
*/
|
||||
time_t lapTime = 0;
|
||||
|
||||
/**
|
||||
* The number of times this player smashed their car
|
||||
*/
|
||||
uint32_t smashedTimes = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the player should be smashed if the game is reloaded
|
||||
*/
|
||||
bool noSmashOnReload = false;
|
||||
|
||||
/**
|
||||
* Whether or not this player has collected their rewards from completing the race
|
||||
*/
|
||||
bool collectedRewards = false;
|
||||
|
||||
/**
|
||||
* Unused
|
||||
*/
|
||||
time_t raceTime = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component that's attached to a manager entity in each race zone that loads player vehicles, keep scores, etc.
|
||||
*/
|
||||
class VehicleRacingControlComponent : public RacingControlComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL;
|
||||
|
||||
VehicleRacingControlComponent(Entity* parentEntity, int32_t componentId);
|
||||
~VehicleRacingControlComponent();
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void Update(float deltaTime);
|
||||
|
||||
/**
|
||||
* Invoked when a player loads into the zone.
|
||||
*/
|
||||
void OnPlayerLoaded(Entity* player);
|
||||
|
||||
/**
|
||||
* Initalize the player's vehicle.
|
||||
*
|
||||
* @param player The player who's vehicle to initialize.
|
||||
* @param initialLoad Is this the first time the player is loading in this race?
|
||||
*/
|
||||
void LoadPlayerVehicle(Entity* player, uint32_t positionNumber, bool initialLoad = false);
|
||||
|
||||
/**
|
||||
* Invoked when the client says it has loaded in.
|
||||
*/
|
||||
void OnRacingClientReady(Entity* player);
|
||||
|
||||
/**
|
||||
* Invoked when the client says it should be smashed.
|
||||
*/
|
||||
void OnRequestDie(Entity* player);
|
||||
|
||||
/**
|
||||
* Invoked when the player has finished respawning.
|
||||
*/
|
||||
void OnRacingPlayerInfoResetFinished(Entity* player);
|
||||
|
||||
/**
|
||||
* Invoked when the player responds to the GUI.
|
||||
*/
|
||||
void HandleMessageBoxResponse(Entity* player, int32_t button, const std::string& id);
|
||||
|
||||
/**
|
||||
* Get the racing data from a player's LWOOBJID.
|
||||
*/
|
||||
RacingPlayerInfo* GetPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Formats a time to a string, currently unused
|
||||
* @param time the time to format
|
||||
* @return the time formatted as string
|
||||
*/
|
||||
static std::string FormatTimeString(time_t time);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* The players that are currently racing
|
||||
*/
|
||||
std::vector<RacingPlayerInfo> m_RacingPlayers;
|
||||
|
||||
/**
|
||||
* The paths that are followed for the camera scenes
|
||||
*/
|
||||
std::u16string m_PathName;
|
||||
|
||||
/**
|
||||
* The ID of the activity for participating in this race
|
||||
*/
|
||||
uint32_t m_ActivityID;
|
||||
|
||||
/**
|
||||
* The world the players return to when they finish the race
|
||||
*/
|
||||
uint32_t m_MainWorld;
|
||||
|
||||
/**
|
||||
* The number of laps that are remaining for the winning player
|
||||
*/
|
||||
uint16_t m_RemainingLaps;
|
||||
|
||||
/**
|
||||
* The ID of the player that's currently winning the race
|
||||
*/
|
||||
LWOOBJID m_LeadingPlayer;
|
||||
|
||||
/**
|
||||
* The overall best lap from all the players
|
||||
*/
|
||||
float m_RaceBestLap;
|
||||
|
||||
/**
|
||||
* The overall best time from all the players
|
||||
*/
|
||||
float m_RaceBestTime;
|
||||
|
||||
/**
|
||||
* Whether or not the race has started
|
||||
*/
|
||||
bool m_Started;
|
||||
|
||||
/**
|
||||
* The time left until the race will start
|
||||
*/
|
||||
float m_StartTimer;
|
||||
|
||||
/**
|
||||
* The time left for loading the players
|
||||
*/
|
||||
float m_LoadTimer;
|
||||
|
||||
/**
|
||||
* Whether or not all players have loaded
|
||||
*/
|
||||
bool m_Loaded;
|
||||
|
||||
/**
|
||||
* The number of loaded players
|
||||
*/
|
||||
uint32_t m_LoadedPlayers;
|
||||
|
||||
/**
|
||||
* All the players that are in the lobby, loaded or not
|
||||
*/
|
||||
std::vector<LWOOBJID> m_LobbyPlayers;
|
||||
|
||||
/**
|
||||
* The number of players that have finished the race
|
||||
*/
|
||||
uint32_t m_Finished;
|
||||
|
||||
/**
|
||||
* The time the race was started
|
||||
*/
|
||||
time_t m_StartTime;
|
||||
|
||||
/**
|
||||
* Timer for tracking how long a player was alone in this race
|
||||
*/
|
||||
float m_EmptyTimer;
|
||||
|
||||
bool m_SoloRacing;
|
||||
|
||||
/**
|
||||
* Value for message box response to know if we are exiting the race via the activity dialogue
|
||||
*/
|
||||
const int32_t m_ActivityExitConfirm = 1;
|
||||
};
|
@ -25,7 +25,7 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "RacingControlComponent.h"
|
||||
#include "VehicleRacingControlComponent.h"
|
||||
#include "RequestServerProjectileImpact.h"
|
||||
#include "SyncSkill.h"
|
||||
#include "StartSkill.h"
|
||||
@ -126,10 +126,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
|
||||
|
||||
std::vector<Entity*> racingControllers = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||
for (Entity* racingController : racingControllers) {
|
||||
auto* racingComponent = racingController->GetComponent<RacingControlComponent>();
|
||||
if (racingComponent != nullptr) {
|
||||
racingComponent->OnPlayerLoaded(entity);
|
||||
}
|
||||
auto* vehicleRacingControlComponent = racingController->GetComponent<VehicleRacingControlComponent>();
|
||||
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnPlayerLoaded(entity);
|
||||
}
|
||||
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
|
@ -73,7 +73,7 @@
|
||||
#include "RenderComponent.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "RacingControlComponent.h"
|
||||
#include "VehicleRacingControlComponent.h"
|
||||
#include "RailActivatorComponent.h"
|
||||
#include "LevelProgressionComponent.h"
|
||||
|
||||
@ -3889,11 +3889,9 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream* inStream, Entity*
|
||||
scriptedActivityComponent->HandleMessageBoxResponse(userEntity, GeneralUtils::UTF16ToWTF8(identifier));
|
||||
}
|
||||
|
||||
auto* racingControlComponent = entity->GetComponent<RacingControlComponent>();
|
||||
auto* vehicleRacingControlComponent = entity->GetComponent<VehicleRacingControlComponent>();
|
||||
|
||||
if (racingControlComponent != nullptr) {
|
||||
racingControlComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier));
|
||||
}
|
||||
if (vehicleRacingControlComponent) vehicleRacingControlComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier));
|
||||
|
||||
for (auto* shootingGallery : EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY)) {
|
||||
shootingGallery->OnMessageBoxResponse(userEntity, iButton, identifier, userData);
|
||||
@ -4137,13 +4135,11 @@ void GameMessages::HandleRacingClientReady(RakNet::BitStream* inStream, Entity*
|
||||
return;
|
||||
}
|
||||
|
||||
auto* racingControlComponent = dZoneManager::Instance()->GetZoneControlObject()->GetComponent<RacingControlComponent>();
|
||||
auto* vehicleRacingControlComponent = dZoneManager::Instance()->GetZoneControlObject()->GetComponent<VehicleRacingControlComponent>();
|
||||
|
||||
if (racingControlComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!vehicleRacingControlComponent) return;
|
||||
|
||||
racingControlComponent->OnRacingClientReady(player);
|
||||
vehicleRacingControlComponent->OnRacingClientReady(player);
|
||||
}
|
||||
|
||||
|
||||
@ -4187,23 +4183,20 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity,
|
||||
|
||||
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
|
||||
|
||||
auto* racingControlComponent = zoneController->GetComponent<RacingControlComponent>();
|
||||
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>();
|
||||
|
||||
Game::logger->Log("HandleRequestDie", "Got die request: %i", entity->GetLOT());
|
||||
|
||||
if (racingControlComponent != nullptr) {
|
||||
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
||||
if (!vehicleRacingControlComponent) return;
|
||||
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
||||
|
||||
if (possessableComponent != nullptr) {
|
||||
entity = EntityManager::Instance()->GetEntity(possessableComponent->GetPossessor());
|
||||
if (possessableComponent) {
|
||||
entity = EntityManager::Instance()->GetEntity(possessableComponent->GetPossessor());
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
racingControlComponent->OnRequestDie(entity);
|
||||
if (!entity) return;
|
||||
}
|
||||
|
||||
vehicleRacingControlComponent->OnRequestDie(entity);
|
||||
}
|
||||
|
||||
|
||||
@ -4230,13 +4223,11 @@ void GameMessages::HandleRacingPlayerInfoResetFinished(RakNet::BitStream* inStre
|
||||
|
||||
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
|
||||
|
||||
auto* racingControlComponent = zoneController->GetComponent<RacingControlComponent>();
|
||||
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>();
|
||||
|
||||
Game::logger->Log("HandleRacingPlayerInfoResetFinished", "Got finished: %i", entity->GetLOT());
|
||||
|
||||
if (racingControlComponent != nullptr) {
|
||||
racingControlComponent->OnRacingPlayerInfoResetFinished(player);
|
||||
}
|
||||
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnRacingPlayerInfoResetFinished(player);
|
||||
}
|
||||
|
||||
void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RacingControlComponent.h"
|
||||
#include "VehicleRacingControlComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
|
||||
void RaceMaelstromGeiser::OnStartup(Entity* self) {
|
||||
@ -59,11 +59,9 @@ void RaceMaelstromGeiser::OnProximityUpdate(Entity* self, Entity* entering, std:
|
||||
|
||||
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
|
||||
|
||||
auto* racingControlComponent = zoneController->GetComponent<RacingControlComponent>();
|
||||
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>();
|
||||
|
||||
if (racingControlComponent != nullptr) {
|
||||
racingControlComponent->OnRequestDie(player);
|
||||
}
|
||||
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnRequestDie(player);
|
||||
}
|
||||
|
||||
void RaceMaelstromGeiser::OnTimerDone(Entity* self, std::string timerName) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "ActVehicleDeathTrigger.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "RacingControlComponent.h"
|
||||
#include "VehicleRacingControlComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "EntityManager.h"
|
||||
#include "PossessorComponent.h"
|
||||
@ -44,9 +44,7 @@ void ActVehicleDeathTrigger::OnCollisionPhantom(Entity* self, Entity* target) {
|
||||
|
||||
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
|
||||
|
||||
auto* racingControlComponent = zoneController->GetComponent<RacingControlComponent>();
|
||||
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>();
|
||||
|
||||
if (racingControlComponent != nullptr) {
|
||||
racingControlComponent->OnRequestDie(player);
|
||||
}
|
||||
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnRequestDie(player);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user