diff --git a/src/Features/ChallengeMode.cpp b/src/Features/ChallengeMode.cpp index 910cc0fa..0869bf3d 100644 --- a/src/Features/ChallengeMode.cpp +++ b/src/Features/ChallengeMode.cpp @@ -15,6 +15,14 @@ ChallengeMode *cm; +bool ChallengeMode::EnsureStatsController() { + if (entityList->GetEntityInfoByClassName("portal_stats_controller")) { + return true; + } + + return server->Create && server->Create("portal_stats_controller", {0, 0, 0}, {0, 0, 0}, nullptr); +} + void ChallengeMode::CreateNode() { if (!sv_bonus_challenge.GetBool() || engine->demoplayer->IsPlaying()) { return; diff --git a/src/Features/ChallengeMode.hpp b/src/Features/ChallengeMode.hpp index 6a230049..ea68742d 100644 --- a/src/Features/ChallengeMode.hpp +++ b/src/Features/ChallengeMode.hpp @@ -12,6 +12,7 @@ class ChallengeMode : public Feature { public: void CreateNode(); + bool EnsureStatsController(); void LoadNodes(SourceGameVersion version); }; diff --git a/src/Modules/Server.cpp b/src/Modules/Server.cpp index 12634d6c..94d9f74a 100644 --- a/src/Modules/Server.cpp +++ b/src/Modules/Server.cpp @@ -616,6 +616,26 @@ static void __cdecl AcceptInput_Hook(void *thisptr, const char *inputName, void console->Print("%.4d %s.%s(%s)\n", session->GetTick(), entName, inputName, parameter.ToString()); } + if (sar.game->Is(SourceGame_PortalStoriesMel) + && client->GetChallengeStatus() == CMStatus::CHALLENGE + && !engine->demoplayer->IsPlaying() + && (!strcasecmp(className, "point_clientcommand") || !strcasecmp(className, "point_servercommand")) + && !strcasecmp(inputName, "Command")) { + const char *command = parameter.ToString(); + command += strspn(command, " \t\r\n"); + if (!strncasecmp(command, "changelevel", 11) && strspn(command + 11, " \t\r\n") > 0) { + if (cm->EnsureStatsController()) { + // Complete after the map's fade, replacing the changelevel command entirely. + // RunScriptCode is a base entity input; queue OnLevelEnd so it reaches our hook. + inputName = "RunScriptCode"; + parameter.fieldType = FIELD_STRING; + parameter.iszVal = "EntFire(\"portal_stats_controller\", \"OnLevelEnd\", 5.0, 0);"; + } else { + console->Print("Could not obtain portal_stats_controller; keeping Mel's original elevator transition.\n"); + } + } + } + // HACKHACK // Deals with maps where you hit a normal transition trigger if (!strcmp(className, "portal_stats_controller") && !strcmp(inputName, "OnLevelEnd") && client->GetChallengeStatus() == CMStatus::CHALLENGE) { diff --git a/src/Utils.hpp b/src/Utils.hpp index b1d44956..4ec6e5b1 100644 --- a/src/Utils.hpp +++ b/src/Utils.hpp @@ -9,6 +9,7 @@ # include # define strdup _strdup # define strcasecmp _stricmp +# define strncasecmp _strnicmp #else # include #endif