mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2025-01-19 07:51:03 +00:00
Merge pull request #599 from earth-metal/master
Create "mods" submenu to allow changing "game" cvar via UI
This commit is contained in:
commit
41931d7a06
3 changed files with 274 additions and 6 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue