Merge branch 'components-wheeeee' of https://github.com/DarkflameUniverse/DarkflameServer into components-wheeeee

This commit is contained in:
David Markowitz 2023-06-26 21:59:02 -07:00
commit d9a3bea6d5
34 changed files with 2258 additions and 2259 deletions

View File

@ -114,7 +114,7 @@ enum class eReplicaComponentType : uint32_t {
CRAFTING, CRAFTING,
POSSESSABLE, POSSESSABLE,
LEVEL_PROGRESSION, LEVEL_PROGRESSION,
POSSESSOR, POSSESSION,
MOUNT_CONTROL, MOUNT_CONTROL,
UNKNOWN_112, UNKNOWN_112,
PROPERTY_PLAQUE, PROPERTY_PLAQUE,

View File

@ -1,5 +1,621 @@
#include "ActivityComponent.h" #include "ActivityComponent.h"
#include "GameMessages.h"
#include "CDClientManager.h"
#include "MissionComponent.h"
#include "Character.h"
#include "dZoneManager.h"
#include "ZoneInstanceManager.h"
#include "Game.h"
#include "dLogger.h"
#include <WorldPackets.h>
#include "EntityManager.h"
#include "ChatPackets.h"
#include "Player.h"
#include "PacketUtils.h"
#include "dServer.h"
#include "GeneralUtils.h"
#include "dZoneManager.h"
#include "dConfig.h"
#include "InventoryComponent.h"
#include "DestroyableComponent.h"
#include "Loot.h"
#include "eMissionTaskType.h"
#include "eMatchUpdate.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "CDCurrencyTableTable.h"
#include "CDActivityRewardsTable.h"
#include "CDActivitiesTable.h"
#include "LeaderboardManager.h"
ActivityComponent::ActivityComponent(Entity* parent) : Component(parent) { ActivityComponent::ActivityComponent(Entity* parent) : Component(parent) {
m_ActivityID = activityID;
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (CDActivities activity : activities) {
m_ActivityInfo = activity;
if (static_cast<LeaderboardType>(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
}
const auto& transferOverride = parent->GetVar<std::u16string>(u"transferZoneID");
if (!transferOverride.empty()) {
m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride));
// TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival)
// NOTE: 1301 is GF survival
if (m_ActivityInfo.instanceMapID == 1301) {
m_ActivityInfo.instanceMapID = 1302;
}
}
}
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
if (destroyableComponent) {
// check for LMIs and set the loot LMIs
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
uint32_t startingLMI = 0;
if (activityRewards.size() > 0) {
startingLMI = activityRewards[0].LootMatrixIndex;
}
if (startingLMI > 0) {
// now time for bodge :)
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
for (const auto& item : objectTemplateActivities) {
if (item.activityRating > 0 && item.activityRating < 5) {
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
}
}
}
}
}
ActivityComponent::~ActivityComponent()
= default;
void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
outBitStream->Write(true);
outBitStream->Write<uint32_t>(m_ActivityPlayers.size());
if (!m_ActivityPlayers.empty()) {
for (const auto& activityPlayer : m_ActivityPlayers) {
outBitStream->Write<LWOOBJID>(activityPlayer->playerID);
for (const auto& activityValue : activityPlayer->values) {
outBitStream->Write<float_t>(activityValue);
}
}
}
}
void ActivityComponent::ReloadConfig() {
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID;
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
} else {
m_ActivityInfo.minTeamSize = activity.minTeamSize;
m_ActivityInfo.minTeams = activity.minTeams;
}
}
}
void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
if (m_ActivityInfo.ActivityID == 103) {
return;
}
if (id == "LobbyExit") {
PlayerLeave(player->GetObjectID());
} else if (id == "PlayButton") {
PlayerJoin(player);
}
}
void ActivityComponent::PlayerJoin(Entity* player) {
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
return;
}
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
if (HasLobby()) {
PlayerJoinLobby(player);
} else if (!IsPlayedBy(player)) {
auto* instance = NewInstance();
instance->AddParticipant(player);
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ActivityComponent::PlayerJoinLobby(Entity* player) {
if (!m_ParentEntity->HasComponent(eReplicaComponentType::QUICK_BUILD))
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
newLobbyPlayer->entityID = player->GetObjectID();
Lobby* playerLobby = nullptr;
auto* character = player->GetCharacter();
if (character != nullptr)
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZone()->GetWorldID());
for (Lobby* lobby : m_Queue) {
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
// If an empty slot in an existing lobby is found
lobby->players.push_back(newLobbyPlayer);
playerLobby = lobby;
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
for (LobbyPlayer* joinedPlayer : lobby->players) {
auto* entity = joinedPlayer->GetEntity();
if (entity == nullptr) {
continue;
}
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
PlayerReady(entity, joinedPlayer->ready);
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
}
}
}
if (!playerLobby) {
// If all lobbies are full
playerLobby = new Lobby();
playerLobby->players.push_back(newLobbyPlayer);
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
m_Queue.push_back(playerLobby);
}
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
// Update the joining player on the match timer
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
}
}
void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
for (Lobby* lobby : m_Queue) {
for (int i = 0; i < lobby->players.size(); ++i) {
if (lobby->players[i]->entityID == playerID) {
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
for (LobbyPlayer* lobbyPlayer : lobby->players) {
auto* entity = lobbyPlayer->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
}
delete lobby->players[i];
lobby->players[i] = nullptr;
lobby->players.erase(lobby->players.begin() + i);
return;
}
}
}
}
void ActivityComponent::Update(float deltaTime) {
std::vector<Lobby*> lobbiesToRemove{};
// Ticks all the lobbies, not applicable for non-instance activities
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* player : lobby->players) {
auto* entity = player->GetEntity();
if (entity == nullptr) {
PlayerLeave(player->entityID);
return;
}
}
if (lobby->players.empty()) {
lobbiesToRemove.push_back(lobby);
continue;
}
// Update the match time for all players
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
for (LobbyPlayer* joinedPlayer : lobby->players) {
auto* entity = joinedPlayer->GetEntity();
if (entity == nullptr)
continue;
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
}
}
lobby->timer -= deltaTime;
}
bool lobbyReady = true;
for (LobbyPlayer* player : lobby->players) {
if (player->ready) continue;
lobbyReady = false;
}
// If everyone's ready, jump the timer
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
lobby->timer = m_ActivityInfo.startDelay / 1000;
// Update players in lobby on switch to start delay
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
for (LobbyPlayer* player : lobby->players) {
auto* entity = player->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
}
}
// The timer has elapsed, start the instance
if (lobby->timer <= 0.0f) {
Game::logger->Log("ActivityComponent", "Setting up instance.");
ActivityInstance* instance = NewInstance();
LoadPlayersIntoInstance(instance, lobby->players);
instance->StartZone();
lobbiesToRemove.push_back(lobby);
}
}
while (!lobbiesToRemove.empty()) {
RemoveLobby(lobbiesToRemove.front());
lobbiesToRemove.erase(lobbiesToRemove.begin());
}
}
void ActivityComponent::RemoveLobby(Lobby* lobby) {
for (int i = 0; i < m_Queue.size(); ++i) {
if (m_Queue[i] == lobby) {
m_Queue.erase(m_Queue.begin() + i);
return;
}
}
}
bool ActivityComponent::HasLobby() const {
// If the player is not in the world he has to be, create a lobby for the transfer
return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID();
}
bool ActivityComponent::IsValidActivity(Entity* player) {
// Makes it so that scripted activities with an unimplemented map cannot be joined
/*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) {
if (m_ParentEntity->GetLOT() == 4860) {
auto* missionComponent = player->GetComponent<MissionComponent>();
missionComponent->CompleteMission(229);
}
ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready.");
static_cast<Player*>(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state
return false;
}*/
return true;
}
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* lobbyPlayer : lobby->players) {
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
}
}
return false;
}
bool ActivityComponent::IsPlayedBy(Entity* player) const {
for (const auto* instance : this->m_Instances) {
for (const auto* instancePlayer : instance->GetParticipants()) {
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
return true;
}
}
return false;
}
bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
for (const auto* instance : this->m_Instances) {
for (const auto* instancePlayer : instance->GetParticipants()) {
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
return true;
}
}
return false;
}
bool ActivityComponent::TakeCost(Entity* player) const {
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
return true;
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr)
return false;
if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount)
return false;
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
return true;
}
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* lobbyPlayer : lobby->players) {
if (lobbyPlayer->entityID == player->GetObjectID()) {
lobbyPlayer->ready = bReady;
// Update players in lobby on player being ready
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
for (LobbyPlayer* otherPlayer : lobby->players) {
auto* entity = otherPlayer->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
}
}
}
}
}
ActivityInstance* ActivityComponent::NewInstance() {
auto* instance = new ActivityInstance(m_ParentEntity, m_ActivityInfo);
m_Instances.push_back(instance);
return instance;
}
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
for (LobbyPlayer* player : lobby) {
auto* entity = player->GetEntity();
if (entity == nullptr || !TakeCost(entity)) {
continue;
}
instance->AddParticipant(entity);
}
}
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
return m_Instances;
}
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
for (const auto* instance : GetInstances()) {
for (const auto* participant : instance->GetParticipants()) {
if (participant->GetObjectID() == playerID)
return const_cast<ActivityInstance*>(instance);
}
}
return nullptr;
}
void ActivityComponent::ClearInstances() {
for (ActivityInstance* instance : m_Instances) {
delete instance;
}
m_Instances.clear();
}
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
for (auto* activityData : m_ActivityPlayers) {
if (activityData->playerID == playerID) {
return activityData;
}
}
return nullptr;
}
void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
if (m_ActivityPlayers[i]->playerID == playerID) {
delete m_ActivityPlayers[i];
m_ActivityPlayers[i] = nullptr;
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return;
}
}
}
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
auto* data = GetActivityPlayerData(playerID);
if (data != nullptr)
return data;
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return GetActivityPlayerData(playerID);
}
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
auto value = -1.0f;
auto* data = GetActivityPlayerData(playerID);
if (data != nullptr) {
value = data->values[std::min(index, (uint32_t)9)];
}
return value;
}
void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
auto* data = AddActivityPlayerData(playerID);
if (data != nullptr) {
data->values[std::min(index, (uint32_t)9)] = value;
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
for (auto* instance : GetInstances()) {
auto participants = instance->GetParticipants();
for (const auto* participant : participants) {
if (participant != nullptr && participant->GetObjectID() == playerID) {
instance->RemoveParticipant(participant);
RemoveActivityPlayerData(playerID);
// If the instance is empty after the delete of the participant, delete the instance too
if (instance->GetParticipants().empty()) {
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
delete instance;
}
return;
}
}
}
}
void ActivityInstance::StartZone() {
if (m_Participants.empty())
return;
const auto& participants = GetParticipants();
if (participants.empty())
return;
auto* leader = participants[0];
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
// only make a team if we have more than one participant
if (participants.size() > 1) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size());
for (const auto& participant : m_Participants) {
bitStream.Write(participant);
}
bitStream.Write(zoneId);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
const auto cloneId = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
for (Entity* player : participants) {
const auto objid = player->GetObjectID();
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, m_ActivityInfo.instanceMapID, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
auto* player = EntityManager::Instance()->GetEntity(objid);
if (player == nullptr)
return;
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
if (player->GetCharacter()) {
player->GetCharacter()->SetZoneID(zoneID);
player->GetCharacter()->SetZoneInstance(zoneInstance);
player->GetCharacter()->SetZoneClone(zoneClone);
}
WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift);
return;
});
}
m_NextZoneCloneID++;
}
void ActivityInstance::RewardParticipant(Entity* participant) {
auto* missionComponent = participant->GetComponent<MissionComponent>();
if (missionComponent) {
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID);
}
// First, get the activity data
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
if (!activityRewards.empty()) {
uint32_t minCoins = 0;
uint32_t maxCoins = 0;
auto* currencyTableTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
if (!currencyTable.empty()) {
minCoins = currencyTable[0].minvalue;
maxCoins = currencyTable[0].maxvalue;
}
LootGenerator::Instance().DropLoot(participant, m_ParentEntity, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
}
}
std::vector<Entity*> ActivityInstance::GetParticipants() const {
std::vector<Entity*> entities;
entities.reserve(m_Participants.size());
for (const auto& id : m_Participants) {
auto* entity = EntityManager::Instance()->GetEntity(id);
if (entity != nullptr)
entities.push_back(entity);
}
return entities;
}
void ActivityInstance::AddParticipant(Entity* participant) {
const auto id = participant->GetObjectID();
if (std::count(m_Participants.begin(), m_Participants.end(), id))
return;
m_Participants.push_back(id);
}
void ActivityInstance::RemoveParticipant(const Entity* participant) {
const auto loadedParticipant = std::find(m_Participants.begin(), m_Participants.end(), participant->GetObjectID());
if (loadedParticipant != m_Participants.end()) {
m_Participants.erase(loadedParticipant);
}
}
uint32_t ActivityInstance::GetScore() const {
return score;
}
void ActivityInstance::SetScore(uint32_t score) {
this->score = score;
}
Entity* LobbyPlayer::GetEntity() const {
return EntityManager::Instance()->GetEntity(entityID);
} }

