From 6578cf1b5663d978783712f4a16488c7214ec20f Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 6 Oct 2024 16:01:42 +0100 Subject: [PATCH] Rework the `map` command suite a little The following funcs are now a suite with almost-identical argument lists. - `map` is for immediately going to a map - Add `-random`/`-r` - `queuemap` is for interacting with the live Round Queue - Add `-random`/`-r` - Performed server-side - `-clear` and `-show` now accept partial params `-c` and `-s` - Fix minor error with 0-argument print's text - `showmap` is for printing information - Add arbitrary map name/ID input - `showmap robo` prints the course `map robo` would resolve to - Add `-gametype`/`-gt`/`-g` - For compatibility testing - Add `-random`/`-r` - Combine with `-gametype` for extra guarantees - `randommap` was deprecated - Will only print replacement instructions of `map -random`, and nothing else --- src/d_clisrv.c | 38 ++- src/d_netcmd.c | 408 ++++++++++++++++++++----------- src/menus/transient/pause-game.c | 2 +- 3 files changed, 304 insertions(+), 144 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d4d025643..970abdb81 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4845,7 +4845,6 @@ static void PT_ReqMapQueue(int node) CONS_Alert(CONS_ERROR, "Recieved REQMAPQUEUE, but unable to add map beyond %u\n", roundqueue.size); // But this one does, because otherwise it's silent failure! - // Todo print the map's name, maybe? char rejectmsg[256]; strlcpy(rejectmsg, "The server couldn't queue your chosen map.", 256); SendServerNotice(reqmapqueue.source, rejectmsg); @@ -4853,6 +4852,43 @@ static void PT_ReqMapQueue(int node) return; } + if (reqmapqueue.newmapnum == NEXTMAP_VOTING) + { + UINT8 numPlayers = 0, i; + for (i = 0; i < MAXPLAYERS; ++i) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + extern consvar_t cv_forcebots; // debug + + if (!(gametypes[reqmapqueue.newgametype]->rules & GTR_BOTS) && players[i].bot && !cv_forcebots.value) + { + // Gametype doesn't support bots + continue; + } + + numPlayers++; + } + + reqmapqueue.newmapnum = G_RandMapPerPlayerCount(G_TOLFlag(reqmapqueue.newgametype), UINT16_MAX, false, false, NULL, numPlayers); + } + + if (reqmapqueue.newmapnum >= nummapheaders) + { + CONS_Alert(CONS_ERROR, "Recieved REQMAPQUEUE, but unable to add map of invalid ID (%u)\n", reqmapqueue.newmapnum); + + char rejectmsg[256]; + strlcpy(rejectmsg, "The server couldn't queue your chosen map.", 256); + SendServerNotice(reqmapqueue.source, rejectmsg); + + return; + } + + G_AddMapToBuffer(reqmapqueue.newmapnum); + UINT8 buf[1+2+1]; UINT8 *buf_p = buf; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index aed1efbfd..175a42470 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2440,6 +2440,7 @@ static void Command_Map_f(void) size_t option_force; size_t option_gametype; size_t option_encore; + size_t option_random; size_t option_skill; size_t option_server; size_t option_match; @@ -2451,7 +2452,7 @@ static void Command_Map_f(void) INT32 newmapnum; - char * mapname; + char * mapname = NULL; char *realmapname = NULL; INT32 newgametype = gametype; @@ -2474,6 +2475,7 @@ static void Command_Map_f(void) option_force = COM_CheckPartialParm("-f"); option_gametype = COM_CheckPartialParm("-g"); option_encore = COM_CheckPartialParm("-e"); + option_random = COM_CheckPartialParm("-r"); option_skill = COM_CheckParm("-skill"); option_server = COM_CheckParm("-server"); option_match = COM_CheckParm("-match"); @@ -2486,30 +2488,105 @@ static void Command_Map_f(void) if (!( first_option = COM_FirstOption() )) first_option = COM_Argc(); - if (first_option < 2) + if (!option_random && first_option < 2) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ - CONS_Printf("map [-gametype ] [-force]:\n"); + CONS_Printf("map [-gametype ] [-force] / [-random]:\n"); CONS_Printf(M_GetText( "Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n" "All parameters are case-insensitive and may be abbreviated.\n")); return; } - mapname = ConcatCommandArgv(1, first_option); + boolean getgametypefrommap = false; - newmapnum = G_FindMapByNameOrCode(mapname, &realmapname); - - if (newmapnum == 0) + // new gametype value + // use current one by default + if (option_gametype) { - CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); - Z_Free(mapname); - return; + newgametype = GetGametypeParm(option_gametype); + if (newgametype == -1) + { + return; + } + } + else if (option_random) + { + if (!Playing()) + { + CONS_Printf("Can't use -random from the menu without -gametype.\n"); + return; + } + } + else if (!Playing() || (netgame == false && grandprixinfo.gp == true)) + { + getgametypefrommap = true; } - if (/*newmapnum != 1 &&*/ M_MapLocked(newmapnum)) + // new encoremode value + if (option_encore) { - ischeating = true; + newencoremode = !newencoremode; + + if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats) + { + CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); + Z_Free(realmapname); + Z_Free(mapname); + return; + } + } + + if (option_random) + { + UINT8 numPlayers = 0; + UINT16 oldmapnum = UINT16_MAX; + + if (Playing()) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; ++i) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + extern consvar_t cv_forcebots; // debug + + if (!(gametypes[newgametype]->rules & GTR_BOTS) && players[i].bot && !cv_forcebots.value) + { + // Gametype doesn't support bots + continue; + } + + numPlayers++; + } + + oldmapnum = (gamestate == GS_LEVEL) + ? (gamemap-1) + : prevmap; + } + + newmapnum = G_RandMapPerPlayerCount(G_TOLFlag(newgametype), oldmapnum, false, false, NULL, numPlayers) + 1; + } + else + { + mapname = ConcatCommandArgv(1, first_option); + + newmapnum = G_FindMapByNameOrCode(mapname, &realmapname); + + if (newmapnum == 0) + { + CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); + Z_Free(mapname); + return; + } + + if (M_MapLocked(newmapnum)) + { + ischeating = true; + } } if (ischeating && !usingcheats) @@ -2520,21 +2597,8 @@ static void Command_Map_f(void) return; } - // new gametype value - // use current one by default - if (option_gametype) + if (getgametypefrommap) { - newgametype = GetGametypeParm(option_gametype); - if (newgametype == -1) - { - Z_Free(realmapname); - Z_Free(mapname); - return; - } - } - else if (!Playing() || (netgame == false && grandprixinfo.gp == true)) - { - newresetplayers = true; if (mapheaderinfo[newmapnum-1]) { // Let's just guess so we don't have to specify the gametype EVERY time... @@ -2553,25 +2617,13 @@ static void Command_Map_f(void) } } - // new encoremode value - if (option_encore) - { - newencoremode = !newencoremode; - - if (!M_SecretUnlocked(SECRET_ENCORE, false) && newencoremode == true && !usingcheats) - { - CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); - Z_Free(realmapname); - Z_Free(mapname); - return; - } - } - - if (!option_force && newgametype == gametype && Playing()) // SRB2Kart + if (!Playing()) + newresetplayers = true; + else if (!option_force && newgametype == gametype) // SRB2Kart newresetplayers = false; // if not forcing and gametypes is the same // don't use a gametype the map doesn't support - if (cht_debug || option_force || cv_skipmapcheck.value) + if (option_random || cht_debug || option_force || cv_skipmapcheck.value) { // The player wants us to trek on anyway. Do so. } @@ -2853,54 +2905,7 @@ static void Got_Mapcmd(const UINT8 **cp, INT32 playernum) static void Command_RandomMap(void) { - INT32 oldmapnum; - INT32 newmapnum; - INT32 newgametype = (Playing() ? gametype : menugametype); - boolean newencore = false; - boolean newresetplayers; - size_t option_gametype; - - if (client && !IsPlayerAdmin(consoleplayer)) - { - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - return; - } - - if ((option_gametype = COM_CheckPartialParm("-g"))) - { - newgametype = GetGametypeParm(option_gametype); - if (newgametype == -1) - return; - } - - // TODO: Handle singleplayer conditions. - // The existing ones are way too annoyingly complicated and "anti-cheat" for my tastes. - - if (Playing()) - { - if (cv_kartencore.value == 1 && (gametypes[newgametype]->rules & GTR_ENCORE)) - { - newencore = true; - } - newresetplayers = false; - - if (gamestate == GS_LEVEL) - { - oldmapnum = gamemap-1; - } - else - { - oldmapnum = prevmap; - } - } - else - { - newresetplayers = true; - oldmapnum = -1; - } - - newmapnum = G_RandMap(G_TOLFlag(newgametype), oldmapnum, false, false, NULL) + 1; - D_MapChange(newmapnum, newgametype, newencore, newresetplayers, 0, false, false); + CONS_Printf("randommap is deprecated, please use \"map -random\" instead.\n"); } static void Command_RestartLevel(void) @@ -2965,13 +2970,14 @@ static void Command_QueueMap_f(void) size_t option_encore; size_t option_clear; size_t option_show; + size_t option_random; boolean usingcheats; boolean ischeating; INT32 newmapnum; - char * mapname; + char * mapname = NULL; char *realmapname = NULL; INT32 newgametype = gametype; @@ -2992,16 +2998,17 @@ static void Command_QueueMap_f(void) usingcheats = CV_CheatsEnabled(); ischeating = (!(netgame || multiplayer) || !K_CanChangeRules(false)); - option_clear = COM_CheckParm("-clear"); + // Early check, rather than multiple copypaste. + if (ischeating && !usingcheats) + { + CONS_Printf(M_GetText("Cheats must be enabled.\n")); + return; + } + + option_clear = COM_CheckPartialParm("-c"); if (option_clear) { - if (ischeating && !usingcheats) - { - CONS_Printf(M_GetText("Cheats must be enabled.\n")); - return; - } - if (roundqueue.size == 0) { CONS_Printf(M_GetText("Round queue is already empty!\n")); @@ -3012,16 +3019,10 @@ static void Command_QueueMap_f(void) return; } - option_show = COM_CheckParm("-show"); + option_show = COM_CheckPartialParm("-s"); if (option_show) { - if (ischeating && !usingcheats) - { - CONS_Printf(M_GetText("Cheats must be enabled.\n")); - return; - } - Handle_MapQueueSend(0, ROUNDQUEUE_CMD_SHOW, false); return; } @@ -3035,44 +3036,21 @@ static void Command_QueueMap_f(void) option_force = COM_CheckPartialParm("-f"); option_gametype = COM_CheckPartialParm("-g"); option_encore = COM_CheckPartialParm("-e"); + option_random = COM_CheckPartialParm("-r"); if (!( first_option = COM_FirstOption() )) first_option = COM_Argc(); - if (first_option < 2) + if (!option_random && first_option < 2) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ - CONS_Printf("queuemap [-gametype ] [-force] / [-clear] / [-spoil]:\n"); + CONS_Printf("queuemap [-gametype ] [-force] / [-random] / [-clear] / [-show]:\n"); CONS_Printf(M_GetText( "Queue up a map by its name, or by its number (though why would you).\n" "All parameters are case-insensitive and may be abbreviated.\n")); return; } - mapname = ConcatCommandArgv(1, first_option); - - newmapnum = G_FindMapByNameOrCode(mapname, &realmapname); - - if (newmapnum == 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); - Z_Free(mapname); - return; - } - - if (/*newmapnum != 1 &&*/ M_MapLocked(newmapnum)) - { - ischeating = true; - } - - if (ischeating && !usingcheats) - { - CONS_Printf(M_GetText("Cheats must be enabled.\n")); - Z_Free(realmapname); - Z_Free(mapname); - return; - } - // new gametype value // use current one by default if (option_gametype) @@ -3100,8 +3078,40 @@ static void Command_QueueMap_f(void) } } + if (option_random) + { + // Unlike map -random, this is a server side RNG roll + newmapnum = NEXTMAP_VOTING + 1; + } + else + { + mapname = ConcatCommandArgv(1, first_option); + + newmapnum = G_FindMapByNameOrCode(mapname, &realmapname); + + if (newmapnum == 0) + { + CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); + Z_Free(mapname); + return; + } + + if (M_MapLocked(newmapnum)) + { + ischeating = true; + } + } + + if (ischeating && !usingcheats) + { + CONS_Printf(M_GetText("Cheats must be enabled.\n")); + Z_Free(realmapname); + Z_Free(mapname); + return; + } + // don't use a gametype the map doesn't support - if (cht_debug || option_force || cv_skipmapcheck.value) + if (option_random || cht_debug || option_force || cv_skipmapcheck.value) { // The player wants us to trek on anyway. Do so. } @@ -5296,25 +5306,139 @@ void SoundTest_OnChange(void) static void Command_Showmap_f(void) { - if (gamestate == GS_LEVEL) + UINT16 printmap = NEXTMAP_INVALID; + + size_t first_option; + size_t option_random; + size_t option_gametype; + + INT32 newgametype = gametype; + + char * mapname = NULL; + char *realmapname = NULL; + + option_gametype = COM_CheckPartialParm("-g"); + option_random = COM_CheckPartialParm("-r"); + + if (!( first_option = COM_FirstOption() )) + first_option = COM_Argc(); + + if (option_gametype) { - if (mapheaderinfo[gamemap-1]->zonttl[0] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + newgametype = GetGametypeParm(option_gametype); + if (newgametype == -1) { - if (mapheaderinfo[gamemap-1]->actnum > 0) - CONS_Printf("%s (%d): %s %s %d\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl, mapheaderinfo[gamemap-1]->actnum); - else - CONS_Printf("%s (%d): %s %s\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl); + return; + } + } + + if (option_random) + { + UINT8 numPlayers = 0; + UINT16 oldmapnum = UINT16_MAX; + if (Playing()) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; ++i) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + extern consvar_t cv_forcebots; // debug + + if (!(gametypes[newgametype]->rules & GTR_BOTS) && players[i].bot && !cv_forcebots.value) + { + // Gametype doesn't support bots + continue; + } + + numPlayers++; + } + + oldmapnum = (gamestate == GS_LEVEL) + ? (gamemap-1) + : prevmap; + } + else if (!option_gametype) + { + CONS_Printf("Can't use -random from the menu without -gametype.\n"); + return; + } + + printmap = G_RandMapPerPlayerCount(G_TOLFlag(newgametype), oldmapnum, false, false, NULL, numPlayers); + } + else if (first_option < 2) + { + if (!Playing()) + { + CONS_Printf(M_GetText("You must be in a game to use this.\n")); + return; + } + + printmap = (gamestate == GS_LEVEL) + ? gamemap-1 + : prevmap; + } + else + { + mapname = ConcatCommandArgv(1, first_option); + + printmap = G_FindMapByNameOrCode(mapname, &realmapname); + + if (printmap == 0) + { + CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname); + Z_Free(mapname); + return; + } + + printmap--; // i hate the gamemap off-by-one system + } + + if (printmap < nummapheaders && mapheaderinfo[printmap]) + { + char *title = G_BuildMapTitle(printmap + 1); + + if (mapheaderinfo[printmap]->menuttl[0]) + { + CONS_Printf("%s (%d): %s / %s\n", mapheaderinfo[printmap]->lumpname, printmap, title, mapheaderinfo[printmap]->menuttl); } else { - if (mapheaderinfo[gamemap-1]->actnum > 0) - CONS_Printf("%s (%d): %s %d\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->actnum); + CONS_Printf("%s (%d): %s\n", mapheaderinfo[printmap]->lumpname, printmap, title); + } + + Z_Free(title); + + if ((option_random || first_option < 2) && !option_gametype) + ; + else if (mapheaderinfo[printmap]->typeoflevel & G_TOLFlag(newgametype)) + { + CONS_Printf(" compatible with this gametype\n"); + } + else + { + newgametype = G_GuessGametypeByTOL(mapheaderinfo[printmap]->typeoflevel); + + if (newgametype == -1) + { + CONS_Printf(" NOT compatible with any known gametype\n"); + } else - CONS_Printf("%s (%d): %s\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl); + { + CONS_Printf(" NOT compatible with this gametype (try \"%s\" instead)\n", gametypes[newgametype]->name); + } } } else - CONS_Printf(M_GetText("You must be in a level to use this.\n")); + { + CONS_Printf("Invalid map ID %u\n", printmap); + } + + Z_Free(realmapname); + Z_Free(mapname); } static void Command_Mapmd5_f(void) diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index fb684650f..5404bcc24 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -353,7 +353,7 @@ void M_HandlePauseMenuGametype(INT32 choice) } else // ideally for "random" only, but no sane fallback for "same" and "next" { - COM_ImmedExecute(va("randommap -gt %s", gametypes[menugametype]->name)); + COM_ImmedExecute(va("map -random -gt %s", gametypes[menugametype]->name)); } } return;