From 32487dcd5fded2b987d64db8f1563f42e06ec466 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 18 May 2025 19:48:40 -0700 Subject: [PATCH 01/10] fix: reverse bounce paths (#1798) --- dGame/dComponents/MovementAIComponent.cpp | 4 +++- dGame/dComponents/MovementAIComponent.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) 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 From 34665f6f5c0ee60535192fee6a218a9306c0af3c Mon Sep 17 00:00:00 2001 From: ElectScholar Date: Fri, 23 May 2025 03:42:39 +0000 Subject: [PATCH 02/10] Fix: Double imaginite issue resolved on mini survival games (#1801) * Add checkcost as replacement for just inventory checks * Create headers for cost methods * clean comments --- dGame/dComponents/ActivityComponent.cpp | 16 ++++++++++++---- dGame/dComponents/ActivityComponent.h | 9 ++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) 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; From 437362cce6b04aed627cfbe99a22c82aedb3d5a9 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 30 May 2025 06:56:47 -0700 Subject: [PATCH 03/10] Add extra checkbox to bug report field (#1803) --- .github/ISSUE_TEMPLATE/bug_report.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 From 92155a3cb4876195147eb2d77b1b7ba3522871be Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 30 May 2025 06:56:58 -0700 Subject: [PATCH 04/10] fix: invert instructions so people read the important ones first (#1802) * fix: invert instructions so people read the important ones first * more emphasis --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 From 68eb20966f841eea32d513adac08abb321ce1eac Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:07:29 -0700 Subject: [PATCH 05/10] fix: Remove hard coded pet flags (#1805) Tested that taming the cat pet (3054) sets flag 807 --- dGame/dComponents/PetComponent.cpp | 34 ++---------------------------- dGame/dComponents/PetComponent.h | 5 ----- 2 files changed, 2 insertions(+), 37 deletions(-) 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 */ From 820c0f00837258b11c49c00e23d755d25b103bfc Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:07:07 -0700 Subject: [PATCH 06/10] fix: ghost mis-matched pointer causing objects to not destruct (#1797) --- dGame/EntityManager.cpp | 18 +++++++----------- dGame/EntityManager.h | 2 +- dWorldServer/WorldServer.cpp | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index ad0c0b1c..66cd7c78 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,12 @@ 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()) { + 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/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 0f433493..c83b82d7 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -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); From b509fd4f10c25307877b964258acbda3e5c9fd26 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sat, 7 Jun 2025 16:30:22 -0700 Subject: [PATCH 07/10] fix: Constructing player to themself (#1808) tested that I can see other players leave and join a world and that i no longer see a white screen when loading between worlds --- dGame/EntityManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index 66cd7c78..4fb754d8 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -364,6 +364,8 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) { 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 { From 37e14979a4db8553323e0ee61c62014b6fe41a90 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 8 Jun 2025 12:14:35 -0700 Subject: [PATCH 08/10] fix: winter race orbs (#1810) Tested that script is loaded --- dCommon/LDFFormat.h | 6 +++ dScripts/CppScripts.cpp | 2 + dScripts/ai/RACING/CMakeLists.txt | 8 ++- .../ai/RACING/TRACK_NS_WINTER/CMakeLists.txt | 3 ++ .../TRACK_NS_WINTER/NsWinterRaceServer.cpp | 53 +++++++++++++++++++ .../TRACK_NS_WINTER/NsWinterRaceServer.h | 15 ++++++ 6 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 dScripts/ai/RACING/TRACK_NS_WINTER/CMakeLists.txt create mode 100644 dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.cpp create mode 100644 dScripts/ai/RACING/TRACK_NS_WINTER/NsWinterRaceServer.h 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/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 From 28583452694553c30802405acc5047b0f37b7b80 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 8 Jun 2025 19:41:19 -0700 Subject: [PATCH 09/10] fix: destroy enemies on entering build mode (#1812) --- dGame/dComponents/PropertyManagementComponent.cpp | 8 ++++++++ dGame/dPropertyBehaviors/Strip.cpp | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index f20a2886..25a7d036 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 b1dd94eb..5e90d2ba 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -111,7 +111,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 From c19ee04c8abdb9d761262c78e893e71f2f6cfc23 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 8 Jun 2025 19:41:43 -0700 Subject: [PATCH 10/10] fix: property behavior crashes (#1813) --- dCommon/DluAssert.h | 2 +- dGame/dPropertyBehaviors/Strip.cpp | 13 ++++++++++--- dGame/dPropertyBehaviors/Strip.h | 3 +++ dWorldServer/WorldServer.cpp | 4 ++-- dZoneManager/dZoneManager.h | 1 + 5 files changed, 17 insertions(+), 6 deletions(-) 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/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index 5e90d2ba..8f5a6aaa 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; } @@ -170,7 +172,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(); @@ -190,11 +191,17 @@ void Strip::RemoveStates(ModelComponent& modelComponent) const { } 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; + + // 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 3f929a7d..6e4aa66d 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -33,6 +33,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/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index c83b82d7..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; 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. */