View File

@ -1,17 +1,379 @@
#ifndef __ACTIVITYCOMPONENT__H__ /*
#define __ACTIVITYCOMPONENT__H__ * Darkflame Universe
* Copyright 2018
*/
#include "CDClientManager.h"
#ifndef ACTIVITYCOMPONENT_H
#define ACTIVITYCOMPONENT_H
#include "BitStream.h"
#include "Entity.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
class Entity; #include "CDActivitiesTable.h"
class ActivityComponent : public Component { /**
* Represents an instance of an activity, having participants and score
*/
class ActivityInstance {
public: public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::INVALID; ActivityInstance(Entity* parent, CDActivities activityInfo) { m_ParentEntity = parent; m_ActivityInfo = activityInfo; };
ActivityComponent(Entity* parent); //~ActivityInstance();
/**
* Adds an entity to this activity
* @param participant the entity to add
*/
void AddParticipant(Entity* participant);
/**
* Removes all the participants from this activity
*/
void ClearParticipants() { m_Participants.clear(); };
/**
* Starts the instance world for this activity and sends all participants there
*/
void StartZone();
/**
* Gives the rewards for completing this activity to some participant
* @param participant the participant to give rewards to
*/
void RewardParticipant(Entity* participant);
/**
* Removes a participant from this activity
* @param participant the participant to remove
*/
void RemoveParticipant(const Entity* participant);
/**
* Returns all the participants of this activity
* @return all the participants of this activity
*/
std::vector<Entity*> GetParticipants() const;
/**
* Currently unused
*/
uint32_t GetScore() const;
/**
* Currently unused
*/
void SetScore(uint32_t score);
private:
/**
* Currently unused
*/
uint32_t score = 0;
/**
* The instance ID of this activity
*/
uint32_t m_NextZoneCloneID = 0;
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo;
/**
* The entity that owns this activity (the entity that has the ActivityComponent)
*/
Entity* m_ParentEntity;
/**
* All the participants of this activity
*/
std::vector<LWOOBJID> m_Participants;
}; };
#endif //!__ACTIVITYCOMPONENT__H__ /**
* Represents an entity in a lobby
*/
struct LobbyPlayer {
/**
* The ID of the entity that is in the lobby
*/
LWOOBJID entityID;
/**
* Whether or not the entity is ready
*/
bool ready = false;
/**
* Returns the entity that is in the lobby
* @return the entity that is in the lobby
*/
Entity* GetEntity() const;
};
/**
* Represents a lobby of players with a timer until it should start the activity
*/
struct Lobby {
/**
* The lobby of players
*/
std::vector<LobbyPlayer*> players;
/**
* The timer that determines when the activity should start
*/
float timer;
};
/**
* Represents the score for the player in an activity, one index might represent score, another one time, etc.
*/
struct ActivityPlayer {
/**
* The entity that the score is tracked for
*/
LWOOBJID playerID;
/**
* The list of score for this entity
*/
float values[10];
};
/**
* Welcome to the absolute behemoth that is the activity component. I have now clue how this was managed in
* live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that
* can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing
* and lobbying.
*/
class ActivityComponent : public Component {
public:
ActivityComponent(Entity* parent);
~ActivityComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
/**
* Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby
* @param player the entity to join the game
*/
void PlayerJoin(Entity* player);
/**
* Makes an entity join the lobby for this minigame, if it exists
* @param player the entity to join
*/
void PlayerJoinLobby(Entity* player);
/**
* Makes the player leave the lobby
* @param playerID the entity to leave the lobby
*/
void PlayerLeave(LWOOBJID playerID);
/**
* Removes the entity from the minigame (and its score)
* @param playerID the entity to remove from the minigame
*/
void PlayerRemove(LWOOBJID playerID);
/**
* Adds all the players to an instance of some activity
* @param instance the instance to load the players into
* @param lobby the players to load into the instance
*/
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
/**
* Removes a lobby from the activity manager
* @param lobby the lobby to remove
*/
void RemoveLobby(Lobby* lobby);
/**
* Marks a player as (un)ready in a lobby
* @param player the entity to mark
* @param bReady true if the entity is ready, false otherwise
*/
void PlayerReady(Entity* player, bool bReady);
/**
* Returns the ID of this activity
* @return the ID of this activity
*/
int GetActivityID() { return m_ActivityInfo.ActivityID; }
/**
* Returns if this activity has a lobby, e.g. if it needs to instance players to some other map
* @return true if this activity has a lobby, false otherwise
*/
bool HasLobby() const;
/**
* Checks if a player is currently waiting in a lobby
* @param player the entity to check for
* @return true if the entity is waiting in a lobby, false otherwise
*/
bool PlayerIsInQueue(Entity* player);
/**
* Checks if an entity is currently playing this activity
* @param player the entity to check
* @return true if the entity is playing this lobby, false otherwise
*/
bool IsPlayedBy(Entity* player) const;
/**
* Checks if an entity is currently playing this activity
* @param playerID the entity to check
* @return true if the entity is playing this lobby, false otherwise
*/
bool IsPlayedBy(LWOOBJID playerID) const;
/**
* Legacy: used to check for unimplemented maps, gladly, this now just returns true :)
*/
bool IsValidActivity(Entity* player);
/**
* Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity
* @param player the entity to take cost for
* @return true if the cost was successfully deducted, false otherwise
*/
bool TakeCost(Entity* player) const;
/**
* Handles any response from a player clicking on a lobby / instance menu
* @param player the entity that clicked
* @param id the message that was passed
*/
void HandleMessageBoxResponse(Entity* player, const std::string& id);
/**
* Creates a new instance for this activity
* @return a new instance for this activity
*/
ActivityInstance* NewInstance();
/**
* Returns all the currently active instances of this activity
* @return all the currently active instances of this activity
*/
const std::vector<ActivityInstance*>& GetInstances() const;
/**
* Returns the instance that some entity is currently playing in
* @param playerID the entity to check for
* @return if any, the instance that the entity is currently in
*/
ActivityInstance* GetInstance(const LWOOBJID playerID);
/**
* @brief Reloads the config settings for this component
*
*/
void ReloadConfig();
/**
* Removes all the instances
*/
void ClearInstances();
/**
* Returns all the score for the players that are currently playing this activity
* @return
*/
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
/**
* Returns activity data for a specific entity (e.g. score and such).
* @param playerID the entity to get data for
* @return the activity data (score) for the passed player in this activity, if it exists
*/
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
/**
* Sets some score value for an entity
* @param playerID the entity to set score for
* @param index the score index to set
* @param value the value to set in for that index
*/
void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value);
/**
* Returns activity score for the passed parameters
* @param playerID the entity to get score for
* @param index the index to get score for
* @return activity score for the passed parameters
*/
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
/**
* Removes activity score tracking for some entity
* @param playerID the entity to remove score for
*/
void RemoveActivityPlayerData(LWOOBJID playerID);
/**
* Adds activity score tracking for some entity
* @param playerID the entity to add the activity score for
* @return the created entry
*/
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
/**
* Sets the mapID that this activity points to
* @param mapID the map ID to set
*/
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
/**
* Returns the LMI that this activity points to for a team size
* @param teamSize the team size to get the LMI for
* @return the LMI that this activity points to for a team size
*/
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
private:
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo;
/**
* All the active instances of this activity
*/
std::vector<ActivityInstance*> m_Instances;
/**
* The current lobbies for this activity
*/
std::vector<Lobby*> m_Queue;
/**
* All the activity score for the players in this activity
*/
std::vector<ActivityPlayer*> m_ActivityPlayers;
/**
* LMIs for team sizes
*/
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
/**
* The activity id
*
*/
int32_t m_ActivityID;
};
#endif // ACTIVITYCOMPONENT_H

