Merge pull request #599 from earth-metal/master

Create "mods" submenu to allow changing "game" cvar via UI
This commit is contained in:
Yamagi 2020-09-12 16:11:26 +02:00 committed by GitHub
commit 41931d7a06
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 274 additions and 6 deletions

View file

@ -50,6 +50,7 @@ static void M_Menu_SaveGame_f(void);
static void M_Menu_PlayerConfig_f(void);
static void M_Menu_DownloadOptions_f(void);
static void M_Menu_Credits_f(void);
static void M_Menu_Mods_f(void);
static void M_Menu_Multiplayer_f(void);
static void M_Menu_JoinServer_f(void);
static void M_Menu_AddressBook_f(void);
@ -84,7 +85,8 @@ M_IsGame(const char *gamename)
{
cvar_t *game = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO);
if (strcmp(game->string, gamename) == 0)
if (strcmp(game->string, gamename) == 0
|| (strcmp(gamename, BASEDIRNAME) == 0 && strcmp(game->string, "") == 0))
{
return true;
}
@ -1955,6 +1957,140 @@ M_Menu_Credits_f(void)
M_PushMenu(M_Credits_MenuDraw, M_Credits_Key);
}
/*
* MODS MENU
*/
static menuframework_s s_mods_menu;
static menulist_s s_mods_list;
static menuaction_s s_mods_apply_action;
static char mods_statusbar[64];
static char **modnames = NULL;
static int nummods;
static void
Mods_NamesInit(void)
{
/* initialize list of mods once, reuse it afterwards (=> it isn't freed) */
if (modnames == NULL)
{
modnames = FS_ListMods(&nummods);
}
}
static void
ModsListFunc(void *unused)
{
if (strcmp(BASEDIRNAME, modnames[s_mods_list.curvalue]) == 0)
{
strcpy(mods_statusbar, "Quake II");
}
else if (strcmp("ctf", modnames[s_mods_list.curvalue]) == 0)
{
strcpy(mods_statusbar, "Quake II Capture The Flag");
}
else if (strcmp("rogue", modnames[s_mods_list.curvalue]) == 0)
{
strcpy(mods_statusbar, "Quake II Mission Pack: Ground Zero");
}
else if (strcmp("xatrix", modnames[s_mods_list.curvalue]) == 0)
{
strcpy(mods_statusbar, "Quake II Mission Pack: The Reckoning");
}
else
{
strcpy(mods_statusbar, "\0");
}
}
static void
ModsApplyActionFunc(void *unused)
{
if (!M_IsGame(modnames[s_mods_list.curvalue]))
{
if(Com_ServerState())
{
// equivalent to "killserver" cmd, but avoids cvar latching below
SV_Shutdown("Server is changing games.\n", false);
NET_Config(false);
}
// called via command buffer so that any running server has time to shutdown
Cbuf_AddText(va("game %s\n", modnames[s_mods_list.curvalue]));
// start the demo cycle in the new game directory
Cbuf_AddText("d1\n");
M_ForceMenuOff();
}
}
static void
Mods_MenuInit(void)
{
int currentmod;
Mods_NamesInit();
// pre-select the current mod for display in the list
for (currentmod = 0; currentmod < nummods; currentmod++)
{
if (M_IsGame(modnames[currentmod]))
{
break;
}
}
s_mods_menu.x = (int)(viddef.width * 0.50f);
s_mods_menu.nitems = 0;
s_mods_list.generic.type = MTYPE_SPINCONTROL;
s_mods_list.generic.name = "mod";
s_mods_list.generic.x = 0;
s_mods_list.generic.y = 0;
s_mods_list.generic.callback = ModsListFunc;
s_mods_list.itemnames = (const char **)modnames;
s_mods_list.curvalue = currentmod < nummods ? currentmod : 0;
s_mods_apply_action.generic.type = MTYPE_ACTION;
s_mods_apply_action.generic.flags = QMF_LEFT_JUSTIFY;
s_mods_apply_action.generic.name = " apply";
s_mods_apply_action.generic.x = 49;
s_mods_apply_action.generic.y = 20;
s_mods_apply_action.generic.callback = ModsApplyActionFunc;
Menu_AddItem(&s_mods_menu, (void *)&s_mods_list);
Menu_AddItem(&s_mods_menu, (void *)&s_mods_apply_action);
Menu_Center(&s_mods_menu);
/* set the original mods statusbar */
ModsListFunc(0);
Menu_SetStatusBar(&s_mods_menu, mods_statusbar);
}
static void
Mods_MenuDraw(void)
{
Menu_AdjustCursor(&s_mods_menu, 1);
Menu_Draw(&s_mods_menu);
M_Popup();
}
static const char *
Mods_MenuKey(int key)
{
return Default_MenuKey(&s_mods_menu, key);
}
static void
M_Menu_Mods_f(void)
{
Mods_MenuInit();
M_PushMenu(Mods_MenuDraw, Mods_MenuKey);
}
/*
* GAME MENU
*/
@ -1969,6 +2105,7 @@ static menuaction_s s_hardp_game_action;
static menuaction_s s_load_game_action;
static menuaction_s s_save_game_action;
static menuaction_s s_credits_action;
static menuaction_s s_mods_action;
static menuseparator_s s_blankline;
static void
@ -2035,9 +2172,17 @@ CreditsFunc(void *unused)
M_Menu_Credits_f();
}
static void
ModsFunc(void *unused)
{
M_Menu_Mods_f();
}
void
Game_MenuInit(void)
{
Mods_NamesInit();
s_game_menu.x = (int)(viddef.width * 0.50f);
s_game_menu.nitems = 0;
@ -2099,9 +2244,21 @@ Game_MenuInit(void)
Menu_AddItem(&s_game_menu, (void *)&s_blankline);
Menu_AddItem(&s_game_menu, (void *)&s_load_game_action);
Menu_AddItem(&s_game_menu, (void *)&s_save_game_action);
Menu_AddItem(&s_game_menu, (void *)&s_blankline);
Menu_AddItem(&s_game_menu, (void *)&s_credits_action);
if(nummods > 1)
{
s_mods_action.generic.type = MTYPE_ACTION;
s_mods_action.generic.flags = QMF_LEFT_JUSTIFY;
s_mods_action.generic.x = 0;
s_mods_action.generic.y = 90;
s_mods_action.generic.name = "mods";
s_mods_action.generic.callback = ModsFunc;
Menu_AddItem(&s_game_menu, (void *)&s_blankline);
Menu_AddItem(&s_game_menu, (void *)&s_mods_action);
}
Menu_Center(&s_game_menu);
}
@ -2837,7 +2994,7 @@ M_Menu_JoinServer_f(void)
static menuframework_s s_startserver_menu;
char **mapnames = NULL;
static int nummaps;
int nummaps;
static menuaction_s s_startserver_start_action;
static menuaction_s s_startserver_dmoptions_action;
@ -4593,6 +4750,7 @@ M_Init(void)
Cmd_AddCommand("menu_playerconfig", M_Menu_PlayerConfig_f);
Cmd_AddCommand("menu_downloadoptions", M_Menu_DownloadOptions_f);
Cmd_AddCommand("menu_credits", M_Menu_Credits_f);
Cmd_AddCommand("menu_mods", M_Menu_Mods_f);
Cmd_AddCommand("menu_multiplayer", M_Menu_Multiplayer_f);
Cmd_AddCommand("menu_video", M_Menu_Video_f);
Cmd_AddCommand("menu_options", M_Menu_Options_f);

