diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f45705..f32fb92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ * Fixed crash when +USEing NPCs that have just exited a scripted sequence (Thanks malortie) * Fixed talk monsters resetting other talk monsters' dying schedule if they are both killed at the same time (Thanks FreeSlave) +### Features + +* Added sv_load_all_maps & sv_stop_loading_all_maps to help automate node graph generation + ## Changes in V1.0.0 Release Candidate 002 ### Bug fixes diff --git a/dlls/client.cpp b/dlls/client.cpp index 961e967..b319974 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -23,8 +23,13 @@ */ +#include +#include +#include + #include "extdll.h" #include "util.h" +#include "filesystem_utils.h" #include "cbase.h" #include "com_model.h" #include "saverestore.h" @@ -40,6 +45,7 @@ #include "usercmd.h" #include "netadr.h" #include "pm_shared.h" +#include "pm_defs.h" #include "UserMessages.h" DLL_GLOBAL unsigned int g_ulFrameCount; @@ -773,6 +779,101 @@ void ParmsChangeLevel() pSaveData->connectionCount = BuildChangeList(pSaveData->levelList, MAX_LEVEL_CONNECTIONS); } +static std::vector g_MapsToLoad; + +static void LoadNextMap() +{ + const std::string mapName = std::move(g_MapsToLoad.back()); + g_MapsToLoad.pop_back(); + + pmove->Con_Printf("Loading map \"%s\" automatically (%d left)\n", + mapName.c_str(), static_cast(g_MapsToLoad.size() + 1)); + + if (g_MapsToLoad.empty()) + { + pmove->Con_Printf("Loading last map\n"); + g_MapsToLoad.shrink_to_fit(); + } + + SERVER_COMMAND(UTIL_VarArgs("map \"%s\"\n", mapName.c_str())); +} + +static void LoadAllMaps() +{ + if (!g_MapsToLoad.empty()) + { + pmove->Con_Printf("Already loading all maps (%d remaining)\nUse sv_stop_loading_all_maps to stop\n", + static_cast(g_MapsToLoad.size())); + return; + } + + FileFindHandle_t handle = FILESYSTEM_INVALID_FIND_HANDLE; + + const char* fileName = g_pFileSystem->FindFirst("maps/*.bsp", &handle); + + if (fileName != nullptr) + { + do + { + std::string mapName = fileName; + mapName.resize(mapName.size() - 4); + + if (std::find_if(g_MapsToLoad.begin(), g_MapsToLoad.end(), [=](const auto& candidate) + { return 0 == stricmp(candidate.c_str(), mapName.c_str()); }) == g_MapsToLoad.end()) + { + g_MapsToLoad.push_back(std::move(mapName)); + } + } while ((fileName = g_pFileSystem->FindNext(handle)) != nullptr); + + g_pFileSystem->FindClose(handle); + + // Sort in reverse order so the first map in alphabetic order is loaded first. + std::sort(g_MapsToLoad.begin(), g_MapsToLoad.end(), [](const auto& lhs, const auto& rhs) + { return rhs < lhs; }); + } + + if (!g_MapsToLoad.empty()) + { + if (CMD_ARGC() == 2) + { + const char* firstMapToLoad = CMD_ARGV(1); + + // Clear out all maps that would have been loaded before this one. + if (auto it = std::find(g_MapsToLoad.begin(), g_MapsToLoad.end(), firstMapToLoad); + it != g_MapsToLoad.end()) + { + const std::size_t numberOfMapsToSkip = g_MapsToLoad.size() - (it - g_MapsToLoad.begin()); + + g_MapsToLoad.erase(it + 1, g_MapsToLoad.end()); + + pmove->Con_Printf("Skipping %d maps to start with \"%s\"\n", + static_cast(numberOfMapsToSkip), g_MapsToLoad.back().c_str()); + } + else + { + pmove->Con_Printf("Unknown map \"%s\", starting from beginning\n", firstMapToLoad); + } + } + + pmove->Con_Printf("Loading %d maps one at a time to generate files\n", static_cast(g_MapsToLoad.size())); + + // Load the first map right now. + LoadNextMap(); + } + else + { + pmove->Con_Printf("No maps to load\n"); + } +} + +void InitMapLoadingUtils() +{ + g_engfuncs.pfnAddServerCommand("sv_load_all_maps", &LoadAllMaps); + // Escape hatch in case the command is executed in error. + g_engfuncs.pfnAddServerCommand("sv_stop_loading_all_maps", []() + { g_MapsToLoad.clear(); }); +} + static bool g_LastAllowBunnyHoppingState = false; // @@ -807,6 +908,13 @@ void StartFrame() g_engfuncs.pfnSetPhysicsKeyValue(player->edict(), "bj", UTIL_dtos1(allowBunnyHopping ? 1 : 0)); } } + + // If we're loading all maps then change maps after 3 seconds (time starts at 1) + // to give the game time to generate files. + if (!g_MapsToLoad.empty() && gpGlobals->time > 4) + { + LoadNextMap(); + } } diff --git a/dlls/client.h b/dlls/client.h index a2cc02d..47fd4b5 100644 --- a/dlls/client.h +++ b/dlls/client.h @@ -24,6 +24,7 @@ extern void ClientCommand(edict_t* pEntity); extern void ClientUserInfoChanged(edict_t* pEntity, char* infobuffer); extern void ServerActivate(edict_t* pEdictList, int edictCount, int clientMax); extern void ServerDeactivate(); +void InitMapLoadingUtils(); extern void StartFrame(); extern void PlayerPostThink(edict_t* pEntity); extern void PlayerPreThink(edict_t* pEntity); diff --git a/dlls/game.cpp b/dlls/game.cpp index d99c486..393676f 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -15,6 +15,7 @@ #include "extdll.h" #include "eiface.h" #include "util.h" +#include "client.h" #include "game.h" #include "filesystem_utils.h" @@ -903,6 +904,8 @@ void GameDLLInit() CVAR_REGISTER(&sk_player_leg3); // END REGISTER CVARS FOR SKILL LEVEL STUFF + InitMapLoadingUtils(); + SERVER_COMMAND("exec skill.cfg\n"); }