View File

@ -1,7 +1,6 @@
set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp" set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
"ActivityComponent.cpp" "ActivityComponent.cpp"
"BaseCombatAIComponent.cpp" "BaseCombatAIComponent.cpp"
"RacingControlComponent.cpp"
"BouncerComponent.cpp" "BouncerComponent.cpp"
"BuffComponent.cpp" "BuffComponent.cpp"
"BuildBorderComponent.cpp" "BuildBorderComponent.cpp"
@ -11,7 +10,7 @@ set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
"ControllablePhysicsComponent.cpp" "ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp" "DestroyableComponent.cpp"
"DonationVendorComponent.cpp" "DonationVendorComponent.cpp"
"GateRushControlComponent.cpp" "GateRushComponent.cpp"
"InventoryComponent.cpp" "InventoryComponent.cpp"
"ItemComponent.cpp" "ItemComponent.cpp"
"LevelProgressionComponent.cpp" "LevelProgressionComponent.cpp"
@ -28,13 +27,14 @@ set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
"PhantomPhysicsComponent.cpp" "PhantomPhysicsComponent.cpp"
"PlayerForcedMovementComponent.cpp" "PlayerForcedMovementComponent.cpp"
"PossessableComponent.cpp" "PossessableComponent.cpp"
"PossessorComponent.cpp" "PossessionComponent.cpp"
"PropertyComponent.cpp" "PropertyComponent.cpp"
"PropertyEntranceComponent.cpp" "PropertyEntranceComponent.cpp"
"PropertyManagementComponent.cpp" "PropertyManagementComponent.cpp"
"PropertyVendorComponent.cpp" "PropertyVendorComponent.cpp"
"ProximityMonitorComponent.cpp" "ProximityMonitorComponent.cpp"
"VehicleRacingControlComponent.cpp" "RacingComponent.cpp"
"RacingControlComponent.cpp"
"RacingSoundTriggerComponent.cpp" "RacingSoundTriggerComponent.cpp"
"RacingStatsComponent.cpp" "RacingStatsComponent.cpp"
"RailActivatorComponent.cpp" "RailActivatorComponent.cpp"

View File

@ -18,8 +18,8 @@ LWOActivityComponent
├── LWOShootingGalleryComponent ├── LWOShootingGalleryComponent
├── LWOScriptedActivityComponent ├── LWOScriptedActivityComponent
| └── LWOBaseRacingControlComponent -> RacingControlComponent | └── LWOBaseRacingControlComponent -> RacingControlComponent
| ├── LWORacingControlComponent -> VehicleRacingControlComponent | ├── LWORacingControlComponent -> RacingComponent
| └── LWOGateRushControlComponent | └── LWOGateRushControlComponent -> GateRushComponent
LWOBaseCombatAIComponent LWOBaseCombatAIComponent
├~~ LWOPathfindingControlComponent ├~~ LWOPathfindingControlComponent
├~~ LWOProximityMonitorComponent ├~~ LWOProximityMonitorComponent

View File

