/* =========================================================================== Copyright (C) 1997-2001 Id Software, Inc. This file is part of Quake 2 source code. Quake 2 source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake 2 source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake 2 source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // ui_utils.c -- misc support functions for the menus #include #ifdef _WIN32 #include #endif #include "../client/client.h" #include "ui_local.h" #include #include /* ======================================================================= MISC UTILITY FUNCTIONS ======================================================================= */ #if 0 /* ================= FreeFileList ================= */ void FreeFileList (char **list, int n) { int i; for (i = 0; i < n; i++) { if (list && list[i]) { free(list[i]); list[i] = 0; } } free(list); } /* ================= ItemInList ================= */ qboolean ItemInList (char *check, int num, char **list) { int i; for (i=0;i= 0) ) { index = widlcardIndex; } return index; } /* ========================== UI_MouseOverAlpha ========================== */ int UI_MouseOverAlpha (menucommon_s *m) { if (ui_mousecursor.menuitem == m) { int alpha; #ifdef NOTTHIRTYFLIGHTS alpha = 125 + 25 * cos(anglemod(cl.time*0.005)); if (alpha>255) alpha = 255; if (alpha<0) alpha = 0; #else alpha = 255; #endif return alpha; } else return 255; } /* ========================== UI_UnbindCommand ========================== */ void UI_UnbindCommand (char *command) { int j, len, len2; char *b; len = (int)strlen(command); for (j=0; j<256; j++) { b = keybindings[j]; if (!b) continue; len2 = (int)strlen(b); // compare with longer length to prevent +attack2 being confused with +attack if ( !strncmp(b, command, max(len, len2)) ) Key_SetBinding (j, ""); } } /* ========================== UI_FindKeysForCommand ========================== */ void UI_FindKeysForCommand (char *command, int *twokeys) { int count, j, len, len2; char *b; twokeys[0] = twokeys[1] = -1; len = (int)strlen(command); count = 0; for (j=0; j<256; j++) { b = keybindings[j]; if (!b) continue; len2 = (int)strlen(b); // compare with longer length to prevent +attack2 being confused with +attack if ( !strncmp(b, command, max(len, len2)) ) { twokeys[count] = j; count++; if (count == 2) break; } } } /* ================= UI_ItemAtMenuCursor ================= */ void *UI_ItemAtMenuCursor (menuframework_s *m) { if (m->cursor < 0 || m->cursor >= m->nitems) return 0; return m->items[m->cursor]; } /* ================= UI_SetMenuStatusBar ================= */ void UI_SetMenuStatusBar (menuframework_s *m, const char *string) { if (!m) return; m->statusbar = string; } /* ================= UI_TallyMenuSlots ================= */ int UI_TallyMenuSlots (menuframework_s *menu) { int i; int total = 0; for (i = 0; i < menu->nitems; i++) { if ( ((menucommon_s *)menu->items[i])->type == MTYPE_LIST ) { int nitems = 0; const char **n = ((menulist_s *)menu->items[i])->itemNames; while (*n) nitems++, n++; total += nitems; } else total++; } return total; } /* ======================================================================= START GAME FUNCS ======================================================================= */ /* =============== UI_StartSPGame =============== */ void UIStartSPGame (void) { // disable updates and start the cinematic going cl.servercount = -1; UI_ForceMenuOff (); Cvar_SetValue( "deathmatch", 0 ); Cvar_SetValue( "coop", 0 ); Cvar_SetValue( "ctf", 0 ); Cvar_SetValue( "ttctf", 0 ); Cvar_SetValue( "gamerules", 0 ); //PGM if (cls.state != ca_disconnected) // don't force loadscreen if disconnected Cbuf_AddText ("loading ; killserver ; wait\n"); Cbuf_AddText ("newgame\n"); cls.key_dest = key_game; } /* =============== UI_StartServer =============== */ void UI_StartServer (char *startmap, qboolean dedicated) { char *spot = NULL; if ( UI_Coop_MenuMode() ) { if (Q_stricmp(startmap, "bunk1") == 0) spot = "start"; else if (Q_stricmp(startmap, "mintro") == 0) spot = "start"; else if (Q_stricmp(startmap, "fact1") == 0) spot = "start"; else if (Q_stricmp(startmap, "power1") == 0) spot = "pstart"; else if (Q_stricmp(startmap, "biggun") == 0) spot = "bstart"; else if (Q_stricmp(startmap, "hangar1") == 0) spot = "unitstart"; else if (Q_stricmp(startmap, "city1") == 0) spot = "unitstart"; else if (Q_stricmp(startmap, "boss1") == 0) spot = "bosstart"; } UI_ForceMenuOff (); if (dedicated) Cvar_ForceSet ("dedicated", "1"); if (spot) { if (Com_ServerState()) Cbuf_AddText ("disconnect\n"); Cbuf_AddText (va("gamemap \"*%s$%s\"\n", startmap, spot)); } else { Cbuf_AddText (va("map %s\n", startmap)); } // UI_ForceMenuOff (); } /* =============== UI_LoadMod =============== */ void UI_LoadMod (char *modName) { if ( Q_strcasecmp(Cvar_VariableString("game"), modName) ) { UI_ForceMenuOff (); Cbuf_AddText (va("changegame %s\n", modName) ); } } /* ======================================================================= VIDEO INFO LOADING ======================================================================= */ #define UI_MAX_VIDMODES 128 char **ui_resolution_names = NULL; char **ui_video_modes = NULL; int ui_num_video_modes = 0; /* ========================== UI_GetVideoModes ========================== */ void UI_GetVideoModes (void) { int i, j=0, w=0, h=0, firstMode=0, numModes=0; float aspect; char *tok, resBuf[12], aspectBuf[8], nameBuf[20]; // qboolean surround = false; // cvar_t *surround_threshold = Cvar_Get ("scr_surroundthreshold", "3.6", 0); // count video modes >= 640x480 for (i=0; i= 640 && h >= 480) { numModes++; if (numModes == 1) firstMode = i; } } // allocate lists ui_resolution_names = malloc ((numModes+2) * sizeof(char *)); memset (ui_resolution_names, 0, (numModes+2) * sizeof(char *)); ui_video_modes = malloc ((numModes+2) * sizeof(char *)); memset (ui_video_modes, 0, (numModes+2) * sizeof(char *)); // add custom resolution item // ui_resolution_names[0] = strdup ("custom ???"); ui_resolution_names[0] = strdup ("[custom ] [ ??? ]"); ui_video_modes[0] = strdup ("-1"); // add to lists for (i=firstMode, j=1; i<(firstMode+numModes); i++, j++) { if ( !VID_GetModeInfo(&w, &h, i) ) break; if (w >= 640 && h >= 480) { aspect = (float)w / (float)h; memset (resBuf, 0, sizeof(resBuf)); memset (aspectBuf, 0, sizeof(aspectBuf)); memset (nameBuf, 0, sizeof(nameBuf)); Com_sprintf (resBuf, sizeof(resBuf), "%dx%d", w, h); // catch surround modes /* if (aspect > surround_threshold->value) { // 3.6f aspect /= 3.0f; surround = true; } */ if (aspect > 2.39f) tok = "24:10"; else if (aspect > 2.3f) tok = "21:9"; else if (aspect > 1.9f) tok = "16:8"; else if (aspect > 1.85f) tok = "17:10"; else if (aspect > 1.65f) tok = "16:9"; else if (aspect > 1.6f) tok = "15:9"; else if (aspect > 1.55f) tok = "16:10"; else if (aspect > 1.3f) tok = "4:3"; else if (aspect > 1.2f) tok = "5:4"; else tok = va("%3.1f:1", aspect); /* if (surround) Com_sprintf (aspectBuf, sizeof(aspectBuf), "3x%s", tok); else */ Com_sprintf (aspectBuf, sizeof(aspectBuf), "%s", tok); // Com_sprintf (nameBuf, sizeof(nameBuf), "%-12s%s", resBuf, aspectBuf); Com_sprintf (nameBuf, sizeof(nameBuf), "[%-9s] [%-5s]", resBuf, aspectBuf); ui_resolution_names[j] = strdup (nameBuf); ui_video_modes[j] = strdup (va("%i", i)); } } ui_num_video_modes = numModes; } /* ========================== UI_FreeVideoModes ========================== */ void UI_FreeVideoModes (void) { if (ui_num_video_modes > 0) { FS_FreeFileList (ui_resolution_names, ui_num_video_modes); FS_FreeFileList (ui_video_modes, ui_num_video_modes); } ui_resolution_names = NULL; ui_video_modes = NULL; ui_num_video_modes = 0; } char **ui_aniso_names = NULL; char **ui_aniso_values = NULL; int ui_num_aniso_values = 0; /* ========================== UI_GetAnisoValues ========================== */ void UI_GetAnisoValues (void) { int i, numValues; float aniso_avail = Cvar_VariableValue("r_anisotropic_avail"); if (aniso_avail < 2.0) numValues = 1; else if (aniso_avail < 4.0) numValues = 2; else if (aniso_avail < 8.0) numValues = 3; else if (aniso_avail < 16.0) numValues = 4; else // >= 16.0 numValues = 5; // allocate lists ui_aniso_names = malloc ((numValues+1) * sizeof(char *)); memset (ui_aniso_names, 0, (numValues+1) * sizeof(char *)); ui_aniso_values = malloc ((numValues+1) * sizeof(char *)); memset (ui_aniso_values, 0, (numValues+1) * sizeof(char *)); // set names and values for (i=0; i 0) { FS_FreeFileList (ui_aniso_names, ui_num_aniso_values); FS_FreeFileList (ui_aniso_values, ui_num_aniso_values); } ui_aniso_names = NULL; ui_aniso_values = NULL; ui_num_aniso_values = 0; } /* ========================== UI_GetVideoInfo ========================== */ void UI_GetVideoInfo (void) { UI_GetVideoModes (); UI_GetAnisoValues (); } /* ========================== UI_FreeVideoInfo ========================== */ void UI_FreeVideoInfo (void) { UI_FreeVideoModes (); UI_FreeAnisoValues (); } /* ======================================================================= MOD LIST LOADING ======================================================================= */ // TODO: Enable this when mod menu is ready #if 0 #define UI_MAX_MODS 256 char **ui_mod_names = NULL; char **ui_mod_values = NULL; qboolean ui_mod_isUnsupported[UI_MAX_MODS]; int ui_num_mods = 0; /* ========================== UI_BuildModList ========================== */ void UI_BuildModList (void) { char findName[1024]; char modDesc[1024]; char modFormatedName[1024]; char **dirnames; char *modDir, *modName; FILE *f; int count = 0, ndirs = 0, nmods = 0; int i; qboolean unsupportedMod; ui_mod_names = malloc(sizeof(char *) * (UI_MAX_MODS+1)); ui_mod_values = malloc(sizeof(char *) * (UI_MAX_MODS+1)); memset(ui_mod_names, 0, sizeof(char *) * (UI_MAX_MODS+1)); memset(ui_mod_values, 0, sizeof(char *) * (UI_MAX_MODS+1)); // add baseq2 first ui_mod_names[0] = strdup("Quake II (vanilla)"); ui_mod_values[0] = strdup(BASEDIRNAME); ui_mod_isUnsupported[0] = false; count++; // get a list of directories Com_sprintf(findName, sizeof(findName), "%s/*.*", FS_HomePath()); dirnames = FS_ListFiles (findName, &ndirs, SFF_SUBDIR, 0); if (!dirnames) { ui_num_mods = count; return; } // go through the directories nmods = ndirs; if (nmods > UI_MAX_MODS) nmods = UI_MAX_MODS; if ( (count + nmods) > UI_MAX_MODS ) nmods = UI_MAX_MODS - count; for (i = 0; i < nmods; i++) { if (dirnames[i] == 0) continue; modDir = COM_SkipPath(dirnames[i]); // Ignore baseq2 if ( !Q_strcasecmp(modDir, BASEDIRNAME) ) continue; // Must have a pak or pk3 file, or a maps dir if ( !Sys_FindFirst( va("%s/*.pak", dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) { Sys_FindClose(); if ( !Sys_FindFirst( va("%s/*.pk3", dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) { Sys_FindClose(); if ( !Sys_FindFirst( va("%s/maps", dirnames[i]), SFF_SUBDIR, 0) ) { Sys_FindClose(); continue; } } } Sys_FindClose(); // check if this mod has a gamex86.dll/gamei386.so without an equivalent KMQ2 dll/so unsupportedMod = false; if ( Sys_FindFirst( va("%s/"STOCK_Q2_GAME_LIBRARY_NAME, dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) { Sys_FindClose(); if ( !Sys_FindFirst( va("%s/"KMQ2_GAME_LIBRARY_NAME, dirnames[i]), 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) { Sys_FindClose(); unsupportedMod = true; // Com_Printf ("UI_BuildModList: mod %s has an unsupported game library.\n", modDir); } } Sys_FindClose(); // try to load description.txt f = fopen( va("%s/description.txt", dirnames[i]), "rb"); if (f != NULL) { fgets(modDesc, sizeof(modDesc), f); fclose(f); modName = modDesc; } else if ( !Q_strcasecmp(modDir, "ctf") ) modName = "Quake II: Capture The Flag"; else if ( !Q_strcasecmp(modDir, "rogue") ) modName = "Quake II: Ground Zero"; else if ( !Q_strcasecmp(modDir, "xatrix") ) modName = "Quake II: The Reckoning"; else if ( !Q_strcasecmp(modDir, "zaero") ) modName = "Zaero Mission Pack"; else if ( !Q_strcasecmp(modDir, "3zb2") ) modName ="3rd Zigrock Bot II"; else if ( !Q_strcasecmp(modDir, "gen") ) modName = "Generations"; else if ( !Q_strcasecmp(modDir, "ra2") ) modName = "Rocket Arena 2"; else if ( !Q_strcasecmp(modDir, "bots") ) modName = "Battle of the Sexes"; else if ( !Q_strcasecmp(modDir, "lmctf") ) modName = "Loki's Minions CTF"; else if ( !Q_strcasecmp(modDir, "wf") ) modName = "Weapons Factory"; else if ( !Q_strcasecmp(modDir, "wod") ) modName = "Weapons of Destruction"; else if ( !Q_strcasecmp(modDir, "rts") ) modName = "Rob the Strogg"; else modName = modDir; if (unsupportedMod) Com_sprintf(modFormatedName, sizeof(modFormatedName), S_COLOR_ORANGE"%s\0", modName); else Q_strncpyz(modFormatedName, sizeof(modFormatedName), modName); if (!FS_ItemInList(modDir, count, ui_mod_values)) { // FS_InsertInList (ui_mod_names, modName, count, 1); // start=1 so first item stays first! FS_InsertInList (ui_mod_names, modFormatedName, count, 1); // start=1 so first item stays first! FS_InsertInList (ui_mod_values, modDir, count, 1); // start=1 so first item stays first! count++; ui_mod_isUnsupported[count] = unsupportedMod; } } if (dirnames) FS_FreeFileList (dirnames, ndirs); ui_num_mods = count; } /* ========================== UI_GetModList ========================== */ void UI_GetModList (void) { UI_BuildModList (); // Com_Printf ("UI_GetModList: found %i mod dirs\n", ui_num_mods); } /* ========================== UI_FreeModList ========================== */ void UI_FreeModList (void) { if (ui_num_mods > 0) { FS_FreeFileList (ui_mod_names, ui_num_mods); FS_FreeFileList (ui_mod_values, ui_num_mods); } ui_mod_names = NULL; ui_mod_values = NULL; ui_num_mods = 0; } #endif /* ======================================================================= GENERIC ASSET LIST LOADING ======================================================================= */ /* ========================== UI_InsertInAssetList ========================== */ void UI_InsertInAssetList (char **list, char *insert, int len, int start) { int i, j; if (!list || !insert) return; if (start < 0) return; // if (start >= len) return; if (start > len) return; // i=start so default stays first! for (i=start; ii; j--) list[j] = list[j-1]; list[i] = strdup(insert); return; } } list[len] = strdup(insert); } /* ========================== UI_LoadAssetList Generic file list loader Used for fonts, huds, and crosshairs ========================== */ char **UI_LoadAssetList (char *dir, char *nameMask, char *firstItem, int *returnCount, int maxItems, qboolean stripExtension, qboolean frontInsert, qboolean (*checkName)(char *p)) { char **list = 0, **itemFiles; char *path = NULL, *curItem, *p, *ext; // char findName[1024]; int nItems = 0, nItemNames, i, baseLen, extLen; // check pointers if (!dir || !nameMask || !firstItem || !returnCount || !checkName) return NULL; if (maxItems < 2) // must allow at least 2 items return NULL; list = malloc(sizeof(char *) * (maxItems+1)); memset(list, 0, sizeof(char *) * (maxItems+1)); // set first item name list[0] = strdup(firstItem); nItemNames = 1; #if 1 itemFiles = FS_GetFileList(va("%s/%s", dir, nameMask), NULL, &nItems); for (i=0; i 0) { FS_FreeFileList (ui_font_names, ui_numfonts); } ui_font_names = NULL; ui_numfonts = 0; } /* ======================================================================= HUD NAME LOADING ======================================================================= */ // TODO: Enable this when HUD loading is working #if 0 #define UI_MAX_HUDS 128 char **ui_hud_names = NULL; int ui_numhuds = 0; /* ========================== UI_IsValidHudName ========================== */ qboolean UI_IsValidHudName (char *name) { int len = (int)strlen(name); if ( !strcmp(name+max(len-4,0), ".hud") ) return true; return false; } /* ========================== UI_LoadHudNames ========================== */ void UI_LoadHudNames (void) { ui_hud_names = UI_LoadAssetList ("scripts/huds", "*.hud", "default", &ui_numhuds, UI_MAX_HUDS, true, true, UI_IsValidHudName); } /* ========================== UI_FreeHudNames ========================== */ void UI_FreeHudNames (void) { if (ui_numhuds > 0){ FS_FreeFileList (ui_hud_names, ui_numhuds); } ui_hud_names = NULL; ui_numhuds = 0; } #endif /* ======================================================================= CROSSHAIR LOADING ======================================================================= */ #define UI_MAX_CROSSHAIRS 101 // none + ch1-ch100 char **ui_crosshair_names = NULL; char **ui_crosshair_display_names = NULL; char **ui_crosshair_values = NULL; int ui_numcrosshairs = 0; /* ========================== UI_SortCrosshairs ========================== */ void UI_SortCrosshairs (char **list, int len) { int i, j; char *temp; qboolean moved; if (!list || len < 2) return; for (i=(len-1); i>0; i--) { moved = false; for (j=0; j atoi(strdup(list[j+1]+2)) ) { temp = list[j]; list[j] = list[j+1]; list[j+1] = temp; moved = true; } } if (!moved) break; // done sorting } } /* ========================== UI_IsValidCrosshairName ========================== */ qboolean UI_IsValidCrosshairName (char *name) { int namelen; if ( !UI_IsValidImageFilename(name) ) return false; // filename must be chxxx if ( strncmp(name, "ch", 2) ) return false; namelen = (int)strlen(strdup(name)); if (namelen < 7 || namelen > 9) return false; if ( !isNumeric(name[2]) ) return false; if ( namelen >= 8 && !isNumeric(name[3]) ) return false; // ch0 is invalid if ( namelen == 7 && name[2] == '0' ) return false; // ch100 is only valid 5-char name if ( namelen == 9 && (name[2] != '1' || name[3] != '0' || name[4] != '0') ) return false; // ch100-ch128 are only valid 5-char names /* if ( namelen == 9 && ( !isNumeric(name[4]) || name[2] != '1' || (name[2] == '1' && name[3] > '2') || (name[2] == '1' && name[3] == '2' && name[4] > '8') ) ) return false;*/ return true; } /* ========================== UI_LoadCrosshairs ========================== */ void UI_LoadCrosshairs (void) { int i; ui_crosshair_names = UI_LoadAssetList ("pics", "ch*.*", "none", &ui_numcrosshairs, UI_MAX_CROSSHAIRS, true, false, UI_IsValidCrosshairName); UI_SortCrosshairs (ui_crosshair_names, ui_numcrosshairs); ui_crosshair_display_names = malloc( sizeof(char *) * (UI_MAX_CROSSHAIRS+1) ); memcpy(ui_crosshair_display_names, ui_crosshair_names, sizeof(char *) * (UI_MAX_CROSSHAIRS+1)); ui_crosshair_display_names[0] = strdup("chnone"); ui_crosshair_values = malloc( sizeof(char *) * (UI_MAX_CROSSHAIRS+1) ); memset(ui_crosshair_values, 0, sizeof(char *) * (UI_MAX_CROSSHAIRS+1) ); for (i=0; i 0) { FS_FreeFileList (ui_crosshair_names, ui_numcrosshairs); if (ui_crosshair_display_names) { if (ui_crosshair_display_names[0]) { free (ui_crosshair_display_names[0]); } free (ui_crosshair_display_names); } FS_FreeFileList (ui_crosshair_values, ui_numcrosshairs); } ui_crosshair_names = NULL; ui_crosshair_display_names = NULL; ui_crosshair_values = NULL; ui_numcrosshairs = 0; } /* ============================================================================= SAVEGAME / SAVESHOT HANDLING ============================================================================= */ char ui_savestrings[UI_MAX_SAVEGAMES][32]; qboolean ui_savevalid[UI_MAX_SAVEGAMES+1]; time_t ui_savetimestamps[UI_MAX_SAVEGAMES]; qboolean ui_savechanged[UI_MAX_SAVEGAMES]; qboolean ui_saveshotvalid[UI_MAX_SAVEGAMES+1]; char ui_mapname[MAX_QPATH]; char ui_saveload_shotname[MAX_QPATH]; /* ========================== UI_Load_Savestrings ========================== */ void UI_Load_Savestrings (qboolean update) { int i; FILE *fp; fileHandle_t f; char name[MAX_OSPATH]; char mapname[MAX_TOKEN_CHARS]; char *ch; time_t old_timestamp; struct stat st; for (i=0; i= UI_MAX_SAVEGAMES) return NULL; if ( ui_savevalid[index] && ui_saveshotvalid[index] ) { if ( index == 0 ) Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/levelshots/%s.pcx", ui_mapname); else // Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/save/kmq2save%03i/shot.jpg", index); #ifdef NOTTHIRTYFLIGHTS Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/"SAVEDIRNAME"/kmq2save%03i/shot.jpg", index); #else Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), "/"SAVEDIRNAME"/save%i/shot.jpg", index); #endif } else if ( ui_saveshotvalid[UI_MAX_SAVEGAMES] ) Com_sprintf(ui_saveload_shotname, sizeof(ui_saveload_shotname), UI_NOSCREEN_NAME); else // no saveshot or nullshot return NULL; return ui_saveload_shotname; } /* ========================== UI_UpdateSavegameData ========================== */ void UI_UpdateSavegameData (void) { UI_Load_Savestrings (true); UI_ValidateSaveshots (); // register saveshots } /* ========================== UI_InitSavegameData ========================== */ void UI_InitSavegameData (void) { int i; for (i=0; i= UI_MAX_SAVEGAMES) return false; return ui_savevalid[index]; } /* ========================== UI_SaveshotIsValid ========================== */ qboolean UI_SaveshotIsValid (int index) { // check index if (index < 0 || index >= UI_MAX_SAVEGAMES) return false; return ui_saveshotvalid[index]; } /* =============== UI_CanOpenSaveMenu =============== */ qboolean UI_CanOpenSaveMenu (void *unused) { return (Com_ServerState() > 0); } /* ======================================================================= SERVER LISTING Copyright (C) 2001-2003 pat@aftermoon.net for modif flanked by ======================================================================= */ int ui_num_servers; // user readable information char ui_local_server_names[UI_MAX_LOCAL_SERVERS][UI_LOCAL_SERVER_NAMELEN]; //char *ui_serverlist_names[UI_MAX_LOCAL_SERVERS+1]; // network address netadr_t ui_local_server_netadr[UI_MAX_LOCAL_SERVERS]; #if 1 // Added code for compute ping time of server broadcasted // The server is displayed like : // "protocol ping hostname mapname nb players/max players" // "udp 100ms Pat q2dm1 2/8" int global_udp_server_time; int global_ipx_server_time; int global_adr_server_time[16]; netadr_t global_adr_server_netadr[16]; /* ========================== UI_AddToServerList ========================== */ void UI_AddToServerList (netadr_t adr, char *info) { int i; int iPing; char *pszProtocol; if (ui_num_servers == UI_MAX_LOCAL_SERVERS) return; while ( *info == ' ' ) info++; // Ignore if duplicated for (i=0; i 0 ) // udp server { iPing = Sys_Milliseconds() - global_udp_server_time ; pszProtocol = "UDP" ; } else // ipx server { iPing = Sys_Milliseconds() - global_ipx_server_time ; pszProtocol = "IPX" ; } } Com_sprintf( ui_local_server_names[ui_num_servers], sizeof(ui_local_server_names[0]), "%s %4dms %s", pszProtocol, iPing, info ) ; ui_local_server_netadr[ui_num_servers] = adr; ui_num_servers++; } // #else void UI_AddToServerList (netadr_t adr, char *info) { int i; if (ui_num_servers == UI_MAX_LOCAL_SERVERS) return; while ( *info == ' ' ) info++; // ignore if duplicated for (i=0 ; i= UI_MAX_LOCAL_SERVERS || index >= ui_num_servers) return; if ( Q_stricmp( ui_local_server_names[index], NO_SERVER_STRING ) == 0 ) return; Com_sprintf (buffer, sizeof(buffer), "connect %s\n", NET_AdrToString (ui_local_server_netadr[index])); Cbuf_AddText (buffer); UI_ForceMenuOff (); cls.disable_screen = 1; // Knightmare- show loading screen } /* ========================== UI_InitServerList ========================== */ void UI_InitServerList (void) { int i; // ui_serverlist_names[UI_MAX_LOCAL_SERVERS] = NULL; for ( i = 0; i < UI_MAX_LOCAL_SERVERS; i++ ) { Com_sprintf (ui_local_server_names[i], sizeof(ui_local_server_names[i]), NO_SERVER_STRING); // ui_serverlist_names[i] = ui_local_server_names[i]; } } /* ======================================================================= START SERVER MAP LIST ======================================================================= */ gametype_names_t gametype_names[] = { {MAP_DM, "dm ffa team teamdm"}, {MAP_COOP, "coop"}, {MAP_CTF, "ctf"}, {MAP_3TCTF, "3tctf"}, }; maptype_t ui_svr_maptype; static int ui_svr_nummaps; char **ui_svr_mapnames; static int ui_svr_maplist_sizes[NUM_MAPTYPES] = {0, 0, 0, 0}; static char **ui_svr_maplists[NUM_MAPTYPES] = {NULL, NULL, NULL, NULL}; int ui_svr_listfile_nummaps; static char **ui_svr_listfile_mapnames; static int ui_svr_arena_nummaps[NUM_MAPTYPES]; static char **ui_svr_arena_mapnames[NUM_MAPTYPES]; //static byte *ui_svr_mapshotvalid; // levelshot truth table static byte *ui_svr_mapshotvalid[NUM_MAPTYPES] = {NULL, NULL, NULL, NULL}; // levelshot truth tables char ui_startserver_shotname [MAX_QPATH]; qboolean ui_svr_coop = false; qboolean ui_svr_ctf = false; void UI_BuildStartSeverLevelshotTables (void); void UI_FreeStartSeverLevelshotTables (void); /* =============== UI_ParseArenaFromFile Partially from Q3 source =============== */ qboolean UI_ParseArenaFromFile (char *filename, char *shortname, char *longname, char *gametypes, size_t bufSize) { int len; fileHandle_t f; char buf[MAX_ARENAS_TEXT]; char *s, *token, *dest; len = FS_FOpenFile (filename, &f, FS_READ); if (!f) { Com_Printf (S_COLOR_RED "UI_ParseArenaFromFile: file not found: %s\n", filename); return false; } if (len >= MAX_ARENAS_TEXT) { Com_Printf (S_COLOR_RED "UI_ParseArenaFromFile: file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT); FS_FCloseFile (f); return false; } FS_Read (buf, len, f); buf[len] = 0; FS_FCloseFile (f); s = buf; // get the opening curly brace token = COM_Parse (&s); if (!token) { Com_Printf ("UI_ParseArenaFromFile: unexpected EOF\n"); return false; } if (token[0] != '{') { Com_Printf ("UI_ParseArenaFromFile: found %s when expecting {\n", token); return false; } // go through all the parms while (s < (buf + len)) { dest = NULL; token = COM_Parse (&s); if (token && (token[0] == '}')) break; if (!token || !s) { Com_Printf ("UI_ParseArenaFromFile: EOF without closing brace\n"); break; } if (!Q_strcasecmp(token, "map")) dest = shortname; else if (!Q_strcasecmp(token, "longname")) dest = longname; else if (!Q_strcasecmp(token, "type")) dest = gametypes; if (dest) { token = COM_Parse (&s); if (!token) { Com_Printf ("UI_ParseArenaFromFile: unexpected EOF\n"); return false; } if (token[0] == '}') { Com_Printf ("UI_ParseArenaFromFile: closing brace without data\n"); break; } if (!s) { Com_Printf ("UI_ParseArenaFromFile: EOF without closing brace\n"); break; } // strncpy(dest, token); Q_strncpyz (dest, bufSize, token); } } if (!shortname || !strlen(shortname)) { Com_Printf (S_COLOR_RED "UI_ParseArenaFromFile: %s: map field not found\n", filename); return false; } if (!strlen(longname)) longname = shortname; return true; } /* =============== UI_SortArenas =============== */ void UI_SortArenas (char **list, int len) { int i, j; char *temp, *s1, *s2; qboolean moved; if (!list || len < 2) return; for (i=(len-1); i>0; i--) { moved = false; for (j=0; j 0) { temp = list[j]; list[j] = list[j+1]; list[j+1] = temp; moved = true; } } if (!moved) break; // done sorting } } /* =============== UI_LoadArenas =============== */ void UI_LoadArenas (void) { char *p, *s, *s2, *tok, *tok2; char **arenafiles = NULL; char **tmplist = NULL; char *path = NULL; // char findName[1024]; char shortname[MAX_TOKEN_CHARS]; char longname[MAX_TOKEN_CHARS]; char gametypes[MAX_TOKEN_CHARS]; char scratch[200]; int i, j, len, narenas = 0, narenanames = 0; size_t nameSize; qboolean type_supported[NUM_MAPTYPES]; // // free existing lists and malloc new ones // for (i=0; i= ui_svr_maplist_sizes[ui_svr_maptype]) return NULL; Q_strncpyz (startmap, sizeof(startmap), strchr( ui_svr_maplists[ui_svr_maptype][index], '\n' ) + 1); if (ui_svr_mapshotvalid[ui_svr_maptype][index] == M_UNSET) { // init levelshot Com_sprintf(mapshotname, sizeof(mapshotname), "/levelshots/%s.pcx", startmap); if (R_DrawFindPic(mapshotname)) ui_svr_mapshotvalid[ui_svr_maptype][index] = M_FOUND; else ui_svr_mapshotvalid[ui_svr_maptype][index] = M_MISSING; } if (ui_svr_mapshotvalid[ui_svr_maptype][index] == M_FOUND) Com_sprintf(ui_startserver_shotname, sizeof(ui_startserver_shotname), "/levelshots/%s.pcx", startmap); else if (ui_svr_mapshotvalid[ui_svr_maptype][ui_svr_maplist_sizes[ui_svr_maptype]] == M_FOUND) Com_sprintf(ui_startserver_shotname, sizeof(ui_startserver_shotname), UI_NOSCREEN_NAME); else return NULL; return ui_startserver_shotname; } /* =============== UI_SetCoopMenuMode =============== */ void UI_SetCoopMenuMode (qboolean value) { ui_svr_coop = value; } /* =============== UI_Coop_MenuMode =============== */ qboolean UI_Coop_MenuMode (void) { return ui_svr_coop; } /* =============== UI_CanOpenDMFlagsMenu =============== */ qboolean UI_CanOpenDMFlagsMenu (void *unused) { return !ui_svr_coop; } /* =============== UI_SetCTFMenuMode =============== */ void UI_SetCTFMenuMode (qboolean value) { ui_svr_ctf = value; } /* =============== UI_CTF_MenuMode =============== */ qboolean UI_CTF_MenuMode (void) { return ui_svr_ctf; } /* ======================================================================= PLAYER MODEL LOADING ======================================================================= */ playermodelinfo_s ui_pmi[MAX_PLAYERMODELS]; char *ui_pmnames[MAX_PLAYERMODELS]; int ui_numplayermodels; // save skins and models here so as to not have to re-register every frame struct model_s *ui_playermodel; struct model_s *ui_weaponmodel; struct image_s *ui_playerskin; char *ui_currentweaponmodel; /* char ui_playerconfig_playermodelname[MAX_QPATH]; char ui_playerconfig_playerskinname[MAX_QPATH]; char ui_playerconfig_weaponmodelname[MAX_QPATH]; color_t ui_player_color_imageColors[] = { #include "ui_playercolors.h" }; // last table entry is null value #define UI_NUM_PLAYER_COLORS ((sizeof(ui_player_color_imageColors) / sizeof(ui_player_color_imageColors[0])) - 1) char **ui_player_color_values = NULL; char **ui_player_color_imageNames = NULL; int ui_numplayercolors = 0; */ /* ========================== UI_IsSkinIcon ========================== */ static qboolean UI_IsSkinIcon (char *name) { int len; char *s, scratch[1024]; Q_strncpyz(scratch, sizeof(scratch), name); *strrchr(scratch, '.') = 0; s = scratch; len = (int)strlen(s); return (!strcmp(s+max(len-2,0), "_i")); } /* ========================== UI_IconOfSkinExists ========================== */ static qboolean UI_IconOfSkinExists (char *skin, char **files, int nfiles, char *suffix) { int i; char scratch[1024]; Q_strncpyz (scratch, sizeof(scratch), skin); *strrchr(scratch, '.') = 0; Q_strncatz (scratch, sizeof(scratch), suffix); // strncat(scratch, "_i.pcx"); for (i = 0; i < nfiles; i++) { if ( strcmp(files[i], scratch) == 0 ) return true; } return false; } /* ========================== UI_IsValidSkin Adds menu support for TGA and JPG skins ========================== */ static qboolean UI_IsValidSkin (char **filelist, int numFiles, int index) { if ( UI_IsValidImageFilename(filelist[index]) && !UI_IsSkinIcon(filelist[index]) ) { if ( UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.pcx") || UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.tga") #ifdef PNG_SUPPORT || UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.png") #endif // PNG_SUPPORT || UI_IconOfSkinExists (filelist[index], filelist, numFiles-1 , "_i.jpg")) return true; } return false; } /* ========================== UI_PlayerConfig_ScanDirectories ========================== */ static qboolean UI_PlayerConfig_ScanDirectories (void) { char findname[1024]; char scratch[1024]; int ndirs = 0, npms = 0; char **dirnames; char *path = NULL; int i; ui_numplayermodels = 0; // loop back to here if there were no valid player models found in the selected path do { // // get a list of directories // do { path = FS_NextPath(path); Com_sprintf( findname, sizeof(findname), "%s/players/*.*", path ); if ( (dirnames = FS_ListFiles(findname, &ndirs, SFF_SUBDIR, 0)) != 0 ) break; } while (path); if (!dirnames) return false; // // go through the subdirectories // npms = ndirs; if (npms > MAX_PLAYERMODELS) npms = MAX_PLAYERMODELS; if ( (ui_numplayermodels + npms) > MAX_PLAYERMODELS ) npms = MAX_PLAYERMODELS - ui_numplayermodels; for (i = 0; i < npms; i++) { int k, s; char *a, *b, *c; char **skinnames; char **skiniconnames; char **imagenames; int nimagefiles; int nskins = 0; qboolean already_added = false; if (dirnames[i] == 0) continue; // check if dirnames[i] is already added to the ui_pmi[i].directory list a = strrchr(dirnames[i], '/'); b = strrchr(dirnames[i], '\\'); c = (a > b) ? a : b; for (k=0; k < ui_numplayermodels; k++) if (!strcmp(ui_pmi[k].directory, c+1)) { already_added = true; break; } if (already_added) { // todo: add any skins for this model not already listed to skinDisplayNames continue; } // verify the existence of tris.md2 // strncpy(scratch, dirnames[i]); // strncat(scratch, "/tris.md2"); Q_strncpyz(scratch, sizeof(scratch), dirnames[i]); Q_strncatz(scratch, sizeof(scratch), "/tris.md2"); if ( !Sys_FindFirst(scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) ) { free(dirnames[i]); dirnames[i] = 0; Sys_FindClose(); continue; } Sys_FindClose(); // verify the existence of at least one skin // strncpy(scratch, va("%s%s", dirnames[i], "/*.*")); // was "/*.pcx" Q_strncpyz(scratch, sizeof(scratch), va("%s%s", dirnames[i], "/*.*")); // was "/*.pcx" imagenames = FS_ListFiles (scratch, &nimagefiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM); if (!imagenames) { free(dirnames[i]); dirnames[i] = 0; continue; } // count valid skins, which consist of a skin with a matching "_i" icon for (k = 0; k < nimagefiles-1; k++) if ( UI_IsValidSkin(imagenames, nimagefiles, k) ) nskins++; if (!nskins) continue; // make short name for the model a = strrchr(dirnames[i], '/'); b = strrchr(dirnames[i], '\\'); c = (a > b) ? a : b; // strncpy(ui_pmi[ui_numplayermodels].displayname, c+1, MAX_DISPLAYNAME-1); // strncpy(ui_pmi[ui_numplayermodels].directory, c+1); Q_strncpyz(ui_pmi[ui_numplayermodels].displayname, sizeof(ui_pmi[ui_numplayermodels].displayname), c+1); Q_strncpyz(ui_pmi[ui_numplayermodels].directory, sizeof(ui_pmi[ui_numplayermodels].directory), c+1); skinnames = malloc(sizeof(char *) * (nskins+1)); memset(skinnames, 0, sizeof(char *) * (nskins+1)); skiniconnames = malloc(sizeof(char *) * (nskins+1)); memset(skiniconnames, 0, sizeof(char *) * (nskins+1)); // copy the valid skins if (nimagefiles) for (s = 0, k = 0; k < nimagefiles-1; k++) { char *a, *b, *c; if ( UI_IsValidSkin(imagenames, nimagefiles, k) ) { a = strrchr(imagenames[k], '/'); b = strrchr(imagenames[k], '\\'); c = (a > b) ? a : b; // strncpy(scratch, c+1); Q_strncpyz(scratch, sizeof(scratch), c+1); if ( strrchr(scratch, '.') ) *strrchr(scratch, '.') = 0; skinnames[s] = strdup(scratch); skiniconnames[s] = strdup(va("/players/%s/%s_i.pcx", ui_pmi[ui_numplayermodels].directory, scratch)); s++; } } // at this point we have a valid player model ui_pmi[ui_numplayermodels].nskins = nskins; ui_pmi[ui_numplayermodels].skinDisplayNames = skinnames; ui_pmi[ui_numplayermodels].skinIconNames = skiniconnames; // make short name for the model // a = strrchr(dirnames[i], '/'); // b = strrchr(dirnames[i], '\\'); // c = (a > b) ? a : b; // Q_strncpyz(ui_pmi[ui_numplayermodels].displayname, sizeof(ui_pmi[ui_numplayermodels].displayname), c+1); // Q_strncpyz(ui_pmi[ui_numplayermodels].directory, sizeof(ui_pmi[ui_numplayermodels].directory), c+1); FS_FreeFileList (imagenames, nimagefiles); ui_numplayermodels++; } if (dirnames) FS_FreeFileList (dirnames, ndirs); // if no valid player models found in path, // try next path, if there is one } while (path); // (s_numplayermodels == 0 && path); return true; //** DMP warning fix } #if 0 /* ========================== UI_BuildPlayerColorList ========================== */ void UI_BuildPlayerColorList (void) { int i, numColors = 0; ui_player_color_values = malloc(sizeof(char *) * (UI_NUM_PLAYER_COLORS+1)); ui_player_color_imageNames = malloc(sizeof(char *) * (UI_NUM_PLAYER_COLORS+1)); memset(ui_player_color_values, 0, sizeof(char *) * (UI_NUM_PLAYER_COLORS+1)); memset(ui_player_color_imageNames, 0, sizeof(char *) * (UI_NUM_PLAYER_COLORS+1)); for (i = 0; i < UI_NUM_PLAYER_COLORS; i++) { // last index is custom color if (i == UI_NUM_PLAYER_COLORS-1) { ui_player_color_values[i] = strdup(UI_ITEMVALUE_WILDCARD); ui_player_color_imageNames[i] = strdup(UI_CUSTOMCOLOR_PIC); } else { ui_player_color_values[i] = strdup(va("%02X%02X%02X", ui_player_color_imageColors[i][0], ui_player_color_imageColors[i][1], ui_player_color_imageColors[i][2])); ui_player_color_imageNames[i] = strdup(UI_SOLIDWHITE_PIC); } numColors++; } ui_numplayercolors = numColors; } #endif /* ========================== UI_LoadPlayerModels ========================== */ void UI_LoadPlayerModels (void) { UI_PlayerConfig_ScanDirectories (); // UI_BuildPlayerColorList (); } /* ========================== UI_FreePlayerModels ========================== */ void UI_FreePlayerModels (void) { int i; for (i = 0; i < ui_numplayermodels; i++) { int j; for (j = 0; j < ui_pmi[i].nskins; j++) { if (ui_pmi[i].skinDisplayNames[j]) free(ui_pmi[i].skinDisplayNames[j]); ui_pmi[i].skinDisplayNames[j] = NULL; } free(ui_pmi[i].skinDisplayNames); ui_pmi[i].skinDisplayNames = NULL; ui_pmi[i].nskins = 0; } /* if (ui_numplayercolors > 0) { FS_FreeFileList (ui_player_color_values, ui_numplayercolors); FS_FreeFileList (ui_player_color_imageNames, ui_numplayercolors); } ui_player_color_values = NULL; ui_player_color_imageNames = NULL; ui_numplayercolors = 0; */ } /* ========================== UI_RefreshPlayerModels Reloads player models if we recently downloaded a player model. ========================== */ void UI_RefreshPlayerModels (void) { if (cls.refreshPlayerModels) { Com_DPrintf ("UI_RefreshPlayerModels: reloading player models due to recent download of a player model.\n"); UI_FreePlayerModels (); UI_LoadPlayerModels (); cls.refreshPlayerModels = false; // clear the flag } } /* ========================== UI_PlayerModelCmpFunc ========================== */ int UI_PlayerModelCmpFunc (const void *_a, const void *_b) { const playermodelinfo_s *a = (const playermodelinfo_s *) _a; const playermodelinfo_s *b = (const playermodelinfo_s *) _b; // // sort by male, female, then alphabetical // if ( strcmp(a->directory, "male") == 0 ) return -1; else if (strcmp( b->directory, "male") == 0 ) return 1; if ( strcmp(a->directory, "female") == 0 ) return -1; else if (strcmp( b->directory, "female") == 0 ) return 1; return strcmp(a->directory, b->directory); } /* ========================== UI_InitPlayerModelInfo ========================== */ void UI_InitPlayerModelInfo (int *modelNum, int *skinNum) { int i; int currentdirectoryindex = 0; int currentskinindex = 0; char currentdirectory[1024]; char currentskin[1024]; if (ui_numplayermodels <= 0) { modelNum = skinNum = 0; return; } Q_strncpyz(currentdirectory, sizeof(currentdirectory), Cvar_VariableString ("skin")); if ( strchr( currentdirectory, '/' ) ) { Q_strncpyz(currentskin, sizeof(currentskin), strchr( currentdirectory, '/' ) + 1); *strchr( currentdirectory, '/' ) = 0; } else if ( strchr( currentdirectory, '\\' ) ) { Q_strncpyz(currentskin, sizeof(currentskin), strchr( currentdirectory, '\\' ) + 1); *strchr( currentdirectory, '\\' ) = 0; } else { Q_strncpyz(currentdirectory, sizeof(currentdirectory), "male"); Q_strncpyz(currentskin, sizeof(currentskin), "grunt"); } qsort( ui_pmi, ui_numplayermodels, sizeof( ui_pmi[0] ), UI_PlayerModelCmpFunc ); memset( ui_pmnames, 0, sizeof( ui_pmnames ) ); for (i = 0; i < ui_numplayermodels; i++) { ui_pmnames[i] = ui_pmi[i].displayname; if (Q_stricmp( ui_pmi[i].directory, currentdirectory ) == 0) { int j; currentdirectoryindex = i; for (j = 0; j < ui_pmi[i].nskins; j++) { if (Q_stricmp( ui_pmi[i].skinDisplayNames[j], currentskin ) == 0) { currentskinindex = j; break; } } } } // precache this player model and skin UI_UpdatePlayerModelInfo (currentdirectoryindex, currentskinindex); if (modelNum) *modelNum = currentdirectoryindex; if (skinNum) *skinNum = currentskinindex; } /* ========================== UI_UpdatePlayerModelInfo ========================== */ void UI_UpdatePlayerModelInfo (int mNum, int sNum) { char scratch[MAX_QPATH]; Com_sprintf (scratch, sizeof(scratch), "players/%s/tris.md2", ui_pmi[mNum].directory); ui_playermodel = R_RegisterModel (scratch); // Q_strncpyz (ui_playerconfig_playermodelname, sizeof(ui_playerconfig_playermodelname), scratch); Com_sprintf (scratch, sizeof(scratch), "players/%s/%s.pcx", ui_pmi[mNum].directory, ui_pmi[mNum].skinDisplayNames[sNum]); ui_playerskin = R_RegisterSkin (scratch); // Q_strncpyz (ui_playerconfig_playerskinname, sizeof(ui_playerconfig_playerskinname), scratch); // show current weapon model (if any) if (ui_currentweaponmodel && strlen(ui_currentweaponmodel)) { Com_sprintf (scratch, sizeof(scratch), "players/%s/%s", ui_pmi[mNum].directory, ui_currentweaponmodel); ui_weaponmodel = R_RegisterModel(scratch); if (!ui_weaponmodel) { Com_sprintf (scratch, sizeof(scratch), "players/%s/weapon.md2", ui_pmi[mNum].directory); ui_weaponmodel = R_RegisterModel (scratch); } } else { Com_sprintf (scratch, sizeof(scratch), "players/%s/weapon.md2", ui_pmi[mNum].directory); ui_weaponmodel = R_RegisterModel (scratch); } // Q_strncpyz (ui_playerconfig_weaponmodelname, sizeof(ui_playerconfig_weaponmodelname), scratch); } /* ========================== UI_UpdatePlayerSkinInfo ========================== */ void UI_UpdatePlayerSkinInfo (int mNum, int sNum) { char scratch[MAX_QPATH]; Com_sprintf(scratch, sizeof(scratch), "players/%s/%s.pcx", ui_pmi[mNum].directory, ui_pmi[mNum].skinDisplayNames[sNum]); ui_playerskin = R_RegisterSkin(scratch); // Q_strncpyz (ui_playerconfig_playerskinname, sizeof(ui_playerconfig_playerskinname), scratch); } /* ========================== UI_HaveValidPlayerModels ========================== */ qboolean UI_HaveValidPlayerModels (void *unused) { return (ui_numplayermodels > 0); }