View file

@ -33,6 +33,7 @@
#define MAX_HANDLES 512
#define MAX_MODS 32
#define MAX_PAKS 100
#ifdef SYSTEMWIDE
@ -1673,9 +1674,18 @@ FS_BuildGameSpecificSearchPath(char *dir)
// are possibly from the new mod dir)
OGG_InitTrackList();
// ...and the current list of maps in the "start network server" menu is
// cleared so that it will be re-initialized when the menu is accessed
mapnames = NULL;
// ...and the current list of maps in the "start network server" menu is
// cleared so that it will be re-initialized when the menu is accessed
if (mapnames != NULL)
{
for (i = 0; i < nummaps; i++)
{
free(mapnames[i]);
}
free(mapnames);
mapnames = NULL;
}
#endif
}
@ -1792,3 +1802,101 @@ FS_InitFilesystem(void)
Com_Printf("Using '%s' for writing.\n", fs_gamedir);
}
extern int qsort_strcomp(const void *s1, const void *s2);
/*
* Combs all Raw search paths to find game dirs containing PAK/PK2/PK3 files.
* Returns an alphabetized array of unique relative dir names.
*/
char**
FS_ListMods(int *nummods)
{
int nmods = 0, numdirchildren, numpacksinchilddir, searchpathlength;
char findnamepattern[MAX_OSPATH], modname[MAX_QPATH], searchpath[MAX_OSPATH];
char **dirchildren, **packsinchilddir, **modnames;
modnames = malloc((MAX_QPATH + 1) * (MAX_MODS + 1));
memset(modnames, 0, (MAX_QPATH + 1) * (MAX_MODS + 1));
// iterate over all Raw paths
for (fsRawPath_t *search = fs_rawPath; search; search = search->next)
{
searchpathlength = strlen(search->path);
if(!searchpathlength)
{
continue;
}
// make sure this Raw path ends with a '/' otherwise FS_ListFiles will open its parent dir
if(search->path[searchpathlength - 1] != '/')
{
Com_sprintf(searchpath, sizeof(searchpath), "%s/", search->path);
}
else
{
strcpy(searchpath, search->path);
}
dirchildren = FS_ListFiles(searchpath, &numdirchildren, 0, 0);
// iterate over the children of this Raw path (unless we've already got enough mods)
for (int i = 0; i < numdirchildren && nmods < MAX_MODS; i++)
{
if(dirchildren[i] == NULL)
{
continue;
}
numpacksinchilddir = 0;
// iterate over supported pack types, but ignore ZIP files (they cause false positives)
for (int j = 0; j < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); j++)
{
if (strcmp("zip", fs_packtypes[j].suffix) != 0)
{
Com_sprintf(findnamepattern, sizeof(findnamepattern), "%s/*.%s", dirchildren[i], fs_packtypes[j].suffix);
packsinchilddir = FS_ListFiles(findnamepattern, &numpacksinchilddir, 0, 0);
FS_FreeList(packsinchilddir, numpacksinchilddir);
// if this dir has some pack files, add it if not already in the list
if (numpacksinchilddir > 0)
{
qboolean matchfound = false;
Com_sprintf(modname, sizeof(modname), "%s", strrchr(dirchildren[i], '/') + 1);
for (int k = 0; k < nmods; k++)
{
if (strcmp(modname, modnames[k]) == 0)
{
matchfound = true;
break;
}
}
if (!matchfound)
{
modnames[nmods] = malloc(strlen(modname) + 1);
strcpy(modnames[nmods], modname);
nmods++;
}
break;
}
}
}
}
FS_FreeList(dirchildren, numdirchildren);
}
modnames[nmods] = 0;
qsort(modnames, nmods, sizeof(modnames[0]), qsort_strcomp);
*nummods = nmods;
return modnames;
}

View file

@ -684,6 +684,7 @@ int FS_LoadFile(char *path, void **buffer);
qboolean FS_FileInGamedir(const char *file);
qboolean FS_AddPAKFromGamedir(const char *pak);
const char* FS_GetNextRawPath(const char* lastRawPath);
char **FS_ListMods(int *nummods);
/* a null buffer will just return the file length without loading */
/* a -1 length is not present */
@ -757,6 +758,7 @@ extern char cfgdir[MAX_OSPATH];
/* Hack for working 'game' cmd */
extern char userGivenGame[MAX_QPATH];
extern char **mapnames;
extern int nummaps;
extern FILE *log_stats_file;