@ -28,7 +28,7 @@
#include "MissionComponent.h" #include "MissionComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "WorldConfig.h" #include "WorldConfig.h"
@ -646,7 +646,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
} }
// Dismount on the possessor hit // Dismount on the possessor hit
auto* possessor = m_ParentEntity->GetComponent<PossessorComponent>(); auto* possessor = m_ParentEntity->GetComponent<PossessionComponent>();
if (possessor) { if (possessor) {
auto possessableId = possessor->GetPossessable(); auto possessableId = possessor->GetPossessable();
if (possessableId != LWOOBJID_EMPTY) { if (possessableId != LWOOBJID_EMPTY) {

View File

@ -0,0 +1,5 @@
#include "GateRushComponent.h"
GateRushComponent::GateRushComponent(Entity* parent) : RacingControlComponent(parent) {
}

View File

@ -6,10 +6,10 @@
class Entity; class Entity;
class GateRushControlComponent : public RacingControlComponent { class GateRushComponent : public RacingControlComponent {
public: public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::GATE_RUSH_CONTROL; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::GATE_RUSH_CONTROL;
GateRushControlComponent(Entity* parent, int32_t componentId); GateRushComponent(Entity* parent);
}; };
#endif //!__GATERUSHCONTROLCOMPONENT__H__ #endif //!__GATERUSHCONTROLCOMPONENT__H__

View File

@ -1,5 +0,0 @@
#include "GateRushControlComponent.h"
GateRushControlComponent::GateRushControlComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) {
}

View File

@ -16,7 +16,7 @@
#include "ItemSet.h" #include "ItemSet.h"
#include "Player.h" #include "Player.h"
#include "PetComponent.h" #include "PetComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
#include "HavokVehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
@ -961,25 +961,25 @@ void InventoryComponent::HandlePossession(Item* item) {
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>(); auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
if (!characterComponent) return; if (!characterComponent) return;
auto* possessorComponent = m_ParentEntity->GetComponent<PossessorComponent>(); auto* possessionComponent = m_ParentEntity->GetComponent<PossessionComponent>();
if (!possessorComponent) return; if (!possessionComponent) return;
// Don't do anything if we are busy dismounting // Don't do anything if we are busy dismounting
if (possessorComponent->GetIsDismounting()) return; if (possessionComponent->GetIsDismounting()) return;
// Check to see if we are already mounting something // Check to see if we are already mounting something
auto* currentlyPossessedEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* currentlyPossessedEntity = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
auto currentlyPossessedItem = possessorComponent->GetMountItemID(); auto currentlyPossessedItem = possessionComponent->GetMountItemID();
if (currentlyPossessedItem) { if (currentlyPossessedItem) {
if (currentlyPossessedEntity) possessorComponent->Dismount(currentlyPossessedEntity); if (currentlyPossessedEntity) possessionComponent->Dismount(currentlyPossessedEntity);
return; return;
} }
GameMessages::SendSetStunned(m_ParentEntity->GetObjectID(), eStateChangeType::PUSH, m_ParentEntity->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); GameMessages::SendSetStunned(m_ParentEntity->GetObjectID(), eStateChangeType::PUSH, m_ParentEntity->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true);
// Set the mount Item ID so that we know what were handling // Set the mount Item ID so that we know what were handling
possessorComponent->SetMountItemID(item->GetId()); possessionComponent->SetMountItemID(item->GetId());
GameMessages::SendSetMountInventoryID(m_ParentEntity, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendSetMountInventoryID(m_ParentEntity, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS);
// Create entity to mount // Create entity to mount
@ -1019,8 +1019,8 @@ void InventoryComponent::HandlePossession(Item* item) {
possessableComponent->SetIsItemSpawned(true); possessableComponent->SetIsItemSpawned(true);
possessableComponent->SetPossessor(m_ParentEntity->GetObjectID()); possessableComponent->SetPossessor(m_ParentEntity->GetObjectID());
// Possess it // Possess it
possessorComponent->SetPossessable(mount->GetObjectID()); possessionComponent->SetPossessable(mount->GetObjectID());
possessorComponent->SetPossessableType(possessableComponent->GetPossessionType()); possessionComponent->SetPossessableType(possessableComponent->GetPossessionType());
} }
GameMessages::SendSetJetPackMode(m_ParentEntity, false); GameMessages::SendSetJetPackMode(m_ParentEntity, false);

View File

@ -18,7 +18,7 @@
#include "Component.h" #include "Component.h"
#include "ItemSetPassiveAbility.h" #include "ItemSetPassiveAbility.h"
#include "eItemSetPassiveAbilityID.h" #include "eItemSetPassiveAbilityID.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "eInventoryType.h" #include "eInventoryType.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "eLootSourceType.h" #include "eLootSourceType.h"

View File

@ -1,6 +1,6 @@
#include "MinigameControlComponent.h"
#include "Entity.h" #include "Entity.h"
#include "MinigameControlComponent.h"
MinigameControlComponent::MinigameControlComponent(Entity* parent) : ActivityComponent(parent) { MinigameControlComponent::MinigameControlComponent(Entity* parent) : ActivityComponent(parent) {

View File

@ -1,5 +1,5 @@
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Inventory.h" #include "Inventory.h"
#include "Item.h" #include "Item.h"
@ -48,7 +48,7 @@ void PossessableComponent::Dismount() {
} }
void PossessableComponent::OnUse(Entity* originator) { void PossessableComponent::OnUse(Entity* originator) {
auto* possessor = originator->GetComponent<PossessorComponent>(); auto* possessor = originator->GetComponent<PossessionComponent>();
if (possessor) { if (possessor) {
possessor->Mount(m_ParentEntity); possessor->Mount(m_ParentEntity);
} }

View File

@ -4,7 +4,7 @@
#include "Entity.h" #include "Entity.h"
#include "Component.h" #include "Component.h"
#include "Item.h" #include "Item.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "eAninmationFlags.h" #include "eAninmationFlags.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"

View File

@ -1,4 +1,4 @@
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
@ -7,11 +7,11 @@
#include "eControlScheme.h" #include "eControlScheme.h"
#include "eStateChangeType.h" #include "eStateChangeType.h"
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) { PossessionComponent::PossessionComponent(Entity* parent) : Component(parent) {
m_Possessable = LWOOBJID_EMPTY; m_Possessable = LWOOBJID_EMPTY;
} }
PossessorComponent::~PossessorComponent() { PossessionComponent::~PossessionComponent() {
if (m_Possessable != LWOOBJID_EMPTY) { if (m_Possessable != LWOOBJID_EMPTY) {
auto* mount = EntityManager::Instance()->GetEntity(m_Possessable); auto* mount = EntityManager::Instance()->GetEntity(m_Possessable);
if (mount) { if (mount) {
@ -26,7 +26,7 @@ PossessorComponent::~PossessorComponent() {
} }
} }
void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void PossessionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate); outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate);
if (m_DirtyPossesor || bIsInitialUpdate) { if (m_DirtyPossesor || bIsInitialUpdate) {
m_DirtyPossesor = false; m_DirtyPossesor = false;
@ -38,7 +38,7 @@ void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
} }
} }
void PossessorComponent::Mount(Entity* mount) { void PossessionComponent::Mount(Entity* mount) {
// Don't do anything if we are busy dismounting // Don't do anything if we are busy dismounting
if (GetIsDismounting() || !mount) return; if (GetIsDismounting() || !mount) return;
@ -62,7 +62,7 @@ void PossessorComponent::Mount(Entity* mount) {
EntityManager::Instance()->SerializeEntity(mount); EntityManager::Instance()->SerializeEntity(mount);
} }
void PossessorComponent::Dismount(Entity* mount, bool forceDismount) { void PossessionComponent::Dismount(Entity* mount, bool forceDismount) {
// Don't do anything if we are busy dismounting // Don't do anything if we are busy dismounting
if (GetIsDismounting() || !mount) return; if (GetIsDismounting() || !mount) return;
SetIsDismounting(true); SetIsDismounting(true);

View File

@ -16,12 +16,12 @@ enum class ePossessionType : uint8_t {
/** /**
* Represents an entity that can posess other entities. Generally used by players to drive a car. * Represents an entity that can posess other entities. Generally used by players to drive a car.
*/ */
class PossessorComponent : public Component { class PossessionComponent : public Component {
public: public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSION;
PossessorComponent(Entity* parent); PossessionComponent(Entity* parent);
~PossessorComponent() override; ~PossessionComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@ -97,13 +97,13 @@ private:
ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION; ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION;
/** /**
* @brief If the possessor is dirty * @brief If the possession is dirty
* *
*/ */
bool m_DirtyPossesor = false; bool m_DirtyPossesor = false;
/** /**
* @brief If the possessor is busy dismounting * @brief If the possession is busy dismounting
* *
*/ */
bool m_IsDismounting = false; bool m_IsDismounting = false;

View File

@ -25,7 +25,7 @@
#include "CppScripts.h" #include "CppScripts.h"
QuickBuildComponent::QuickBuildComponent(Entity* entity, uint32_t componentId) : ActivityComponent(entity) { QuickBuildComponent::QuickBuildComponent(Entity* entity) : ActivityComponent(entity) {
m_ComponentId = componentId; m_ComponentId = componentId;
std::u16string checkPreconditions = entity->GetVar<std::u16string>(u"CheckPrecondition"); std::u16string checkPreconditions = entity->GetVar<std::u16string>(u"CheckPrecondition");

View File

@ -0,0 +1,5 @@
#include "RacingComponent.h"
RacingComponent::RacingComponent(Entity* parent) : RacingControlComponent(parent) {
}

View File

@ -0,0 +1,16 @@
#ifndef __RACINGCOMPONENT__H__
#define __RACINGCOMPONENT__H__
#include "RacingControlComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class RacingComponent : public RacingControlComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL;
RacingComponent(Entity* parent);
};
#endif //!__RACINGCOMPONENT__H__

View File

@ -1,5 +1,880 @@
/**
* Thanks to Simon for his early research on the racing system.
*/
#include "RacingControlComponent.h" #include "RacingControlComponent.h"
RacingControlComponent::RacingControlComponent(Entity* parent, int32_t componentId) : ScriptedActivityComponent(parent, componentId) { #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 "PossessionComponent.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
RacingControlComponent::RacingControlComponent(Entity* parent) : ScriptedActivityComponent(parent) {
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* possessionComponent = player->GetComponent<PossessionComponent>();
if (possessionComponent != nullptr) {
possessionComponent->SetPossessable(carEntity->GetObjectID());
possessionComponent->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";
} }

View File

@ -1,16 +1,254 @@
#ifndef __RACINGCONTROLCOMPONENT__H__ /**
#define __RACINGCONTROLCOMPONENT__H__ * Thanks to Simon for his early research on the racing system.
*/
#pragma once
#include "BitStream.h"
#include "Entity.h"
#include "ScriptedActivityComponent.h" #include "ScriptedActivityComponent.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
class Entity; /**
* 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 RacingControlComponent : public ScriptedActivityComponent { class RacingControlComponent : public ScriptedActivityComponent {
public: public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL;
RacingControlComponent(Entity* parent, int32_t componentId);
RacingControlComponent(Entity* parentEntity);
~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;
}; };
#endif //!__RACINGCONTROLCOMPONENT__H__

View File

@ -1,621 +1,5 @@
#include "ScriptedActivityComponent.h" #include "ScriptedActivityComponent.h"
#include "GameMessages.h"
#include "CDClientManager.h"
#include "MissionComponent.h"
#include "Character.h"
#include "dZoneManager.h"
#include "ZoneInstanceManager.h"
#include "Game.h"
#include "dLogger.h"
#include <WorldPackets.h>
#include "EntityManager.h"
#include "ChatPackets.h"
#include "Player.h"
#include "PacketUtils.h"
#include "dServer.h"
#include "GeneralUtils.h"
#include "dZoneManager.h"
#include "dConfig.h"
#include "InventoryComponent.h"
#include "DestroyableComponent.h"
#include "Loot.h"
#include "eMissionTaskType.h"
#include "eMatchUpdate.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "CDCurrencyTableTable.h" ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent) : ActivityComponent(parent) {
#include "CDActivityRewardsTable.h"
#include "CDActivitiesTable.h"
#include "LeaderboardManager.h"
ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activityID) : Component(parent) {
m_ActivityID = activityID;
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (CDActivities activity : activities) {
m_ActivityInfo = activity;
if (static_cast<LeaderboardType>(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
}
const auto& transferOverride = parent->GetVar<std::u16string>(u"transferZoneID");
if (!transferOverride.empty()) {
m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride));
// TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival)
// NOTE: 1301 is GF survival
if (m_ActivityInfo.instanceMapID == 1301) {
m_ActivityInfo.instanceMapID = 1302;
}
}
}
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
if (destroyableComponent) {
// check for LMIs and set the loot LMIs
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
uint32_t startingLMI = 0;
if (activityRewards.size() > 0) {
startingLMI = activityRewards[0].LootMatrixIndex;
}
if (startingLMI > 0) {
// now time for bodge :)
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
for (const auto& item : objectTemplateActivities) {
if (item.activityRating > 0 && item.activityRating < 5) {
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
}
}
}
}
}
ScriptedActivityComponent::~ScriptedActivityComponent()
= default;
void ScriptedActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
outBitStream->Write(true);
outBitStream->Write<uint32_t>(m_ActivityPlayers.size());
if (!m_ActivityPlayers.empty()) {
for (const auto& activityPlayer : m_ActivityPlayers) {
outBitStream->Write<LWOOBJID>(activityPlayer->playerID);
for (const auto& activityValue : activityPlayer->values) {
outBitStream->Write<float_t>(activityValue);
}
}
}
}
void ScriptedActivityComponent::ReloadConfig() {
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID;
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
} else {
m_ActivityInfo.minTeamSize = activity.minTeamSize;
m_ActivityInfo.minTeams = activity.minTeams;
}
}
}
void ScriptedActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
if (m_ActivityInfo.ActivityID == 103) {
return;
}
if (id == "LobbyExit") {
PlayerLeave(player->GetObjectID());
} else if (id == "PlayButton") {
PlayerJoin(player);
}
}
void ScriptedActivityComponent::PlayerJoin(Entity* player) {
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
return;
}
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
if (HasLobby()) {
PlayerJoinLobby(player);
} else if (!IsPlayedBy(player)) {
auto* instance = NewInstance();
instance->AddParticipant(player);
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ScriptedActivityComponent::PlayerJoinLobby(Entity* player) {
if (!m_ParentEntity->HasComponent(eReplicaComponentType::QUICK_BUILD))
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
newLobbyPlayer->entityID = player->GetObjectID();
Lobby* playerLobby = nullptr;
auto* character = player->GetCharacter();
if (character != nullptr)
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZone()->GetWorldID());
for (Lobby* lobby : m_Queue) {
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
// If an empty slot in an existing lobby is found
lobby->players.push_back(newLobbyPlayer);
playerLobby = lobby;
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
for (LobbyPlayer* joinedPlayer : lobby->players) {
auto* entity = joinedPlayer->GetEntity();
if (entity == nullptr) {
continue;
}
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
PlayerReady(entity, joinedPlayer->ready);
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
}
}
}
if (!playerLobby) {
// If all lobbies are full
playerLobby = new Lobby();
playerLobby->players.push_back(newLobbyPlayer);
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
m_Queue.push_back(playerLobby);
}
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
// Update the joining player on the match timer
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
}
}
void ScriptedActivityComponent::PlayerLeave(LWOOBJID playerID) {
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
for (Lobby* lobby : m_Queue) {
for (int i = 0; i < lobby->players.size(); ++i) {
if (lobby->players[i]->entityID == playerID) {
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
for (LobbyPlayer* lobbyPlayer : lobby->players) {
auto* entity = lobbyPlayer->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
}
delete lobby->players[i];
lobby->players[i] = nullptr;
lobby->players.erase(lobby->players.begin() + i);
return;
}
}
}
}
void ScriptedActivityComponent::Update(float deltaTime) {
std::vector<Lobby*> lobbiesToRemove{};
// Ticks all the lobbies, not applicable for non-instance activities
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* player : lobby->players) {
auto* entity = player->GetEntity();
if (entity == nullptr) {
PlayerLeave(player->entityID);
return;
}
}
if (lobby->players.empty()) {
lobbiesToRemove.push_back(lobby);
continue;
}
// Update the match time for all players
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
for (LobbyPlayer* joinedPlayer : lobby->players) {
auto* entity = joinedPlayer->GetEntity();
if (entity == nullptr)
continue;
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
}
}
lobby->timer -= deltaTime;
}
bool lobbyReady = true;
for (LobbyPlayer* player : lobby->players) {
if (player->ready) continue;
lobbyReady = false;
}
// If everyone's ready, jump the timer
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
lobby->timer = m_ActivityInfo.startDelay / 1000;
// Update players in lobby on switch to start delay
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
for (LobbyPlayer* player : lobby->players) {
auto* entity = player->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
}
}
// The timer has elapsed, start the instance
if (lobby->timer <= 0.0f) {
Game::logger->Log("ScriptedActivityComponent", "Setting up instance.");
ActivityInstance* instance = NewInstance();
LoadPlayersIntoInstance(instance, lobby->players);
instance->StartZone();
lobbiesToRemove.push_back(lobby);
}
}
while (!lobbiesToRemove.empty()) {
RemoveLobby(lobbiesToRemove.front());
lobbiesToRemove.erase(lobbiesToRemove.begin());
}
}
void ScriptedActivityComponent::RemoveLobby(Lobby* lobby) {
for (int i = 0; i < m_Queue.size(); ++i) {
if (m_Queue[i] == lobby) {
m_Queue.erase(m_Queue.begin() + i);
return;
}
}
}
bool ScriptedActivityComponent::HasLobby() const {
// If the player is not in the world he has to be, create a lobby for the transfer
return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID();
}
bool ScriptedActivityComponent::IsValidActivity(Entity* player) {
// Makes it so that scripted activities with an unimplemented map cannot be joined
/*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) {
if (m_ParentEntity->GetLOT() == 4860) {
auto* missionComponent = player->GetComponent<MissionComponent>();
missionComponent->CompleteMission(229);
}
ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready.");
static_cast<Player*>(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state
return false;
}*/
return true;
}
bool ScriptedActivityComponent::PlayerIsInQueue(Entity* player) {
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* lobbyPlayer : lobby->players) {
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
}
}
return false;
}
bool ScriptedActivityComponent::IsPlayedBy(Entity* player) const {
for (const auto* instance : this->m_Instances) {
for (const auto* instancePlayer : instance->GetParticipants()) {
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
return true;
}
}
return false;
}
bool ScriptedActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
for (const auto* instance : this->m_Instances) {
for (const auto* instancePlayer : instance->GetParticipants()) {
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
return true;
}
}
return false;
}
bool ScriptedActivityComponent::TakeCost(Entity* player) const {
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
return true;
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr)
return false;
if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount)
return false;
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
return true;
}
void ScriptedActivityComponent::PlayerReady(Entity* player, bool bReady) {
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* lobbyPlayer : lobby->players) {
if (lobbyPlayer->entityID == player->GetObjectID()) {
lobbyPlayer->ready = bReady;
// Update players in lobby on player being ready
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
for (LobbyPlayer* otherPlayer : lobby->players) {
auto* entity = otherPlayer->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
}
}
}
}
}
ActivityInstance* ScriptedActivityComponent::NewInstance() {
auto* instance = new ActivityInstance(m_ParentEntity, m_ActivityInfo);
m_Instances.push_back(instance);
return instance;
}
void ScriptedActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
for (LobbyPlayer* player : lobby) {
auto* entity = player->GetEntity();
if (entity == nullptr || !TakeCost(entity)) {
continue;
}
instance->AddParticipant(entity);
}
}
const std::vector<ActivityInstance*>& ScriptedActivityComponent::GetInstances() const {
return m_Instances;
}
ActivityInstance* ScriptedActivityComponent::GetInstance(const LWOOBJID playerID) {
for (const auto* instance : GetInstances()) {
for (const auto* participant : instance->GetParticipants()) {
if (participant->GetObjectID() == playerID)
return const_cast<ActivityInstance*>(instance);
}
}
return nullptr;
}
void ScriptedActivityComponent::ClearInstances() {
for (ActivityInstance* instance : m_Instances) {
delete instance;
}
m_Instances.clear();
}
ActivityPlayer* ScriptedActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
for (auto* activityData : m_ActivityPlayers) {
if (activityData->playerID == playerID) {
return activityData;
}
}
return nullptr;
}
void ScriptedActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
if (m_ActivityPlayers[i]->playerID == playerID) {
delete m_ActivityPlayers[i];
m_ActivityPlayers[i] = nullptr;
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return;
}
}
}
ActivityPlayer* ScriptedActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
auto* data = GetActivityPlayerData(playerID);
if (data != nullptr)
return data;
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return GetActivityPlayerData(playerID);
}
float_t ScriptedActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
auto value = -1.0f;
auto* data = GetActivityPlayerData(playerID);
if (data != nullptr) {
value = data->values[std::min(index, (uint32_t)9)];
}
return value;
}
void ScriptedActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
auto* data = AddActivityPlayerData(playerID);
if (data != nullptr) {
data->values[std::min(index, (uint32_t)9)] = value;
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ScriptedActivityComponent::PlayerRemove(LWOOBJID playerID) {
for (auto* instance : GetInstances()) {
auto participants = instance->GetParticipants();
for (const auto* participant : participants) {
if (participant != nullptr && participant->GetObjectID() == playerID) {
instance->RemoveParticipant(participant);
RemoveActivityPlayerData(playerID);
// If the instance is empty after the delete of the participant, delete the instance too
if (instance->GetParticipants().empty()) {
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
delete instance;
}
return;
}
}
}
}
void ActivityInstance::StartZone() {
if (m_Participants.empty())
return;
const auto& participants = GetParticipants();
if (participants.empty())
return;
auto* leader = participants[0];
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
// only make a team if we have more than one participant
if (participants.size() > 1) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size());
for (const auto& participant : m_Participants) {
bitStream.Write(participant);
}
bitStream.Write(zoneId);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
const auto cloneId = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
for (Entity* player : participants) {
const auto objid = player->GetObjectID();
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, m_ActivityInfo.instanceMapID, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
auto* player = EntityManager::Instance()->GetEntity(objid);
if (player == nullptr)
return;
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
if (player->GetCharacter()) {
player->GetCharacter()->SetZoneID(zoneID);
player->GetCharacter()->SetZoneInstance(zoneInstance);
player->GetCharacter()->SetZoneClone(zoneClone);
}
WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift);
return;
});
}
m_NextZoneCloneID++;
}
void ActivityInstance::RewardParticipant(Entity* participant) {
auto* missionComponent = participant->GetComponent<MissionComponent>();
if (missionComponent) {
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID);
}
// First, get the activity data
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
if (!activityRewards.empty()) {
uint32_t minCoins = 0;
uint32_t maxCoins = 0;
auto* currencyTableTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
if (!currencyTable.empty()) {
minCoins = currencyTable[0].minvalue;
maxCoins = currencyTable[0].maxvalue;
}
LootGenerator::Instance().DropLoot(participant, m_ParentEntity, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
}
}
std::vector<Entity*> ActivityInstance::GetParticipants() const {
std::vector<Entity*> entities;
entities.reserve(m_Participants.size());
for (const auto& id : m_Participants) {
auto* entity = EntityManager::Instance()->GetEntity(id);
if (entity != nullptr)
entities.push_back(entity);
}
return entities;
}
void ActivityInstance::AddParticipant(Entity* participant) {
const auto id = participant->GetObjectID();
if (std::count(m_Participants.begin(), m_Participants.end(), id))
return;
m_Participants.push_back(id);
}
void ActivityInstance::RemoveParticipant(const Entity* participant) {
const auto loadedParticipant = std::find(m_Participants.begin(), m_Participants.end(), participant->GetObjectID());
if (loadedParticipant != m_Participants.end()) {
m_Participants.erase(loadedParticipant);
}
}
uint32_t ActivityInstance::GetScore() const {
return score;
}
void ActivityInstance::SetScore(uint32_t score) {
this->score = score;
}
Entity* LobbyPlayer::GetEntity() const {
return EntityManager::Instance()->GetEntity(entityID);
} }

