diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index b91a920d..8921d917 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -16,7 +16,10 @@ body: I have validated that this issue is not a syntax error of either MySQL or SQLite. required: true - label: > - I have pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there. + I have downloaded/pulled the latest version of the main branch of DarkflameServer and have confirmed that the issue exists there. + required: true + - label: > + I have verified that my boot.cfg is configured as per the [README](https://github.com/DarkflameUniverse/DarkflameServer?tab=readme-ov-file#allowing-a-user-to-connect-to-your-server). required: true - type: input id: server-version diff --git a/README.md b/README.md index 7fce47ee..34233d56 100644 --- a/README.md +++ b/README.md @@ -324,13 +324,15 @@ While a character has a gmlevel of anything but `0`, some gameplay behavior will Some changes to the client `boot.cfg` file are needed to play on your server. ## Allowing a user to connect to your server +**ALL OF THESE CHANGES ARE REQUIRED. PLEASE FULLY READ THIS SECTION** + To connect to a server follow these steps: * In the client directory, locate `boot.cfg` -* Open it in a text editor and locate where it says `AUTHSERVERIP=0:` -* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers -* Next locate the line `UGCUSE3DSERVICES=7:` +* Open `boot.cfg` in a text editor and locate the line `UGCUSE3DSERVICES=7:` * Ensure the number after the 7 is a `0` * Alternatively, remove the line with `UGCUSE3DSERVICES` altogether +* Next locate where it says `AUTHSERVERIP=0:` +* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers * Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system * Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users. As an example, here is what the boot.cfg is required to contain for a server with the ip 12.34.56.78 diff --git a/dCommon/DluAssert.h b/dCommon/DluAssert.h index f099443a..802c6d16 100644 --- a/dCommon/DluAssert.h +++ b/dCommon/DluAssert.h @@ -4,7 +4,7 @@ #include #ifdef _DEBUG -# define DluAssert(expression) do { assert(expression) } while(0) +# define DluAssert(expression) do { assert(expression); } while(0) #else # define DluAssert(expression) #endif diff --git a/dCommon/LDFFormat.h b/dCommon/LDFFormat.h index 054ddb42..7c2e939b 100644 --- a/dCommon/LDFFormat.h +++ b/dCommon/LDFFormat.h @@ -83,6 +83,12 @@ public: this->value = value; } + //! Initializer + LDFData(const std::string& key, const T& value) { + this->key = GeneralUtils::ASCIIToUTF16(key); + this->value = value; + } + //! Destructor ~LDFData(void) override {} diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index ad0c0b1c..4fb754d8 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -320,7 +320,7 @@ const std::unordered_map& EntityManager::GetSpawnPointEnt return m_SpawnPoints; } -void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr, const bool skipChecks) { +void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr) { if (!entity) { LOG("Attempted to construct null entity"); return; @@ -363,16 +363,14 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr entity->WriteComponents(stream, eReplicaPacketType::CONSTRUCTION); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) { - if (skipChecks) { - Game::server->Send(stream, UNASSIGNED_SYSTEM_ADDRESS, true); - } else { - for (auto* player : PlayerManager::GetAllPlayers()) { - if (player->GetPlayerReadyForUpdates()) { - Game::server->Send(stream, player->GetSystemAddress(), false); - } else { - auto* ghostComponent = player->GetComponent(); - if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID()); - } + for (auto* player : PlayerManager::GetAllPlayers()) { + // Don't need to construct the player to themselves + if (entity->GetObjectID() == player->GetObjectID()) continue; + if (player->GetPlayerReadyForUpdates()) { + Game::server->Send(stream, player->GetSystemAddress(), false); + } else { + auto* ghostComponent = player->GetComponent(); + if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID()); } } } else { diff --git a/dGame/EntityManager.h b/dGame/EntityManager.h index fdbb1a55..5d22280f 100644 --- a/dGame/EntityManager.h +++ b/dGame/EntityManager.h @@ -42,7 +42,7 @@ public: const std::unordered_map GetAllEntities() const { return m_Entities; } #endif - void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, bool skipChecks = false); + void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); void SerializeEntity(Entity* entity); void SerializeEntity(const Entity& entity); diff --git a/dGame/dComponents/ActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp index 0e6778b0..cd71c5cb 100644 --- a/dGame/dComponents/ActivityComponent.cpp +++ b/dGame/dComponents/ActivityComponent.cpp @@ -334,7 +334,7 @@ bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const { return false; } -bool ActivityComponent::TakeCost(Entity* player) const { +bool ActivityComponent::CheckCost(Entity* player) const { if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0) return true; @@ -345,11 +345,19 @@ bool ActivityComponent::TakeCost(Entity* player) const { if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount) return false; - inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount); - return true; } +bool ActivityComponent::TakeCost(Entity* player) const{ + + auto* inventoryComponent = player->GetComponent(); + if (CheckCost(player)) { + inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount); + return true; + } + else return false; +} + void ActivityComponent::PlayerReady(Entity* player, bool bReady) { for (Lobby* lobby : m_Queue) { for (LobbyPlayer* lobbyPlayer : lobby->players) { @@ -382,7 +390,7 @@ ActivityInstance* ActivityComponent::NewInstance() { void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector& lobby) const { for (LobbyPlayer* player : lobby) { auto* entity = player->GetEntity(); - if (entity == nullptr || !TakeCost(entity)) { + if (entity == nullptr || !CheckCost(entity)) { continue; } diff --git a/dGame/dComponents/ActivityComponent.h b/dGame/dComponents/ActivityComponent.h index 296c6ccc..ec482a42 100644 --- a/dGame/dComponents/ActivityComponent.h +++ b/dGame/dComponents/ActivityComponent.h @@ -234,10 +234,17 @@ public: */ bool IsPlayedBy(LWOOBJID playerID) const; + /** + * Checks if the entity has enough cost to play this activity + * @param player the entity to check + * @return true if the entity has enough cost to play this activity, false otherwise + */ + bool CheckCost(Entity* player) const; + /** * 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 + * @return true if the cost was taken, false otherwise */ bool TakeCost(Entity* player) const; diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index efe711b3..9463e7d6 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -54,6 +54,7 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : m_SourcePosition = m_Parent->GetPosition(); m_Paused = false; m_SavedVelocity = NiPoint3Constant::ZERO; + m_IsBounced = false; if (!m_Parent->GetComponent()) SetPath(m_Parent->GetVarAsString(u"attached_path")); } @@ -158,8 +159,9 @@ void MovementAIComponent::Update(const float deltaTime) { if (m_Path->pathBehavior == PathBehavior::Loop) { SetPath(m_Path->pathWaypoints); } else if (m_Path->pathBehavior == PathBehavior::Bounce) { + m_IsBounced = !m_IsBounced; std::vector waypoints = m_Path->pathWaypoints; - std::reverse(waypoints.begin(), waypoints.end()); + if (m_IsBounced) std::reverse(waypoints.begin(), waypoints.end()); SetPath(waypoints); } else if (m_Path->pathBehavior == PathBehavior::Once) { Stop(); diff --git a/dGame/dComponents/MovementAIComponent.h b/dGame/dComponents/MovementAIComponent.h index 15b5aaed..dbb0661c 100644 --- a/dGame/dComponents/MovementAIComponent.h +++ b/dGame/dComponents/MovementAIComponent.h @@ -321,6 +321,8 @@ private: bool m_Paused; NiPoint3 m_SavedVelocity; + + bool m_IsBounced{}; }; #endif // MOVEMENTAICOMPONENT_H diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 66ce9313..c183fcea 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -42,35 +42,6 @@ std::unordered_map PetComponent::activePets{}; * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID * while the faction ones could be checked using their respective missions. */ -const std::map PetComponent::petFlags{ - { 3050, 801 }, // Elephant - { 3054, 803 }, // Cat - { 3195, 806 }, // Triceratops - { 3254, 807 }, // Terrier - { 3261, 811 }, // Skunk - { 3672, 813 }, // Bunny - { 3994, 814 }, // Crocodile - { 5635, 815 }, // Doberman - { 5636, 816 }, // Buffalo - { 5637, 818 }, // Robot Dog - { 5639, 819 }, // Red Dragon - { 5640, 820 }, // Tortoise - { 5641, 821 }, // Green Dragon - { 5643, 822 }, // Panda, see mission 786 - { 5642, 823 }, // Mantis - { 6720, 824 }, // Warthog - { 3520, 825 }, // Lion, see mission 1318 - { 7638, 826 }, // Goat - { 7694, 827 }, // Crab - { 12294, 829 }, // Reindeer - { 12431, 830 }, // Stegosaurus, see mission 1386 - { 12432, 831 }, // Saber cat, see mission 1389 - { 12433, 832 }, // Gryphon, see mission 1392 - { 12434, 833 }, // Alien, see mission 1188 - // 834: unknown?, see mission 506, 688 - { 16210, 836 }, // Ninjago Earth Dragon, see mission 1836 - { 13067, 838 }, // Skeleton dragon -}; PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { m_PetInfo = CDClientManager::GetTable()->GetByID(componentId); // TODO: Make reference when safe @@ -556,9 +527,8 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { ); // Triggers the catch a pet missions - if (petFlags.find(m_Parent->GetLOT()) != petFlags.end()) { - tamer->GetCharacter()->SetPlayerFlag(petFlags.at(m_Parent->GetLOT()), true); - } + constexpr auto PET_FLAG_BASE = 800; + tamer->GetCharacter()->SetPlayerFlag(PET_FLAG_BASE + m_ComponentId, true); auto* missionComponent = tamer->GetComponent(); diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index fed3f49a..5ab39021 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -250,11 +250,6 @@ private: */ static std::unordered_map currentActivities; - /** - * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet - */ - static const std::map petFlags; - /** * The ID of the component in the pet component table */ diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index 2dd7f0d2..5dba7c19 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -272,6 +272,10 @@ void PropertyManagementComponent::OnStartBuilding() { model->HandleMsg(reset); } } + + for (auto* const entity : Game::entityManager->GetEntitiesInGroup("SpawnedPropertyEnemies")) { + if (entity) entity->Smash(); + } } void PropertyManagementComponent::OnFinishBuilding() { @@ -296,6 +300,10 @@ void PropertyManagementComponent::OnFinishBuilding() { model->HandleMsg(reset); } } + + for (auto* const entity : Game::entityManager->GetEntitiesInGroup("SpawnedPropertyEnemies")) { + if (entity) entity->Smash(); + } } void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const NiPoint3 position, NiQuaternion rotation) { diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index a483e842..a2f06e55 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -84,9 +84,11 @@ void Strip::HandleMsg(MigrateActionsMessage& msg) { template<> void Strip::HandleMsg(GameMessages::RequestUse& msg) { - if (m_PausedTime > 0.0f) return; + if (m_PausedTime > 0.0f || !HasMinimumActions()) return; - if (m_Actions[m_NextActionIndex].GetType() == "OnInteract") { + auto& nextAction = GetNextAction(); + + if (nextAction.GetType() == "OnInteract") { IncrementAction(); m_WaitingForAction = false; } @@ -113,7 +115,9 @@ void Strip::Spawn(LOT lot, Entity& entity) { info.pos = entity.GetPosition(); info.rot = NiQuaternionConstant::IDENTITY; info.spawnerID = entity.GetObjectID(); - Game::entityManager->ConstructEntity(Game::entityManager->CreateEntity(info, nullptr, &entity)); + auto* const spawnedEntity = Game::entityManager->CreateEntity(info, nullptr, &entity); + spawnedEntity->AddToGroup("SpawnedPropertyEnemies"); + Game::entityManager->ConstructEntity(spawnedEntity); } // Spawns a specific drop for all @@ -139,6 +143,7 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) { // Default velocity is 3 units per second. entity.SetVelocity(NiPoint3{0.0f, isFlyDown ? -3.0f : 3.0f, 0.0f}); + modelComponent.SetVelocity(NiPoint3{0.0f, isFlyDown ? -3.0f : 3.0f, 0.0f}); } else if (nextActionType == "MoveRight" || nextActionType == "MoveLeft") { bool isMoveLeft = nextActionType == "MoveLeft"; @@ -205,7 +210,6 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) { LOG("Tried to play action (%s) which is not supported.", nextActionType.data()); g_WarnedActions.insert(nextActionType.data()); } - return; } IncrementAction(); @@ -259,13 +263,19 @@ bool Strip::CheckMovement(float deltaTime, ModelComponent& modelComponent) { } void Strip::Update(float deltaTime, ModelComponent& modelComponent) { + // No point in running a strip with only one action. + // Strips are also designed to have 2 actions or more to run. + if (!HasMinimumActions()) return; + if (!CheckMovement(deltaTime, modelComponent)) return; + // Don't run this strip if we're paused. m_PausedTime -= deltaTime; if (m_PausedTime > 0.0f) return; m_PausedTime = 0.0f; + // Return here if we're waiting for external interactions to continue. if (m_WaitingForAction) return; auto& entity = *modelComponent.GetParent(); diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h index b84bbd38..a0085f9e 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -37,6 +37,9 @@ public: void SpawnDrop(LOT dropLOT, Entity& entity); void ProcNormalAction(float deltaTime, ModelComponent& modelComponent); void RemoveStates(ModelComponent& modelComponent) const; + + // 2 actions are required for strips to work + bool HasMinimumActions() const { return m_Actions.size() >= 2; } private: // Indicates this Strip is waiting for an action to be taken upon it to progress to its actions bool m_WaitingForAction{ false }; diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index bd483e73..4c0ba749 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -336,6 +336,7 @@ #include "NsRaceServer.h" #include "TrialFactionArmorServer.h" #include "ImaginationBackPack.h" +#include "NsWinterRaceServer.h" #include #include @@ -656,6 +657,7 @@ namespace { //FB {"scripts\\ai\\NS\\WH\\L_ROCKHYDRANT_BROKEN.lua", []() {return new RockHydrantBroken();}}, {"scripts\\ai\\NS\\L_NS_WH_FANS.lua", []() {return new WhFans();}}, + {"scripts\\ai\\RACING\\TRACK_NS_WINTER\\NS_WINTER_RACE_SERVER.lua", []() {return new NsWinterRaceServer();}}, //WBL {"scripts\\zone\\LUPs\\WBL_generic_zone.lua", []() {return new WblGenericZone();}}, diff --git a/dScripts/ai/RACING/CMakeLists.txt b/dScripts/ai/RACING/CMakeLists.txt index a51273e6..84a837a0 100644 --- a/dScripts/ai/RACING/CMakeLists.txt +++ b/dScripts/ai/RACING/CMakeLists.txt @@ -13,6 +13,12 @@ foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_NS}) set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_NS/${file}") endforeach() +add_subdirectory(TRACK_NS_WINTER) + +foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_NS_WINTER}) + set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_NS_WINTER/${file}") +endforeach() + add_subdirectory(TRACK_GF) foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_GF}) @@ -26,5 +32,5 @@ foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_FV}) endforeach() add_library(dScriptsAiRacing OBJECT ${DSCRIPTS_SOURCES_AI_RACING}) -target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS" "TRACK_NS" "TRACK_GF" "TRACK_FV") +target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS" "TRACK_NS" "TRACK_NS_WINTER" "TRACK_GF" "TRACK_FV") target_precompile_headers(dScriptsAiRacing REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/RACING/TRACK_NS_WINTER/CMakeLists.txt b/dScripts/ai/RACING/TRACK_NS_WINTER/CMakeLists.txt new file mode 100644 index 00000000..e435329e --- /dev/null +++ b/dScripts/ai/RACING/TRACK_NS_WINTER/CMakeLists.txt @@ -0,0 +1,3 @@ +set(DSCRIPTS_SOURCES_AI_RACING_TRACK_NS_WINTER + "NsWinterRaceServer.cpp" + PARENT_SCOPE) diff --git a/dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.cpp b/dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.cpp new file mode 100644 index 00000000..9c3cc009 --- /dev/null +++ b/dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.cpp @@ -0,0 +1,53 @@ +#include "NsWinterRaceServer.h" +#include "GameMessages.h" +#include "RacingControlComponent.h" + +using std::unique_ptr; +using std::make_unique; + +void NsWinterRaceServer::OnStartup(Entity* self) { + GameMessages::ConfigureRacingControl config; + auto& raceSet = config.racingSettings; + + raceSet.push_back(make_unique>("GameType", u"Racing")); + raceSet.push_back(make_unique>("GameState", u"Starting")); + raceSet.push_back(make_unique>("Number_Of_PlayersPerTeam", 6)); + raceSet.push_back(make_unique>("Minimum_Players_to_Start", 2)); + raceSet.push_back(make_unique>("Minimum_Players_for_Group_Achievments", 2)); + + raceSet.push_back(make_unique>("Car_Object", 7703)); + raceSet.push_back(make_unique>("Race_PathName", u"MainPath")); + raceSet.push_back(make_unique>("Current_Lap", 1)); + raceSet.push_back(make_unique>("Number_of_Laps", 3)); + raceSet.push_back(make_unique>("activityID", 42)); + + raceSet.push_back(make_unique>("Place_1", 100)); + raceSet.push_back(make_unique>("Place_2", 90)); + raceSet.push_back(make_unique>("Place_3", 80)); + raceSet.push_back(make_unique>("Place_4", 70)); + raceSet.push_back(make_unique>("Place_5", 60)); + raceSet.push_back(make_unique>("Place_6", 50)); + + raceSet.push_back(make_unique>("Num_of_Players_1", 15)); + raceSet.push_back(make_unique>("Num_of_Players_2", 25)); + raceSet.push_back(make_unique>("Num_of_Players_3", 50)); + raceSet.push_back(make_unique>("Num_of_Players_4", 85)); + raceSet.push_back(make_unique>("Num_of_Players_5", 90)); + raceSet.push_back(make_unique>("Num_of_Players_6", 100)); + + raceSet.push_back(make_unique>("Number_of_Spawn_Groups", 1)); + raceSet.push_back(make_unique>("Red_Spawners", 4847)); + raceSet.push_back(make_unique>("Blue_Spawners", 4848)); + raceSet.push_back(make_unique>("Blue_Flag", 4850)); + raceSet.push_back(make_unique>("Red_Flag", 4851)); + raceSet.push_back(make_unique>("Red_Point", 4846)); + raceSet.push_back(make_unique>("Blue_Point", 4845)); + raceSet.push_back(make_unique>("Red_Mark", 4844)); + raceSet.push_back(make_unique>("Blue_Mark", 4843)); + + const auto racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL); + for (auto* const racingController : racingControllers) { + auto* const racingComponent = racingController->GetComponent(); + if (racingComponent) racingComponent->MsgConfigureRacingControl(config); + } +} diff --git a/dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.h b/dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.h new file mode 100644 index 00000000..6478c723 --- /dev/null +++ b/dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.h @@ -0,0 +1,15 @@ +// Darkflame Universe +// Copyright 2025 + +#ifndef NSWINTERRACESERVER_H +#define NSWINTERRACESERVER_H + +#include "CppScripts.h" +#include "RaceImaginationServer.h" + +class NsWinterRaceServer : public RaceImaginationServer { +public: + void OnStartup(Entity* self) override; +}; + +#endif //!NSWINTERRACESERVER_H diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 0f433493..9e9581b7 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -713,12 +713,12 @@ void HandleMasterPacket(Packet* packet) { //Create our user and send them in: UserManager::Instance()->CreateUser(it->second.sysAddr, username.GetAsString(), userHash); - auto zone = Game::zoneManager->GetZone(); - if (zone) { + if (Game::zoneManager->HasZone()) { float x = 0.0f; float y = 0.0f; float z = 0.0f; + auto zone = Game::zoneManager->GetZone(); if (zone->GetZoneID().GetMapID() == 1100) { auto pos = zone->GetSpawnPos(); x = pos.x; @@ -1045,7 +1045,7 @@ void HandlePacket(Packet* packet) { const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID()); - Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS, true); + Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS); if (respawnPoint != NiPoint3Constant::ZERO) { GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, NiQuaternionConstant::IDENTITY); diff --git a/dZoneManager/dZoneManager.h b/dZoneManager/dZoneManager.h index f9abee8c..fc07a4b8 100644 --- a/dZoneManager/dZoneManager.h +++ b/dZoneManager/dZoneManager.h @@ -29,6 +29,7 @@ public: /* Gets a pointer to the currently loaded zone. */ Zone* GetZoneMut() const; const Zone* GetZone() const { return GetZoneMut(); }; + bool HasZone() const { return m_pZone != nullptr; }; void LoadZone(const LWOZONEID& zoneID); //Discard the current zone (if any) and loads a new zone. /* Adds a spawner to the zone with the specified ID. */