View File

@ -1,381 +1,18 @@
/*
* Darkflame Universe
* Copyright 2018
*/
#include "CDClientManager.h" #ifndef __SCRIPTEDACTIVITYCOMPONENT__H__
#define __SCRIPTEDACTIVITYCOMPONENT__H__
#ifndef SCRIPTEDACTIVITYCOMPONENT_H #include "ActivityComponent.h"
#define SCRIPTEDACTIVITYCOMPONENT_H
#include "BitStream.h"
#include "Entity.h"
#include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "CDActivitiesTable.h" class Entity;
/** class ScriptedActivityComponent : public ActivityComponent {
* Represents an instance of an activity, having participants and score
*/
class ActivityInstance {
public:
ActivityInstance(Entity* parent, CDActivities activityInfo) { m_ParentEntity = parent; m_ActivityInfo = activityInfo; };
//~ActivityInstance();
/**
* Adds an entity to this activity
* @param participant the entity to add
*/
void AddParticipant(Entity* participant);
/**
* Removes all the participants from this activity
*/
void ClearParticipants() { m_Participants.clear(); };
/**
* Starts the instance world for this activity and sends all participants there
*/
void StartZone();
/**
* Gives the rewards for completing this activity to some participant
* @param participant the participant to give rewards to
*/
void RewardParticipant(Entity* participant);
/**
* Removes a participant from this activity
* @param participant the participant to remove
*/
void RemoveParticipant(const Entity* participant);
/**
* Returns all the participants of this activity
* @return all the participants of this activity
*/
std::vector<Entity*> GetParticipants() const;
/**
* Currently unused
*/
uint32_t GetScore() const;
/**
* Currently unused
*/
void SetScore(uint32_t score);
private:
/**
* Currently unused
*/
uint32_t score = 0;
/**
* The instance ID of this activity
*/
uint32_t m_NextZoneCloneID = 0;
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo;
/**
* The entity that owns this activity (the entity that has the ScriptedActivityComponent)
*/
Entity* m_ParentEntity;
/**
* All the participants of this activity
*/
std::vector<LWOOBJID> m_Participants;
};
/**
* Represents an entity in a lobby
*/
struct LobbyPlayer {
/**
* The ID of the entity that is in the lobby
*/
LWOOBJID entityID;
/**
* Whether or not the entity is ready
*/
bool ready = false;
/**
* Returns the entity that is in the lobby
* @return the entity that is in the lobby
*/
Entity* GetEntity() const;
};
/**
* Represents a lobby of players with a timer until it should start the activity
*/
struct Lobby {
/**
* The lobby of players
*/
std::vector<LobbyPlayer*> players;
/**
* The timer that determines when the activity should start
*/
float timer;
};
/**
* Represents the score for the player in an activity, one index might represent score, another one time, etc.
*/
struct ActivityPlayer {
/**
* The entity that the score is tracked for
*/
LWOOBJID playerID;
/**
* The list of score for this entity
*/
float values[10];
};
/**
* Welcome to the absolute behemoth that is the scripted activity component. I have now clue how this was managed in
* live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that
* can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing
* and lobbying.
*/
class ScriptedActivityComponent : public Component {
public: public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY;
ScriptedActivityComponent(Entity* parent);
ScriptedActivityComponent(Entity* parent, int activityID);
~ScriptedActivityComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
/**
* Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby
* @param player the entity to join the game
*/
void PlayerJoin(Entity* player);
/**
* Makes an entity join the lobby for this minigame, if it exists
* @param player the entity to join
*/
void PlayerJoinLobby(Entity* player);
/**
* Makes the player leave the lobby
* @param playerID the entity to leave the lobby
*/
void PlayerLeave(LWOOBJID playerID);
/**
* Removes the entity from the minigame (and its score)
* @param playerID the entity to remove from the minigame
*/
void PlayerRemove(LWOOBJID playerID);
/**
* Adds all the players to an instance of some activity
* @param instance the instance to load the players into
* @param lobby the players to load into the instance
*/
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
/**
* Removes a lobby from the activity manager
* @param lobby the lobby to remove
*/
void RemoveLobby(Lobby* lobby);
/**
* Marks a player as (un)ready in a lobby
* @param player the entity to mark
* @param bReady true if the entity is ready, false otherwise
*/
void PlayerReady(Entity* player, bool bReady);
/**
* Returns the ID of this activity
* @return the ID of this activity
*/
int GetActivityID() { return m_ActivityInfo.ActivityID; }
/**
* Returns if this activity has a lobby, e.g. if it needs to instance players to some other map
* @return true if this activity has a lobby, false otherwise
*/
bool HasLobby() const;
/**
* Checks if a player is currently waiting in a lobby
* @param player the entity to check for
* @return true if the entity is waiting in a lobby, false otherwise
*/
bool PlayerIsInQueue(Entity* player);
/**
* Checks if an entity is currently playing this activity
* @param player the entity to check
* @return true if the entity is playing this lobby, false otherwise
*/
bool IsPlayedBy(Entity* player) const;
/**
* Checks if an entity is currently playing this activity
* @param playerID the entity to check
* @return true if the entity is playing this lobby, false otherwise
*/
bool IsPlayedBy(LWOOBJID playerID) const;
/**
* Legacy: used to check for unimplemented maps, gladly, this now just returns true :)
*/
bool IsValidActivity(Entity* player);
/**
* Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity
* @param player the entity to take cost for
* @return true if the cost was successfully deducted, false otherwise
*/
bool TakeCost(Entity* player) const;
/**
* Handles any response from a player clicking on a lobby / instance menu
* @param player the entity that clicked
* @param id the message that was passed
*/
void HandleMessageBoxResponse(Entity* player, const std::string& id);
/**
* Creates a new instance for this activity
* @return a new instance for this activity
*/
ActivityInstance* NewInstance();
/**
* Returns all the currently active instances of this activity
* @return all the currently active instances of this activity
*/
const std::vector<ActivityInstance*>& GetInstances() const;
/**
* Returns the instance that some entity is currently playing in
* @param playerID the entity to check for
* @return if any, the instance that the entity is currently in
*/
ActivityInstance* GetInstance(const LWOOBJID playerID);
/**
* @brief Reloads the config settings for this component
*
*/
void ReloadConfig();
/**
* Removes all the instances
*/
void ClearInstances();
/**
* Returns all the score for the players that are currently playing this activity
* @return
*/
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
/**
* Returns activity data for a specific entity (e.g. score and such).
* @param playerID the entity to get data for
* @return the activity data (score) for the passed player in this activity, if it exists
*/
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
/**
* Sets some score value for an entity
* @param playerID the entity to set score for
* @param index the score index to set
* @param value the value to set in for that index
*/
void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value);
/**
* Returns activity score for the passed parameters
* @param playerID the entity to get score for
* @param index the index to get score for
* @return activity score for the passed parameters
*/
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
/**
* Removes activity score tracking for some entity
* @param playerID the entity to remove score for
*/
void RemoveActivityPlayerData(LWOOBJID playerID);
/**
* Adds activity score tracking for some entity
* @param playerID the entity to add the activity score for
* @return the created entry
*/
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
/**
* Sets the mapID that this activity points to
* @param mapID the map ID to set
*/
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
/**
* Returns the LMI that this activity points to for a team size
* @param teamSize the team size to get the LMI for
* @return the LMI that this activity points to for a team size
*/
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
private:
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo;
/**
* All the active instances of this activity
*/
std::vector<ActivityInstance*> m_Instances;
/**
* The current lobbies for this activity
*/
std::vector<Lobby*> m_Queue;
/**
* All the activity score for the players in this activity
*/
std::vector<ActivityPlayer*> m_ActivityPlayers;
/**
* LMIs for team sizes
*/
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
/**
* The activity id
*
*/
int32_t m_ActivityID;
}; };
#endif // SCRIPTEDACTIVITYCOMPONENT_H #endif //!__SCRIPTEDACTIVITYCOMPONENT__H__

View File

@ -1,880 +0,0 @@
/**
* 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";
}

View File

@ -1,254 +0,0 @@
/**
* 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;
};

View File

@ -56,9 +56,8 @@
#include "PetComponent.h" #include "PetComponent.h"
#include "HavokVehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
#include "RacingControlComponent.h"
#include "SoundTriggerComponent.h" #include "SoundTriggerComponent.h"
#include "ShootingGalleryComponent.h" #include "ShootingGalleryComponent.h"
#include "RailActivatorComponent.h" #include "RailActivatorComponent.h"
@ -71,10 +70,11 @@
#include "MinigameControlComponent.h" #include "MinigameControlComponent.h"
#include "ItemComponent.h" #include "ItemComponent.h"
#include "DonationVendorComponent.h" #include "DonationVendorComponent.h"
#include "GateRushControlComponent.h" #include "GateRushComponent.h"
#include "RacingSoundTriggerComponent.h" #include "RacingSoundTriggerComponent.h"
#include "AchievementVendorComponent.h" #include "AchievementVendorComponent.h"
#include "MutableModelBehaviorComponent.h" #include "MutableModelBehaviorComponent.h"
#include "RacingComponent.h"
// Table includes // Table includes
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
@ -266,7 +266,7 @@ void Entity::Initialize() {
case eReplicaComponentType::CHARACTER: case eReplicaComponentType::CHARACTER:
AddComponent<CharacterComponent>(m_Character); AddComponent<CharacterComponent>(m_Character);
AddComponent<MissionComponent>(); AddComponent<MissionComponent>();
AddComponent<PossessorComponent>(); AddComponent<PossessionComponent>();
AddComponent<LevelProgressionComponent>(); AddComponent<LevelProgressionComponent>();
AddComponent<PlayerForcedMovementComponent>(); AddComponent<PlayerForcedMovementComponent>();
break; break;
@ -430,7 +430,7 @@ void Entity::Initialize() {
AddComponent<RocketLaunchpadControlComponent>(componentId); AddComponent<RocketLaunchpadControlComponent>(componentId);
break; break;
case eReplicaComponentType::RACING_CONTROL: case eReplicaComponentType::RACING_CONTROL:
AddComponent<RacingControlComponent>(componentId); AddComponent<RacingComponent>(componentId);
m_IsGhostingCandidate = false; m_IsGhostingCandidate = false;
break; break;
case eReplicaComponentType::MISSION_OFFER: case eReplicaComponentType::MISSION_OFFER:
@ -470,7 +470,7 @@ void Entity::Initialize() {
hasProximityMonitorComponent = true; hasProximityMonitorComponent = true;
break; break;
case eReplicaComponentType::GATE_RUSH_CONTROL: case eReplicaComponentType::GATE_RUSH_CONTROL:
AddComponent<GateRushControlComponent>(componentId); AddComponent<GateRushComponent>(componentId);
break; break;
case eReplicaComponentType::RACING_SOUND_TRIGGER: case eReplicaComponentType::RACING_SOUND_TRIGGER:
AddComponent<RacingSoundTriggerComponent>(); AddComponent<RacingSoundTriggerComponent>();
@ -1038,11 +1038,11 @@ void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u
Kill(EntityManager::Instance()->GetEntity(source)); Kill(EntityManager::Instance()->GetEntity(source));
return; return;
} }
auto* possessorComponent = GetComponent<PossessorComponent>(); auto* possessionComponent = GetComponent<PossessionComponent>();
if (possessorComponent) { if (possessionComponent) {
if (possessorComponent->GetPossessable() != LWOOBJID_EMPTY) { if (possessionComponent->GetPossessable() != LWOOBJID_EMPTY) {
auto* mount = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* mount = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (mount) possessorComponent->Dismount(mount, true); if (mount) possessionComponent->Dismount(mount, true);
} }
} }

View File

@ -25,7 +25,7 @@
#include "CDClientManager.h" #include "CDClientManager.h"
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
#include "SkillComponent.h" #include "SkillComponent.h"
#include "VehicleRacingControlComponent.h" #include "RacingComponent.h"
#include "RequestServerProjectileImpact.h" #include "RequestServerProjectileImpact.h"
#include "SyncSkill.h" #include "SyncSkill.h"
#include "StartSkill.h" #include "StartSkill.h"
@ -126,8 +126,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
std::vector<Entity*> racingControllers = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL); std::vector<Entity*> racingControllers = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
for (Entity* racingController : racingControllers) { for (Entity* racingController : racingControllers) {
auto* vehicleRacingControlComponent = racingController->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = racingController->GetComponent<RacingComponent>();
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnPlayerLoaded(entity); if (racingComponent) racingComponent->OnPlayerLoaded(entity);
} }
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity(); Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();

View File

@ -72,8 +72,8 @@
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "VehicleRacingControlComponent.h" #include "RacingComponent.h"
#include "RailActivatorComponent.h" #include "RailActivatorComponent.h"
#include "LevelProgressionComponent.h" #include "LevelProgressionComponent.h"
@ -3889,9 +3889,9 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream* inStream, Entity*
scriptedActivityComponent->HandleMessageBoxResponse(userEntity, GeneralUtils::UTF16ToWTF8(identifier)); scriptedActivityComponent->HandleMessageBoxResponse(userEntity, GeneralUtils::UTF16ToWTF8(identifier));
} }
auto* vehicleRacingControlComponent = entity->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = entity->GetComponent<RacingComponent>();
if (vehicleRacingControlComponent) vehicleRacingControlComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier)); if (racingComponent) racingComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier));
for (auto* shootingGallery : EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY)) { for (auto* shootingGallery : EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY)) {
shootingGallery->OnMessageBoxResponse(userEntity, iButton, identifier, userData); shootingGallery->OnMessageBoxResponse(userEntity, iButton, identifier, userData);
@ -4053,21 +4053,21 @@ void GameMessages::HandleDismountComplete(RakNet::BitStream* inStream, Entity* e
// If we aren't possessing somethings, the don't do anything // If we aren't possessing somethings, the don't do anything
if (objectId != LWOOBJID_EMPTY) { if (objectId != LWOOBJID_EMPTY) {
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
auto* mount = EntityManager::Instance()->GetEntity(objectId); auto* mount = EntityManager::Instance()->GetEntity(objectId);
// make sure we have the things we need and they aren't null // make sure we have the things we need and they aren't null
if (possessorComponent && mount) { if (possessionComponent && mount) {
if (!possessorComponent->GetIsDismounting()) return; if (!possessionComponent->GetIsDismounting()) return;
possessorComponent->SetIsDismounting(false); possessionComponent->SetIsDismounting(false);
possessorComponent->SetPossessable(LWOOBJID_EMPTY); possessionComponent->SetPossessable(LWOOBJID_EMPTY);
possessorComponent->SetPossessableType(ePossessionType::NO_POSSESSION); possessionComponent->SetPossessableType(ePossessionType::NO_POSSESSION);
// character related things // character related things
auto* character = entity->GetComponent<CharacterComponent>(); auto* character = entity->GetComponent<CharacterComponent>();
if (character) { if (character) {
// If we had an active item turn it off // If we had an active item turn it off
if (possessorComponent->GetMountItemID() != LWOOBJID_EMPTY) GameMessages::SendMarkInventoryItemAsActive(entity->GetObjectID(), false, eUnequippableActiveType::MOUNT, possessorComponent->GetMountItemID(), entity->GetSystemAddress()); if (possessionComponent->GetMountItemID() != LWOOBJID_EMPTY) GameMessages::SendMarkInventoryItemAsActive(entity->GetObjectID(), false, eUnequippableActiveType::MOUNT, possessionComponent->GetMountItemID(), entity->GetSystemAddress());
possessorComponent->SetMountItemID(LWOOBJID_EMPTY); possessionComponent->SetMountItemID(LWOOBJID_EMPTY);
} }
// Set that the controllabel phsyics comp is teleporting // Set that the controllabel phsyics comp is teleporting
@ -4135,11 +4135,11 @@ void GameMessages::HandleRacingClientReady(RakNet::BitStream* inStream, Entity*
return; return;
} }
auto* vehicleRacingControlComponent = dZoneManager::Instance()->GetZoneControlObject()->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = dZoneManager::Instance()->GetZoneControlObject()->GetComponent<RacingComponent>();
if (!vehicleRacingControlComponent) return; if (!racingComponent) return;
vehicleRacingControlComponent->OnRacingClientReady(player); racingComponent->OnRacingClientReady(player);
} }
@ -4183,11 +4183,11 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity,
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject(); auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = zoneController->GetComponent<RacingComponent>();
Game::logger->Log("HandleRequestDie", "Got die request: %i", entity->GetLOT()); Game::logger->Log("HandleRequestDie", "Got die request: %i", entity->GetLOT());
if (!vehicleRacingControlComponent) return; if (!racingComponent) return;
auto* possessableComponent = entity->GetComponent<PossessableComponent>(); auto* possessableComponent = entity->GetComponent<PossessableComponent>();
if (possessableComponent) { if (possessableComponent) {
@ -4196,7 +4196,7 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity,
if (!entity) return; if (!entity) return;
} }
vehicleRacingControlComponent->OnRequestDie(entity); racingComponent->OnRequestDie(entity);
} }
@ -4223,11 +4223,11 @@ void GameMessages::HandleRacingPlayerInfoResetFinished(RakNet::BitStream* inStre
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject(); auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = zoneController->GetComponent<RacingComponent>();
Game::logger->Log("HandleRacingPlayerInfoResetFinished", "Got finished: %i", entity->GetLOT()); Game::logger->Log("HandleRacingPlayerInfoResetFinished", "Got finished: %i", entity->GetLOT());
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnRacingPlayerInfoResetFinished(player); if (racingComponent) racingComponent->OnRacingPlayerInfoResetFinished(player);
} }
void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) { void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) {

View File

@ -58,7 +58,7 @@
#include "ProximityMonitorComponent.h" #include "ProximityMonitorComponent.h"
#include "dpShapeSphere.h" #include "dpShapeSphere.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "HavokVehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "BuffComponent.h" #include "BuffComponent.h"
#include "SkillComponent.h" #include "SkillComponent.h"
@ -413,9 +413,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size()); std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size());
RenderComponent::PlayAnimation(entity, anim); RenderComponent::PlayAnimation(entity, anim);
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
if (possessorComponent) { if (possessionComponent) {
auto* possessedComponent = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* possessedComponent = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (possessedComponent) RenderComponent::PlayAnimation(possessedComponent, anim); if (possessedComponent) RenderComponent::PlayAnimation(possessedComponent, anim);
} }
} }
@ -474,7 +474,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
controllablePhysicsComponent->SetSpeedMultiplier(boost); controllablePhysicsComponent->SetSpeedMultiplier(boost);
// speedboost possesables // speedboost possesables
auto* possessor = entity->GetComponent<PossessorComponent>(); auto* possessor = entity->GetComponent<PossessionComponent>();
if (possessor) { if (possessor) {
auto possessedID = possessor->GetPossessable(); auto possessedID = possessor->GetPossessable();
if (possessedID != LWOOBJID_EMPTY) { if (possessedID != LWOOBJID_EMPTY) {
@ -935,9 +935,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
if (possessorComponent) { if (possessionComponent) {
auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* possassableEntity = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (possassableEntity != nullptr) { if (possassableEntity != nullptr) {
auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent<HavokVehiclePhysicsComponent>(); auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent<HavokVehiclePhysicsComponent>();
@ -962,12 +962,12 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
if (chatCommand == "dismount" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (chatCommand == "dismount" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
if (possessorComponent) { if (possessionComponent) {
auto possessableId = possessorComponent->GetPossessable(); auto possessableId = possessionComponent->GetPossessable();
if (possessableId != LWOOBJID_EMPTY) { if (possessableId != LWOOBJID_EMPTY) {
auto* possessableEntity = EntityManager::Instance()->GetEntity(possessableId); auto* possessableEntity = EntityManager::Instance()->GetEntity(possessableId);
if (possessableEntity) possessorComponent->Dismount(possessableEntity, true); if (possessableEntity) possessionComponent->Dismount(possessableEntity, true);
} }
} }
} }
@ -1615,13 +1615,13 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
if ((chatCommand == "boost") && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if ((chatCommand == "boost") && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
if (possessorComponent == nullptr) { if (possessionComponent == nullptr) {
return; return;
} }
auto* vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* vehicle = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (vehicle == nullptr) { if (vehicle == nullptr) {
return; return;
@ -1647,10 +1647,10 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
if ((chatCommand == "unboost") && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if ((chatCommand == "unboost") && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
if (possessorComponent == nullptr) return; if (possessionComponent == nullptr) return;
auto* vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* vehicle = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (vehicle == nullptr) return; if (vehicle == nullptr) return;
GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);

View File

@ -25,7 +25,7 @@
#include "dZoneManager.h" #include "dZoneManager.h"
#include "Player.h" #include "Player.h"
#include "Zone.h" #include "Zone.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "HavokVehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "dConfig.h" #include "dConfig.h"
@ -100,7 +100,7 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
} }
*/ */
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessionComponent = entity->GetComponent<PossessionComponent>();
NiPoint3 position; NiPoint3 position;
inStream.Read(position.x); inStream.Read(position.x);
@ -165,8 +165,8 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
bool updateChar = true; bool updateChar = true;
if (possessorComponent != nullptr) { if (possessionComponent != nullptr) {
auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* possassableEntity = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (possassableEntity != nullptr) { if (possassableEntity != nullptr) {
auto* possessableComponent = possassableEntity->GetComponent<PossessableComponent>(); auto* possessableComponent = possassableEntity->GetComponent<PossessableComponent>();

View File

@ -1,9 +1,9 @@
#include "RaceMaelstromGeiser.h" #include "RaceMaelstromGeiser.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "VehicleRacingControlComponent.h" #include "RacingComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
void RaceMaelstromGeiser::OnStartup(Entity* self) { void RaceMaelstromGeiser::OnStartup(Entity* self) {
@ -37,13 +37,13 @@ void RaceMaelstromGeiser::OnProximityUpdate(Entity* self, Entity* entering, std:
vehicle = entering; vehicle = entering;
} else if (entering->IsPlayer()) { } else if (entering->IsPlayer()) {
auto* possessorComponent = entering->GetComponent<PossessorComponent>(); auto* possessionComponent = entering->GetComponent<PossessionComponent>();
if (possessorComponent == nullptr) { if (possessionComponent == nullptr) {
return; return;
} }
vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); vehicle = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (vehicle == nullptr) { if (vehicle == nullptr) {
return; return;
@ -59,9 +59,9 @@ void RaceMaelstromGeiser::OnProximityUpdate(Entity* self, Entity* entering, std:
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject(); auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = zoneController->GetComponent<RacingComponent>();
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnRequestDie(player); if (racingComponent) racingComponent->OnRequestDie(player);
} }
void RaceMaelstromGeiser::OnTimerDone(Entity* self, std::string timerName) { void RaceMaelstromGeiser::OnTimerDone(Entity* self, std::string timerName) {

View File

@ -1,10 +1,10 @@
#include "ActVehicleDeathTrigger.h" #include "ActVehicleDeathTrigger.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "VehicleRacingControlComponent.h" #include "RacingComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
void ActVehicleDeathTrigger::OnCollisionPhantom(Entity* self, Entity* target) { void ActVehicleDeathTrigger::OnCollisionPhantom(Entity* self, Entity* target) {
@ -22,13 +22,13 @@ void ActVehicleDeathTrigger::OnCollisionPhantom(Entity* self, Entity* target) {
return; return;
} else if (target->IsPlayer()) { } else if (target->IsPlayer()) {
auto* possessorComponent = target->GetComponent<PossessorComponent>(); auto* possessionComponent = target->GetComponent<PossessionComponent>();
if (possessorComponent == nullptr) { if (possessionComponent == nullptr) {
return; return;
} }
vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); vehicle = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (vehicle == nullptr) { if (vehicle == nullptr) {
return; return;
@ -44,7 +44,7 @@ void ActVehicleDeathTrigger::OnCollisionPhantom(Entity* self, Entity* target) {
auto* zoneController = dZoneManager::Instance()->GetZoneControlObject(); auto* zoneController = dZoneManager::Instance()->GetZoneControlObject();
auto* vehicleRacingControlComponent = zoneController->GetComponent<VehicleRacingControlComponent>(); auto* racingComponent = zoneController->GetComponent<RacingComponent>();
if (vehicleRacingControlComponent) vehicleRacingControlComponent->OnRequestDie(player); if (racingComponent) racingComponent->OnRequestDie(player);
} }

View File

@ -5,7 +5,7 @@
#include "Player.h" #include "Player.h"
#include "Character.h" #include "Character.h"
#include "ShootingGalleryComponent.h" #include "ShootingGalleryComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "SimplePhysicsComponent.h" #include "SimplePhysicsComponent.h"
#include "MovementAIComponent.h" #include "MovementAIComponent.h"
@ -105,7 +105,7 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->SetIsRacing(true); characterComponent->SetIsRacing(true);
characterComponent->SetCurrentActivity(eGameActivity::SHOOTING_GALLERY); characterComponent->SetCurrentActivity(eGameActivity::SHOOTING_GALLERY);
auto* possessor = player->GetComponent<PossessorComponent>(); auto* possessor = player->GetComponent<PossessionComponent>();
if (possessor) { if (possessor) {
possessor->SetPossessable(self->GetObjectID()); possessor->SetPossessable(self->GetObjectID());
possessor->SetPossessableType(ePossessionType::NO_POSSESSION); possessor->SetPossessableType(ePossessionType::NO_POSSESSION);

View File

@ -1,6 +1,6 @@
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "RaceImaginePowerup.h" #include "RaceImaginePowerup.h"
#include "eRacingTaskParam.h" #include "eRacingTaskParam.h"
#include "MissionComponent.h" #include "MissionComponent.h"
@ -9,13 +9,13 @@
void RaceImaginePowerup::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, void RaceImaginePowerup::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1,
int32_t param2, int32_t param3) { int32_t param2, int32_t param3) {
if (sender->IsPlayer() && args == "powerup") { if (sender->IsPlayer() && args == "powerup") {
auto* possessorComponent = sender->GetComponent<PossessorComponent>(); auto* possessionComponent = sender->GetComponent<PossessionComponent>();
if (possessorComponent == nullptr) { if (possessionComponent == nullptr) {
return; return;
} }
auto* vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* vehicle = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable());
if (vehicle == nullptr) { if (vehicle == nullptr) {
return; return;